26ee7819007dd06b132525175cb9fd2c84627193
[koperkapel.git] / koperkapel / scenes / base.py
1 """ Scene utilities. """
2
3 import functools
4
5
6 def apply_events(f):
7     """ Decorator that applies events to an engine. """
8     @functools.wraps(f)
9     def wrap(self, *args, **kw):
10         events = f(self, *args, **kw)
11         self._apply_events(events)
12     return wrap
13
14
15 class Engine:
16     """ A holder for game state & scene management.
17         """
18
19     def __init__(self, app, scene, world):
20         self._app = app
21         self._scene = scene
22         self._world = world
23
24     def _apply_events(self, events):
25         if not events:
26             return
27         for ev in events:
28             ev.apply(self)
29
30     def change_scene(self, scene):
31         self._apply_events(self._scene.exit(self._world.proxy()))
32         self._scene = scene
33         self._apply_events(self._scene.enter(self._world.proxy()))
34
35     def change_world(self, *args, **kw):
36         self._world.apply_event(*args, **kw)
37
38     def quit_game(self):
39         from pgzero.game import exit
40         exit()
41
42     def move_screen(self, offset):
43         self._scene.move_screen(offset)
44
45     @apply_events
46     def update(self, dt):
47         return self._scene.update(self._world.proxy(), self, dt)
48
49     def draw(self):
50         self._scene.draw(self._app.screen)
51
52     @apply_events
53     def on_mouse_down(self, pos, button):
54         return self._scene.on_mouse_down(pos, button)
55
56     @apply_events
57     def on_mouse_up(self, pos, button):
58         return self._scene.on_mouse_up(pos, button)
59
60     @apply_events
61     def on_key_down(self, key, mod, unicode):
62         return self._scene.on_key_down(key, mod, unicode)
63
64     @apply_events
65     def on_key_up(self, key, mod):
66         return self._scene.on_key_up(key, mod)
67
68     @apply_events
69     def on_music_end(self):
70         return self._scene.on_music_end()
71
72
73 class Event:
74     """ Base class for events. """
75
76     ENGINE_METHOD = "unknown_event"
77
78     def __init__(self, *args, **kw):
79         self._args = args
80         self._kw = kw
81
82     def apply(self, engine):
83         getattr(engine, self.ENGINE_METHOD)(*self._args, **self._kw)
84
85
86 class ChangeSceneEvent(Event):
87     """ Change to a new scene. """
88
89     ENGINE_METHOD = "change_scene"
90
91
92 class WorldEvent(Event):
93     """ Be a hero. Change the world. """
94
95     ENGINE_METHOD = "change_world"
96
97
98 class QuitEvent(Event):
99     """ Quit the game. """
100
101     ENGINE_METHOD = "quit_game"
102
103
104 class MoveViewportEvent(Event):
105     """ Change to a new scene. """
106
107     ENGINE_METHOD = "move_screen"
108
109
110 class Layer:
111     """ A single layer of actors. """
112
113     def __init__(self, name):
114         self.name = name
115         self.actors = []
116
117     def __iter__(self):
118         return iter(self.actors)
119
120     def __getitem__(self, i):
121         return self.actors[i]
122
123     def __len__(self):
124         return len(self.actors)
125
126     def add(self, actor):
127         self.actors.append(actor)
128         return actor
129
130     def clear(self):
131         self.actors.clear()
132
133     def remove(self, actor):
134         self.actors.remove(actor)
135         return actor
136
137
138 class Actors:
139     """ Layers of actors.
140
141     Actors may be rendered in different layers. Layers with lower levels
142     are rendered lower than layers with higher ones.
143     """
144
145     def __init__(self):
146         self._ordered_layers = []
147         self._layers = {}
148         self.add_layer("default", 0)
149
150     def __getattr__(self, name):
151         return self._layers[name]
152
153     def add_layer(self, name, level):
154         layer = self._layers[name] = Layer(name)
155         self._ordered_layers.append((level, name))
156         self._ordered_layers.sort()
157         return layer
158
159     def add(self, actor, layer="default"):
160         return self._layers[layer].add(actor)
161
162     def remove(self, actor, layer="default"):
163         return self._layers[layer].remove(actor)
164
165     def draw(self, screen):
166         for lvl, name in self._ordered_layers:
167             for actor in self._layers[name]:
168                 actor.draw()  # TODO: allow an option screen to be passed in
169
170
171 class Scene:
172     """ Base class for scenes. """
173
174     def __init__(self):
175         self.actors = Actors()
176         self.viewport = (0, 0)
177
178     def move_screen(self, offset):
179         self.viewport = (self.viewport[0] + offset[0],
180                          self.viewport[1] + offset[1])
181
182     def calc_offset(self, x, y):
183         """ Return a position offset by the viewport. """
184         return x - self.viewport[0], y - self.viewport[1]
185
186     def enter(self, world):
187         pass
188
189     def exit(self, world):
190         pass
191
192     def update(self, world, engine, dt):
193         pass
194
195     def draw(self, screen):
196         screen.clear()
197         self.actors.draw(screen)
198
199     def on_mouse_down(self, pos, button):
200         pass
201
202     def on_mouse_up(self, pos, button):
203         pass
204
205     def on_key_down(self, key, mod, unicode):
206         pass
207
208     def on_key_up(self, key, mod):
209         pass
210
211     def on_music_end(self):
212         pass