Allow actors to be placed on different layers.
[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 Actors:
113     """ A list of actors.
114
115     Actors may be rendered in different layers. Layers with lower levels
116     are rendered lower than layers with higher ones.
117     """
118
119     def __init__(self):
120         self._layer_order = []
121         self._layers = {}
122         self.add_layer("default", 0)
123
124     def add_layer(self, name, level):
125         self._layer_order.append((level, name))
126         self._layer_order.sort()
127         self._layers[name] = []
128
129     def add(self, actor, layer="default"):
130         self._layers[layer].append(actor)
131         return actor
132
133     def remove(self, actor, layer="default"):
134         self._layers[layer].remove(actor)
135         return actor
136
137     def draw(self, screen):
138         for lvl, layer in self._layer_order:
139             for actor in self._layers[layer]:
140                 actor.draw()  # TODO: allow an option screen to be passed in
141
142
143 class Scene:
144     """ Base class for scenes. """
145
146     def __init__(self):
147         self.actors = Actors()
148
149     def enter(self, world):
150         pass
151
152     def exit(self, world):
153         pass
154
155     def update(self, world, dt):
156         pass
157
158     def draw(self, screen, viewport):
159         screen.clear()
160         self.actors.draw(screen)
161
162     def on_mouse_down(self, pos, button):
163         pass
164
165     def on_mouse_up(self, pos, button):
166         pass
167
168     def on_key_down(self, key, mod, unicode):
169         pass
170
171     def on_key_up(self, key, mod):
172         pass
173
174     def on_music_end(self):
175         pass