Added the ability to create prefabs.
This commit is contained in:
parent
871646fccc
commit
18595087ff
5 changed files with 304 additions and 59 deletions
83
gamebase.py
83
gamebase.py
|
@ -5,6 +5,7 @@ import heapq as _hq
|
||||||
import gamemap as _gm
|
import gamemap as _gm
|
||||||
import gameevents as _ge
|
import gameevents as _ge
|
||||||
import random as _ra
|
import random as _ra
|
||||||
|
import os as _os
|
||||||
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
|
||||||
|
@ -40,6 +41,7 @@ class GameBase(object):
|
||||||
self.level = None
|
self.level = None
|
||||||
self.persist = {} # {level : {thingID : thing}}
|
self.persist = {} # {level : {thingID : thing}}
|
||||||
self.singletons = {} # {thingName : thing}
|
self.singletons = {} # {thingName : thing}
|
||||||
|
self.prefabs = {} # {thingName : thing}
|
||||||
self.ps2 = '? '
|
self.ps2 = '? '
|
||||||
self.eventQueue = []
|
self.eventQueue = []
|
||||||
self.gameTime = 0.0
|
self.gameTime = 0.0
|
||||||
|
@ -411,9 +413,9 @@ Object can be the name of the object, or its coordinates."""
|
||||||
|
|
||||||
# load the new level
|
# load the new level
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
self.level, self.nextThing = _gm.GameMap.read(args[0], int(args[1]), self.singletons, preLoaded, self.nextThing)
|
self.level, self.nextThing = _gm.GameMap.read(args[0], int(args[1]), self.singletons, self.prefabs, preLoaded, self.nextThing)
|
||||||
else:
|
else:
|
||||||
self.level, self.nextThing = _gm.GameMap.read(args[0], None, self.singletons, preLoaded, self.nextThing)
|
self.level, self.nextThing = _gm.GameMap.read(args[0], None, self.singletons, self.prefabs, preLoaded, self.nextThing)
|
||||||
|
|
||||||
if self.level == None:
|
if self.level == None:
|
||||||
raise GameError("Map could not be loaded.")
|
raise GameError("Map could not be loaded.")
|
||||||
|
@ -422,7 +424,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
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.nextThing = self.level.addThing(self.persist[args[0]][i], self.nextThing, True) #nextThing shouldn't change
|
self.nextThing = self.level.addThing(self.persist[args[0]][i], self.prefabs, 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)
|
||||||
|
@ -439,7 +441,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
self.player.x, self.player.y = mx, my
|
self.player.x, self.player.y = mx, my
|
||||||
#print("Player moved.")
|
#print("Player moved.")
|
||||||
self.player.prevx, self.player.prevy = self.player.x, self.player.y
|
self.player.prevx, self.player.prevy = self.player.x, self.player.y
|
||||||
self.nextThing = self.level.addThing(self.player, self.nextThing) # The player needs to be added to the new level.
|
self.nextThing = self.level.addThing(self.player, self.prefabs, self.nextThing) # The player needs to be added to the new level.
|
||||||
if self.onLevelLoad != None:
|
if self.onLevelLoad != None:
|
||||||
self.onLevelLoad()
|
self.onLevelLoad()
|
||||||
self.parseScript(self.level.enterScript)
|
self.parseScript(self.level.enterScript)
|
||||||
|
@ -472,8 +474,16 @@ Object can be the name of the object, or its coordinates."""
|
||||||
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
fileName = 'saves/' + args[0].replace(' ', '_') + '.dat'
|
||||||
if args[0].endswith('.dat'): # This is really for absolute paths, but doesn't really check for that.
|
if args[0].endswith('.dat'): # This is really for absolute paths, but doesn't really check for that.
|
||||||
fileName = args[0]
|
fileName = args[0]
|
||||||
with open(fileName, 'wb') as f:
|
try:
|
||||||
|
f = open(fileName, 'wb')
|
||||||
_pi.dump(data, f, protocol=prot)
|
_pi.dump(data, f, protocol=prot)
|
||||||
|
except FileNotFoundException as err:
|
||||||
|
_os.mkdir('saves')
|
||||||
|
try:
|
||||||
|
f = open(fileName, 'wb')
|
||||||
|
_pi.dump(data, f, protocol=prot)
|
||||||
|
except OSError as newErr:
|
||||||
|
print("Save failed: {}".format(newErr), file = self.outstream)
|
||||||
|
|
||||||
# delete things in the current map from the persist dict to prevent item duplication
|
# delete things in the current map from the persist dict to prevent item duplication
|
||||||
persistedThings = tuple(self.persist[self.level.name].keys())
|
persistedThings = tuple(self.persist[self.level.name].keys())
|
||||||
|
@ -487,6 +497,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
def loadGame(self, args):
|
def loadGame(self, args):
|
||||||
if len(args) < 1:
|
if len(args) < 1:
|
||||||
print("Save file must have a name!", file = self.outstream)
|
print("Save file must have a name!", file = self.outstream)
|
||||||
|
print('\n'.join(_os.listdir('./saves')), file = self.outstream)
|
||||||
return
|
return
|
||||||
|
|
||||||
# pickle protocol 4 for Python 3.4.0 to 3.7.x
|
# pickle protocol 4 for Python 3.4.0 to 3.7.x
|
||||||
|
@ -589,6 +600,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
|
|
||||||
def loadGameData(self, dataFile):
|
def loadGameData(self, dataFile):
|
||||||
yaml = _yaml.YAML()
|
yaml = _yaml.YAML()
|
||||||
|
yaml.register_class(_gt.Prefab)
|
||||||
yaml.register_class(_gt.Item)
|
yaml.register_class(_gt.Item)
|
||||||
yaml.register_class(_gt.Useable)
|
yaml.register_class(_gt.Useable)
|
||||||
yaml.register_class(_gt.NPC)
|
yaml.register_class(_gt.NPC)
|
||||||
|
@ -598,6 +610,23 @@ Object can be the name of the object, or its coordinates."""
|
||||||
data = None
|
data = None
|
||||||
with open(dataFile, 'r') as f:
|
with open(dataFile, 'r') as f:
|
||||||
data = yaml.load(f)
|
data = yaml.load(f)
|
||||||
|
self.prefabs = {}
|
||||||
|
if 'prefabs' in data:
|
||||||
|
for thing in data['prefabs']:
|
||||||
|
if not isinstance(thing, _gt.Thing):
|
||||||
|
print("Non-thing in prefabs, ignoring.\n", _sys.stderr)
|
||||||
|
continue
|
||||||
|
thing.thingID = self.nextThing
|
||||||
|
self.nextThing += 1
|
||||||
|
if thing.thingType in 'iun':
|
||||||
|
self.nextThing = _gm.GameMap.addThingRecursive(thing.customValues, self.prefabs, self.nextThing)
|
||||||
|
if thing.thingType == 'n':
|
||||||
|
for i in thing.tempInventory:
|
||||||
|
if i.thingID == -1:
|
||||||
|
i.thingID = self.nextThing
|
||||||
|
self.nextThing = _gm.GameMap.addThingRecursive(i.customValues, self.prefabs, self.nextThing + 1)
|
||||||
|
# for prefabs, we don't add their items to their "real" inventory and don't delete the temporary one.
|
||||||
|
self.prefabs[thing.name] = thing
|
||||||
# In this context, 'singleton' means a 'thing' that can be accessed by
|
# In this context, 'singleton' means a 'thing' that can be accessed by
|
||||||
# any map. This is useful for when you have major characters who will
|
# any map. This is useful for when you have major characters who will
|
||||||
# show up in many scenes, and their inventory must stay the same, for
|
# show up in many scenes, and their inventory must stay the same, for
|
||||||
|
@ -613,12 +642,12 @@ Object can be the name of the object, or its coordinates."""
|
||||||
thing.thingID = self.nextThing
|
thing.thingID = self.nextThing
|
||||||
self.nextThing += 1
|
self.nextThing += 1
|
||||||
if thing.thingType in 'iun':
|
if thing.thingType in 'iun':
|
||||||
self.nextThing = _gm.GameMap.addThingRecursive(thing.customValues, self.nextThing)
|
self.nextThing = _gm.GameMap.addThingRecursive(thing.customValues, self.prefabs, self.nextThing)
|
||||||
if thing.thingType == 'n':
|
if thing.thingType == 'n':
|
||||||
for i in thing.tempInventory:
|
for i in thing.tempInventory:
|
||||||
if i.thingID == -1:
|
if i.thingID == -1:
|
||||||
i.thingID = self.nextThing
|
i.thingID = self.nextThing
|
||||||
self.nextThing = _gm.GameMap.addThingRecursive(i.customValues, self.nextThing + 1)
|
self.nextThing = _gm.GameMap.addThingRecursive(i.customValues, self.prefabs, self.nextThing + 1)
|
||||||
thing.addThing(i)
|
thing.addThing(i)
|
||||||
del thing.tempInventory
|
del thing.tempInventory
|
||||||
self.singletons[thing.name] = thing
|
self.singletons[thing.name] = thing
|
||||||
|
@ -749,7 +778,7 @@ Object can be the name of the object, or its coordinates."""
|
||||||
raise GameError("'Drop' event cannot be handled.")
|
raise GameError("'Drop' event cannot be handled.")
|
||||||
e.item.x = e.actor.x
|
e.item.x = e.actor.x
|
||||||
e.item.y = e.actor.y
|
e.item.y = e.actor.y
|
||||||
self.nextThing = self.level.addThing(e.item, self.nextThing, True) # nextThing shouldn't change
|
self.nextThing = self.level.addThing(e.item, self.prefabs, self.nextThing, True) # nextThing shouldn't change
|
||||||
e.actor.removeThing(e.item)
|
e.actor.removeThing(e.item)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -917,11 +946,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'item {}'.format(self.nextThing)
|
name = 'item {}'.format(self.nextThing)
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gt.Item.defaultGraphic[0]
|
bgc = _gt.Item.defaultGraphic.background
|
||||||
if fgc == None:
|
if fgc == None:
|
||||||
fgc = _gt.Item.defaultGraphic[1]
|
fgc = _gt.Item.defaultGraphic.foreground
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gt.Item.defaultGraphic[2]
|
shape = _gt.Item.defaultGraphic.shape
|
||||||
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:]
|
||||||
|
@ -950,11 +979,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'useable {}'.format(self.nextThing)
|
name = 'useable {}'.format(self.nextThing)
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gt.Useable.defaultGraphic[0]
|
bgc = _gt.Useable.defaultGraphic.background
|
||||||
if fgc == None:
|
if fgc == None:
|
||||||
fgc = _gt.Useable.defaultGraphic[1]
|
fgc = _gt.Useable.defaultGraphic.foreground
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gt.Useable.defaultGraphic[2]
|
shape = _gt.Useable.defaultGraphic.shape
|
||||||
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:]
|
||||||
|
@ -977,11 +1006,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'character {}'.format(self.nextThing)
|
name = 'character {}'.format(self.nextThing)
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gt.NPC.defaultGraphic[0]
|
bgc = _gt.NPC.defaultGraphic.background
|
||||||
if fgc == None:
|
if fgc == None:
|
||||||
fgc = _gt.NPC.defaultGraphic[1]
|
fgc = _gt.NPC.defaultGraphic.foreground
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gt.NPC.defaultGraphic[2]
|
shape = _gt.NPC.defaultGraphic.shape
|
||||||
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:]
|
||||||
|
@ -1002,11 +1031,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'door {}'.format(self.nextThing)
|
name = 'door {}'.format(self.nextThing)
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gt.Door.defaultGraphic[0]
|
bgc = _gt.Door.defaultGraphic.background
|
||||||
if fgc == None:
|
if fgc == None:
|
||||||
fgc = _gt.Door.defaultGraphic[1]
|
fgc = _gt.Door.defaultGraphic.foreground
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gt.Door.defaultGraphic[2]
|
shape = _gt.Door.defaultGraphic.shape
|
||||||
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:]
|
||||||
|
@ -1023,11 +1052,11 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if name == None:
|
if name == None:
|
||||||
name = 'somewhere'
|
name = 'somewhere'
|
||||||
if bgc == None:
|
if bgc == None:
|
||||||
bgc = _gt.MapExit.defaultGraphic[0]
|
bgc = _gt.MapExit.defaultGraphic.background
|
||||||
if fgc == None:
|
if fgc == None:
|
||||||
fgc = _gt.MapExit.defaultGraphic[1]
|
fgc = _gt.MapExit.defaultGraphic.foreground
|
||||||
if shape == None:
|
if shape == None:
|
||||||
shape = _gt.MapExit.defaultGraphic[2]
|
shape = _gt.MapExit.defaultGraphic.shape
|
||||||
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:]
|
||||||
|
@ -1054,21 +1083,21 @@ Object can be the name of the object, or its coordinates."""
|
||||||
single.prevx, single.prevy = x, y
|
single.prevx, single.prevy = x, y
|
||||||
else:
|
else:
|
||||||
raise GameError("{} not a valid thing type.".format(args[0]))
|
raise GameError("{} not a valid thing type.".format(args[0]))
|
||||||
self.nextThing = self.level.addThing(thing, self.nextThing, persist)
|
self.nextThing = self.level.addThing(thing, self.prefabs, self.nextThing, persist)
|
||||||
return thing
|
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."""
|
||||||
name = 'item {}'.format(self.nextThing)
|
name = 'item {}'.format(self.nextThing)
|
||||||
x, y = self.playerx, self.playery
|
x, y = self.player.x, self.player.y
|
||||||
fgc, bgc, shape = _gt.Item.defaultGraphic['fgc'], _gt.Item.defaultGraphic['bgc'], _gt.Item.defaultGraphic['shape']
|
fgc, bgc, shape = _gt.Item.defaultGraphic.foreground, _gt.Item.defaultGraphic.background, _gt.Item.defaultGraphic.shape
|
||||||
description = 'A nondescript item.'
|
description = 'A nondescript item.'
|
||||||
persist = True
|
persist = True
|
||||||
useFunc = ''
|
useFunc = ''
|
||||||
useOnFunc = ''
|
useOnFunc = ''
|
||||||
customValues = {}
|
customValues = {}
|
||||||
ranged = False
|
ranged = False
|
||||||
for i in args[1:]:
|
for i in args[0:]:
|
||||||
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=':
|
||||||
|
|
36
gamemap.py
36
gamemap.py
|
@ -50,6 +50,7 @@ class GameMap(object):
|
||||||
tileRegex = re.compile(r'([a-z ])([0-9]+|[ ])')
|
tileRegex = re.compile(r'([a-z ])([0-9]+|[ ])')
|
||||||
matrixRegex = re.compile(r'(?:[ \t]*(?:[a-z ](?:[0-9]+|[ ]))+(\n))+')
|
matrixRegex = re.compile(r'(?:[ \t]*(?:[a-z ](?:[0-9]+|[ ]))+(\n))+')
|
||||||
yaml = ruamel.yaml.YAML()
|
yaml = ruamel.yaml.YAML()
|
||||||
|
yaml.register_class(_gt.Prefab)
|
||||||
yaml.register_class(_gt.Item)
|
yaml.register_class(_gt.Item)
|
||||||
yaml.register_class(_gt.Useable)
|
yaml.register_class(_gt.Useable)
|
||||||
yaml.register_class(_gt.NPC)
|
yaml.register_class(_gt.NPC)
|
||||||
|
@ -96,7 +97,16 @@ class GameMap(object):
|
||||||
return text.replace('\n', end)
|
return text.replace('\n', end)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def read(infile = None, prevMap = None, singletons = None, preLoaded = False, nextThing = 0):
|
def resolvePrefab(prefabs: dict, thing: _gt.Thing):
|
||||||
|
"""Resolve what type of prefab a thing is. If it isn't a prefab or there are no prefabs loaded, just return the thing."""
|
||||||
|
if prefabs == None or len(prefabs) == 0 or not isinstance(thing, _gt.Prefab):
|
||||||
|
return thing
|
||||||
|
if thing.name not in prefabs:
|
||||||
|
raise MapError(f"Attempted to load an unrecognized prefab named {thing.name}.")
|
||||||
|
return prefabs[thing.name].fromPrefab(thing)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def read(infile = None, prevMap = None, singletons = None, prefabs = None, preLoaded = False, nextThing = 0):
|
||||||
"""Read map data and return a Map object. If infile is not provided, then
|
"""Read map data and return a Map object. If infile is not provided, then
|
||||||
it will read from stdin. Otherwise, it should be a valid file name.
|
it will read from stdin. Otherwise, it should be a valid file name.
|
||||||
Entering a map through stdin will be obsolete once testing is over."""
|
Entering a map through stdin will be obsolete once testing is over."""
|
||||||
|
@ -139,7 +149,7 @@ Entering a map through stdin will be obsolete once testing is over."""
|
||||||
level = GameMap(infile, mapGraph, mapMatrix, dimensions)
|
level = GameMap(infile, mapGraph, mapMatrix, dimensions)
|
||||||
|
|
||||||
# Now, load other info
|
# Now, load other info
|
||||||
nextThing = GameMap.loadThings(level, info, prevMap, singletons, preLoaded, nextThing)
|
nextThing = GameMap.loadThings(level, info, prevMap, singletons, prefabs, preLoaded, nextThing)
|
||||||
|
|
||||||
return level, nextThing
|
return level, nextThing
|
||||||
|
|
||||||
|
@ -204,7 +214,7 @@ list of lists of tuples."""
|
||||||
return mat, graph, dim
|
return mat, graph, dim
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def loadThings(level, info, prevMap = None, singletons = None, preLoaded = False, nextThing = 0):
|
def loadThings(level, info, prevMap = None, singletons = None, prefabs = None, preLoaded = False, nextThing = 0):
|
||||||
"""load the things from the xml part of the map file."""
|
"""load the things from the xml part of the map file."""
|
||||||
if 'openingText' in info:
|
if 'openingText' in info:
|
||||||
level.openingText = info['openingText']
|
level.openingText = info['openingText']
|
||||||
|
@ -231,14 +241,14 @@ list of lists of tuples."""
|
||||||
for thing in info['loadOnce']:
|
for thing in info['loadOnce']:
|
||||||
#print(type(thing))
|
#print(type(thing))
|
||||||
if isinstance(thing, _gt.Thing):
|
if isinstance(thing, _gt.Thing):
|
||||||
nextThing = level.addThing(thing, nextThing, True)
|
nextThing = level.addThing(GameMap.resolvePrefab(prefabs, thing), prefabs, nextThing, True)
|
||||||
else:
|
else:
|
||||||
raise MapError("Non-thing loaded as a thing:\n{}".format(thing))
|
raise MapError("Non-thing loaded as a thing:\n{}".format(thing))
|
||||||
if 'loadAlways' in info:
|
if 'loadAlways' in info:
|
||||||
for thing in info['loadAlways']:
|
for thing in info['loadAlways']:
|
||||||
#print(type(thing))
|
#print(type(thing))
|
||||||
if isinstance(thing, _gt.Thing):
|
if isinstance(thing, _gt.Thing):
|
||||||
nextThing = level.addThing(thing, nextThing)
|
nextThing = level.addThing(GameMap.resolvePrefab(prefabs, thing), prefabs, nextThing)
|
||||||
if ((thing.thingType == 'x' and not hasKnownEntrance) or thing.thingType == 'a') and prevMap == thing.exitid:
|
if ((thing.thingType == 'x' and not hasKnownEntrance) or thing.thingType == 'a') and prevMap == thing.exitid:
|
||||||
level.playerStart = (thing.x, thing.y)
|
level.playerStart = (thing.x, thing.y)
|
||||||
hasKnownEntrance = True
|
hasKnownEntrance = True
|
||||||
|
@ -247,14 +257,14 @@ list of lists of tuples."""
|
||||||
single = singletons[thing.name]
|
single = singletons[thing.name]
|
||||||
single.x, single.y = thing.x, thing.y
|
single.x, single.y = thing.x, thing.y
|
||||||
single.prevx, single.prevy = thing.x, thing.y
|
single.prevx, single.prevy = thing.x, thing.y
|
||||||
nextThing = level.addThing(single, nextThing)
|
nextThing = level.addThing(single, prefabs, nextThing)
|
||||||
else:
|
else:
|
||||||
raise MapError("Non-thing loaded as a thing:\n{}".format(thing))
|
raise MapError("Non-thing loaded as a thing:\n{}".format(thing))
|
||||||
return nextThing
|
return nextThing
|
||||||
|
|
||||||
# stuff the gameshell itself might use
|
# stuff the gameshell itself might use
|
||||||
|
|
||||||
def addThing(self, thing, nextThing = 0, persist = False):
|
def addThing(self, thing, prefabs = None, nextThing = 0, persist = False):
|
||||||
if thing == None: # it must be a singleton
|
if thing == None: # it must be a singleton
|
||||||
return nextThing
|
return nextThing
|
||||||
#if thing.name in self.thingNames: # resolved
|
#if thing.name in self.thingNames: # resolved
|
||||||
|
@ -266,12 +276,12 @@ list of lists of tuples."""
|
||||||
# so they need IDs as well.
|
# so they need IDs as well.
|
||||||
# Let's only add them if they weren't already loaded.
|
# Let's only add them if they weren't already loaded.
|
||||||
if thing.thingType in 'iun':
|
if thing.thingType in 'iun':
|
||||||
nextThing = GameMap.addThingRecursive(thing.customValues, nextThing)
|
nextThing = GameMap.addThingRecursive(thing.customValues, prefabs, nextThing)
|
||||||
if thing.thingType == 'n':
|
if thing.thingType == 'n':
|
||||||
for i in thing.tempInventory:
|
for i in thing.tempInventory:
|
||||||
if i.thingID == -1:
|
if i.thingID == -1:
|
||||||
i.thingID = nextThing
|
i.thingID = nextThing
|
||||||
nextThing = GameMap.addThingRecursive(i.customValues, nextThing + 1)
|
nextThing = GameMap.addThingRecursive(i.customValues, prefabs, nextThing + 1)
|
||||||
thing.addThing(i)
|
thing.addThing(i)
|
||||||
del thing.tempInventory
|
del thing.tempInventory
|
||||||
pos = self.coordsToInt(thing.x, thing.y)
|
pos = self.coordsToInt(thing.x, thing.y)
|
||||||
|
@ -289,21 +299,21 @@ list of lists of tuples."""
|
||||||
return nextThing
|
return nextThing
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def addThingRecursive(container, nextThing = 0):
|
def addThingRecursive(container, prefabs = None, nextThing = 0):
|
||||||
if isinstance(container, _gt.Thing):
|
if isinstance(container, _gt.Thing):
|
||||||
if container.thingID == -1:
|
if container.thingID == -1:
|
||||||
container.thingID = nextThing
|
container.thingID = nextThing
|
||||||
nextThing = GameMap.addThingRecursive(container.customValues, nextThing)
|
nextThing = GameMap.addThingRecursive(container.customValues, prefabs, nextThing)
|
||||||
return nextThing + 1
|
return nextThing + 1
|
||||||
else:
|
else:
|
||||||
return nextThing
|
return nextThing
|
||||||
elif isinstance(container, dict):
|
elif isinstance(container, dict):
|
||||||
for i in container:
|
for i in container:
|
||||||
nextThing = GameMap.addThingRecursive(container[i], nextThing)
|
nextThing = GameMap.addThingRecursive(GameMap.resolvePrefab(prefabs, container[i]), prefabs, nextThing)
|
||||||
return nextThing
|
return nextThing
|
||||||
elif isinstance(container, list):
|
elif isinstance(container, list):
|
||||||
for i in container:
|
for i in container:
|
||||||
nextThing = GameMap.addThingRecursive(i, nextThing)
|
nextThing = GameMap.addThingRecursive(GameMap.resolvePrefab(prefabs, i), prefabs, nextThing)
|
||||||
return nextThing
|
return nextThing
|
||||||
else:
|
else:
|
||||||
return nextThing
|
return nextThing
|
||||||
|
|
|
@ -422,7 +422,8 @@ If -l is given, a map legend will be printed under the map."""
|
||||||
|
|
||||||
def runScript(self, args):
|
def runScript(self, args):
|
||||||
"""simple wrapper for gameBase.runscript."""
|
"""simple wrapper for gameBase.runscript."""
|
||||||
self.gameBase.parseScript(' '.join(args))
|
args = ['"' + a + '"' for a in args]
|
||||||
|
self.gameBase.parseScript(' '.join(args) + '\n')
|
||||||
|
|
||||||
# IO calls
|
# IO calls
|
||||||
|
|
||||||
|
|
205
gamethings.py
205
gamethings.py
|
@ -62,11 +62,7 @@ class Thing(object):
|
||||||
self.playerx = playerx
|
self.playerx = playerx
|
||||||
if playery:
|
if playery:
|
||||||
self.playery = playery
|
self.playery = playery
|
||||||
self.passable = bool(flags & 1)
|
self.flags = flags
|
||||||
self.talkable = bool(flags & 2)
|
|
||||||
self.lookable = bool(flags & 4)
|
|
||||||
self.takeable = bool(flags & 8)
|
|
||||||
self.useable = bool(flags & 16)
|
|
||||||
self.graphic = ThingGraphic('clear', '#7F7F7F', ' ')
|
self.graphic = ThingGraphic('clear', '#7F7F7F', ' ')
|
||||||
self.thingID = -1 # ID not assigned
|
self.thingID = -1 # ID not assigned
|
||||||
|
|
||||||
|
@ -79,6 +75,82 @@ class Thing(object):
|
||||||
return False
|
return False
|
||||||
return self.name == other.name
|
return self.name == other.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def passable(self):
|
||||||
|
return bool(self.flags & 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def talkable(self):
|
||||||
|
return bool(self.flags & 2)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lookable(self):
|
||||||
|
return bool(self.flags & 4)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def takeable(self):
|
||||||
|
return bool(self.flags & 8)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def useable(self):
|
||||||
|
return bool(self.flags & 16)
|
||||||
|
|
||||||
|
def fromPrefab(self, prefab):
|
||||||
|
"""This only exists as a catch-all in case an unsupported thing type is prefabed."""
|
||||||
|
parts = prefab.details
|
||||||
|
# set default values for optional arguments
|
||||||
|
name = self.name
|
||||||
|
description = self.description
|
||||||
|
playerx = prefab.x + (self.playerx - self.x)
|
||||||
|
playery = prefab.y + (self.playery - self.y)
|
||||||
|
bgc = self.graphic.background
|
||||||
|
fgc = self.graphic.foreground
|
||||||
|
shape = self.graphic.shape
|
||||||
|
if parts != None:
|
||||||
|
if 'name' in parts:
|
||||||
|
name = parts['name']
|
||||||
|
if 'description' in parts:
|
||||||
|
description = parts['description']
|
||||||
|
# load graphic
|
||||||
|
if 'graphic' in parts:
|
||||||
|
if 'bgc' in parts['graphic']:
|
||||||
|
bgc = parts['graphic']['bgc']
|
||||||
|
if 'fgc' in parts['graphic']:
|
||||||
|
fgc = parts['graphic']['fgc']
|
||||||
|
if 'shape' in parts['graphic']:
|
||||||
|
shape = parts['graphic']['shape']
|
||||||
|
if 'useLocation' in parts:
|
||||||
|
playerx, playery = parts['useLocation']
|
||||||
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
|
return Thing(self.thingType, name, prefab.x, prefab.y, description, self.flags, playerx, playery)
|
||||||
|
|
||||||
|
class Prefab(Thing):
|
||||||
|
"""Basically a placeholder for a predefined prefab. This is for reading in YAML."""
|
||||||
|
yaml_flag = u'!Prefab'
|
||||||
|
defaultGraphic = ThingGraphic('clear', 'clear', 'x')
|
||||||
|
|
||||||
|
def __init__(self, name: str, x: int, y: int, **kwargs):
|
||||||
|
super(Prefab, self).__init__('z', name, x, y, '', 1) # 'p' is already taken by the player.
|
||||||
|
self.details = None # This is so that custom overrides can be made for specific instances.
|
||||||
|
if "details" in kwargs:
|
||||||
|
self.details = kwargs["details"]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_yaml(cls, representer, node):
|
||||||
|
# save usual things
|
||||||
|
ret = {'name': node.name, 'location': (node.x, node.y), 'details': self.details}
|
||||||
|
return representer.represent_mapping(cls.yaml_flag, ret)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_yaml(cls, constructor, node):
|
||||||
|
parts = CommentedMap()
|
||||||
|
constructor.construct_mapping(node, parts, True)
|
||||||
|
# set default values for optional arguments
|
||||||
|
mdetails = None
|
||||||
|
if 'details' in parts:
|
||||||
|
mdetails = dict(parts['details'])
|
||||||
|
return cls(parts['name'], parts['location'][0], parts['location'][1], details = mdetails)
|
||||||
|
|
||||||
class Observer(Thing):
|
class Observer(Thing):
|
||||||
"""ABC for things that have a dict of events that they should listen to."""
|
"""ABC for things that have a dict of events that they should listen to."""
|
||||||
|
|
||||||
|
@ -162,7 +234,7 @@ class Item(Thing):
|
||||||
if isinstance(customValues[v], tuple):
|
if isinstance(customValues[v], tuple):
|
||||||
customValues[v] = list(customValues[v])
|
customValues[v] = list(customValues[v])
|
||||||
if 'ranged' in parts:
|
if 'ranged' in parts:
|
||||||
useOnFunc = parts['ranged']
|
ranged = parts['ranged']
|
||||||
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
||||||
parts['description'], useFunc, useOnFunc, customValues, ranged, graphic)
|
parts['description'], useFunc, useOnFunc, customValues, ranged, graphic)
|
||||||
|
|
||||||
|
@ -172,6 +244,46 @@ class Item(Thing):
|
||||||
def useOn(self, user, gameBase, thing, args) -> float:
|
def useOn(self, user, gameBase, thing, args) -> float:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def fromPrefab(self, prefab):
|
||||||
|
parts = prefab.details
|
||||||
|
# set default values for optional arguments
|
||||||
|
name = self.name
|
||||||
|
useFunc = self.useFunc
|
||||||
|
useOnFunc = self.useOnFunc
|
||||||
|
customValues = dict(self.customValues)
|
||||||
|
ranged = self.ranged
|
||||||
|
bgc = self.graphic.background
|
||||||
|
fgc = self.graphic.foreground
|
||||||
|
shape = self.graphic.shape
|
||||||
|
description = self.description
|
||||||
|
if parts:
|
||||||
|
if 'name' in parts:
|
||||||
|
name = parts['name']
|
||||||
|
if 'description' in parts:
|
||||||
|
description = parts['description']
|
||||||
|
# load graphic
|
||||||
|
if 'graphic' in parts:
|
||||||
|
if 'bgc' in parts['graphic']:
|
||||||
|
bgc = parts['graphic']['bgc']
|
||||||
|
if 'fgc' in parts['graphic']:
|
||||||
|
fgc = parts['graphic']['fgc']
|
||||||
|
if 'shape' in parts['graphic']:
|
||||||
|
shape = parts['graphic']['shape']
|
||||||
|
# load use functions
|
||||||
|
if 'useFunc' in parts:
|
||||||
|
useFunc = parts['useFunc']
|
||||||
|
if 'useOnFunc' in parts:
|
||||||
|
useOnFunc = parts['useOnFunc']
|
||||||
|
if 'customValues' in parts:
|
||||||
|
customValues = dict(parts['customValues'])
|
||||||
|
for v in customValues:
|
||||||
|
if isinstance(customValues[v], tuple):
|
||||||
|
customValues[v] = list(customValues[v])
|
||||||
|
if 'ranged' in parts:
|
||||||
|
ranged = parts['ranged']
|
||||||
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
|
return Item(name, prefab.x, prefab.y, description, useFunc, useOnFunc, customValues, ranged, graphic)
|
||||||
|
|
||||||
class Useable(Thing):
|
class Useable(Thing):
|
||||||
yaml_flag = u'!Useable'
|
yaml_flag = u'!Useable'
|
||||||
defaultGraphic = ThingGraphic('clear', '#0000FF', '#')
|
defaultGraphic = ThingGraphic('clear', '#0000FF', '#')
|
||||||
|
@ -241,6 +353,44 @@ class Useable(Thing):
|
||||||
def use(self, user, gameBase, args) -> float:
|
def use(self, user, gameBase, args) -> float:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def fromPrefab(self, prefab):
|
||||||
|
parts = prefab.details
|
||||||
|
# set default values for optional arguments
|
||||||
|
name = self.name
|
||||||
|
description = self.description
|
||||||
|
useFunc = self.useFunc
|
||||||
|
customValues = dict(self.customValues)
|
||||||
|
playerx = prefab.x + (self.playerx - self.x)
|
||||||
|
playery = prefab.y + (self.playery - self.y)
|
||||||
|
bgc = self.graphic.background
|
||||||
|
fgc = self.graphic.foreground
|
||||||
|
shape = self.graphic.shape
|
||||||
|
if parts != None:
|
||||||
|
if 'name' in parts:
|
||||||
|
name = parts['name']
|
||||||
|
if 'description' in parts:
|
||||||
|
description = parts['description']
|
||||||
|
# load graphic
|
||||||
|
if 'graphic' in parts:
|
||||||
|
if 'bgc' in parts['graphic']:
|
||||||
|
bgc = parts['graphic']['bgc']
|
||||||
|
if 'fgc' in parts['graphic']:
|
||||||
|
fgc = parts['graphic']['fgc']
|
||||||
|
if 'shape' in parts['graphic']:
|
||||||
|
shape = parts['graphic']['shape']
|
||||||
|
# load use functions
|
||||||
|
if 'useFunc' in parts:
|
||||||
|
useFunc = parts['useFunc']
|
||||||
|
if 'customValues' in parts:
|
||||||
|
customValues = dict(parts['customValues'])
|
||||||
|
for v in customValues:
|
||||||
|
if isinstance(customValues[v], tuple):
|
||||||
|
customValues[v] = list(customValues[v])
|
||||||
|
if 'useLocation' in parts:
|
||||||
|
playerx, playery = parts['useLocation']
|
||||||
|
graphic = ThingGraphic(bgc, fgc, shape)
|
||||||
|
return Useable(name, prefab.x, prefab.y, description, useFunc, customValues, playerx, playery, graphic)
|
||||||
|
|
||||||
class Character(Thing):
|
class Character(Thing):
|
||||||
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
||||||
|
|
||||||
|
@ -352,7 +502,6 @@ class NPC(Character, Observer):
|
||||||
parts = CommentedMap()
|
parts = CommentedMap()
|
||||||
constructor.construct_mapping(node, parts, True)
|
constructor.construct_mapping(node, parts, True)
|
||||||
# set default values for optional arguments
|
# set default values for optional arguments
|
||||||
following = False
|
|
||||||
minventory = []
|
minventory = []
|
||||||
mcustomValues = {}
|
mcustomValues = {}
|
||||||
mplayerx, mplayery = parts['location']
|
mplayerx, mplayery = parts['location']
|
||||||
|
@ -382,6 +531,48 @@ class NPC(Character, Observer):
|
||||||
description = parts['description'], behaviors = parts['behaviors'], tempInventory = minventory, customValues = mcustomValues,
|
description = parts['description'], behaviors = parts['behaviors'], tempInventory = minventory, customValues = mcustomValues,
|
||||||
playerx = mplayerx, plyery = mplayery, graphic = mgraphic)
|
playerx = mplayerx, plyery = mplayery, graphic = mgraphic)
|
||||||
|
|
||||||
|
def fromPrefab(self, prefab):
|
||||||
|
parts = prefab.details
|
||||||
|
# set default values for optional arguments
|
||||||
|
mname = self.name
|
||||||
|
mdescription = self.description
|
||||||
|
minventory = list(self.tempInventory)
|
||||||
|
mcustomValues = dict(self.customValues)
|
||||||
|
mbehaviors = dict(self.behaviors)
|
||||||
|
mplayerx = prefab.x + (self.playerx - self.x)
|
||||||
|
mplayery = prefab.y + (self.playery - self.y)
|
||||||
|
bgc = self.graphic.background
|
||||||
|
fgc = self.graphic.foreground
|
||||||
|
shape = self.graphic.shape
|
||||||
|
if prefab != None:
|
||||||
|
if 'name' in parts:
|
||||||
|
mname = parts['name']
|
||||||
|
if 'description' in parts:
|
||||||
|
mdescription = parts['description']
|
||||||
|
# load graphic
|
||||||
|
if 'graphic' in parts:
|
||||||
|
if 'bgc' in parts['graphic']:
|
||||||
|
bgc = parts['graphic']['bgc']
|
||||||
|
if 'fgc' in parts['graphic']:
|
||||||
|
fgc = parts['graphic']['fgc']
|
||||||
|
if 'shape' in parts['graphic']:
|
||||||
|
shape = parts['graphic']['shape']
|
||||||
|
# load use functions
|
||||||
|
if 'inventory' in parts:
|
||||||
|
inventory = parts['inventory']
|
||||||
|
if 'customValues' in parts:
|
||||||
|
mcustomValues = dict(parts['customValues'])
|
||||||
|
for v in mcustomValues:
|
||||||
|
if isinstance(mcustomValues[v], tuple):
|
||||||
|
mcustomValues[v] = list(mcustomValues[v])
|
||||||
|
if 'useLocation' in parts:
|
||||||
|
playerx, playery = parts['useLocation']
|
||||||
|
if 'behaviors' in parts:
|
||||||
|
mbehaviors = dict(parts['behaviors'])
|
||||||
|
mgraphic = ThingGraphic(bgc, fgc, shape)
|
||||||
|
return NPC(name = mname, x = prefab.x, y = prefab.y, description = mdescription, behaviors = mbehaviors, tempInventory = minventory, customValues = mcustomValues,
|
||||||
|
playerx = mplayerx, plyery = mplayery, graphic = mgraphic)
|
||||||
|
|
||||||
class Door(Thing):
|
class Door(Thing):
|
||||||
yaml_flag = u'!Door'
|
yaml_flag = u'!Door'
|
||||||
defaultGraphic = ThingGraphic('clear', '#7F3F00', '#')
|
defaultGraphic = ThingGraphic('clear', '#7F3F00', '#')
|
||||||
|
|
|
@ -64,3 +64,17 @@ loadOnce:
|
||||||
distance: 2
|
distance: 2
|
||||||
isFollowing: True
|
isFollowing: True
|
||||||
target: You
|
target: You
|
||||||
|
- !Prefab
|
||||||
|
name: ball
|
||||||
|
location: [4, 12]
|
||||||
|
- !Prefab
|
||||||
|
name: ball
|
||||||
|
location: [7, 12]
|
||||||
|
- !Prefab
|
||||||
|
name: box
|
||||||
|
location: [4, 10]
|
||||||
|
- !Prefab
|
||||||
|
name: box
|
||||||
|
location: [7, 10]
|
||||||
|
details:
|
||||||
|
useLocation: [6, 10]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue