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) )
No comments:
Post a Comment
Conduct: Be nice and don't advertise or plug without adding value to the conversation.