various tweaks and fixes
This commit is contained in:
parent
ed7fe60a6d
commit
ed7d265b48
5 changed files with 524 additions and 212 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,5 +1,7 @@
|
||||||
maps
|
maps
|
||||||
saves
|
saves
|
||||||
|
dialogs
|
||||||
__pycache__
|
__pycache__
|
||||||
gameshell.geany
|
gameshell.geany
|
||||||
gameexpr.py
|
gameexpr.py
|
||||||
|
game.py
|
||||||
|
|
447
gamebase.py
447
gamebase.py
|
@ -8,13 +8,19 @@ import random as _ra
|
||||||
import sys as _sys
|
import sys as _sys
|
||||||
import pickle as _pi
|
import pickle as _pi
|
||||||
import ruamel.yaml as _yaml
|
import ruamel.yaml as _yaml
|
||||||
|
import textwrap as _tw
|
||||||
|
|
||||||
class GameError(RuntimeError):
|
class GameError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
class _ScriptBreak(object):
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
class GameBase(object):
|
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"
|
#coordRegex1 = _re.compile(r'(-?[a-zA-Z]+) ?(-?[0-9]+)') # "B12" or "B 12"
|
||||||
#coordRegex2 = _re.compile(r'\(([0-9]+), ([0-9]+)\)') # "(2, 12)"
|
#coordRegex2 = _re.compile(r'\(([0-9]+), ([0-9]+)\)') # "(2, 12)"
|
||||||
#coordRegex3 = _re.compile(r'(-?[0-9]+),? (-?[0-9]+)') # "2 12" or "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('container', self.container)
|
||||||
self.registerUseFunc('info', self.info)
|
self.registerUseFunc('info', self.info)
|
||||||
self.registerUseFunc('wear', self.wear)
|
self.registerUseFunc('wear', self.wear)
|
||||||
|
self.registerBehavior('stand', self.stand)
|
||||||
self.registerBehavior('wander', self.wander)
|
self.registerBehavior('wander', self.wander)
|
||||||
self.registerScript('if', self.ifScript)
|
self.registerScript('if', self.ifScript)
|
||||||
self.registerScript('printf', self.printfScript)
|
self.registerScript('printf', self.printfScript)
|
||||||
|
self.registerScript('get', self.getCustomValue)
|
||||||
self.registerScript('set', self.setCustomValue)
|
self.registerScript('set', self.setCustomValue)
|
||||||
|
self.registerScript('del', self.delCustomValue)
|
||||||
self.registerScript('spawn', self.spawnThing)
|
self.registerScript('spawn', self.spawnThing)
|
||||||
self.registerScript('give', self.giveToPlayer)
|
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
|
# Helper functions
|
||||||
|
|
||||||
|
@ -112,12 +125,30 @@ to get input from stdin."""
|
||||||
def parseCoords(self, args, usePlayerCoords = True, allowInventory = True):
|
def parseCoords(self, args, usePlayerCoords = True, allowInventory = True):
|
||||||
"""Takes an argument list, and figures out what it's talking about.
|
"""Takes an argument list, and figures out what it's talking about.
|
||||||
Returns (thing, x, y). "Thing" can be None."""
|
Returns (thing, x, y). "Thing" can be None."""
|
||||||
coordStr = args[0]
|
|
||||||
if len(args) > 1:
|
|
||||||
coordStr += ' ' + args[1]
|
|
||||||
#print(coordStr)
|
#print(coordStr)
|
||||||
x = -1
|
x = -1
|
||||||
y = -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)
|
match = GameBase.coordRegex.match(coordStr)
|
||||||
if match != None: # if coordinates were given
|
if match != None: # if coordinates were given
|
||||||
#print(match.group())
|
#print(match.group())
|
||||||
|
@ -139,27 +170,7 @@ Returns (thing, x, y). "Thing" can be None."""
|
||||||
else:
|
else:
|
||||||
return thing, x, y
|
return thing, x, y
|
||||||
else: # if a name was given
|
else: # if a name was given
|
||||||
name = ' '.join(args)
|
return None, -1, -1
|
||||||
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
|
|
||||||
|
|
||||||
def justifyText(self, text, width = 80):
|
def justifyText(self, text, width = 80):
|
||||||
ret = []
|
ret = []
|
||||||
|
@ -181,10 +192,12 @@ Returns (thing, x, y). "Thing" can be None."""
|
||||||
|
|
||||||
return '\n'.join(ret)
|
return '\n'.join(ret)
|
||||||
|
|
||||||
def getValueFromString(self, arg: str):
|
def getValueFromString(self, arg: str, env = None):
|
||||||
|
if env == None:
|
||||||
|
env = self.customValues
|
||||||
val = None
|
val = None
|
||||||
validIdent = _re.match(r'[_A-Za-z][_0-9A-Za-z]*', arg)
|
validIdent = _re.match(r'[_A-Za-z][_0-9A-Za-z]*', arg)
|
||||||
if arg[0] in '"\'': # assume it's a string
|
if arg[0] in '"\'' and arg[-1] == arg[0]: # assume it's a string
|
||||||
val = arg[1:-1]
|
val = arg[1:-1]
|
||||||
elif _re.match(r'[+-]?(?:[0-9]*[.])?[0-9]+', arg) != None:
|
elif _re.match(r'[+-]?(?:[0-9]*[.])?[0-9]+', arg) != None:
|
||||||
if '.' in arg:
|
if '.' in arg:
|
||||||
|
@ -204,51 +217,71 @@ Returns (thing, x, y). "Thing" can be None."""
|
||||||
elif arg.casefold() == 'playerinv':
|
elif arg.casefold() == 'playerinv':
|
||||||
val = self.playerInv
|
val = self.playerInv
|
||||||
elif validIdent != None:
|
elif validIdent != None:
|
||||||
if 'scriptLocal' in self.customValues and validIdent.group() in self.customValues['scriptLocal']:
|
group = validIdent.group()
|
||||||
val = self.customValues['scriptLocal'][arg]
|
if 'scriptLocal' in self.customValues and group in self.customValues['scriptLocal']:
|
||||||
elif validIdent.group() in self.customValues:
|
val = self.customValues['scriptLocal'][group]
|
||||||
val = self.customValues[arg]
|
elif group in env:
|
||||||
|
val = env[group]
|
||||||
else:
|
else:
|
||||||
return False # for if statements; if a variable doesn't exist, should evaluate to False
|
return False # for if statements; if a variable doesn't exist, should evaluate to False
|
||||||
# evaluate all values of all indecies
|
# evaluate all values of all indecies
|
||||||
if validIdent.end() < len(arg):
|
openBracket = validIdent.end()
|
||||||
if arg[validIdent.end()] == '[':
|
if openBracket < len(arg):
|
||||||
openBracket = validIdent.end()
|
if arg[openBracket] == '[':
|
||||||
ptr = openBracket
|
ptr = openBracket
|
||||||
depth = 0
|
depth = 0
|
||||||
while ptr < len(arg):
|
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] != '[':
|
if depth == 0 and arg[ptr] != '[':
|
||||||
raise GameError('Invalid value syntax: {}'.format(arg))
|
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:
|
else:
|
||||||
raise GameError('Invalid value syntax: {}'.format(arg))
|
raise GameError('Invalid value syntax: {}'.format(arg))
|
||||||
else:
|
else:
|
||||||
raise GameError('Invalid argument to getValueFromString: {}'.format(arg))
|
raise GameError('Invalid argument to getValueFromString: {}'.format(arg))
|
||||||
return val
|
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"""
|
"""Generalized comparisons, may eventually be extended to other operators"""
|
||||||
lval = self.getValueFromString(left)
|
if len(args) == 1:
|
||||||
rval = self.getValueFromString(right)
|
return bool(self.getValueFromString(args[0]))
|
||||||
if operator == '==':
|
elif len(args) == 3 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
|
||||||
return lval == rval
|
lval = self.getValueFromString(args[0])
|
||||||
elif operator == '!=':
|
operator = args[1]
|
||||||
return lval != rval
|
rval = self.getValueFromString(args[2])
|
||||||
elif operator == '<=':
|
if operator == '==':
|
||||||
return lval <= rval
|
return lval == rval
|
||||||
elif operator == '>=':
|
elif operator == '!=':
|
||||||
return lval >= rval
|
return lval != rval
|
||||||
elif operator == '<':
|
elif operator == '<=':
|
||||||
return lval < rval
|
return lval <= rval
|
||||||
elif operator == '>':
|
elif operator == '>=':
|
||||||
return lval > rval
|
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
|
# commands
|
||||||
|
|
||||||
|
@ -278,6 +311,7 @@ The letter is not case-sensitive."""
|
||||||
# Now we have a heading! Let's see if we can get there...
|
# Now we have a heading! Let's see if we can get there...
|
||||||
if (x, y) == (self.playerx, self.playery):
|
if (x, y) == (self.playerx, self.playery):
|
||||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, x, y, 0.0)))
|
#_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
|
return
|
||||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||||
if dist == -1:
|
if dist == -1:
|
||||||
|
@ -343,7 +377,7 @@ Character can be the name of the character, or their coordinates."""
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
if args[0] == 'the':
|
if args[0] == 'the':
|
||||||
args.pop(0)
|
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):
|
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
|
||||||
print("{} cannot talk to {}.".format(self.playerName, thing.name), file = self.outstream)
|
print("{} cannot talk to {}.".format(self.playerName, thing.name), file = self.outstream)
|
||||||
elif thing == None:
|
elif thing == None:
|
||||||
|
@ -423,15 +457,15 @@ the name of an item in the player's inventory."""
|
||||||
def useOn(self, args, speed):
|
def useOn(self, args, speed):
|
||||||
"""Called by use when there is an 'on' clause"""
|
"""Called by use when there is an 'on' clause"""
|
||||||
onIndex = args.index('on')
|
onIndex = args.index('on')
|
||||||
item, x, y = self.parseCoords(args[:onIndex])
|
item, x, y = self.parseCoords(args[:onIndex], usePlayerCoords = False, allowInventory = True)
|
||||||
if args[onIndex+1] == 'the':
|
if args[onIndex+1] == 'the':
|
||||||
onIndex += 1
|
onIndex += 1
|
||||||
thing, x, y = self.parseCoords(args[onIndex+1:])
|
thing, x, y = self.parseCoords(args[onIndex+1:], usePlayerCoords = True, allowInventory = True)
|
||||||
useArgs = []
|
useArgs = []
|
||||||
if 'with' in args:
|
if 'with' in args:
|
||||||
useArgs = args[args.index('with')+1:]
|
useArgs = args[args.index('with')+1:]
|
||||||
if item == None or item.thingID not in self.playerInv:
|
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()))
|
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||||
return
|
return
|
||||||
if thing == None and x < 0 and y < 0:
|
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)
|
args.pop(0)
|
||||||
if args[0] == 'the':
|
if args[0] == 'the':
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
thing, x, y = self.parseCoords(args)
|
thing, x, y = self.parseCoords(args, allowInventory = False)
|
||||||
if thing == None:
|
if thing == None:
|
||||||
print("There is nothing to take.", file = self.outstream)
|
print("There is nothing to take.", file = self.outstream)
|
||||||
return
|
return
|
||||||
|
@ -522,7 +556,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
for i in self.playerInv:
|
for i in self.playerInv:
|
||||||
thingName = self.playerInv[i].name
|
thingName = self.playerInv[i].name
|
||||||
if ' '.join(args) == thingName:
|
if ' '.join(args) == thingName:
|
||||||
self.setEvent(0.0, _ge.DropEvent(self.playerInv[args[0]]))
|
self.setEvent(0.0, _ge.DropEvent(self.playerInv[i]))
|
||||||
return
|
return
|
||||||
print('{0} does not have a {1}.'.format(self.playerName, args[0]), file = self.outstream)
|
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:
|
else:
|
||||||
self.level, self.nextThing = _gm.GameMap.read(args[0], None, preLoaded, self.nextThing)
|
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
|
# get persistent things from it
|
||||||
if args[0] in self.persist:
|
if args[0] in self.persist:
|
||||||
persistedThings = tuple(self.persist[args[0]].keys())
|
persistedThings = tuple(self.persist[args[0]].keys())
|
||||||
for i in persistedThings:
|
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
|
del self.persist[args[0]][i] # delete them from the persist dict to prevent item duplication
|
||||||
|
|
||||||
print(self.level.openingText, file = self.outstream)
|
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:
|
if self.level.name not in self.persist:
|
||||||
self.persist[self.level.name] = {}
|
self.persist[self.level.name] = {}
|
||||||
for i in self.level.persistent:
|
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
|
# 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!
|
# save it!
|
||||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||||
|
@ -613,7 +650,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
fileName = args[0]
|
fileName = args[0]
|
||||||
x, y, levelname = 1, 1, 'testing/test1.txt'
|
x, y, levelname = 1, 1, 'testing/test1.txt'
|
||||||
with open(fileName, 'rb') as f:
|
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)
|
#print(levelname, x, y, file = self.outstream)
|
||||||
self.loadMap((levelname, x, y))
|
self.loadMap((levelname, x, y))
|
||||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
#_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]
|
del literalStr[l]
|
||||||
l -= 1
|
l -= 1
|
||||||
elif instr[c] in ' \t\n;}{':
|
elif instr[c] in ' \t\n;}{':
|
||||||
if argStart != l:
|
if l > 0 and instr[c-1] == '\\':
|
||||||
ret.append(''.join(literalStr[argStart:l]))
|
del literalStr[l-1]
|
||||||
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]
|
|
||||||
l -= 1
|
l -= 1
|
||||||
elif instr[c] == '}': # close block
|
else:
|
||||||
if len(ret) > 0:
|
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.append(ret)
|
||||||
script = stack.pop()
|
script = []
|
||||||
ret = []
|
ret.append(script)
|
||||||
del literalStr[l]
|
ret = []
|
||||||
l -= 1
|
del literalStr[l]
|
||||||
argStart = l + 1
|
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
|
c += 1
|
||||||
l += 1
|
l += 1
|
||||||
if argStart != l:
|
if argStart != l:
|
||||||
ret.append(''.join(literalStr[argStart:l]))
|
ret.append(''.join(literalStr[argStart:l]))
|
||||||
script.append(ret)
|
if len(ret) > 0:
|
||||||
|
script.append(ret)
|
||||||
#print('After parsing: {}'.format(script))
|
#print('After parsing: {}'.format(script))
|
||||||
self.customValues['scriptLocal'] = {}
|
self.customValues['scriptLocal'] = {}
|
||||||
self.runScript(script)
|
ret = self.runScript(script)
|
||||||
del self.customValues['scriptLocal']
|
if 'scriptLocal' in self.customValues:
|
||||||
|
del self.customValues['scriptLocal']
|
||||||
|
if isinstance(ret, _ScriptBreak):
|
||||||
|
ret = ret.value
|
||||||
|
return ret
|
||||||
|
|
||||||
def runScript(self, script: list):
|
def runScript(self, script: list):
|
||||||
"""run a script"""
|
"""run a script"""
|
||||||
|
ret = False
|
||||||
for line in script:
|
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
|
# run a player command
|
||||||
self.getIO('playercmd')(line[1:])
|
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:
|
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):
|
def gameEventLoop(self):
|
||||||
#print(self.skipLoop)
|
#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.prevx, self.prevy = self.playerx, self.playery
|
||||||
self.playerx, self.playery = e.x, e.y
|
self.playerx, self.playery = e.x, e.y
|
||||||
else:
|
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
|
return False
|
||||||
|
|
||||||
def handleArrive(self, e):
|
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)
|
thing = self.level.getThingAtCoords(self.playerx, self.playery)
|
||||||
if thing:
|
if thing:
|
||||||
if thing.thingType == 'x':
|
if thing.thingType == 'x':
|
||||||
a = self.requestInput('Do you want to go {0}? (Y/n)'.format(str(thing)))
|
self.parseScript(thing.onUse)
|
||||||
if a != 'n' and a != 'N':
|
if (isinstance(thing.key, bool) and thing.key == True) or (isinstance(thing.key, str) and self.parseScript(thing.key)):
|
||||||
self.loadMap((thing.destination, thing.exitid))
|
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
|
return True
|
||||||
else:
|
else:
|
||||||
actor = self.level.getThingByName(e.actor)
|
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):
|
def handleDrop(self, e):
|
||||||
e.item.x = self.playerx
|
e.item.x = self.playerx
|
||||||
e.item.y = self.playery
|
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]
|
del self.playerInv[e.item.thingID]
|
||||||
return True
|
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))
|
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
||||||
|
|
||||||
if thing.customValues['pattern'] == 'single':
|
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':
|
elif thing.customValues['pattern'] == 'loop':
|
||||||
if not 'cursor' in thing.customValues:
|
if not 'cursor' in thing.customValues:
|
||||||
thing.customValues['cursor'] = 0
|
thing.customValues['cursor'] = 0
|
||||||
cursor = thing.customValues['cursor']
|
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'])
|
thing.customValues['cursor'] = (cursor + 1) % len(thing.customValues['text'])
|
||||||
elif thing.customValues['pattern'] == 'once':
|
elif thing.customValues['pattern'] == 'once':
|
||||||
if not 'cursor' in thing.customValues:
|
if not 'cursor' in thing.customValues:
|
||||||
thing.customValues['cursor'] = 0
|
thing.customValues['cursor'] = 0
|
||||||
cursor = thing.customValues['cursor']
|
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:
|
if cursor < len(thing.customValues['text']) - 1:
|
||||||
thing.customValues['cursor'] += 1
|
thing.customValues['cursor'] += 1
|
||||||
elif thing.customValues['pattern'] == 'random':
|
elif thing.customValues['pattern'] == 'random':
|
||||||
cursor = _ra.randrange(len(thing.customValues['text']))
|
cursor = _ra.randrange(len(thing.customValues['text']))
|
||||||
print(self.justifyText(thing.customValues['text'][cursor]), file = self.outstream)
|
print(self.justifyText(str(thing.customValues['text'][cursor])), file = self.outstream)
|
||||||
thing.customValues['cursor'] = cursor
|
thing.customValues['cursor'] = cursor
|
||||||
|
|
||||||
if 'set' in thing.customValues:
|
if 'set' in thing.customValues:
|
||||||
if 'cursor' 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:
|
else:
|
||||||
self.customVals[thing.customValues['set']] = 0
|
self.customValues[thing.customValues['set']] = True
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
def container(self, thing, args):
|
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)))
|
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
|
||||||
|
|
||||||
# evaluate condition
|
# evaluate condition
|
||||||
if args[1] in ('==', '!=', '<=', '>=', '<', '>'):
|
if len(args) > 1 and args[1] in ('==', '!=', '<=', '>=', '<', '>', 'in'):
|
||||||
if len(args) < 4:
|
if len(args) < 4:
|
||||||
raise GameError('Incomplete If statement: if {}'.format(' '.join(args)))
|
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:
|
else:
|
||||||
ret = bool(self.parseValue(args[0]))
|
ret = bool(self.getValueFromString(args[0]))
|
||||||
args = args[1:]
|
args = args[1:]
|
||||||
if inverse:
|
if inverse:
|
||||||
ret = not ret
|
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 condition is true, evaluate further
|
||||||
if ret:
|
if ret:
|
||||||
if isinstance(args[-1], list):
|
if isinstance(args[-1], list):
|
||||||
self.runScript(args[-1])
|
return self.runScript(args[-1])
|
||||||
else:
|
else:
|
||||||
self.runScript([args])
|
return self.runScript([args])
|
||||||
|
|
||||||
def printfScript(self, args):
|
def printfScript(self, args):
|
||||||
"""Assume the first argument is a stringable object, and format all others."""
|
"""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]"""
|
"""takes [customValue, op, value]"""
|
||||||
env = self.customValues
|
if env == None:
|
||||||
|
env = self.customValues
|
||||||
if len(args) > 0 and args[0] == 'local':
|
if len(args) > 0 and args[0] == 'local':
|
||||||
if 'scriptLocal' in self.customValues:
|
if 'scriptLocal' in self.customValues:
|
||||||
env = self.customValues['scriptLocal']
|
env = self.customValues['scriptLocal']
|
||||||
|
@ -925,7 +1008,9 @@ Object can be the name of the object, or its coordinates."""
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
if len(args) < 3 or args[1] not in ('=', '+=', '-=', '*=', '/=', '%=', '//=', '**=', 'b=', '!=', '|=', '&=', '^='):
|
if len(args) < 3 or args[1] not in ('=', '+=', '-=', '*=', '/=', '%=', '//=', '**=', 'b=', '!=', '|=', '&=', '^='):
|
||||||
raise GameError('Arguments are not fit for the setCustomValue script.')
|
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
|
# set the value to a default value (0, 0.0, '', etc.) if not yet assigned
|
||||||
if args[0] not in env and args[1] != '=':
|
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
|
env[args[0]] &= val
|
||||||
elif args[1] == '^=':
|
elif args[1] == '^=':
|
||||||
env[args[0]] ^= val
|
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):
|
def spawnThing(self, args):
|
||||||
"""Spawns a thing. [type name=... location=... etc]"""
|
"""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:]:
|
for i in args[1:]:
|
||||||
if i[0:5].casefold() == 'name=':
|
if i[0:5].casefold() == 'name=':
|
||||||
name = i[5:]
|
name = i[5:]
|
||||||
elif i[0:10].casefold() == 'location=':
|
elif i[0:9].casefold() == 'location=':
|
||||||
_, x, y = self.parseCoords(i[10:].split())
|
_, x, y = self.parseCoords(i[9:].split(), usePlayerCoords = False, allowInventory = False)
|
||||||
elif i[0:4].casefold() == 'fgc=':
|
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:]))
|
raise GameError("Invalid color: {}".format(i[4:]))
|
||||||
for j in i[4:]:
|
for j in i[5:]:
|
||||||
if j not in '0123456789abcdefABCDEF':
|
if j not in '0123456789abcdefABCDEF':
|
||||||
raise GameError("Invalid color: {}".format(i[4:]))
|
raise GameError("Invalid color: {}".format(i[4:]))
|
||||||
fgc = i[4:]
|
fgc = i[4:]
|
||||||
elif i[0:4].casefold() == 'bgc=':
|
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:]))
|
raise GameError("Invalid color: {}".format(i[4:]))
|
||||||
for j in i[4:]:
|
for j in i[5:]:
|
||||||
if j not in '0123456789abcdefABCDEF':
|
if j not in '0123456789abcdefABCDEF':
|
||||||
raise GameError("Invalid color: {}".format(i[4:]))
|
raise GameError("Invalid color: {}".format(i[4:]))
|
||||||
bgc = i[4:]
|
bgc = i[4:]
|
||||||
elif i[0:6].casefold() == 'shape=':
|
elif i[0:6].casefold() == 'shape=':
|
||||||
if len(i[6:]) != 7 or i[0] != '#':
|
if i[6] not in 'ox^ #|-':
|
||||||
raise GameError("Invalid color: {}".format(i[6:]))
|
raise GameError("Invalid shape: {}".format(i[6]))
|
||||||
for j in i[6:]:
|
shape = i[6]
|
||||||
if j not in '0123456789abcdefABCDEF':
|
|
||||||
raise GameError("Invalid color: {}".format(i[6:]))
|
|
||||||
shape = i[6:]
|
|
||||||
elif i == 'persist':
|
elif i == 'persist':
|
||||||
persist = True
|
persist = True
|
||||||
if x < 0 or y < 0:
|
if x < 0 or y < 0:
|
||||||
raise GameError("Cannot spawn thing: bad location")
|
raise GameError("Cannot spawn thing: bad location")
|
||||||
|
|
||||||
# Unfortunately, there needs to be a case for each type.
|
# Unfortunately, there needs to be a case for each type.
|
||||||
|
thing = None
|
||||||
if args[0].casefold() == 'item':
|
if args[0].casefold() == 'item':
|
||||||
# spawn an item
|
# spawn an item
|
||||||
description = 'A nondescript item.'
|
description = 'A nondescript item.'
|
||||||
|
@ -1027,12 +1119,12 @@ Object can be the name of the object, or its coordinates."""
|
||||||
ranged = False
|
ranged = False
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'item {}'.format(self.nextThing)
|
name = 'item {}'.format(self.nextThing)
|
||||||
if fgc == None:
|
|
||||||
fgc = _gm.Item.defaultGraphic['fgc']
|
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gm.Item.defaultGraphic['bgc']
|
bgc = _gm.Item.defaultGraphic[0]
|
||||||
|
if fgc == None:
|
||||||
|
fgc = _gm.Item.defaultGraphic[1]
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gm.Item.defaultGraphic['shape']
|
shape = _gm.Item.defaultGraphic[2]
|
||||||
for i in args[1:]:
|
for i in args[1:]:
|
||||||
if i[0:12].casefold() == 'description=':
|
if i[0:12].casefold() == 'description=':
|
||||||
description = i[12:]
|
description = i[12:]
|
||||||
|
@ -1045,9 +1137,8 @@ Object can be the name of the object, or its coordinates."""
|
||||||
elif i[0:3].casefold() == 'cv:':
|
elif i[0:3].casefold() == 'cv:':
|
||||||
equalsign = i.index('=')
|
equalsign = i.index('=')
|
||||||
cv = i[3:equalsign]
|
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))
|
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':
|
elif args[0].casefold() == 'useable':
|
||||||
# spawn a useable thing
|
# spawn a useable thing
|
||||||
description = 'A nondescript 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
|
playerx, playery = x, y
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'useable {}'.format(self.nextThing)
|
name = 'useable {}'.format(self.nextThing)
|
||||||
if fgc == None:
|
|
||||||
fgc = _gm.Item.defaultGraphic['fgc']
|
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gm.Item.defaultGraphic['bgc']
|
bgc = _gm.Useable.defaultGraphic[0]
|
||||||
|
if fgc == None:
|
||||||
|
fgc = _gm.Useable.defaultGraphic[1]
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gm.Item.defaultGraphic['shape']
|
shape = _gm.Useable.defaultGraphic[2]
|
||||||
for i in args[1:]:
|
for i in args[1:]:
|
||||||
if i[0:12].casefold() == 'description=':
|
if i[0:12].casefold() == 'description=':
|
||||||
description = i[12:]
|
description = i[12:]
|
||||||
|
@ -1072,24 +1163,23 @@ Object can be the name of the object, or its coordinates."""
|
||||||
elif i[0:3].casefold() == 'cv:':
|
elif i[0:3].casefold() == 'cv:':
|
||||||
equalsign = i.index('=')
|
equalsign = i.index('=')
|
||||||
cv = i[3:equalsign]
|
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))
|
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':
|
elif args[0].casefold() == 'npc':
|
||||||
# spawn an NPC
|
# spawn an NPC
|
||||||
description = 'A nondescript character.'
|
description = 'A nondescript character.'
|
||||||
behavior = ''
|
behavior = 'stand'
|
||||||
inv = []
|
inv = []
|
||||||
customValues = {}
|
customValues = {}
|
||||||
playerx, playery = x, y
|
playerx, playery = x, y
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'character {}'.format(self.nextThing)
|
name = 'character {}'.format(self.nextThing)
|
||||||
if fgc == None:
|
|
||||||
fgc = _gm.Item.defaultGraphic['fgc']
|
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gm.Item.defaultGraphic['bgc']
|
bgc = _gm.NPC.defaultGraphic[0]
|
||||||
|
if fgc == None:
|
||||||
|
fgc = _gm.NPC.defaultGraphic[1]
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gm.Item.defaultGraphic['shape']
|
shape = _gm.NPC.defaultGraphic[2]
|
||||||
for i in args[1:]:
|
for i in args[1:]:
|
||||||
if i[0:12].casefold() == 'description=':
|
if i[0:12].casefold() == 'description=':
|
||||||
description = i[12:]
|
description = i[12:]
|
||||||
|
@ -1100,9 +1190,8 @@ Object can be the name of the object, or its coordinates."""
|
||||||
elif i[0:3].casefold() == 'cv:':
|
elif i[0:3].casefold() == 'cv:':
|
||||||
equalsign = i.index('=')
|
equalsign = i.index('=')
|
||||||
cv = i[3:equalsign]
|
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))
|
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':
|
elif args[0].casefold() == 'door':
|
||||||
# spawn a door
|
# spawn a door
|
||||||
description = 'a nondescript door.'
|
description = 'a nondescript door.'
|
||||||
|
@ -1110,32 +1199,33 @@ Object can be the name of the object, or its coordinates."""
|
||||||
key = None
|
key = None
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'door {}'.format(self.nextThing)
|
name = 'door {}'.format(self.nextThing)
|
||||||
if fgc == None:
|
|
||||||
fgc = _gm.Item.defaultGraphic['fgc']
|
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gm.Item.defaultGraphic['bgc']
|
bgc = _gm.Door.defaultGraphic[0]
|
||||||
|
if fgc == None:
|
||||||
|
fgc = _gm.Door.defaultGraphic[1]
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gm.Item.defaultGraphic['shape']
|
shape = _gm.Door.defaultGraphic[2]
|
||||||
for i in args[1:]:
|
for i in args[1:]:
|
||||||
if i[0:12].casefold() == 'description=':
|
if i[0:12].casefold() == 'description=':
|
||||||
description = i[12:]
|
description = i[12:]
|
||||||
elif i.casefold() == 'locked':
|
elif i.casefold() == 'locked':
|
||||||
locked = True
|
locked = True
|
||||||
thing = _gm.Door(name, x, y, locked, description, key, (bgc, fgc, shape))
|
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':
|
elif args[0].casefold() == 'mapexit':
|
||||||
# spawn an exit to another map (use with EXTREME caution!)
|
# spawn an exit to another map (use with EXTREME caution!)
|
||||||
destination = ''
|
destination = ''
|
||||||
prefix = None
|
prefix = None
|
||||||
exitid = 0
|
exitid = 0
|
||||||
|
onUse = ''
|
||||||
|
key = True
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'somewhere'
|
name = 'somewhere'
|
||||||
if fgc == None:
|
|
||||||
fgc = _gm.Item.defaultGraphic['fgc']
|
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gm.Item.defaultGraphic['bgc']
|
bgc = _gm.MapExit.defaultGraphic[0]
|
||||||
|
if fgc == None:
|
||||||
|
fgc = _gm.MapExit.defaultGraphic[1]
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gm.Item.defaultGraphic['shape']
|
shape = _gm.MapExit.defaultGraphic[2]
|
||||||
for i in args[1:]:
|
for i in args[1:]:
|
||||||
if i[0:12].casefold() == 'destination=':
|
if i[0:12].casefold() == 'destination=':
|
||||||
destination = i[12:]
|
destination = i[12:]
|
||||||
|
@ -1143,8 +1233,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
prefix = i[7:]
|
prefix = i[7:]
|
||||||
elif i[0:7].casefold() == 'exitid=':
|
elif i[0:7].casefold() == 'exitid=':
|
||||||
exitid = int(i[7:])
|
exitid = int(i[7:])
|
||||||
thing = _gm.MapExit(name, x, y, exitid, destination, prefix, (bgc, fgc, shape))
|
elif i[0:6].casefold() == 'onuse=':
|
||||||
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
|
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':
|
elif args[0].casefold() == 'mapentrance':
|
||||||
# spawn a map entrance
|
# spawn a map entrance
|
||||||
exitid = 0
|
exitid = 0
|
||||||
|
@ -1152,7 +1245,10 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if i[0:7].casefold() == 'exitid=':
|
if i[0:7].casefold() == 'exitid=':
|
||||||
exitid = int(i[7:])
|
exitid = int(i[7:])
|
||||||
thing = _gm.MapEntrance(x, y, exitid, name)
|
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):
|
def giveToPlayer(self, args):
|
||||||
"""We get to assume it's an item."""
|
"""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=':
|
if i[0:5].casefold() == 'name=':
|
||||||
name = i[5:]
|
name = i[5:]
|
||||||
elif i[0:10].casefold() == 'location=':
|
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=':
|
elif i[0:4].casefold() == 'fgc=':
|
||||||
if len(i[4:]) != 7 or i[0] != '#':
|
if len(i[4:]) != 7 or i[0] != '#':
|
||||||
raise GameError("Invalid color: {}".format(i[4:]))
|
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]
|
cv = i[3:equalsign]
|
||||||
customValues[cv] = getValueFromString(i[equalsign+1:])
|
customValues[cv] = getValueFromString(i[equalsign+1:])
|
||||||
thing = _gm.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, (bgc, fgc, shape))
|
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
|
# behaviors
|
||||||
|
|
||||||
|
def stand(self, actor):
|
||||||
|
pass
|
||||||
|
|
||||||
def wander(self, actor):
|
def wander(self, actor):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
39
gamemap.py
39
gamemap.py
|
@ -358,7 +358,7 @@ class MapExit(Thing):
|
||||||
yaml_flag = u'!MapExit'
|
yaml_flag = u'!MapExit'
|
||||||
defaultGraphic = ('clear', '#FF0000', 'x')
|
defaultGraphic = ('clear', '#FF0000', 'x')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, exitid: int, destination: str, prefix: None, graphic = defaultGraphic):
|
def __init__(self, name, x: int, y: int, exitid: int, destination: str, prefix = None, onUse = '', key = True, graphic = defaultGraphic):
|
||||||
description = name
|
description = name
|
||||||
if prefix:
|
if prefix:
|
||||||
description = "{0} {1}".format(prefix, name)
|
description = "{0} {1}".format(prefix, name)
|
||||||
|
@ -366,6 +366,8 @@ class MapExit(Thing):
|
||||||
self.exitid = exitid
|
self.exitid = exitid
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
self.prefix = prefix
|
self.prefix = prefix
|
||||||
|
self.onUse = onUse
|
||||||
|
self.key = key
|
||||||
self.graphic = graphic
|
self.graphic = graphic
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -384,6 +386,10 @@ class MapExit(Thing):
|
||||||
ret['graphic'] = graphic
|
ret['graphic'] = graphic
|
||||||
if node.prefix != None:
|
if node.prefix != None:
|
||||||
ret['prefix'] = node.prefix
|
ret['prefix'] = node.prefix
|
||||||
|
if node.onUse != '':
|
||||||
|
ret['onUse'] = node.onUse
|
||||||
|
if node.key != True:
|
||||||
|
ret['key'] = node.key
|
||||||
return representer.represent_mapping(cls.yaml_flag, ret)
|
return representer.represent_mapping(cls.yaml_flag, ret)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -392,6 +398,8 @@ class MapExit(Thing):
|
||||||
constructor.construct_mapping(node, parts, True)
|
constructor.construct_mapping(node, parts, True)
|
||||||
# set default values for optional arguments
|
# set default values for optional arguments
|
||||||
prefix = None
|
prefix = None
|
||||||
|
onUse = ''
|
||||||
|
key = True
|
||||||
bgc = MapExit.defaultGraphic[0]
|
bgc = MapExit.defaultGraphic[0]
|
||||||
fgc = MapExit.defaultGraphic[1]
|
fgc = MapExit.defaultGraphic[1]
|
||||||
shape = MapExit.defaultGraphic[2]
|
shape = MapExit.defaultGraphic[2]
|
||||||
|
@ -406,8 +414,12 @@ class MapExit(Thing):
|
||||||
graphic = (bgc, fgc, shape)
|
graphic = (bgc, fgc, shape)
|
||||||
if 'prefix' in parts:
|
if 'prefix' in parts:
|
||||||
prefix = parts['prefix']
|
prefix = parts['prefix']
|
||||||
|
if 'onUse' in parts:
|
||||||
|
onUse = parts['onUse']
|
||||||
|
if 'key' in parts:
|
||||||
|
key = parts['key']
|
||||||
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
||||||
parts['id'], parts['destination'], prefix, graphic)
|
parts['id'], parts['destination'], prefix, onUse, key, graphic)
|
||||||
|
|
||||||
class MapEntrance(Thing):
|
class MapEntrance(Thing):
|
||||||
yaml_flag = u'!MapEntrance'
|
yaml_flag = u'!MapEntrance'
|
||||||
|
@ -417,7 +429,7 @@ class MapEntrance(Thing):
|
||||||
def __init__(self, x: int, y: int, exitid: int, name = None):
|
def __init__(self, x: int, y: int, exitid: int, name = None):
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'entrance {}'.format(exitid)
|
name = 'entrance {}'.format(exitid)
|
||||||
super(MapEntrance, self).__init__('a', name, x, y, description, 1)
|
super(MapEntrance, self).__init__('a', name, x, y, '', 1)
|
||||||
self.exitid = exitid
|
self.exitid = exitid
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -574,6 +586,7 @@ Entering a map through stdin will be obsolete once testing is over."""
|
||||||
yaml.register_class(NPC)
|
yaml.register_class(NPC)
|
||||||
yaml.register_class(Door)
|
yaml.register_class(Door)
|
||||||
yaml.register_class(MapExit)
|
yaml.register_class(MapExit)
|
||||||
|
yaml.register_class(MapEntrance)
|
||||||
if infile != None:
|
if infile != None:
|
||||||
try:
|
try:
|
||||||
with open(infile, 'r') as f:
|
with open(infile, 'r') as f:
|
||||||
|
@ -582,7 +595,7 @@ Entering a map through stdin will be obsolete once testing is over."""
|
||||||
print("The file could not be read.")
|
print("The file could not be read.")
|
||||||
return None, nextThing
|
return None, nextThing
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("No file was specified for loading.")
|
raise MapError("No file was specified for loading.")
|
||||||
|
|
||||||
# Now what we do with the data
|
# Now what we do with the data
|
||||||
mat = None
|
mat = None
|
||||||
|
@ -593,10 +606,10 @@ Entering a map through stdin will be obsolete once testing is over."""
|
||||||
#print(layout.text)
|
#print(layout.text)
|
||||||
match = GameMap.matrixRegex.match(layout.lstrip())
|
match = GameMap.matrixRegex.match(layout.lstrip())
|
||||||
if match == None:
|
if match == None:
|
||||||
raise RuntimeError('Map read a file without a map matrix.')
|
raise MapError('Map read a file without a map matrix.')
|
||||||
mat = match.group()
|
mat = match.group()
|
||||||
else:
|
else:
|
||||||
raise RuntimeError('Map read a file without a map matrix.')
|
raise MapError('Map read a file without a map matrix.')
|
||||||
|
|
||||||
# generate matrix and graph first
|
# generate matrix and graph first
|
||||||
mapMatrix, mapGraph, dimensions = GameMap.parseMatrix(mat)
|
mapMatrix, mapGraph, dimensions = GameMap.parseMatrix(mat)
|
||||||
|
@ -632,7 +645,7 @@ list of lists of tuples."""
|
||||||
if x == 0:
|
if x == 0:
|
||||||
x = len(mat[l])
|
x = len(mat[l])
|
||||||
elif x != len(mat[l]):
|
elif x != len(mat[l]):
|
||||||
raise RuntimeError("Map matrix has jagged edges.")
|
raise MapError("Map matrix has jagged edges.")
|
||||||
l += 1
|
l += 1
|
||||||
#x = 0
|
#x = 0
|
||||||
mat.append([])
|
mat.append([])
|
||||||
|
@ -644,7 +657,7 @@ list of lists of tuples."""
|
||||||
else:
|
else:
|
||||||
matrixStr = matrixStr[i:]
|
matrixStr = matrixStr[i:]
|
||||||
else: # This should happen when it finishes?
|
else: # This should happen when it finishes?
|
||||||
raise RuntimeError("Unexpected token in map matrix: '{0}'".format(matrixStr))
|
raise MapError("Unexpected token in map matrix: '{0}'".format(matrixStr))
|
||||||
y = len(mat) - 1
|
y = len(mat) - 1
|
||||||
|
|
||||||
# Now for the graph
|
# Now for the graph
|
||||||
|
@ -995,19 +1008,19 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
newPos = self.coordsToInt(x, y)
|
newPos = self.coordsToInt(x, y)
|
||||||
else:
|
else:
|
||||||
x, y = self.intToCoords(x)
|
x, y = self.intToCoords(x)
|
||||||
if thing:
|
if thing != None:
|
||||||
oldPos = self.coordsToInt(thing.x, thing.y)
|
oldPos = self.coordsToInt(thing.x, thing.y)
|
||||||
if oldPos in self.thingPos:
|
if oldPos in self.thingPos:
|
||||||
self.thingPos[oldPos].remove(name)
|
self.thingPos[oldPos].remove(thing.thingID)
|
||||||
if len(self.thingPos[oldPos]) == 0:
|
if len(self.thingPos[oldPos]) == 0:
|
||||||
del self.thingPos[oldPos]
|
del self.thingPos[oldPos]
|
||||||
if newPos not in self.thingPos:
|
if newPos not in self.thingPos:
|
||||||
self.thingPos[newPos] = [name]
|
self.thingPos[newPos] = [thing.thingID]
|
||||||
else:
|
else:
|
||||||
self.thingPos[newPos].append(name)
|
self.thingPos[newPos].append(thing.thingID)
|
||||||
relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y
|
relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y
|
||||||
thing.x, thing.y = x, y
|
thing.x, thing.y = x, y
|
||||||
thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery
|
thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("There is nothing to move.".format(name))
|
raise MapError("There is nothing to move.")
|
||||||
|
|
||||||
|
|
202
gameshell.py
202
gameshell.py
|
@ -27,36 +27,26 @@ class GameShell(Shell):
|
||||||
self.outstream = _sys.stdout
|
self.outstream = _sys.stdout
|
||||||
self.gameBase = gameBase
|
self.gameBase = gameBase
|
||||||
self.colorMode = 0
|
self.colorMode = 0
|
||||||
|
self.gameTitle = 'Game Shell' # should be changed for actual games
|
||||||
|
self.startLevel = 'testing/test1.yml' # should be changed for actual games
|
||||||
|
self.openingText = '{}\nIn Development'
|
||||||
self.ps2 = '?> '
|
self.ps2 = '?> '
|
||||||
|
self.__inGame = False
|
||||||
|
|
||||||
# register functions
|
# register functions
|
||||||
|
|
||||||
self.registerCommand('map', self.showMap)
|
self.registerCommand('load', self.gameBase.loadGame) # should always be available
|
||||||
self.registerCommand('ls', self.showMap)
|
self.registerCommand('flippetywick', self.devMode)
|
||||||
self.registerCommand('go', self.gameBase.go)
|
|
||||||
self.registerCommand('move', self.gameBase.go)
|
|
||||||
self.registerCommand('walk', self.gameBase.go)
|
|
||||||
self.registerCommand('look', self.gameBase.look)
|
|
||||||
self.registerCommand('talk', self.gameBase.talk)
|
|
||||||
self.registerCommand('use', self.gameBase.use)
|
|
||||||
self.registerCommand('loadMap', self.gameBase.loadMap)
|
|
||||||
self.registerCommand('man', self.man)
|
|
||||||
self.registerCommand('help', self.man)
|
|
||||||
self.registerCommand('save', self.gameBase.saveGame)
|
|
||||||
self.registerCommand('load', self.gameBase.loadGame)
|
|
||||||
self.registerCommand('take', self.gameBase.take)
|
|
||||||
self.registerCommand('get', self.gameBase.take)
|
|
||||||
self.registerCommand('drop', self.gameBase.drop)
|
|
||||||
self.registerCommand('inv', self.inv)
|
|
||||||
self.registerCommand('bag', self.inv)
|
|
||||||
self.registerCommand('items', self.inv)
|
|
||||||
self.registerCommand('status', self.status)
|
|
||||||
self.registerCommand('options', self.options)
|
self.registerCommand('options', self.options)
|
||||||
self.registerCommand('colorTest', self.colorTest)
|
self.registerCommand('colorTest', self.colorTest)
|
||||||
self.registerAlias('run', ['go', '-r'])
|
self.registerCommand('man', self.man)
|
||||||
|
self.registerCommand('help', self.man)
|
||||||
|
self.registerCommand('new', self.newGame)
|
||||||
self.gameBase.registerIO('container', self.container)
|
self.gameBase.registerIO('container', self.container)
|
||||||
self.gameBase.registerIO('dialog', self.dialog)
|
self.gameBase.registerIO('dialog', self.dialog)
|
||||||
self.gameBase.registerIO('info', self.info)
|
self.gameBase.registerIO('info', self.info)
|
||||||
|
self.gameBase.registerIO('playercmd', self.playercmd)
|
||||||
|
self.menuMode()
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
|
@ -108,6 +98,39 @@ class GameShell(Shell):
|
||||||
self.__colorTestRoutine(0, 128) # dark
|
self.__colorTestRoutine(0, 128) # dark
|
||||||
self.__colorTestRoutine(0, 256) # medium
|
self.__colorTestRoutine(0, 256) # medium
|
||||||
self.__colorTestRoutine(128, 256) # light
|
self.__colorTestRoutine(128, 256) # light
|
||||||
|
|
||||||
|
def __getAdjFloors(self, level, x, y):
|
||||||
|
"""Get the floor colors of the 8 nearest tiles."""
|
||||||
|
adjFloors = []
|
||||||
|
for i in range(y-1, y+2):
|
||||||
|
if i < 0 or i >= level.dimensions[1]:
|
||||||
|
continue
|
||||||
|
for j in range(x-1, x+2):
|
||||||
|
if j < 0 or j >= level.dimensions[0]:
|
||||||
|
continue
|
||||||
|
if level.mapMatrix[i][j][0] == 'e':
|
||||||
|
adjFloors.append(level.mapMatrix[i][j][1])
|
||||||
|
return adjFloors
|
||||||
|
|
||||||
|
def __getUnderWallColor(self, level, x, y):
|
||||||
|
adjFloors = self.__getAdjFloors(level, x, y)
|
||||||
|
if len(adjFloors) > 0:
|
||||||
|
counts = {}
|
||||||
|
highestCount = 0
|
||||||
|
ret = -1
|
||||||
|
for i in adjFloors:
|
||||||
|
if i in counts:
|
||||||
|
counts[i] += 1
|
||||||
|
else:
|
||||||
|
counts[i] = 1
|
||||||
|
if counts[i] > highestCount:
|
||||||
|
ret = i
|
||||||
|
highestCount = counts[i]
|
||||||
|
elif counts[i] == highestCount and i < ret:
|
||||||
|
ret = i
|
||||||
|
return ret
|
||||||
|
else:
|
||||||
|
return -1
|
||||||
|
|
||||||
def showMap(self, args):
|
def showMap(self, args):
|
||||||
"""map [-l]
|
"""map [-l]
|
||||||
|
@ -170,8 +193,15 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
sides += GameShell.DOWN
|
sides += GameShell.DOWN
|
||||||
if x > 0 and level.mapMatrix[y][x-1][0] == 'w':
|
if x > 0 and level.mapMatrix[y][x-1][0] == 'w':
|
||||||
sides += GameShell.LEFT
|
sides += GameShell.LEFT
|
||||||
|
underWallColor = self.__getUnderWallColor(level, x, y)
|
||||||
|
if level.floorColors[underWallColor] != floorColor and underWallColor != -1:
|
||||||
|
floorColor = level.floorColors[underWallColor]
|
||||||
|
rows[-1].append(self.color(floorColor[1:], fg = False))
|
||||||
rows[-1].append(GameShell.WALLS[sides])
|
rows[-1].append(GameShell.WALLS[sides])
|
||||||
else:
|
else:
|
||||||
|
if level.floorColors[level.mapMatrix[y][x][1]] != floorColor:
|
||||||
|
floorColor = level.floorColors[level.mapMatrix[y][x][1]]
|
||||||
|
rows[-1].append(self.color(floorColor[1:], fg = False))
|
||||||
rows[-1].append(' ')
|
rows[-1].append(' ')
|
||||||
index += 1
|
index += 1
|
||||||
rows[-1] = ''.join(rows[-1])
|
rows[-1] = ''.join(rows[-1])
|
||||||
|
@ -235,26 +265,108 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
def inv(self, args):
|
def inv(self, args):
|
||||||
print('\n'.join([self.gameBase.playerInv[i].name for i in self.gameBase.playerInv]))
|
print('\n'.join([self.gameBase.playerInv[i].name for i in self.gameBase.playerInv]))
|
||||||
|
|
||||||
|
def newGame(self, args):
|
||||||
|
if self.__inGame:
|
||||||
|
response = input("Do you want to save before exiting (Y/n/x)? ")
|
||||||
|
if len(response) > 0:
|
||||||
|
if response[0] in 'Nn':
|
||||||
|
super(GameShell, self).exitShell(args)
|
||||||
|
elif response[0] in 'Xx': # cancel
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
gameBase.saveGame([sf])
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
gameBase.saveGame([sf])
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
try:
|
||||||
|
self.gameBase.loadMap([self.startLevel])
|
||||||
|
except RuntimeError as e:
|
||||||
|
print(e)
|
||||||
|
return
|
||||||
|
self.gameMode()
|
||||||
|
|
||||||
|
def gameMode(self):
|
||||||
|
"""Mode for in-game."""
|
||||||
|
if not self.__inGame:
|
||||||
|
self.registerCommand('map', self.showMap)
|
||||||
|
self.registerCommand('ls', self.showMap)
|
||||||
|
self.registerCommand('go', self.gameBase.go)
|
||||||
|
self.registerCommand('move', self.gameBase.go)
|
||||||
|
self.registerCommand('walk', self.gameBase.go)
|
||||||
|
self.registerCommand('look', self.gameBase.look)
|
||||||
|
self.registerCommand('talk', self.gameBase.talk)
|
||||||
|
self.registerCommand('use', self.gameBase.use)
|
||||||
|
self.registerCommand('save', self.gameBase.saveGame)
|
||||||
|
self.registerCommand('take', self.gameBase.take)
|
||||||
|
self.registerCommand('get', self.gameBase.take)
|
||||||
|
self.registerCommand('drop', self.gameBase.drop)
|
||||||
|
self.registerCommand('inv', self.inv)
|
||||||
|
self.registerCommand('bag', self.inv)
|
||||||
|
self.registerCommand('items', self.inv)
|
||||||
|
self.registerAlias('run', ['go', '-r'])
|
||||||
|
self.__inGame = True
|
||||||
|
|
||||||
|
def menuMode(self, text = None):
|
||||||
|
"""Mode for main menus and the like."""
|
||||||
|
if self.__inGame:
|
||||||
|
self.registerCommand('map', self.showMap)
|
||||||
|
self.unRegisterCommand('ls', self.showMap)
|
||||||
|
self.unRegisterCommand('go', self.gameBase.go)
|
||||||
|
self.unRegisterCommand('move', self.gameBase.go)
|
||||||
|
self.unRegisterCommand('walk', self.gameBase.go)
|
||||||
|
self.unRegisterCommand('look', self.gameBase.look)
|
||||||
|
self.unRegisterCommand('talk', self.gameBase.talk)
|
||||||
|
self.unRegisterCommand('use', self.gameBase.use)
|
||||||
|
self.unRegisterCommand('save', self.gameBase.saveGame)
|
||||||
|
self.unRegisterCommand('take', self.gameBase.take)
|
||||||
|
self.unRegisterCommand('get', self.gameBase.take)
|
||||||
|
self.unRegisterCommand('drop', self.gameBase.drop)
|
||||||
|
self.unRegisterCommand('inv', self.inv)
|
||||||
|
self.unRegisterCommand('bag', self.inv)
|
||||||
|
self.unRegisterCommand('items', self.inv)
|
||||||
|
self.unRegisterAlias('run', ['go', '-r'])
|
||||||
|
self.__inGame = False
|
||||||
|
if text == None:
|
||||||
|
print(self.openingText.format(self.gameTitle))
|
||||||
|
else:
|
||||||
|
print(text.format(self.gameTitle))
|
||||||
|
|
||||||
|
def devMode(self, args):
|
||||||
|
print('Dev mode activated. Please dev responsibly.')
|
||||||
|
self.gameMode()
|
||||||
|
self.registerCommand('dev', self.runScript)
|
||||||
|
self.registerCommand('status', self.status)
|
||||||
|
self.registerCommand('loadMap', self.gameBase.loadMap)
|
||||||
|
|
||||||
|
def runScript(self, args):
|
||||||
|
"""simple wrapper for gameBase.runscript."""
|
||||||
|
self.gameBase.parseScript(' '.join(args))
|
||||||
|
|
||||||
# IO calls
|
# IO calls
|
||||||
|
|
||||||
def container(self, inv, cont):
|
def container(self, inv, cont):
|
||||||
"""container IO"""
|
"""container IO"""
|
||||||
# Pretty print: get length of the longest inventory item's name
|
# Pretty print: get length of the longest inventory item's name
|
||||||
longestLen = 0
|
longestLen = 0
|
||||||
longestStr = ""
|
|
||||||
for i in inv:
|
for i in inv:
|
||||||
if len(i) > longestLen:
|
if len(inv[i].name) > longestLen:
|
||||||
longestLen = len(i)
|
longestLen = len(inv[i].name)
|
||||||
longestStr = i
|
|
||||||
if longestLen > 0:
|
if longestLen > 0:
|
||||||
print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:"))
|
print('{{0:<{0}}}{1}'.format(max(6, longestLen+2), "Container:").format("Inv:"))
|
||||||
i = 0
|
i = 0
|
||||||
invKeys = tuple(inv.keys())
|
invKeys = tuple(inv.keys())
|
||||||
while i < len(invKeys) and i < len(cont):
|
while i < len(invKeys) and i < len(cont):
|
||||||
print('{{0:<{0}}}{1}'.format(longestLen+2, cont[i].name).format(invKeys[i]))
|
print('{{0:<{0}}}{1}'.format(longestLen+2, cont[i].name).format(inv[invKeys[i]].name))
|
||||||
i += 1
|
i += 1
|
||||||
while i < len(invKeys):
|
while i < len(invKeys):
|
||||||
print(invKeys[i])
|
print(inv[invKeys[i]].name)
|
||||||
i += 1
|
i += 1
|
||||||
while i < len(cont):
|
while i < len(cont):
|
||||||
print(' '*(longestLen+2) + cont[i].name)
|
print(' '*(longestLen+2) + cont[i].name)
|
||||||
|
@ -308,7 +420,7 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
charsRead += len(items[instr])
|
charsRead += len(items[instr])
|
||||||
else:
|
else:
|
||||||
print('{} not here.'.format(instr))
|
print('{} not here.'.format(instr))
|
||||||
input('<strike RETURN>')
|
input('<ENTER>')
|
||||||
for i in items:
|
for i in items:
|
||||||
print(' ', i)
|
print(' ', i)
|
||||||
instr = input("Choose an item to view, or exit: ")
|
instr = input("Choose an item to view, or exit: ")
|
||||||
|
@ -336,7 +448,10 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
return ret
|
return ret
|
||||||
# if ret == 1 or ret == 0, go back again.
|
# if ret == 1 or ret == 0, go back again.
|
||||||
elif 'opener' in dialogObj:
|
elif 'opener' in dialogObj:
|
||||||
print(_tw.fill(dialogObj['opener'], width = TERM_SIZE))
|
toPrint = dialogObj['opener'].split('\n') # split by lines so that fill doesn't erase newlines
|
||||||
|
for line in toPrint:
|
||||||
|
print(_tw.fill(line, width = TERM_SIZE))
|
||||||
|
input('<ENTER>')
|
||||||
while isinstance(dialogObj, dict):
|
while isinstance(dialogObj, dict):
|
||||||
if 'action' in dialogObj:
|
if 'action' in dialogObj:
|
||||||
action = dialogObj['action']
|
action = dialogObj['action']
|
||||||
|
@ -350,8 +465,8 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
if ans[0] == '?':
|
if ans[0] == '?':
|
||||||
condEnd = ans.index(':')
|
condEnd = ans.index(':')
|
||||||
cond = ans[1:condEnd].strip().split()
|
cond = ans[1:condEnd].strip().split()
|
||||||
if self.gameBase.compareValues(cond[1], cond[0], cond[2]):
|
if self.gameBase.compareValues(cond):
|
||||||
print(_tw.fill('{}: {}'.format(j+1, ans[condEnd+1:]), width = TERM_SIZE))
|
print(_tw.fill('{}: {}'.format(j+1, ans[condEnd+1:].strip()), width = TERM_SIZE))
|
||||||
j += 1
|
j += 1
|
||||||
else:
|
else:
|
||||||
skips.append(i)
|
skips.append(i)
|
||||||
|
@ -389,6 +504,31 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
self.gameBase.gameEventLoop()
|
self.gameBase.gameEventLoop()
|
||||||
|
|
||||||
|
def exitShell(self, args):
|
||||||
|
if self.__inGame:
|
||||||
|
response = input("Do you want to save before exiting (Y/n/x)? ")
|
||||||
|
if len(response) > 0:
|
||||||
|
if response[0] in 'Nn':
|
||||||
|
super(GameShell, self).exitShell(args)
|
||||||
|
elif response[0] in 'Xx': # cancel
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
gameBase.saveGame([sf])
|
||||||
|
super(GameShell, self).exitShell(args)
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
else:
|
||||||
|
sf = input('Save file: ')
|
||||||
|
if len(sf) > 0:
|
||||||
|
gameBase.saveGame([sf])
|
||||||
|
super(GameShell, self).exitShell(args)
|
||||||
|
else:
|
||||||
|
print('No save file given, cancelling exit.')
|
||||||
|
else:
|
||||||
|
super(GameShell, self).exitShell(args)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
sh = GameShell(GameBase())
|
sh = GameShell(GameBase())
|
||||||
|
|
46
shell.py
46
shell.py
|
@ -140,13 +140,25 @@ class Shell(object):
|
||||||
conventionally called args or argv"""
|
conventionally called args or argv"""
|
||||||
self.__commands[commandName] = command
|
self.__commands[commandName] = command
|
||||||
|
|
||||||
|
def unRegisterCommand(self, commandName: str):
|
||||||
|
"""Remove a command. May be useful for inheriting classes and the like."""
|
||||||
|
del self.__commands[commandName]
|
||||||
|
|
||||||
def registerAlias(self, a: str, original: list):
|
def registerAlias(self, a: str, original: list):
|
||||||
"""makes 'a' an alias for original.
|
"""makes 'a' an alias for original.
|
||||||
'a' must be one token, but original can be multiple."""
|
'a' must be one token, but original can be multiple."""
|
||||||
self.__aliases[a] = original
|
self.__aliases[a] = original
|
||||||
|
|
||||||
|
def unRegisterAlias(self, commandName: str):
|
||||||
|
"""Remove an alias. May be useful for inheriting classes and the like."""
|
||||||
|
del self.__aliases[commandName]
|
||||||
|
|
||||||
def registerBatch(self, name: str, commands: list):
|
def registerBatch(self, name: str, commands: list):
|
||||||
self.__batches[name] = commands
|
self.__batches[name] = commands
|
||||||
|
|
||||||
|
def unRegisterBatch(self, commandName: str):
|
||||||
|
"""Remove a command. May be useful for inheriting classes and the like."""
|
||||||
|
del self.__batches[commandName]
|
||||||
|
|
||||||
def getAlias(self, a: str):
|
def getAlias(self, a: str):
|
||||||
if a in self.__aliases:
|
if a in self.__aliases:
|
||||||
|
@ -164,29 +176,39 @@ conventionally called args or argv"""
|
||||||
"""Take a line of text and turn it into an argument list"""
|
"""Take a line of text and turn it into an argument list"""
|
||||||
if instr == '':
|
if instr == '':
|
||||||
return []
|
return []
|
||||||
|
literalStr = list(instr)
|
||||||
inQuotes = False
|
inQuotes = False
|
||||||
ret = []
|
ret = []
|
||||||
argStart = 0
|
argStart = 0
|
||||||
c = 0
|
c = 0
|
||||||
|
l = 0
|
||||||
while c < len(instr):
|
while c < len(instr):
|
||||||
if inQuotes:
|
if inQuotes:
|
||||||
if instr[c] == '"':
|
if instr[c] == '"':
|
||||||
inQuotes = False
|
if instr[c-1] == '\\':
|
||||||
ret.append(instr[argStart:c])
|
del literalStr[l-1]
|
||||||
argStart = c+1
|
l -= 1
|
||||||
|
else:
|
||||||
|
inQuotes = False
|
||||||
|
del literalStr[l]
|
||||||
|
l -= 1
|
||||||
else:
|
else:
|
||||||
if instr[c] == '"':
|
if instr[c] == '"':
|
||||||
inQuotes = True
|
if l > 0 and instr[c-1] == '\\':
|
||||||
if argStart != c:
|
del literalStr[l-1]
|
||||||
ret.append(instr[argStart:c])
|
l -= 1
|
||||||
argStart = c+1
|
else:
|
||||||
|
inQuotes = True
|
||||||
|
del literalStr[l]
|
||||||
|
l -= 1
|
||||||
elif instr[c] in ' \t\n':
|
elif instr[c] in ' \t\n':
|
||||||
if argStart != c:
|
if argStart != l:
|
||||||
ret.append(instr[argStart:c])
|
ret.append(''.join(literalStr[argStart:l]))
|
||||||
argStart = c + 1
|
argStart = l + 1
|
||||||
c += 1
|
c += 1
|
||||||
if argStart != c:
|
l += 1
|
||||||
ret.append(instr[argStart:c])
|
if argStart != l:
|
||||||
|
ret.append(''.join(literalStr[argStart:l]))
|
||||||
a = self.getAlias(ret[0])
|
a = self.getAlias(ret[0])
|
||||||
if a:
|
if a:
|
||||||
ret = a + ret[1:]
|
ret = a + ret[1:]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue