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:
Patrick Marsee 2020-05-10 23:26:21 -04:00
parent ee5c4da549
commit 4a398cc2a3
11 changed files with 546 additions and 253 deletions

View file

@ -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/