Color roaches according to their abilities.
[koperkapel.git] / koperkapel / world.py
1 """ World and player state. """
2
3 from .scenes.base import WorldEvent
4
5
6 class World:
7     """ World and player state. """
8
9     def __init__(self):
10         self._state = self._build_initial_state()
11
12     @property
13     def level(self):
14         return self._state["level"]
15
16     @property
17     def roaches(self):
18         return self._state["roaches"]
19
20     def _build_initial_state(self):
21         state = {}
22         state["roaches"] = [
23             self._build_roach("roachel", smart=True),
24             self._build_roach("roeginald", strong=True),
25             self._build_roach("roichard", fast=True),
26             self._build_roach("roupert"),
27         ]
28         state["vehicles"] = {
29             "current": "walking",
30             "available": ["walking"],
31         }
32         state["level"] = {
33             "name": "level1",
34         }
35         return state
36
37     def _build_roach(self, name, health=5, **kw):
38         roach = {
39             "name": name,
40             "health": health,
41         }
42         roach.update(kw)
43         return roach
44
45     def _apply_set(self, updates):
46         for name, value in updates.items():
47             parts = name.split(".")
48             obj = self._state
49             for p in parts[:-1]:
50                 if isinstance(obj, dict):
51                     obj = obj[p]
52                 elif isinstance(obj, list):
53                     obj = obj[int(p)]
54                 else:
55                     raise KeyError("%r not found in world" % (name,))
56             obj[parts[-1]] = value
57
58     def proxy(self):
59         return WorldDictProxy(self._state)
60
61     def apply_event(self, action, *args, **kw):
62         if action == "set":
63             return self._apply_set(*args, **kw)
64         raise ValueError("Unknown world event action: %r" % (action,))
65
66
67 def _maybe_subproxy(proxy, name, value):
68     """ Return a sub world proxy if appropriate. """
69     if isinstance(value, dict):
70         prefix = "%s%s." % (proxy._prefix, name)
71         return WorldDictProxy(value, _prefix=prefix, _top=proxy._top)
72     elif isinstance(value, list):
73         prefix = "%s%s." % (proxy._prefix, name)
74         return WorldListProxy(value, _prefix=prefix, _top=proxy._top)
75     return value
76
77
78 class WorldBaseProxy:
79     """ Base for world proxies. """
80
81     def __init__(self, state, _prefix='', _top=None):
82         if _top is None:
83             _top = self
84             _events = []
85         else:
86             _events = None
87         self.__dict__.update({
88             "_state": state,
89             "_prefix": _prefix,
90             "_top": _top,
91             "_events": _events,
92         })
93
94     def _record_change(self, fullname, value):
95         self._events.append(WorldEvent("set", {
96             fullname: value
97         }))
98
99     def pop_events(self):
100         events, self._events = self._events, []
101         return events
102
103
104 class WorldDictProxy(WorldBaseProxy):
105     """ World dictionary proxy that records changes and produces events. """
106
107     def items(self):
108         return (
109             (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
110
111     def __setattr__(self, name, value):
112         self._top._record_change("%s%s" % (self._prefix, name), value)
113
114     def __getattr__(self, name):
115         # return None for attributes that don't exist
116         value = self._state.get(name)
117         return _maybe_subproxy(self, name, value)
118
119
120 class WorldListProxy(WorldBaseProxy):
121     """ World list proxy that records changes and produces events. """
122
123     def __setitem__(self, index, value):
124         self._top._record_change("%s%s" % (self._prefix, index), value)
125
126     def __getitem__(self, index):
127         return _maybe_subproxy(self, index, self._state[index])