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