d13b07c10ea5c8e3c81289c0ad33792639136e13
[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["serums"] = [
29             "smart", "strong", "fast",
30         ]
31         state["vehicles"] = {
32             "current": "walking",
33         }
34         state["level"] = {
35             "name": "level1",
36         }
37         return state
38
39     def _build_roach(self, name, health=5, **kw):
40         roach = {
41             "name": name,
42             "health": health,
43         }
44         roach.update(kw)
45         return roach
46
47     def _apply_set(self, updates):
48         for name, value in updates.items():
49             parts = name.split(".")
50             obj = self._state
51             for p in parts[:-1]:
52                 if isinstance(obj, dict):
53                     obj = obj[p]
54                 elif isinstance(obj, list):
55                     obj = obj[int(p)]
56                 else:
57                     raise KeyError("%r not found in world" % (name,))
58             obj[parts[-1]] = value
59
60     def proxy(self):
61         return WorldDictProxy(self._state)
62
63     def apply_event(self, action, *args, **kw):
64         if action == "set":
65             return self._apply_set(*args, **kw)
66         raise ValueError("Unknown world event action: %r" % (action,))
67
68
69 def _maybe_subproxy(proxy, name, value):
70     """ Return a sub world proxy if appropriate. """
71     if isinstance(value, dict):
72         prefix = "%s%s." % (proxy._prefix, name)
73         return WorldDictProxy(value, _prefix=prefix, _top=proxy._top)
74     elif isinstance(value, list):
75         prefix = "%s%s." % (proxy._prefix, name)
76         return WorldListProxy(value, _prefix=prefix, _top=proxy._top)
77     return value
78
79
80 class WorldBaseProxy:
81     """ Base for world proxies. """
82
83     def __init__(self, state, _prefix='', _top=None):
84         if _top is None:
85             _top = self
86             _events = []
87         else:
88             _events = None
89         self.__dict__.update({
90             "_state": state,
91             "_prefix": _prefix,
92             "_top": _top,
93             "_events": _events,
94         })
95
96     def _record_change(self, fullname, value):
97         self._events.append(WorldEvent("set", {
98             fullname: value
99         }))
100
101     def pop_events(self):
102         events, self._events = self._events, []
103         return events
104
105
106 class WorldDictProxy(WorldBaseProxy):
107     """ World dictionary proxy that records changes and produces events. """
108
109     def items(self):
110         return (
111             (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
112
113     def __setattr__(self, name, value):
114         self._top._record_change("%s%s" % (self._prefix, name), value)
115
116     def __getattr__(self, name):
117         # return None for attributes that don't exist
118         value = self._state.get(name)
119         return _maybe_subproxy(self, name, value)
120
121
122 class WorldListProxy(WorldBaseProxy):
123     """ World list proxy that records changes and produces events. """
124
125     def __setitem__(self, index, value):
126         self._top._record_change("%s%s" % (self._prefix, index), value)
127
128     def __getitem__(self, index):
129         return _maybe_subproxy(self, index, self._state[index])
130
131     def __len__(self):
132         return len(self._state)
133
134     def __bool__(self):
135         return bool(self._state)