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
User avatar
Posts: 20
Joined: Thu Oct 12, 2017 6:26 am

A Location Manager

#1 Post by docclox » Mon Oct 23, 2017 9:47 am

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.


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

        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.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):
                "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:
#   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:
        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):
                    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"
label engineering:
#  a label for the engineering door while it's locked
label engineering_locked:
    "the door won't open"
label kitchens:
label bar:
# 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")


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

Re: A Location Manager

#2 Post by docclox » Tue Oct 24, 2017 5:09 pm

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