2a094f62b1b2dbbea04f77fc149a5412c24bfe81
[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 doesn't allow blitting to anything other than
169                 # the game scene
170                 screen.blit(actor._surf, actor.topleft)
171
172
173 class Scene:
174     """ Base class for scenes. """
175
176     def __init__(self):
177         self.actors = Actors()
178         self.viewport = (0, 0)
179
180     def move_screen(self, offset):
181         self.viewport = (self.viewport[0] + offset[0],
182                          self.viewport[1] + offset[1])
183
184     def calc_offset(self, x, y):
185         """ Return a position offset by the viewport. """
186         return x - self.viewport[0], y - self.viewport[1]
187
188     def enter(self, world):
189         pass
190
191     def exit(self, world):
192         pass
193
194     def update(self, world, engine, dt):
195         pass
196
197     def draw(self, screen):
198         screen.clear()
199         self.actors.draw(screen)
200
201     def on_mouse_down(self, pos, button):
202         pass
203
204     def on_mouse_up(self, pos, button):
205         pass
206
207     def on_key_down(self, key, mod, unicode):
208         pass
209
210     def on_key_up(self, key, mod):
211         pass
212
213     def on_music_end(self):
214         pass