Home

Thursday, December 23, 2021

Python: Text RPG - Entry 1.1 - Map Movement Trial

Python Text RPG

-Map Movement- 

 

Was trying out a different solution for controlling the connections and organization of locations in the game. I got some ideas from this trial. But! The solution requires really heavy lifting and is very verbose. Neither of which I'm fond of dealing with.

I'm going for clean and easy to implement.

 

Things that worked:

I did appreciate having more explicit function calls, but also I'm not a fan of having a bunch of them because I'm lazy. But in the long run having a catch all function like 'children' could be bad idea for separation of responsibilities. I also liked the look and readability of the type hinting in the function signature.

I like the idea that the game manager knows about all the locations in the game and they are not nested in multiple layers of location object. But that also presents a name space difficulty. I can't have two northshire abbey's. One in Northshire, and one in Westfall. It also makes pretenses a data grouping difficulty. How does the game_manager know that 'floors_0-4' belong to the lions pride inn in goldshire and not some other structure or map? if I get into explicit naming like expansion_lordaron_goldshire_lions_pride_Inn_floor_1, then it is a pain if I change or move something.


Things that didn't work:

Too explicite for connections I want to take groupings of data for granted.

I didn't think through the granularity and relationships between. They currently only work at the 'zone' category level. Anything 'below' that and the child relationships are broken. A fixable logic error but this day sprint is out of time.


Things I would do differently:

define everything important all at once Location(zone type, name, visible, and lock)

I like the simplicity of everything be a general area 'zone' or a specific facility or place 'site' and nothing else. Elwynn Forest is a zone, goldshire is a site. Deadmine in Westfall would also be a site

 _______________________________________________________________

 

 # Stub
class Container(): pass

# Stub
class Grid(): pass

_______________________________________________________________

 
class Location(): pass # Ugly code for Location parameter signature
class Location():

    # Initialize class variables
    name_ = ""
    notes = ""
    locked = True
    # sublocations = [] # TBD: Should the Game_Manager own this data or the parent Location?
    play_grid = None
    contents = None
    options = []
    intro = ""

    def __init__( self, name, locked, notes="", intro="", options=None, play_grid:Grid=None, contents:Container=None ) -> None:
        # Initialize object's variables
        self.name_ = name
        self.notes = notes
        self.locked = locked
        # self.sublocations = sublocations if sublocations else [] # TBD
        self.play_grid = play_grid
        self.contents = contents
        self.options = options
        self.intro = intro


    def __str__( self ):
        return f"{self.name_}"


    def __repr__( self ):
        # TODO: make a list comprehension that spits out a formatted string of all object variables.
        return f"<{self.name_}: {self.locked}, {self.play_grid}, {self.contents} >"


    def set_intro( self, msg ):
        self.intro = str( msg )


    def fetch_intro( self ):
        return self.intro


    def set_options( self, *options ):
        self.options = [ str( o ) for o in options ]


    def fetch_options( self ):
        return self.options

_______________________________________________________________

 
 

class Game_Manger():

    previous_location = None
    current_location = None
    locations = []
    travel_paths = []
    available = [] # optimization? or iterate through locations.locked.
    location_type = { "zone" : [], "subzone" : [], "site" : [], "section" : [] }
    # TBD this is a heavy lifting solution

    # Create Locations
    def register_location( self, location:Location ) -> Location:
        """Appends the provided location to the games locations and create a travel_paths array"""
        if location not in self.locations and location.name_ not in ''.join( str( self.locations ) ):
            self.locations.append( location )
            self.travel_paths.append( [] )
            self.update_travel_locations()
        return location


    def register_location_to_category( self, location:Location, category:str ) -> None:
        """Appends the provided location to the specified category key for indexing"""
        # Should be more explicit? add_zone, add_subzone?
        if str(category).lower() in self.location_type:
            if location not in self.location_type[ category ] or location.name_ not in ''.join( str( self.location_type[ category ] ) ):
                self.location_type[category].append( location )


    # Create Connections between Locations
    def register_path_to_locations( self, parent:Location, *children:Location ) -> [ Location ]:
        """Returns a list of children added to the parents travel_paths list"""
        catch = []
        index = self.locations.index( parent )
        for child in children:
            if child not in self.travel_paths[ index ] or child.name_ not in ''.join( str( self.travel_paths[ index ] ) ):
                self.travel_paths[index].append( child ) # Todo: Store index numbers rather than object reference?
                catch.append( child )
        return catch


    # Enable travel to a location
    def unlock_location( self, location:Location ) -> bool:
        """Unlock the provided location and add it to the available"""
        self.location.locked = False
        self.available.append( location )
        return location.locked
    

    def lock_location( self, location:Location ) -> bool:
        """Lock the provided location and remove it from the available"""
        self.location.locked = True
        self.available.remove( self.available.index( location ) )
        return location.locked
    
    def fetch_category( self, locale:Location ) -> str:
        for category in self.location_type.keys():
            if locale in self.location_type[category]:
                return category

    def fetch_index_to_location( self, index:int ) -> Location:
        return self.locations[int(index)]


    def fetch_location_to_index( self, locale ) -> int:
        return self.locations.index(locale)


    def update_travel_locations( self ) -> None:
        self.available = [ (idx, locale) for idx, locale in enumerate(self.locations) if locale.locked == False ]


    def travel_locations( self ) -> [ Location ]:
        """Get list of unlocked locations"""
        return self.available


    def travel_neighbors( self, locale:Location ) -> [ Location ]:
        return [ p for p in self.travel_paths[ self.fetch_location_to_index( locale ) ] if p in self.location_type[ self.fetch_category( locale ) ] ]
        

    def travel_children( self, locale:Location ) -> [ Location ]:
        return [ p for p in self.travel_paths[ self.fetch_location_to_index( locale ) ] if p in self.location_type[ 'subzone' ] or p in self.location_type[ 'site' ] or p in self.location_type[ 'section' ] ]


    def fetch_current_location( self ) -> Location:
        return self.current_location


    def travel_to_location( self, new_location:Location ) -> Location:
        """Travel from current location to the provided one"""
        self.previous_location = self.current_location
        self.current_location = new_location
        return self.current_location


    def travel_return_to_previous( self ) -> Location:
        """Travel from the current location to the previous one"""
        self.current_location = self.previous_location
        return self.current_location


_______________________________________________________________
 
 

Use Example:

gm = Game_Manger()

zo1 = gm.register_location( Location( "Stormwind", False, "Human capital" ) )
gm.register_location_to_category( zo1, "zone" )

zo2 = gm.register_location( Location( "Westfall", False, "low level grassland farms" ) )
gm.register_location_to_category( zo2, "zone" )

zo3 = gm.register_location( Location( "Duskwood", False, "mid level dangerous woods" ) )
gm.register_location_to_category( zo3, "zone" )

zo4 = gm.register_location( Location( "Redridge Mountains", False, "mid level mountains" ) )
gm.register_location_to_category( zo4,"zone" )

zo5 = gm.register_location( Location( "Burning Steppes", False, "high level lava flows" ) )
gm.register_location_to_category( zo5, "zone" )

zo6 = gm.register_location( Location( "Elwynn Forest", False, "Lowbie starting region" ) )
gm.register_location_to_category( zo6, "zone" )


a1 = gm.register_location( Location( "Northshire Valley", False, "Welcome and start of new game tutorial" ) )
gm.register_location_to_category( a1, "subzone" )

a2 = gm.register_location( Location( "Goldshire", False, "first lowbie town" ) )
gm.register_location_to_category( a2, "subzone" )

gm.register_path_to_locations( zo6, a1, a2 )
gm.register_path_to_locations( zo6, zo1, zo2, zo3, zo4, zo5 )
print( "CHILDREN:", zo6, gm.travel_children( zo6 ) )
print( "NEIGHBORS:", zo6, gm.travel_neighbors( zo6 ) )
print()


b2 = gm.register_location( Location( "Echo Ridge Mine", False, "The first dungeon" ) )
gm.register_location_to_category( b2, "site" )

c3 = gm.register_location( Location( "Northshire vineyards", False, "A small grape farm" ) )
gm.register_location_to_category( c3, "site" )

d4 = gm.register_location( Location( "Northshire river", False, "a river cutting through the northshire zone" ) )
gm.register_location_to_category( d4, "site" )

b = gm.register_location( Location( "Northshire Abbey", False, "The building" ) )
gm.register_location_to_category( b, "site" )

gm.register_path_to_locations(a1, a2)
gm.register_path_to_locations(a1, b, b2, c3, d4)
print( "CHILDREN:", a1, gm.travel_children(a1) )
print( "NEIGHBORS:", a1, gm.travel_neighbors(a1) )
print()

c = gm.register_location( Location( "Northshire Abbey Exit", False, "General starting spot" ) )
gm.register_location_to_category( c, "section" )
d = gm.register_location( Location( "Main Hall", False, "zone exit > trainers and quest givers" ) )
gm.register_location_to_category( d, "section" )
e = gm.register_location( Location( "Hall of Arms", False, "Get more quests" ) )
gm.register_location_to_category( e, "section" )
f = gm.register_location( Location( "Library Wing", False, "Get training" ) )
gm.register_location_to_category( f, "section" )

gm.register_path_to_locations( a1, c, d, e, f )
print( "CHILDREN:", a1, gm.travel_children(a1) )
print( "NEIGHBORS:", a1, gm.travel_neighbors(a1) )


-END-

No comments:

Post a Comment

Conduct: Be nice and don't advertise or plug without adding value to the conversation.