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