Pass read-only world proxy to scene functions. Changes ideally made only through...
authorSimon Cross <hodgestar@gmail.com>
Wed, 2 Mar 2016 19:23:08 +0000 (21:23 +0200)
committerSimon Cross <hodgestar@gmail.com>
Wed, 2 Mar 2016 19:23:08 +0000 (21:23 +0200)
koperkapel/scenes/base.py
koperkapel/scenes/level.py
koperkapel/scenes/roaches.py
koperkapel/world.py

index aa30be492baa518327975e8d5a96377dc88e3935..19ba212fce407eb91047652b5a114ceed719284c 100644 (file)
@@ -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)
index 70f82c59d338133f9bf7173412a54e199be3714a..5c86629078551df0d69459b20d61b77b08093ffa 100644 (file)
@@ -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()
index aab4a9a2be480a310f5e820a8abf2beb77f705bd..74f32a96f3344acacddb3406633e9cce62edbaab 100644 (file)
@@ -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)
index 8a4af23d1407a08c32c02aef57eb9d493deed882..28f98b3da91ccad0365c2eb45ef0c12b3373ac23 100644 (file)
@@ -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])