befc1a0a6b6d22ac51c9a46133a01309315c42ae
[koperkapel.git] / koperkapel / world.py
1 """ World and player state. """
2
3 from .scenes.base import WorldEvent
4
5
6 NAMES =[
7         "roupert",
8         "roachel",
9         "roeginald",
10         "roichard",
11         "rory",
12         "roalph",
13         "roabia",
14         "roafi",
15         "roaman",
16         "roemus",
17         "roadley",
18         "roanaell",
19         "roashwan",
20         "roashid",
21         "roaphael",
22         "roenfield",
23         "roani",
24         "roaya",
25         "roaza",
26         "robekka",
27         "rogan",
28         "roiana",
29         "roberta",
30        ]
31
32
33 class World:
34     """ World and player state. """
35
36     def __init__(self):
37         self._state = self._build_initial_state()
38
39     @property
40     def level(self):
41         return self._state["level"]
42
43     @property
44     def roaches(self):
45         return self._state["roaches"]
46
47     def _build_initial_state(self):
48         state = {}
49         state["roaches"] = [
50             #self._build_roach("roachel", smart=True),
51             #self._build_roach("roeginald", strong=True),
52             #self._build_roach("roichard", fast=True),
53             self._build_roach("roupert"),
54         ]
55         state["serums"] = [
56             "smart", "strong", "fast",
57         ]
58         state["vehicles"] = {
59             "current": "walking",
60             "walking": {
61                 "seating": [
62                     "roachel", None, "roeginald",
63                     None, None, None,
64                 ]
65             },
66             "robot": {"seating": []},
67             "roomba": {"seating": []},
68             "quadcopter": {"seating": []},
69         }
70         state["level"] = {
71             "name": "level1",
72         }
73         return state
74
75     def _build_roach(self, name, health=5, **kw):
76         roach = {
77             "name": name,
78             "health": health,
79         }
80         roach.update(kw)
81         return roach
82
83     def _apply_set(self, updates):
84         for name, value in updates.items():
85             parts = name.split(".")
86             obj = self._state
87             for p in parts[:-1]:
88                 if isinstance(obj, dict):
89                     obj = obj[p]
90                 elif isinstance(obj, list):
91                     obj = obj[int(p)]
92                 else:
93                     raise KeyError("%r not found in world" % (name,))
94             obj[parts[-1]] = value
95
96     def proxy(self):
97         return WorldDictProxy(self._state)
98
99     def add_new_roach(self):
100         roach_names = [x['name'] for x in self.roaches]
101         for cand in NAMES:
102             if cand not in roach_names:
103                 roach = self._build_roach(cand)
104                 self._state['roaches'].append(roach)
105                 break
106
107     def apply_event(self, action, *args, **kw):
108         if action == "set":
109             return self._apply_set(*args, **kw)
110         raise ValueError("Unknown world event action: %r" % (action,))
111
112
113 def _maybe_subproxy(proxy, name, value):
114     """ Return a sub world proxy if appropriate. """
115     if isinstance(value, dict):
116         prefix = "%s%s." % (proxy._prefix, name)
117         return WorldDictProxy(value, _prefix=prefix, _top=proxy._top)
118     elif isinstance(value, list):
119         prefix = "%s%s." % (proxy._prefix, name)
120         return WorldListProxy(value, _prefix=prefix, _top=proxy._top)
121     return value
122
123
124 class WorldBaseProxy:
125     """ Base for world proxies. """
126
127     def __init__(self, state, _prefix='', _top=None):
128         if _top is None:
129             _top = self
130             _events = []
131         else:
132             _events = None
133         self.__dict__.update({
134             "_state": state,
135             "_prefix": _prefix,
136             "_top": _top,
137             "_events": _events,
138         })
139
140     def _record_change(self, fullname, value):
141         self._events.append(WorldEvent("set", {
142             fullname: value
143         }))
144
145     def pop_events(self):
146         events, self._events = self._events, []
147         return events
148
149
150 class WorldDictProxy(WorldBaseProxy):
151     """ World dictionary proxy that records changes and produces events. """
152
153     def items(self):
154         return (
155             (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
156
157     def __setattr__(self, name, value):
158         self._top._record_change("%s%s" % (self._prefix, name), value)
159
160     def __getattr__(self, name):
161         # return None for attributes that don't exist
162         value = self._state.get(name)
163         return _maybe_subproxy(self, name, value)
164
165     def __setitem__(self, name, value):
166         return self.__setattr__(name, value)
167
168     def __getitem__(self, name):
169         return self.__getattr__(name)
170
171
172 class WorldListProxy(WorldBaseProxy):
173     """ World list proxy that records changes and produces events. """
174
175     def __setitem__(self, index, value):
176         self._top._record_change("%s%s" % (self._prefix, index), value)
177
178     def __getitem__(self, index):
179         return _maybe_subproxy(self, index, self._state[index])
180
181     def __len__(self):
182         return len(self._state)
183
184     def __bool__(self):
185         return bool(self._state)