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
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.customValues = {} # for setting flags and such
|
||||
self.level = None
|
||||
self.persist = {} # {level : {thingName : thing}}
|
||||
self.singletons = {} # {thingID : thing}
|
||||
self.persist = {} # {level : {thingID : thing}}
|
||||
self.singletons = {} # {thingName : thing}
|
||||
self.ps2 = '? '
|
||||
self.eventQueue = []
|
||||
self.gameTime = 0.0
|
||||
|
@ -112,12 +112,23 @@ to get input from stdin."""
|
|||
|
||||
return '\n'.join(ret)
|
||||
|
||||
def smoothMove(self, actor, loc, speed, action = None, immediate = False):
|
||||
"""Move a thing smoothly at a constant speed."""
|
||||
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.
|
||||
|
||||
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:
|
||||
self.setEvent(0.0, _ge.ArriveEvent(actor, self.level.coordsToInt(actor.x, actor.y), speed, action, loc, timeTaken = speed))
|
||||
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)
|
||||
if dist == -1:
|
||||
print('{0} cannot reach there.'.format(actor.name), file = self.outstream)
|
||||
|
@ -125,11 +136,15 @@ to get input from stdin."""
|
|||
elif dist == 1:
|
||||
self.setEvent(speed, _ge.ArriveEvent(actor, path[0], speed, action, loc, timeTaken = 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:
|
||||
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)
|
||||
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
|
||||
|
||||
# commands
|
||||
|
@ -155,6 +170,9 @@ The letter is not case-sensitive."""
|
|||
raise GameError("Cannot move: No level has been loaded.")
|
||||
if self.player == None:
|
||||
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
|
||||
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
|
||||
speed = 0.3333333
|
||||
|
@ -181,6 +199,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
if self.player == None:
|
||||
raise GameError("Cannot look: Player character doesn't exist.")
|
||||
if len(args) == 0:
|
||||
# no arguments: print the level description
|
||||
print(self.justifyText(self.level.description), file = self.outstream)
|
||||
else:
|
||||
if args[0] == 'at':
|
||||
|
@ -188,15 +207,18 @@ Object can be the name of the object, or its coordinates."""
|
|||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
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:
|
||||
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:
|
||||
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
||||
return
|
||||
elif thing == None:
|
||||
print("There is nothing to see here.\n", file = self.outstream)
|
||||
return
|
||||
else:
|
||||
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:
|
||||
raise GameError("Cannot talk: Player character doesn't exist.")
|
||||
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:
|
||||
if args[0] == 'to':
|
||||
args.pop(0)
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
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:
|
||||
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:
|
||||
print("{} cannot talk to {}.".format(self.player.name, thing.name), file = self.outstream)
|
||||
return
|
||||
elif thing == None:
|
||||
print("There is nobody here.\n", file = self.outstream)
|
||||
return
|
||||
else:
|
||||
_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.")
|
||||
if self.player == None:
|
||||
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
|
||||
useArgs = []
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
def useOn(self, args, speed):
|
||||
"""Called by use when there is an 'on' clause"""
|
||||
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)
|
||||
if args[onIndex+1] == 'the':
|
||||
onIndex += 1
|
||||
|
@ -293,11 +333,11 @@ the name of an item in the player's inventory."""
|
|||
|
||||
if not item.ranged:
|
||||
# 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:
|
||||
if not self.level.lineOfSight(self.player.x, self.player.y, x, y):
|
||||
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:
|
||||
print("{} cannot see that.".format(self.player.name), file = self.outstream)
|
||||
return
|
||||
|
@ -315,6 +355,9 @@ Object can be the name of the object, or its coordinates."""
|
|||
if self.player == None:
|
||||
raise GameError("Cannot take: Player character doesn't exist.")
|
||||
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':
|
||||
speed = 0.3333333
|
||||
args.pop(0)
|
||||
|
@ -330,7 +373,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
return
|
||||
|
||||
# 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):
|
||||
"""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.")
|
||||
if self.player == None:
|
||||
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':
|
||||
args.pop(0)
|
||||
thingName = ' '.join(args)
|
||||
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:
|
||||
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)
|
||||
return
|
||||
|
||||
# choose pickle protocol depending on python version:
|
||||
# 3 for Python 3.0.0 to 3.3.x, 4 for Python 3.4.0 to 3.7.x
|
||||
prot = _pi.HIGHEST_PROTOCOL
|
||||
# pickle protocol 4 for Python 3.4.0 to 3.7.x
|
||||
prot = 4
|
||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||
if args[0].endswith('.dat'):
|
||||
fileName = args[0]
|
||||
|
@ -475,10 +520,10 @@ Object can be the name of the object, or its coordinates."""
|
|||
ret = 0
|
||||
for case in cases:
|
||||
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)
|
||||
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)
|
||||
break
|
||||
else:
|
||||
|
@ -504,19 +549,15 @@ Object can be the name of the object, or its coordinates."""
|
|||
if ans[0] == '?':
|
||||
condEnd = ans.index(':')
|
||||
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())
|
||||
#print(_tw.fill('{}: {}'.format(j+1, ans[condEnd+1:].strip()), width = TERM_SIZE))
|
||||
j += 1
|
||||
else:
|
||||
skips.append(i)
|
||||
else:
|
||||
options.append(ans)
|
||||
#print(_tw.fill('{}: {}'.format(j+1, ans), width = TERM_SIZE))
|
||||
j += 1
|
||||
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
|
||||
for i in skips:
|
||||
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':
|
||||
if len(action) == 4:
|
||||
return 1
|
||||
return int(action[4:])
|
||||
return int(action[5:])
|
||||
elif action == 'exit':
|
||||
return 0
|
||||
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
|
||||
# show up in many scenes, and their inventory must stay the same, for
|
||||
# example.
|
||||
#print(data)
|
||||
self.singletons = {}
|
||||
if 'singletons' in data:
|
||||
for thing in data['singletons']:
|
||||
|
@ -580,6 +622,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
thing.addThing(i)
|
||||
del thing.tempInventory
|
||||
self.singletons[thing.name] = thing
|
||||
#print(self.singletons)
|
||||
return data
|
||||
|
||||
def gameEventLoop(self):
|
||||
|
@ -591,12 +634,11 @@ Object can be the name of the object, or its coordinates."""
|
|||
ev = _hq.heappop(self.eventQueue)
|
||||
self.gameTime = ev[0]
|
||||
e = ev[1]
|
||||
if e.eventType not in self.__gameEvents:
|
||||
raise GameError("Unhandled event.")
|
||||
ret = False
|
||||
for i in self.__gameEvents[e.eventType]:
|
||||
ret = ret or i(e)
|
||||
self.observe(e)
|
||||
if e.eventType in self.__gameEvents:
|
||||
for i in self.__gameEvents[e.eventType]:
|
||||
ret = ret or i(e)
|
||||
self.observe(e) # An event can still be observed even if it has no callback.
|
||||
if ret:
|
||||
break
|
||||
if len(self.eventQueue) == 0:
|
||||
|
@ -604,8 +646,8 @@ Object can be the name of the object, or its coordinates."""
|
|||
_ge.resetEventNum()
|
||||
self.skipLoop = True
|
||||
|
||||
def setEvent(self, t, e, skip = False):
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t, e))
|
||||
def setEvent(self, timeFromNow: float, event: _ge.GameEvent, skip = False):
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + timeFromNow, event))
|
||||
self.skipLoop = skip
|
||||
|
||||
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.action)
|
||||
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:
|
||||
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):
|
||||
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:
|
||||
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
|
||||
|
||||
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)
|
||||
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 ("action = None")
|
||||
if thing:
|
||||
#print("thing != None")
|
||||
if thing.thingType == 'x':
|
||||
#print("thing is exit")
|
||||
if e.actor == self.player:
|
||||
#print("actor = player")
|
||||
self.parseScript(thing.onUse)
|
||||
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)))
|
||||
if a != 'n' and a != 'N':
|
||||
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)
|
||||
else:
|
||||
e.action()
|
||||
return False # so that use works.
|
||||
#elif isinstance(e.locus, _gl.Locus):
|
||||
# self.smoothMove(e.actor, e.locus, e.speed, e.action, immediate = True)
|
||||
else:
|
||||
# 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
|
||||
|
||||
def handleUse(self, e):
|
||||
def handleUse(self, e: _ge.UseEvent):
|
||||
"""Called when a UseEvent needs processed."""
|
||||
if e.thing.useFunc == '':
|
||||
print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream)
|
||||
return True
|
||||
self.setEvent(self.__useFuncs[e.thing.useFunc](e.thing, e.args), _ge.NoOpEvent())
|
||||
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 == '':
|
||||
print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream)
|
||||
return True
|
||||
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.thing, e.args), _ge.NoOpEvent())
|
||||
self.setEvent(self.__useFuncs[e.item.useOnFunc](e.item, e.target, e.args), _ge.NoOpEvent())
|
||||
return False
|
||||
|
||||
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.y = e.actor.y
|
||||
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
|
||||
|
||||
def handleBehave(self, e):
|
||||
|
@ -720,15 +771,24 @@ Object can be the name of the object, or its coordinates."""
|
|||
"""Just prints the given text."""
|
||||
if 'pattern' not in thing.customValues or 'text' not in thing.customValues:
|
||||
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
||||
# 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':
|
||||
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':
|
||||
if not 'cursor' in thing.customValues:
|
||||
thing.customValues['cursor'] = 0
|
||||
cursor = thing.customValues['cursor']
|
||||
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||
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':
|
||||
if not 'cursor' in thing.customValues:
|
||||
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)
|
||||
if cursor < len(thing.customValues['text']) - 1:
|
||||
thing.customValues['cursor'] += 1
|
||||
|
||||
|
||||
elif thing.customValues['pattern'] == 'random':
|
||||
cursor = _ra.randrange(len(thing.customValues['text']))
|
||||
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
|
||||
|
||||
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."""
|
||||
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)
|
||||
return 0.0
|
||||
if thing.lock(item.name):
|
||||
if door.lock(key.name):
|
||||
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
|
||||
else:
|
||||
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':
|
||||
ranged = True
|
||||
elif i[0:3].casefold() == 'cv:':
|
||||
cvEnv = customValues
|
||||
equalsign = i.index('=')
|
||||
cv = i[3:equalsign]
|
||||
customValues[cv] = self.getValueFromString(i[equalsign+1:])
|
||||
thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
|
||||
ns = cv.find(':')
|
||||
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':
|
||||
# spawn a 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('=')
|
||||
cv = i[3:equalsign]
|
||||
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':
|
||||
# spawn an NPC
|
||||
description = 'A nondescript character.'
|
||||
|
@ -926,7 +993,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
equalsign = i.index('=')
|
||||
cv = i[3:equalsign]
|
||||
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':
|
||||
# spawn a door
|
||||
description = 'a nondescript door.'
|
||||
|
@ -945,7 +1012,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
description = i[12:]
|
||||
elif i.casefold() == 'locked':
|
||||
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':
|
||||
# spawn an exit to another map (use with EXTREME caution!)
|
||||
destination = ''
|
||||
|
@ -972,7 +1039,7 @@ Object can be the name of the object, or its coordinates."""
|
|||
onUse = i[6:]
|
||||
elif i[0:4].casefold() == 'key=':
|
||||
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':
|
||||
# spawn a map entrance
|
||||
exitid = 0
|
||||
|
@ -980,6 +1047,11 @@ Object can be the name of the object, or its coordinates."""
|
|||
if i[0:7].casefold() == 'exitid=':
|
||||
exitid = int(i[7:])
|
||||
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:
|
||||
raise GameError("{} not a valid thing type.".format(args[0]))
|
||||
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('=')
|
||||
cv = i[3:equalsign]
|
||||
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
|
||||
self.player.addThing(thing)
|
||||
self.nextThing += 1
|
||||
|
@ -1106,25 +1178,35 @@ Object can be the name of the object, or its coordinates."""
|
|||
# behaviors
|
||||
|
||||
def stand(self, actor):
|
||||
pass
|
||||
return 0
|
||||
|
||||
def wander(self, actor):
|
||||
pass
|
||||
return 0
|
||||
|
||||
def follow(self, actor, event):
|
||||
# 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
|
||||
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:
|
||||
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)))
|
||||
return -1
|
||||
return 0
|
||||
|
||||
# stuff for extended classes to use
|
||||
def registerUseFunc(self, name, func):
|
||||
|
@ -1159,3 +1241,7 @@ always give the player a turn, False otherwise."""
|
|||
if name not in self.__IOCalls:
|
||||
raise GameError("No IO call for {}.".format(name))
|
||||
return self.__IOCalls[name]
|
||||
|
||||
# |\_/|
|
||||
# /0 0\
|
||||
# \o/
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue