One vehicle at a time.
[koperkapel.git] / koperkapel / world.py
index 8a4af23d1407a08c32c02aef57eb9d493deed882..d13b07c10ea5c8e3c81289c0ad33792639136e13 100644 (file)
@@ -1,5 +1,7 @@
 """ World and player state. """
 
+from .scenes.base import WorldEvent
+
 
 class World:
     """ World and player state. """
@@ -18,37 +20,116 @@ class World:
     def _build_initial_state(self):
         state = {}
         state["roaches"] = [
-            self._build_roach("roachel", intelligence=3),
-            self._build_roach("roeginald", strength=3),
-            self._build_roach("roichard", quickness=3),
+            self._build_roach("roachel", smart=True),
+            self._build_roach("roeginald", strong=True),
+            self._build_roach("roichard", fast=True),
+            self._build_roach("roupert"),
+        ]
+        state["serums"] = [
+            "smart", "strong", "fast",
         ]
+        state["vehicles"] = {
+            "current": "walking",
+        }
         state["level"] = {
             "name": "level1",
         }
         return state
 
-    def _build_roach(self, name, **kw):
-        attributes = {
-            "intelligence": 1,
-            "strength": 1,
-            "quickness": 1,
-            "health": 5,
-        }
-        attributes.update(kw)
-        return {
+    def _build_roach(self, name, health=5, **kw):
+        roach = {
             "name": name,
-            "attributes": attributes,
+            "health": health,
         }
+        roach.update(kw)
+        return roach
 
     def _apply_set(self, updates):
         for name, value in updates.items():
             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 None for attributes that don't exist
+        value = self._state.get(name)
+        return _maybe_subproxy(self, name, value)
+
+
+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])
+
+    def __len__(self):
+        return len(self._state)
+
+    def __bool__(self):
+        return bool(self._state)