Initial commit
This commit is contained in:
commit
565b6ba903
12 changed files with 2284 additions and 0 deletions
628
gamebase.py
Normal file
628
gamebase.py
Normal file
|
@ -0,0 +1,628 @@
|
|||
# gamebase.py
|
||||
|
||||
import re as _re
|
||||
import heapq as _hq
|
||||
import gamemap as _gm
|
||||
import gameevents as _ge
|
||||
import random as _ra
|
||||
import sys as _sys
|
||||
import pickle as _pi
|
||||
|
||||
class GameBase(object):
|
||||
|
||||
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"
|
||||
|
||||
def __init__(self):
|
||||
self.outstream = _sys.stdout
|
||||
self.__useFuncs = {}
|
||||
self.__gameEvents = {}
|
||||
self.customVals = {} # for setting flags and such
|
||||
self.level = None
|
||||
self.persist = {} # {level : {thingName : thing}}
|
||||
self.ps2 = '? '
|
||||
self.eventQueue = []
|
||||
self.gameTime = 0.0
|
||||
|
||||
# player info
|
||||
self.playerx = -1
|
||||
self.playery = -1
|
||||
self.prevx = -1 # prevx and prevy are for moving the player out of the way
|
||||
self.prevy = -1 # when the tile they're standing on becomes blocked.
|
||||
self.playerName = 'You'
|
||||
self.playerInv = {}
|
||||
|
||||
# function deligates
|
||||
self.onLevelLoad = None # str : level name? -> None
|
||||
self.onContainer = None # list : contents -> list : newContents, float : timeOpen
|
||||
|
||||
# default events and useFuncs
|
||||
self.registerEvent('noop', self.handleNoOp)
|
||||
self.registerEvent('go', self.handleGo)
|
||||
self.registerEvent('arrive', self.handleArrive)
|
||||
self.registerEvent('use', self.handleUse)
|
||||
self.registerEvent('useon', self.handleUseOn)
|
||||
self.registerEvent('take', self.handleTake)
|
||||
self.registerEvent('drop', self.handleDrop)
|
||||
self.registerUseFunc('examine', self.examine)
|
||||
self.registerUseFunc('key', self.key)
|
||||
|
||||
# Helper functions
|
||||
|
||||
def requestInput(self, prompt = ''):
|
||||
"""Like input by default, but should be overridden if you don't want
|
||||
to get input from stdin."""
|
||||
return input(prompt)
|
||||
|
||||
def letterToNumber(self, letter):
|
||||
if letter.isdecimal():
|
||||
return int(letter)
|
||||
ret = 0
|
||||
sign = 1
|
||||
start = 0
|
||||
for i in range(len(letter)):
|
||||
if letter[i] == '-':
|
||||
sign = -sign
|
||||
start -= 1
|
||||
elif letter[i].isalpha():
|
||||
if letter[i].isupper():
|
||||
ret += (ord(letter[i]) - ord('A')) * (26**(i+start))
|
||||
else:
|
||||
ret += (ord(letter[i]) - ord('a')) * (26**(i+start))
|
||||
else:
|
||||
return ret * sign
|
||||
return ret * sign
|
||||
|
||||
def numberToLetter(self, number):
|
||||
if isinstance(number, str):
|
||||
return number
|
||||
ret = ''
|
||||
sign = ''
|
||||
if number == 0:
|
||||
return 'A'
|
||||
elif number < 0:
|
||||
sign = '-'
|
||||
number = -number
|
||||
while number > 0:
|
||||
ret += chr(ord('A') + number % 26)
|
||||
number = int(number / 26)
|
||||
return sign + ret
|
||||
|
||||
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
|
||||
match = GameBase.coordRegex.match(coordStr)
|
||||
if match != None: # if coordinates were given
|
||||
#print(match.group())
|
||||
groups = match.groups()
|
||||
ind = 0
|
||||
for i in range(len(groups)):
|
||||
if groups[i] == None or groups[i] == match.group():
|
||||
continue
|
||||
else:
|
||||
ind = i
|
||||
break
|
||||
#print(groups[ind], groups[ind+1])
|
||||
x = self.letterToNumber(groups[ind])
|
||||
y = self.letterToNumber(groups[ind+1])
|
||||
#print(x, y)
|
||||
thing = self.level.getThingAtCoords(x, y)
|
||||
if thing != None and usePlayerCoords:
|
||||
return thing, thing.playerx, thing.playery
|
||||
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:
|
||||
if name in self.playerInv:
|
||||
thing = self.playerInv[name]
|
||||
if thing != None:
|
||||
return thing, -1, -1
|
||||
else:
|
||||
return None, -1, -1
|
||||
#raise RuntimeError("'None' item named '{0}' in player inventory.".format(name))
|
||||
else:
|
||||
return None, -1, -1
|
||||
#raise ValueError("{0} cannot be reached.".format(name))
|
||||
else: # nothing
|
||||
return None, -1, -1
|
||||
#raise ValueError("{0} cannot be reached.".format(name))
|
||||
return None, x, y
|
||||
|
||||
def justifyText(self, text, width = 80):
|
||||
ret = []
|
||||
text = text.lstrip().rstrip()
|
||||
|
||||
# start by making it all one long line.
|
||||
text = _re.sub(r'\s{2,}', r' ', text)
|
||||
|
||||
# then add newlines as needed.
|
||||
#i = 80
|
||||
#i = get_terminal_size()[0]
|
||||
i = width
|
||||
while i < len(text):
|
||||
while text[i] != ' ':
|
||||
i -= 1
|
||||
ret.append(text[:i])
|
||||
text = text[i+1:]
|
||||
ret.append(text)
|
||||
|
||||
return '\n'.join(ret)
|
||||
|
||||
def go(self, args):
|
||||
"""go [-r] [to [the]] destination [additional arguments...]
|
||||
Go to a location. "walk" and "move" are aliases of go.
|
||||
-r: run to the location. "r" and "run" are aliases of -r.
|
||||
The "run" command is an alias of "go -r".
|
||||
"to" and "the" do nothing, and are intended only to make certain commands
|
||||
make more sense from a linguistic perspective (for instance, one could
|
||||
say "go to the balcony" rather than just "go balcony").
|
||||
Destination can be a coordinate pair or object name. For instance, if one
|
||||
wanted to go to D6, one could say "go to D6", "go to d 6", or "go to 3 6".
|
||||
The letter is not case-sensitive."""
|
||||
if self.level == None:
|
||||
raise RuntimeError("Cannot move: No level has been loaded.")
|
||||
speed = 0.6666667
|
||||
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
|
||||
speed = 0.3333333
|
||||
args.pop(0)
|
||||
if args[0] == 'to':
|
||||
args.pop(0)
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
thing, x, y = self.parseCoords(args, allowInventory = False)
|
||||
|
||||
# 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)))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
else:
|
||||
pos = self.level.coordsToInt(self.playerx, self.playery)
|
||||
space = path[pos]
|
||||
if space == -1:
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.ArriveEvent(self.playerName, self.playerx, self.playery, 0.0)))
|
||||
return
|
||||
#target = self.level.coordsToInt(x, y)
|
||||
t = 1
|
||||
#while space != target:
|
||||
while space != -1:
|
||||
newx, newy = self.level.intToCoords(space)
|
||||
space = path[space]
|
||||
if space != -1:
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
else:
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed)))
|
||||
break
|
||||
t += 1
|
||||
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.ArriveEvent(self.playerName, newx, newy, t * speed)))
|
||||
return
|
||||
|
||||
def look(self, args):
|
||||
"""look [at [the]] object
|
||||
Describe an object.
|
||||
"at" and "the" do nothing, and are intended only to make certain commands
|
||||
make more sense from a linguistic perspective (for instance, one could
|
||||
say "look at the balcony" rather than just "look balcony").
|
||||
Object can be the name of the object, or its coordinates."""
|
||||
if len(args) == 0:
|
||||
print(self.justifyText(self.level.description), file = self.outstream)
|
||||
else:
|
||||
if args[0] == 'at':
|
||||
args.pop(0)
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
|
||||
if thing:
|
||||
print(self.justifyText(str(thing)), file = self.outstream)
|
||||
else:
|
||||
print("There is nothing to see here.\n", file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
|
||||
def use(self, args):
|
||||
"""use [-r] [the] object [on [the] object2]
|
||||
Use an object. If the player is not already close to it, they will go to it.
|
||||
use [-r] [the] item on [the] object
|
||||
Use an item from the player's inventory on an object.
|
||||
-r: run to the location. "r" and "run" are aliases of -r.
|
||||
"the" does nothing, and is intended only to make certain commands
|
||||
make more sense from a linguistic perspective (for instance, one could
|
||||
say "use the lever" rather than just "use lever").
|
||||
Object can be the name of the object, or its coordinates. It can also be
|
||||
the name of an item in the player's inventory."""
|
||||
speed = 0.6666667
|
||||
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
|
||||
speed = 0.3333333
|
||||
args.pop(0)
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
if 'on' in args:
|
||||
self.useOn(args, speed)
|
||||
return
|
||||
thing, x, y = self.parseCoords(args)
|
||||
if thing == None:
|
||||
print("There is nothing to use.", file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
if thing.thingType != 'u' and thing.name not in self.playerInv:
|
||||
print("The {0} cannot be used.".format(thing.name), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
# Similar to go, but not quite the same.
|
||||
if (x, y) == (self.playerx, self.playery) or thing.name in self.playerInv:
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.UseEvent(thing)))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
else:
|
||||
pos = self.level.coordsToInt(self.playerx, self.playery)
|
||||
space = path[pos]
|
||||
#target = self.level.coordsToInt(x, y)
|
||||
t = 1
|
||||
#while space != target:
|
||||
while space != -1:
|
||||
newx, newy = self.level.intToCoords(space)
|
||||
space = path[space]
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
t += 1
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.UseEvent(thing)))
|
||||
return
|
||||
|
||||
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])
|
||||
if args[onIndex+1] == 'the':
|
||||
onIndex += 1
|
||||
thing, x, y = self.parseCoords(args[onIndex+1:])
|
||||
if item == None or item.name not in self.playerInv:
|
||||
print("There is no {0} in the inventory.".format(item.name), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
if thing == None and x < 0 and y < 0:
|
||||
print("Argument contains 'to' but with no real predicate.", file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
# Similar to go, but not quite the same.
|
||||
if (x, y) == (self.playerx, self.playery):
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.UseOnEvent(item, thing)))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
if thing != None:
|
||||
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream)
|
||||
else:
|
||||
print('{0} cannot reach {1}{2}.'.format(self.playerName, self.numberToLetter(x), y), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
else:
|
||||
pos = self.level.coordsToInt(self.playerx, self.playery)
|
||||
space = path[pos]
|
||||
#target = self.level.coordsToInt(x, y)
|
||||
t = 1
|
||||
#while space != target:
|
||||
while space != -1:
|
||||
newx, newy = self.level.intToCoords(space)
|
||||
space = path[space]
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
t += 1
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.UseOnEvent(item, thing)))
|
||||
return
|
||||
|
||||
def take(self, args):
|
||||
"""take [the] item
|
||||
Take an item. 'get' is an alias of take.
|
||||
If the player is not already close to it, they will go to it.
|
||||
"the" does nothing, and is intended only to make certain commands
|
||||
make more sense from a linguistic perspective (for instance, one could
|
||||
say "take the flask" rather than just "take flask").
|
||||
Object can be the name of the object, or its coordinates."""
|
||||
speed = 0.6666667
|
||||
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
|
||||
speed = 0.3333333
|
||||
args.pop(0)
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
thing, x, y = self.parseCoords(args)
|
||||
if thing == None:
|
||||
print("There is nothing to take.", file = self.outstream)
|
||||
return
|
||||
if thing.thingType != 'i':
|
||||
print("The {0} cannot be taken.".format(thing.name), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
# Similar to go, but not quite the same.
|
||||
if (x, y) == (self.playerx, self.playery):
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + 0.125, _ge.TakeEvent(thing)))
|
||||
return
|
||||
dist, path = self.level.path(x, y, self.playerx, self.playery)
|
||||
if dist == -1:
|
||||
print('{0} cannot reach the {1}.'.format(self.playerName, thing.name), file = self.outstream)
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
else:
|
||||
pos = self.level.coordsToInt(self.playerx, self.playery)
|
||||
space = path[pos]
|
||||
#target = self.level.coordsToInt(x, y)
|
||||
t = 1
|
||||
#while space != target:
|
||||
while space != -1:
|
||||
newx, newy = self.level.intToCoords(space)
|
||||
space = path[space]
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
t += 1
|
||||
#newx, newy = self.level.intToCoords(space)
|
||||
#_hq.heappush(self.eventQueue, (self.gameTime + t * speed, _ge.GoEvent(self.playerName, newx, newy)))
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + t * speed + 0.125, _ge.TakeEvent(thing)))
|
||||
return
|
||||
|
||||
def drop(self, args):
|
||||
"""drop [the] item"""
|
||||
if args[0] == 'the':
|
||||
args.pop(0)
|
||||
if args[0] in self.playerInv:
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.DropEvent(self.playerInv[args[0]])))
|
||||
else:
|
||||
print('{0} do not have a {1}.'.format(self.playerName, args[0]), file = self.outstream)
|
||||
|
||||
def loadMap(self, args):
|
||||
# loadMap (fileName)
|
||||
# loadMap (fileName, entrance)
|
||||
# loadMap (fileName, x, y)
|
||||
# save persistent things in previous level
|
||||
if self.level != None:
|
||||
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)
|
||||
|
||||
preLoaded = False
|
||||
if args[0] in self.persist:
|
||||
preLoaded = True
|
||||
|
||||
# load the new level
|
||||
if len(args) == 2:
|
||||
self.level = _gm.GameMap.read(args[0], int(args[1]), preLoaded)
|
||||
else:
|
||||
self.level = _gm.GameMap.read(args[0], preLoaded)
|
||||
|
||||
# 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)
|
||||
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.outstream.getvalue())
|
||||
if len(args) <= 2:
|
||||
self.playerx, self.playery = self.level.playerStart
|
||||
else:
|
||||
self.playerx, self.playery = int(args[1]), int(args[2])
|
||||
self.prevx, self.prevy = self.playerx, self.playery
|
||||
if self.onLevelLoad != None:
|
||||
self.onLevelLoad()
|
||||
|
||||
def saveGame(self, args):
|
||||
if len(args) < 1:
|
||||
print("Save file must have a name!", file = self.outstream)
|
||||
return
|
||||
|
||||
# choose pickle protocol depending on python version:
|
||||
# 3 for Python 3.0.0 to 3.3.x, 4 for Python 3.4.0 to 3.7.x
|
||||
prot = _pi.HIGHEST_PROTOCOL
|
||||
|
||||
# save persistent things so that the current level can be recalled as-is
|
||||
if self.level != None:
|
||||
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)
|
||||
|
||||
# build data object to be saved
|
||||
data = (self.playerName, self.playerx, self.playery, self.playerInv, self.level.name, self.persist, self.eventQueue, self.gameTime)
|
||||
|
||||
# save it!
|
||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||
if args[0].endswith('.dat'): # This is really for absolute paths, but doesn't really check for that.
|
||||
fileName = args[0]
|
||||
with open(fileName, 'wb') as f:
|
||||
_pi.dump(data, f, protocol=prot)
|
||||
|
||||
# delete things in the current map from the persist dict to prevent item duplication
|
||||
persistedThings = tuple(self.persist[self.level.name].keys())
|
||||
for i in persistedThings:
|
||||
del self.persist[self.level.name][i]
|
||||
|
||||
# push a no-op event so that saving doesn't cost player characters time
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
def loadGame(self, args):
|
||||
if len(args) < 1:
|
||||
print("Save file must have a name!", file = self.outstream)
|
||||
return
|
||||
|
||||
# choose pickle protocol depending on python version:
|
||||
# 3 for Python 3.0.0 to 3.3.x, 4 for Python 3.4.0 to 3.7.x
|
||||
prot = _pi.HIGHEST_PROTOCOL
|
||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||
if args[0].endswith('.dat'):
|
||||
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 = _pi.load(f)
|
||||
#print(levelname, x, y, file = self.outstream)
|
||||
self.loadMap((levelname, x, y))
|
||||
_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||
return
|
||||
|
||||
def gameEventLoop(self):
|
||||
while len(self.eventQueue) > 0:
|
||||
ev = _hq.heappop(self.eventQueue)
|
||||
self.gameTime = ev[0]
|
||||
e = ev[1]
|
||||
if self.__gameEvents[e.eventType](e):
|
||||
break
|
||||
if len(self.eventQueue) == 0:
|
||||
self.gameTime = 0.0
|
||||
_ge.resetEventNum()
|
||||
|
||||
# default event handlers
|
||||
|
||||
def handleNoOp(self, e):
|
||||
return True
|
||||
|
||||
def handleGo(self, e):
|
||||
if e.actor == self.playerName:
|
||||
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)
|
||||
return False
|
||||
|
||||
def handleArrive(self, e):
|
||||
if e.actor == self.playerName:
|
||||
self.prevx, self.prevy = self.playerx, self.playery
|
||||
self.playerx, self.playery = e.x, e.y
|
||||
print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(self.playerName, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
|
||||
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))
|
||||
return True
|
||||
else:
|
||||
actor = self.level.getThingByName(e.actor)
|
||||
if actor:
|
||||
self.level.moveThing(e.actor, e.x, e.y)
|
||||
print('{0} arrived at {1}{2} after {3:.1f} seconds.'.format(actor.name, self.numberToLetter(e.x), e.y, e.t), file = self.outstream)
|
||||
thing = self.level.getThingAtCoords(actor.x, actor.y)
|
||||
if thing and thing != actor:
|
||||
if thing.thingType == 'x':
|
||||
print('{0} went {1}.'.format(actor.name, str(thing)))
|
||||
self.level.removeThing(actor.name)
|
||||
else:
|
||||
print('There is nothing by the name of {0}.'.format(e.actor), file = self.outstream)
|
||||
return False
|
||||
|
||||
def handleUse(self, e):
|
||||
if e.thing.useFunc == '':
|
||||
print('The {0} cannot be used by itself.'.format(e.thing.name), file = self.outstream)
|
||||
return True
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + self.__useFuncs[e.thing.useFunc](e.thing), _ge.NoOpEvent()))
|
||||
return False
|
||||
|
||||
def handleUseOn(self, e):
|
||||
if e.item.useOnFunc == '':
|
||||
print('The {0} cannot be used on other objects.'.format(e.item.name), file = self.outstream)
|
||||
return True
|
||||
_hq.heappush(self.eventQueue, (self.gameTime + self.__useFuncs[e.item.useOnFunc](e.item, e.thing), _ge.NoOpEvent()))
|
||||
return False
|
||||
|
||||
def handleTake(self, e):
|
||||
self.level.removeThing(e.item.name)
|
||||
self.playerInv[e.item.name] = e.item
|
||||
return True
|
||||
|
||||
def handleDrop(self, e):
|
||||
e.item.x = self.playerx
|
||||
e.item.y = self.playery
|
||||
self.level.addThing(e.item, True)
|
||||
del self.playerInv[e.item.name]
|
||||
return True
|
||||
|
||||
# default useFuncs: take a list of arguments, return the time the use took
|
||||
|
||||
def examine(self, thing):
|
||||
"""Just prints the given text."""
|
||||
if 'pattern' not in thing.customValues or 'text' not in thing.customValues:
|
||||
raise ValueError("Non-examinable thing {0} examined.".format(thing.name))
|
||||
|
||||
if thing.customValues['pattern'] == 'single':
|
||||
print(self.justifyText(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)
|
||||
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)
|
||||
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)
|
||||
thing.customValues['cursor'] = cursor
|
||||
|
||||
if 'set' in thing.customValues:
|
||||
if 'cursor' in thing.customValues:
|
||||
self.customVals[thing.customValues['set']] = thing.customValues[cursor]
|
||||
else:
|
||||
self.customVals[thing.customValues['set']] = 0
|
||||
return 0.0
|
||||
|
||||
def container(self, thing):
|
||||
"""Acts as a container. Items can be traded between the container and the player's inventory."""
|
||||
items = thing.customValues['items']
|
||||
thing.customValues['items'], timeOpen = self.onContainer(items)
|
||||
return timeOpen
|
||||
|
||||
def key(self, item, thing):
|
||||
"""Item is a key, which unlocks a door. This may be implemented for containers later."""
|
||||
if isinstance(thing, tuple) or thing.thingType != 'd':
|
||||
print("That is not a door.", file = self.outstream)
|
||||
return 0.0
|
||||
if thing.lock(item.name):
|
||||
print("The key fits the lock.", file = self.outstream)
|
||||
if not thing.passable and self.playerx == thing.x and self.playery == thing.y:
|
||||
self.playerx, self.playery = self.prevx, self.prevy
|
||||
else:
|
||||
print("The key doesn't fit that lock.", file = self.outstream)
|
||||
return 0.125
|
||||
|
||||
# stuff for extended classes to use
|
||||
def registerUseFunc(self, name, func):
|
||||
"""Registers a function for use by things in the map, but not directly
|
||||
callable by the player."""
|
||||
self.__useFuncs[name] = func
|
||||
|
||||
def registerEvent(self, name, func):
|
||||
"""Registers a function to handle an event in the event loop.
|
||||
They should take the event as an argument, and return True if it should
|
||||
always give the player a turn, False otherwise."""
|
||||
self.__gameEvents[name] = func
|
Loading…
Add table
Add a link
Reference in a new issue