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