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':
|
||||
args.pop(0)
|
||||
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
|
||||
if thing:
|
||||
print(self.justifyText(str(thing)), file = self.outstream)
|
||||
else:
|
||||
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
|
||||
print("{} cannot see that.".format(self.playerName))
|
||||
elif thing == None:
|
||||
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()))
|
||||
|
||||
def use(self, args):
|
||||
|
|
|
@ -98,3 +98,8 @@ class DropEvent(GameEvent):
|
|||
def __init__(self, item):
|
||||
super(DropEvent, self).__init__('drop')
|
||||
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 xml.etree.ElementTree as ET
|
||||
import ruamel.yaml
|
||||
import math as _mt
|
||||
from ruamel.yaml.comments import CommentedMap # for loading classes
|
||||
|
||||
class Thing(object):
|
||||
|
@ -122,9 +123,6 @@ class Useable(Thing):
|
|||
self.customValues = customValues
|
||||
self.graphic = graphic
|
||||
|
||||
def use(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def to_yaml(cls, representer, node):
|
||||
# save usual things
|
||||
|
@ -182,12 +180,15 @@ class Useable(Thing):
|
|||
parts['description'], useFunc, customValues, playerx, playery, graphic)
|
||||
|
||||
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')):
|
||||
super(NPC, self).__init__('c', name, x, y, description, 6)
|
||||
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, playerx, playery)
|
||||
self.behavior = behavior
|
||||
self.following = following
|
||||
self.friendly = friendly
|
||||
self.inventory = []
|
||||
self.inventory = inventory
|
||||
self.customValues = customValues
|
||||
self.graphic = graphic
|
||||
|
||||
def give(self, item):
|
||||
|
@ -199,6 +200,69 @@ class NPC(Thing):
|
|||
def dialog(self):
|
||||
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):
|
||||
yaml_flag = u'!Door'
|
||||
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
|
||||
|
||||
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
|
||||
def __coordsToInt(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:
|
||||
return x + y * self.dimensions[0]
|
||||
else:
|
||||
return x + y * width
|
||||
|
||||
def intToCoords(self, pos):
|
||||
def intToCoords(self, pos: int):
|
||||
return pos % self.dimensions[0], int(pos / self.dimensions[0])
|
||||
|
||||
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])
|
||||
return ret
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
|
||||
def getThingByName(self, name):
|
||||
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])
|
||||
return ret
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
|
||||
def getThingByID(self, thingID):
|
||||
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]))
|
||||
return ret
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
|
||||
def removeThingByName(self, name):
|
||||
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]))
|
||||
return ret
|
||||
else:
|
||||
return None
|
||||
return []
|
||||
|
||||
def removeThingByID(self, thingID):
|
||||
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]
|
||||
else:
|
||||
self.thingPos[newPos].append(name)
|
||||
relPlayerx, relPlayery = thing.playerx - thing.x, thing.playery - thing.y
|
||||
thing.x, thing.y = x, y
|
||||
thing.playerx, thing.playery = thing.x + relPlayerx, thing.y + relPlayery
|
||||
else:
|
||||
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