From 1dde3db82037abbc3a493ea6479cd828794b0bd1 Mon Sep 17 00:00:00 2001 From: Simon Cross Date: Wed, 2 Mar 2016 21:23:08 +0200 Subject: [PATCH] Pass read-only world proxy to scene functions. Changes ideally made only through events. --- koperkapel/scenes/base.py | 6 +-- koperkapel/scenes/level.py | 2 +- koperkapel/scenes/roaches.py | 10 ++--- koperkapel/world.py | 73 +++++++++++++++++++++++++++++++++++- 4 files changed, 81 insertions(+), 10 deletions(-) diff --git a/koperkapel/scenes/base.py b/koperkapel/scenes/base.py index aa30be4..19ba212 100644 --- a/koperkapel/scenes/base.py +++ b/koperkapel/scenes/base.py @@ -29,9 +29,9 @@ class Engine: ev.apply(self) def change_scene(self, scene): - self._apply_events(self._scene.exit(self._world)) + self._apply_events(self._scene.exit(self._world.proxy())) self._scene = scene - self._apply_events(self._scene.enter(self._world)) + self._apply_events(self._scene.enter(self._world.proxy())) def change_world(self, *args, **kw): self._world.apply_event(*args, **kw) @@ -46,7 +46,7 @@ class Engine: @apply_events def update(self, dt): - return self._scene.update(self._world, dt) + return self._scene.update(self._world.proxy(), dt) def draw(self): self._scene.draw(self._app.screen, self._viewport) diff --git a/koperkapel/scenes/level.py b/koperkapel/scenes/level.py index 70f82c5..5c86629 100644 --- a/koperkapel/scenes/level.py +++ b/koperkapel/scenes/level.py @@ -11,7 +11,7 @@ class LevelScene(Scene): """ Level scene. """ def enter(self, world): - self._level_data = levels.load(world.level["name"]) + self._level_data = levels.load(world.level.name) self._tiles = self._level_data['tiles'] self._surface = None self._render() diff --git a/koperkapel/scenes/roaches.py b/koperkapel/scenes/roaches.py index aab4a9a..74f32a9 100644 --- a/koperkapel/scenes/roaches.py +++ b/koperkapel/scenes/roaches.py @@ -14,15 +14,15 @@ class RoachesScene(Scene): def update(self, world, dt): for i, roach in enumerate(world.roaches): - if roach["name"] in self._roach_actors: - actor = self._roach_actors[roach["name"]] + if roach.name in self._roach_actors: + actor = self._roach_actors[roach.name] else: actor = TextButton("%s [%s]" % ( - roach["name"], + roach.name, ", ".join("%s=%s" % kv for kv in sorted( - roach["attributes"].items()), + roach.attributes.items()), ))) - self._roach_actors[roach["name"]] = actor + self._roach_actors[roach.name] = actor self.actors.add(actor) # TODO: remove missing roaches actor.pos = (300, 100 + i * 100) diff --git a/koperkapel/world.py b/koperkapel/world.py index 8a4af23..28f98b3 100644 --- a/koperkapel/world.py +++ b/koperkapel/world.py @@ -1,5 +1,7 @@ """ World and player state. """ +from .scenes.base import WorldEvent + class World: """ World and player state. """ @@ -45,10 +47,79 @@ class World: parts = name.split(".") obj = self._state for p in parts[:-1]: - obj = obj[p] + if isinstance(obj, dict): + obj = obj[p] + elif isinstance(obj, list): + obj = obj[int(p)] + else: + raise KeyError("%r not found in world" % (name,)) obj[parts[-1]] = value + def proxy(self): + return WorldDictProxy(self._state) + def apply_event(self, action, *args, **kw): if action == "set": return self._apply_set(*args, **kw) raise ValueError("Unknown world event action: %r" % (action,)) + + +def _maybe_subproxy(proxy, name, value): + """ Return a sub world proxy if appropriate. """ + if isinstance(value, dict): + prefix = "%s%s." % (proxy._prefix, name) + return WorldDictProxy(value, _prefix=prefix, _top=proxy._top) + elif isinstance(value, list): + prefix = "%s%s." % (proxy._prefix, name) + return WorldListProxy(value, _prefix=prefix, _top=proxy._top) + return value + + +class WorldBaseProxy: + """ Base for world proxies. """ + + def __init__(self, state, _prefix='', _top=None): + if _top is None: + _top = self + _events = [] + else: + _events = None + self.__dict__.update({ + "_state": state, + "_prefix": _prefix, + "_top": _top, + "_events": _events, + }) + + def _record_change(self, fullname, value): + self._events.append(WorldEvent("set", { + fullname: value + })) + + def pop_events(self): + events, self._events = self._events, [] + return events + + +class WorldDictProxy(WorldBaseProxy): + """ World dictionary proxy that records changes and produces events. """ + + def items(self): + return ( + (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items()) + + def __setattr__(self, name, value): + self._top._record_change("%s%s" % (self._prefix, name), value) + + def __getattr__(self, name): + return _maybe_subproxy(self, name, self._state[name]) + + +class WorldListProxy(WorldBaseProxy): + """ World list proxy that records changes and produces events. """ + + def __setitem__(self, index, value): + self._top._record_change("%s%s" % (self._prefix, index), value) + + def __getitem__(self, index): + return _maybe_subproxy(self, index, self._state[index]) -- 2.34.1