Fixed most input validation bugs, and did too many improvements to remember. Did I mention that I am typing these words with my hands?
This commit is contained in:
parent
ee5c4da549
commit
4a398cc2a3
11 changed files with 546 additions and 253 deletions
173
gamethings.py
173
gamethings.py
|
@ -1,8 +1,51 @@
|
|||
#gamethings.py
|
||||
"""Standard thing classes.
|
||||
|
||||
Classes:
|
||||
- ThingGraphic: Represents how thing is to be visualized.
|
||||
- Thing: The abstract base class for every thing.
|
||||
- Observer: The abstract base class for things that listen to events.
|
||||
- Item: A thing that can exist in a character's inventory.
|
||||
- Useable: A thing that can be used by a character.
|
||||
- Character: A thing that represents a character.
|
||||
- NPC: A character that is not controlled by the player.
|
||||
- Door: A thing that sometimes blocks paths.
|
||||
- MapExit: A technical thing that marks a map transition.
|
||||
"""
|
||||
|
||||
import ruamel.yaml
|
||||
from ruamel.yaml.comments import CommentedMap
|
||||
|
||||
class ThingGraphic(object):
|
||||
"""Represent how a thing is to be visualized.
|
||||
|
||||
Background color, foreground color, and shape are represented.
|
||||
This could hypothetically be extended to hold sprites, textures or models."""
|
||||
|
||||
def __init__(self, background: str, foreground: str, shape: str):
|
||||
"""Create a graphic for a thing.
|
||||
|
||||
The background and foregrond must be strings with color hex representations.
|
||||
For instance: '#00FF00' means bright green.
|
||||
The shape must be a string containing exactly one of the following shapes:
|
||||
o: circle
|
||||
x: cross
|
||||
-: horizontal line
|
||||
|: vertical line
|
||||
#: square
|
||||
^: triangle
|
||||
A graphic may be invalid.
|
||||
"""
|
||||
if not isinstance(background, str):
|
||||
raise TypeError("Background must be a string of form '#[0-9A-Fa-f]{6}'.")
|
||||
if not isinstance(foreground, str):
|
||||
raise TypeError("Foreground must be a string of form '#[0-9A-Fa-f]{6}'.")
|
||||
if not isinstance(shape, str):
|
||||
raise TypeError("Shape must be a string of form '[-|ox#^]'.")
|
||||
self.background = background
|
||||
self.foreground = foreground
|
||||
self.shape = shape
|
||||
|
||||
class Thing(object):
|
||||
|
||||
def __init__(self, thingType: str, name: str, x: int, y: int, description: str, flags: int, playerx = None, playery = None, **kwargs):
|
||||
|
@ -24,7 +67,7 @@ class Thing(object):
|
|||
self.lookable = bool(flags & 4)
|
||||
self.takeable = bool(flags & 8)
|
||||
self.useable = bool(flags & 16)
|
||||
self.graphic = ('clear', '#7F7F7F', ' ')
|
||||
self.graphic = ThingGraphic('clear', '#7F7F7F', ' ')
|
||||
self.thingID = -1 # ID not assigned
|
||||
|
||||
def __str__(self):
|
||||
|
@ -49,7 +92,7 @@ class Observer(Thing):
|
|||
|
||||
class Item(Thing):
|
||||
yaml_flag = u'!Item'
|
||||
defaultGraphic = ('clear', '#00BF00', '^')
|
||||
defaultGraphic = ThingGraphic('clear', '#00BF00', '^')
|
||||
|
||||
def __init__(self, name, x: int, y: int, description: str, useFunc: str, useOnFunc: str, customValues: dict, ranged: bool, graphic = defaultGraphic):
|
||||
super(Item, self).__init__('i', name, x, y, description, 13)
|
||||
|
@ -68,12 +111,12 @@ class Item(Thing):
|
|||
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
||||
# save graphic
|
||||
graphic = {}
|
||||
if node.graphic[0] != Item.defaultGraphic[0]:
|
||||
graphic['bgc'] = node.graphic[0]
|
||||
if node.graphic[1] != Item.defaultGraphic[1]:
|
||||
graphic['fgc'] = node.graphic[1]
|
||||
if node.graphic[2] != Item.defaultGraphic[2]:
|
||||
graphic['shape'] = node.graphic[2]
|
||||
if node.graphic.background != Item.defaultGraphic.background:
|
||||
graphic['bgc'] = node.graphic.background
|
||||
if node.graphic.foreground != Item.defaultGraphic.forground:
|
||||
graphic['fgc'] = node.graphic.foreground
|
||||
if node.graphic.shape != Item.defaultGraphic.shape:
|
||||
graphic['shape'] = node.graphicshape
|
||||
if len(graphic) > 0:
|
||||
ret['graphic'] = graphic
|
||||
# save use functions
|
||||
|
@ -96,9 +139,9 @@ class Item(Thing):
|
|||
useOnFunc = ''
|
||||
customValues = {}
|
||||
ranged = False
|
||||
bgc = Item.defaultGraphic[0]
|
||||
fgc = Item.defaultGraphic[1]
|
||||
shape = Item.defaultGraphic[2]
|
||||
bgc = Item.defaultGraphic.background
|
||||
fgc = Item.defaultGraphic.foreground
|
||||
shape = Item.defaultGraphic.shape
|
||||
# load graphic
|
||||
if 'graphic' in parts:
|
||||
if 'bgc' in parts['graphic']:
|
||||
|
@ -107,7 +150,7 @@ class Item(Thing):
|
|||
fgc = parts['graphic']['fgc']
|
||||
if 'shape' in parts['graphic']:
|
||||
shape = parts['graphic']['shape']
|
||||
graphic = (bgc, fgc, shape)
|
||||
graphic = ThingGraphic(bgc, fgc, shape)
|
||||
# load use functions
|
||||
if 'useFunc' in parts:
|
||||
useFunc = parts['useFunc']
|
||||
|
@ -131,7 +174,7 @@ class Item(Thing):
|
|||
|
||||
class Useable(Thing):
|
||||
yaml_flag = u'!Useable'
|
||||
defaultGraphic = ('clear', '#0000FF', '#')
|
||||
defaultGraphic = ThingGraphic('clear', '#0000FF', '#')
|
||||
|
||||
def __init__(self, name, x: int, y: int, description: str, useFunc: str, customValues: dict, playerx = None, playery = None, graphic = defaultGraphic):
|
||||
super(Useable, self).__init__('u', name, x, y, description, 16, playerx, playery)
|
||||
|
@ -145,12 +188,12 @@ class Useable(Thing):
|
|||
ret = {'name': node.name, 'location': (node.x, node.y), 'description': node.description}
|
||||
# save graphic
|
||||
graphic = {}
|
||||
if node.graphic[0] != Useable.defaultGraphic[0]:
|
||||
graphic['bgc'] = node.graphic[0]
|
||||
if node.graphic[1] != Useable.defaultGraphic[1]:
|
||||
graphic['fgc'] = node.graphic[1]
|
||||
if node.graphic[2] != Useable.defaultGraphic[2]:
|
||||
graphic['shape'] = node.graphic[2]
|
||||
if node.graphic.background != Useable.defaultGraphic.background:
|
||||
graphic['bgc'] = node.graphic.background
|
||||
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||
graphic['fgc'] = node.graphic.foreground
|
||||
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||
graphic['shape'] = node.graphic.shape
|
||||
if len(graphic) > 0:
|
||||
ret['graphic'] = graphic
|
||||
# save use functions
|
||||
|
@ -170,9 +213,9 @@ class Useable(Thing):
|
|||
useFunc = ''
|
||||
customValues = {}
|
||||
playerx, playery = parts['location']
|
||||
bgc = Useable.defaultGraphic[0]
|
||||
fgc = Useable.defaultGraphic[1]
|
||||
shape = Useable.defaultGraphic[2]
|
||||
bgc = Useable.defaultGraphic.background
|
||||
fgc = Useable.defaultGraphic.foreground
|
||||
shape = Useable.defaultGraphic.shape
|
||||
# load graphic
|
||||
if 'graphic' in parts:
|
||||
if 'bgc' in parts['graphic']:
|
||||
|
@ -181,7 +224,7 @@ class Useable(Thing):
|
|||
fgc = parts['graphic']['fgc']
|
||||
if 'shape' in parts['graphic']:
|
||||
shape = parts['graphic']['shape']
|
||||
graphic = (bgc, fgc, shape)
|
||||
graphic = ThingGraphic(bgc, fgc, shape)
|
||||
# load use functions
|
||||
if 'useFunc' in parts:
|
||||
useFunc = parts['useFunc']
|
||||
|
@ -199,7 +242,7 @@ class Useable(Thing):
|
|||
pass
|
||||
|
||||
class Character(Thing):
|
||||
defaultGraphic = ('clear', '#000000', 'o')
|
||||
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
||||
|
||||
def __init__(self, inventory: dict, customValues: dict, graphic = defaultGraphic, **kwargs):
|
||||
super(Character, self).__init__(**kwargs)
|
||||
|
@ -211,7 +254,7 @@ class Character(Thing):
|
|||
self.__inventory = inventory
|
||||
self.customValues = customValues
|
||||
self.graphic = graphic
|
||||
self.thingNames = {}
|
||||
self.thingNames = {} #{str: int}
|
||||
# set up inventory shtuff
|
||||
for i in self.__inventory:
|
||||
if self.__inventory[i].name in self.thingNames:
|
||||
|
@ -229,7 +272,10 @@ class Character(Thing):
|
|||
self.thingNames[thing.name] = [thing.thingID]
|
||||
|
||||
def getThingByID(self, thingID):
|
||||
return self.__inventory[thingID]
|
||||
if thingID in self.__inventory:
|
||||
return self.__inventory[thingID]
|
||||
else:
|
||||
return None
|
||||
|
||||
def getThingByName(self, name):
|
||||
if name in self.thingNames:
|
||||
|
@ -247,10 +293,10 @@ class Character(Thing):
|
|||
|
||||
def removeThingByName(self, name):
|
||||
ret = self.getThingByName(name)
|
||||
self.thingNames[ret.name].remove(thingID)
|
||||
self.thingNames[ret.name].remove(ret.thingID)
|
||||
if len(self.thingNames[ret.name]) == 0:
|
||||
del self.thingNames[ret.name]
|
||||
del self.__inventory[thingID]
|
||||
del self.__inventory[ret.thingID]
|
||||
return ret
|
||||
|
||||
def removeThing(self, ret):
|
||||
|
@ -267,7 +313,7 @@ class Character(Thing):
|
|||
|
||||
class NPC(Character, Observer):
|
||||
yaml_flag = u'!NPC'
|
||||
defaultGraphic = ('clear', '#000000', 'o')
|
||||
defaultGraphic = ThingGraphic('clear', '#000000', 'o')
|
||||
|
||||
def __init__(self, behaviors: dict, tempInventory: list, **kwargs):
|
||||
if 'graphic' not in kwargs:
|
||||
|
@ -284,12 +330,12 @@ class NPC(Character, Observer):
|
|||
'description': node.description, 'behaviors': node.behaviors}
|
||||
# save graphic
|
||||
graphic = {}
|
||||
if node.graphic[0] != NPC.defaultGraphic[0]:
|
||||
graphic['bgc'] = node.graphic[0]
|
||||
if node.graphic[1] != NPC.defaultGraphic[1]:
|
||||
graphic['fgc'] = node.graphic[1]
|
||||
if node.graphic[2] != NPC.defaultGraphic[2]:
|
||||
graphic['shape'] = node.graphic[2]
|
||||
if node.graphic.background != Useable.defaultGraphic.background:
|
||||
graphic['bgc'] = node.graphic.background
|
||||
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||
graphic['fgc'] = node.graphic.foreground
|
||||
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||
graphic['shape'] = node.graphic.shape
|
||||
if len(graphic) > 0:
|
||||
ret['graphic'] = graphic
|
||||
# save use functions
|
||||
|
@ -310,9 +356,9 @@ class NPC(Character, Observer):
|
|||
minventory = []
|
||||
mcustomValues = {}
|
||||
mplayerx, mplayery = parts['location']
|
||||
bgc = NPC.defaultGraphic[0]
|
||||
fgc = NPC.defaultGraphic[1]
|
||||
shape = NPC.defaultGraphic[2]
|
||||
bgc = NPC.defaultGraphic.background
|
||||
fgc = NPC.defaultGraphic.foreground
|
||||
shape = NPC.defaultGraphic.shape
|
||||
# load graphic
|
||||
if 'graphic' in parts:
|
||||
if 'bgc' in parts['graphic']:
|
||||
|
@ -321,7 +367,7 @@ class NPC(Character, Observer):
|
|||
fgc = parts['graphic']['fgc']
|
||||
if 'shape' in parts['graphic']:
|
||||
shape = parts['graphic']['shape']
|
||||
mgraphic = (bgc, fgc, shape)
|
||||
mgraphic = ThingGraphic(bgc, fgc, shape)
|
||||
# load use functions
|
||||
if 'inventory' in parts:
|
||||
inventory = parts['inventory']
|
||||
|
@ -338,7 +384,7 @@ class NPC(Character, Observer):
|
|||
|
||||
class Door(Thing):
|
||||
yaml_flag = u'!Door'
|
||||
defaultGraphic = ('clear', '#7F3F00', '#')
|
||||
defaultGraphic = ThingGraphic('clear', '#7F3F00', '#')
|
||||
|
||||
def __init__(self, name, x: int, y: int, locked: bool, description = None, key = None, graphic = defaultGraphic):
|
||||
self.descBase = description
|
||||
|
@ -379,12 +425,12 @@ class Door(Thing):
|
|||
ret = {'name': node.name, 'location': (node.x, node.y)}
|
||||
# save graphic
|
||||
graphic = {}
|
||||
if node.graphic[0] != Door.defaultGraphic[0]:
|
||||
graphic['bgc'] = node.graphic[0]
|
||||
if node.graphic[1] != Door.defaultGraphic[1]:
|
||||
graphic['fgc'] = node.graphic[1]
|
||||
if node.graphic[2] != Door.defaultGraphic[2]:
|
||||
graphic['shape'] = node.graphic[2]
|
||||
if node.graphic.background != Useable.defaultGraphic.background:
|
||||
graphic['bgc'] = node.graphic.background
|
||||
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||
graphic['fgc'] = node.graphic.foreground
|
||||
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||
graphic['shape'] = node.graphic.shape
|
||||
if len(graphic) > 0:
|
||||
ret['graphic'] = graphic
|
||||
# save door state
|
||||
|
@ -404,9 +450,9 @@ class Door(Thing):
|
|||
description = None
|
||||
locked = False
|
||||
key = None
|
||||
bgc = Door.defaultGraphic[0]
|
||||
fgc = Door.defaultGraphic[1]
|
||||
shape = Door.defaultGraphic[2]
|
||||
bgc = Door.defaultGraphic.background
|
||||
fgc = Door.defaultGraphic.foreground
|
||||
shape = Door.defaultGraphic.shape
|
||||
# load graphic
|
||||
if 'graphic' in parts:
|
||||
if 'bgc' in parts['graphic']:
|
||||
|
@ -415,7 +461,7 @@ class Door(Thing):
|
|||
fgc = parts['graphic']['fgc']
|
||||
if 'shape' in parts['graphic']:
|
||||
shape = parts['graphic']['shape']
|
||||
graphic = (bgc, fgc, shape)
|
||||
graphic = ThingGraphic(bgc, fgc, shape)
|
||||
# load door state
|
||||
if 'description' in parts:
|
||||
description = parts['description']
|
||||
|
@ -428,7 +474,7 @@ class Door(Thing):
|
|||
|
||||
class MapExit(Thing):
|
||||
yaml_flag = u'!MapExit'
|
||||
defaultGraphic = ('clear', '#FF0000', 'x')
|
||||
defaultGraphic = ThingGraphic('clear', '#FF0000', 'x')
|
||||
|
||||
def __init__(self, name, x: int, y: int, exitid: int, destination: str, prefix = None, onUse = '', key = True, graphic = defaultGraphic):
|
||||
description = name
|
||||
|
@ -448,12 +494,12 @@ class MapExit(Thing):
|
|||
ret = {'name': node.name, 'location': (node.x, node.y), 'id': node.exitid, 'destination': node.destination}
|
||||
# save graphic
|
||||
graphic = {}
|
||||
if node.graphic[0] != MapExit.defaultGraphic[0]:
|
||||
graphic['bgc'] = node.graphic[0]
|
||||
if node.graphic[1] != MapExit.defaultGraphic[1]:
|
||||
graphic['fgc'] = node.graphic[1]
|
||||
if node.graphic[2] != MapExit.defaultGraphic[2]:
|
||||
graphic['shape'] = node.graphic[2]
|
||||
if node.graphic.background != Useable.defaultGraphic.background:
|
||||
graphic['bgc'] = node.graphic.background
|
||||
if node.graphic.foreground != Useable.defaultGraphic.foreground:
|
||||
graphic['fgc'] = node.graphic.foreground
|
||||
if node.graphic.shape != Useable.defaultGraphic.shape:
|
||||
graphic['shape'] = node.graphic.shape
|
||||
if len(graphic) > 0:
|
||||
ret['graphic'] = graphic
|
||||
if node.prefix != None:
|
||||
|
@ -472,9 +518,9 @@ class MapExit(Thing):
|
|||
prefix = None
|
||||
onUse = ''
|
||||
key = True
|
||||
bgc = MapExit.defaultGraphic[0]
|
||||
fgc = MapExit.defaultGraphic[1]
|
||||
shape = MapExit.defaultGraphic[2]
|
||||
bgc = MapExit.defaultGraphic.background
|
||||
fgc = MapExit.defaultGraphic.foreground
|
||||
shape = MapExit.defaultGraphic.shape
|
||||
# load graphic
|
||||
if 'graphic' in parts:
|
||||
if 'bgc' in parts['graphic']:
|
||||
|
@ -483,7 +529,7 @@ class MapExit(Thing):
|
|||
fgc = parts['graphic']['fgc']
|
||||
if 'shape' in parts['graphic']:
|
||||
shape = parts['graphic']['shape']
|
||||
graphic = (bgc, fgc, shape)
|
||||
graphic = ThingGraphic(bgc, fgc, shape)
|
||||
if 'prefix' in parts:
|
||||
prefix = parts['prefix']
|
||||
if 'onUse' in parts:
|
||||
|
@ -495,7 +541,7 @@ class MapExit(Thing):
|
|||
|
||||
class MapEntrance(Thing):
|
||||
yaml_flag = u'!MapEntrance'
|
||||
defaultGraphic = ('clear', 'clear', 'x')
|
||||
defaultGraphic = ThingGraphic('clear', 'clear', 'x')
|
||||
# no graphic - should not be drawn
|
||||
|
||||
def __init__(self, x: int, y: int, exitid: int, name = None):
|
||||
|
@ -503,6 +549,7 @@ class MapEntrance(Thing):
|
|||
name = 'entrance {}'.format(exitid)
|
||||
super(MapEntrance, self).__init__('a', name, x, y, '', 1)
|
||||
self.exitid = exitid
|
||||
#self.graphic = MapEntrance.defaultGraphic
|
||||
|
||||
@classmethod
|
||||
def to_yaml(cls, representer, node):
|
||||
|
@ -519,7 +566,7 @@ class MapEntrance(Thing):
|
|||
|
||||
class PlayerCharacter(Character):
|
||||
"""Player object. Cannot be created with yaml."""
|
||||
defaultGraphic = ('clear', '#0000FF', 'o')
|
||||
defaultGraphic = ThingGraphic('clear', '#0000FF', 'o')
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
if 'name' not in kwargs:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue