various tweaks and fixes

This commit is contained in:
Patrick Marsee 2019-06-08 13:42:00 -04:00
parent ed7fe60a6d
commit ed7d265b48
5 changed files with 524 additions and 212 deletions

View file

@ -8,13 +8,19 @@ import random as _ra
import sys as _sys
import pickle as _pi
import ruamel.yaml as _yaml
import textwrap as _tw
class GameError(RuntimeError):
pass
class _ScriptBreak(object):
def __init__(self, value):
self.value = value
class GameBase(object):
coordRegex = _re.compile(r'((-?[a-zA-Z]+) ?(-?[0-9]+))|((-?[0-9]+),? (-?[0-9]+))|(\(([0-9]+), ([0-9]+)\))')
coordRegex = _re.compile(r'(?:(-?[a-zA-Z]+) ?(-?[0-9]+))|(?:(-?[0-9]+),? (-?[0-9]+))|(?:\(([0-9]+), ([0-9]+)\))')
#coordRegex1 = _re.compile(r'(-?[a-zA-Z]+) ?(-?[0-9]+)') # "B12" or "B 12"
#coordRegex2 = _re.compile(r'\(([0-9]+), ([0-9]+)\)') # "(2, 12)"
#coordRegex3 = _re.compile(r'(-?[0-9]+),? (-?[0-9]+)') # "2 12" or "2, 12"
@ -61,12 +67,19 @@ class GameBase(object):
self.registerUseFunc('container', self.container)
self.registerUseFunc('info', self.info)
self.registerUseFunc('wear', self.wear)
self.registerBehavior('stand', self.stand)
self.registerBehavior('wander', self.wander)
self.registerScript('if', self.ifScript)
self.registerScript('printf', self.printfScript)
self.registerScript('get', self.getCustomValue)
self.registerScript('set', self.setCustomValue)
self.registerScript('del', self.delCustomValue)
self.registerScript('spawn', self.spawnThing)
self.registerScript('give', self.giveToPlayer)
self.registerScript('move', self.moveThingScript)
self.registerScript('cvget', self.cvget)
self.registerScript('cvmod', self.cvmod)
self.registerScript('cvdel', self.cvdel)
# Helper functions
@ -112,12 +125,30 @@ to get input from stdin."""
def parseCoords(self, args, usePlayerCoords = True, allowInventory = True):
"""Takes an argument list, and figures out what it's talking about.
Returns (thing, x, y). "Thing" can be None."""
coordStr = args[0]
if len(args) > 1:
coordStr += ' ' + args[1]
#print(coordStr)
x = -1
y = -1
# first, try by name for objects in the map
name = ' '.join(args)
thing = self.level.getThingByName(name)
if thing != None:
if usePlayerCoords:
return thing, thing.playerx, thing.playery
else:
return thing, thing.x, thing.y
# Second, try by name for objects in the inventory
if allowInventory:
for i in self.playerInv:
thingName = self.playerInv[i].name
if name == thingName:
return self.playerInv[i], -1, -1
# Third, try by location
coordStr = args[0]
if len(args) > 1:
coordStr += ' ' + args[1]
match = GameBase.coordRegex.match(coordStr)
if match != None: # if coordinates were given
#print(match.group())
@ -139,27 +170,7 @@ Returns (thing, x, y). "Thing" can be None."""
else:
return thing, x, y
else: # if a name was given
name = ' '.join(args)
thing = self.level.getThingByName(name)
if thing != None and usePlayerCoords:
return thing, thing.playerx, thing.playery
elif thing != None:
return thing, thing.x, thing.y
elif allowInventory:
for i in self.playerInv:
thingName = self.playerInv[i].name
if name == thingName:
thing = self.playerInv[i]
if thing != None:
return thing, -1, -1
else:
return None, -1, -1
#raise RuntimeError("'None' item named '{0}' in player inventory.".format(name))
return None, -1, -1
else: # nothing
return None, -1, -1
#raise ValueError("{0} cannot be reached.".format(name))
return None, x, y
return None, -1, -1
def justifyText(self, text, width = 80):
ret = []
@ -181,10 +192,12 @@ Returns (thing, x, y). "Thing" can be None."""
return '\n'.join(ret)
def getValueFromString(self, arg: str):
def getValueFromString(self, arg: str, env = None):
if env == None:
env = self.customValues
val = None
validIdent = _re.match(r'[_A-Za-z][_0-9A-Za-z]*', arg)
if arg[0] in '"\'': # assume it's a string
if arg[0] in '"\'' and arg[-1] == arg[0]: # assume it's a string
val = arg[1:-1]
elif _re.match(r'[+-]?(?:[0-9]*[.])?[0-9]+', arg) != None:
if '.' in arg:
@ -204,51 +217,71 @@ Returns (thing, x, y). "Thing" can be None."""
elif arg.casefold() == 'playerinv':
val = self.playerInv
elif validIdent != None:
if 'scriptLocal' in self.customValues and validIdent.group() in self.customValues['scriptLocal']:
val = self.customValues['scriptLocal'][arg]
elif validIdent.group() in self.customValues:
val = self.customValues[arg]
group = validIdent.group()
if 'scriptLocal' in self.customValues and group in self.customValues['scriptLocal']:
val = self.customValues['scriptLocal'][group]
elif group in env:
val = env[group]
else:
return False # for if statements; if a variable doesn't exist, should evaluate to False
# evaluate all values of all indecies
if validIdent.end() < len(arg):
if arg[validIdent.end()] == '[':
openBracket = validIdent.end()
openBracket = validIdent.end()
if openBracket < len(arg):
if arg[openBracket] == '[':
ptr = openBracket
depth = 0
while ptr < len(arg):
if ptr == '[':
depth += 1
elif ptr == ']':
depth -= 1
if depth == 0:
var = var[self.getValueFromString(arg[openBracket+1:ptr])]
openBracket = ptr + 1
ptr += 1
if depth == 0 and arg[ptr] != '[':
raise GameError('Invalid value syntax: {}'.format(arg))
if arg[ptr] == '[':
depth += 1
elif arg[ptr] == ']':
depth -= 1
if depth == 0:
index = self.getValueFromString(arg[openBracket+1:ptr])
if index in val:
val = val[index]
else:
return False
openBracket = ptr + 1
ptr += 1
else:
raise GameError('Invalid value syntax: {}'.format(arg))
else:
raise GameError('Invalid argument to getValueFromString: {}'.format(arg))
return val
def compareValues(self, operator: str, left: str, right: str):
def compareValues(self, args: list):
"""Generalized comparisons, may eventually be extended to other operators"""
lval = self.getValueFromString(left)
rval = self.getValueFromString(right)
if operator == '==':
return lval == rval
elif operator == '!=':
return lval != rval
elif operator == '<=':
return lval <= rval
elif operator == '>=':
return lval >= rval
elif operator == '<':
return lval < rval
elif operator == '>':
return lval > rval
if len(args) == 1:
return bool(self.getValueFromString(args[0]))
elif len(args) == 3 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
lval = self.getValueFromString(args[0])
operator = args[1]
rval = self.getValueFromString(args[2])
if operator == '==':
return lval == rval
elif operator == '!=':
return lval != rval
elif operator == '<=':
return lval <= rval
elif operator == '>=':
return lval >= rval
elif operator == '<':
return lval < rval
elif operator == '>':
return lval > rval
elif operator == 'in':
if args[2].casefold() == 'playerinv':
for i in rval:
if rval[i].name == lval:
return True
return False
else:
return lval in rval
else:
raise GameError("Condition cannot be evaluated: {}".format(' '.join(args)))
# commands
@ -278,6 +311,7 @@ The letter is not case-sensitive."""
# Now we have a heading! Let's see if we can get there...
if (x, y) == (self.playerx, self.playery):
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0)))
self.setEvent(0.0, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0))
return
dist, path = self.level.path(x, y, self.playerx, self.playery)
if dist == -1:
@ -343,7 +377,7 @@ Character can be the name of the character, or their coordinates."""
args.pop(0)
if args[0] == 'the':
args.pop(0)
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
thing, x, y = self.parseCoords(args, usePlayerCoords = False, allowInventory = False)
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
print("{} cannot talk to {}.".format(self.playerName, thing.name), file = self.outstream)
elif thing == None:
@ -423,15 +457,15 @@ the name of an item in the player's inventory."""
def useOn(self, args, speed):
"""Called by use when there is an 'on' clause"""
onIndex = args.index('on')
item, x, y = self.parseCoords(args[:onIndex])
item, x, y = self.parseCoords(args[:onIndex], usePlayerCoords = False, allowInventory = True)
if args[onIndex+1] == 'the':
onIndex += 1
thing, x, y = self.parseCoords(args[onIndex+1:])
thing, x, y = self.parseCoords(args[onIndex+1:], usePlayerCoords = True, allowInventory = True)
useArgs = []
if 'with' in args:
useArgs = args[args.index('with')+1:]
if item == None or item.thingID not in self.playerInv:
print("There is no {0} in the inventory.".format(item.name), file = self.outstream)
print("There is no such item in the inventory.", file = self.outstream)
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return
if thing == None and x < 0 and y < 0:
@ -481,7 +515,7 @@ Object can be the name of the object, or its coordinates."""
args.pop(0)
if args[0] == 'the':
args.pop(0)
thing, x, y = self.parseCoords(args)
thing, x, y = self.parseCoords(args, allowInventory = False)
if thing == None:
print("There is nothing to take.", file = self.outstream)
return
@ -522,7 +556,7 @@ Object can be the name of the object, or its coordinates."""
for i in self.playerInv:
thingName = self.playerInv[i].name
if ' '.join(args) == thingName:
self.setEvent(0.0, _ge.DropEvent(self.playerInv[args[0]]))
self.setEvent(0.0, _ge.DropEvent(self.playerInv[i]))
return
print('{0} does not have a {1}.'.format(self.playerName, args[0]), file = self.outstream)
@ -547,11 +581,14 @@ Object can be the name of the object, or its coordinates."""
else:
self.level, self.nextThing = _gm.GameMap.read(args[0], None, preLoaded, self.nextThing)
if self.level == None:
raise GameError("Map could not be loaded.")
# get persistent things from it
if args[0] in self.persist:
persistedThings = tuple(self.persist[args[0]].keys())
for i in persistedThings:
self.level.addThing(self.persist[args[0]][i], True)
self.nextThing = self.level.addThing(self.persist[args[0]][i], self.nextThing, True) #nextThing shouldn't change
del self.persist[args[0]][i] # delete them from the persist dict to prevent item duplication
print(self.level.openingText, file = self.outstream)
@ -579,10 +616,10 @@ Object can be the name of the object, or its coordinates."""
if self.level.name not in self.persist:
self.persist[self.level.name] = {}
for i in self.level.persistent:
self.persist[self.level.name][i] = self.level.getThingByName(i)
self.persist[self.level.name][i] = self.level.getThingByID(i)
# build data object to be saved
data = (self.playerName, self.playerx, self.playery, self.playerInv, self.level.name, self.persist, self.eventQueue, self.gameTime, self.nextThing)
data = (self.playerName, self.playerx, self.playery, self.playerInv, self.level.name, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing)
# save it!
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
@ -613,7 +650,7 @@ Object can be the name of the object, or its coordinates."""
fileName = args[0]
x, y, levelname = 1, 1, 'testing/test1.txt'
with open(fileName, 'rb') as f:
self.playerName, x, y, self.playerInv, levelname, self.persist, self.eventQueue, self.gameTime, self.nextThing = _pi.load(f)
self.playerName, x, y, self.playerInv, levelname, self.persist, self.eventQueue, self.customValues, self.gameTime, self.nextThing = _pi.load(f)
#print(levelname, x, y, file = self.outstream)
self.loadMap((levelname, x, y))
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
@ -659,45 +696,70 @@ Object can be the name of the object, or its coordinates."""
del literalStr[l]
l -= 1
elif instr[c] in ' \t\n;}{':
if argStart != l:
ret.append(''.join(literalStr[argStart:l]))
if instr[c] == ';':
script.append(ret)
ret = []
elif instr[c] == '{': # block
stack.append(script)
script.append(ret)
script = []
ret.append(script)
ret = []
del literalStr[l]
if l > 0 and instr[c-1] == '\\':
del literalStr[l-1]
l -= 1
elif instr[c] == '}': # close block
if len(ret) > 0:
else:
if argStart != l:
ret.append(''.join(literalStr[argStart:l]))
if instr[c] == ';':
if len(ret) > 0:
script.append(ret)
ret = []
elif instr[c] == '{': # block
stack.append(script)
script.append(ret)
script = stack.pop()
ret = []
del literalStr[l]
l -= 1
argStart = l + 1
script = []
ret.append(script)
ret = []
del literalStr[l]
l -= 1
elif instr[c] == '}': # close block
if len(ret) > 0:
script.append(ret)
ret = []
script = stack.pop()
del literalStr[l]
l -= 1
argStart = l + 1
c += 1
l += 1
if argStart != l:
ret.append(''.join(literalStr[argStart:l]))
script.append(ret)
if len(ret) > 0:
script.append(ret)
#print('After parsing: {}'.format(script))
self.customValues['scriptLocal'] = {}
self.runScript(script)
del self.customValues['scriptLocal']
ret = self.runScript(script)
if 'scriptLocal' in self.customValues:
del self.customValues['scriptLocal']
if isinstance(ret, _ScriptBreak):
ret = ret.value
return ret
def runScript(self, script: list):
"""run a script"""
ret = False
for line in script:
if line[0].casefold() == 'playercmd':
if len(line) == 0:
# empty line
continue
elif line[0].casefold() == 'playercmd':
# run a player command
self.getIO('playercmd')(line[1:])
elif line[0].casefold() == 'break':
# exit early
return _ScriptBreak(ret)
elif line[0] in self.__scripts:
# run a script
ret = self.__scripts[line[0]](line[1:])
if isinstance(ret, _ScriptBreak):
return ret
else:
self.__scripts[line[0]](line[1:])
# conditional evaluation
ret = self.compareValues(line)
# We specifically want the return value of the last line executed.
return ret
def gameEventLoop(self):
#print(self.skipLoop)
@ -730,7 +792,8 @@ Object can be the name of the object, or its coordinates."""
self.prevx, self.prevy = self.playerx, self.playery
self.playerx, self.playery = e.x, e.y
else:
self.level.moveThing(e.actor, e.x, e.y)
actor = self.level.getThingByName(e.actor)
self.level.moveThing(actor, e.x, e.y)
return False
def handleArrive(self, e):
@ -741,9 +804,11 @@ Object can be the name of the object, or its coordinates."""
thing = self.level.getThingAtCoords(self.playerx, self.playery)
if thing:
if thing.thingType == 'x':
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))
self.parseScript(thing.onUse)
if (isinstance(thing.key, bool) and thing.key == True) or (isinstance(thing.key, str) and self.parseScript(thing.key)):
a = self.requestInput('Do you want to go {0}? (Y/n)'.format(str(thing)))
if a != 'n' and a != 'N':
self.loadMap((thing.destination, thing.exitid))
return True
else:
actor = self.level.getThingByName(e.actor)
@ -781,7 +846,7 @@ Object can be the name of the object, or its coordinates."""
def handleDrop(self, e):
e.item.x = self.playerx
e.item.y = self.playery
self.level.addThing(e.item, True)
self.nextThing = self.level.addThing(e.item, self.nextThing, True) # nextThing shouldn't change
del self.playerInv[e.item.thingID]
return True
@ -796,30 +861,30 @@ Object can be the name of the object, or its coordinates."""
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
if thing.customValues['pattern'] == 'single':
print(self.justifyText(thing.customValues['text']), file = self.outstream)
print(self.justifyText(str(thing.customValues['text'])), file = self.outstream)
elif thing.customValues['pattern'] == 'loop':
if not 'cursor' in thing.customValues:
thing.customValues['cursor'] = 0
cursor = thing.customValues['cursor']
print(self.justifyText(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'])
elif thing.customValues['pattern'] == 'once':
if not 'cursor' in thing.customValues:
thing.customValues['cursor'] = 0
cursor = thing.customValues['cursor']
print(self.justifyText(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:
thing.customValues['cursor'] += 1
elif thing.customValues['pattern'] == 'random':
cursor = _ra.randrange(len(thing.customValues['text']))
print(self.justifyText(thing.customValues['text'][cursor]), file = self.outstream)
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
thing.customValues['cursor'] = cursor
if 'set' in thing.customValues:
if 'cursor' in thing.customValues:
self.customVals[thing.customValues['set']] = thing.customValues[cursor]
self.customValues[thing.customValues['set']] = thing.customValues[cursor]
else:
self.customVals[thing.customValues['set']] = 0
self.customValues[thing.customValues['set']] = True
return 0.0
def container(self, thing, args):
@ -893,12 +958,13 @@ Object can be the name of the object, or its coordinates."""
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
# evaluate condition
if args[1] in ('==', '!=', '<=', '>=', '<', '>'):
if len(args) > 1 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
if len(args) < 4:
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
ret = self.compareValues(args[1], args[0], args[2])
ret = self.compareValues(args[:3])
args = args[3:]
else:
ret = bool(self.parseValue(args[0]))
ret = bool(self.getValueFromString(args[0]))
args = args[1:]
if inverse:
ret = not ret
@ -906,17 +972,34 @@ Object can be the name of the object, or its coordinates."""
# if condition is true, evaluate further
if ret:
if isinstance(args[-1], list):
self.runScript(args[-1])
return self.runScript(args[-1])
else:
self.runScript([args])
return self.runScript([args])
def printfScript(self, args):
"""Assume the first argument is a stringable object, and format all others."""
print(args[0].format(*[self.getValueFromString(i) for i in args[1:]]), file = self.outstream)
ret = args[0].format(*[self.getValueFromString(i) for i in args[1:]])
print(ret, file = self.outstream)
return ret
def setCustomValue(self, args):
def getCustomValue(self, args, env = None):
norm = True
if env == None:
env = self.customValues
else:
norm = False
if len(args) > 0 and args[0] == 'local':
if 'scriptLocal' in self.customValues:
env = self.customValues['scriptLocal']
else:
raise GameError('Attempted to set a local variable without local scope.')
args.pop(0)
return self.getValueFromString(args[0], env)
def setCustomValue(self, args, env = None):
"""takes [customValue, op, value]"""
env = self.customValues
if env == None:
env = self.customValues
if len(args) > 0 and args[0] == 'local':
if 'scriptLocal' in self.customValues:
env = self.customValues['scriptLocal']
@ -925,7 +1008,9 @@ Object can be the name of the object, or its coordinates."""
args.pop(0)
if len(args) < 3 or args[1] not in ('=', '+=', '-=', '*=', '/=', '%=', '//=', '**=', 'b=', '!=', '|=', '&=', '^='):
raise GameError('Arguments are not fit for the setCustomValue script.')
val = self.getValueFromString(args[2])
# This next line allows cvmod to use values in the thing's customValues dict,
# but doing this means that accessing a game CV requires setting it to a local var.
val = self.getValueFromString(args[2], env)
# set the value to a default value (0, 0.0, '', etc.) if not yet assigned
if args[0] not in env and args[1] != '=':
@ -975,6 +1060,15 @@ Object can be the name of the object, or its coordinates."""
env[args[0]] &= val
elif args[1] == '^=':
env[args[0]] ^= val
return env[args[0]]
def delCustomValue(self, args, env = None):
"""To clean up after a map."""
if env == None:
env = self.customValues
for i in args:
if i in env:
del(env[i])
def spawnThing(self, args):
"""Spawns a thing. [type name=... location=... etc]"""
@ -989,35 +1083,33 @@ Object can be the name of the object, or its coordinates."""
for i in args[1:]:
if i[0:5].casefold() == 'name=':
name = i[5:]
elif i[0:10].casefold() == 'location=':
_, x, y = self.parseCoords(i[10:].split())
elif i[0:9].casefold() == 'location=':
_, x, y = self.parseCoords(i[9:].split(), usePlayerCoords = False, allowInventory = False)
elif i[0:4].casefold() == 'fgc=':
if len(i[4:]) != 7 or i[0] != '#':
if len(i[4:]) != 7 or i[4] != '#':
raise GameError("Invalid color: {}".format(i[4:]))
for j in i[4:]:
for j in i[5:]:
if j not in '0123456789abcdefABCDEF':
raise GameError("Invalid color: {}".format(i[4:]))
fgc = i[4:]
elif i[0:4].casefold() == 'bgc=':
if len(i[4:]) != 7 or i[0] != '#':
if len(i[4:]) != 7 or i[4] != '#':
raise GameError("Invalid color: {}".format(i[4:]))
for j in i[4:]:
for j in i[5:]:
if j not in '0123456789abcdefABCDEF':
raise GameError("Invalid color: {}".format(i[4:]))
bgc = i[4:]
elif i[0:6].casefold() == 'shape=':
if len(i[6:]) != 7 or i[0] != '#':
raise GameError("Invalid color: {}".format(i[6:]))
for j in i[6:]:
if j not in '0123456789abcdefABCDEF':
raise GameError("Invalid color: {}".format(i[6:]))
shape = i[6:]
if i[6] not in 'ox^ #|-':
raise GameError("Invalid shape: {}".format(i[6]))
shape = i[6]
elif i == 'persist':
persist = True
if x < 0 or y < 0:
raise GameError("Cannot spawn thing: bad location")
# Unfortunately, there needs to be a case for each type.
thing = None
if args[0].casefold() == 'item':
# spawn an item
description = 'A nondescript item.'
@ -1027,12 +1119,12 @@ Object can be the name of the object, or its coordinates."""
ranged = False
if name == None:
name = 'item {}'.format(self.nextThing)
if fgc == None:
fgc = _gm.Item.defaultGraphic['fgc']
if bgc == None:
bgc = _gm.Item.defaultGraphic['bgc']
bgc = _gm.Item.defaultGraphic[0]
if fgc == None:
fgc = _gm.Item.defaultGraphic[1]
if shape == None:
shape = _gm.Item.defaultGraphic['shape']
shape = _gm.Item.defaultGraphic[2]
for i in args[1:]:
if i[0:12].casefold() == 'description=':
description = i[12:]
@ -1045,9 +1137,8 @@ Object can be the name of the object, or its coordinates."""
elif i[0:3].casefold() == 'cv:':
equalsign = i.index('=')
cv = i[3:equalsign]
customValues[cv] = getValueFromString(i[equalsign+1:])
customValues[cv] = self.getValueFromString(i[equalsign+1:])
thing = _gm.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
elif args[0].casefold() == 'useable':
# spawn a useable thing
description = 'A nondescript useable thing.'
@ -1056,12 +1147,12 @@ Object can be the name of the object, or its coordinates."""
playerx, playery = x, y
if name == None:
name = 'useable {}'.format(self.nextThing)
if fgc == None:
fgc = _gm.Item.defaultGraphic['fgc']
if bgc == None:
bgc = _gm.Item.defaultGraphic['bgc']
bgc = _gm.Useable.defaultGraphic[0]
if fgc == None:
fgc = _gm.Useable.defaultGraphic[1]
if shape == None:
shape = _gm.Item.defaultGraphic['shape']
shape = _gm.Useable.defaultGraphic[2]
for i in args[1:]:
if i[0:12].casefold() == 'description=':
description = i[12:]
@ -1072,24 +1163,23 @@ Object can be the name of the object, or its coordinates."""
elif i[0:3].casefold() == 'cv:':
equalsign = i.index('=')
cv = i[3:equalsign]
customValues[cv] = getValueFromString(i[equalsign+1:])
customValues[cv] = self.getValueFromString(i[equalsign+1:])
thing = _gm.Useable(name, x, y, description, useFunc, customValues, playerx, playery, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
elif args[0].casefold() == 'npc':
# spawn an NPC
description = 'A nondescript character.'
behavior = ''
behavior = 'stand'
inv = []
customValues = {}
playerx, playery = x, y
if name == None:
name = 'character {}'.format(self.nextThing)
if fgc == None:
fgc = _gm.Item.defaultGraphic['fgc']
if bgc == None:
bgc = _gm.Item.defaultGraphic['bgc']
bgc = _gm.NPC.defaultGraphic[0]
if fgc == None:
fgc = _gm.NPC.defaultGraphic[1]
if shape == None:
shape = _gm.Item.defaultGraphic['shape']
shape = _gm.NPC.defaultGraphic[2]
for i in args[1:]:
if i[0:12].casefold() == 'description=':
description = i[12:]
@ -1100,9 +1190,8 @@ Object can be the name of the object, or its coordinates."""
elif i[0:3].casefold() == 'cv:':
equalsign = i.index('=')
cv = i[3:equalsign]
customValues[cv] = getValueFromString(i[equalsign+1:])
customValues[cv] = self.getValueFromString(i[equalsign+1:])
thing = _gm.NPC(name, x, y, description, behavior, inv, customValues, playerx, playery, False, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
elif args[0].casefold() == 'door':
# spawn a door
description = 'a nondescript door.'
@ -1110,32 +1199,33 @@ Object can be the name of the object, or its coordinates."""
key = None
if name == None:
name = 'door {}'.format(self.nextThing)
if fgc == None:
fgc = _gm.Item.defaultGraphic['fgc']
if bgc == None:
bgc = _gm.Item.defaultGraphic['bgc']
bgc = _gm.Door.defaultGraphic[0]
if fgc == None:
fgc = _gm.Door.defaultGraphic[1]
if shape == None:
shape = _gm.Item.defaultGraphic['shape']
shape = _gm.Door.defaultGraphic[2]
for i in args[1:]:
if i[0:12].casefold() == 'description=':
description = i[12:]
elif i.casefold() == 'locked':
locked = True
thing = _gm.Door(name, x, y, locked, description, key, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
elif args[0].casefold() == 'mapexit':
# spawn an exit to another map (use with EXTREME caution!)
destination = ''
prefix = None
exitid = 0
onUse = ''
key = True
if name == None:
name = 'somewhere'
if fgc == None:
fgc = _gm.Item.defaultGraphic['fgc']
if bgc == None:
bgc = _gm.Item.defaultGraphic['bgc']
bgc = _gm.MapExit.defaultGraphic[0]
if fgc == None:
fgc = _gm.MapExit.defaultGraphic[1]
if shape == None:
shape = _gm.Item.defaultGraphic['shape']
shape = _gm.MapExit.defaultGraphic[2]
for i in args[1:]:
if i[0:12].casefold() == 'destination=':
destination = i[12:]
@ -1143,8 +1233,11 @@ Object can be the name of the object, or its coordinates."""
prefix = i[7:]
elif i[0:7].casefold() == 'exitid=':
exitid = int(i[7:])
thing = _gm.MapExit(name, x, y, exitid, destination, prefix, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
elif i[0:6].casefold() == 'onuse=':
onUse = i[6:]
elif i[0:4].casefold() == 'key=':
key = i[4:]
thing = _gm.MapExit(name, x, y, exitid, destination, prefix, onUse, key, (bgc, fgc, shape))
elif args[0].casefold() == 'mapentrance':
# spawn a map entrance
exitid = 0
@ -1152,7 +1245,10 @@ Object can be the name of the object, or its coordinates."""
if i[0:7].casefold() == 'exitid=':
exitid = int(i[7:])
thing = _gm.MapEntrance(x, y, exitid, name)
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
else:
raise GameError("{} not a valid thing type.".format(args[0]))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
return thing
def giveToPlayer(self, args):
"""We get to assume it's an item."""
@ -1169,7 +1265,7 @@ Object can be the name of the object, or its coordinates."""
if i[0:5].casefold() == 'name=':
name = i[5:]
elif i[0:10].casefold() == 'location=':
_, x, y = self.parseCoords(i[10:].split())
_, x, y = self.parseCoords(i[10:].split(), usePlayerCoords = False, allowInventory = False)
elif i[0:4].casefold() == 'fgc=':
if len(i[4:]) != 7 or i[0] != '#':
raise GameError("Invalid color: {}".format(i[4:]))
@ -1206,10 +1302,49 @@ Object can be the name of the object, or its coordinates."""
cv = i[3:equalsign]
customValues[cv] = getValueFromString(i[equalsign+1:])
thing = _gm.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
thing.thingID = self.nextThing
self.playerInv[thing.thingID] = thing
self.nextThing += 1
return thing
def moveThingScript(self, args):
colon = args.index(':')
thing, x, y = self.parseCoords(args[0:colon], usePlayerCoords = False, allowInventory = False)
if thing == None:
return False
_, newX, newY = self.parseCoords(args[colon+1:], usePlayerCoords = True, allowInventory = False)
self.level.moveThing(thing, newX, newY)
def cvget(self, args):
thing, x, y = self.parseCoords(args[0:-1], usePlayerCoords = False, allowInventory = True)
if thing != None and thing.thingType in 'ciu':
return self.getCustomValue(args[-1:], env = thing.customValues)
else:
raise GameError("Thing described in cvmod doesn't exist or isn't a character, item, or useable.")
def cvmod(self, args):
"""Modify a custom value in a thing.
args = thing identifier operator value"""
thing, x, y = self.parseCoords(args[0:-3], usePlayerCoords = False, allowInventory = True)
if thing != None and thing.thingType in 'ciu':
return self.setCustomValue(args[-3:], env = thing.customValues)
else:
raise GameError("Thing described in cvmod doesn't exist or isn't a character, item, or useable.")
def cvdel(self, args):
"""Delete custom values."""
colon = args.index(':')
thing, x, y = self.parseCoords(args[0:colon], usePlayerCoords = False, allowInventory = True)
if thing != None and thing.thingType in 'ciu':
return self.setCustomValue(args[colon+1:], env = thing.customValues)
else:
raise GameError("Thing described in cvmod doesn't exist or isn't a character, item, or useable.")
# behaviors
def stand(self, actor):
pass
def wander(self, actor):
pass