various tweaks and fixes
This commit is contained in:
parent
ed7fe60a6d
commit
ed7d265b48
5 changed files with 524 additions and 212 deletions
447
gamebase.py
447
gamebase.py
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue