Dungeon engine problem
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Dungeon engine problem
Hello again.
I'm also using this engine as a backup for my project:
viewtopic.php?f=51&t=19245&start=90
Yet I'm having some problems and will thank you if anyone can help me.
It seems there's a bug where you can walk throught walls. Also, events transport you outside the dungeon yet there's not way to keep exploring it. Finally, I want to add sprites for enemies and characters. I got the gist of the battle system from the original post, but will also need a bit of assistanse in the future, problably.
I'm also using this engine as a backup for my project:
viewtopic.php?f=51&t=19245&start=90
Yet I'm having some problems and will thank you if anyone can help me.
It seems there's a bug where you can walk throught walls. Also, events transport you outside the dungeon yet there's not way to keep exploring it. Finally, I want to add sprites for enemies and characters. I got the gist of the battle system from the original post, but will also need a bit of assistanse in the future, problably.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
Still can´t solve the problem, here's the script, I hope someone can help me how to figure how to make the walls block the movement and act like proper walls:
Code: Select all
init -1 python:
class Stage(object):
'''
Class which contains map itself, auto mapping record, and encounter enemy.
'''
def __init__(self, map, enemy=None):
self.map=map
self.enemy=enemy
self.mapped=[]
for n,i in enumerate(map):
self.mapped.append([])
for j in i:
self.mapped[n].append(0)
class Coordinate(object):
'''
Class used for calculating relative coordinate.
'''
def __init__(self, stage=None, y=0, x=0, dy=0, dx=0):
self.stage=stage
self.y=y
self.x=x
self.dy=dy
self.dx=dx
class Minimap(object):
'''
A minimap. Minimap(current_coordinate).sm is a displayable to show this minimap.
'''
def __init__(self,child):
self.sm = SpriteManager(ignore_time=True)
for n,i in enumerate(child.stage.map):
for m, j in enumerate(i):
if child.stage.mapped[n][m]==1:
if j in ["1"]:
d = Solid("#666", xysize=(12,12))
else:
d = Solid("#fff9", xysize=(12,12))
else:
d = Solid("#0000", xysize=(12,12))
self.add(d,n,m)
if child.dy==-1:
self.add(Text("↑",size=12),child.y,child.x)
elif child.dx==1:
self.add(Text("→",size=12),child.y,child.x)
elif child.dy==1:
self.add(Text("↓",size=12),child.y,child.x)
else:
self.add(Text("←",size=12),child.y,child.x)
def add(self, d,n,m):
s = self.sm.create(d)
s.x = m*12+12
s.y = n*12+12
screen move:
# Screen which shows move buttons and a minimap
fixed style_group "move":
if front1.stage.map[front1.y][front1.x] is not "1":
textbutton "↑" action Return(value=front1) xcenter .2 ycenter .7
textbutton "→" action Return(value=turnright) xcenter .3 ycenter .8
textbutton "↓" action Return(value=turnback) xcenter .2 ycenter .9
textbutton "←" action Return(value=turnleft) xcenter .1 ycenter .8
add Minimap(here).sm
style move_button_text:
size 60
# Assign background images.
# "left0" means a wall on the lefthand, "front2" means a further wall on the front, and so on.
# left2, front2, right2
# left1, front1, right1
# left0, here , right0
image floor = "floor.png"
image left0 = "left0.png"
image right0 = Transform("left0.png", xzoom=-1)
image front1 ="front1.png"
image left1 = "left1.png"
image right1 = Transform("left1.png", xzoom=-1)
image front2 = "front2.png"
image left2 = "left2.png"
image right2 = Transform("left2.png", xzoom=-1)
label dungeon:
# To start exploring, call or jump to this label
# To exit, create an event which has return or jump statement.
while True:
# Calculate relative coordinates
python:
turnright=Coordinate(here.stage, here.y,here.x, here.dx,-here.dy)
turnleft=Coordinate(here.stage, here.y, here.x, -here.dx,here.dy)
turnback=Coordinate(here.stage, here.y,here.x, -here.dy,-here.dx)
right0=Coordinate(here.stage, here.y+here.dx,here.x-here.dy, here.dy,here.dx)
left0=Coordinate(here.stage, here.y-here.dx,here.x+here.dy, here.dy,here.dx)
front1=Coordinate(here.stage, here.y+here.dy,here.x+here.dx, here.dy,here.dx)
right1=Coordinate(here.stage, front1.y+front1.dx,front1.x-front1.dy, here.dy,here.dx)
left1=Coordinate(here.stage, front1.y-front1.dx,front1.x+front1.dy, here.dy,here.dx)
front2=Coordinate(here.stage, front1.y+front1.dy,front1.x+front1.dx, here.dy,here.dx)
right2=Coordinate(here.stage, front2.y+front2.dx,front2.x-front2.dy, here.dy,here.dx)
left2=Coordinate(here.stage, front2.y-front2.dx,front2.x+front2.dy, here.dy,here.dx)
# Composite background images. Try-except clauses are used to prevent the List Out of Index Error
scene
show floor
python:
for i in ["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]:
try:
j=globals()[i]
if j.stage.map[j.y][j.x]=="1":
renpy.show(i)
except:
pass
# Record maps
python:
for i in [left1, right1, front1, left0, right0, here]:
here.stage.mapped[i.y][i.x]==1
# Check events. If it happens, call a label or jump out to a label.
if here.stage.enemy is not None and renpy.random.random()< .2:
call battle(player=hero, enemy=here.stage.enemy)
if here.stage.map[here.y][here.x]=="a":
jump exit
# Otherwise, call the move screen
$ renpy.block_rollback()
call screen move
$ here=_return
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
Problem solved, I had to change 'is not' for '!='
Yet I have other question, how do I define different images for the walls? I want to make different dungeons bit the script la a bit limiting.
Yet I have other question, how do I define different images for the walls? I want to make different dungeons bit the script la a bit limiting.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- hell_oh_world
- Miko-Class Veteran
- Posts: 777
- Joined: Fri Jul 12, 2019 5:21 am
- Contact:
Re: Dungeon engine problem
if this is the list of names for the images/etc...
then you can pre-define presets of this...
Be mindful that you need to also set the images for each preset that you make.
Then before you call the dungeon label you just need to set the dungeon_preset variable to whatever preset you like.
Code: Select all
["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]
Code: Select all
# presets
define ancient = ("ancient_left2", ...)
define modern = ("modern_left2", ...)
default dungeon_preset = ancient # ancient by default
Code: Select all
...
# Composite background images. Try-except clauses are used to prevent the List Out of Index Error
scene
show floor
python:
for i in dungeon_preset:
...
Then before you call the dungeon label you just need to set the dungeon_preset variable to whatever preset you like.
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
Amazing, it worked. Thank you so much.hell_oh_world wrote: ↑Fri Oct 15, 2021 9:20 pm if this is the list of names for the images/etc...then you can pre-define presets of this...Code: Select all
["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]
Code: Select all
# presets define ancient = ("ancient_left2", ...) define modern = ("modern_left2", ...) default dungeon_preset = ancient # ancient by default
Be mindful that you need to also set the images for each preset that you make.Code: Select all
... # Composite background images. Try-except clauses are used to prevent the List Out of Index Error scene show floor python: for i in dungeon_preset: ...
Then before you call the dungeon label you just need to set the dungeon_preset variable to whatever preset you like.
With yet another obstacle appeared, I want to make random encounters from with different monsters I'm making: bats, trolls, mimics, etc, some in the same floor and even two or more and the same time.
Code: Select all
python:
# Create skills (name, type, hit, power)
attack = Skill("Attack", "attack", 70, 20)
escape = Skill("Escape", "escape")
# Create battle actors (name, max_hp, skills)
hero = Actor("Hero",100, [attack,escape])
goblin = Actor("Goblin",40, [attack])
bat = Actor("Bat",20, [attack])
# Create a dungeon stage (map,enemy)
# "1" means wall, "0" means path.
stage1=Stage([
"1111111111",
"1111011001",
"1000000001",
"1110111101",
"1000000001",
"1111111111",
],
enemy=goblin,bat)
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- hell_oh_world
- Miko-Class Veteran
- Posts: 777
- Joined: Fri Jul 12, 2019 5:21 am
- Contact:
Re: Dungeon engine problem
Looking at the framework you're using, I think there's still a lot of things needed to be modified if you want multiple enemies to spawn at the same stage.
The former, however, can be done I guess.
The former, however, can be done I guess.
Code: Select all
python:
# Create skills (name, type, hit, power)
attack = Skill("Attack", "attack", 70, 20)
escape = Skill("Escape", "escape")
# Create battle actors (name, max_hp, skills)
hero = Actor("Hero",100, [attack,escape])
goblin = Actor("Goblin",40, [attack])
bat = Actor("Bat",20, [attack])
enemies = (goblin, bat)
enemy = renpy.random.choice(enemies) # choose a random enemy from the enemies
# Create a dungeon stage (map,enemy)
# "1" means wall, "0" means path.
stage1=Stage([
"1111111111",
"1111011001",
"1000000001",
"1110111101",
"1000000001",
"1111111111",
],
enemy=enemy)
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
It does, but I have to restart the game to alternate between monsters. Also, there's no problem with 1 monster at a time. Thank you so much for your help.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
Nyaatrap also designed other dungeon script that I consider better but have a few issues with it. This is the code:
I debugged the best I could, ando also it requires adventure.rpy, tilemap.rpy, adventure_tilemap.rpy (you can find the files in github).
My issues are that every time I start the test game, it takes a long time to work like, 3-5 seconds instead of the inmediate response of the previous dungeon engine. Is there a way to fix it or is a problem with the render an my computer?
The next one is that the image becomes opaque when the crawling begins, again, I don't know if its a problem with the engine, the way I debugged or my computer.
And finally, is there a way in the script to make the minimap to be drawn as you explore the dungeon instead of being complete from the beginning? I want to make a lot of hidden rooms in the map and it kinda ruins it if the player can see the whole map.
Also, the FULL CAPITALS in the 'Screen automove' part has a function? I didn't know what to do with it so I blocked it.
Code: Select all
## This file adds pseudo-3D dungeon crawling function into adventurer framework.
## This framework requires adventure.rpy, tilemap.rpy, and adventure_tilemap.rpy.
## To play the sample game, download the dungeon folder then place it in the game directory.
## To show sample dungeon correctly, set screen size 800x600.
#This file adds a pseudo 3D dungeon search function to # # adventure.
#You will need # # adventure.rpy, tilemap.rpy, adventure_tilemap.rpy.
# # To run the sample, you need to download the image in the dungeon folder and place it in the game directory.
# # The screen size must be set to 800x600 for the sample to display correctly.
# ################################################# ############################
## How to Use
# ################################################# ############################
# # First of all, define the image type with a list of strings.
# # The string will be the prefix of the image file to display
# # The first part of the list is the background image that is displayed in common on all layers.
define dungeonset = ["base", "floor", "wall", "door"]
# # Next, define the number and overlap of images such as walls to be displayed in the dungeon as a two-dimensional array.
# # In this example, 9 images can be displayed farthest and 3 images can be displayed closest.
#Think of # # c0 as the position where the player is, and imagine that the top is at the back of the screen.
# # The string will be the suffix of the image file to be displayed
define dungeon_layers = [
["llll6", "lll6", "ll6", "l6", "c6", "r6", "rr6", "rrr6", "rrrr6"],
["lll5", "ll5", "l5", "c5", "r5", "rr5", "rrr5"],
["lll4", "ll4", "l4", "c4", "r4", "rr4", "rrr4"],
["ll3", "l3", "c3", "r3", "rr3"],
["ll2", "l2", "c2", "r2", "rr2"],
["l1","c1","r1"],
["l0","c0","r0"],
]
# # Prepare an image created by combining the prefix and suffix defined above.
## "floor_lll6.png" など
# # Only the background image should have the same name as the first in the list defined at the beginning.
## "base.png"
# # Express the map you want to draw as a two-dimensional array of integers.
# # The value of the array is the index of the list defined above
# # dungeonset = ["dungeonbase", "floor", ...] 1 represents the floor.
#In the case of # # 0 or the empty set, it will not be drawn and the background image behind it will be visible.
define map2 =[
[ 2 , 3 , 2 , 2 , 2 , 2 , 2 , 2 ],
[ 2 , 1 , 2 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 3 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 2 , 1 , 1 , 2 , 1 , 2 ],
[ 2 , 1 , 0 , 0 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 1 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 0 , 1 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 1 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 1 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 0 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 0 , 1 , 2 , 2 , 3 , 2 ],
[ 2 , 1 , 0 , 1 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 0 , 0 , 1 , 1 , 1 , 2 ],
[ 2 , 1 , 1 , 1 , 1 , 2 , 1 , 2 ],
[ 2 , 0 , 1 , 2 , 2 , 2 , 1 , 2 ],
[ 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ]
]
# # Make a list of tile types that cannot be penetrated.
define collision = (0, 2, 3)
# # Define the dungeon image with LayeredMap (map, tileset, layers, imagefolder, filetype, mirror).
# # map, tileset, layers are defined above, imagefolder is the path with the image and filetype is the extension.
# # When mirror is set to "left" or "right", the image on the specified side is drawn by inverting the image on the opposite side.
define dungeon_image = LayeredMap(map2, dungeonset, layers=dungeon_layers, imagefolder="dungeon", mirror = "left")
# # In addition, prepare a tile map to be used as a minimap.
define mm_tileset = [Solid("#000", xysize=(16,16)), Solid("#633", xysize=(16,16)), Solid("#ea3", xysize=(16,16)), Solid("#f33", xysize=(16,16))]
define minimap = Tilemap(map2, mm_tileset, 16)
# # Use them to define levels in Dungeon (image, music, tilemap, collision).
define level.dungeon = Dungeon(image=dungeon_image, tilemap = minimap, collision=collision)
# # Finally, define the adventurer with the DungeonPlayer class.
# # The dungeon pos is a set of (x, y, dx, dy), and if dx is 1, it turns to fold, and if dx is -1, it turns to fold.
#Although it is an alias to avoid duplication of definition with # # adventure.rpy,
# # After the game starts, return to player and use it.
default dungeonplayer = DungeonPlayer("dungeon", pos=(1,1,0,1), turn=0, icon = Text("P"))
# # Defines a dungeon event.
# # Events have different conditions depending on the trigger.
# # The default trigger = "move" is called when you move over that coordinate.
# # "movefrom" --When you move away from that coordinate
# # "nextto"-when moving next to that coordinate
# # "moveto"-When trying to move to an intrusive tile
# # "faceto" --When you move or turn around and catch the tile one step ahead of you
# # "click" --When you click on the tile
# # "clickto" --When you click one step before the tile
# # "stay"-When you are on the tile regardless of state. This is also determined before action.
# # Passive events are triggered when the player moves up, down, left, or right on the pos.
define ev.entrance = Event("dungeon", pos=(1,1), trigger="stay", once=True)
label entrance:
"Enter point"
return
#Events without # # pos will occur anywhere within that level.
define ev.nothing = Event("dungeon", trigger = "click", priority=100)
label nothing:
"There is nothing"
return
#If # # pos is an integer or string that matches the value of the binary array that defined the map, that event will be fired.
define ev.collision_wall = Event("dungeon", pos=2, trigger="moveto")
label collision_wall:
with vpunch
return
define ev.collision_pit = Event("dungeon", pos=0, trigger="moveto")
label collision_pit :
"There is a pit"
return
# # player.next_pos contains the coordinates to move to next.
#You can get the corresponding coordinates with # # player.front_pos, back_pos, left_pos, right_pos.
# # player.front2_pos and back2_pos are the coordinates of 2 steps before and 2 steps behind.
# # If you give (x, y) coordinates to the return value, it will move to that location.
define ev.collision_door = Event("dungeon", pos=3, trigger="moveto")
label collision_door :
if player.front_pos == player.next_pos:
scene black with dissolve
return player.front2_pos
else:
with vpunch
return
# # If you give the return value a character string and the coordinates of the destination, it will move to that level.
define ev.exit = Event("dungeon", pos=(1,0), trigger="moveto", priority = -1)
label exit:
menu:
"Do you exit?"
"yes":
scene black with dissolve
"You exited"
return "dungeon", (1, 1, 0, 1)
"no":
pass
return
# # If you give an image to the event, you can display it on the tile of the dungeon.
image sprite_icon = Solid("#4a3", xysize=(16,16))
image sprite_image = "dungeon/sprite.png"
define ev.sprite = Event("dungeon", pos=(1, 8), trigger = "click", icon = "sprite_icon", image="sprite_image")
label sprite:
"Hello"
return
# # The display position of the image is adjusted by giving the following properties to LayeredMap.
# # horizon_height is the eye level height and 1.0 is the bottom edge of the screen.
# # tile_length is the width of the tile where the player is standing, and 1.0 is the right edge of the screen.
# # first_distance is the distance to the object on the same tile as the viewpoint
# # The larger the value, the harder it is to reduce the size of the next and subsequent objects.
# # Shading is a color that overlays a distant image, usually giving it black ("# 000").
#Jumping from the # # start label to adventure_dungeon will start the search.
# ################################################# ############################
## Definitions
# ################################################# ############################
# ################################################# ############################
## Main label
## Jumping to this label starts dungeon adventure
label adventure_dungeon:
# rename back
$ player = dungeonplayer
# Update event list in the current level
$ player.update_events()
$ player.action = "stay"
$ player.update_dungeonmap()
$ player.automove=False
# Play music
if player.music:
if renpy.music.get_playing() != player.music:
play music player.music fadeout 1.0
# Show background
if player.image:
scene black with Dissolve(.25)
scene expression player.image
with Dissolve(.25)
jump adventure_dungeon_loop
label adventure_dungeon_loop:
while True:
# check passive events
$ block()
$ _events = player.get_events(player.pos, player.action)
# sub loop to execute all passive events
$ _loop = 0
while _loop < len(_events):
$ player.event = _events[_loop]
$ block()
$ player.happened_events.add(player.event.name)
call expression player.event.label or player.event.name
$ player.done_events.add(player.event.name)
if player.move_pos(_return):
jump adventure_dungeon
$ _loop += 1
$ block()
# show eventmap or dungeon navigator
if player.in_dungeon():
call screen dungeon_navigator(player)
else:
call screen eventmap_navigator(player)
if isinstance(_return, basestring):
$ player.action = _return
elif isinstance(_return, tuple):
if player.get_tile(pos = _return, numeric=True) in player.collision:
$ player.action = "collide"
$ player.next_pos = _return
$ player.automove = False
elif player.compare(_return):
$ player.action = "rotate"
$ player.move_pos(_return)
else:
$ player.action = "move"
$ player.move_pos(_return)
# Show background
if player.image:
$ player.update_dungeonmap()
scene expression player.image
else:
$ player.action = "move"
$ player.move_pos(_return)
# ################################################# ############################
## Dungeon navigator screen
## screen that shows orientation buttons in dungeon
screen dungeon_navigator(player):
on "show" action Show("automove_screen", player=player)
# When outside of navigation is clicked
button:
xysize (config.screen_width, config.screen_height)
action Return("click")
# move buttons
fixed fit_first True style_prefix "move" align 0.02, 0.97:
grid 3 3 :
textbutton "Q" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "turnleft_pos"), Return(player.turnleft_pos)]
textbutton "W" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "front_pos"), Return(player.front_pos)]
textbutton "E" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "turnright_pos"), Return(player.turnright_pos)]
textbutton "A" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "left_pos"), Return(player.left_pos)]
textbutton "S" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "back_pos"), Return(player.back_pos)]
textbutton "D" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "right_pos"), Return(player.right_pos)]
null
textbutton "X" action NullAction() alternate [SetField(player, "automove", True), SetField(player, "move_dir", "turnback_pos"),Return(player.turnback_pos)]
null
# move keys
for i in ["repeat_2","2", "toggle_skip"]:
key i action [SetField(player, "automove", True), SetField(player, "move_dir", "front_pos"), Return(player.front_pos)]
for i in ["repeat_w", "w","focus_up"]:
key i action Return(player.front_pos)
for i in ["repeat_s", "s","focus_down"]:
key i action Return(player.back_pos)
for i in ["repeat_d","d", "rollforward"]:
key i action Return(player.right_pos)
for i in ["repeat_a","a", "rollback"]:
key i action Return(player.left_pos)
for i in ["repeat_q", "q", "focus_left"]:
key i action Return(player.turnleft_pos)
for i in ["repeat_e", "e", "focus_right"]:
key i action Return(player.turnright_pos)
for i in ["repeat_x", "x",]:
key i action Return(player.turnback_pos)
# override rollforward/rollback
key 'mousedown_4' action Return(player.turnleft_pos)
key 'mousedown_5' action Return(player.turnright_pos)
# add minimap
add player.minimap(area=(270,270)) align 0.98, 0.97
style move_button_text:
size 60
style move_button:
xysize (60, 60)
init 2 python :
config.keymap['button_alternate'].append('mousedown_1')
config.keymap['button_ignore'].remove('mousedown_1')
# ################################################# ############################
## Automove screen
## this screen delays key clicks on dungeon navigator, and returns front_pos
screen automove_screen(player):
#without 1000----------(THIS PART OVER HERE)
if player.automove:
if player.automove == True:
timer 0.25 action [SetField(player, "automove", "fast"), Hide("automove_screen"), Return(getattr(player, player.move_dir))]
else:
timer 0.025 action [Hide("automove_screen"), Return(getattr(player, player.move_dir))]
else:
timer 0.025 action Hide("automove_screen")
for i in ["repeat_w", "w","repeat_W","W", "focus_up"]:
key i action [SetField(player, "automove", False)]
for i in ["repeat_s", "s","repeat_S","S", "focus_down"]:
key i action [SetField(player, "automove", False)]
for i in ["repeat_d","d", "repeat_D","D", "rollforward"]:
key i action [SetField(player, "automove", False)]
for i in ["repeat_a","a", "repeat_A","A", "rollback"]:
key i action [SetField(player, "automove", False)]
for i in ["repeat_q", "q","repeat_Q","Q", "focus_left"]:
key i action [SetField(player, "automove", False)]
for i in ["repeat_e", "e","repeat_E","E", "focus_right"]:
key i action [SetField(player, "automove", False)]
for i in ["mouseup_1", "dismiss", "game_menu", "hide_windows", "skip", "toggle_skip","stop_skipping"]:
key i action [SetField(player, "automove", False)]
init python:
# when dialogue is shown, disable auto move
def disable_automove(event, interact=True, **kwargs):
if interact and event == "begin":
if getattr(store, "player", None) and isinstance(player, DungeonPlayer):
player.automove = False
config.all_character_callbacks.append(disable_automove)
# ################################################# ############################
## Dungeon class.
init -5 python:
class Dungeon(TiledLevel):
"""
Expanded Level class that holds collision.
"""
def __init__(self, image=None, music=None, tilemap=None, collision=None):
super(Dungeon, self).__init__(image, music, tilemap)
self.collision = collision
# ################################################# ############################
## DungeonPlayer class
class DungeonPlayer ( TilemapPlayer ):
"""
Expanded Player Class that stores various methods and data for dungeon crawling.
"""
def __init__(self, level=None, pos=None, icon=None, mask_tilemap=False, **kwargs):
super(DungeonPlayer, self).__init__(level, pos, icon, mask_tilemap, **kwargs)
self.next_pos = self.pos
self.automove = False
self.move_dir =None
@property
def collision(self):
return self.get_level(self.level).collision
@property
def turnback_pos(self):
return Coordinate(*self.pos).turnback().unpack()
@property
def turnleft_pos(self):
return Coordinate(*self.pos).turnleft().unpack()
@property
def turnright_pos(self):
return Coordinate(*self.pos).turnright().unpack()
@property
def front_pos ( self ):
return Coordinate(*self.pos).front().unpack()
@property
def front2_pos ( self ):
return Coordinate(*self.pos).front2().unpack()
@property
def back_pos(self):
return Coordinate(*self.pos).back().unpack()
@property
def back2_pos(self):
return Coordinate(*self.pos).back2().unpack()
@property
def left_pos(self):
return Coordinate(*self.pos).left().unpack()
@property
def right_pos(self):
return Coordinate(*self.pos).right().unpack()
def compare(self, target):
# compares self coordinate to target.
if isinstance(self.pos, tuple):
return Coordinate(*self.pos).compare(target)
return False
def in_dungeon(self):
# returns true if player is in dungeon
return isinstance(self.get_level(self.level), Dungeon)
def get_events(self, pos = None, action= None):
# returns event list that happens in the given pos.
pos = pos or self.pos
actions = ["stay"]
if action == "click":
actions += ["click", "clickto"]
if action == "move":
actions += ["move", "movefrom", "nextto", "faceto"]
if action == "rotate":
actions += ["faceto"]
if action == "collide":
actions += ["moveto"]
if self.in_dungeon() and action:
loop = [player.pos, player.front_pos, player.back_pos, player.left_pos, player.right_pos]
else:
loop = [pos]
events = []
for i in self.current_events:
for pos in loop:
if i.once and self.happened(i):
continue
if action and i.trigger not in actions:
continue
if action == None or i.pos == None or i.pos == pos:
if eval(i.cond):
events.append(i)
break
elif self.in_dungeon() and (i.pos == self.get_tile(pos=pos) or Coordinate(*pos).compare(i.pos)):
if i.trigger in ("clickto", "faceto") and not Coordinate(*player.front_pos).compare(pos):
continue
elif i.trigger == "movefrom" and not Coordinate(*player.previous_pos).compare(pos):
continue
elif i.trigger == "nextto" and Coordinate(*player.pos).compare(pos):
continue
elif i.trigger == "moveto" and not Coordinate(*player.next_pos).compare(pos):
continue
elif i.trigger in ("stay", "click", "move") and not Coordinate(*player.pos).compare(pos):
continue
elif eval(i.cond):
events.append(i)
break
if action:
return self.cut_events(events)
else:
return events
def add_dungeon_objects(self):
# for updating dungeon image
if not self.in_dungeon():
return
objects = []
for i in self.get_events():
if i.image and i.pos:
objects.append((i.pos, i.image))
self.image.objects = objects
def add_dungeon_replaced_tiles(self):
# for updating dungeon image
if not self.in_dungeon():
return
if self.level in self.replaced_tiles.keys():
self.image.replaced_tiles = self.replaced_tiles[self.level]
def update_dungeonmap(self):
# update LayeredMap and Tilemap.
# it should be called every time before dungeon image is shown.
self.image.pov=self.pos
self.add_dungeon_objects()
self.add_dungeon_replaced_tiles()
self .update_tilemap ()
def minimap(self, level=None, pos=None, area=None):
# it returns tilemap displayable around player.
level = level or self.level
pos = pos or self.pos
tm = self.get_level(level).tilemap
w = self.tilemap.tile_width
h = self.tilemap.tile_height
xpos = (area[0]-w)/2
ypos = ( area [ 1 ] - h) / 2
tm.area=(pos[0]*w - xpos, pos[1]*h - ypos, area[0], area[1])
return tm
# ################################################# ############################
## Coordinate class
init -10 python:
class Coordinate(object):
"""
A class that calculates coordinates.
"""
def __init__(self, x=0, y=0, dx=0, dy=0):
self.x=x
self .y = y
self.dx=dx
self .dy = dy
def turnback(self):
return Coordinate(self.x, self.y, -self.dx, -self.dy)
def turnleft(self):
return Coordinate(self.x, self.y, self.dy, -self.dx)
def turnright(self):
return Coordinate(self.x, self.y, -self.dy, self.dx)
def front ( self ):
return Coordinate(self.x+self.dx, self.y+self.dy, self.dx, self.dy)
def front2 ( self ):
return self.front().front()
def back(self):
return Coordinate(self.x-self.dx, self.y-self.dy, self.dx, self.dy)
def back2(self):
return self.back().back()
def left(self):
return Coordinate(self.x+self.dy, self.y-self.dx, self.dx, self.dy)
def right(self):
return Coordinate(self.x-self.dy, self.y+self.dx, self.dx, self.dy)
def moveright(self):
return Coordinate(self.x+1, self.y, 1, 0)
def moveleft(self):
return Coordinate(self.x-1, self.y, -1, 0)
def movebottom(self):
return Coordinate(self.x, self.y+1, 0, 1)
def movetop(self):
return Coordinate(self.x, self.y-1, 0, -1)
def moveto ( self , x , y ):
return Coordinate(self.x+x, self.y+y, self.dx, self.dy)
def unpack(self):
return (self.x, self.y, self.dx, self.dy)
def compare(self, target):
# Returns True if current coord and target coord shares x and y.
if isinstance(target, tuple):
if self.x==target[0] and self.y==target[1]:
return True
if isinstance(target, Coordinate):
if self.x==target.x and self.y==target.y:
return True
return False
# ################################################# ############################
## LayeredMap class
class LayeredMap(renpy.Displayable):
"""
This creates a displayable by layering other displayables. It has the following field values.
map - A 2-dimensional list of strings that represent index of a tileset.
tileset - A list of image prefix that is used as a tile of tilemap.
layers - 2-dimensional list of strings to be shown in the perspective view. The first list is farthest layers,
from left to right. the last list is the nearest layers. this string is used as suffix of displayable.
imagefolder - folder path that contains llayer images
filetype - file extention of tha image
mirror - if "left" or "right", it uses flipped images from the other side.
substitution - if {index:("condition", "image prefix")} is given, and condision is satisfied, tileset[index] uses "image_prefix".
if {index:(None, "image prefix")} is given, and image is not found, tileset[index] uses "image_prefix".
horizon_height - height of horizon relative to screen.
tile_length - length of the nearest tile relative to screen.
first_distance - distance to the first object. this determines focal length.
shading - if color is given, it will blend the color over sprites.
object_offset - xyoffset added on every objects.
preload - if True, default, it loads all textures before it's shown.
"""
def __init__(self, map, tileset, layers = None, imagefolder = "", filetype = "png", mirror=None, substitution = None,
horizon_height = 0.5, tile_length = 1.0, first_distance = 1.0, shading = None, object_offset = (0,0), preload=True,
**properties):
super(LayeredMap, self).__init__(**properties)
self.map = map
self.tileset = tileset
self.layers = layers
self.imagefolder = imagefolder
self.filetype = filetype
self.mirror = mirror
self.substitution = substitution
self.horizon_height = horizon_height
self.tile_length = tile_length
self.first_distance = first_distance
self.shading = shading
self.pov = (0,0,0,0)
self.objects = []
self.object_offset = object_offset
self.replaced_tiles = {}
self.width = 0
self.height = 0
self.images = []
if preload:
for prefix in self .tileset:
for i in self.layers:
for surfix in i:
im_name = "{}/{}_{}.{}".format(self.imagefolder, prefix, surfix, self.filetype)
if renpy.loadable(im_name):
self.images.append(Image(im_name))
def render(self, width, height, st, at):
import re
pattern = "[0-9]+"
# render background
child_render = renpy.render(Image("{}/{}.{}".format(self.imagefolder, self.tileset[0], self.filetype)), width, height, st, at)
self.width, self.height = child_render.get_size()
render = renpy.Render(self.width, self.height)
render.blit(child_render, (0,0))
# depth loop
depth = len(self.layers)
for d in range(depth):
coord = Coordinate(*self.pov)
for i in xrange(depth-1-d):
coord = coord.front()
# breadth loop
breadth = len(self.layers[d])
center = breadth//2
wrange = range(breadth-center)
for k in xrange(center):
wrange.insert( 0 , k + center + 1 )
for b in wrange:
coord2 = coord
if b > center:
for j in xrange(b-center):
coord2 = coord2.right()
else:
for j in xrange(center-b):
coord2 = coord2.left()
# Get index of tileset
x, y = coord2.x, coord2.y
try:
tile = self .map [y] [x]
except IndexError:
tile = 0
# if tile is replaced
if self.replaced_tiles:
if (x, y) in self.replaced_tiles.keys():
tile = self.replaced_tiles[(x,y)]
# change value to integer
if not tile:
tile = 0
elif isinstance(tile, basestring):
tile = int(re.findall(pattern, tile)[0])
# blit image if tile is not None
if tile:
if self.mirror == "left" and b<center:
surfix = self.layers[d][breadth-b-1]
flip=True
elif self.mirror == "right" and b>center:
surfix = self.layers[d][breadth-b-1]
flip=True
else:
surfix=self.layers[d][b]
flip=False
prefix = self.tileset[tile]
if self.substitution and tile in self.substitution.keys():
if self.substitution[tile][0] is None and renpy.loadable("{}/{}_{}.{}".format(self.imagefolder, prefix, surfix, self.filetype)):
prefix = self.substitution[tile][1]
if eval(self.substitution[tile][0]):
prefix = self.substitution[tile][1]
im_name = "{}/{}_{}.{}".format(self.imagefolder, prefix, surfix, self.filetype)
image = Image(im_name) if renpy.loadable(im_name) else Null()
if flip:
image = Transform(image, xzoom=-1)
render.blit(renpy.render(image, self.width, self.height, st, at), (0,0))
# blit image over tile
if self.objects:
for pos, im in self.objects:
try:
if pos == self.map[y][x] or isinstance(pos, tuple) and (pos[0], pos[1]) == (x,y):
zoom = (self.first_distance)/(depth-d+self.first_distance - 1.0)
if b<center:
xpos = 0.5 - self.tile_length*zoom*(center-b)
else:
xpos = 0.5 + self.tile_length*zoom*(b-center)
im = renpy.displayable(im)
im = Fixed(
Transform(im, zoom=zoom, anchor=(0.5, 0.5), xpos=xpos, ypos=self.horizon_height),
)
if self.shading:
overlay = Solid(self.shading)
im = AlphaBlend(Transform(im, alpha = float(depth-1-d)/float(depth-1)), im, overlay)
render.blit(renpy.render(im, self.width, self.height, st, at), self.object_offset)
except IndexError:
pass
# Redraw regularly
# renpy.redraw(self, 1.0/30)
return render
def per_interact(self):
# Redraw per interact.
renpy.redraw(self, 0)
def visit(self):
# If the displayable has child displayables, this method should be overridden to return a list of those displayables.
return self.images
My issues are that every time I start the test game, it takes a long time to work like, 3-5 seconds instead of the inmediate response of the previous dungeon engine. Is there a way to fix it or is a problem with the render an my computer?
The next one is that the image becomes opaque when the crawling begins, again, I don't know if its a problem with the engine, the way I debugged or my computer.
And finally, is there a way in the script to make the minimap to be drawn as you explore the dungeon instead of being complete from the beginning? I want to make a lot of hidden rooms in the map and it kinda ruins it if the player can see the whole map.
Also, the FULL CAPITALS in the 'Screen automove' part has a function? I didn't know what to do with it so I blocked it.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
- KebecMaslow
- Regular
- Posts: 39
- Joined: Mon Feb 28, 2011 12:28 pm
- Projects: Here's Evil
- Organization: Grupo Tricksters
- Location: At my pc
- Contact:
Re: Dungeon engine problem
Hello again, still can't figure how to make the minimap to show only the zones the player visited, neither can figure why the dungeon gets darker.
These are the rest of the code needed to use the dungeon.
There's also the option of hiding the minimap, but that will make navigation a problem.
These are the rest of the code needed to use the dungeon.
Code: Select all
## This file provides adventure game framework that uses event maps.
# # A file that adds an adventure game framework for exploring a 2D map with events.
By managing the # # label as a pair with the object, you can place it as an event on the map and call it.
# ################################################# ############################
## How to Use
# ################################################# ############################
# # First of all, define the level (map / stage) where the event is placed by Level (image, music).
# # image is the background displayed when you are at that level, and music is the music that plays.
It can also be defined in the # # level namespace.
define level.east = Level(image=Solid("#6a6"))
define level.west = Level(image=Solid("#339"))
# # Define the place where the event will occur next with place (level, pos, cond, icon).
# # level is the level at which the location will be placed, giving the level defined above as a string.
# # pos will be the coordinates relative to the monitor at that location. (0.5, 0.5) is the center of the screen.
When # # cond is satisfied, the icon will appear on the background of the level and clicking on it will take you to that location.
It can also be defined in the # # place namespace.
define place.home = Place(level="east", pos=(.8,.5), icon=Text("home"))
define place.e_station = Place(level="east", pos=(.6,.7), icon=Text("east-station"))
define place.w_station = Place(level="west", pos=(.4,.4), icon=Text("west-station"))
define place.shop = Place(level="west", pos=(.2,.5), icon=Text("shop"))
# # Then Player (place) the explorer who holds the current position, achievement event, etc.
Defined by default in # # or Player (level, pos)
# # place is the starting point. You can also use level and pos.
# # If you add an arbitrary parameter, you can use it as a condition to call each event.
default player = Player("home", turn=0)
# # Each event is defined by a pair of define and label.
# # define Label name = Event (place, cond, priority, once, multi) or
# # define Label name = Define with Event (level, pos, cond, priority, once, multi).
# # When the explorer moves to the location of place, it calls the label with the same name as the event.
Given # # cond, the event will only be executed if the conditional expression is met.
# # priority is the priority of occurrence, and is executed in ascending order of numbers. The default is 0.
If # # once is set to True, it will only be executed once. By default it runs many times.
If # # multi is set to True, other events will occur at the same time.
It can also be defined in the # # event and ev namespaces.
define ev.myhome = Event("home")
label myhome:
"this is my home"
Press # # return to return to the search screen
return
define ev.e_station = Event("e_station")
_Estation label :
After # # return, you can specify the level and location to move to next with a character string.
return "w_station"
define ev.w_station = Event("w_station")
label w_station:
return "e_station"
define ev.shop = Event("shop")
label shop :
"this is a shop"
return
You can evaluate if the event was called in the past with # # player.happened (ev).
# # player.done (ev) can be used to evaluate if the event has run to the end in the past.
define place.shop2 = Place(level="west", pos=(.1,.1), cond="player.done(ev.shop)", icon=Text("hidden shop"))
If you give # # icon, an additional image will be displayed above that location on the event map.
define ev.shop2 = Event("shop2", cond="player.done(ev.shop)", icon=Text("(Now on sale)", size=20, yoffset=30))
label shop2:
"this is a hidden shop."
return
If you give only the # # level, the event will fire at any coordinate.
If you set # # trigger to "click", you will see the event when you click on a place where there is no place to move on the map.
# # Also, if you define a label, that label name will be called instead of the event name.
define ev.west_nothing = Event("west", trigger = "click", priority=99)
define ev.east_nothing = Event("east", trigger = "click", priority=99, label="west_nothing")
label west_nothing:
"There is nothing there"
return
If # # trigger is set to "stay", the event will be confirmed every time the move screen appears.
# # This label is called just before each turn to keep track of the turn.
define ev.turn = Event(priority = 999, trigger = "stay", multi=True)
tag turn :
$ player.turn += 1
return
If you add a button to # # eventmap_screen that returns a string, you can use that string as a trigger.
# # start Jumping from the label to adventure will start the search.
# ################################################# ############################
## Definitions
# ################################################# ############################
# ################################################# ############################
## Main label
## Jumping to this label starts exploration
tag adventure :
# Update event list in the current level
$ player.update_events()
$ player.action = "stay"
# Play music
if player.music:
if renpy.music.get_playing() != player.music:
play music player.music fadeout 1.0
# Show background
if player.image:
scene black with Dissolve(.25)
show expression player.image
with Dissolve(.25)
jump adventure_loop
tag adventure_loop :
while True:
# returns event list events
$ block()
$ _events = player.get_events(player.pos, player.action)
# sub loop to execute events
$ _loop = 0
while _loop < len(_events):
$ player.event = _events[_loop]
$ block()
$ player.happened_events.add(player.event.name)
call expression player.event.label or player.event.name
$ player.done_events.add(player.event.name)
if player.move_pos(_return):
jump adventure
$ _loop += 1
$ block()
# show eventmap navigator
call screen eventmap_navigator(player)
if isinstance(_return, basestring):
$ player.action = _return
else:
$ player.action = "move"
$ player.move_pos(_return)
label after_load():
# Update event list in the current level
$ player.update_events()
return
init python:
def block():
# blocks rollback then allows saving data in the current interaction
config.skipping = None
renpy.checkpoint()
renpy.block_rollback()
renpy.retain_after_load()
# ################################################# ############################
## Eventmap navigator screen
## screen that shows events and places over the current level
screen eventmap_navigator(player):
# When outside of places is clicked.
button:
xysize (config.screen_width, config.screen_height)
action Return("click")
## show places
for i in player.get_places():
button:
pos i.pos
action Return(i)
if i.icon:
add i.icon
## show event icons
for i in player.get_events():
button:
if i.icon:
pos i.pos
add i.icon
# ################################################# ############################
## Level class
heat - 10 python :
class Level(object):
"""
Class that represents level that places events on it. It has following fields:
image - Image that is shown behind events.
music - Music that is played while player is in this level.
"""
def __init__(self, image=None, music=None):
self.image = image
self.music = music
# ################################################# ############################
## Place class
class Place(object):
"""
Class that places events on a level.
This class's fields are same to event class
"""
def __init__(self, level=None, pos=None, cond="True", priority=0, icon=None):
self.level = level
self.pos = pos
self.cond = cond
self.priority = int(priority)
self.icon = icon
self.once = False
self.multi = False
# ################################################# ############################
## Event class
class Event(object):
"""
Class that represents events that is places on level or place. It has the following fields:
level - String of level where this events placed onto.
pos - (x, y) coordinate on the screen.
cond - Condition that evaluates this event happen or not. This should be quoted expression.
If self.call is None, this determines to show its icon.
priority - An event with higher value happens earlier. default is 0.
once - Set this true prevents calling this event second time.
multi - Set this true don't prevent other events in the same interaction.
trigger - Determine when to evaluate this event happen.
"move", default, is evaluated after moved to this pos,
"moveto" is evaluated when trying to move this pos, and returns to the previous pos.
"stay" is evaluated every time before screen is event map screen is shown.
None never call this linked event. It's used to show only icon.
icon - icon that is shown on an eventmap.
image - it's reserved for later use.
label - If it's given this label is called instead of object name.
"""
def __init__(self, level=None, pos=None, cond="True", priority=0, once=False, multi=False,
trigger="move", icon=None, image=None, label=None):
self.place = level if Player.get_place(level) else None
self._level = None if self.place else level
self._pos = None if self.place else pos
self.cond = cond
self.priority = int(priority)
self.once = once
self.multi = multi
self.trigger = trigger
self.icon = icon
self.image = image
self.label = label
@property
def level ( self )
return Player.get_place(self.place).level if self.place else self._level
@ level.setter
def level ( self , value )
self._level = value
@property
def pos(self):
return Player.get_place(self.place).pos if self.place else self._pos
@ pos.setter
def pos(self, value):
self._pos = value
# ################################################# ############################
## Player class
class Player(object):
"""
Class that stores various methods and data for exploring. It has the following fields:
level - Current level.
pos - Current coordinate that event is happening.
previous_pos - Previous coordinate. when moving failed, player returns to this pos.
event - Current event.
image - Shortcut to level.image.
music - Shortcut to level.music.
happening(event) - returns True if an event is happened.
done(event) - returns True if an event is happened and done.
"""
def __init__(self, level=None, pos=None, **kwargs):
place = Player.get_place(level)
self.level = place.level if place else level
self.pos = place.pos if place else pos
self.previous_pos = self.pos
self.action = "stay"
self.event = None
self.current_events = []
self.current_places = []
self.done_events = set()
self.happened_events = set()
for i in kwargs.keys():
setattr(self, i, kwargs[i])
@property
def music(self):
return self.get_level(self.level).music
@property
def image(self):
return self.get_level(self.level).image
def happened(self, ev):
# returns True if this event is happened.
return ev.name in self.happened_events
def done(self, ev):
# returns True if this event is done.
return ev.name in self.done_events
def update_events(self, check=True):
# get current_events and current_places in the current level
# get current events
self.current_events = []
for i in dir(store.event) + dir(store.ev) + dir(store):
if not i.startswith("_") and i != "i":
ev = self.get_event(i)
if isinstance(ev, Event) and (ev.level == None or ev.level == self.level):
ev.name = i.split(".")[1] if i.count(".") else i
self.current_events.append(ev)
if check:
try:
eval(ev.cond)
except:
raise Exception("Invalid syntax '{}' in '{}'".format(ev.cond, ev.name))
self.current_events.sort(key = lambda ev: ev.priority)
# get current places
self.current_places = []
for i in dir(store.place) + dir(store.place) + dir(store):
if not i.startswith("_"):
pl = self.get_place(i)
if isinstance(pl, Place) and (pl.level == None or pl.level == self.level):
self.current_places.append(pl)
self.current_places.sort(key = lambda pl: pl.priority)
def get_places(self):
# returns place list that is shown in the navigation screen.
events = []
for i in self.current_places:
if i.once and self.happened(i):
continue
if eval(i.cond):
events.append(i)
events.sort(key = lambda ev: -ev.priority)
return events
def get_events(self, pos = None, action= None):
# returns event list that happens in the given pos.
actions = ["stay"]
if action == "move":
actions += ["move"]
if action == "click":
actions += ["click"]
events = []
for i in self.current_events:
if i.once and self.happened(i):
continue
if action and i.trigger not in actions:
continue
if action == None or i.pos == None or i.pos == pos:
if eval(i.cond):
events.append(i)
if action:
return self.cut_events(events)
else:
return events
def cut_events(self, events):
# If non-multi is found, remove second or later non-multi events
found = False
for i in events[:]:
if not i.multi:
if found:
events.remove(i)
else:
found=True
return events
def move_pos(self, _return):
# Changes own level and pos
# if nothing changed, return None
# don't move
if not _return:
return None
self.previous_pos = self.pos
# try place
rv = self.get_place(_return)
if rv and isinstance(rv, Place):
self.level = rv.level
self.pos = rv.pos
# try level
rv = self.get_level(_return)
if rv and isinstance(rv, Level):
self.level = _return
self.pos = None
# try tuple
if isinstance(_return, tuple):
rv = self.get_level(_return[0])
if rv and isinstance(rv, Level):
self.level = _return[0]
self.pos = _return[1]
else:
self.pos = _return
# move
return True
@classmethod
def get_level(cls, name):
# returns level object from name
if isinstance(name, Level):
return name
elif isinstance(name, basestring):
obj = getattr(store.level, name, None) or getattr(store, name, None)
if obj:
return obj
@classmethod
def get_place(cls, name):
# returns place object from name
if isinstance(name, Place):
return name
elif isinstance(name, basestring):
obj = getattr(store.place, name, None) or getattr(store, name, None)
if obj:
return obj
@classmethod
def get_event(cls, name):
# returns event object from name
if isinstance(name, Event):
return name
elif isinstance(name, basestring):
obj = getattr(store.event, name, None) or getattr(store.ev, name, None) or getattr(store, name, None)
if obj:
return obj
# ################################################# ############################
## Create namespace
init -999 python in level:
pass
init -999 python in place:
pass
init -999 python in event:
pass
init -999 python in ev:
pass
Code: Select all
## This file adds tilemap exploring function into adventure framework.
## This framework requires tilemap.rpy and adventure.rpy.
This file adds the function to search the tile map to # # adventure.
You will need # # tilemap.rpy and adventure.rpy.
# ################################################# ############################
## How to Use
# ################################################# ############################
# # First, create a tile map.
# tilemap1 = Tilemap (map1, tileset, 32, 32)
# # Then use it to define the level with TiledLevel (image, music, tilemap).
# # image is the image defined from the tilemap and tilemap is the original tilemap object.
# # Here, init offset is set because the tilemap defined in tilemap.rpy is used.
init offset = 1
define level.field = TiledLevel("tilemap1", tilemap=tilemap1)
# # Then define the adventurer with default using TilemapPlayer (level, pos, icon).
# # level is the level at which the game starts and pos is the coordinates of that tile.
# # Unlike normal adventure, it is given as a pair of integers (x, y).
# # icon is the image to be displayed on the tile where the adventurer is currently.
Although it is an alias to avoid duplication of definition with # # adventure.rpy,
# # After the game starts, return to player and use it.
default tilemapplayer = TilemapPlayer("field", pos=(0,0), turn=0, icon = Text("P"))
# # Defines an event on the tile map.
# # define Label name = Define with Event (level, pos, cond, priority, once, multi).
# # This is an event that occurs when you click. If you give an icon, the image will be displayed on the tilemap.
# # Tilemaps are clickable at all coordinates, so you don't need to set place or active.
define ev.iconA = Event("field", pos=(5,0), icon=Text("A"))
label iconA:
"icon A is clicked"
return
define ev.iconB = Event("field", pos=(8,7), icon=Text("B"))
label iconB:
"icon B is clicked"
return
Events without # # pos will occur anywhere within that level.
define ev.ev0 = Event("field", priority=99)
label ev0:
"there is nothing there"
return
# # The next event is an event that is called before the player operates.
define ev.enter = Event("field", once=True, trigger="stay")
label enter:
"enter point"
return
If # # pos is an integer or a string, the event will be fired if the string matches the value of the binary array that defined the map.
# # This is especially effective when a character string is used to define the tile map.
define ev.ev1 = Event("field", pos=1, priority=1)
label ev1:
"mapchip'1' is clicked"
return
define ev.ev2 = Event("field", pos=2, priority=1)
label ev2:
"mapchip'2' is clicked"
return
define ev.outside_map = Event("field", trigger = "click", priority=99)
label outside_map :
"You clicked outside of the map"
return
Jumping from the # # start label to adventure_tilemap will start the search.
# ################################################# ############################
## Definitions
# ################################################# ############################
# ################################################# ############################
## Main label
## Jumping to this label starts exploration
label adventure_tilemap :
# rename back
$ player = tilemapplayer
# Update event list in the current level
$ player.update_events()
$ player.action = "stay"
$ player.update_tilemap()
# Play music
if player.music:
if renpy.music.get_playing() != player.music:
play music player.music fadeout 1.0
# Show background
if player.image:
scene black with Dissolve(.25)
show expression player.image
with Dissolve(.25)
jump adventure_tilemap_loop
label adventure_tilemap_loop :
while True:
# check passive events
$ block()
$ _events = player.get_events(player.pos, player.action)
# sub loop to execute all passive events
$ _loop = 0
while _loop < len(_events):
$ player.event = _events[_loop]
$ block()
$ player.happened_events.add(player.event.name)
call expression player.event.label or player.event.name
$ player.done_events.add(player.event.name)
if player.move_pos(_return):
jump adventure_tilemap
$ _loop += 1
$ block()
# show eventmap or tilemap navigator
if player.in_tilemap():
call screen tilemap_navigator(player)
else:
call screen eventmap_navigator(player)
if isinstance(_return, basestring):
$ player.action = _return
else:
$ player.action = "move"
$ player.move_pos(_return)
$ player.update_tilemap()
# ################################################# ############################
# # Tilemap navigator screen
screen tilemap_navigator(player):
# show coordinate
python:
tilemap = player.tilemap
width = tilemap.tile_width
height = tilemap.tile_height
on "show" action SetField(tilemap, "show_cursor", True)
on "hide" action SetField(tilemap, "show_cursor", False)
# When outside of map is clicked.
button:
xysize (config.screen_width, config.screen_height)
action Return("click")
if tilemap.coordinate:
## show coordinate
$ x, y = tilemap.coordinate
text "([x], [y])"
## returns coordinate of tiles
key "button_select" action Return((x, y))
# ################################################# ############################
## TiledLevel class.
heat - 9 python :
class TiledLevel(Level):
"""
Expanded Level class that hols tilemap object.
"""
def __init__(self, image=None, music=None, tilemap=None):
super(TiledLevel, self).__init__(image, music)
self .tilemap = tilemap
# ################################################# ############################
## TilemapPlayer class
class TilemapPlayer(Player):
"""
Expanded Player Class that stores various methods and data for tilemap exploring.
icon - an image that is shown on the coordinate of the player
mask_tilemap - if True, only seen_tiles is shown
"""
def __init__(self, level=None, pos=None, icon=None, mask_tilemap=False, **kwargs):
super(TilemapPlayer, self).__init__(level, pos, **kwargs)
self.replaced_tiles = {}
self.seen_tiles = {}
self.icon = icon
self .mask_tilemap = False
@property
def tilemap(self):
return self.get_level(self.level).tilemap
def in_tilemap ( self ):
# returns true if player is in tilemap
return isinstance ( self .tilemap, Tilemap)
def get_events(self, pos=None, action=None):
# returns event list that happens in the given pos.
# this overwrites the same method in player class.
pos = pos or self . post
actions = ["stay"]
if action == "move":
actions += ["move"]
if action == "click":
actions += ["click"]
events = []
for i in self.current_events:
if i.once and self.happened(i):
continue
if action and i.trigger not in actions:
continue
if action == None or i.pos == None or i.pos == pos:
if eval(i.cond):
events.append(i)
elif self.in_tilemap() and i.pos == self.get_tile(pos=pos):
if eval(i.cond):
events.append(i)
if action:
return self.cut_events(events)
else:
return events
def get_tile(self, level=None, pos=None, numeric=False):
# returns tile from current or given pos
import re
pattern = "[0-9]+"
if not self .in_tilemap ():
return
level = level or self.level
pos = pos or self . post
if (pos[0], pos[1]) in self.replaced_tiles.get(level, ()):
tile = level.replaced_tiles[level][(pos[0], pos[1])]
else:
tile = self.get_level(level).tilemap.map[pos[1]][pos[0]]
if tile and numeric and isinstance(tile, basestring):
tile = int(re.findall(pattern, tile)[0])
return tile
def set_tile(self, tile, level=None, pos=None):
# changes tile value on given pos
if not self .in_tilemap ():
return
level = level or self.level
pos = pos or self . post
self.replaced_tiles.setdefault("level", {})
self.replaced_tiles[level].setdefault((pos[0], pos[1]), tile)
def set_seen_tile(self, level=None, pos=None, around=1):
# set tiles as seen around given pos.
# if around is 1, nine tiles around given tile is set.
if not self .in_tilemap ():
return
level = level or self.level
pos = pos or self . post
if not level in self.seen_tiles.keys():
self.seen_tiles[level] = [[0 for j in xrange(len(self.tilemap.map[0]))] for i in xrange(len(self.tilemap.map))]
for x in xrange(-around, 1+around):
for y in xrange(-around, 1+around):
if pos[1]+y >= 0 and pos[0]+x >=0:
try:
self.seen_tiles[level][pos[1]+y][pos[0]+x]=1
except IndexError:
pass
def add_mask(self):
# for updating tilemap
if not self .in_tilemap ():
return
if self.level in self.seen_tiles.keys():
self.tilemap.mask = self.seen_tiles[self.level]
def add_objects(self):
# for updating tilemap
if not self .in_tilemap ():
return
objects = []
for i in self.get_events():
if i.icon and i.pos:
objects.append((i.pos, i.icon))
if self.icon and self.pos:
objects.append(((self.pos[0], self.pos[1]), self.icon))
self.tilemap.objects = objects
def add_replaced_tiles(self):
# for updating tilemap
if not self .in_tilemap ():
return
if self.level in self.replaced_tiles.keys():
self.tilemap.replaced_tiles = self.replaced_tiles[self.level]
def update_tilemap ( self ):
# add objects, replaced tiles, and mask on tilemap
# it should be called every time before tilemap is shown.
self.add_objects()
self.add_replaced_tiles()
if self .mask_tilemap:
self.set_seen_tile()
self.add_mask()
Code: Select all
## This file defines Tilemap class that create single map from small tile images.
# # File to add Tilemap class to arrange small images into one image.
# ################################################# ############################
## How to Use
# ################################################# ############################
init python:
# # First, define each tile you want to use for your map as a list of displayable objects.
## 例: tileset = ["water.png", "forest.png", ...]
# # Here, we are drawing a solid color rectangle using the function of Ren'py.
tileset = [Solid("#77f", xysize=(32,32)), Solid("#ff9", xysize=(32,32)), Solid("#3f6", xysize=(32,32)), Solid("#656", xysize=(32,32))]
# # If you want to split a single image with tiles arranged, you can also use the following function.
# tileset = read_spritesheet(filename, sprite_width, sprite_height, columns=1, rows=1, spacing=0, margin=0)
# # The map you want to draw next is represented by an integer or a two-dimensional array of strings starting with an integer.
# # The value of the array is the index of the list defined above
# # tileset = ["water.png", "forest.png", ...] 0 represents "water.png".
map1 = [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
[0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1],
[1,1,1,1,1,2,2,2,2,2,2,2,1,1,1,1,1],
[1,1,1,1,1,2,2,2,2,2,2,2,1,1,1,1,1],
[1,1,1,1,1,2,2,2,2,2,2,2,1,1,1,1,1],
[1,1,1,1,1,2,2,2,2,2,2,2,1,1,1,1,1],
[1,1,1,1,1,1,1,2,2,2,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
]
# # You can also read the map defined in the text file with the following function.
# When the # numeral to True, you read to convert the string to an integer.
# map1 = read_spreadsheet("cave.tsv", separator=",", numeral=False)
# # Finally, define the tilemap in the form of Tilemap (map, tileset, tile_width, tile_height, cursor).
# # Map, Tileset is as defined above, tile_width, tile_height is the size of each tile.
Given a # # cursor, the image will be displayed under the mouse.
# Display of # cursor can be controlled by show_cursor = True / False.
# When the # isometric to True to draw from the perspective of looking down diagonally.
tilemap1 = Tilemap(map1, tileset, 32, 32, cursor = Solid("#faa6", xysize=(32,32)))
# Tilemap is displayable. When using it in a show statement, use it after associating it with an image tag.
image tilemap1 = tilemap1
# # When the game starts, jump here with jump sample_tilemap.
label sample_tilemap :
# # Displays the map image defined in the image.
show tilemap1 at truecenter
# If the # tilemap.area to a position other than None, draws only the range.
$ tilemap1.area = ( 64 , 64 , 256 , 256 )
pause
# If the # tilemap.area to None, and draw all image.
$ tilemap1.area = None
# # The coordinates of the tile the mouse is hovering in the tilemap.coordinate has been stored.
call screen track_coordinate(tilemap1)
"[_return]"
return
# ################################################# ############################
## Screen that shows coordinate of tilemap
screen track_coordinate(tilemap):
text "Click a tile to return its coordinate" align .5, .9
# show coordinate
if tilemap.coordinate:
text "[tilemap.coordinate]"
key "button_select" action Return(tilemap.coordinate)
# ################################################# ############################
## Definitions
# ################################################# ############################
heat - 10 python :
class Tilemap(renpy.Displayable):
"""
This creates a displayable by tiling other displayables. It has the following field values.
map - A 2-dimensional list of strings that represent index of a tileset.
tileset - A list of displayables that is used as a tile of tilemap.
tile_width - width of each tile.
tile_height - height of each tile.
tile_offset - blank pixel of (left, top) side of each tile
isometric - if true, isometric tile is used.
area - (x,y,w,h) tuple to render. If it's None, default, it renders all tiles.
mask - 2- dimensional list of 0 or 1. If it's 0, tile will not be rendered.
cursor - If not None, it's a displayable that is shown under mouse.
show_cursor - if True, show cursor
objects - if dict {(x,y): image} is given, this image is shown on the (x,y) tile.
replaced_tiles - if dict {(x,y): value} is given, its coordinate's value is replaced.
interact - If True, default, it restarts interaction when mouse is moved to another tile.
coordinate - (x, y) coordinate of a tile where mouse is hovering.
"""
def __init__(self, map, tileset, tile_width, tile_height = None, tile_offset = (0,0),
isometric = False, area = None, mask = None, cursor = None, interact = True,
objects = None, object_offset = (0,0), replaced_tiles = None, **properties):
super(Tilemap, self).__init__(**properties)
self.map = map
self.tileset = tileset
self.tile_width = tile_width
self.tile_height = tile_height or tile_width
self.tile_offset = tile_offset
self.isometric = isometric
self.area = area
self.mask = mask
self.cursor = cursor
self.show_cursor = True
self.interact = interact
self.objects = objects
self.object_offset = object_offset
self.replaced_tiles = replaced_tiles
self.coordinate = None
def render(self, width, height, st, at):
render = renpy.Render(width, height)
# Blit all tiles into the render.
if self.area == None:
# Get tile position
for y in xrange(len(self.map)):
for x in xrange(len(self.map[y])):
# render
self._render(render, st, at, x, y)
# Adjust the render size.
if self.isometric:
area = (
-len(self.map)*self.tile_width/2,
0 ,
(len(self.map) + len(self.map[0]))*self.tile_width/2,
(max(len(self.map[0]), len(self.map)) + 1)*self.tile_height
)
else:
area = (0, 0, len(self.map[0])*self.tile_width, len(self.map)*self.tile_height)
render = render.subsurface(area)
# Blit only tiles around the area into the render
else:
# Calculate where the area is positioned on the entire map.
self.xoffset = divmod(self.area[0], self.tile_width)
self.yoffset = divmod(self.area[1], self.tile_height)
# Get tile position
for y in xrange(self.yoffset[0], min(len(self.map), self.area[3]//self.tile_height+self.yoffset[0]+1)):
for x in xrange(self.xoffset[0], min(len(self.map[y]), self.area[2]//self.tile_width+self.xoffset[0]+1)):
if 0 <= y < len(self.map) and 0 <= x < len(self.map[0]):
# render
self._render(render, st, at, x, y)
# Crop the render.
render = render.subsurface(self.area)
# Redraw regularly
# renpy.redraw(self, 1.0/30)
return render
def _render(self, render, st, at, x, y):
import re
pattern = "[0-9]+"
# don't render if mask is given
if self . mask and not self .mask [y] [x]:
return
# Get index of tileset
tile = self .map [y] [x]
# if tile is replaced
if self.replaced_tiles:
if (x, y) in self.replaced_tiles.keys():
tile = self.replaced_tiles[(x,y)]
# change value to integer
if not tile:
tile = 0
elif isinstance(tile, basestring):
tile = int(re.findall(pattern, tile)[0])
# Get tile position
if self.isometric:
tile_pos = (x-y-1)*self.tile_width/2, (x+y)*self.tile_height/2
else:
tile_pos = x*self.tile_width, y*self.tile_height
# Blit
render.blit(renpy.render(self.tileset[tile], self.tile_width, self.tile_height, st, at), tile_pos)
# show cursor
if self.cursor and self.show_cursor and self.coordinate:
if self .coordinate == (x, y):
render.blit(renpy.render(self.cursor, self.tile_width, self.tile_height, st, at), tile_pos)
# show objects
if self.objects:
for pos, icon in self.objects:
if pos == self.map[y][x] or (pos[0], pos[1]) == (x, y):
tile_pos = (tile_pos[0]+self.object_offset[0], tile_pos[1]+self.object_offset[1])
im = renpy.displayable(icon)
render.blit(renpy.render(im, self.tile_width, self.tile_height, st, at), tile_pos)
def event(self, ev, x, y, st):
# Get index of tile where mouse is hovered.
if self.isometric:
tile_x = (x-self.tile_offset[0])/self.tile_width + (y-self.tile_offset[1])/self.tile_height - len(self.map)/2
tile_y = -(x/self.tile_width-self.tile_offset[0]) + (y-self.tile_offset[1])/self.tile_height + len(self.map)/2
if len(self.map) % 2 ==1:
tile_x -= 0.5
tile_y + = 0.5
else:
tile_x = (x-self.tile_offset[0])/self.tile_width
tile_y = (y-self.tile_offset[1])/self.tile_height
# if area is given, adjust coordinate
if self.area:
tile_x = tile_x + self.area[0]/self.tile_width
tile_y = tile_y + self.area[1]/self.tile_height
# Make coordinate None if it's out of displayable
if tile_x < 0 or tile_y < 0 or tile_x >= len(self.map[0]) or tile_y >= len(self.map):
coordinate = None
else:
coordinate = int(tile_x), int(tile_y)
# Restart interaction only if coordinate has changed
if self.coordinate != coordinate:
self.coordinate = coordinate
if self.interact:
renpy.restart_interaction()
# Call event regularly
renpy.timeout(1.0/60)
def per_interact(self):
# Redraw per interact.
renpy.redraw(self, 0)
def visit(self):
# If the displayable has child displayables, this method should be overridden to return a list of those displayables.
return self.tileset
def read_spritesheet(file, sprite_width, sprite_height=None, cols=1, rows=1, spacing=0, margin=0, transpose=False):
# Function that returns a list of displayables from a spritesheet.
sprite_height = sprite_height or sprite_width
sprites=[]
if transpose:
cols, rows = rows, cols
for r in xrange(rows):
for c in xrange(cols):
rect = ((sprite_width+spacing)*c+margin, (sprite_height+spacing)*r+margin, sprite_width, sprite_height)
sprites.append(im.Crop(file, rect))
return sprites
def read_spreadsheet(file, separator=",", numeral=False):
# Function that returns a 2-dimensional list from a text file.
# If numeral is True, string will convert to integer.
rv = []
f = renpy.file(file)
for l in f:
l = l.decode("utf-8")
a = l.rstrip().split(separator)
rv2 = []
for x in a:
if x.isdecimal() and numeral:
x = int(x)
elif numeral:
x = 0
rv2.append(x.strip())
rv.append(rv2)
f.close()
return rv
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."
Who is online
Users browsing this forum: Bing [Bot], Vamp-sama