-
-
Notifications
You must be signed in to change notification settings - Fork 95
/
Copy pathevents.py
291 lines (222 loc) · 6.87 KB
/
events.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
import logging
import re
from dataclasses import dataclass
from typing import Any
from typing import Collection
from typing import Dict
from typing import Set
from typing import Type
from typing import Union
from ppb import Vector
from ppb.abc import Scene
from ppb.buttons import MouseButton
from ppb.keycodes import KeyCode
__all__ = (
'StartScene',
'EventMixin',
'PreRender',
'Quit',
'Render',
'ReplaceScene',
'SceneContinued',
'ScenePaused',
'SceneStarted',
'SceneStopped',
'StopScene',
'Update',
)
boundaries_finder = re.compile('(.)([A-Z][a-z]+)')
boundaries_finder_2 = re.compile('([a-z0-9])([A-Z])')
def camel_to_snake(txt):
s1 = boundaries_finder.sub(r'\1_\2', txt)
return boundaries_finder_2.sub(r'\1_\2', s1).lower()
class BadEventHandlerException(TypeError):
def __init__(self, instance, method, event):
object_type = type(instance)
event_type = type(event)
o_name = object_type.__name__
e_name = event_type.__name__
article = ['a', 'an'][int(e_name.lower()[0] in "aeiou")]
message = f"""
The signature of {o_name}.{method}() is incorrect:
it should accept {article} {e_name} object and a signal function.
{e_name} is a dataclass that represents an event. Its attributes
tell you about the event.
The signal function is a function you can call that accepts an event instance
as its only parameter. Call it to add an event to the queue. You don't have to
use it, but it is a mandatory argument provided by ppb.
It should look like this:
def {method}({e_name.lower()}_event: {e_name}, signal_function):
(Your code goes here.)
"""
super().__init__(message)
class EventMixin:
def __event__(self, bag, fire_event):
elog = logging.getLogger('game.events')
name = camel_to_snake(type(bag).__name__)
meth_name = 'on_' + name
meth = getattr(self, meth_name, None)
if callable(meth):
try:
elog.debug(f"Calling handler {meth} for {name}")
meth(bag, fire_event)
except TypeError as ex:
from inspect import signature
sig = signature(meth)
try:
sig.bind(bag, fire_event)
except TypeError:
raise BadEventHandlerException(self, meth_name, bag) from ex
else:
raise
# Remember to define scene at the end so the pargs version of __init__() still works
@dataclass
class ButtonPressed:
"""
Fired when a button is pressed
"""
button: MouseButton
position: Vector # Scene position
# TODO: Add frame position
scene: Scene = None
@dataclass
class ButtonReleased:
"""
Fired when a button is released
"""
button: MouseButton
position: Vector # Scene position
# TODO: Add frame position
scene: Scene = None
@dataclass
class StartScene:
"""
Fired to start a new scene.
new_scene can be an instance or a class. If a class, must include kwargs.
If new_scene is an instance kwargs should be empty or None.
Before the previous scene pauses, a ScenePaused event will be fired.
Any events signaled in response will be delivered to the new scene.
After the ScenePaused event and any follow up events have been delivered, a
SceneStarted event will be sent.
Examples:
* `signal(new_scene=StartScene(MyScene(player=player))`
* `signal(new_scene=StartScene, kwargs={"player": player}`
"""
new_scene: Union[Scene, Type[Scene]]
kwargs: Dict[str, Any] = None
scene: Scene = None
@dataclass
class KeyPressed:
key: KeyCode
mods: Set[KeyCode]
scene: Scene = None
@dataclass
class KeyReleased:
key: KeyCode
mods: Set[KeyCode]
scene: Scene = None
@dataclass
class MouseMotion:
"""An event to represent mouse motion."""
position: Vector
screen_position: Vector
delta: Vector
buttons: Collection[MouseButton]
scene: Scene = None
@dataclass
class PreRender:
"""
Fired before rendering.
"""
scene: Scene = None
@dataclass
class Quit:
"""
Fired on an OS Quit event.
You may also fire this event to stop the engine.
"""
scene: Scene = None
@dataclass
class Render:
"""
Fired at render.
"""
scene: Scene = None
@dataclass
class ReplaceScene:
"""
Fired to replace the current scene with a new one.
new_scene can be an instance or a class. If a class, must include kwargs.
If new_scene is an instance kwargs should be empty or None.
Before the previous scene stops, a SceneStopped event will be fired.
Any events signaled in response will be delivered to the new scene.
After the SceneStopped event and any follow up events have been delivered,
a SceneStarted event will be sent.
Examples:
* `signal(new_scene=ReplaceScene(MyScene(player=player))`
* `signal(new_scene=ReplaceScene, kwargs={"player": player}`
"""
new_scene: Union[Scene, Type[Scene]]
kwargs: Dict[str, Any] = None
scene: Scene = None
@dataclass
class SceneContinued:
"""
Fired when a paused scene continues.
This is delivered to a scene as it resumes operation after being paused via
a ScenePaused event.
From the middle of the event lifetime that begins with SceneStarted.
"""
scene: Scene = None
@dataclass
class SceneStarted:
"""
Fired when a scene starts.
This is delivered to a Scene shortly after it starts. The beginning of the
scene lifetime, ended with SceneStopped, paused with ScenePaused, and
resumed from a pause with SceneContinued.
"""
scene: Scene = None
@dataclass
class SceneStopped:
"""
Fired when a scene stops.
This is delivered to a scene and it's objects when a StopScene or
ReplaceScene event is sent to the engine.
The end of the scene lifetime, started with SceneStarted.
"""
scene: Scene = None
@dataclass
class ScenePaused:
"""
Fired when a scene pauses.
This is delivered to a scene about to be paused when a StartScene event is
sent to the engine. When this scene resumes it will receive a
SceneContinued event.
A middle event in the scene lifetime, started with SceneStarted.
"""
scene: Scene = None
@dataclass
class StopScene:
"""
Fired to stop a scene.
Before the scene stops, a SceneStopped event will be fired. Any events
signaled in response will be delivered to the previous scene if it exists.
If there is a paused scene on the stack, a SceneContinued event will be
fired after the responses to the SceneStopped event.
"""
scene: Scene = None
@dataclass
class Idle:
"""
An engine plumbing event to pump timing information to subsystems.
"""
time_delta: float
scene: Scene = None
@dataclass
class Update:
"""
Fired on game tick
"""
time_delta: float
scene: Scene = None