Mercurial > skaapsteker
view skaapsteker/dialogue.py @ 190:e4b466368b89
Fix dialogue.
author | Simon Cross <hodgestar@gmail.com> |
---|---|
date | Wed, 06 Apr 2011 21:19:26 +0200 |
parents | c3dfcd241c3a |
children | 897eec397cbb |
line wrap: on
line source
import json from . import data class DSM(object): """Dialogue State Machine! Parameters ---------- json_filename : str Path to file under data/ that contains JSON description of state machine. world : object Something to allow states to introspect the game state with. """ def __init__(self, json_filename, world): self.world = world self.state = "start" self.states = AttrDict() src = json.loads(data.load(json_filename).read()) for state, state_src in src.iteritems(): pseudo_path = [json_filename, state] self.states[state] = DsmState(state, state_src, pseudo_path) assert self.state in self.states, "DSM must have start state" def get_state(self): return self.states[self.state] def event(self, ev): my_locals = { "state": self.states, "world" : self.world, } my_locals.update(ev.items) state = self.states[self.state] next_state = state.event(my_locals) if next_state.name in self.states: self.state = next_state.name def choice(self, i): self.event(DsmEvent(choice=i)) class AttrDict(dict): def __getattr__(self, name): if name not in self: raise AttributeError("No attribute %r" % (name,)) return self[name] class DsmEvent(object): def __init__(self, choice=None): self.items = { "choice": choice, } class DsmState(object): """State within a DSM. """ def __init__(self, name, state_src, base_path): self.name = name self.text = state_src.get("text", None) self.choices = [] self.triggers = [] choices = state_src.get("choices", []) for i, choice in enumerate(choices): pseudo_path = base_path + ["choice-%d" % i] self.choices.append((i, choice["text"])) next_state_code = choice.get("next", None) if next_state_code is not None: self.triggers.append( Trigger("choice == %d" % i, next_state_code, pseudo_path)) events = state_src.get("events", []) for i, event in enumerate(events): pseudo_path = base_path + ["event-%d" % i] self.triggers.append(Trigger(event["matches"], event["next"], pseudo_path)) auto_next = state_src.get("auto_next", None) if auto_next is not None: pseudo_path = base_path + ["auto_next"] self.triggers.append(Trigger("""True""", auto_next, pseudo_path)) def __repr__(self): return "<%r name=%r>" % (self.__class__.__name__, self.name) def event(self, my_locals): for trigger in self.triggers: next_state = trigger.fire(my_locals) if next_state is not None: return next_state class Trigger(object): """Matches DSM events and triggers state transitions. """ def __init__(self, matches_code, next_state_code, pseudo_path): self._matches = compile(matches_code, "<%s>" % ":".join(pseudo_path + ["match"]), "eval") self._next_state = compile(next_state_code, "<%s>" % ":".join(pseudo_path + ["next"]), "eval") def fire(self, my_locals): if eval(self._matches, {}, my_locals.copy()): return eval(self._next_state, {}, my_locals.copy()) return None class DummyWorld(object): def __init__(self): self._fox_has_tea = False self._fox_is_shapeshifted = False self._fox_is_disguised = False self._fire_started = False self._cannon_destroyed = False self._vase_broken = False self._fish_in_house = False self._torn_kimono = False self._masks_destroyed = False self.npcs = {} def npc_is(self, npc_name, state): return self.npcs[npc_name].state == state def fox_has_tea(self): return self._fox_has_tea def fox_is_shapeshifted(self): return self._fox_is_shapeshifted def fox_is_disguised(self): return self._fox_is_disguised def fire_started(self): return self._fire_started def cannon_destroyed(self): return self._cannon_destroyed def vase_broken(self): return self._vase_broken def fish_in_house(self): return self._fish_in_house def torn_kimono(self): return self._torn_kimono def masks_destroyed(self): return self._masks_destroyed