Rejiggered players to have a thing in the map, and made the inventory system better.

This commit is contained in:
Patrick Marsee 2019-07-04 18:48:47 -04:00
parent ed7d265b48
commit 5010042430
4 changed files with 347 additions and 287 deletions

View file

@ -42,12 +42,9 @@ class GameBase(object):
self.nextThing = 0 self.nextThing = 0
# player info # player info
self.playerx = -1
self.playery = -1
self.prevx = -1 # prevx and prevy are for moving the player out of the way
self.prevy = -1 # when the tile they're standing on becomes blocked.
self.playerName = 'You' self.playerName = 'You'
self.playerInv = {} self.playerDescription = 'The main character.'
self.player = None # reference to the player's 'thing'
# function deligates # function deligates
self.onLevelLoad = None # str : level name? -> None self.onLevelLoad = None # str : level name? -> None
@ -125,6 +122,8 @@ to get input from stdin."""
def parseCoords(self, args, usePlayerCoords = True, allowInventory = True): def parseCoords(self, args, usePlayerCoords = True, allowInventory = True):
"""Takes an argument list, and figures out what it's talking about. """Takes an argument list, and figures out what it's talking about.
Returns (thing, x, y). "Thing" can be None.""" Returns (thing, x, y). "Thing" can be None."""
if self.level == None:
raise GameError('Coordinates cannot be parsed because there is no map.')
#print(coordStr) #print(coordStr)
x = -1 x = -1
y = -1 y = -1
@ -140,10 +139,8 @@ Returns (thing, x, y). "Thing" can be None."""
# Second, try by name for objects in the inventory # Second, try by name for objects in the inventory
if allowInventory: if allowInventory:
for i in self.playerInv: if name in self.player.thingNames:
thingName = self.playerInv[i].name return self.player.getThingByName(name), -1, -1
if name == thingName:
return self.playerInv[i], -1, -1
# Third, try by location # Third, try by location
coordStr = args[0] coordStr = args[0]
@ -208,14 +205,16 @@ Returns (thing, x, y). "Thing" can be None."""
val = True val = True
elif arg.casefold() == 'false': elif arg.casefold() == 'false':
val = False val = False
elif arg.casefold() == 'playerx': elif arg.casefold() == 'playerx' and self.player != None:
val = self.playerx val = self.player.x
elif arg.casefold() == 'playery': elif arg.casefold() == 'playery' and self.player != None:
val = self.playery val = self.player.y
elif arg.casefold() == 'playername': elif arg.casefold() == 'playername' and self.player != None:
val = self.playerName val = self.player.name
elif arg.casefold() == 'playerinv': elif arg.casefold() == 'playerinv' and self.player != None:
val = self.playerInv val = self.player.inventory
elif arg.casefold() == 'playerdesc' and self.player != None:
val = self.player.description
elif validIdent != None: elif validIdent != None:
group = validIdent.group() group = validIdent.group()
if 'scriptLocal' in self.customValues and group in self.customValues['scriptLocal']: if 'scriptLocal' in self.customValues and group in self.customValues['scriptLocal']:
@ -274,10 +273,7 @@ Returns (thing, x, y). "Thing" can be None."""
return lval > rval return lval > rval
elif operator == 'in': elif operator == 'in':
if args[2].casefold() == 'playerinv': if args[2].casefold() == 'playerinv':
for i in rval: return lval in self.player.thingNames
if rval[i].name == lval:
return True
return False
else: else:
return lval in rval return lval in rval
else: else:
@ -298,6 +294,8 @@ wanted to go to D6, one could say "go to D6", "go to d 6", or "go to 3 6".
The letter is not case-sensitive.""" The letter is not case-sensitive."""
if self.level == None: if self.level == None:
raise GameError("Cannot move: No level has been loaded.") raise GameError("Cannot move: No level has been loaded.")
if self.player == None:
raise GameError("Cannot move: Player character doesn't exist.")
speed = 0.6666667 speed = 0.6666667
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run': if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
speed = 0.3333333 speed = 0.3333333
@ -309,20 +307,20 @@ The letter is not case-sensitive."""
thing, x, y = self.parseCoords(args, allowInventory = False) thing, x, y = self.parseCoords(args, allowInventory = False)
# Now we have a heading! Let's see if we can get there... # Now we have a heading! Let's see if we can get there...
if (x, y) == (self.playerx, self.playery): if (x, y) == (self.player.x, self.player.y):
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0))) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0)))
self.setEvent(0.0, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0)) self.setEvent(0.0, _ge.ArriveEvent(self.player, self.player.x, self.player.y, 0.0))
return return
dist, path = self.level.path(x, y, self.playerx, self.playery) dist, path = self.level.path(x, y, self.player.x, self.player.y)
if dist == -1: if dist == -1:
print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream) print('{0} cannot reach {1}{2}.'.format(self.player.name, self.numberToLetter(x), y), file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
else: else:
pos = self.level.coordsToInt(self.playerx, self.playery) pos = self.level.coordsToInt(self.player.x, self.player.y)
space = path[pos] space = path[pos]
if space == -1: if space == -1:
self.setEvent(0.0, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0)) self.setEvent(0.0, _ge.ArriveEvent(self.player, self.player.x, self.player.y, 0.0))
return return
#target = self.level.coordsToInt(x, y) #target = self.level.coordsToInt(x, y)
t = 1 t = 1
@ -331,13 +329,13 @@ The letter is not case-sensitive."""
newx, newy = self.level.intToCoords(space) newx, newy = self.level.intToCoords(space)
space = path[space] space = path[space]
if space != -1: if space != -1:
self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) self.setEvent(t * speed, _ge.GoEvent(self.player, newx, newy))
else: else:
self.setEvent(t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed)) self.setEvent(t * speed, _ge.ArriveEvent(self.player, newx, newy, t * speed))
break break
t += 1 t += 1
#newx, newy = self.level.intToCoords(space) #newx, newy = self.level.intToCoords(space)
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed))) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.player, newx, newy, t * speed)))
return return
def look(self, args): def look(self, args):
@ -347,6 +345,10 @@ Describe an object.
make more sense from a linguistic perspective (for instance, one could make more sense from a linguistic perspective (for instance, one could
say "look at the balcony" rather than just "look balcony"). say "look at the balcony" rather than just "look balcony").
Object can be the name of the object, or its coordinates.""" Object can be the name of the object, or its coordinates."""
if self.level == None:
raise GameError("Cannot look: No level has been loaded.")
if self.player == None:
raise GameError("Cannot look: Player character doesn't exist.")
if len(args) == 0: if len(args) == 0:
print(self.justifyText(self.level.description), file = self.outstream) print(self.justifyText(self.level.description), file = self.outstream)
else: else:
@ -355,8 +357,8 @@ Object can be the name of the object, or its coordinates."""
if args[0] == 'the': if args[0] == 'the':
args.pop(0) args.pop(0)
thing, x, y = self.parseCoords(args, usePlayerCoords = False) thing, x, y = self.parseCoords(args, usePlayerCoords = False)
if not self.level.lineOfSight(self.playerx, self.playery, x, y): if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
print("{} cannot see that.".format(self.playerName), file = self.outstream) print("{} cannot see that.".format(self.player.name), file = self.outstream)
elif thing == None: elif thing == None:
print("There is nothing to see here.\n", file = self.outstream) print("There is nothing to see here.\n", file = self.outstream)
else: else:
@ -370,6 +372,10 @@ Talk to a character.
make more sense from a linguistic perspective (for instance, one could make more sense from a linguistic perspective (for instance, one could
say "talk to the guard" rather than just "talk guard"). say "talk to the guard" rather than just "talk guard").
Character can be the name of the character, or their coordinates.""" Character can be the name of the character, or their coordinates."""
if self.level == None:
raise GameError("Cannot talk: No level has been loaded.")
if self.player == None:
raise GameError("Cannot talk: Player character doesn't exist.")
if len(args) == 0: if len(args) == 0:
print(self.justifyText(self.level.description), file = self.outstream) print(self.justifyText(self.level.description), file = self.outstream)
else: else:
@ -378,8 +384,8 @@ Character can be the name of the character, or their coordinates."""
if args[0] == 'the': if args[0] == 'the':
args.pop(0) args.pop(0)
thing, x, y = self.parseCoords(args, usePlayerCoords = False, allowInventory = False) thing, x, y = self.parseCoords(args, usePlayerCoords = False, allowInventory = False)
if not self.level.lineOfSight(self.playerx, self.playery, x, y): if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
print("{} cannot talk to {}.".format(self.playerName, thing.name), file = self.outstream) print("{} cannot talk to {}.".format(self.player.name, thing.name), file = self.outstream)
elif thing == None: elif thing == None:
print("There is nobody here.\n", file = self.outstream) print("There is nobody here.\n", file = self.outstream)
else: else:
@ -407,6 +413,10 @@ make more sense from a linguistic perspective (for instance, one could
say "use the lever" rather than just "use lever"). say "use the lever" rather than just "use lever").
Object can be the name of the object, or its coordinates. It can also be 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.""" the name of an item in the player's inventory."""
if self.level == None:
raise GameError("Cannot use: No level has been loaded.")
if self.player == None:
raise GameError("Cannot use: Player character doesn't exist.")
speed = 0.6666667 speed = 0.6666667
useArgs = [] useArgs = []
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run': if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
@ -424,22 +434,22 @@ the name of an item in the player's inventory."""
print("There is nothing to use.", file = self.outstream) print("There is nothing to use.", file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
if thing.thingType != 'u' and thing.thingID not in self.playerInv: if thing.thingType != 'u' and thing not in self.player.inventory:
print("The {0} cannot be used.".format(thing.name), file = self.outstream) print("The {0} cannot be used.".format(thing.name), file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
# Similar to go, but not quite the same. # Similar to go, but not quite the same.
if (x, y) == (self.playerx, self.playery) or thing.thingID in self.playerInv: if (x, y) == (self.player.x, self.player.y) or thing in self.player.inventory:
self.setEvent(0.125, _ge.UseEvent(thing, useArgs)) self.setEvent(0.125, _ge.UseEvent(thing, useArgs))
return return
dist, path = self.level.path(x, y, self.playerx, self.playery) dist, path = self.level.path(x, y, self.player.x, self.player.y)
if dist == -1: if dist == -1:
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) print('{0} cannot reach the {1}.'.format(self.player.name, thing.name), file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
else: else:
pos = self.level.coordsToInt(self.playerx, self.playery) pos = self.level.coordsToInt(self.player.x, self.player.y)
space = path[pos] space = path[pos]
#target = self.level.coordsToInt(x, y) #target = self.level.coordsToInt(x, y)
t = 1 t = 1
@ -447,7 +457,7 @@ the name of an item in the player's inventory."""
while space != -1: while space != -1:
newx, newy = self.level.intToCoords(space) newx, newy = self.level.intToCoords(space)
space = path[space] space = path[space]
self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) self.setEvent(t * speed, _ge.GoEvent(self.player, newx, newy))
t += 1 t += 1
#newx, newy = self.level.intToCoords(space) #newx, newy = self.level.intToCoords(space)
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
@ -464,7 +474,7 @@ the name of an item in the player's inventory."""
useArgs = [] useArgs = []
if 'with' in args: if 'with' in args:
useArgs = args[args.index('with')+1:] useArgs = args[args.index('with')+1:]
if item == None or item.thingID not in self.playerInv: if item == None or item not in self.player.inventory:
print("There is no such item in the inventory.", file = self.outstream) print("There is no such item in the inventory.", file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
@ -473,32 +483,40 @@ the name of an item in the player's inventory."""
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
# Similar to go, but not quite the same. if not item.ranged:
if (x, y) == (self.playerx, self.playery): # Similar to go, but not quite the same.
self.setEvent(0.125, _ge.UseOnEvent(item, thing, useArgs)) if (x, y) == (self.player.x, self.player.y):
return self.setEvent(0.125, _ge.UseOnEvent(item, thing, useArgs))
dist, path = self.level.path(x, y, self.playerx, self.playery) return
if dist == -1: dist, path = self.level.path(x, y, self.player.x, self.player.y)
if thing != None: if dist == -1:
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) if thing != None:
print('{0} cannot reach the {1}.'.format(self.player.name, thing.name), file = self.outstream)
else:
print('{0} cannot reach {1}{2}.'.format(self.player.name, self.numberToLetter(x), y), file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return
else: else:
print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream) pos = self.level.coordsToInt(self.player.x, self.player.y)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) space = path[pos]
return #target = self.level.coordsToInt(x, y)
t = 1
#while space != target:
while space != -1:
newx, newy = self.level.intToCoords(space)
space = path[space]
self.setEvent(t * speed, _ge.GoEvent(self.player, newx, newy))
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, useArgs))
else: else:
pos = self.level.coordsToInt(self.playerx, self.playery) if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
space = path[pos] print("{} cannot use the {} on that from here.".format(self.player.name, item.name), file = self.outstream)
#target = self.level.coordsToInt(x, y) elif thing == None:
t = 1 print("There is nothing to use the {} on here.".format(item.name), file = self.outstream)
#while space != target: else:
while space != -1: self.setEvent(0, _ge.UseOnEvent(item, thing, useArgs))
newx, newy = self.level.intToCoords(space)
space = path[space]
self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy))
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, useArgs))
return return
def take(self, args): def take(self, args):
@ -509,6 +527,10 @@ If the player is not already close to it, they will go to it.
make more sense from a linguistic perspective (for instance, one could make more sense from a linguistic perspective (for instance, one could
say "take the flask" rather than just "take flask"). say "take the flask" rather than just "take flask").
Object can be the name of the object, or its coordinates.""" Object can be the name of the object, or its coordinates."""
if self.level == None:
raise GameError("Cannot take: No level has been loaded.")
if self.player == None:
raise GameError("Cannot take: Player character doesn't exist.")
speed = 0.6666667 speed = 0.6666667
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run': if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
speed = 0.3333333 speed = 0.3333333
@ -525,16 +547,16 @@ Object can be the name of the object, or its coordinates."""
return return
# Similar to go, but not quite the same. # Similar to go, but not quite the same.
if (x, y) == (self.playerx, self.playery): if (x, y) == (self.player.x, self.player.y):
self.setEvent(0.125, _ge.TakeEvent(thing)) self.setEvent(0.125, _ge.TakeEvent(self.player, thing))
return return
dist, path = self.level.path(x, y, self.playerx, self.playery) dist, path = self.level.path(x, y, self.player.x, self.player.y)
if dist == -1: if dist == -1:
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream) print('{0} cannot reach the {1}.'.format(self.player.name, thing.name), file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
else: else:
pos = self.level.coordsToInt(self.playerx, self.playery) pos = self.level.coordsToInt(self.player.x, self.player.y)
space = path[pos] space = path[pos]
#target = self.level.coordsToInt(x, y) #target = self.level.coordsToInt(x, y)
t = 1 t = 1
@ -542,23 +564,31 @@ Object can be the name of the object, or its coordinates."""
while space != -1: while space != -1:
newx, newy = self.level.intToCoords(space) newx, newy = self.level.intToCoords(space)
space = path[space] space = path[space]
self.setEvent(t * speed, _ge.GoEvent(self.playerName, newx, newy)) self.setEvent(t * speed, _ge.GoEvent(self.player, newx, newy))
t += 1 t += 1
#newx, newy = self.level.intToCoords(space) #newx, newy = self.level.intToCoords(space)
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy))) #_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.player.name, newx, newy)))
self.setEvent(t * speed + 0.125, _ge.TakeEvent(thing)) self.setEvent(t * speed + 0.125, _ge.TakeEvent(self.player, thing))
return return
def drop(self, args): def drop(self, args):
"""drop [the] item""" """drop [the] item"""
if self.level == None:
raise GameError("Cannot drop: No level has been loaded.")
if self.player == None:
raise GameError("Cannot drop: Player character doesn't exist.")
if args[0] == 'the': if args[0] == 'the':
args.pop(0) args.pop(0)
for i in self.playerInv: #for i in self.player.inventory:
thingName = self.playerInv[i].name # thingName = self.player.inventory[i].name
if ' '.join(args) == thingName: # if ' '.join(args) == thingName:
self.setEvent(0.0, _ge.DropEvent(self.playerInv[i])) # self.setEvent(0.0, _ge.DropEvent(self.player.inventory[i]))
return # return
print('{0} does not have a {1}.'.format(self.playerName, args[0]), file = self.outstream) thingName = ' '.join(args)
if thingName in self.player.thingNames:
self.setEvent(0.0, _ge.DropEvent(self.player.getThingByName(thingName)))
else:
print('{0} does not have a {1}.'.format(self.player.name, args[0]), file = self.outstream)
def loadMap(self, args): def loadMap(self, args):
# loadMap (fileName) # loadMap (fileName)
@ -593,15 +623,27 @@ Object can be the name of the object, or its coordinates."""
print(self.level.openingText, file = self.outstream) print(self.level.openingText, file = self.outstream)
#print(self.outstream.getvalue()) #print(self.outstream.getvalue())
if len(args) <= 2: x, y = self.level.playerStart
self.playerx, self.playery = self.level.playerStart if len(args) == 3:
x, y = int(args[1]), int(args[2])
if self.player == None:
# create a player to put in the level.
self.player = _gm.PlayerCharacter(x, y, self.playerDescription, {}, {}, self.playerName)
print("Player created.")
else: else:
self.playerx, self.playery = int(args[1]), int(args[2]) self.player.x, self.player.y = x, y
self.prevx, self.prevy = self.playerx, self.playery print("Player moved.")
self.player.prevx, self.player.prevy = self.player.x, self.player.y
self.nextThing = self.level.addThing(self.player, self.nextThing) # The player needs to be added to the new level.
if self.onLevelLoad != None: if self.onLevelLoad != None:
self.onLevelLoad() self.onLevelLoad()
self.parseScript(self.level.enterScript) self.parseScript(self.level.enterScript)
def unloadMap(self, args):
"""Args is only there just in case."""
self.level = None
self.player = None
def saveGame(self, args): def saveGame(self, args):
if len(args) < 1: if len(args) < 1:
print("Save file must have a name!", file = self.outstream) print("Save file must have a name!", file = self.outstream)
@ -619,7 +661,7 @@ Object can be the name of the object, or its coordinates."""
self.persist[self.level.name][i] = self.level.getThingByID(i) self.persist[self.level.name][i] = self.level.getThingByID(i)
# build data object to be saved # build data object to be saved
data = (self.playerName, self.playerx, self.playery, self.playerInv, self.level.name, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing) data = (self.player, self.level.name, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing)
# save it! # save it!
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat' fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
@ -650,9 +692,9 @@ Object can be the name of the object, or its coordinates."""
fileName = args[0] fileName = args[0]
x, y, levelname = 1, 1, 'testing/test1.txt' x, y, levelname = 1, 1, 'testing/test1.txt'
with open(fileName, 'rb') as f: with open(fileName, 'rb') as f:
self.playerName, x, y, self.playerInv, levelname, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing = _pi.load(f) self.player, levelname, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing = _pi.load(f)
#print(levelname, x, y, file = self.outstream) #print(levelname, x, y, file = self.outstream)
self.loadMap((levelname, x, y)) self.loadMap((levelname, self.player.x, self.player.y))
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent())) #_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return return
@ -788,41 +830,59 @@ Object can be the name of the object, or its coordinates."""
return True return True
def handleGo(self, e): def handleGo(self, e):
if e.actor == self.playerName: #if e.actor == self.playerName:
self.prevx, self.prevy = self.playerx, self.playery # self.prevx, self.prevy = self.playerx, self.playery
self.playerx, self.playery = e.x, e.y # self.playerx, self.playery = e.x, e.y
else: #else:
actor = self.level.getThingByName(e.actor) if e.actor == None:
self.level.moveThing(actor, e.x, e.y) raise GameError("'Go' event raised for no object.")
self.level.moveThing(e.actor, e.x, e.y)
return False return False
def handleArrive(self, e): def handleArrive(self, e):
if e.actor == self.playerName: #if e.actor == self.playerName:
self.prevx, self.prevy = self.playerx, self.playery # self.prevx, self.prevy = self.playerx, self.playery
self.playerx, self.playery = e.x, e.y # self.playerx, self.playery = e.x, e.y
print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(self.playerName, self.numberToLetter(e.x), e.y, e.t), file = self.outstream) # print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(self.playerName, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
thing = self.level.getThingAtCoords(self.playerx, self.playery) # thing = self.level.getThingAtCoords(self.playerx, self.playery)
if thing: # if thing:
if thing.thingType == 'x': # if thing.thingType == 'x':
# self.parseScript(thing.onUse)
# if (isinstance(thing.key, bool) and thing.key == True) or (isinstance(thing.key, str) and self.parseScript(thing.key)):
# a = self.requestInput('Do you want to go {0}? (Y/n)'.format(str(thing)))
# if a != 'n' and a != 'N':
# self.loadMap((thing.destination, thing.exitid))
# return True
#else:
# actor = self.level.getThingByName(e.actor)
# if actor:
# self.level.moveThing(e.actor, e.x, e.y)
# print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(actor.name, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
# thing = self.level.getThingAtCoords(actor.x, actor.y)
# if thing and thing != actor:
# if thing.thingType == 'x':
# print('{0} went {1}.'.format(actor.name, str(thing)))
# self.level.removeThing(actor.name)
# else:
# print('There is nothing by the name of {0}.'.format(e.actor), file = self.outstream)
# return False
if e.actor == None:
raise GameError("'Go' event raised for no object.")
thing = self.level.getThingAtCoords(e.x, e.y)
self.level.moveThing(e.actor, e.x, e.y)
print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(e.actor.name, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
if thing:
if thing.thingType == 'x':
if e.actor == self.player:
self.parseScript(thing.onUse) self.parseScript(thing.onUse)
if (isinstance(thing.key, bool) and thing.key == True) or (isinstance(thing.key, str) and self.parseScript(thing.key)): if (isinstance(thing.key, bool) and thing.key == True) or (isinstance(thing.key, str) and self.parseScript(thing.key)):
a = self.requestInput('Do you want to go {0}? (Y/n)'.format(str(thing))) a = self.requestInput('Do you want to go {0}? (Y/n)'.format(str(thing)))
if a != 'n' and a != 'N': if a != 'n' and a != 'N':
self.loadMap((thing.destination, thing.exitid)) self.loadMap((thing.destination, thing.exitid))
return True else:
else: print('{0} went {1}.'.format(actor.name, str(thing)))
actor = self.level.getThingByName(e.actor) self.level.removeThing(actor.name)
if actor: return True
self.level.moveThing(e.actor, e.x, e.y)
print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(actor.name, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
thing = self.level.getThingAtCoords(actor.x, actor.y)
if thing and thing != actor:
if thing.thingType == 'x':
print('{0} went {1}.'.format(actor.name, str(thing)))
self.level.removeThing(actor.name)
else:
print('There is nothing by the name of {0}.'.format(e.actor), file = self.outstream)
return False
def handleUse(self, e): def handleUse(self, e):
if e.thing.useFunc == '': if e.thing.useFunc == '':
@ -839,15 +899,19 @@ Object can be the name of the object, or its coordinates."""
return False return False
def handleTake(self, e): def handleTake(self, e):
if e.actor == None or e.actor.thingType not in 'np' or e.item == None:
raise GameError("'Take' event cannot be handled.")
self.level.removeThingByID(e.item.thingID) self.level.removeThingByID(e.item.thingID)
self.playerInv[e.item.thingID] = e.item e.actor.addThing(e.item)
return True return True
def handleDrop(self, e): def handleDrop(self, e):
e.item.x = self.playerx if e.actor == None or e.actor.thingType not in 'np':
e.item.y = self.playery raise GameError("'Drop' event cannot be handled.")
e.item.x = e.actor.x
e.item.y = e.actor.y
self.nextThing = self.level.addThing(e.item, self.nextThing, True) # nextThing shouldn't change self.nextThing = self.level.addThing(e.item, self.nextThing, True) # nextThing shouldn't change
del self.playerInv[e.item.thingID] self.actor.removeThing(e.item)
return True return True
def handleBehave(self, e): def handleBehave(self, e):
@ -890,7 +954,7 @@ Object can be the name of the object, or its coordinates."""
def container(self, thing, args): def container(self, thing, args):
"""Acts as a container. Items can be traded between the container and the player's inventory.""" """Acts as a container. Items can be traded between the container and the player's inventory."""
items = list(thing.customValues['items']) items = list(thing.customValues['items'])
self.playerInv, thing.customValues['items'], timeOpen = self.getIO('container')(self.playerInv, items) thing.customValues['items'], timeOpen = self.getIO('container')(self.player, items)
return timeOpen return timeOpen
def info(self, thing, args): def info(self, thing, args):
@ -901,31 +965,24 @@ Object can be the name of the object, or its coordinates."""
def wear(self, item, args): def wear(self, item, args):
"""Wear clotherg or otherwise equip a passive item.""" """Wear clotherg or otherwise equip a passive item."""
# An item must be in the player's inventory in order to wear it. # An item must be in the player's inventory in order to wear it.
inInv = False inInv = item in self.player.inventory
for i in self.playerInv:
if i == item.thingID:
inInv = True
break
if not inInv: if not inInv:
print("You cannot wear what you are not carrying.", file = self.outstream) print("You cannot wear what you are not carrying.", file = self.outstream)
return 0.0 return 0.0
if 'wearing' not in self.customValues: if 'wearing' not in self.customValues:
self.customValues['wearing'] = {} self.customValues['wearing'] = {}
if item.customValues['slot'] not in self.customValues['wearing']: if item.customValues['slot'] not in self.customValues['wearing']:
self.customValues['wearing'][item.customValues['slot']] = item self.customValues['wearing'][item.customValues['slot']] = self.player.removeThing(item)
# This is so a player can't put on a garment, then drop it while # This is so a player can't put on a garment, then drop it while
# still also wearing it. # still also wearing it.
del self.playerInv[item.thingID] print("{} put on the {}.".format(self.player.name, item.name), file = self.outstream)
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 else: # the player must be wearing something that will get in the way
# put the old clothes back in the inventory # put the old clothes back in the inventory
oldItem = self.customValues['wearing'][item.customValues['slot']] self.player.addThing(self.customValues['wearing'][item.customValues['slot']])
self.playerInv[oldItem.thingID] = oldItem self.customValues['wearing'][item.customValues['slot']] = self.player.removeThing(item)
self.customValues['wearing'][item.customValues['slot']] = item
# This is so a player can't put on a garment, then drop it while # This is so a player can't put on a garment, then drop it while
# still also wearing it. # still also wearing it.
del self.playerInv[item.thingID] print("{} took off the {} and put on the {}.".format(self.player.name, oldItem.name, item.name), file = self.outstream)
print("{} took off the {} and put on the {}.".format(self.playerName, oldItem.name, item.name), file = self.outstream)
return 1.0 return 1.0
# item use-on functions # item use-on functions
@ -937,8 +994,8 @@ Object can be the name of the object, or its coordinates."""
return 0.0 return 0.0
if thing.lock(item.name): if thing.lock(item.name):
print("The key fits the lock.", file = self.outstream) print("The key fits the lock.", file = self.outstream)
if not thing.passable and self.playerx == thing.x and self.playery == thing.y: if not thing.passable and self.player.x == thing.x and self.player.y == thing.y:
self.playerx, self.playery = self.prevx, self.prevy self.player.x, self.player.y = self.player.prevx, self.player.prevy
else: else:
print("The key doesn't fit that lock.", file = self.outstream) print("The key doesn't fit that lock.", file = self.outstream)
return 0.125 return 0.125
@ -1191,7 +1248,7 @@ Object can be the name of the object, or its coordinates."""
equalsign = i.index('=') equalsign = i.index('=')
cv = i[3:equalsign] cv = i[3:equalsign]
customValues[cv] = self.getValueFromString(i[equalsign+1:]) customValues[cv] = self.getValueFromString(i[equalsign+1:])
thing = _gm.NPC(name, x, y, description, behavior, inv, customValues, playerx, playery, False, (bgc, fgc, shape)) thing = _gm.NPC(name, x, y, description, behavior, inv, customValues, playerx, playery, (bgc, fgc, shape))
elif args[0].casefold() == 'door': elif args[0].casefold() == 'door':
# spawn a door # spawn a door
description = 'a nondescript door.' description = 'a nondescript door.'
@ -1303,7 +1360,7 @@ Object can be the name of the object, or its coordinates."""
customValues[cv] = getValueFromString(i[equalsign+1:]) customValues[cv] = getValueFromString(i[equalsign+1:])
thing = _gm.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape)) thing = _gm.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
thing.thingID = self.nextThing thing.thingID = self.nextThing
self.playerInv[thing.thingID] = thing self.player.addThing(thing)
self.nextThing += 1 self.nextThing += 1
return thing return thing

View file

@ -92,13 +92,15 @@ class UseOnEvent(GameEvent):
self.args = args self.args = args
class TakeEvent(GameEvent): class TakeEvent(GameEvent):
def __init__(self, item): def __init__(self, actor, item):
super(TakeEvent, self).__init__('take') super(TakeEvent, self).__init__('take')
self.actor = actor
self.item = item self.item = item
class DropEvent(GameEvent): class DropEvent(GameEvent):
def __init__(self, item): def __init__(self, actor, item):
super(DropEvent, self).__init__('drop') super(DropEvent, self).__init__('drop')
self.actor = actor
self.item = item self.item = item
class BehaveEvent(GameEvent): class BehaveEvent(GameEvent):

View file

@ -16,6 +16,8 @@ class Thing(object):
self.y = y self.y = y
self.playerx = x self.playerx = x
self.playery = y self.playery = y
self.prevx = x # if an area gets double-occupied, a thing can get pushed back.
self.prevy = y
if playerx: if playerx:
self.playerx = playerx self.playerx = playerx
if playery: if playery:
@ -179,27 +181,84 @@ class Useable(Thing):
return cls(parts['name'], parts['location'][0], parts['location'][1], return cls(parts['name'], parts['location'][0], parts['location'][1],
parts['description'], useFunc, customValues, playerx, playery, graphic) parts['description'], useFunc, customValues, playerx, playery, graphic)
class NPC(Thing): class Character(Thing):
defaultGraphic = ('clear', '#000000', 'o')
def __init__(self, thingType: str, name: str, x: int, y: int,
description: str, inventory: dict, customValues: dict,
flags: int, playerx = None, playery = None, graphic = defaultGraphic):
super(Character, self).__init__(thingType, name, x, y, description, flags)
if inventory == None:
inventory = {} # create a new dict for the inventory.
# This couldn't be in the NPC constructor because
# then all characters would share a reference to
# the same empty inventory.
self.__inventory = inventory
self.customValues = customValues
self.graphic = graphic
self.thingNames = {}
# set up inventory shtuff
for i in self.__inventory:
if self.__inventory[i].name in self.thingNames:
self.thingNames[self.__inventory[i].name].append(i)
else:
self.thingNames[self.__inventory[i].name] = [i]
def addThing(self, thing):
if not isinstance(thing, Item):
raise TypeError("Only items can be added to a character's inventory.")
self.__inventory[thing.thingID] = thing
if thing.name in self.thingNames:
self.thingNames[thing.name].append(thing.thingID)
else:
self.thingNames[thing.name] = [thing.thingID]
def getThingByID(self, thingID):
return self.__inventory[thingID]
def getThingByName(self, name):
if name in self.thingNames:
return self.__inventory[self.thingNames[name][0]]
else:
return None
def removeThingByID(self, thingID):
ret = self.__inventory[thingID]
self.thingNames[ret.name].remove(thingID)
if len(self.thingNames[ret.name]) == 0:
del self.thingNames[ret.name]
del self.__inventory[thingID]
return ret
def removeThingByName(self, name):
ret = self.getThingByName(name)
self.thingNames[ret.name].remove(thingID)
if len(self.thingNames[ret.name]) == 0:
del self.thingNames[ret.name]
del self.__inventory[thingID]
return ret
def removeThing(self, ret):
self.thingNames[ret.name].remove(ret.thingID)
if len(self.thingNames[ret.name]) == 0:
del self.thingNames[ret.name]
del self.__inventory[ret.thingID]
return ret
@property
def inventory(self):
"""Get the inventory as a list."""
return list(self.__inventory.values())
class NPC(Character):
yaml_flag = u'!NPC' yaml_flag = u'!NPC'
defaultGraphic = ('clear', '#000000', 'o') defaultGraphic = ('clear', '#000000', 'o')
def __init__(self, name, x: int, y: int, description: str, behavior: str, inventory: list, customValues: dict, playerx = None, playery = None, following = False, graphic = defaultGraphic): def __init__(self, name, x: int, y: int, description: str, behavior: str, inventory: list, customValues: dict, playerx = None, playery = None, graphic = defaultGraphic):
super(NPC, self).__init__('c', name, x, y, description, 6, playerx, playery) super(NPC, self).__init__('n', name, x, y, description, None, customValues, 6, playerx, playery, graphic)
self.behavior = behavior self.behavior = behavior
self.following = following
self.inventory = inventory
self.customValues = customValues
self.graphic = graphic
self.behaveEvent = None self.behaveEvent = None
self.tempInventory = inventory # should be deleted once NPC is loaded
def give(self, item):
self.inventory.append(item)
def drop(self, index):
return self.inventory.pop(index)
def dialog(self):
pass
@classmethod @classmethod
def to_yaml(cls, representer, node): def to_yaml(cls, representer, node):
@ -217,8 +276,6 @@ class NPC(Thing):
if len(graphic) > 0: if len(graphic) > 0:
ret['graphic'] = graphic ret['graphic'] = graphic
# save use functions # save use functions
if node.following:
ret['following'] = node.following
if len(node.inventory) > 0: if len(node.inventory) > 0:
ret['inventory'] = node.inventory ret['inventory'] = node.inventory
if len(node.customValues) > 0: if len(node.customValues) > 0:
@ -249,8 +306,6 @@ class NPC(Thing):
shape = parts['graphic']['shape'] shape = parts['graphic']['shape']
graphic = (bgc, fgc, shape) graphic = (bgc, fgc, shape)
# load use functions # load use functions
if 'following' in parts:
useFunc = parts['following']
if 'inventory' in parts: if 'inventory' in parts:
inventory = parts['inventory'] inventory = parts['inventory']
if 'customValues' in parts: if 'customValues' in parts:
@ -262,7 +317,7 @@ class NPC(Thing):
playerx, playery = parts['useLocation'] playerx, playery = parts['useLocation']
return cls(parts['name'], parts['location'][0], parts['location'][1], return cls(parts['name'], parts['location'][0], parts['location'][1],
parts['description'], parts['behavior'], inventory, customValues, parts['description'], parts['behavior'], inventory, customValues,
playerx, playery, following, graphic) playerx, playery, graphic)
class Door(Thing): class Door(Thing):
yaml_flag = u'!Door' yaml_flag = u'!Door'
@ -445,6 +500,13 @@ class MapEntrance(Thing):
# set default values for optional arguments # set default values for optional arguments
return cls(parts['location'][0], parts['location'][1], parts['id']) return cls(parts['location'][0], parts['location'][1], parts['id'])
class PlayerCharacter(Character):
"""Player object. Cannot be created with yaml."""
defaultGraphic = ('clear', '#0000FF', 'o')
def __init__(self, x: int, y: int, description: str, inventory: dict, customValues: dict, name = 'You', graphic = defaultGraphic):
super(PlayerCharacter, self).__init__('p', name, x, y, description, inventory, customValues, 5, graphic=graphic)
class MapError(RuntimeError): class MapError(RuntimeError):
pass pass
@ -493,86 +555,6 @@ class GameMap(object):
break break
return text.replace('\n', end) return text.replace('\n', end)
@staticmethod
def convert(infile: str):
"""Convert an XML map to a YAML one."""
data = None
with open(infile, 'r') as f:
data = f.read()
info = ET.fromstring(data)
layout = info.find('layout')
ret = {}
if layout == None:
raise MapError('No layout in {0}.'.format(infile))
# "Layout" needs some work before we convert it.
ret['layout'] = GameMap.__cleanStr(layout.text.strip())
# now the rest of the things
if 'openingText' in info.attrib:
ret['openingText'] = info.attrib['openingText']
else:
raise MapError('No opening text in {0}.'.format(infile))
if 'playerStart' in info.attrib:
ps = info.attrib['playerStart'].split(',')
ret['playerStart'] = (int(ps[0]), int(ps[1]))
else:
raise MapError('No player start position in {0}.'.format(infile))
if info.text != None:
ret['description'] = GameMap.__cleanStr(info.text, ' ')
else:
raise MapError('No description in {0}.'.format(infile))
# get map colors
floorColors = ['#9F7F5F']
floorColorsStr = info.find('floorColors')
if floorColorsStr != None:
floorColors = floorColorsStr.text.lstrip().split()
if len(floorColors) == 0:
floorColors.append('#9F7F5F')
ret['floorColors'] = floorColors
wallColors = ['#7F3F0F']
wallColorsStr = info.find('wallColors')
if wallColorsStr != None:
wallColors = wallColorsStr.text.lstrip().split()
if len(wallColors) == 0:
wallColors.append('#7F3F0F')
ret['wallColors'] = wallColors
# get things
ret['loadAlways'] = []
ret['loadOnce'] = []
for node in info:
if node.tag == 'loadOnce':
# Things in the load-once section are only loaded the first
# time that the map is loaded, and saved in the player's
# save file.
for node1 in node:
if node1.tag == 'door':
ret['loadOnce'].append(GameMap.__loadDoor(node1))
elif node1.tag == 'useable':
ret['loadOnce'].append(GameMap.__loadUseable(node1))
elif node1.tag == 'item':
ret['loadOnce'].append(GameMap.__loadItem(node1))
elif node.tag == 'exit':
ret['loadAlways'].append(GameMap.__loadExit(None, node, -1)) # weird arguments: there is no actual level
elif node.tag == 'door':
ret['loadAlways'].append(GameMap.__loadDoor(node))
elif node.tag == 'useable':
ret['loadAlways'].append(GameMap.__loadUseable(node))
#start saving
outfile = infile[:-3] + 'yml'
yaml = ruamel.yaml.YAML()
yaml.indent(mapping=4, sequence=4, offset=2)
yaml.register_class(Item)
yaml.register_class(Useable)
yaml.register_class(Door)
yaml.register_class(MapExit)
with open(outfile, 'w') as f:
f.write('%YAML 1.2\n---\n')
yaml.dump(ret, f)
@staticmethod @staticmethod
def read(infile = None, prevMap = None, preLoaded = False, nextThing = 0): def read(infile = None, prevMap = None, preLoaded = False, nextThing = 0):
"""Read map data and return a Map object. If infile is not provided, then """Read map data and return a Map object. If infile is not provided, then
@ -729,12 +711,15 @@ list of lists of tuples."""
nextThing += 1 nextThing += 1
# Some things, like containers, have other things as custom values, # Some things, like containers, have other things as custom values,
# so they need IDs as well. # so they need IDs as well.
if thing.thingType in 'iuc': if thing.thingType in 'iun':
nextThing = self.addThingRecursive(thing.customValues, nextThing) nextThing = self.addThingRecursive(thing.customValues, nextThing)
if thing.thingType == 'c': if thing.thingType == 'n':
for i in thing.inventory: for i in thing.tempInventory:
i.thingID = nextThing if i.thingID == -1:
nextThing += 1 i.thingID = nextThing
nextThing = self.addThingRecursive(i.customValues, nextThing + 1)
thing.addThing(i)
del thing.tempInventory
pos = self.coordsToInt(thing.x, thing.y) pos = self.coordsToInt(thing.x, thing.y)
if pos not in self.thingPos: if pos not in self.thingPos:
self.thingPos[pos] = [thing.thingID] self.thingPos[pos] = [thing.thingID]
@ -751,8 +736,12 @@ list of lists of tuples."""
def addThingRecursive(self, container, nextThing = 0): def addThingRecursive(self, container, nextThing = 0):
if isinstance(container, Thing): if isinstance(container, Thing):
container.thingID = nextThing if container.thingID == -1:
return nextThing + 1 container.thingID = nextThing
nextThing = self.addThingRecursive(container.customValues, nextThing)
return nextThing + 1
else:
return nextThing
elif isinstance(container, dict): elif isinstance(container, dict):
for i in container: for i in container:
nextThing = self.addThingRecursive(container[i], nextThing) nextThing = self.addThingRecursive(container[i], nextThing)
@ -838,6 +827,10 @@ The closeEnough parameter will create a path that lands beside the source if nec
# return -1, [] # meaning you can't get there # return -1, [] # meaning you can't get there
def lineOfSight(self, x1, y1, x2, y2): def lineOfSight(self, x1, y1, x2, y2):
"""Test for line of signt from one tile to another."""
# Trivial case first:
if abs(x1 - x2) <= 1 and abs(y1 - y2) <= 1:
return True
Dx = x2 - x1 Dx = x2 - x1
Dy = y2 - y1 Dy = y2 - y1
y = y1 + 0.5 y = y1 + 0.5
@ -913,10 +906,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
def getThingsAtPos(self, pos): def getThingsAtPos(self, pos):
if pos in self.thingPos: if pos in self.thingPos:
ret = [] return [self.things[i] for i in self.thingPos[pos]]
for i in self.thingPos[pos]:
ret.append(self.things[i])
return ret
else: else:
return [] return []
@ -1019,6 +1009,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
else: else:
self.thingPos[newPos].append(thing.thingID) self.thingPos[newPos].append(thing.thingID)
relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y
thing.prevx, thing.prevy = thing.x, thing.y
thing.x, thing.y = x, y thing.x, thing.y = x, y
thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery
else: else:

View file

@ -46,7 +46,6 @@ class GameShell(Shell):
self.gameBase.registerIO('dialog', self.dialog) self.gameBase.registerIO('dialog', self.dialog)
self.gameBase.registerIO('info', self.info) self.gameBase.registerIO('info', self.info)
self.gameBase.registerIO('playercmd', self.playercmd) self.gameBase.registerIO('playercmd', self.playercmd)
self.menuMode()
# Helper functions # Helper functions
@ -148,25 +147,33 @@ If -l is given, a map legend will be printed under the map."""
level = self.gameBase.level level = self.gameBase.level
textColor = level.wallColors[0] textColor = level.wallColors[0]
floorColor = level.floorColors[0] floorColor = level.floorColors[0]
priorities = {'p': 1, 'n': 2, 'i': 3, 'u': 4, 'd': 5, 'x': 6, 'a': 7}
for y in range(level.dimensions[1]): for y in range(level.dimensions[1]):
rows.append(['{0}{1:2} {2}{3}'.format(self.clearColor(), y, self.color(textColor[1:]), self.color(floorColor[1:], fg = False))]) rows.append(['{0}{1:2} {2}{3}'.format(self.clearColor(), y, self.color(textColor[1:]), self.color(floorColor[1:], fg = False))])
rows[-1].append(self.color(textColor[1:])) rows[-1].append(self.color(textColor[1:]))
for x in range(level.dimensions[0]): for x in range(level.dimensions[0]):
pos = level.mapMatrix[y][x] pos = level.mapMatrix[y][x]
thing = level.getThingAtPos(index) things = level.getThingsAtPos(index)
if x == self.gameBase.playerx and y == self.gameBase.playery: #if x == self.gameBase.playerx and y == self.gameBase.playery:
if '#0000FF' != textColor: # if self.gameBase.player != textColor:
textColor = '#0000FF' # textColor = '#0000FF'
rows[-1].append(self.color(textColor[1:])) # rows[-1].append(self.color(textColor[1:]))
rows[-1].append('()') # rows[-1].append('()')
elif thing: if len(things) > 0:
# Prioritize types: p, n, i, u, d, x, a
thing = things[0]
for i in things[1:]:
if priorities[i.thingType] < priorities[thing.thingType]:
thing = i
if thing.graphic[1] != textColor: if thing.graphic[1] != textColor:
textColor = thing.graphic[1] textColor = thing.graphic[1]
rows[-1].append(self.color(textColor[1:])) rows[-1].append(self.color(textColor[1:]))
if thing.thingType == 'x': # exit if thing.thingType == 'p': # player
rows[-1].append('()')
elif thing.thingType == 'x': # exit
rows[-1].append('X{0}'.format(thing.exitid)) rows[-1].append('X{0}'.format(thing.exitid))
exits[thing.exitid] = (thing.name, thing.graphic[1]) exits[thing.exitid] = (thing.name, thing.graphic[1])
elif thing.thingType == 'c': # useable elif thing.thingType == 'n': # NPC
characters[len(characters)+1] = (thing.name, thing.graphic[1]) characters[len(characters)+1] = (thing.name, thing.graphic[1])
rows[-1].append('C{0}'.format(len(characters))) rows[-1].append('C{0}'.format(len(characters)))
elif thing.thingType == 'd': # door elif thing.thingType == 'd': # door
@ -178,7 +185,7 @@ If -l is given, a map legend will be printed under the map."""
elif thing.thingType == 'i': # item elif thing.thingType == 'i': # item
items[len(items)+1] = (thing.name, thing.graphic[1]) items[len(items)+1] = (thing.name, thing.graphic[1])
rows[-1].append('I{0}'.format(len(items))) rows[-1].append('I{0}'.format(len(items)))
elif thing.thingType == 'a': # entrance else: # entrance
rows[-1].append(' ') rows[-1].append(' ')
elif pos[0] == 'w': elif pos[0] == 'w':
if level.wallColors[level.mapMatrix[y][x][1]] != textColor: if level.wallColors[level.mapMatrix[y][x][1]] != textColor:
@ -245,12 +252,12 @@ If -l is given, a map legend will be printed under the map."""
ret.append("custom values:") ret.append("custom values:")
for i in self.gameBase.customValues: for i in self.gameBase.customValues:
ret.append("{0:<22}: {1}".format(i, self.gameBase.customValues[i])) ret.append("{0:<22}: {1}".format(i, self.gameBase.customValues[i]))
ret.append("Player name:{0:.>68}".format(self.gameBase.playerName)) ret.append("Player name:{0:.>68}".format(self.gameBase.player.name))
ret.append("Player position:{0:.>64}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.playerx), self.gameBase.playery))) ret.append("Player position:{0:.>64}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.player.x), self.gameBase.player.y)))
ret.append("Prev. position:{0:.>65}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.prevx), self.gameBase.prevy))) ret.append("Prev. position:{0:.>65}".format("{0}{1}".format(self.gameBase.numberToLetter(self.gameBase.player.prevx), self.gameBase.player.prevy)))
ret.append("Inventory:") ret.append("Inventory:")
for i in self.gameBase.playerInv: for i in self.gameBase.player.inventory:
ret.append("{0:<8}: {1}".format(i, self.gameBase.playerInv[i].name)) ret.append("{0:<8}: {1}".format(i.thingID, i.name))
ret.append("Things:\nID Name X Y") ret.append("Things:\nID Name X Y")
for i in self.gameBase.level.things: for i in self.gameBase.level.things:
j = self.gameBase.level.things[i] j = self.gameBase.level.things[i]
@ -263,7 +270,7 @@ If -l is given, a map legend will be printed under the map."""
return return
def inv(self, args): def inv(self, args):
print('\n'.join([self.gameBase.playerInv[i].name for i in self.gameBase.playerInv])) print('\n'.join([i for i in self.gameBase.player.thingNames]))
def newGame(self, args): def newGame(self, args):
if self.__inGame: if self.__inGame:
@ -351,22 +358,23 @@ If -l is given, a map legend will be printed under the map."""
# IO calls # IO calls
def container(self, inv, cont): def container(self, player, cont):
"""container IO""" """container IO
Player is modified through side-effect."""
# Pretty print: get length of the longest inventory item's name # Pretty print: get length of the longest inventory item's name
longestLen = 0 longestLen = 0
for i in inv: for i in player.thingNames:
if len(inv[i].name) > longestLen: if len(i) > longestLen:
longestLen = len(inv[i].name) longestLen = len(i)
if longestLen > 0: if longestLen > 0:
inv = player.inventory # do this assignment because player.inventory is O(n)
print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:")) print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:"))
i = 0 i = 0
invKeys = tuple(inv.keys()) while i < len(inv) and i < len(cont):
while i < len(invKeys) and i < len(cont): print('{{0:<{0}}}{1}'.format(longestLen+2, cont[i].name).format(inv[i].name))
print('{{0:<{0}}}{1}'.format(longestLen+2, cont[i].name).format(inv[invKeys[i]].name))
i += 1 i += 1
while i < len(invKeys): while i < len(inv):
print(inv[invKeys[i]].name) print(inv[i].name)
i += 1 i += 1
while i < len(cont): while i < len(cont):
print(' '*(longestLen+2) + cont[i].name) print(' '*(longestLen+2) + cont[i].name)
@ -387,26 +395,27 @@ If -l is given, a map legend will be printed under the map."""
thing = ' '.join(instr[1:]) thing = ' '.join(instr[1:])
for i in range(len(cont)): for i in range(len(cont)):
if thing == cont[i].name: if thing == cont[i].name:
inv[cont[i].thingID] = cont[i] player.addThing(cont[i])
del cont[i] del cont[i]
timeSpent += 0.5 timeSpent += 0.5
print("{0} taken.".format(thing)) print("{0} taken.".format(thing))
break break
else:
# If it got here, it didn't find it.
print("No {0} in container.".format(thing))
elif instr[0] == "store": elif instr[0] == "store":
# store something in the container # store something in the container
if instr[1] == "the": if instr[1] == "the":
del instr[1] del instr[1]
thingName = ' '.join(instr[1:]) thingName = ' '.join(instr[1:])
for i in inv: if thingName in player.thingNames:
thing = inv[i].name cont.append(player.removeThingByName(thingName))
if thing == thingName: print("{0} stored.".format(thingName))
cont.append(inv[i]) timeSpent += 0.5
del inv[i] else:
print("{0} stored.".format(thing)) print("No {0} in inventory.".format(thingName))
timeSpent += 0.5
break # so that all things with the same name don't get stored
instr = input("Take, store, or exit: ") instr = input("Take, store, or exit: ")
return inv, cont, timeSpent return cont, timeSpent
def info(self, items): def info(self, items):
"""IO for collections of information""" """IO for collections of information"""
@ -532,4 +541,5 @@ If -l is given, a map legend will be printed under the map."""
if __name__ == '__main__': if __name__ == '__main__':
sh = GameShell(GameBase()) sh = GameShell(GameBase())
sh.menuMode()
sh.run() sh.run()