The player can now have simple conversations with NPCs.

This commit is contained in:
Patrick Marsee 2019-05-20 21:13:56 -04:00
parent e0b88e7a45
commit e81345e793
4 changed files with 115 additions and 10 deletions

View file

@ -7,6 +7,10 @@ import gameevents as _ge
import random as _ra
import sys as _sys
import pickle as _pi
import ruamel.yaml as _yaml
class GameError(RuntimeError):
pass
class GameBase(object):
@ -18,6 +22,7 @@ class GameBase(object):
def __init__(self):
self.outstream = _sys.stdout
self.__useFuncs = {}
self.__behaviors = {}
self.__gameEvents = {}
self.__IOCalls = {} # {str : function}
self.customVals = {} # for setting flags and such
@ -49,9 +54,11 @@ class GameBase(object):
self.registerEvent('useon', self.handleUseOn)
self.registerEvent('take', self.handleTake)
self.registerEvent('drop', self.handleDrop)
self.registerEvent('behave', self.handleBehave)
self.registerUseFunc('examine', self.examine)
self.registerUseFunc('key', self.key)
self.registerUseFunc('container', self.container)
self.registerBehavior('wander', self.wander)
# Helper functions
@ -178,7 +185,7 @@ Destination can be a coordinate pair or object name. For instance, if one
wanted to go to D6, one could say "go to D6", "go to d 6", or "go to 3 6".
The letter is not case-sensitive."""
if self.level == None:
raise RuntimeError("Cannot move: No level has been loaded.")
raise GameError("Cannot move: No level has been loaded.")
speed = 0.6666667
if args[0] == '-r' or args[0] == 'r' or args[0] == 'run':
speed = 0.3333333
@ -236,13 +243,46 @@ Object can be the name of the object, or its coordinates."""
args.pop(0)
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
print("{} cannot see that.".format(self.playerName))
print("{} cannot see that.".format(self.playerName), file = self.outstream)
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 talk(self, args):
"""talk [to [the]] character
Talk to a character.
"to" and "the" do nothing, and are intended only to make certain commands
make more sense from a linguistic perspective (for instance, one could
say "talk to the guard" rather than just "talk guard").
Character can be the name of the character, or their coordinates."""
if len(args) == 0:
print(self.justifyText(self.level.description), file = self.outstream)
else:
if args[0] == 'to':
args.pop(0)
if args[0] == 'the':
args.pop(0)
thing, x, y = self.parseCoords(args, usePlayerCoords = False)
if not self.level.lineOfSight(self.playerx, self.playery, x, y):
print("{} cannot talk to {}.".format(self.playerName, thing.name), file = self.outstream)
elif thing == None:
print("There is nobody here.\n", file = self.outstream)
else:
if 'dialogs' in thing.customValues:
if isinstance(thing.customValues['dialogs'], list):
if 'dialogPtr' in thing.customValues and isinstance(thiong.customValues['dialogPtr'], int):
self.dialog(thing.customValues['dialogs'][thing.customValues['dialogPtr']], thing)
else:
self.dialog(thing.customValues['dialogs'][0], thing)
elif isinstance(thing.customValues['dialogs'], str):
self.dialog(thing.customValues['dialogs'], thing)
else:
raise GameError("Character '{}' has dialog of an unexpected type.".format(thing.name))
else:
print("{} has nothing to say to you.".format(thing.name), file = self.outstream)
def use(self, args):
"""use [-r] [the] object [on [the] object2]
Use an object. If the player is not already close to it, they will go to it.
@ -491,6 +531,13 @@ Object can be the name of the object, or its coordinates."""
#_hq.heappush(self.eventQueue, (self.gameTime, _ge.NoOpEvent()))
return
def dialog(self, dialogName, conversant):
yaml = _yaml.YAML()
dialog = None
with open(dialogName, 'r') as f:
dialog = yaml.load(f)
self.getIO('dialog')(dialog)
def gameEventLoop(self):
#print(self.skipLoop)
if self.skipLoop:
@ -576,6 +623,9 @@ Object can be the name of the object, or its coordinates."""
self.level.addThing(e.item, True)
del self.playerInv[e.item.name]
return True
def handleBehave(self, e):
self.__behaviors[e.actor.behavior](e.actor)
# default useFuncs: take a list of arguments, return the time the use took
@ -629,12 +679,21 @@ Object can be the name of the object, or its coordinates."""
else:
print("The key doesn't fit that lock.", file = self.outstream)
return 0.125
# behaviors
def wander(self, actor):
pass
# stuff for extended classes to use
def registerUseFunc(self, name, func):
"""Registers a function for use by things in the map, but not directly
callable by the player."""
self.__useFuncs[name] = func
def registerBehavior(self, name, func):
"""Registers a function for use as an NPC's behavior."""
self.__behaviors[name] = func
def registerEvent(self, name, func):
"""Registers a function to handle an event in the event loop.
@ -648,4 +707,6 @@ always give the player a turn, False otherwise."""
def getIO(self, name):
"""This is so derived classes can access their IOCalls."""
if name not in self.__IOCalls:
raise GameError("No IO call for {}.".format(name))
return self.__IOCalls[name]