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