added "info" and "wear" use functions, and fixed many bugs related to storing things by ID.
This commit is contained in:
parent
1b41c46105
commit
8355dccb33
4 changed files with 165 additions and 44 deletions
93
gamebase.py
93
gamebase.py
|
@ -25,7 +25,7 @@ class GameBase(object):
|
|||
self.__behaviors = {}
|
||||
self.__gameEvents = {}
|
||||
self.__IOCalls = {} # {str : function}
|
||||
self.customVals = {} # for setting flags and such
|
||||
self.customValues = {} # for setting flags and such
|
||||
self.level = None
|
||||
self.persist = {} # {level : {thingName : thing}}
|
||||
self.ps2 = '? '
|
||||
|
@ -58,6 +58,8 @@ class GameBase(object):
|
|||
self.registerUseFunc('examine', self.examine)
|
||||
self.registerUseFunc('key', self.key)
|
||||
self.registerUseFunc('container', self.container)
|
||||
self.registerUseFunc('info', self.info)
|
||||
self.registerUseFunc('wear', self.wear)
|
||||
self.registerBehavior('wander', self.wander)
|
||||
|
||||
# Helper functions
|
||||
|
@ -138,16 +140,16 @@ Returns (thing, x, y). "Thing" can be None."""
|
|||
elif thing != None:
|
||||
return thing, thing.x, thing.y
|
||||
elif allowInventory:
|
||||
if name in self.playerInv:
|
||||
thing = self.playerInv[name]
|
||||
for i in self.playerInv:
|
||||
thingName = self.playerInv[i].name
|
||||
if name == thingName:
|
||||
thing = self.playerInv[i]
|
||||
if thing != None:
|
||||
return thing, -1, -1
|
||||
else:
|
||||
return None, -1, -1
|
||||
#raise RuntimeError("'None' item named '{0}' in player inventory.".format(name))
|
||||
else:
|
||||
return None, -1, -1
|
||||
#raise ValueError("{0} cannot be reached.".format(name))
|
||||
else: # nothing
|
||||
return None, -1, -1
|
||||
#raise ValueError("{0} cannot be reached.".format(name))
|
||||
|
@ -295,6 +297,7 @@ say "use the lever" rather than just "use lever").
|
|||
Object can be the name of the object, or its coordinates. It can also be
|
||||
the name of an item in the player's inventory."""
|
||||
speed = 0.6666667
|
||||
useArgs = []
|
||||
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
|
||||
speed = 0.3333333
|
||||
args.pop(0)
|
||||
|
@ -303,19 +306,21 @@ the name of an item in the player's inventory."""
|
|||
if 'on' in args:
|
||||
self.useOn(args, speed)
|
||||
return
|
||||
if 'with' in args:
|
||||
useArgs = args[args.index('with')+1:]
|
||||
thing, x, y = self.parseCoords(args)
|
||||
if thing == None:
|
||||
print("There is nothing to use.", file = self.outstream)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
if thing.thingType != 'u' and thing.name not in self.playerInv:
|
||||
if thing.thingType != 'u' and thing.thingID not in self.playerInv:
|
||||
print("The {0} cannot be used.".format(thing.name), file = self.outstream)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
# Similar to go, but not quite the same.
|
||||
if (x, y) == (self.playerx, self.playery) or thing.name in self.playerInv:
|
||||
self.setEvent(0.125, _ge.UseEvent(thing))
|
||||
if (x, y) == (self.playerx, self.playery) or thing.thingID in self.playerInv:
|
||||
self.setEvent(0.125, _ge.UseEvent(thing, useArgs))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
|
@ -335,7 +340,7 @@ the name of an item in the player's inventory."""
|
|||
t += 1
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
self.setEvent(t * speed + 0.125, _ge.UseEvent(thing))
|
||||
self.setEvent(t * speed + 0.125, _ge.UseEvent(thing, useArgs))
|
||||
return
|
||||
|
||||
def useOn(self, args, speed):
|
||||
|
@ -345,7 +350,10 @@ the name of an item in the player's inventory."""
|
|||
if args[onIndex+1] == 'the':
|
||||
onIndex += 1
|
||||
thing, x, y = self.parseCoords(args[onIndex+1:])
|
||||
if item == None or item.name not in self.playerInv:
|
||||
useArgs = []
|
||||
if 'with' in args:
|
||||
useArgs = args[args.index('with')+1:]
|
||||
if item == None or item.thingID not in self.playerInv:
|
||||
print("There is no {0} in the inventory.".format(item.name), file = self.outstream)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
@ -356,7 +364,7 @@ the name of an item in the player's inventory."""
|
|||
|
||||
# Similar to go, but not quite the same.
|
||||
if (x, y) == (self.playerx, self.playery):
|
||||
self.setEvent(0.125, _ge.UseOnEvent(item, thing))
|
||||
self.setEvent(0.125, _ge.UseOnEvent(item, thing, useArgs))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
|
@ -379,7 +387,7 @@ the name of an item in the player's inventory."""
|
|||
t += 1
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
self.setEvent(t * speed + 0.125, _ge.UseOnEvent(item, thing))
|
||||
self.setEvent(t * speed + 0.125, _ge.UseOnEvent(item, thing, useArgs))
|
||||
return
|
||||
|
||||
def take(self, args):
|
||||
|
@ -434,10 +442,12 @@ Object can be the name of the object, or its coordinates."""
|
|||
"""drop [the] item"""
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
if args[0] in self.playerInv:
|
||||
for i in self.playerInv:
|
||||
thingName = self.playerInv[i].name
|
||||
if ' '.join(args) == thingName:
|
||||
self.setEvent(0.0, _ge.DropEvent(self.playerInv[args[0]]))
|
||||
else:
|
||||
print('{0} do not have a {1}.'.format(self.playerName, args[0]), file = self.outstream)
|
||||
return
|
||||
print('{0} does not have a {1}.'.format(self.playerName, args[0]), file = self.outstream)
|
||||
|
||||
def loadMap(self, args):
|
||||
# loadMap (fileName)
|
||||
|
@ -602,26 +612,26 @@ Object can be the name of the object, or its coordinates."""
|
|||
if e.thing.useFunc == '':
|
||||
print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream)
|
||||
return True
|
||||
self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing), _ge.NoOpEvent())
|
||||
self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing, e.args), _ge.NoOpEvent())
|
||||
return False
|
||||
|
||||
def handleUseOn(self, e):
|
||||
if e.item.useOnFunc == '':
|
||||
print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream)
|
||||
return True
|
||||
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.thing), _ge.NoOpEvent())
|
||||
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.thing, e.args), _ge.NoOpEvent())
|
||||
return False
|
||||
|
||||
def handleTake(self, e):
|
||||
self.level.removeThingByName(e.item.name)
|
||||
self.playerInv[e.item.name] = e.item
|
||||
self.level.removeThingByID(e.item.thingID)
|
||||
self.playerInv[e.item.thingID] = e.item
|
||||
return True
|
||||
|
||||
def handleDrop(self, e):
|
||||
e.item.x = self.playerx
|
||||
e.item.y = self.playery
|
||||
self.level.addThing(e.item, True)
|
||||
del self.playerInv[e.item.name]
|
||||
del self.playerInv[e.item.thingID]
|
||||
return True
|
||||
|
||||
def handleBehave(self, e):
|
||||
|
@ -629,7 +639,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
|
||||
# default useFuncs: take a list of arguments, return the time the use took
|
||||
|
||||
def examine(self, thing):
|
||||
def examine(self, thing, args):
|
||||
"""Just prints the given text."""
|
||||
if 'pattern' not in thing.customValues or 'text' not in thing.customValues:
|
||||
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
||||
|
@ -661,13 +671,50 @@ Object can be the name of the object, or its coordinates."""
|
|||
self.customVals[thing.customValues['set']] = 0
|
||||
return 0.0
|
||||
|
||||
def container(self, thing):
|
||||
def container(self, thing, args):
|
||||
"""Acts as a container. Items can be traded between the container and the player's inventory."""
|
||||
items = list(thing.customValues['items'])
|
||||
self.playerInv, thing.customValues['items'], timeOpen = self.getIO('container')(self.playerInv, items)
|
||||
return timeOpen
|
||||
|
||||
def key(self, item, thing):
|
||||
def info(self, thing, args):
|
||||
"""Acts as a bookshelf, filing cabinet, bulletin board, or anything that contains raw info."""
|
||||
items = dict(thing.customValues['items'])
|
||||
return self.getIO('info')(items)
|
||||
|
||||
def wear(self, item, args):
|
||||
"""Wear clotherg or otherwise equip a passive item."""
|
||||
# An item must be in the player's inventory in order to wear it.
|
||||
inInv = False
|
||||
for i in self.playerInv:
|
||||
if i == item.thingID:
|
||||
inInv = True
|
||||
break
|
||||
if not inInv:
|
||||
print("You cannot wear what you are not carrying.", file = self.outstream)
|
||||
return 0.0
|
||||
if 'wearing' not in self.customValues:
|
||||
self.customValues['wearing'] = {}
|
||||
if item.customValues['slot'] not in self.customValues['wearing']:
|
||||
self.customValues['wearing'][item.customValues['slot']] = item
|
||||
# This is so a player can't put on a garment, then drop it while
|
||||
# still also wearing it.
|
||||
del self.playerInv[item.thingID]
|
||||
print("{} put on the {}.".format(self.playerName, item.name), file = self.outstream)
|
||||
else: # the player must be wearing something that will get in the way
|
||||
# put the old clothes back in the inventory
|
||||
oldItem = self.customValues['wearing'][item.customValues['slot']]
|
||||
self.playerInv[oldItem.thingID] = oldItem
|
||||
self.customValues['wearing'][item.customValues['slot']] = item
|
||||
# This is so a player can't put on a garment, then drop it while
|
||||
# still also wearing it.
|
||||
del self.playerInv[item.thingID]
|
||||
print("{} took off the {} and put on the {}.".format(self.playerName, oldItem.name, item.name), file = self.outstream)
|
||||
return 1.0
|
||||
|
||||
# item use-on functions
|
||||
|
||||
def key(self, item, thing, args):
|
||||
"""Item is a key, which unlocks a door. This may be implemented for containers later."""
|
||||
if isinstance(thing, tuple) or thing.thingType != 'd':
|
||||
print("That is not a door.", file = self.outstream)
|
||||
|
|
|
@ -79,15 +79,17 @@ class ArriveEvent(GameEvent):
|
|||
self.t = t
|
||||
|
||||
class UseEvent(GameEvent):
|
||||
def __init__(self, thing):
|
||||
def __init__(self, thing, args):
|
||||
super(UseEvent, self).__init__('use')
|
||||
self.thing = thing
|
||||
self.args = args
|
||||
|
||||
class UseOnEvent(GameEvent):
|
||||
def __init__(self, item, thing):
|
||||
def __init__(self, item, thing, args):
|
||||
super(UseOnEvent, self).__init__('useon')
|
||||
self.thing = thing # thing can be a coordinate pair?
|
||||
self.item = item
|
||||
self.args = args
|
||||
|
||||
class TakeEvent(GameEvent):
|
||||
def __init__(self, item):
|
||||
|
|
53
gamemap.py
53
gamemap.py
|
@ -409,6 +409,29 @@ class MapExit(Thing):
|
|||
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
||||
parts['id'], parts['destination'], prefix, graphic)
|
||||
|
||||
class MapEntrance(Thing):
|
||||
yaml_flag = u'!MapEntrance'
|
||||
# no graphic - should not be drawn
|
||||
|
||||
def __init__(self, x: int, y: int, exitid: int):
|
||||
if prefix:
|
||||
description = "{0} {1}".format(prefix, name)
|
||||
super(MapEntrance, self).__init__('a', name, x, y, description, 1)
|
||||
self.exitid = exitid
|
||||
|
||||
@classmethod
|
||||
def to_yaml(cls, representer, node):
|
||||
# save usual things
|
||||
ret = {'location': (node.x, node.y), 'id': node.exitid}
|
||||
return representer.represent_mapping(cls.yaml_flag, ret)
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, constructor, node):
|
||||
parts = CommentedMap()
|
||||
constructor.construct_mapping(node, parts, True)
|
||||
# set default values for optional arguments
|
||||
return cls(parts['location'][0], parts['location'][1], parts['id'])
|
||||
|
||||
class MapError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
@ -575,7 +598,6 @@ Entering a map through stdin will be obsolete once testing is over."""
|
|||
|
||||
# generate matrix and graph first
|
||||
mapMatrix, mapGraph, dimensions = GameMap.parseMatrix(mat)
|
||||
playerStart = (-1, -1)
|
||||
level = GameMap(infile, mapGraph, mapMatrix, dimensions)
|
||||
|
||||
# Now, load other info
|
||||
|
@ -664,6 +686,7 @@ list of lists of tuples."""
|
|||
level.wallColors.append('#7F3F0F')
|
||||
|
||||
# get things
|
||||
hasKnownEntrance = False
|
||||
if 'loadOnce' in info and not preLoaded:
|
||||
for thing in info['loadOnce']:
|
||||
#print(type(thing))
|
||||
|
@ -672,8 +695,9 @@ list of lists of tuples."""
|
|||
for thing in info['loadAlways']:
|
||||
#print(type(thing))
|
||||
nextThing = level.addThing(thing, nextThing)
|
||||
if thing.thingType == 'x' and prevMap == thing.exitid:
|
||||
if ((thing.thingType == 'x' and not hasKnownEntrance) or thing.thingType == 'a') and prevMap == thing.exitid:
|
||||
level.playerStart = (thing.x, thing.y)
|
||||
hasKnownEntrance = True
|
||||
return nextThing
|
||||
|
||||
# stuff the gameshell itself might use
|
||||
|
@ -686,6 +710,14 @@ list of lists of tuples."""
|
|||
if thing.thingID == -1: # This is to ensure that we don't double up IDs.
|
||||
thing.thingID = nextThing
|
||||
nextThing += 1
|
||||
# Some things, like containers, have other things as custom values,
|
||||
# so they need IDs as well.
|
||||
if thing.thingType in 'iuc':
|
||||
nextThing = self.addThingRecursive(thing.customValues, nextThing)
|
||||
if thing.thingType == 'c':
|
||||
for i in thing.inventory:
|
||||
i.thingID = nextThing
|
||||
nextThing += 1
|
||||
pos = self.coordsToInt(thing.x, thing.y)
|
||||
if pos not in self.thingPos:
|
||||
self.thingPos[pos] = [thing.thingID]
|
||||
|
@ -700,6 +732,21 @@ list of lists of tuples."""
|
|||
self.persistent.append(thing.thingID)
|
||||
return nextThing
|
||||
|
||||
def addThingRecursive(self, container, nextThing = 0):
|
||||
if isinstance(container, Thing):
|
||||
container.thingID = nextThing
|
||||
return nextThing + 1
|
||||
elif isinstance(container, dict):
|
||||
for i in container:
|
||||
nextThing = self.addThingRecursive(container[i], nextThing)
|
||||
return nextThing
|
||||
elif isinstance(container, list):
|
||||
for i in container:
|
||||
nextThing = self.addThingRecursive(i, nextThing)
|
||||
return nextThing
|
||||
else:
|
||||
return nextThing
|
||||
|
||||
def getThing(self, **kwargs):
|
||||
if 'name' in kwargs:
|
||||
return self.getThingByName(kwargs['name'])
|
||||
|
@ -885,7 +932,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
|||
if len(self.thingPos[oldPos]) == 0:
|
||||
del self.thingPos[oldPos]
|
||||
oldName = thing.name
|
||||
if oldName in self.thingPos:
|
||||
if oldName in self.thingNames:
|
||||
self.thingNames[oldName].remove(thing.thingID)
|
||||
if len(self.thingNames[oldName]) == 0:
|
||||
del self.thingNames[oldName]
|
||||
|
|
39
gameshell.py
39
gameshell.py
|
@ -56,6 +56,7 @@ class GameShell(Shell):
|
|||
self.registerAlias('run', ['go', '-r'])
|
||||
self.gameBase.registerIO('container', self.container)
|
||||
self.gameBase.registerIO('dialog', self.dialog)
|
||||
self.gameBase.registerIO('info', self.info)
|
||||
|
||||
# Helper functions
|
||||
|
||||
|
@ -212,6 +213,9 @@ If -l is given, a map legend will be printed under the map."""
|
|||
ret.append("Player name:{0:.>68}".format(self.gameBase.playerName))
|
||||
ret.append("Player position:{0:.>64}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.playerx), self.gameBase.playery)))
|
||||
ret.append("Prev. position:{0:.>65}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.prevx), self.gameBase.prevy)))
|
||||
ret.append("Inventory:")
|
||||
for i in self.gameBase.playerInv:
|
||||
ret.append("{0:<8}: {1}".format(i, self.gameBase.playerInv[i].name))
|
||||
ret.append("Things:\nID Name X Y")
|
||||
for i in self.gameBase.level.things:
|
||||
j = self.gameBase.level.things[i]
|
||||
|
@ -224,7 +228,7 @@ If -l is given, a map legend will be printed under the map."""
|
|||
return
|
||||
|
||||
def inv(self, args):
|
||||
print('\n'.join([i for i in self.gameBase.playerInv]))
|
||||
print('\n'.join([self.gameBase.playerInv[i].name for i in self.gameBase.playerInv]))
|
||||
|
||||
def container(self, inv, cont):
|
||||
"""container IO"""
|
||||
|
@ -236,7 +240,7 @@ If -l is given, a map legend will be printed under the map."""
|
|||
longestLen = len(i)
|
||||
longestStr = i
|
||||
if longestLen > 0:
|
||||
print('{{0:<{0}}}{1}'.format(longestLen+2, "Container:").format("Inv:"))
|
||||
print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:"))
|
||||
i = 0
|
||||
invKeys = tuple(inv.keys())
|
||||
while i < len(invKeys) and i < len(cont):
|
||||
|
@ -264,7 +268,7 @@ If -l is given, a map legend will be printed under the map."""
|
|||
thing = ' '.join(instr[1:])
|
||||
for i in range(len(cont)):
|
||||
if thing == cont[i].name:
|
||||
inv[cont[i].name] = cont[i]
|
||||
inv[cont[i].thingID] = cont[i]
|
||||
del cont[i]
|
||||
timeSpent += 0.5
|
||||
print("{0} taken.".format(thing))
|
||||
|
@ -273,15 +277,36 @@ If -l is given, a map legend will be printed under the map."""
|
|||
# store something in the container
|
||||
if instr[1] == "the":
|
||||
del instr[1]
|
||||
thing = ' '.join(instr[1:])
|
||||
if thing in inv:
|
||||
cont.append(inv[thing])
|
||||
del inv[thing]
|
||||
thingName = ' '.join(instr[1:])
|
||||
for i in inv:
|
||||
thing = inv[i].name
|
||||
if thing == thingName:
|
||||
cont.append(inv[i])
|
||||
del inv[i]
|
||||
print("{0} stored.".format(thing))
|
||||
timeSpent += 0.5
|
||||
break # so that all things with the same name don't get stored
|
||||
instr = input("Take, store, or exit: ")
|
||||
return inv, cont, timeSpent
|
||||
|
||||
def info(self, items):
|
||||
"""IO for collections of information"""
|
||||
charsRead = 0
|
||||
for i in items:
|
||||
print(' ', i)
|
||||
instr = input("Choose an item to view, or exit: ")
|
||||
while instr != 'exit':
|
||||
if instr in items:
|
||||
print(_tw.fill(items[instr], width = TERM_SIZE))
|
||||
charsRead += len(items[instr])
|
||||
else:
|
||||
print('{} not here.'.format(instr))
|
||||
input('<strike RETURN>')
|
||||
for i in items:
|
||||
print(' ', i)
|
||||
instr = input("Choose an item to view, or exit: ")
|
||||
return charsRead / 27 # based on average 250 words per minute, and word length of 5.5 + 1 for space.
|
||||
|
||||
def dialog(self, dialogObj):
|
||||
if 'opener' in dialogObj:
|
||||
print(_tw.fill(dialogObj['opener'], width = TERM_SIZE))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue