""" World and player state. """
+from .scenes.base import WorldEvent
+from .roaches import build_roach
+
class World:
""" World and player state. """
@property
def level(self):
- return self._level["level"]
+ return self._state["level"]
@property
def roaches(self):
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),
+ build_roach(self, "roupert"),
+ ]
+ state["serums"] = [
+ "smart", "strong", "fast",
]
+ state["vehicles"] = {
+ "current": "walking",
+ "walking": {
+ "seating": [
+ "roachel", None, "roeginald",
+ None, None, None,
+ ]
+ },
+ "robot": {"seating": []},
+ "roomba": {"seating": []},
+ "quadcopter": {"seating": []},
+ }
state["level"] = {
- "file": "level1.json",
+ "name": "level1",
}
return state
- def _build_roach(self, name, **kw):
- attributes = {
- "intelligence": 1,
- "strength": 1,
- "quickness": 1,
- "health": 5,
- }
- attributes.update(kw)
- return {
- "name": name,
- "attributes": attributes,
- }
+ def _get_obj(self, name):
+ parts = name.split(".")
+ obj = self._state
+ for p in parts[:-1]:
+ if isinstance(obj, dict):
+ obj = obj[p]
+ elif isinstance(obj, list):
+ obj = obj[int(p)]
+ else:
+ raise KeyError("%r not found in world" % (name,))
+ return obj, parts[-1]
+
+ def _apply_set(self, action, updates):
+ for name, value in updates.items():
+ obj, key = self._get_obj(name)
+ obj[key] = value
+
+ def _apply_append(self, action, updates):
+ for name, value in updates.items():
+ obj, key = self._get_obj(name)
+ obj.append(value)
+
+ def _apply_reset(self, action):
+ self._state = self._build_initial_state()
+
+ def _apply_unknown(self, action, *args, **kw):
+ raise ValueError("Unknown world event action: %r" % (action,))
+
+ def proxy(self):
+ return WorldDictProxy(self._state)
+
+ def apply_event(self, action, *args, **kw):
+ handler = getattr(self, "_apply_%s" % (action,))
+ return handler(action, *args, **kw)
+
+
+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, action="set"):
+ self._events.append(WorldEvent(action, {
+ 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 __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)
+
+ def __setitem__(self, name, value):
+ return self.__setattr__(name, value)
+
+ def __getitem__(self, name):
+ return self.__getattr__(name)
+
+ def items(self):
+ return (
+ (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
+
+
+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)
- def apply_event(self, *args, **kw):
- pass
+ def append(self, value):
+ self._top._record_change(self._prefix, value, action="append")