From 18595087ff4d38417cd10dcacca429b929342293 Mon Sep 17 00:00:00 2001 From: Patrick Marsee Date: Sun, 14 Nov 2021 22:25:30 -0500 Subject: [PATCH] Added the ability to create prefabs. --- gamebase.py | 93 +++++++++++++-------- gamemap.py | 48 ++++++----- gameshell.py | 3 +- gamethings.py | 205 ++++++++++++++++++++++++++++++++++++++++++++-- testing/test1.yml | 14 ++++ 5 files changed, 304 insertions(+), 59 deletions(-) diff --git a/gamebase.py b/gamebase.py index 374798f..2a88e9c 100644 --- a/gamebase.py +++ b/gamebase.py @@ -5,6 +5,7 @@ import heapq as _hq import gamemap as _gm import gameevents as _ge import random as _ra +import os as _os import sys as _sys import pickle as _pi import ruamel.yaml as _yaml @@ -40,6 +41,7 @@ class GameBase(object): self.level = None self.persist = {} # {level : {thingID : thing}} self.singletons = {} # {thingName : thing} + self.prefabs = {} # {thingName : thing} self.ps2 = '? ' self.eventQueue = [] self.gameTime = 0.0 @@ -411,9 +413,9 @@ Object can be the name of the object, or its coordinates.""" # load the new level 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: - 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: 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: persistedThings = tuple(self.persist[args[0]].keys()) 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 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 #print("Player moved.") 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: self.onLevelLoad() 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' 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: + try: + f = open(fileName, 'wb') _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 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): if len(args) < 1: print("Save file must have a name!", file = self.outstream) + print('\n'.join(_os.listdir('./saves')), file = self.outstream) return # 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): yaml = _yaml.YAML() + yaml.register_class(_gt.Prefab) yaml.register_class(_gt.Item) yaml.register_class(_gt.Useable) yaml.register_class(_gt.NPC) @@ -598,6 +610,23 @@ Object can be the name of the object, or its coordinates.""" data = None with open(dataFile, 'r') as 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 # 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 @@ -613,12 +642,12 @@ Object can be the name of the object, or its coordinates.""" thing.thingID = self.nextThing self.nextThing += 1 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': for i in thing.tempInventory: if i.thingID == -1: 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) del thing.tempInventory 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.") e.item.x = e.actor.x 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) return True @@ -917,11 +946,11 @@ Object can be the name of the object, or its coordinates.""" if name == None: name = 'item {}'.format(self.nextThing) if bgc == None: - bgc = _gt.Item.defaultGraphic[0] + bgc = _gt.Item.defaultGraphic.background if fgc == None: - fgc = _gt.Item.defaultGraphic[1] + fgc = _gt.Item.defaultGraphic.foreground if shape == None: - shape = _gt.Item.defaultGraphic[2] + shape = _gt.Item.defaultGraphic.shape for i in args[1:]: if i[0:12].casefold() == 'description=': description = i[12:] @@ -950,11 +979,11 @@ Object can be the name of the object, or its coordinates.""" if name == None: name = 'useable {}'.format(self.nextThing) if bgc == None: - bgc = _gt.Useable.defaultGraphic[0] + bgc = _gt.Useable.defaultGraphic.background if fgc == None: - fgc = _gt.Useable.defaultGraphic[1] + fgc = _gt.Useable.defaultGraphic.foreground if shape == None: - shape = _gt.Useable.defaultGraphic[2] + shape = _gt.Useable.defaultGraphic.shape for i in args[1:]: if i[0:12].casefold() == 'description=': description = i[12:] @@ -977,11 +1006,11 @@ Object can be the name of the object, or its coordinates.""" if name == None: name = 'character {}'.format(self.nextThing) if bgc == None: - bgc = _gt.NPC.defaultGraphic[0] + bgc = _gt.NPC.defaultGraphic.background if fgc == None: - fgc = _gt.NPC.defaultGraphic[1] + fgc = _gt.NPC.defaultGraphic.foreground if shape == None: - shape = _gt.NPC.defaultGraphic[2] + shape = _gt.NPC.defaultGraphic.shape for i in args[1:]: if i[0:12].casefold() == 'description=': description = i[12:] @@ -1002,11 +1031,11 @@ Object can be the name of the object, or its coordinates.""" if name == None: name = 'door {}'.format(self.nextThing) if bgc == None: - bgc = _gt.Door.defaultGraphic[0] + bgc = _gt.Door.defaultGraphic.background if fgc == None: - fgc = _gt.Door.defaultGraphic[1] + fgc = _gt.Door.defaultGraphic.foreground if shape == None: - shape = _gt.Door.defaultGraphic[2] + shape = _gt.Door.defaultGraphic.shape for i in args[1:]: if i[0:12].casefold() == 'description=': description = i[12:] @@ -1023,11 +1052,11 @@ Object can be the name of the object, or its coordinates.""" if name == None: name = 'somewhere' if bgc == None: - bgc = _gt.MapExit.defaultGraphic[0] + bgc = _gt.MapExit.defaultGraphic.background if fgc == None: - fgc = _gt.MapExit.defaultGraphic[1] + fgc = _gt.MapExit.defaultGraphic.foreground if shape == None: - shape = _gt.MapExit.defaultGraphic[2] + shape = _gt.MapExit.defaultGraphic.shape for i in args[1:]: if i[0:12].casefold() == 'destination=': destination = i[12:] @@ -1054,21 +1083,21 @@ Object can be the name of the object, or its coordinates.""" single.prevx, single.prevy = x, y else: 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 def giveToPlayer(self, args): """We get to assume it's an item.""" name = 'item {}'.format(self.nextThing) - x, y = self.playerx, self.playery - fgc, bgc, shape = _gt.Item.defaultGraphic['fgc'], _gt.Item.defaultGraphic['bgc'], _gt.Item.defaultGraphic['shape'] + x, y = self.player.x, self.player.y + fgc, bgc, shape = _gt.Item.defaultGraphic.foreground, _gt.Item.defaultGraphic.background, _gt.Item.defaultGraphic.shape description = 'A nondescript item.' persist = True useFunc = '' useOnFunc = '' customValues = {} ranged = False - for i in args[1:]: + for i in args[0:]: if i[0:5].casefold() == 'name=': name = i[5:] elif i[0:10].casefold() == 'location=': @@ -1108,11 +1137,11 @@ Object can be the name of the object, or its coordinates.""" equalsign = i.index('=') cv = i[3:equalsign] customValues[cv] = getValueFromString(i[equalsign+1:]) - thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, _gt.ThingGraphic(bgc, fgc, shape)) - thing.thingID = self.nextThing - self.player.addThing(thing) - self.nextThing += 1 - return thing + thing = _gt.Item(name, x, y, description, useFunc, useOnFunc, customValues, ranged, _gt.ThingGraphic(bgc, fgc, shape)) + thing.thingID = self.nextThing + self.player.addThing(thing) + self.nextThing += 1 + return thing def moveThingScript(self, args): colon = args.index(':') diff --git a/gamemap.py b/gamemap.py index 5db855f..825c98e 100644 --- a/gamemap.py +++ b/gamemap.py @@ -50,6 +50,7 @@ class GameMap(object): tileRegex = re.compile(r'([a-z ])([0-9]+|[ ])') matrixRegex = re.compile(r'(?:[ \t]*(?:[a-z ](?:[0-9]+|[ ]))+(\n))+') yaml = ruamel.yaml.YAML() + yaml.register_class(_gt.Prefab) yaml.register_class(_gt.Item) yaml.register_class(_gt.Useable) yaml.register_class(_gt.NPC) @@ -96,7 +97,16 @@ class GameMap(object): return text.replace('\n', end) @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 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.""" @@ -139,7 +149,7 @@ Entering a map through stdin will be obsolete once testing is over.""" level = GameMap(infile, mapGraph, mapMatrix, dimensions) # 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 @@ -204,7 +214,7 @@ list of lists of tuples.""" return mat, graph, dim @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.""" if 'openingText' in info: level.openingText = info['openingText'] @@ -231,14 +241,14 @@ list of lists of tuples.""" for thing in info['loadOnce']: #print(type(thing)) if isinstance(thing, _gt.Thing): - nextThing = level.addThing(thing, nextThing, True) + nextThing = level.addThing(GameMap.resolvePrefab(prefabs, thing), prefabs, nextThing, True) else: raise MapError("Non-thing loaded as a thing:\n{}".format(thing)) if 'loadAlways' in info: for thing in info['loadAlways']: #print(type(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: level.playerStart = (thing.x, thing.y) hasKnownEntrance = True @@ -247,14 +257,14 @@ list of lists of tuples.""" single = singletons[thing.name] single.x, single.y = thing.x, thing.y single.prevx, single.prevy = thing.x, thing.y - nextThing = level.addThing(single, nextThing) + nextThing = level.addThing(single, prefabs, nextThing) else: raise MapError("Non-thing loaded as a thing:\n{}".format(thing)) return nextThing # 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 return nextThing #if thing.name in self.thingNames: # resolved @@ -266,14 +276,14 @@ list of lists of tuples.""" # so they need IDs as well. # Let's only add them if they weren't already loaded. if thing.thingType in 'iun': - nextThing = GameMap.addThingRecursive(thing.customValues, nextThing) - if thing.thingType == 'n': - for i in thing.tempInventory: - if i.thingID == -1: - i.thingID = nextThing - nextThing = GameMap.addThingRecursive(i.customValues, nextThing + 1) - thing.addThing(i) - del thing.tempInventory + nextThing = GameMap.addThingRecursive(thing.customValues, prefabs, nextThing) + if thing.thingType == 'n': + for i in thing.tempInventory: + if i.thingID == -1: + i.thingID = nextThing + nextThing = GameMap.addThingRecursive(i.customValues, prefabs, nextThing + 1) + thing.addThing(i) + del thing.tempInventory pos = self.coordsToInt(thing.x, thing.y) if pos not in self.thingPos: self.thingPos[pos] = [thing.thingID] @@ -289,21 +299,21 @@ list of lists of tuples.""" return nextThing @staticmethod - def addThingRecursive(container, nextThing = 0): + def addThingRecursive(container, prefabs = None, nextThing = 0): if isinstance(container, _gt.Thing): if container.thingID == -1: container.thingID = nextThing - nextThing = GameMap.addThingRecursive(container.customValues, nextThing) + nextThing = GameMap.addThingRecursive(container.customValues, prefabs, nextThing) return nextThing + 1 else: return nextThing elif isinstance(container, dict): for i in container: - nextThing = GameMap.addThingRecursive(container[i], nextThing) + nextThing = GameMap.addThingRecursive(GameMap.resolvePrefab(prefabs, container[i]), prefabs, nextThing) return nextThing elif isinstance(container, list): for i in container: - nextThing = GameMap.addThingRecursive(i, nextThing) + nextThing = GameMap.addThingRecursive(GameMap.resolvePrefab(prefabs, i), prefabs, nextThing) return nextThing else: return nextThing diff --git a/gameshell.py b/gameshell.py index 27dbc12..b66ce6b 100644 --- a/gameshell.py +++ b/gameshell.py @@ -422,7 +422,8 @@ If -l is given, a map legend will be printed under the map.""" def runScript(self, args): """simple wrapper for gameBase.runscript.""" - self.gameBase.parseScript(' '.join(args)) + args = ['"' + a + '"' for a in args] + self.gameBase.parseScript(' '.join(args) + '\n') # IO calls diff --git a/gamethings.py b/gamethings.py index 8909bbf..ae615c7 100644 --- a/gamethings.py +++ b/gamethings.py @@ -62,11 +62,7 @@ class Thing(object): self.playerx = playerx if playery: self.playery = playery - self.passable = bool(flags & 1) - self.talkable = bool(flags & 2) - self.lookable = bool(flags & 4) - self.takeable = bool(flags & 8) - self.useable = bool(flags & 16) + self.flags = flags self.graphic = ThingGraphic('clear', '#7F7F7F', ' ') self.thingID = -1 # ID not assigned @@ -79,6 +75,82 @@ class Thing(object): return False 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): """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): customValues[v] = list(customValues[v]) if 'ranged' in parts: - useOnFunc = parts['ranged'] + ranged = parts['ranged'] return cls(parts['name'], parts['location'][0], parts['location'][1], parts['description'], useFunc, useOnFunc, customValues, ranged, graphic) @@ -171,6 +243,46 @@ class Item(Thing): def useOn(self, user, gameBase, thing, args) -> float: 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): yaml_flag = u'!Useable' @@ -240,6 +352,44 @@ class Useable(Thing): def use(self, user, gameBase, args) -> float: 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): defaultGraphic = ThingGraphic('clear', '#000000', 'o') @@ -352,7 +502,6 @@ class NPC(Character, Observer): parts = CommentedMap() constructor.construct_mapping(node, parts, True) # set default values for optional arguments - following = False minventory = [] mcustomValues = {} mplayerx, mplayery = parts['location'] @@ -381,6 +530,48 @@ class NPC(Character, Observer): return cls(name = parts['name'], x = parts['location'][0], y = parts['location'][1], description = parts['description'], behaviors = parts['behaviors'], tempInventory = minventory, customValues = mcustomValues, 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): yaml_flag = u'!Door' diff --git a/testing/test1.yml b/testing/test1.yml index 9214145..839d37c 100644 --- a/testing/test1.yml +++ b/testing/test1.yml @@ -64,3 +64,17 @@ loadOnce: distance: 2 isFollowing: True 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]