Line of sight now works, and is required to look at things. Foundations for NPCs has also been laid.
This commit is contained in:
parent
267f0e9123
commit
e0b88e7a45
4 changed files with 151 additions and 17 deletions
|
@ -235,10 +235,12 @@ Object can be the name of the object, or its coordinates."""
|
||||||
if args[0] == 'the':
|
if args[0] == 'the':
|
||||||
args.pop(0)
|
args.pop(0)
|
||||||
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
|
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
|
||||||
if thing:
|
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
|
||||||
print(self.justifyText(str(thing)), file = self.outstream)
|
print("{} cannot see that.".format(self.playerName))
|
||||||
else:
|
elif thing == None:
|
||||||
print("There is nothing to see here.\n", file = self.outstream)
|
print("There is nothing to see here.\n", file = self.outstream)
|
||||||
|
else:
|
||||||
|
print(self.justifyText(str(thing)), file = self.outstream)
|
||||||
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
|
||||||
|
|
||||||
def use(self, args):
|
def use(self, args):
|
||||||
|
|
|
@ -98,3 +98,8 @@ class DropEvent(GameEvent):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
super(DropEvent, self).__init__('drop')
|
super(DropEvent, self).__init__('drop')
|
||||||
self.item = item
|
self.item = item
|
||||||
|
|
||||||
|
class BehaveEvent(GameEvent):
|
||||||
|
def __init__(self, actor):
|
||||||
|
super(BehaveEvent, self).__init__('behave')
|
||||||
|
self.actor = actor
|
||||||
|
|
140
gamemap.py
140
gamemap.py
|
@ -3,6 +3,7 @@ import re
|
||||||
import heapq
|
import heapq
|
||||||
import xml.etree.ElementTree as ET
|
import xml.etree.ElementTree as ET
|
||||||
import ruamel.yaml
|
import ruamel.yaml
|
||||||
|
import math as _mt
|
||||||
from ruamel.yaml.comments import CommentedMap # for loading classes
|
from ruamel.yaml.comments import CommentedMap # for loading classes
|
||||||
|
|
||||||
class Thing(object):
|
class Thing(object):
|
||||||
|
@ -122,9 +123,6 @@ class Useable(Thing):
|
||||||
self.customValues = customValues
|
self.customValues = customValues
|
||||||
self.graphic = graphic
|
self.graphic = graphic
|
||||||
|
|
||||||
def use(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_yaml(cls, representer, node):
|
def to_yaml(cls, representer, node):
|
||||||
# save usual things
|
# save usual things
|
||||||
|
@ -182,12 +180,15 @@ class Useable(Thing):
|
||||||
parts['description'], useFunc, customValues, playerx, playery, graphic)
|
parts['description'], useFunc, customValues, playerx, playery, graphic)
|
||||||
|
|
||||||
class NPC(Thing):
|
class NPC(Thing):
|
||||||
|
yaml_flag = u'!NPC'
|
||||||
|
defaultGraphic = ('clear', '#000000', 'o')
|
||||||
|
|
||||||
def __init__(self, name, x: int, y: int, description: str, friendly = True, following = False, graphic = ('clear', '#000000', 'o')):
|
def __init__(self, name, x: int, y: int, description: str, behavior: str, inventory: list, customValues: dict, playerx = None, playery = None, following = False, graphic = defaultGraphic):
|
||||||
super(NPC, self).__init__('c', name, x, y, description, 6)
|
super(NPC, self).__init__('c', name, x, y, description, 6, playerx, playery)
|
||||||
|
self.behavior = behavior
|
||||||
self.following = following
|
self.following = following
|
||||||
self.friendly = friendly
|
self.inventory = inventory
|
||||||
self.inventory = []
|
self.customValues = customValues
|
||||||
self.graphic = graphic
|
self.graphic = graphic
|
||||||
|
|
||||||
def give(self, item):
|
def give(self, item):
|
||||||
|
@ -199,6 +200,69 @@ class NPC(Thing):
|
||||||
def dialog(self):
|
def dialog(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_yaml(cls, representer, node):
|
||||||
|
# save usual things
|
||||||
|
ret = {'name': node.name, 'location': (node.x, node.y),
|
||||||
|
'description': node.description, 'behavior': node.behavior}
|
||||||
|
# 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 len(graphic) > 0:
|
||||||
|
ret['graphic'] = graphic
|
||||||
|
# save use functions
|
||||||
|
if node.following:
|
||||||
|
ret['following'] = node.following
|
||||||
|
if len(node.inventory) > 0:
|
||||||
|
ret['inventory'] = node.inventory
|
||||||
|
if len(node.customValues) > 0:
|
||||||
|
ret['customValues'] = node.customValues
|
||||||
|
if node.x != node.playerx or node.y != node.playery:
|
||||||
|
ret['useLocation'] = (node.playerx, node.playery)
|
||||||
|
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
|
||||||
|
following = False
|
||||||
|
inventory = []
|
||||||
|
customValues = {}
|
||||||
|
playerx, playery = parts['location']
|
||||||
|
bgc = NPC.defaultGraphic[0]
|
||||||
|
fgc = NPC.defaultGraphic[1]
|
||||||
|
shape = NPC.defaultGraphic[2]
|
||||||
|
# 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']
|
||||||
|
graphic = (bgc, fgc, shape)
|
||||||
|
# load use functions
|
||||||
|
if 'following' in parts:
|
||||||
|
useFunc = parts['following']
|
||||||
|
if 'inventory' in parts:
|
||||||
|
inventory = parts['inventory']
|
||||||
|
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']
|
||||||
|
return cls(parts['name'], parts['location'][0], parts['location'][1],
|
||||||
|
parts['description'], parts['behavior'], inventory, customValues,
|
||||||
|
playerx, playery, following, graphic)
|
||||||
|
|
||||||
class Door(Thing):
|
class Door(Thing):
|
||||||
yaml_flag = u'!Door'
|
yaml_flag = u'!Door'
|
||||||
defaultGraphic = ('clear', '#7F3F00', '#')
|
defaultGraphic = ('clear', '#7F3F00', '#')
|
||||||
|
@ -713,19 +777,65 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
# return -1, [] # meaning you can't get there
|
# return -1, [] # meaning you can't get there
|
||||||
|
|
||||||
def lineOfSight(self, x1, y1, x2, y2):
|
def lineOfSight(self, x1, y1, x2, y2):
|
||||||
pass
|
Dx = x2 - x1
|
||||||
|
Dy = y2 - y1
|
||||||
|
y = y1 + 0.5
|
||||||
|
x = x1 + 0.5
|
||||||
|
lst = []
|
||||||
|
if abs(Dx) >= abs(Dy):
|
||||||
|
if Dx < 0:
|
||||||
|
x = x2 + 0.5
|
||||||
|
y = y2 + 0.5
|
||||||
|
dy = Dy / Dx
|
||||||
|
while(x < max(x2, x1) - 0.5):
|
||||||
|
x += 1
|
||||||
|
lst.append(self.coordsToInt(_mt.floor(x), _mt.floor(y)))
|
||||||
|
if _mt.floor(y) != _mt.floor(y + dy):
|
||||||
|
lst.append(self.coordsToInt(_mt.floor(x), _mt.floor(y + dy)))
|
||||||
|
y += dy
|
||||||
|
elif abs(Dx) < abs(Dy):
|
||||||
|
if Dy < 0:
|
||||||
|
x = x2 + 0.5
|
||||||
|
y = y2 + 0.5
|
||||||
|
dx = Dx / Dy
|
||||||
|
while(y < max(y2, y1) - 0.5):
|
||||||
|
y += 1
|
||||||
|
lst.append(self.coordsToInt(_mt.floor(x), _mt.floor(y)))
|
||||||
|
if _mt.floor(x) != _mt.floor(x + dx):
|
||||||
|
lst.append(self.coordsToInt(_mt.floor(x + dx), _mt.floor(y)))
|
||||||
|
x += dx
|
||||||
|
|
||||||
|
# Here is where we actually check:
|
||||||
|
for space in lst:
|
||||||
|
if not self.isPassable(space):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def isPassable(self, x, y = -1):
|
||||||
|
pos = x
|
||||||
|
if y == -1:
|
||||||
|
x, y = self.intToCoords(x)
|
||||||
|
else:
|
||||||
|
pos = self.coordsToInt(x, y)
|
||||||
|
if self.mapMatrix[y][x][0] == 'w':
|
||||||
|
return False
|
||||||
|
thingsInSpace = self.getThingsAtPos(pos)
|
||||||
|
for thing in thingsInSpace:
|
||||||
|
if not thing.passable:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __coordsToInt(x, y, width):
|
def __coordsToInt(x, y, width):
|
||||||
return x + y * width
|
return x + y * width
|
||||||
|
|
||||||
def coordsToInt(self, x, y, width = -1):
|
def coordsToInt(self, x: int, y: int, width = -1):
|
||||||
if width < 0:
|
if width < 0:
|
||||||
return x + y * self.dimensions[0]
|
return x + y * self.dimensions[0]
|
||||||
else:
|
else:
|
||||||
return x + y * width
|
return x + y * width
|
||||||
|
|
||||||
def intToCoords(self, pos):
|
def intToCoords(self, pos: int):
|
||||||
return pos % self.dimensions[0], int(pos / self.dimensions[0])
|
return pos % self.dimensions[0], int(pos / self.dimensions[0])
|
||||||
|
|
||||||
def getThingAtCoords(self, x, y):
|
def getThingAtCoords(self, x, y):
|
||||||
|
@ -747,7 +857,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
ret.append(self.things[i])
|
ret.append(self.things[i])
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return None
|
return []
|
||||||
|
|
||||||
def getThingByName(self, name):
|
def getThingByName(self, name):
|
||||||
if name in self.thingNames:
|
if name in self.thingNames:
|
||||||
|
@ -762,7 +872,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
ret.append(self.things[i])
|
ret.append(self.things[i])
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return None
|
return []
|
||||||
|
|
||||||
def getThingByID(self, thingID):
|
def getThingByID(self, thingID):
|
||||||
if thingID in self.things:
|
if thingID in self.things:
|
||||||
|
@ -806,7 +916,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
ret.append(self.removeThingByThing(self.things[i]))
|
ret.append(self.removeThingByThing(self.things[i]))
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return None
|
return []
|
||||||
|
|
||||||
def removeThingByName(self, name):
|
def removeThingByName(self, name):
|
||||||
if name in self.thingNames:
|
if name in self.thingNames:
|
||||||
|
@ -821,7 +931,7 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
ret.append(self.removeThingByThing(self.things[i]))
|
ret.append(self.removeThingByThing(self.things[i]))
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
return None
|
return []
|
||||||
|
|
||||||
def removeThingByID(self, thingID):
|
def removeThingByID(self, thingID):
|
||||||
if thingID in self.things:
|
if thingID in self.things:
|
||||||
|
@ -845,7 +955,9 @@ The closeEnough parameter will create a path that lands beside the source if nec
|
||||||
self.thingPos[newPos] = [name]
|
self.thingPos[newPos] = [name]
|
||||||
else:
|
else:
|
||||||
self.thingPos[newPos].append(name)
|
self.thingPos[newPos].append(name)
|
||||||
|
relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y
|
||||||
thing.x, thing.y = x, y
|
thing.x, thing.y = x, y
|
||||||
|
thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery
|
||||||
else:
|
else:
|
||||||
raise RuntimeError("There is nothing to move.".format(name))
|
raise RuntimeError("There is nothing to move.".format(name))
|
||||||
|
|
||||||
|
|
15
testing/testDialog.yml
Normal file
15
testing/testDialog.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
# This is a sample dialog tree, represented as nested dicts in YAML.
|
||||||
|
opener: Hello, I'm a sample-dialog NPC. I assure you, I eventually turn
|
||||||
|
out to be a really well-rounded character.
|
||||||
|
action: answer
|
||||||
|
answers:
|
||||||
|
- Okay.
|
||||||
|
- Really? Well, tell me more!
|
||||||
|
replies:
|
||||||
|
- opener: Yup.
|
||||||
|
action: exit
|
||||||
|
- opener: I would, but there's still a bunch of foreshadowing and stuff we
|
||||||
|
have to get through first.
|
||||||
|
action: back
|
Loading…
Add table
Add a link
Reference in a new issue