Fixed most input validation bugs, and did too many improvements to remember. Did I mention that I am typing these words with my hands?
This commit is contained in:
parent
ee5c4da549
commit
4a398cc2a3
11 changed files with 546 additions and 253 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,3 +5,5 @@ __pycache__
|
||||||
gameshell.geany
|
gameshell.geany
|
||||||
gameexpr.py
|
gameexpr.py
|
||||||
game.py
|
game.py
|
||||||
|
solution.txt
|
||||||
|
|
||||||
|
|
236
gamebase.py
236
gamebase.py
|
@ -38,8 +38,8 @@ class GameBase(object):
|
||||||
self.__scripts = {} # functions with the same signature, but not callable by the user
|
self.__scripts = {} # functions with the same signature, but not callable by the user
|
||||||
self.customValues = {} # for setting flags and such
|
self.customValues = {} # for setting flags and such
|
||||||
self.level = None
|
self.level = None
|
||||||
self.persist = {} # {level : {thingName : thing}}
|
self.persist = {} # {level : {thingID : thing}}
|
||||||
self.singletons = {} # {thingID : thing}
|
self.singletons = {} # {thingName : thing}
|
||||||
self.ps2 = '? '
|
self.ps2 = '? '
|
||||||
self.eventQueue = []
|
self.eventQueue = []
|
||||||
self.gameTime = 0.0
|
self.gameTime = 0.0
|
||||||
|
@ -112,12 +112,23 @@ to get input from stdin."""
|
||||||
|
|
||||||
return '\n'.join(ret)
|
return '\n'.join(ret)
|
||||||
|
|
||||||
def smoothMove(self, actor, loc, speed, action = None, immediate = False):
|
def smoothMove(self, actor: _gt.Thing, loc: _gl.Locus, speed: float, action = None, immediate = False, closeEnough = True):
|
||||||
"""Move a thing smoothly at a constant speed."""
|
"""Move a thing smoothly at a constant speed.
|
||||||
|
|
||||||
|
actor should be a Thing.
|
||||||
|
loc should be a locus.
|
||||||
|
speed should be a number.
|
||||||
|
action should be a callable with 0 arguments. A lambda would be good here.
|
||||||
|
If immediate is True, the go event would happen immediately.
|
||||||
|
closeEnough should be True only if the path is to a non-passable point locus."""
|
||||||
|
if not isinstance(actor, _gt.Thing):
|
||||||
|
raise TypeError("The actor passed to smoothMove must be a Thing.")
|
||||||
|
if not isinstance(loc, _gl.Locus):
|
||||||
|
raise TypeError("The locus passed to smoothMove must be a Locus.")
|
||||||
if (actor.x, actor.y) in loc:
|
if (actor.x, actor.y) in loc:
|
||||||
self.setEvent(0.0, _ge.ArriveEvent(actor, self.level.coordsToInt(actor.x, actor.y), speed, action, loc, timeTaken = speed))
|
self.setEvent(0.0, _ge.ArriveEvent(actor, self.level.coordsToInt(actor.x, actor.y), speed, action, loc, timeTaken = speed))
|
||||||
return
|
return
|
||||||
dist, path, endPoint = self.level.path(actor.x, actor.y, loc)
|
dist, path, endPoint = self.level.path(actor.x, actor.y, loc, closeEnough)
|
||||||
#print(path)
|
#print(path)
|
||||||
if dist == -1:
|
if dist == -1:
|
||||||
print('{0} cannot reach there.'.format(actor.name), file = self.outstream)
|
print('{0} cannot reach there.'.format(actor.name), file = self.outstream)
|
||||||
|
@ -125,11 +136,15 @@ to get input from stdin."""
|
||||||
elif dist == 1:
|
elif dist == 1:
|
||||||
self.setEvent(speed, _ge.ArriveEvent(actor, path[0], speed, action, loc, timeTaken = speed))
|
self.setEvent(speed, _ge.ArriveEvent(actor, path[0], speed, action, loc, timeTaken = speed))
|
||||||
return speed
|
return speed
|
||||||
|
elif dist == 0:
|
||||||
|
# We should try to avoid this branch, because it means an error happened.
|
||||||
|
self.setEvent(0.0, _ge.ArriveEvent(actor, self.level.coordsToInt(actor.x, actor.y), speed, action, loc, timeTaken = speed))
|
||||||
|
return
|
||||||
elif immediate:
|
elif immediate:
|
||||||
self.setEvent(0, _ge.GoEvent(actor, path, speed, action, loc))
|
self.setEvent(0, _ge.GoEvent(actor, path, speed, action, loc, closeEnough))
|
||||||
return speed * (dist - 1)
|
return speed * (dist - 1)
|
||||||
else:
|
else:
|
||||||
self.setEvent(speed, _ge.GoEvent(actor, path, speed, action, loc, timeTaken = speed))
|
self.setEvent(speed, _ge.GoEvent(actor, path, speed, action, loc, closeEnough, timeTaken = speed))
|
||||||
return speed * dist
|
return speed * dist
|
||||||
|
|
||||||
# commands
|
# commands
|
||||||
|
@ -155,6 +170,9 @@ The letter is not case-sensitive."""
|
||||||
raise GameError("Cannot move: No level has been loaded.")
|
raise GameError("Cannot move: No level has been loaded.")
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot move: Player character doesn't exist.")
|
raise GameError("Cannot move: Player character doesn't exist.")
|
||||||
|
if len(args) == 0:
|
||||||
|
print(f"{self.player.name} goes nowhere.", file = self.outstream)
|
||||||
|
return
|
||||||
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
|
||||||
|
@ -181,6 +199,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot look: Player character doesn't exist.")
|
raise GameError("Cannot look: Player character doesn't exist.")
|
||||||
if len(args) == 0:
|
if len(args) == 0:
|
||||||
|
# no arguments: print the level description
|
||||||
print(self.justifyText(self.level.description), file = self.outstream)
|
print(self.justifyText(self.level.description), file = self.outstream)
|
||||||
else:
|
else:
|
||||||
if args[0] == 'at':
|
if args[0] == 'at':
|
||||||
|
@ -188,15 +207,18 @@ 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 = _gu.parseCoords(self.level, args, usePlayerCoords = False, player = self.player)
|
thing, x, y = _gu.parseCoords(self.level, args, usePlayerCoords = False, player = self.player)
|
||||||
if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
if thing == None:
|
||||||
|
print("There is nothing to see here.", file = self.outstream)
|
||||||
|
return
|
||||||
|
elif thing in self.player.inventory:
|
||||||
|
print(self.justifyText(str(thing)), file = self.outstream)
|
||||||
|
return
|
||||||
|
elif not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
||||||
if self.autoMove:
|
if self.autoMove:
|
||||||
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: print(self.justifyText(str(thing)), file = self.outstream))
|
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: print(self.justifyText(str(thing)), file = self.outstream), closeEnough = False)
|
||||||
else:
|
else:
|
||||||
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
||||||
return
|
return
|
||||||
elif thing == None:
|
|
||||||
print("There is nothing to see here.\n", file = self.outstream)
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
print(self.justifyText(str(thing)), file = self.outstream)
|
print(self.justifyText(str(thing)), file = self.outstream)
|
||||||
|
|
||||||
|
@ -212,22 +234,26 @@ Character can be the name of the character, or their coordinates."""
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot talk: Player character doesn't exist.")
|
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(f'{self.player.name} says, "Hello!" Nobody responds.', file = self.outstream)
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
if args[0] == 'to':
|
if args[0] == 'to':
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
if args[0] == 'the':
|
if args[0] == 'the':
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
thing, x, y = _gu.parseCoords(self.level, args, usePlayerCoords = False)
|
thing, x, y = _gu.parseCoords(self.level, args, usePlayerCoords = False)
|
||||||
if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
if thing == None:
|
||||||
|
if len(args) > 0:
|
||||||
|
print(f"There is nobody named {' '.join(args)}.", file = self.outstream)
|
||||||
|
else:
|
||||||
|
print(f"There is nobody there.", file = self.outstream)
|
||||||
|
return
|
||||||
|
elif not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
||||||
if self.autoMove:
|
if self.autoMove:
|
||||||
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: _gu.startDialog(thing, self.outstream, self.dialog))
|
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: _gu.startDialog(thing, self.outstream, self.dialog), closeEnough = False)
|
||||||
else:
|
else:
|
||||||
print("{} cannot talk to {}.".format(self.player.name, thing.name), file = self.outstream)
|
print("{} cannot talk to {}.".format(self.player.name, thing.name), file = self.outstream)
|
||||||
return
|
return
|
||||||
elif thing == None:
|
|
||||||
print("There is nobody here.\n", file = self.outstream)
|
|
||||||
return
|
|
||||||
else:
|
else:
|
||||||
_gu.startDialog(thing, self.outstream, self.dialog)
|
_gu.startDialog(thing, self.outstream, self.dialog)
|
||||||
|
|
||||||
|
@ -246,6 +272,9 @@ the name of an item in the player's inventory."""
|
||||||
raise GameError("Cannot use: No level has been loaded.")
|
raise GameError("Cannot use: No level has been loaded.")
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot use: Player character doesn't exist.")
|
raise GameError("Cannot use: Player character doesn't exist.")
|
||||||
|
if len(args) == 0:
|
||||||
|
print(f"{self.player.name} pokes the air as if pressing a button.", file = self.outstream)
|
||||||
|
return
|
||||||
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':
|
||||||
|
@ -269,12 +298,23 @@ the name of an item in the player's inventory."""
|
||||||
return
|
return
|
||||||
|
|
||||||
# Similar to go, but not quite the same.
|
# Similar to go, but not quite the same.
|
||||||
self.smoothMove(self.player, ((x, y),), speed, lambda: self.setEvent(0.125, _ge.UseEvent(self.player, thing, useArgs)))
|
if thing.thingType == 'i': # it must be in inventory to pass the last check.
|
||||||
|
self.setEvent(0.125, _ge.UseEvent(self.player, thing, useArgs))
|
||||||
|
else:
|
||||||
|
self.smoothMove(self.player, _gl.PointLocus(x, y), speed, lambda: self.setEvent(0.125, _ge.UseEvent(self.player, thing, useArgs)))
|
||||||
return
|
return
|
||||||
|
|
||||||
def useOn(self, args, speed):
|
def useOn(self, args, speed):
|
||||||
"""Called by use when there is an 'on' clause"""
|
"""Called by use when there is an 'on' clause"""
|
||||||
onIndex = args.index('on')
|
onIndex = args.index('on')
|
||||||
|
if len(args[:onIndex]) == 0:
|
||||||
|
# just starts with 'use on ...'
|
||||||
|
print(f"{self.player.name} tries to use nothing, but nothing happens.", file = self.outstream)
|
||||||
|
return
|
||||||
|
if len(args[onIndex + 1:]) == 0:
|
||||||
|
# just ends with '... on'
|
||||||
|
print(f"{self.player.name} tries to use the {' '.join(args[:onIndex])} on nothing, but nothing happens.", file = self.outstream)
|
||||||
|
return
|
||||||
item, x, y = _gu.parseCoords(self.level, args[:onIndex], usePlayerCoords = False, player = self.player)
|
item, x, y = _gu.parseCoords(self.level, args[:onIndex], usePlayerCoords = False, player = self.player)
|
||||||
if args[onIndex+1] == 'the':
|
if args[onIndex+1] == 'the':
|
||||||
onIndex += 1
|
onIndex += 1
|
||||||
|
@ -293,11 +333,11 @@ the name of an item in the player's inventory."""
|
||||||
|
|
||||||
if not item.ranged:
|
if not item.ranged:
|
||||||
# Similar to go, but not quite the same.
|
# Similar to go, but not quite the same.
|
||||||
self.smoothMove(self.player, ((x, y),), speed, lambda: self.setEvent(0.125, _ge.UseOnEvent(self.player, item, thing, useArgs)))
|
self.smoothMove(self.player, _gl.PointLocus(x, y), speed, lambda: self.setEvent(0.125, _ge.UseOnEvent(self.player, item, thing, useArgs)))
|
||||||
else:
|
else:
|
||||||
if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
||||||
if self.autoMove:
|
if self.autoMove:
|
||||||
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: self.setEvent(0.125, _ge.UseOnEvent(self.player, item, thing, useArgs)))
|
self.smoothMove(self.player, _gm.LoSLocus(x, y, self.level), 0.3333333, lambda: self.setEvent(0.125, _ge.UseOnEvent(self.player, item, thing, useArgs)), closeEnough = False)
|
||||||
else:
|
else:
|
||||||
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
||||||
return
|
return
|
||||||
|
@ -315,6 +355,9 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot take: Player character doesn't exist.")
|
raise GameError("Cannot take: Player character doesn't exist.")
|
||||||
speed = 0.6666667
|
speed = 0.6666667
|
||||||
|
if len(args) == 0:
|
||||||
|
print(f"{self.player.name} reaches out and grasps at nothing.", file = self.outstream)
|
||||||
|
return
|
||||||
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
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
|
@ -330,7 +373,7 @@ 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.
|
||||||
self.smoothMove(self.player, ((x, y),), speed, lambda: self.setEvent(0.125, _ge.TakeEvent(self.player, thing)))
|
self.smoothMove(self.player, _gl.PointLocus(x, y), speed, lambda: self.setEvent(0.125, _ge.TakeEvent(self.player, thing)))
|
||||||
|
|
||||||
def drop(self, args):
|
def drop(self, args):
|
||||||
"""drop [the] item"""
|
"""drop [the] item"""
|
||||||
|
@ -338,11 +381,14 @@ Object can be the name of the object, or its coordinates."""
|
||||||
raise GameError("Cannot drop: No level has been loaded.")
|
raise GameError("Cannot drop: No level has been loaded.")
|
||||||
if self.player == None:
|
if self.player == None:
|
||||||
raise GameError("Cannot drop: Player character doesn't exist.")
|
raise GameError("Cannot drop: Player character doesn't exist.")
|
||||||
|
if len(args) == 0:
|
||||||
|
print(f"{self.player.name} falls over.", file = self.outstream)
|
||||||
|
return
|
||||||
if args[0] == 'the':
|
if args[0] == 'the':
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
thingName = ' '.join(args)
|
thingName = ' '.join(args)
|
||||||
if thingName in self.player.thingNames:
|
if thingName in self.player.thingNames:
|
||||||
self.setEvent(0.0, _ge.DropEvent(self.player.getThingByName(thingName)))
|
self.setEvent(0.0, _ge.DropEvent(self.player, self.player.getThingByName(thingName)))
|
||||||
else:
|
else:
|
||||||
print('{0} does not have a {1}.'.format(self.player.name, args[0]), file = self.outstream)
|
print('{0} does not have a {1}.'.format(self.player.name, args[0]), file = self.outstream)
|
||||||
|
|
||||||
|
@ -443,9 +489,8 @@ Object can be the name of the object, or its coordinates."""
|
||||||
print("Save file must have a name!", file = self.outstream)
|
print("Save file must have a name!", file = self.outstream)
|
||||||
return
|
return
|
||||||
|
|
||||||
# choose pickle protocol depending on python version:
|
# pickle protocol 4 for Python 3.4.0 to 3.7.x
|
||||||
# 3 for Python 3.0.0 to 3.3.x, 4 for Python 3.4.0 to 3.7.x
|
prot = 4
|
||||||
prot = _pi.HIGHEST_PROTOCOL
|
|
||||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||||
if args[0].endswith('.dat'):
|
if args[0].endswith('.dat'):
|
||||||
fileName = args[0]
|
fileName = args[0]
|
||||||
|
@ -475,10 +520,10 @@ Object can be the name of the object, or its coordinates."""
|
||||||
ret = 0
|
ret = 0
|
||||||
for case in cases:
|
for case in cases:
|
||||||
cond = case['case'].split() # should be list like ["value", "==", 1]
|
cond = case['case'].split() # should be list like ["value", "==", 1]
|
||||||
if len(cond) == 1 and (cond[0] == 'else' or _gs.getValueFromString(cond[0], {'scriptLocal': {}, 'global': self.customValues})):
|
if len(cond) == 1 and (cond[0] == 'else' or _gs.getValueFromString(cond[0], _gs.ScriptEnvironment(self.customValues, {}))):
|
||||||
ret = self.runDialog(case)
|
ret = self.runDialog(case)
|
||||||
break
|
break
|
||||||
elif len(cond) == 3 and _gs.compareValues(cond, {'scriptLocal': {}, 'global': self.customValues}):
|
elif len(cond) == 3 and _gs.compareValues(cond, _gs.ScriptEnvironment(self.customValues, {})):
|
||||||
ret = self.runDialog(case)
|
ret = self.runDialog(case)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
@ -504,19 +549,15 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if ans[0] == '?':
|
if ans[0] == '?':
|
||||||
condEnd = ans.index(':')
|
condEnd = ans.index(':')
|
||||||
cond = ans[1:condEnd].strip().split()
|
cond = ans[1:condEnd].strip().split()
|
||||||
if _gs.compareValues(cond, {'scriptLocal': {}, 'global': self.customValues}):
|
if _gs.compareValues(cond, _gs.ScriptEnvironment(self.customValues, {})):
|
||||||
options.append(ans[condEnd+1:].strip())
|
options.append(ans[condEnd+1:].strip())
|
||||||
#print(_tw.fill('{}: {}'.format(j+1, ans[condEnd+1:].strip()), width = TERM_SIZE))
|
|
||||||
j += 1
|
j += 1
|
||||||
else:
|
else:
|
||||||
skips.append(i)
|
skips.append(i)
|
||||||
else:
|
else:
|
||||||
options.append(ans)
|
options.append(ans)
|
||||||
#print(_tw.fill('{}: {}'.format(j+1, ans), width = TERM_SIZE))
|
|
||||||
j += 1
|
j += 1
|
||||||
answer = self.getIO('respondDialog')(options)
|
answer = self.getIO('respondDialog')(options)
|
||||||
#print(answer)
|
|
||||||
#answer = int(input(self.ps2)) - 1
|
|
||||||
# account for offset if there were answer options that didn't meet conditions
|
# account for offset if there were answer options that didn't meet conditions
|
||||||
for i in skips:
|
for i in skips:
|
||||||
if i <= answer:
|
if i <= answer:
|
||||||
|
@ -534,7 +575,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
elif len(action) >= 4 and action[:4] == 'back':
|
elif len(action) >= 4 and action[:4] == 'back':
|
||||||
if len(action) == 4:
|
if len(action) == 4:
|
||||||
return 1
|
return 1
|
||||||
return int(action[4:])
|
return int(action[5:])
|
||||||
elif action == 'exit':
|
elif action == 'exit':
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
|
@ -561,6 +602,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
# any map. This is useful for when you have major characters who will
|
# any map. This is useful for when you have major characters who will
|
||||||
# show up in many scenes, and their inventory must stay the same, for
|
# show up in many scenes, and their inventory must stay the same, for
|
||||||
# example.
|
# example.
|
||||||
|
#print(data)
|
||||||
self.singletons = {}
|
self.singletons = {}
|
||||||
if 'singletons' in data:
|
if 'singletons' in data:
|
||||||
for thing in data['singletons']:
|
for thing in data['singletons']:
|
||||||
|
@ -580,6 +622,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
thing.addThing(i)
|
thing.addThing(i)
|
||||||
del thing.tempInventory
|
del thing.tempInventory
|
||||||
self.singletons[thing.name] = thing
|
self.singletons[thing.name] = thing
|
||||||
|
#print(self.singletons)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def gameEventLoop(self):
|
def gameEventLoop(self):
|
||||||
|
@ -591,12 +634,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
ev = _hq.heappop(self.eventQueue)
|
ev = _hq.heappop(self.eventQueue)
|
||||||
self.gameTime = ev[0]
|
self.gameTime = ev[0]
|
||||||
e = ev[1]
|
e = ev[1]
|
||||||
if e.eventType not in self.__gameEvents:
|
|
||||||
raise GameError("Unhandled event.")
|
|
||||||
ret = False
|
ret = False
|
||||||
for i in self.__gameEvents[e.eventType]:
|
if e.eventType in self.__gameEvents:
|
||||||
ret = ret or i(e)
|
for i in self.__gameEvents[e.eventType]:
|
||||||
self.observe(e)
|
ret = ret or i(e)
|
||||||
|
self.observe(e) # An event can still be observed even if it has no callback.
|
||||||
if ret:
|
if ret:
|
||||||
break
|
break
|
||||||
if len(self.eventQueue) == 0:
|
if len(self.eventQueue) == 0:
|
||||||
|
@ -604,8 +646,8 @@ Object can be the name of the object, or its coordinates."""
|
||||||
_ge.resetEventNum()
|
_ge.resetEventNum()
|
||||||
self.skipLoop = True
|
self.skipLoop = True
|
||||||
|
|
||||||
def setEvent(self, t, e, skip = False):
|
def setEvent(self, timeFromNow: float, event: _ge.GameEvent, skip = False):
|
||||||
_hq.heappush(self.eventQueue, (self.gameTime + t, e))
|
_hq.heappush(self.eventQueue, (self.gameTime + timeFromNow, event))
|
||||||
self.skipLoop = skip
|
self.skipLoop = skip
|
||||||
|
|
||||||
def clearEvents(self, actor = None):
|
def clearEvents(self, actor = None):
|
||||||
|
@ -634,13 +676,13 @@ Object can be the name of the object, or its coordinates."""
|
||||||
#print(e.actor.x, e.actor.y)
|
#print(e.actor.x, e.actor.y)
|
||||||
#print(e.action)
|
#print(e.action)
|
||||||
if len(e.path) > 1:
|
if len(e.path) > 1:
|
||||||
self.setEvent(e.speed, _ge.GoEvent(e.actor, e.path[1:], e.speed, e.action, e.locus, e.timeTaken + e.speed))
|
self.setEvent(e.speed, _ge.GoEvent(e.actor, e.path[1:], e.speed, e.action, e.locus, e.closeEnough, e.timeTaken + e.speed))
|
||||||
else:
|
else:
|
||||||
self.setEvent(e.speed, _ge.ArriveEvent(e.actor, e.path[0], e.speed, e.action, e.locus, e.timeTaken + e.speed))
|
self.setEvent(e.speed, _ge.ArriveEvent(e.actor, e.path[0], e.speed, e.action, e.locus, e.timeTaken + e.speed))
|
||||||
elif isinstance(e.locus, _gl.Locus):
|
elif isinstance(e.locus, _gl.Locus):
|
||||||
self.smoothMove(e.actor, e.locus, e.speed, e.action, immediate = True)
|
self.smoothMove(e.actor, e.locus, e.speed, e.action, True, e.closeEnough)
|
||||||
else:
|
else:
|
||||||
print('{0} cannot !reach there.'.format(e.actor.name), file = self.outstream)
|
print('{0} cannot reach there.'.format(e.actor.name), file = self.outstream)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handleArrive(self, e):
|
def handleArrive(self, e):
|
||||||
|
@ -652,11 +694,16 @@ Object can be the name of the object, or its coordinates."""
|
||||||
self.level.moveThing(e.actor, e.pos)
|
self.level.moveThing(e.actor, e.pos)
|
||||||
if e.action == None:
|
if e.action == None:
|
||||||
#print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(e.actor.name, _gu.numberToLetter(e.x), e.y, e.t), file = self.outstream)
|
#print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(e.actor.name, _gu.numberToLetter(e.x), e.y, e.t), file = self.outstream)
|
||||||
|
#print ("action = None")
|
||||||
if thing:
|
if thing:
|
||||||
|
#print("thing != None")
|
||||||
if thing.thingType == 'x':
|
if thing.thingType == 'x':
|
||||||
|
#print("thing is exit")
|
||||||
if e.actor == self.player:
|
if e.actor == self.player:
|
||||||
|
#print("actor = 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)):
|
||||||
|
#print("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))
|
||||||
|
@ -665,25 +712,29 @@ Object can be the name of the object, or its coordinates."""
|
||||||
self.level.removeThing(actor.name)
|
self.level.removeThing(actor.name)
|
||||||
else:
|
else:
|
||||||
e.action()
|
e.action()
|
||||||
|
return False # so that use works.
|
||||||
#elif isinstance(e.locus, _gl.Locus):
|
#elif isinstance(e.locus, _gl.Locus):
|
||||||
# self.smoothMove(e.actor, e.locus, e.speed, e.action, immediate = True)
|
# self.smoothMove(e.actor, e.locus, e.speed, e.action, immediate = True)
|
||||||
else:
|
else:
|
||||||
# checking the locus again would inevitably fail, so we just give up.
|
# checking the locus again would inevitably fail, so we just give up.
|
||||||
print('{0} cannot @reach there.'.format(e.actor.name), file = self.outstream)
|
print('{0} cannot reach there.'.format(e.actor.name), file = self.outstream)
|
||||||
return e.actor == self.player
|
return e.actor == self.player
|
||||||
|
|
||||||
def handleUse(self, e):
|
def handleUse(self, e: _ge.UseEvent):
|
||||||
|
"""Called when a UseEvent needs processed."""
|
||||||
if e.thing.useFunc == '':
|
if e.thing.useFunc == '':
|
||||||
print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream)
|
print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream)
|
||||||
return True
|
return True
|
||||||
self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing, e.args), _ge.NoOpEvent())
|
self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing, e.args), _ge.NoOpEvent())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handleUseOn(self, e):
|
def handleUseOn(self, e: _ge.UseOnEvent):
|
||||||
|
"""Called when a UseOnEvent needs processed.
|
||||||
|
It calls the item's useOnFunc in the useFuncs map with the item, target, and arguments."""
|
||||||
if e.item.useOnFunc == '':
|
if e.item.useOnFunc == '':
|
||||||
print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream)
|
print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream)
|
||||||
return True
|
return True
|
||||||
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.thing, e.args), _ge.NoOpEvent())
|
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.target, e.args), _ge.NoOpEvent())
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def handleTake(self, e):
|
def handleTake(self, e):
|
||||||
|
@ -699,7 +750,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
e.item.x = e.actor.x
|
e.item.x = e.actor.x
|
||||||
e.item.y = e.actor.y
|
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
|
||||||
self.actor.removeThing(e.item)
|
e.actor.removeThing(e.item)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def handleBehave(self, e):
|
def handleBehave(self, e):
|
||||||
|
@ -720,15 +771,24 @@ Object can be the name of the object, or its coordinates."""
|
||||||
"""Just prints the given text."""
|
"""Just prints the given text."""
|
||||||
if 'pattern' not in thing.customValues or 'text' not in thing.customValues:
|
if 'pattern' not in thing.customValues or 'text' not in thing.customValues:
|
||||||
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
||||||
|
# Start out with a cursor at 0.
|
||||||
|
if not 'cursor' in thing.customValues:
|
||||||
|
thing.customValues['cursor'] = 0
|
||||||
|
|
||||||
|
# With 'single', the cursor will not be modified.
|
||||||
if thing.customValues['pattern'] == 'single':
|
if thing.customValues['pattern'] == 'single':
|
||||||
print(self.justifyText(str(thing.customValues['text'])), file = self.outstream)
|
if isinstance(thing.customValues['text'], str):
|
||||||
|
print(self.justifyText(str(thing.customValues['text'])), file = self.outstream)
|
||||||
|
elif isinstance(thing.customValues['text'], list):
|
||||||
|
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||||
|
|
||||||
|
# With 'loop', the given strings will keep looping.
|
||||||
elif thing.customValues['pattern'] == 'loop':
|
elif thing.customValues['pattern'] == 'loop':
|
||||||
if not 'cursor' in thing.customValues:
|
|
||||||
thing.customValues['cursor'] = 0
|
|
||||||
cursor = thing.customValues['cursor']
|
cursor = thing.customValues['cursor']
|
||||||
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||||
thing.customValues['cursor'] = (cursor + 1) % len(thing.customValues['text'])
|
thing.customValues['cursor'] = (cursor + 1) % len(thing.customValues['text'])
|
||||||
|
|
||||||
|
# With 'once', the strings will be used one by one, until it gets to the end. The last one is then repeated.
|
||||||
elif thing.customValues['pattern'] == 'once':
|
elif thing.customValues['pattern'] == 'once':
|
||||||
if not 'cursor' in thing.customValues:
|
if not 'cursor' in thing.customValues:
|
||||||
thing.customValues['cursor'] = 0
|
thing.customValues['cursor'] = 0
|
||||||
|
@ -736,6 +796,8 @@ Object can be the name of the object, or its coordinates."""
|
||||||
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||||
if cursor < len(thing.customValues['text']) - 1:
|
if cursor < len(thing.customValues['text']) - 1:
|
||||||
thing.customValues['cursor'] += 1
|
thing.customValues['cursor'] += 1
|
||||||
|
|
||||||
|
|
||||||
elif thing.customValues['pattern'] == 'random':
|
elif thing.customValues['pattern'] == 'random':
|
||||||
cursor = _ra.randrange(len(thing.customValues['text']))
|
cursor = _ra.randrange(len(thing.customValues['text']))
|
||||||
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||||
|
@ -784,14 +846,14 @@ Object can be the name of the object, or its coordinates."""
|
||||||
|
|
||||||
# item use-on functions
|
# item use-on functions
|
||||||
|
|
||||||
def key(self, item, thing, args):
|
def key(self, key : _gt.Item, door : _gt.Door, args):
|
||||||
"""Item is a key, which unlocks a door. This may be implemented for containers later."""
|
"""Item is a key, which unlocks a door. This may be implemented for containers later."""
|
||||||
if isinstance(thing, tuple) or thing.thingType != 'd':
|
if isinstance(door, tuple) or door.thingType != 'd':
|
||||||
print("That is not a door.", file = self.outstream)
|
print("That is not a door.", file = self.outstream)
|
||||||
return 0.0
|
return 0.0
|
||||||
if thing.lock(item.name):
|
if door.lock(key.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.player.x == thing.x and self.player.y == thing.y:
|
if not door.passable and self.player.x == door.x and self.player.y == door.y:
|
||||||
self.player.x, self.player.y = self.player.prevx, self.player.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)
|
||||||
|
@ -870,10 +932,15 @@ Object can be the name of the object, or its coordinates."""
|
||||||
elif i.casefold() == 'ranged':
|
elif i.casefold() == 'ranged':
|
||||||
ranged = True
|
ranged = True
|
||||||
elif i[0:3].casefold() == 'cv:':
|
elif i[0:3].casefold() == 'cv:':
|
||||||
|
cvEnv = customValues
|
||||||
equalsign = i.index('=')
|
equalsign = i.index('=')
|
||||||
cv = i[3:equalsign]
|
cv = i[3:equalsign]
|
||||||
customValues[cv] = self.getValueFromString(i[equalsign+1:])
|
ns = cv.find(':')
|
||||||
thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
|
while ns != -1:
|
||||||
|
cvEnv[cv[1:ns]] = {}
|
||||||
|
cvEnv = cvEnv[cv[1:ns]]
|
||||||
|
cvEnv[cv] = self.getValueFromString(i[equalsign+1:])
|
||||||
|
thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, _gt.ThingGraphic(bgc, fgc, shape))
|
||||||
elif args[0].casefold() == 'useable':
|
elif args[0].casefold() == 'useable':
|
||||||
# spawn a useable thing
|
# spawn a useable thing
|
||||||
description = 'A nondescript useable thing.'
|
description = 'A nondescript useable thing.'
|
||||||
|
@ -899,7 +966,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 = _gt.Useable(name, x, y, description, useFunc, customValues, playerx, playery, (bgc, fgc, shape))
|
thing = _gt.Useable(name, x, y, description, useFunc, customValues, playerx, playery, _gt.ThingGraphic(bgc, fgc, shape))
|
||||||
elif args[0].casefold() == 'npc':
|
elif args[0].casefold() == 'npc':
|
||||||
# spawn an NPC
|
# spawn an NPC
|
||||||
description = 'A nondescript character.'
|
description = 'A nondescript character.'
|
||||||
|
@ -926,7 +993,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 = _gt.NPC(name, x, y, description, behavior, inv, customValues, playerx, playery, (bgc, fgc, shape))
|
thing = _gt.NPC(name, x, y, description, behavior, inv, customValues, playerx, playery, _gt.ThingGraphic(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.'
|
||||||
|
@ -945,7 +1012,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
description = i[12:]
|
description = i[12:]
|
||||||
elif i.casefold() == 'locked':
|
elif i.casefold() == 'locked':
|
||||||
locked = True
|
locked = True
|
||||||
thing = _gt.Door(name, x, y, locked, description, key, (bgc, fgc, shape))
|
thing = _gt.Door(name, x, y, locked, description, key, _gt.ThingGraphic(bgc, fgc, shape))
|
||||||
elif args[0].casefold() == 'mapexit':
|
elif args[0].casefold() == 'mapexit':
|
||||||
# spawn an exit to another map (use with EXTREME caution!)
|
# spawn an exit to another map (use with EXTREME caution!)
|
||||||
destination = ''
|
destination = ''
|
||||||
|
@ -972,7 +1039,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
onUse = i[6:]
|
onUse = i[6:]
|
||||||
elif i[0:4].casefold() == 'key=':
|
elif i[0:4].casefold() == 'key=':
|
||||||
key = i[4:]
|
key = i[4:]
|
||||||
thing = _gt.MapExit(name, x, y, exitid, destination, prefix, onUse, key, (bgc, fgc, shape))
|
thing = _gt.MapExit(name, x, y, exitid, destination, prefix, onUse, key, _gt.ThingGraphic(bgc, fgc, shape))
|
||||||
elif args[0].casefold() == 'mapentrance':
|
elif args[0].casefold() == 'mapentrance':
|
||||||
# spawn a map entrance
|
# spawn a map entrance
|
||||||
exitid = 0
|
exitid = 0
|
||||||
|
@ -980,6 +1047,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if i[0:7].casefold() == 'exitid=':
|
if i[0:7].casefold() == 'exitid=':
|
||||||
exitid = int(i[7:])
|
exitid = int(i[7:])
|
||||||
thing = _gt.MapEntrance(x, y, exitid, name)
|
thing = _gt.MapEntrance(x, y, exitid, name)
|
||||||
|
elif args[0].casefold() == 'singleton':
|
||||||
|
if name in self.singletons:
|
||||||
|
single = self.singletons[name]
|
||||||
|
single.x, single.y = x, y
|
||||||
|
single.prevx, single.prevy = x, y
|
||||||
else:
|
else:
|
||||||
raise GameError("{} not a valid thing type.".format(args[0]))
|
raise GameError("{} not a valid thing type.".format(args[0]))
|
||||||
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
|
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
|
||||||
|
@ -1036,7 +1108,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] = getValueFromString(i[equalsign+1:])
|
customValues[cv] = getValueFromString(i[equalsign+1:])
|
||||||
thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
|
thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, _gt.ThingGraphic(bgc, fgc, shape))
|
||||||
thing.thingID = self.nextThing
|
thing.thingID = self.nextThing
|
||||||
self.player.addThing(thing)
|
self.player.addThing(thing)
|
||||||
self.nextThing += 1
|
self.nextThing += 1
|
||||||
|
@ -1106,25 +1178,35 @@ Object can be the name of the object, or its coordinates."""
|
||||||
# behaviors
|
# behaviors
|
||||||
|
|
||||||
def stand(self, actor):
|
def stand(self, actor):
|
||||||
pass
|
return 0
|
||||||
|
|
||||||
def wander(self, actor):
|
def wander(self, actor):
|
||||||
pass
|
return 0
|
||||||
|
|
||||||
def follow(self, actor, event):
|
def follow(self, actor, event):
|
||||||
# make sure we only follow who we want to
|
# make sure we only follow who we want to
|
||||||
if event.actor.name != actor.customValues["follow"]["target"]:
|
if "follow" not in actor.customValues:
|
||||||
return 0
|
return 0
|
||||||
|
if "isFollowing" not in actor.customValues["follow"]:
|
||||||
|
return 0
|
||||||
|
if actor.customValues["follow"]["isFollowing"]:
|
||||||
|
if event.actor.name != actor.customValues["follow"]["target"]:
|
||||||
|
if actor.customValues["follow"]["target"] == '':
|
||||||
|
# This is a quick-and-dirty fix for an issue I was having with followers.
|
||||||
|
actor.customValues["follow"]["target"] = actor
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
dest = 0
|
||||||
|
#print(event.eventType)
|
||||||
|
if event.eventType == 'go':
|
||||||
|
dest = event.path[-1]
|
||||||
|
elif event.eventType == 'arrive':
|
||||||
|
dest = event.pos
|
||||||
|
x, y = self.level.intToCoords(dest)
|
||||||
|
self.smoothMove(actor, _gl.CircleLocus(x, y, 2), event.speed, action = lambda: self.setEvent(0, _ge.BehaveEvent(actor)), closeEnough = False)
|
||||||
|
return -1
|
||||||
else:
|
else:
|
||||||
dest = 0
|
return 0
|
||||||
#print(event.eventType)
|
|
||||||
if event.eventType == 'go':
|
|
||||||
dest = event.path[-1]
|
|
||||||
elif event.eventType == 'arrive':
|
|
||||||
dest = event.pos
|
|
||||||
x, y = self.level.intToCoords(dest)
|
|
||||||
self.smoothMove(actor, _gl.CircleLocus(x, y, 2), event.speed, action = lambda: self.setEvent(0, _ge.BehaveEvent(actor)))
|
|
||||||
return -1
|
|
||||||
|
|
||||||
# stuff for extended classes to use
|
# stuff for extended classes to use
|
||||||
def registerUseFunc(self, name, func):
|
def registerUseFunc(self, name, func):
|
||||||
|
@ -1159,3 +1241,7 @@ always give the player a turn, False otherwise."""
|
||||||
if name not in self.__IOCalls:
|
if name not in self.__IOCalls:
|
||||||
raise GameError("No IO call for {}.".format(name))
|
raise GameError("No IO call for {}.".format(name))
|
||||||
return self.__IOCalls[name]
|
return self.__IOCalls[name]
|
||||||
|
|
||||||
|
# |\_/|
|
||||||
|
# /0 0\
|
||||||
|
# \o/
|
||||||
|
|
|
@ -63,13 +63,14 @@ class NoOpEvent(GameEvent):
|
||||||
|
|
||||||
class GoEvent(GameEvent):
|
class GoEvent(GameEvent):
|
||||||
|
|
||||||
def __init__(self, actor, path, speed, action = None, locus = None, timeTaken = 0):
|
def __init__(self, actor, path, speed, action = None, locus = None, closeEnough = True, timeTaken = 0):
|
||||||
super(GoEvent, self).__init__('go')
|
super(GoEvent, self).__init__('go')
|
||||||
self.actor = actor
|
self.actor = actor
|
||||||
self.path = path
|
self.path = path
|
||||||
self.speed = speed
|
self.speed = speed
|
||||||
self.action = action
|
self.action = action
|
||||||
self.locus = locus
|
self.locus = locus
|
||||||
|
self.closeEnough = closeEnough
|
||||||
self.timeTaken = timeTaken
|
self.timeTaken = timeTaken
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -95,10 +96,10 @@ class UseEvent(GameEvent):
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
class UseOnEvent(GameEvent):
|
class UseOnEvent(GameEvent):
|
||||||
def __init__(self, actor, item, thing, args):
|
def __init__(self, actor, item, target, args):
|
||||||
super(UseOnEvent, self).__init__('useon')
|
super(UseOnEvent, self).__init__('useon')
|
||||||
self.actor = actor
|
self.actor = actor
|
||||||
self.thing = thing # thing can be a coordinate pair?
|
self.target = target # thing can be a coordinate pair?
|
||||||
self.item = item
|
self.item = item
|
||||||
self.args = args
|
self.args = args
|
||||||
|
|
||||||
|
@ -118,3 +119,7 @@ class BehaveEvent(GameEvent):
|
||||||
def __init__(self, actor):
|
def __init__(self, actor):
|
||||||
super(BehaveEvent, self).__init__('behave')
|
super(BehaveEvent, self).__init__('behave')
|
||||||
self.actor = actor
|
self.actor = actor
|
||||||
|
|
||||||
|
# |\_/|
|
||||||
|
# /0 0\
|
||||||
|
# \o/
|
||||||
|
|
42
gamemap.py
42
gamemap.py
|
@ -338,7 +338,6 @@ list of lists of tuples."""
|
||||||
# if startThing and not startThing.passable:
|
# if startThing and not startThing.passable:
|
||||||
# return -1, [], -1 # meaning you can't get there
|
# return -1, [], -1 # meaning you can't get there
|
||||||
dist, prev, endPoint = self.dijkstra(x1, y1, loc, closeEnough)
|
dist, prev, endPoint = self.dijkstra(x1, y1, loc, closeEnough)
|
||||||
#endPoint = self.coordsToInt(x2, y2)
|
|
||||||
numVertex = self.dimensions[0] * self.dimensions[1]
|
numVertex = self.dimensions[0] * self.dimensions[1]
|
||||||
if endPoint > -1 and dist[endPoint] < numVertex + 1:
|
if endPoint > -1 and dist[endPoint] < numVertex + 1:
|
||||||
pathList = [endPoint]
|
pathList = [endPoint]
|
||||||
|
@ -356,35 +355,39 @@ list of lists of tuples."""
|
||||||
The closeEnough parameter will create a path that lands beside the source if
|
The closeEnough parameter will create a path that lands beside the source if
|
||||||
necessary. The loc parameter is an optional locus which will cause the function
|
necessary. The loc parameter is an optional locus which will cause the function
|
||||||
to return once it finds a point that's in the locus."""
|
to return once it finds a point that's in the locus."""
|
||||||
# first test to see that the start point is passable
|
|
||||||
startThing = self.getThingAtCoords(x1, y1)
|
|
||||||
startPoint = self.coordsToInt(x1, y1)
|
|
||||||
endPoint = -1 # until one matches the locus, which it might not.
|
endPoint = -1 # until one matches the locus, which it might not.
|
||||||
numVertex = self.dimensions[0] * self.dimensions[1]
|
numVertex = self.dimensions[0] * self.dimensions[1]
|
||||||
dist = [numVertex + 1 for i in range(numVertex)]
|
dist = [numVertex + 1 for i in range(numVertex)]
|
||||||
prev = [-1 for i in range(numVertex)]
|
prev = [-1 for i in range(numVertex)]
|
||||||
|
# validation
|
||||||
|
if loc == None or not self.__validateCoords(x1, y1):
|
||||||
|
# start point is out-of-bounds or there is no destination
|
||||||
|
return dist, prev, endPoint
|
||||||
|
# first test to see that the start point is passable
|
||||||
|
startThing = self.getThingAtCoords(x1, y1)
|
||||||
|
startPoint = self.coordsToInt(x1, y1)
|
||||||
dist[startPoint] = 0
|
dist[startPoint] = 0
|
||||||
#if closeEnough:
|
|
||||||
# if startThing and not startThing.passable:
|
|
||||||
# dist[startPoint] = -1 # This is so it doesn't path into a non-passable end point.
|
|
||||||
queue = []
|
queue = []
|
||||||
heapq.heappush(queue, (dist[startPoint], startPoint))
|
heapq.heappush(queue, (dist[startPoint], startPoint))
|
||||||
|
|
||||||
while len(queue) > 0:
|
while len(queue) > 0:
|
||||||
u = heapq.heappop(queue)[1]
|
u = heapq.heappop(queue)[1]
|
||||||
if loc != None and self.intToCoords(u) in loc:
|
if loc != None and self.intToCoords(u) in loc:
|
||||||
return dist, prev, u
|
if endPoint == -1 or dist[u] < dist[endPoint]:
|
||||||
|
endPoint = u
|
||||||
|
#print(f"endPoint: {endPoint}; Reason: in locus")
|
||||||
for v in self.mapGraph[u]:
|
for v in self.mapGraph[u]:
|
||||||
thing = self.getThingAtPos(v)
|
thing = self.getThingAtPos(v)
|
||||||
if thing and not thing.passable:
|
if thing and not thing.passable:
|
||||||
if closeEnough and self.intToCoords(v) in loc:
|
if closeEnough and self.intToCoords(v) in loc and (endPoint == -1 or dist[u] < dist[endPoint]):
|
||||||
return dist, prev, u
|
endPoint = u
|
||||||
|
#print(f"endPoint: {endPoint}; Reason: good enough")
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
tempDist = dist[u] + 1
|
tempDist = dist[u] + 1
|
||||||
if tempDist < dist[v]:
|
if tempDist < dist[v]:
|
||||||
dist[v] = tempDist
|
dist[v] = tempDist
|
||||||
if dist[u] != -1:
|
if dist[u] != numVertex + 1:
|
||||||
prev[v] = u
|
prev[v] = u
|
||||||
heapq.heappush(queue, (dist[v], v))
|
heapq.heappush(queue, (dist[v], v))
|
||||||
|
|
||||||
|
@ -396,6 +399,8 @@ to return once it finds a point that's in the locus."""
|
||||||
|
|
||||||
def lineOfSight(self, x1, y1, x2, y2):
|
def lineOfSight(self, x1, y1, x2, y2):
|
||||||
"""Test for line of signt from one tile to another."""
|
"""Test for line of signt from one tile to another."""
|
||||||
|
if not (self.__validateCoords(x1, y1) and self.__validateCoords(x2, y2)):
|
||||||
|
return False
|
||||||
# Trivial case first:
|
# Trivial case first:
|
||||||
if abs(x1 - x2) <= 1 and abs(y1 - y2) <= 1:
|
if abs(x1 - x2) <= 1 and abs(y1 - y2) <= 1:
|
||||||
return True
|
return True
|
||||||
|
@ -410,10 +415,14 @@ to return once it finds a point that's in the locus."""
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def isPassable(self, x, y = -1):
|
def isPassable(self, x, y = -1):
|
||||||
pos = x
|
|
||||||
if y == -1:
|
if y == -1:
|
||||||
|
if not self.__validatePos(x):
|
||||||
|
return False
|
||||||
|
pos = x
|
||||||
x, y = self.intToCoords(x)
|
x, y = self.intToCoords(x)
|
||||||
else:
|
else:
|
||||||
|
if not self.__validateCoords(x, y):
|
||||||
|
return False
|
||||||
pos = self.coordsToInt(x, y)
|
pos = self.coordsToInt(x, y)
|
||||||
if self.mapMatrix[y][x][0] == 'w':
|
if self.mapMatrix[y][x][0] == 'w':
|
||||||
return False
|
return False
|
||||||
|
@ -423,6 +432,12 @@ to return once it finds a point that's in the locus."""
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def __validateCoords(self, x: int, y: int) -> bool:
|
||||||
|
return x >= 0 and x < self.dimensions[0] and y >= 0 and y < self.dimensions[1]
|
||||||
|
|
||||||
|
def __validatePos(self, pos: int) -> bool:
|
||||||
|
return pos >= 0 and pos < self.dimensions[0] * self.dimensions[1]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __coordsToInt(x, y, width):
|
def __coordsToInt(x, y, width):
|
||||||
return x + y * width
|
return x + y * width
|
||||||
|
@ -633,3 +648,6 @@ class LoSLocus(_gl.Locus):
|
||||||
ret.append(point)
|
ret.append(point)
|
||||||
return iter(ret)
|
return iter(ret)
|
||||||
|
|
||||||
|
# |\_/|
|
||||||
|
# /0 0\
|
||||||
|
# \o/
|
||||||
|
|
114
gamesequence.py
114
gamesequence.py
|
@ -1,48 +1,92 @@
|
||||||
# gamesequence.py
|
# gamesequence.py
|
||||||
|
"""This contains the functions and classes necessary to parse and execute the scripting language.
|
||||||
|
|
||||||
|
Classes:
|
||||||
|
- SequenceError: Derived from RuntimeError.
|
||||||
|
- ScriptBreak: Represent a 'break' statement.
|
||||||
|
- ScriptEnvironment: Represent the environment of a script.
|
||||||
|
|
||||||
|
Functions:
|
||||||
|
- getValueFromString: Translate a literal or variable to a value.
|
||||||
|
- compareValues: compare values for a conditional statement.
|
||||||
|
- ifScript: Execute an 'if' statement.
|
||||||
|
- getCustomValue: Get the value of a global variable.
|
||||||
|
- setCustomValue: Set the value of a global variable.
|
||||||
|
- delCustomValue: Delete a global variable.
|
||||||
|
- runScript: Execute a script.
|
||||||
|
- parseScript: Parse a script.
|
||||||
|
"""
|
||||||
|
|
||||||
import re as _re
|
import re as _re
|
||||||
|
|
||||||
class SequenceError(RuntimeError):
|
class SequenceError(RuntimeError):
|
||||||
|
"""Derived from RuntimeError.
|
||||||
|
|
||||||
|
Raise whenever there is a runtime execution problem while parsing or executing a script.
|
||||||
|
"""
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ScriptBreak(object):
|
class ScriptBreak(object):
|
||||||
|
"""Class created when a 'break' statement is read."""
|
||||||
|
|
||||||
def __init__(self, value):
|
def __init__(self, value):
|
||||||
|
"""Create a script break object with the value returned from the script broken from."""
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
|
class ScriptEnvironment(object):
|
||||||
|
"""Represent the environment of a script.
|
||||||
|
|
||||||
def getValueFromString(arg: str, env: dict):
|
This contains a dictionary of all global variables, as well as local variables."""
|
||||||
#if env == None:
|
|
||||||
#env = self.customValues
|
def __init__(self, globalVars: dict, localVars: dict):
|
||||||
|
"""Create a script environment.
|
||||||
|
|
||||||
|
Global vars will survive beyond the end of the script.
|
||||||
|
Local vars are all dropped at the end of a script."""
|
||||||
|
|
||||||
|
if not isinstance(globalVars, dict):
|
||||||
|
raise TypeError("Global variables must be in a dictionary.")
|
||||||
|
if not isinstance(globalVars, dict):
|
||||||
|
raise TypeError("Local variables must be in a dictionary.")
|
||||||
|
self.globalVars = globalVars
|
||||||
|
self.localVars = localVars
|
||||||
|
|
||||||
|
def getValueFromString(arg: str, env: ScriptEnvironment):
|
||||||
|
"""Translate a literal or variable name into a value.
|
||||||
|
|
||||||
|
arg should be a string representing a literal or identifier.
|
||||||
|
env should be a ScriptEnvironment.
|
||||||
|
Return the value described by the first literal or variable name."""
|
||||||
val = None
|
val = None
|
||||||
|
# We test for a valid identifier here, before all the if-elif starts.
|
||||||
validIdent = _re.match(r'[_A-Za-z][_0-9A-Za-z]*', arg)
|
validIdent = _re.match(r'[_A-Za-z][_0-9A-Za-z]*', arg)
|
||||||
if arg[0] in '"\'' and arg[-1] == arg[0]: # assume it's a string
|
if arg[0] in '"\'' and arg[-1] == arg[0]:
|
||||||
|
# The argument is a string literal.
|
||||||
val = arg[1:-1]
|
val = arg[1:-1]
|
||||||
elif _re.match(r'[+-]?(?:[0-9]*[.])?[0-9]+', arg) != None:
|
elif _re.match(r'[+-]?(?:[0-9]*[.])?[0-9]+', arg) != None:
|
||||||
|
# The argument is a number.
|
||||||
if '.' in arg:
|
if '.' in arg:
|
||||||
|
# The argument is a float.
|
||||||
val = float(arg)
|
val = float(arg)
|
||||||
else:
|
else:
|
||||||
|
# The argument is an int.
|
||||||
val = int(arg)
|
val = int(arg)
|
||||||
elif arg.casefold() == 'true':
|
elif arg.casefold() == 'true':
|
||||||
|
# The argument is the boolean value 'true'.
|
||||||
val = True
|
val = True
|
||||||
elif arg.casefold() == 'false':
|
elif arg.casefold() == 'false':
|
||||||
|
# The argument is the boolean value 'false'.
|
||||||
val = False
|
val = False
|
||||||
#elif arg.casefold() == 'playerx' and self.player != None:
|
|
||||||
# val = self.player.x
|
|
||||||
#elif arg.casefold() == 'playery' and self.player != None:
|
|
||||||
# val = self.player.y
|
|
||||||
#elif arg.casefold() == 'playername' and self.player != None:
|
|
||||||
# val = self.player.name
|
|
||||||
#elif arg.casefold() == 'playerinv' and self.player != None:
|
|
||||||
# val = self.player.inventory
|
|
||||||
#elif arg.casefold() == 'playerdesc' and self.player != None:
|
|
||||||
# val = self.player.description
|
|
||||||
elif validIdent != None and env != None:
|
elif validIdent != None and env != None:
|
||||||
|
# The argument is a variable name.
|
||||||
group = validIdent.group()
|
group = validIdent.group()
|
||||||
if 'scriptLocal' in env and group in env['scriptLocal']:
|
if group in env.localVars:
|
||||||
val = env['scriptLocal'][group]
|
# The variable is local.
|
||||||
elif group in env['global']:
|
val = env.localVars[group]
|
||||||
val = env['global'][group]
|
elif group in env.globalVars:
|
||||||
|
# The variable is global.
|
||||||
|
val = env.globalVars[group]
|
||||||
else:
|
else:
|
||||||
return False # for if statements; if a variable doesn't exist, should evaluate to False
|
return False # for if statements; if a variable doesn't exist, should evaluate to False
|
||||||
# evaluate all values of all indecies
|
# evaluate all values of all indecies
|
||||||
|
@ -67,16 +111,19 @@ def getValueFromString(arg: str, env: dict):
|
||||||
return False
|
return False
|
||||||
openBracket = ptr + 1
|
openBracket = ptr + 1
|
||||||
ptr += 1
|
ptr += 1
|
||||||
|
if depth != 0:
|
||||||
|
#depth should always == 0 at this point
|
||||||
|
raise SequenceError('Mismatched number of open and close brackets: {}'.format(arg))
|
||||||
else:
|
else:
|
||||||
raise SequenceError('Invalid value syntax: {}'.format(arg))
|
raise SequenceError('Invalid value syntax: {}'.format(arg))
|
||||||
else:
|
else:
|
||||||
raise SequenceError('Invalid argument to getValueFromString: {}'.format(arg))
|
raise SequenceError('Invalid argument to getValueFromString: {}'.format(arg))
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def compareValues(args: list, env: dict):
|
def compareValues(args: list, env: ScriptEnvironment):
|
||||||
"""Generalized comparisons, may eventually be extended to other operators"""
|
"""Generalized comparisons, may eventually be extended to other operators"""
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
return bool(getValueFromString(args[0]), env)
|
return bool(getValueFromString(args[0], env))
|
||||||
elif len(args) == 3 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
|
elif len(args) == 3 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
|
||||||
lval = getValueFromString(args[0], env)
|
lval = getValueFromString(args[0], env)
|
||||||
operator = args[1]
|
operator = args[1]
|
||||||
|
@ -94,14 +141,11 @@ def compareValues(args: list, env: dict):
|
||||||
elif operator == '>':
|
elif operator == '>':
|
||||||
return lval > rval
|
return lval > rval
|
||||||
elif operator == 'in':
|
elif operator == 'in':
|
||||||
#if args[2].casefold() == 'playerinv':
|
|
||||||
# return lval in self.player.thingNames
|
|
||||||
#else:
|
|
||||||
return lval in rval
|
return lval in rval
|
||||||
else:
|
else:
|
||||||
raise SequenceError("Condition cannot be evaluated: {}".format(' '.join(args)))
|
raise SequenceError("Condition cannot be evaluated: {}".format(' '.join(args)))
|
||||||
|
|
||||||
def ifScript(args, env: dict, externalScripts: dict):
|
def ifScript(args, env: ScriptEnvironment, externalScripts: dict):
|
||||||
"""If statement: if [not] value [op value] script"""
|
"""If statement: if [not] value [op value] script"""
|
||||||
if len(args) < 2:
|
if len(args) < 2:
|
||||||
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
|
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
|
||||||
|
@ -120,7 +164,7 @@ def ifScript(args, env: dict, externalScripts: dict):
|
||||||
ret = compareValues(args[:3], env)
|
ret = compareValues(args[:3], env)
|
||||||
args = args[3:]
|
args = args[3:]
|
||||||
else:
|
else:
|
||||||
ret = bool(getValueFromString(args[0]), env)
|
ret = bool(getValueFromString(args[0], env))
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
if inverse:
|
if inverse:
|
||||||
ret = not ret
|
ret = not ret
|
||||||
|
@ -141,9 +185,9 @@ def setCustomValue(args, env: dict):
|
||||||
"""takes [customValue, op, value]"""
|
"""takes [customValue, op, value]"""
|
||||||
if env == None:
|
if env == None:
|
||||||
raise SequenceError("Cannot set a value from an empty environment.")
|
raise SequenceError("Cannot set a value from an empty environment.")
|
||||||
scope = env['scriptLocal']
|
scope = env.localVars
|
||||||
if len(args) > 0 and args[0] == 'global':
|
if len(args) > 0 and args[0] == 'global':
|
||||||
scope = env['global']
|
scope = env.globalVars
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
if len(args) < 3 or args[1] not in ('=', '+=', '-=', '*=', '/=', '%=', '//=', '**=', 'b=', '!=', '|=', '&=', '^='):
|
if len(args) < 3 or args[1] not in ('=', '+=', '-=', '*=', '/=', '%=', '//=', '**=', 'b=', '!=', '|=', '&=', '^='):
|
||||||
raise SequenceError('Arguments are not fit for the setCustomValue script.')
|
raise SequenceError('Arguments are not fit for the setCustomValue script.')
|
||||||
|
@ -201,18 +245,18 @@ def setCustomValue(args, env: dict):
|
||||||
scope[args[0]] ^= val
|
scope[args[0]] ^= val
|
||||||
return scope[args[0]]
|
return scope[args[0]]
|
||||||
|
|
||||||
def delCustomValue(args, env: dict):
|
def delCustomValue(args, env: ScriptEnvironment):
|
||||||
"""To clean up after a map."""
|
"""To clean up after a map."""
|
||||||
if env == None:
|
if env == None:
|
||||||
raise SequenceError("Cannot delete a value from an empty environment.")
|
raise SequenceError("Cannot delete a value from an empty environment.")
|
||||||
for i in args:
|
for i in args:
|
||||||
if i in env['global']:
|
if i in env.globalVars:
|
||||||
del(env['global'][i])
|
del(env.globalVars[i])
|
||||||
|
|
||||||
_requireEnv = {'get' : getCustomValue, 'set' : setCustomValue, 'del' : delCustomValue}
|
_requireEnv = {'get' : getCustomValue, 'set' : setCustomValue, 'del' : delCustomValue}
|
||||||
_requireScripts = {'if' : ifScript}
|
_requireScripts = {'if' : ifScript}
|
||||||
|
|
||||||
def runScript(script: list, env: dict, externalScripts: dict):
|
def runScript(script: list, env: ScriptEnvironment, externalScripts: dict):
|
||||||
"""run a script"""
|
"""run a script"""
|
||||||
ret = False
|
ret = False
|
||||||
for line in script:
|
for line in script:
|
||||||
|
@ -228,13 +272,13 @@ def runScript(script: list, env: dict, externalScripts: dict):
|
||||||
ret = _requireEnv[line[0]](line[1:], env)
|
ret = _requireEnv[line[0]](line[1:], env)
|
||||||
elif line[0].casefold() == 'break':
|
elif line[0].casefold() == 'break':
|
||||||
# exit early
|
# exit early
|
||||||
return _ScriptBreak(ret)
|
return ScriptBreak(ret)
|
||||||
elif line[0] in externalScripts:
|
elif line[0] in externalScripts:
|
||||||
# run a script
|
# run a script
|
||||||
ret = externalScripts[line[0]](line[1:])
|
ret = externalScripts[line[0]](line[1:])
|
||||||
else:
|
else:
|
||||||
# conditional evaluation
|
# conditional evaluation
|
||||||
compareValues(line, env)
|
return compareValues(line, env)
|
||||||
|
|
||||||
if isinstance(ret, ScriptBreak):
|
if isinstance(ret, ScriptBreak):
|
||||||
return ret
|
return ret
|
||||||
|
@ -306,9 +350,7 @@ def parseScript(instr: str, envGlobal: dict, externalScripts: dict):
|
||||||
ret.append(''.join(literalStr[argStart:l]))
|
ret.append(''.join(literalStr[argStart:l]))
|
||||||
if len(ret) > 0:
|
if len(ret) > 0:
|
||||||
script.append(ret)
|
script.append(ret)
|
||||||
#print('After parsing: {}'.format(script))
|
env = ScriptEnvironment(envGlobal, {})
|
||||||
env = {'global' : envGlobal, 'scriptLocal' : {}}
|
|
||||||
#self.customValues['scriptLocal'] = {}
|
|
||||||
ret = runScript(script, env, externalScripts)
|
ret = runScript(script, env, externalScripts)
|
||||||
if isinstance(ret, ScriptBreak):
|
if isinstance(ret, ScriptBreak):
|
||||||
ret = ret.value
|
ret = ret.value
|
||||||
|
|
170
gameshell.py
170
gameshell.py
|
@ -3,6 +3,7 @@
|
||||||
from shell import Shell
|
from shell import Shell
|
||||||
from gamebase import GameBase
|
from gamebase import GameBase
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
|
import os as _os
|
||||||
#import re
|
#import re
|
||||||
import heapq
|
import heapq
|
||||||
#import gamemap
|
#import gamemap
|
||||||
|
@ -23,12 +24,12 @@ class GameShell(Shell):
|
||||||
WALLS = ('++', '++', ' +', '++', '++', '||', '++', '|+', '+ ', '++',
|
WALLS = ('++', '++', ' +', '++', '++', '||', '++', '|+', '+ ', '++',
|
||||||
'==', '++', '++', '+|', '++', '++')
|
'==', '++', '++', '+|', '++', '++')
|
||||||
|
|
||||||
def __init__(self, gameBase):
|
def __init__(self, gameBase, gameData = 'testing/testdata.yml'):
|
||||||
super(GameShell, self).__init__()
|
super(GameShell, self).__init__()
|
||||||
self.outstream = _sys.stdout
|
self.outstream = _sys.stdout
|
||||||
self.gameBase = gameBase
|
self.gameBase = gameBase
|
||||||
self.colorMode = 0
|
self.colorMode = 0
|
||||||
data = self.gameBase.loadGameData('testing/testdata.yml')
|
data = self.gameBase.loadGameData(gameData)
|
||||||
self.gameTitle = 'Game Shell' # should be changed for actual games
|
self.gameTitle = 'Game Shell' # should be changed for actual games
|
||||||
if 'title' in data:
|
if 'title' in data:
|
||||||
self.gameTitle = data['title']
|
self.gameTitle = data['title']
|
||||||
|
@ -38,12 +39,16 @@ class GameShell(Shell):
|
||||||
self.openingText = '{}\nIn Development'
|
self.openingText = '{}\nIn Development'
|
||||||
if 'openingText' in data:
|
if 'openingText' in data:
|
||||||
self.openingText = data['openingText']
|
self.openingText = data['openingText']
|
||||||
|
if 'playerName' in data:
|
||||||
|
self.gameBase.playerName = data['playerName']
|
||||||
|
if 'playerDescription' in data:
|
||||||
|
self.gameBase.playerDescription = data['playerDescription']
|
||||||
self.ps2 = '?> '
|
self.ps2 = '?> '
|
||||||
self.__inGame = False
|
self.__inGame = False
|
||||||
|
|
||||||
# register functions
|
# register functions
|
||||||
|
|
||||||
self.registerCommand('load', self.gameBase.loadGame) # should always be available
|
self.registerCommand('load', self.loadGame) # should always be available
|
||||||
self.registerCommand('flippetywick', self.devMode)
|
self.registerCommand('flippetywick', self.devMode)
|
||||||
self.registerCommand('options', self.options)
|
self.registerCommand('options', self.options)
|
||||||
self.registerCommand('colorTest', self.colorTest)
|
self.registerCommand('colorTest', self.colorTest)
|
||||||
|
@ -62,7 +67,6 @@ class GameShell(Shell):
|
||||||
|
|
||||||
def man(self, args):
|
def man(self, args):
|
||||||
super(GameShell, self).man(args)
|
super(GameShell, self).man(args)
|
||||||
#heapq.heappush(self.gameBase.eventQueue, (self.gameBase.gameTime, gameevents.NoOpEvent()))
|
|
||||||
|
|
||||||
def options(self, args):
|
def options(self, args):
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -171,25 +175,26 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
for i in things[1:]:
|
for i in things[1:]:
|
||||||
if priorities[i.thingType] < priorities[thing.thingType]:
|
if priorities[i.thingType] < priorities[thing.thingType]:
|
||||||
thing = i
|
thing = i
|
||||||
if thing.graphic[1] != textColor:
|
if thing.graphic.foreground != textColor:
|
||||||
textColor = thing.graphic[1]
|
textColor = thing.graphic.foreground
|
||||||
|
#print(textColor)
|
||||||
rows[-1].append(self.color(textColor[1:]))
|
rows[-1].append(self.color(textColor[1:]))
|
||||||
if thing.thingType == 'p': # player
|
if thing.thingType == 'p': # player
|
||||||
rows[-1].append('()')
|
rows[-1].append('()')
|
||||||
elif thing.thingType == 'x': # exit
|
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.foreground)
|
||||||
elif thing.thingType == 'n': # NPC
|
elif thing.thingType == 'n': # NPC
|
||||||
characters[len(characters)+1] = (thing.name, thing.graphic[1])
|
characters[len(characters)+1] = (thing.name, thing.graphic.foreground)
|
||||||
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
|
||||||
doors[len(doors)+1] = (thing.name, thing.graphic[1])
|
doors[len(doors)+1] = (thing.name, thing.graphic.foreground)
|
||||||
rows[-1].append('D{0}'.format(len(doors)))
|
rows[-1].append('D{0}'.format(len(doors)))
|
||||||
elif thing.thingType == 'u': # useable
|
elif thing.thingType == 'u': # useable
|
||||||
useables[len(useables)+1] = (thing.name, thing.graphic[1])
|
useables[len(useables)+1] = (thing.name, thing.graphic.foreground)
|
||||||
rows[-1].append('U{0}'.format(len(useables)))
|
rows[-1].append('U{0}'.format(len(useables)))
|
||||||
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.foreground)
|
||||||
rows[-1].append('I{0}'.format(len(items)))
|
rows[-1].append('I{0}'.format(len(items)))
|
||||||
else: # entrance
|
else: # entrance
|
||||||
rows[-1].append(' ')
|
rows[-1].append(' ')
|
||||||
|
@ -254,7 +259,7 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
ret.append("Gametime:{0:.>71.3}".format(self.gameBase.gameTime))
|
ret.append("Gametime:{0:.>71.3}".format(self.gameBase.gameTime))
|
||||||
ret.append("Event queue:")
|
ret.append("Event queue:")
|
||||||
for i in sorted(self.gameBase.eventQueue):
|
for i in sorted(self.gameBase.eventQueue):
|
||||||
ret.append("{0:.<8.3}:{1:.>72}".format(i[0], str(i[1])))
|
ret.append("{0:.<8.3}:{1:.>71}".format(i[0], str(i[1])))
|
||||||
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]))
|
||||||
|
@ -283,19 +288,19 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
response = input("Do you want to save before exiting (Y/n/x)? ")
|
response = input("Do you want to save before exiting (Y/n/x)? ")
|
||||||
if len(response) > 0:
|
if len(response) > 0:
|
||||||
if response[0] in 'Nn':
|
if response[0] in 'Nn':
|
||||||
super(GameShell, self).exitShell(args)
|
pass
|
||||||
elif response[0] in 'Xx': # cancel
|
elif response[0] in 'Xx': # cancel
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
sf = input('Save file: ')
|
sf = input('Save file: ')
|
||||||
if len(sf) > 0:
|
if len(sf) > 0:
|
||||||
gameBase.saveGame([sf])
|
self.gameBase.saveGame([sf])
|
||||||
else:
|
else:
|
||||||
print('No save file given, cancelling exit.')
|
print('No save file given, cancelling exit.')
|
||||||
else:
|
else:
|
||||||
sf = input('Save file: ')
|
sf = input('Save file: ')
|
||||||
if len(sf) > 0:
|
if len(sf) > 0:
|
||||||
gameBase.saveGame([sf])
|
self.gameBase.saveGame([sf])
|
||||||
else:
|
else:
|
||||||
print('No save file given, cancelling exit.')
|
print('No save file given, cancelling exit.')
|
||||||
try:
|
try:
|
||||||
|
@ -305,6 +310,43 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
return
|
return
|
||||||
self.gameMode()
|
self.gameMode()
|
||||||
|
|
||||||
|
def loadGame(self, args):
|
||||||
|
# Check if there's a name in args. If not, just present a list of save files.
|
||||||
|
if len(args) == 0:
|
||||||
|
import pickle as _pi
|
||||||
|
for fileName in _os.listdir('saves'):
|
||||||
|
with open(f'saves/{fileName}', 'rb') as f:
|
||||||
|
player, levelname, persist, eventQueue, customValues, gameTime, nextThing = _pi.load(f)
|
||||||
|
print(f'{fileName[:-4]}\n - {levelname[5:-4]}')
|
||||||
|
return
|
||||||
|
if self.__inGame:
|
||||||
|
response = input("Do you want to save before exiting (Y/n/x)? ")
|
||||||
|
if len(response) > 0:
|
||||||
|
if response[0] in 'Nn':
|
||||||
|
pass
|
||||||
|
elif response[0] in 'Xx': # cancel
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
self.gameBase.saveGame([sf])
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
self.gameBase.saveGame([sf])
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.gameBase.loadGame(args)
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(e)
|
||||||
|
return
|
||||||
|
self.gameMode()
|
||||||
|
|
||||||
def gameMode(self):
|
def gameMode(self):
|
||||||
"""Mode for in-game."""
|
"""Mode for in-game."""
|
||||||
if not self.__inGame:
|
if not self.__inGame:
|
||||||
|
@ -366,7 +408,7 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
|
|
||||||
# IO calls
|
# IO calls
|
||||||
|
|
||||||
def container(self, player, cont):
|
def container(self, player, cont: list):
|
||||||
"""container IO
|
"""container IO
|
||||||
Player is modified through side-effect."""
|
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
|
||||||
|
@ -393,36 +435,64 @@ Player is modified through side-effect."""
|
||||||
print(i.name)
|
print(i.name)
|
||||||
# Now, actually interacting with the container
|
# Now, actually interacting with the container
|
||||||
timeSpent = 0.5 # using a container always takes at least 1/2 second, even just opening and closing it again.
|
timeSpent = 0.5 # using a container always takes at least 1/2 second, even just opening and closing it again.
|
||||||
instr = input("Take, store, or exit: ")
|
instr = input("Look, take, store, or exit: ")
|
||||||
while instr != "exit":
|
while instr != "exit":
|
||||||
instr = instr.split()
|
instr = instr.split()
|
||||||
if instr[0] == "take":
|
if len(instr) != 0:
|
||||||
# take something out of the container
|
if instr[0] == "take":
|
||||||
if instr[1] == "the":
|
# take something out of the container
|
||||||
del instr[1]
|
if len(instr) > 1 and instr[1] == "the":
|
||||||
thing = ' '.join(instr[1:])
|
del instr[1]
|
||||||
for i in range(len(cont)):
|
thingName = ' '.join(instr[1:])
|
||||||
if thing == cont[i].name:
|
if len(thingName) == 0:
|
||||||
player.addThing(cont[i])
|
print(f"{self.gameBase.player.name} takes nothing.")
|
||||||
del cont[i]
|
else:
|
||||||
|
for i in range(len(cont)):
|
||||||
|
if thingName == cont[i].name:
|
||||||
|
player.addThing(cont[i])
|
||||||
|
del cont[i]
|
||||||
|
timeSpent += 0.5
|
||||||
|
print(f"{thingName} taken.")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If it got here, it didn't find it.
|
||||||
|
print(f"No {thingName} in container.")
|
||||||
|
elif instr[0] == "store":
|
||||||
|
# store something in the container
|
||||||
|
if len(instr) > 1 and instr[1] == "the":
|
||||||
|
del instr[1]
|
||||||
|
thingName = ' '.join(instr[1:])
|
||||||
|
if len(thingName) == 0:
|
||||||
|
print(f"{self.gameBase.player.name} stores nothing.")
|
||||||
|
elif thingName in player.thingNames:
|
||||||
|
cont.append(player.removeThingByName(thingName))
|
||||||
|
print(f"{thingName} stored.")
|
||||||
timeSpent += 0.5
|
timeSpent += 0.5
|
||||||
print("{0} taken.".format(thing))
|
else:
|
||||||
break
|
print(f"No {thingName} in inventory.")
|
||||||
else:
|
elif instr[0] == "look":
|
||||||
# If it got here, it didn't find it.
|
# look at something in the container
|
||||||
print("No {0} in container.".format(thing))
|
if len(instr) > 1 and instr[1] == "at":
|
||||||
elif instr[0] == "store":
|
del instr[1]
|
||||||
# store something in the container
|
if len(instr) > 1 and instr[1] == "the":
|
||||||
if instr[1] == "the":
|
del instr[1]
|
||||||
del instr[1]
|
thingName = ' '.join(instr[1:])
|
||||||
thingName = ' '.join(instr[1:])
|
if len(thingName) == 0:
|
||||||
if thingName in player.thingNames:
|
print(f"{self.gameBase.player.name} looks at nothing.")
|
||||||
cont.append(player.removeThingByName(thingName))
|
else:
|
||||||
print("{0} stored.".format(thingName))
|
# Check the container first.
|
||||||
timeSpent += 0.5
|
for i in range(len(cont)):
|
||||||
else:
|
if thingName == cont[i].name:
|
||||||
print("No {0} in inventory.".format(thingName))
|
print(self.gameBase.justifyText(str(cont[i])))
|
||||||
instr = input("Take, store, or exit: ")
|
break
|
||||||
|
else:
|
||||||
|
# If it wasn't there, try the player's inventory.
|
||||||
|
if thingName in player.thingNames:
|
||||||
|
print(self.gameBase.justifyText(str(player.getThingByName(thingName))))
|
||||||
|
else:
|
||||||
|
# If we get here, it just isn't there.
|
||||||
|
print(f"There is no {thingName} to look at.")
|
||||||
|
instr = input("Look, take, store, or exit: ")
|
||||||
return cont, timeSpent
|
return cont, timeSpent
|
||||||
|
|
||||||
def info(self, items):
|
def info(self, items):
|
||||||
|
@ -456,7 +526,11 @@ Player is modified through side-effect."""
|
||||||
print(_tw.fill('{}: {}'.format(lineNo+1, options[lineNo]), width = TERM_SIZE))
|
print(_tw.fill('{}: {}'.format(lineNo+1, options[lineNo]), width = TERM_SIZE))
|
||||||
answer = -1
|
answer = -1
|
||||||
while answer < 0 or answer >= len(options):
|
while answer < 0 or answer >= len(options):
|
||||||
answer = int(input(self.ps2)) - 1
|
answerString = input(self.ps2)
|
||||||
|
if not answerString.isdigit():
|
||||||
|
# If the player inputs a non-integer, just prompt again.
|
||||||
|
continue
|
||||||
|
answer = int(answerString) - 1
|
||||||
return answer
|
return answer
|
||||||
|
|
||||||
def endDialog(self):
|
def endDialog(self):
|
||||||
|
@ -479,14 +553,14 @@ Player is modified through side-effect."""
|
||||||
else:
|
else:
|
||||||
sf = input('Save file: ')
|
sf = input('Save file: ')
|
||||||
if len(sf) > 0:
|
if len(sf) > 0:
|
||||||
gameBase.saveGame([sf])
|
self.gameBase.saveGame([sf])
|
||||||
super(GameShell, self).exitShell(args)
|
super(GameShell, self).exitShell(args)
|
||||||
else:
|
else:
|
||||||
print('No save file given, cancelling exit.')
|
print('No save file given, cancelling exit.')
|
||||||
else:
|
else:
|
||||||
sf = input('Save file: ')
|
sf = input('Save file: ')
|
||||||
if len(sf) > 0:
|
if len(sf) > 0:
|
||||||
gameBase.saveGame([sf])
|
self.gameBase.saveGame([sf])
|
||||||
super(GameShell, self).exitShell(args)
|
super(GameShell, self).exitShell(args)
|
||||||
else:
|
else:
|
||||||
print('No save file given, cancelling exit.')
|
print('No save file given, cancelling exit.')
|
||||||
|
@ -497,3 +571,7 @@ if __name__ == '__main__':
|
||||||
sh = GameShell(GameBase())
|
sh = GameShell(GameBase())
|
||||||
sh.menuMode()
|
sh.menuMode()
|
||||||
sh.run()
|
sh.run()
|
||||||
|
|
||||||
|
# |\_/|
|
||||||
|
# /0 0\
|
||||||
|
# \o/
|
||||||
|
|
173
gamethings.py
173
gamethings.py
|
@ -1,8 +1,51 @@
|
||||||
#gamethings.py
|
#gamethings.py
|
||||||
|
"""Standard thing classes.
|
||||||
|
|
||||||
|
Classes:
|
||||||
|
- ThingGraphic: Represents how thing is to be visualized.
|
||||||
|
- Thing: The abstract base class for every thing.
|
||||||
|
- Observer: The abstract base class for things that listen to events.
|
||||||
|
- Item: A thing that can exist in a character's inventory.
|
||||||
|
- Useable: A thing that can be used by a character.
|
||||||
|
- Character: A thing that represents a character.
|
||||||
|
- NPC: A character that is not controlled by the player.
|
||||||
|
- Door: A thing that sometimes blocks paths.
|
||||||
|
- MapExit: A technical thing that marks a map transition.
|
||||||
|
"""
|
||||||
|
|
||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
from ruamel.yaml.comments import CommentedMap
|
from ruamel.yaml.comments import CommentedMap
|
||||||
|
|
||||||
|
class ThingGraphic(object):
|
||||||
|
"""Represent how a thing is to be visualized.
|
||||||
|
|
||||||
|
Background color, foreground color, and shape are represented.
|
||||||
|
This could hypothetically be extended to hold sprites, textures or models."""
|
||||||
|
|
||||||
|
def __init__(self, background: str, foreground: str, shape: str):
|
||||||
|
"""Create a graphic for a thing.
|
||||||
|
|
||||||
|
The background and foregrond must be strings with color hex representations.
|
||||||
|
For instance: '#00FF00' means bright green.
|
||||||
|
The shape must be a string containing exactly one of the following shapes:
|
||||||
|
o: circle
|
||||||
|
x: cross
|
||||||
|
-: horizontal line
|
||||||
|
|: vertical line
|
||||||
|
#: square
|
||||||
|
^: triangle
|
||||||
|
A graphic may be invalid.
|
||||||
|
"""
|
||||||
|
if not isinstance(background, str):
|
||||||
|
raise TypeError("Background must be a string of form '#[0-9A-Fa-f]{6}'.")
|
||||||
|
if not isinstance(foreground, str):
|
||||||
|
raise TypeError("Foreground must be a string of form '#[0-9A-Fa-f]{6}'.")
|
||||||
|
if not isinstance(shape, str):
|
||||||
|
raise TypeError("Shape must be a string of form '[-|ox#^]'.")
|
||||||
|
self.background = background
|
||||||
|
self.foreground = foreground
|
||||||
|
self.shape = shape
|
||||||
|
|
||||||
class Thing(object):
|
class Thing(object):
|
||||||
|
|
||||||
def __init__(self, thingType: str, name: str, x: int, y: int, description: str, flags: int, playerx = None, playery = None, **kwargs):
|
def __init__(self, thingType: str, name: str, x: int, y: int, description: str, flags: int, playerx = None, playery = None, **kwargs):
|
||||||
|
@ -24,7 +67,7 @@ class Thing(object):
|
||||||
self.lookable = bool(flags & 4)
|
self.lookable = bool(flags & 4)
|
||||||
self.takeable = bool(flags & 8)
|
self.takeable = bool(flags & 8)
|
||||||
self.useable = bool(flags & 16)
|
self.useable = bool(flags & 16)
|
||||||
self.graphic = ('clear', '#7F7F7F', ' ')
|
self.graphic = ThingGraphic('clear', '#7F7F7F', ' ')
|
||||||
self.thingID = -1 # ID not assigned
|
self.thingID = -1 # ID not assigned
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -49,7 +92,7 @@ class Observer(Thing):
|
||||||
|
|
||||||
class Item(Thing):
|
class Item(Thing):
|
||||||
yaml_flag = u'!Item'
|
yaml_flag = u'!Item'
|
||||||
defaultGraphic = ('clear', '#00BF00', '^')
|
defaultGraphic = ThingGraphic('clear', '#00BF00', '^')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, description: str, useFunc: str, useOnFunc: str, customValues: dict, ranged: bool, graphic = defaultGraphic):
|
def __init__(self, name, x: int, y: int, description: str, useFunc: str, useOnFunc: str, customValues: dict, ranged: bool, graphic = defaultGraphic):
|
||||||
super(Item, self).__init__('i', name, x, y, description, 13)
|
super(Item, self).__init__('i', name, x, y, description, 13)
|
||||||
|
@ -68,12 +111,12 @@ class Item(Thing):
|
||||||
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
||||||
# save graphic
|
# save graphic
|
||||||
graphic = {}
|
graphic = {}
|
||||||
if node.graphic[0] != Item.defaultGraphic[0]:
|
if node.graphic.background != Item.defaultGraphic.background:
|
||||||
graphic['bgc'] = node.graphic[0]
|
graphic['bgc'] = node.graphic.background
|
||||||
if node.graphic[1] != Item.defaultGraphic[1]:
|
if node.graphic.foreground != Item.defaultGraphic.forground:
|
||||||
graphic['fgc'] = node.graphic[1]
|
graphic['fgc'] = node.graphic.foreground
|
||||||
if node.graphic[2] != Item.defaultGraphic[2]:
|
if node.graphic.shape != Item.defaultGraphic.shape:
|
||||||
graphic['shape'] = node.graphic[2]
|
graphic['shape'] = node.graphicshape
|
||||||
if len(graphic) > 0:
|
if len(graphic) > 0:
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
# save use functions
|
# save use functions
|
||||||
|
@ -96,9 +139,9 @@ class Item(Thing):
|
||||||
useOnFunc = ''
|
useOnFunc = ''
|
||||||
customValues = {}
|
customValues = {}
|
||||||
ranged = False
|
ranged = False
|
||||||
bgc = Item.defaultGraphic[0]
|
bgc = Item.defaultGraphic.background
|
||||||
fgc = Item.defaultGraphic[1]
|
fgc = Item.defaultGraphic.foreground
|
||||||
shape = Item.defaultGraphic[2]
|
shape = Item.defaultGraphic.shape
|
||||||
# load graphic
|
# load graphic
|
||||||
if 'graphic' in parts:
|
if 'graphic' in parts:
|
||||||
if 'bgc' in parts['graphic']:
|
if 'bgc' in parts['graphic']:
|
||||||
|
@ -107,7 +150,7 @@ class Item(Thing):
|
||||||
fgc = parts['graphic']['fgc']
|
fgc = parts['graphic']['fgc']
|
||||||
if 'shape' in parts['graphic']:
|
if 'shape' in parts['graphic']:
|
||||||
shape = parts['graphic']['shape']
|
shape = parts['graphic']['shape']
|
||||||
graphic = (bgc, fgc, shape)
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
# load use functions
|
# load use functions
|
||||||
if 'useFunc' in parts:
|
if 'useFunc' in parts:
|
||||||
useFunc = parts['useFunc']
|
useFunc = parts['useFunc']
|
||||||
|
@ -131,7 +174,7 @@ class Item(Thing):
|
||||||
|
|
||||||
class Useable(Thing):
|
class Useable(Thing):
|
||||||
yaml_flag = u'!Useable'
|
yaml_flag = u'!Useable'
|
||||||
defaultGraphic = ('clear', '#0000FF', '#')
|
defaultGraphic = ThingGraphic('clear', '#0000FF', '#')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, description: str, useFunc: str, customValues: dict, playerx = None, playery = None, graphic = defaultGraphic):
|
def __init__(self, name, x: int, y: int, description: str, useFunc: str, customValues: dict, playerx = None, playery = None, graphic = defaultGraphic):
|
||||||
super(Useable, self).__init__('u', name, x, y, description, 16, playerx, playery)
|
super(Useable, self).__init__('u', name, x, y, description, 16, playerx, playery)
|
||||||
|
@ -145,12 +188,12 @@ class Useable(Thing):
|
||||||
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
||||||
# save graphic
|
# save graphic
|
||||||
graphic = {}
|
graphic = {}
|
||||||
if node.graphic[0] != Useable.defaultGraphic[0]:
|
if node.graphic.background != Useable.defaultGraphic.background:
|
||||||
graphic['bgc'] = node.graphic[0]
|
graphic['bgc'] = node.graphic.background
|
||||||
if node.graphic[1] != Useable.defaultGraphic[1]:
|
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||||
graphic['fgc'] = node.graphic[1]
|
graphic['fgc'] = node.graphic.foreground
|
||||||
if node.graphic[2] != Useable.defaultGraphic[2]:
|
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||||
graphic['shape'] = node.graphic[2]
|
graphic['shape'] = node.graphic.shape
|
||||||
if len(graphic) > 0:
|
if len(graphic) > 0:
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
# save use functions
|
# save use functions
|
||||||
|
@ -170,9 +213,9 @@ class Useable(Thing):
|
||||||
useFunc = ''
|
useFunc = ''
|
||||||
customValues = {}
|
customValues = {}
|
||||||
playerx, playery = parts['location']
|
playerx, playery = parts['location']
|
||||||
bgc = Useable.defaultGraphic[0]
|
bgc = Useable.defaultGraphic.background
|
||||||
fgc = Useable.defaultGraphic[1]
|
fgc = Useable.defaultGraphic.foreground
|
||||||
shape = Useable.defaultGraphic[2]
|
shape = Useable.defaultGraphic.shape
|
||||||
# load graphic
|
# load graphic
|
||||||
if 'graphic' in parts:
|
if 'graphic' in parts:
|
||||||
if 'bgc' in parts['graphic']:
|
if 'bgc' in parts['graphic']:
|
||||||
|
@ -181,7 +224,7 @@ class Useable(Thing):
|
||||||
fgc = parts['graphic']['fgc']
|
fgc = parts['graphic']['fgc']
|
||||||
if 'shape' in parts['graphic']:
|
if 'shape' in parts['graphic']:
|
||||||
shape = parts['graphic']['shape']
|
shape = parts['graphic']['shape']
|
||||||
graphic = (bgc, fgc, shape)
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
# load use functions
|
# load use functions
|
||||||
if 'useFunc' in parts:
|
if 'useFunc' in parts:
|
||||||
useFunc = parts['useFunc']
|
useFunc = parts['useFunc']
|
||||||
|
@ -199,7 +242,7 @@ class Useable(Thing):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Character(Thing):
|
class Character(Thing):
|
||||||
defaultGraphic = ('clear', '#000000', 'o')
|
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
||||||
|
|
||||||
def __init__(self, inventory: dict, customValues: dict, graphic = defaultGraphic, **kwargs):
|
def __init__(self, inventory: dict, customValues: dict, graphic = defaultGraphic, **kwargs):
|
||||||
super(Character, self).__init__(**kwargs)
|
super(Character, self).__init__(**kwargs)
|
||||||
|
@ -211,7 +254,7 @@ class Character(Thing):
|
||||||
self.__inventory = inventory
|
self.__inventory = inventory
|
||||||
self.customValues = customValues
|
self.customValues = customValues
|
||||||
self.graphic = graphic
|
self.graphic = graphic
|
||||||
self.thingNames = {}
|
self.thingNames = {} #{str: int}
|
||||||
# set up inventory shtuff
|
# set up inventory shtuff
|
||||||
for i in self.__inventory:
|
for i in self.__inventory:
|
||||||
if self.__inventory[i].name in self.thingNames:
|
if self.__inventory[i].name in self.thingNames:
|
||||||
|
@ -229,7 +272,10 @@ class Character(Thing):
|
||||||
self.thingNames[thing.name] = [thing.thingID]
|
self.thingNames[thing.name] = [thing.thingID]
|
||||||
|
|
||||||
def getThingByID(self, thingID):
|
def getThingByID(self, thingID):
|
||||||
return self.__inventory[thingID]
|
if thingID in self.__inventory:
|
||||||
|
return self.__inventory[thingID]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
def getThingByName(self, name):
|
def getThingByName(self, name):
|
||||||
if name in self.thingNames:
|
if name in self.thingNames:
|
||||||
|
@ -247,10 +293,10 @@ class Character(Thing):
|
||||||
|
|
||||||
def removeThingByName(self, name):
|
def removeThingByName(self, name):
|
||||||
ret = self.getThingByName(name)
|
ret = self.getThingByName(name)
|
||||||
self.thingNames[ret.name].remove(thingID)
|
self.thingNames[ret.name].remove(ret.thingID)
|
||||||
if len(self.thingNames[ret.name]) == 0:
|
if len(self.thingNames[ret.name]) == 0:
|
||||||
del self.thingNames[ret.name]
|
del self.thingNames[ret.name]
|
||||||
del self.__inventory[thingID]
|
del self.__inventory[ret.thingID]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def removeThing(self, ret):
|
def removeThing(self, ret):
|
||||||
|
@ -267,7 +313,7 @@ class Character(Thing):
|
||||||
|
|
||||||
class NPC(Character, Observer):
|
class NPC(Character, Observer):
|
||||||
yaml_flag = u'!NPC'
|
yaml_flag = u'!NPC'
|
||||||
defaultGraphic = ('clear', '#000000', 'o')
|
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
||||||
|
|
||||||
def __init__(self, behaviors: dict, tempInventory: list, **kwargs):
|
def __init__(self, behaviors: dict, tempInventory: list, **kwargs):
|
||||||
if 'graphic' not in kwargs:
|
if 'graphic' not in kwargs:
|
||||||
|
@ -284,12 +330,12 @@ class NPC(Character, Observer):
|
||||||
'description': node.description, 'behaviors': node.behaviors}
|
'description': node.description, 'behaviors': node.behaviors}
|
||||||
# save graphic
|
# save graphic
|
||||||
graphic = {}
|
graphic = {}
|
||||||
if node.graphic[0] != NPC.defaultGraphic[0]:
|
if node.graphic.background != Useable.defaultGraphic.background:
|
||||||
graphic['bgc'] = node.graphic[0]
|
graphic['bgc'] = node.graphic.background
|
||||||
if node.graphic[1] != NPC.defaultGraphic[1]:
|
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||||
graphic['fgc'] = node.graphic[1]
|
graphic['fgc'] = node.graphic.foreground
|
||||||
if node.graphic[2] != NPC.defaultGraphic[2]:
|
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||||
graphic['shape'] = node.graphic[2]
|
graphic['shape'] = node.graphic.shape
|
||||||
if len(graphic) > 0:
|
if len(graphic) > 0:
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
# save use functions
|
# save use functions
|
||||||
|
@ -310,9 +356,9 @@ class NPC(Character, Observer):
|
||||||
minventory = []
|
minventory = []
|
||||||
mcustomValues = {}
|
mcustomValues = {}
|
||||||
mplayerx, mplayery = parts['location']
|
mplayerx, mplayery = parts['location']
|
||||||
bgc = NPC.defaultGraphic[0]
|
bgc = NPC.defaultGraphic.background
|
||||||
fgc = NPC.defaultGraphic[1]
|
fgc = NPC.defaultGraphic.foreground
|
||||||
shape = NPC.defaultGraphic[2]
|
shape = NPC.defaultGraphic.shape
|
||||||
# load graphic
|
# load graphic
|
||||||
if 'graphic' in parts:
|
if 'graphic' in parts:
|
||||||
if 'bgc' in parts['graphic']:
|
if 'bgc' in parts['graphic']:
|
||||||
|
@ -321,7 +367,7 @@ class NPC(Character, Observer):
|
||||||
fgc = parts['graphic']['fgc']
|
fgc = parts['graphic']['fgc']
|
||||||
if 'shape' in parts['graphic']:
|
if 'shape' in parts['graphic']:
|
||||||
shape = parts['graphic']['shape']
|
shape = parts['graphic']['shape']
|
||||||
mgraphic = (bgc, fgc, shape)
|
mgraphic = ThingGraphic(bgc, fgc, shape)
|
||||||
# load use functions
|
# load use functions
|
||||||
if 'inventory' in parts:
|
if 'inventory' in parts:
|
||||||
inventory = parts['inventory']
|
inventory = parts['inventory']
|
||||||
|
@ -338,7 +384,7 @@ class NPC(Character, Observer):
|
||||||
|
|
||||||
class Door(Thing):
|
class Door(Thing):
|
||||||
yaml_flag = u'!Door'
|
yaml_flag = u'!Door'
|
||||||
defaultGraphic = ('clear', '#7F3F00', '#')
|
defaultGraphic = ThingGraphic('clear', '#7F3F00', '#')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, locked: bool, description = None, key = None, graphic = defaultGraphic):
|
def __init__(self, name, x: int, y: int, locked: bool, description = None, key = None, graphic = defaultGraphic):
|
||||||
self.descBase = description
|
self.descBase = description
|
||||||
|
@ -379,12 +425,12 @@ class Door(Thing):
|
||||||
ret = {'name': node.name, 'location': (node.x, node.y)}
|
ret = {'name': node.name, 'location': (node.x, node.y)}
|
||||||
# save graphic
|
# save graphic
|
||||||
graphic = {}
|
graphic = {}
|
||||||
if node.graphic[0] != Door.defaultGraphic[0]:
|
if node.graphic.background != Useable.defaultGraphic.background:
|
||||||
graphic['bgc'] = node.graphic[0]
|
graphic['bgc'] = node.graphic.background
|
||||||
if node.graphic[1] != Door.defaultGraphic[1]:
|
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||||
graphic['fgc'] = node.graphic[1]
|
graphic['fgc'] = node.graphic.foreground
|
||||||
if node.graphic[2] != Door.defaultGraphic[2]:
|
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||||
graphic['shape'] = node.graphic[2]
|
graphic['shape'] = node.graphic.shape
|
||||||
if len(graphic) > 0:
|
if len(graphic) > 0:
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
# save door state
|
# save door state
|
||||||
|
@ -404,9 +450,9 @@ class Door(Thing):
|
||||||
description = None
|
description = None
|
||||||
locked = False
|
locked = False
|
||||||
key = None
|
key = None
|
||||||
bgc = Door.defaultGraphic[0]
|
bgc = Door.defaultGraphic.background
|
||||||
fgc = Door.defaultGraphic[1]
|
fgc = Door.defaultGraphic.foreground
|
||||||
shape = Door.defaultGraphic[2]
|
shape = Door.defaultGraphic.shape
|
||||||
# load graphic
|
# load graphic
|
||||||
if 'graphic' in parts:
|
if 'graphic' in parts:
|
||||||
if 'bgc' in parts['graphic']:
|
if 'bgc' in parts['graphic']:
|
||||||
|
@ -415,7 +461,7 @@ class Door(Thing):
|
||||||
fgc = parts['graphic']['fgc']
|
fgc = parts['graphic']['fgc']
|
||||||
if 'shape' in parts['graphic']:
|
if 'shape' in parts['graphic']:
|
||||||
shape = parts['graphic']['shape']
|
shape = parts['graphic']['shape']
|
||||||
graphic = (bgc, fgc, shape)
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
# load door state
|
# load door state
|
||||||
if 'description' in parts:
|
if 'description' in parts:
|
||||||
description = parts['description']
|
description = parts['description']
|
||||||
|
@ -428,7 +474,7 @@ class Door(Thing):
|
||||||
|
|
||||||
class MapExit(Thing):
|
class MapExit(Thing):
|
||||||
yaml_flag = u'!MapExit'
|
yaml_flag = u'!MapExit'
|
||||||
defaultGraphic = ('clear', '#FF0000', 'x')
|
defaultGraphic = ThingGraphic('clear', '#FF0000', 'x')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, exitid: int, destination: str, prefix = None, onUse = '', key = True, graphic = defaultGraphic):
|
def __init__(self, name, x: int, y: int, exitid: int, destination: str, prefix = None, onUse = '', key = True, graphic = defaultGraphic):
|
||||||
description = name
|
description = name
|
||||||
|
@ -448,12 +494,12 @@ class MapExit(Thing):
|
||||||
ret = {'name': node.name, 'location': (node.x, node.y), 'id': node.exitid, 'destination': node.destination}
|
ret = {'name': node.name, 'location': (node.x, node.y), 'id': node.exitid, 'destination': node.destination}
|
||||||
# save graphic
|
# save graphic
|
||||||
graphic = {}
|
graphic = {}
|
||||||
if node.graphic[0] != MapExit.defaultGraphic[0]:
|
if node.graphic.background != Useable.defaultGraphic.background:
|
||||||
graphic['bgc'] = node.graphic[0]
|
graphic['bgc'] = node.graphic.background
|
||||||
if node.graphic[1] != MapExit.defaultGraphic[1]:
|
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||||
graphic['fgc'] = node.graphic[1]
|
graphic['fgc'] = node.graphic.foreground
|
||||||
if node.graphic[2] != MapExit.defaultGraphic[2]:
|
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||||
graphic['shape'] = node.graphic[2]
|
graphic['shape'] = node.graphic.shape
|
||||||
if len(graphic) > 0:
|
if len(graphic) > 0:
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
if node.prefix != None:
|
if node.prefix != None:
|
||||||
|
@ -472,9 +518,9 @@ class MapExit(Thing):
|
||||||
prefix = None
|
prefix = None
|
||||||
onUse = ''
|
onUse = ''
|
||||||
key = True
|
key = True
|
||||||
bgc = MapExit.defaultGraphic[0]
|
bgc = MapExit.defaultGraphic.background
|
||||||
fgc = MapExit.defaultGraphic[1]
|
fgc = MapExit.defaultGraphic.foreground
|
||||||
shape = MapExit.defaultGraphic[2]
|
shape = MapExit.defaultGraphic.shape
|
||||||
# load graphic
|
# load graphic
|
||||||
if 'graphic' in parts:
|
if 'graphic' in parts:
|
||||||
if 'bgc' in parts['graphic']:
|
if 'bgc' in parts['graphic']:
|
||||||
|
@ -483,7 +529,7 @@ class MapExit(Thing):
|
||||||
fgc = parts['graphic']['fgc']
|
fgc = parts['graphic']['fgc']
|
||||||
if 'shape' in parts['graphic']:
|
if 'shape' in parts['graphic']:
|
||||||
shape = parts['graphic']['shape']
|
shape = parts['graphic']['shape']
|
||||||
graphic = (bgc, fgc, shape)
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
if 'prefix' in parts:
|
if 'prefix' in parts:
|
||||||
prefix = parts['prefix']
|
prefix = parts['prefix']
|
||||||
if 'onUse' in parts:
|
if 'onUse' in parts:
|
||||||
|
@ -495,7 +541,7 @@ class MapExit(Thing):
|
||||||
|
|
||||||
class MapEntrance(Thing):
|
class MapEntrance(Thing):
|
||||||
yaml_flag = u'!MapEntrance'
|
yaml_flag = u'!MapEntrance'
|
||||||
defaultGraphic = ('clear', 'clear', 'x')
|
defaultGraphic = ThingGraphic('clear', 'clear', 'x')
|
||||||
# no graphic - should not be drawn
|
# no graphic - should not be drawn
|
||||||
|
|
||||||
def __init__(self, x: int, y: int, exitid: int, name = None):
|
def __init__(self, x: int, y: int, exitid: int, name = None):
|
||||||
|
@ -503,6 +549,7 @@ class MapEntrance(Thing):
|
||||||
name = 'entrance {}'.format(exitid)
|
name = 'entrance {}'.format(exitid)
|
||||||
super(MapEntrance, self).__init__('a', name, x, y, '', 1)
|
super(MapEntrance, self).__init__('a', name, x, y, '', 1)
|
||||||
self.exitid = exitid
|
self.exitid = exitid
|
||||||
|
#self.graphic = MapEntrance.defaultGraphic
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_yaml(cls, representer, node):
|
def to_yaml(cls, representer, node):
|
||||||
|
@ -519,7 +566,7 @@ class MapEntrance(Thing):
|
||||||
|
|
||||||
class PlayerCharacter(Character):
|
class PlayerCharacter(Character):
|
||||||
"""Player object. Cannot be created with yaml."""
|
"""Player object. Cannot be created with yaml."""
|
||||||
defaultGraphic = ('clear', '#0000FF', 'o')
|
defaultGraphic = ThingGraphic('clear', '#0000FF', 'o')
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
if 'name' not in kwargs:
|
if 'name' not in kwargs:
|
||||||
|
|
|
@ -46,6 +46,8 @@ def parseCoords(level, args, usePlayerCoords = True, player = None):
|
||||||
Returns (thing, x, y). "Thing" can be None."""
|
Returns (thing, x, y). "Thing" can be None."""
|
||||||
if level == None:
|
if level == None:
|
||||||
raise ValueError('Coordinates cannot be parsed because there is no map.')
|
raise ValueError('Coordinates cannot be parsed because there is no map.')
|
||||||
|
if len(args) == 0:
|
||||||
|
return None, -1, -1
|
||||||
x = -1
|
x = -1
|
||||||
y = -1
|
y = -1
|
||||||
|
|
||||||
|
|
9
shell.py
9
shell.py
|
@ -133,7 +133,10 @@ class Shell(object):
|
||||||
self.__exit = False
|
self.__exit = False
|
||||||
|
|
||||||
def man(self, args):
|
def man(self, args):
|
||||||
help(self.__commands[args[0]])
|
if (len(args)):
|
||||||
|
help(self.__commands[args[0]])
|
||||||
|
else:
|
||||||
|
print("Usage: help <commamd>\nCommands:\n\t{}".format('\n\t'.join(list(self.__commands.keys()))))
|
||||||
|
|
||||||
def registerCommand(self, commandName: str, command: _types.FunctionType):
|
def registerCommand(self, commandName: str, command: _types.FunctionType):
|
||||||
"""command must be a function that takes one argument: a list of strings,
|
"""command must be a function that takes one argument: a list of strings,
|
||||||
|
@ -288,10 +291,10 @@ $>=0 - $>=n is the nth and later arguments"""
|
||||||
else:
|
else:
|
||||||
self.handleUnknownCommand(command)
|
self.handleUnknownCommand(command)
|
||||||
|
|
||||||
def handleUnknownCommand(self, command):
|
def handleUnknownCommand(self, command: list):
|
||||||
"""Handle commands that aren't registered. Override this if you want to do
|
"""Handle commands that aren't registered. Override this if you want to do
|
||||||
something with those commands."""
|
something with those commands."""
|
||||||
print("Bad command.")
|
print(f"Unknown command: {' '.join(command)}")
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Runs at the end of each loop. Does nothing by default. Override this if
|
"""Runs at the end of each loop. Does nothing by default. Override this if
|
||||||
|
|
|
@ -51,6 +51,16 @@ loadAlways:
|
||||||
none: none # might this work to prevent this character from doing anything?
|
none: none # might this work to prevent this character from doing anything?
|
||||||
customValues:
|
customValues:
|
||||||
dialogs: testing/testDialog.yml
|
dialogs: testing/testDialog.yml
|
||||||
- !Singleton
|
loadOnce:
|
||||||
|
- !NPC
|
||||||
name: follower
|
name: follower
|
||||||
|
description: a follower
|
||||||
location: [6, 26]
|
location: [6, 26]
|
||||||
|
behaviors:
|
||||||
|
go: [-1, follow]
|
||||||
|
arrive: [-1, follow]
|
||||||
|
customValues:
|
||||||
|
follow:
|
||||||
|
distance: 2
|
||||||
|
isFollowing: True
|
||||||
|
target: You
|
||||||
|
|
|
@ -3,17 +3,17 @@
|
||||||
title: Sample Text
|
title: Sample Text
|
||||||
openingText: "{}\nsample text"
|
openingText: "{}\nsample text"
|
||||||
startLevel: testing/test1.yml
|
startLevel: testing/test1.yml
|
||||||
singletons:
|
#singletons:
|
||||||
- !NPC
|
# - !NPC
|
||||||
name: follower
|
# name: follower
|
||||||
description: a follower
|
# description: a follower
|
||||||
location: [0, 0]
|
# location: [0, 0]
|
||||||
behaviors:
|
# behaviors:
|
||||||
go: [-1, follow]
|
# go: [-1, follow]
|
||||||
arrive: [-1, follow]
|
# arrive: [-1, follow]
|
||||||
customValues:
|
# customValues:
|
||||||
follow:
|
# follow:
|
||||||
distance: 2
|
# distance: 2
|
||||||
isFollowing: True
|
# isFollowing: True
|
||||||
target: You
|
# target: You
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue