Dungeon engine problem

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
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.
Post Reply
Message
Author
User avatar
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

#1 Post by KebecMaslow »

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.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."

User avatar
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

#2 Post by KebecMaslow »

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."

User avatar
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

#3 Post by KebecMaslow »

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.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Contact:

Re: Dungeon engine problem

#4 Post by hell_oh_world »

if this is the list of names for the images/etc...

Code: Select all

["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]
then you can pre-define presets of this...

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:
                ...
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.

User avatar
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

#5 Post by KebecMaslow »

hell_oh_world wrote: Fri Oct 15, 2021 9:20 pm if this is the list of names for the images/etc...

Code: Select all

["left2", "right2", "front2", "left1", "right1", "front1", "left0", "right0"]
then you can pre-define presets of this...

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:
                ...
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.
Amazing, it worked. Thank you so much.

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)
Of course is the wrong way but my programming skills are in diapers. How do I create a list where the script choose the enemy or group or enemies if possible?
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."

User avatar
hell_oh_world
Miko-Class Veteran
Posts: 777
Joined: Fri Jul 12, 2019 5:21 am
Contact:

Re: Dungeon engine problem

#6 Post by hell_oh_world »

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.

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)

User avatar
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

#7 Post by KebecMaslow »

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."

User avatar
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

#8 Post by KebecMaslow »

Nyaatrap also designed other dungeon script that I consider better but have a few issues with it. This is the code:

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
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.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."

User avatar
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

#9 Post by KebecMaslow »

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.

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
There's also the option of hiding the minimap, but that will make navigation a problem.
"The men grew fond of themselves, forgetting all the fears and hardships of the past... until the darkness came back for them."

Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot]