Merge branch 'master' of ctpug.org.za:koperkapel
[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 add(self, actor):
120         self.actors.append(actor)
121         return actor
122
123     def remove(self, actor):
124         self.actors.remove(actor)
125         return actor
126
127
128 class Actors:
129     """ Layers of actors.
130
131     Actors may be rendered in different layers. Layers with lower levels
132     are rendered lower than layers with higher ones.
133     """
134
135     def __init__(self):
136         self._ordered_layers = []
137         self._layers = {}
138         self.add_layer("default", 0)
139
140     def __getattr__(self, name):
141         return self._layers[name]
142
143     def add_layer(self, name, level):
144         layer = self._layers[name] = Layer(name)
145         self._ordered_layers.append((level, name))
146         self._ordered_layers.sort()
147         return layer
148
149     def add(self, actor, layer="default"):
150         return self._layers[layer].add(actor)
151
152     def remove(self, actor, layer="default"):
153         return self._layers[layer].remove(actor)
154
155     def draw(self, screen):
156         for lvl, name in self._ordered_layers:
157             for actor in self._layers[name].actors:
158                 actor.draw()  # TODO: allow an option screen to be passed in
159
160
161 class Scene:
162     """ Base class for scenes. """
163
164     def __init__(self):
165         self.actors = Actors()
166
167     def enter(self, world):
168         pass
169
170     def exit(self, world):
171         pass
172
173     def update(self, world, dt):
174         pass
175
176     def draw(self, screen, viewport):
177         screen.clear()
178         self.actors.draw(screen)
179
180     def on_mouse_down(self, pos, button):
181         pass
182
183     def on_mouse_up(self, pos, button):
184         pass
185
186     def on_key_down(self, key, mod, unicode):
187         pass
188
189     def on_key_up(self, key, mod):
190         pass
191
192     def on_music_end(self):
193         pass