armour
[koperkapel.git] / koperkapel / world.py
1 """ World and player state. """
2
3 from .scenes.base import WorldEvent
4 from .roaches import build_roach
5
6
7 class World:
8     """ World and player state. """
9
10     def __init__(self):
11         self._state = self._build_initial_state()
12
13     @property
14     def level(self):
15         return self._state["level"]
16
17     @property
18     def roaches(self):
19         return self._state["roaches"]
20
21     def _build_initial_state(self):
22         state = {}
23         state["roaches"] = [
24             build_roach(self, "roupert"),
25         ]
26         state["serums"] = []
27         state["vehicles"] = {
28             "current": "walking",
29             "walking": {
30                 "seating": [
31                     "roachel", None, "roeginald",
32                     None, None, None,
33                 ]
34             },
35             "robot": {"seating": []},
36             "roomba": {"seating": []},
37             "quadcopter": {"seating": []},
38         }
39         state["weapons"] = {
40             "current": "spit",
41         }
42         state["level"] = {
43             "name": "level1",
44         }
45         return state
46
47     def _get_obj(self, name):
48         parts = name.split(".")
49         obj = self._state
50         for p in parts[:-1]:
51             if isinstance(obj, dict):
52                 obj = obj[p]
53             elif isinstance(obj, list):
54                 obj = obj[int(p)]
55             else:
56                 raise KeyError("%r not found in world" % (name,))
57         return obj, parts[-1]
58
59     def _apply_set(self, action, updates):
60         for name, value in updates.items():
61             obj, key = self._get_obj(name)
62             obj[key] = value
63
64     def _apply_append(self, action, updates):
65         for name, value in updates.items():
66             obj, key = self._get_obj(name)
67             obj.append(value)
68
69     def _apply_reset(self, action):
70         self._state = self._build_initial_state()
71
72     def _apply_unknown(self, action, *args, **kw):
73         raise ValueError("Unknown world event action: %r" % (action,))
74
75     def proxy(self):
76         return WorldDictProxy(self._state)
77
78     def apply_event(self, action, *args, **kw):
79         handler = getattr(self, "_apply_%s" % (action,))
80         return handler(action, *args, **kw)
81
82
83 def _maybe_subproxy(proxy, name, value):
84     """ Return a sub world proxy if appropriate. """
85     if isinstance(value, dict):
86         prefix = "%s%s." % (proxy._prefix, name)
87         return WorldDictProxy(value, _prefix=prefix, _top=proxy._top)
88     elif isinstance(value, list):
89         prefix = "%s%s." % (proxy._prefix, name)
90         return WorldListProxy(value, _prefix=prefix, _top=proxy._top)
91     return value
92
93
94 class WorldBaseProxy:
95     """ Base for world proxies. """
96
97     def __init__(self, state, _prefix='', _top=None):
98         if _top is None:
99             _top = self
100             _events = []
101         else:
102             _events = None
103         self.__dict__.update({
104             "_state": state,
105             "_prefix": _prefix,
106             "_top": _top,
107             "_events": _events,
108         })
109
110     def _record_change(self, fullname, value, action="set"):
111         self._events.append(WorldEvent(action, {
112             fullname: value
113         }))
114
115     def pop_events(self):
116         events, self._events = self._events, []
117         return events
118
119
120 class WorldDictProxy(WorldBaseProxy):
121     """ World dictionary proxy that records changes and produces events. """
122
123     def __setattr__(self, name, value):
124         self._top._record_change("%s%s" % (self._prefix, name), value)
125
126     def __getattr__(self, name):
127         # return None for attributes that don't exist
128         value = self._state.get(name)
129         return _maybe_subproxy(self, name, value)
130
131     def __setitem__(self, name, value):
132         return self.__setattr__(name, value)
133
134     def __getitem__(self, name):
135         return self.__getattr__(name)
136
137     def items(self):
138         return (
139             (k, _maybe_subproxy(self, k, v)) for k, v in self._state.items())
140
141
142 class WorldListProxy(WorldBaseProxy):
143     """ World list proxy that records changes and produces events. """
144
145     def __setitem__(self, index, value):
146         self._top._record_change("%s%s" % (self._prefix, index), value)
147
148     def __getitem__(self, index):
149         return _maybe_subproxy(self, index, self._state[index])
150
151     def __len__(self):
152         return len(self._state)
153
154     def __bool__(self):
155         return bool(self._state)
156
157     def append(self, value):
158         self._top._record_change(self._prefix, value, action="append")