A Location Manager

A place for Ren'Py tutorials and reusable Ren'Py code.
Forum rules
Do not post questions here!

This forum is for example code you want to show other people. Ren'Py questions should be asked in the Ren'Py Questions and Announcements forum.
Post Reply
Message
Author
User avatar
docclox
Newbie
Posts: 20
Joined: Thu Oct 12, 2017 6:26 am
Contact:

A Location Manager

#1 Post by docclox »

I'm writing code where the player explores a space station. As usual with these things, there are locked doors, hidden passages and the like.

And I've been getting into all manner of mess trying to keep track of where the player is, how many times he's been there, whether a passage is usable or not.

[edit]

Let's try that again. Now improved with actual working code.

Code: Select all

init python in navman:
#
#   Represents locations on the map. Places the player can visit
#
    class Place:
        def __init__(self,  name, desc, *args, **kw):
            self.name = name            # name used to form labels
            self.desc = desc            # human friendly version
            self.links = []                # array of connections to other areas. 
                                        # Doors, corridors, teleport portals, whatever
            self.visit_count = 0        # count how many times the player has been here
                                        # user data, used here to find a key and unlock a door

        def add_link(self, link):
            self.links.append(link)

        def find_link(self, name):
            for l in self.links:
                if l.there.name == name:
                    return l
            return None

#
#   represents the link between two locations.
#    may be locked or hidden
#
    class Link:
        def __init__(self,  here, there, locked, hidden):
            self.here = here
            self.there = there
            self.locked = locked
            self.hidden = hidden

#
#   keeps track of all the places and has functions to determine
#   if they're visible or not, and to return a smart label object
#    when trying to move
#
    class Layout:
        def __init__(self,  *args, **kw):
            self.hash = {}
            self.places = []
            self.loc = kw.get("start", None)
            self.last_loc = None

        def add_place(self, place):
            self.places.append(place)
            self.hash[place.name] = place
            self.hash[place.desc] = place
            return place

        def lookup(self, name):
            return self.hash.get(name, None)

        def unlock(self, s0, s1):
            p0 = self.lookup(s0)
            p1 = self.lookup(s1)
            l0 = p0.find_link(s1)
            l0.locked = False
            l1 = p1.find_link(s0)
            l1.locked = False

        def link_to(self, name):
            renpy.log(
                "Looking for link from Here ({}) to {}".format(
                    self.loc, name
                )
            )
            here = self.lookup(self.loc)
            if here == None:
                return False
            l = here.find_link(name)
            return l 

        def change_loc(self, name):
            self.last_loc = self.loc
            self.loc = name

        def go_to(self, name):
            l = self.link_to(name)
            if l == None:
                return NavLabel(self.loc, self.loc, self.loc, self)
            if l.hidden:
                return NavLabel(self.loc, self.loc, self.loc, self, hidden=True)
                return self.loc
            if l.locked:
                return NavLabel("{}_locked".format(name), self.loc, self.loc, self, locked=True)
            return NavLabel(name, name, self.loc, self)

        def reachable(self, name):
            renpy.log("*** Checking reachability ***")
#
#           one thing I want from this is to determine which locations appear on an imagemap
#           so we want the current location to be visible. Maybe "visible" would be a better 
#           name ...
#
            if self.loc == name:
                return True
            l = self.link_to(name)
            rv = (l != None)
            renpy.log("*** returning {} ***".format(rv))
            return rv


#
#    This is the smart label. Has a label string to goto, 
#    plus a pile of other stuff that may come in useful
#
    class NavLabel:
        def __init__(self, label_str, new_str, old_str, layout, locked=False, hidden=False):
            self.label_str = label_str
            self.new_str = new_str
            self.old_str = old_str
            self.new_loc = layout.lookup(new_str)
            self.old_loc = layout.lookup(old_str)
            self.locked = locked
            self.hidden = hidden
            self.layout = layout

#
#   List of places
#
    spec = [
        Place( "loadingbay", "Loading Bay" ),
        Place( "engineering", "Engineering" ),
        Place( "kitchens", "Kitchens" ),
        Place( "bar", "Bar" ),
        Place( "elite", "Elite Dining" ),
    ]
#
#   create a layout and add in the places
#
    layout = Layout(start = "loadingbay")
    for p in spec:
        layout.add_place(p)
#
#   list of links. S1 is "here" and s1 is there. 
#    links are one way, but get added in pairs so you don't
#    need to add them twice. If you want one way doors or 
#    doors that are only locked from one side, you'll need to 
#    tweak things a bit
#
#    "locked" and "hidden" are optional fields. 
#    Both default to False, which is probably what you want most of the time.
#
    link_list = [
        {   "s0"        : "loadingbay",
            "s1"        : "engineering",
            "locked"    : True,
        },
        {   "s0"        : "loadingbay",
            "s1"        : "kitchens",
        },
        {   "s0"        : "kitchens",
            "s1"        : "bar",
        },
        {   "s0"        : "bar",
            "s1"        : "elite",
        },
    ]
#
#    create objects for those links and add them to the places
#
    for d in link_list:
        p0 = layout.lookup(d["s0"])
        p1 = layout.lookup(d["s1"])
        locked = d.get("locked", False)
        hidden = d.get("hidden", False)
        p0.add_link(Link(p0, p1, locked, hidden))
        p1.add_link(Link(p1, p0, locked, hidden))

#
# import the navman namespace and set up a style for the meny
#
init python:
    import store.navman as navman
    style.my_menu_style = Style(style.default)
    style.my_menu_style.background  = Frame("my_frame.png", 20, 20, 20, 20)

#
# menu screen. The main advantage here is that we make it by looping over the place list.
#
# With a little tweaking it can make image maps and the like as well.
#
screen my_menu:
    frame:
        style "my_menu_style"
        xpadding 40
        ypadding 40
        xalign 0.02
        yalign 0.5
        has vbox
        for i in navman.spec:
            if navman.layout.reachable(i.name):
                textbutton(i.desc):
                    action Return(navman.layout.go_to(i.name))


image black = '#000000'

# The game starts here.
label start:
    scene black

label map_loop:
#
#     call the screen and get a menu choice
#
    call screen my_menu
#
#   if the passage wasn't locked, change the location
#   and bumpt the visit count
#
    if not _return.locked:
        $ navman.layout.change_loc(_return.new_str)
        $ _return.new_loc.visit_count += 1
#
#    now we can jump to the label
#
    call expression _return.label_str from _stuffy_funk
#
#   and back to the map loop
#
    jump map_loop

#
# labels to jump to - the names need to match the Place names
#
label loadingbay:
    "loading bay"
    return
label engineering:
    "engineering"
    return
#
#  a label for the engineering door while it's locked
#
label engineering_locked:
    "the door won't open"
    return
label kitchens:
    "kitchens"
    return
label bar:
    "bar"
    return
#
# the third time you visit the Elite Dining room
# you spot the engineering room key that was 
# staring you in the face the whole time - 
# or whatever - this is just an illustration
#
label elite:
    "Elite Dining"
    if _return.new_loc.visit_count == 3:
        "You find a key"
        $ _return.layout.unlock("loadingbay", "engineering")
    return


mega:///#!fEYixBKS!RX3GbERJ-JFkGaNa599_sZkXAzWBi2I48CWCVaT38Os

User avatar
docclox
Newbie
Posts: 20
Joined: Thu Oct 12, 2017 6:26 am
Contact:

Re: A Location Manager

#2 Post by docclox »

Got a rather better version of that together. Uses a loop to generate the menu as well as labels based on the location code. Handles locked doors and hidden passages as before. And there's a sample project with a five room layout to demo how it works. I hope someone finds it useful.

I updated the first post rather than post it here - hope that's OK.

Post Reply

Who is online

Users browsing this forum: No registered users