7115194c87e57d6cc299eda89850714172615126
[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, action, 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 _apply_reset(self, action):
97         self._state = self._build_initial_state()
98
99     def _apply_unknown(self, action, *args, **kw):
100         raise ValueError("Unknown world event action: %r" % (action,))
101
102     def proxy(self):
103         return WorldDictProxy(self._state)
104
105     def add_new_roach(self):
106         roach_names = [x['name'] for x in self.roaches]
107         for cand in NAMES:
108             if cand not in roach_names:
109                 roach = self._build_roach(cand)
110                 self._state['roaches'].append(roach)
111                 break
112
113     def apply_event(self, action, *args, **kw):
114         handler = getattr(self, "_apply_%s" % (action,))
115         return handler(action, *args, **kw)
116
117
118 def _maybe_subproxy(proxy, name, value):
119     """ Return a sub world proxy if appropriate. """
120     if isinstance(value, dict):
121         prefix = "%s%s." % (proxy._prefix, name)
122         return WorldDictProxy(value, _prefix=prefix, _top=proxy._top)
123     elif isinstance(value, list):
124         prefix = "%s%s." % (proxy._prefix, name)
125         return WorldListProxy(value, _prefix=prefix, _top=proxy._top)
126     return value
127
128
129 class WorldBaseProxy:
130     """ Base for world proxies. """
131
132     def __init__(self, state, _prefix='', _top=None):
133         if _top is None:
134             _top = self
135             _events = []
136         else:
137             _events = None
138         self.__dict__.update({
139             "_state": state,
140             "_prefix": _prefix,
141             "_top": _top,
142             "_events": _events,
143         })
144
145     def _record_change(self, fullname, value):
146         self._events.append(WorldEvent("set", {
147             fullname: value
148         }))
149
150     def pop_events(self):
151         events, self._events = self._events, []
152         return events
153
154
155 class WorldDictProxy(WorldBaseProxy):
156     """ World dictionary proxy that records changes and produces events. """
157
158     def items(self):
159         return (
160             (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
161
162     def __setattr__(self, name, value):
163         self._top._record_change("%s%s" % (self._prefix, name), value)
164
165     def __getattr__(self, name):
166         # return None for attributes that don't exist
167         value = self._state.get(name)
168         return _maybe_subproxy(self, name, value)
169
170     def __setitem__(self, name, value):
171         return self.__setattr__(name, value)
172
173     def __getitem__(self, name):
174         return self.__getattr__(name)
175
176
177 class WorldListProxy(WorldBaseProxy):
178     """ World list proxy that records changes and produces events. """
179
180     def __setitem__(self, index, value):
181         self._top._record_change("%s%s" % (self._prefix, index), value)
182
183     def __getitem__(self, index):
184         return _maybe_subproxy(self, index, self._state[index])
185
186     def __len__(self):
187         return len(self._state)
188
189     def __bool__(self):
190         return bool(self._state)