mima-engine 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mima-engine might be problematic. Click here for more details.

Files changed (114) hide show
  1. mima/__init__.py +1 -0
  2. mima/backend/__init__.py +1 -0
  3. mima/backend/pygame_assets.py +345 -0
  4. mima/backend/pygame_audio.py +75 -0
  5. mima/backend/pygame_backend.py +399 -0
  6. mima/backend/pygame_events.py +430 -0
  7. mima/collision.py +237 -0
  8. mima/engine.py +197 -0
  9. mima/maps/__init__.py +0 -0
  10. mima/maps/template.py +41 -0
  11. mima/maps/tile.py +20 -0
  12. mima/maps/tile_animation.py +7 -0
  13. mima/maps/tile_info.py +10 -0
  14. mima/maps/tile_layer.py +52 -0
  15. mima/maps/tiled/__init__.py +0 -0
  16. mima/maps/tiled/tiled_layer.py +48 -0
  17. mima/maps/tiled/tiled_map.py +95 -0
  18. mima/maps/tiled/tiled_object.py +79 -0
  19. mima/maps/tiled/tiled_objectgroup.py +25 -0
  20. mima/maps/tiled/tiled_template.py +49 -0
  21. mima/maps/tiled/tiled_tile.py +90 -0
  22. mima/maps/tiled/tiled_tileset.py +45 -0
  23. mima/maps/tilemap.py +159 -0
  24. mima/maps/tileset.py +32 -0
  25. mima/maps/tileset_info.py +9 -0
  26. mima/maps/transition_map.py +148 -0
  27. mima/objects/__init__.py +0 -0
  28. mima/objects/animated_sprite.py +198 -0
  29. mima/objects/attribute_effect.py +26 -0
  30. mima/objects/attributes.py +123 -0
  31. mima/objects/creature.py +332 -0
  32. mima/objects/dynamic.py +182 -0
  33. mima/objects/effects/__init__.py +0 -0
  34. mima/objects/effects/colorize_screen.py +36 -0
  35. mima/objects/effects/light.py +107 -0
  36. mima/objects/effects/walking_on_grass.py +38 -0
  37. mima/objects/effects/walking_on_water.py +41 -0
  38. mima/objects/loader.py +103 -0
  39. mima/objects/projectile.py +86 -0
  40. mima/objects/sprite.py +110 -0
  41. mima/objects/world/__init__.py +0 -0
  42. mima/objects/world/color_gate.py +68 -0
  43. mima/objects/world/color_switch.py +105 -0
  44. mima/objects/world/container.py +171 -0
  45. mima/objects/world/floor_switch.py +111 -0
  46. mima/objects/world/gate.py +174 -0
  47. mima/objects/world/light_source.py +124 -0
  48. mima/objects/world/logic_gate.py +163 -0
  49. mima/objects/world/movable.py +338 -0
  50. mima/objects/world/oneway.py +168 -0
  51. mima/objects/world/pickup.py +88 -0
  52. mima/objects/world/switch.py +165 -0
  53. mima/objects/world/teleport.py +288 -0
  54. mima/scene_engine.py +79 -0
  55. mima/scripts/__init__.py +2 -0
  56. mima/scripts/command.py +24 -0
  57. mima/scripts/commands/__init__.py +0 -0
  58. mima/scripts/commands/add_quest.py +19 -0
  59. mima/scripts/commands/change_map.py +15 -0
  60. mima/scripts/commands/close_dialog.py +8 -0
  61. mima/scripts/commands/give_item.py +24 -0
  62. mima/scripts/commands/give_resource.py +51 -0
  63. mima/scripts/commands/move_map.py +152 -0
  64. mima/scripts/commands/move_to.py +49 -0
  65. mima/scripts/commands/oneway_move.py +57 -0
  66. mima/scripts/commands/parallel.py +53 -0
  67. mima/scripts/commands/play_sound.py +13 -0
  68. mima/scripts/commands/present_item.py +51 -0
  69. mima/scripts/commands/progress_quest.py +12 -0
  70. mima/scripts/commands/quit_game.py +8 -0
  71. mima/scripts/commands/save_game.py +13 -0
  72. mima/scripts/commands/screen_fade.py +65 -0
  73. mima/scripts/commands/serial.py +46 -0
  74. mima/scripts/commands/set_facing_direction.py +21 -0
  75. mima/scripts/commands/set_spawn_map.py +14 -0
  76. mima/scripts/commands/show_choices.py +43 -0
  77. mima/scripts/commands/show_dialog.py +11 -0
  78. mima/scripts/commands/take_coins.py +23 -0
  79. mima/scripts/script_processor.py +40 -0
  80. mima/states/__init__.py +0 -0
  81. mima/states/game_state.py +162 -0
  82. mima/states/quest.py +72 -0
  83. mima/types/__init__.py +0 -0
  84. mima/types/alignment.py +7 -0
  85. mima/types/blend.py +8 -0
  86. mima/types/damage.py +42 -0
  87. mima/types/direction.py +44 -0
  88. mima/types/gate_color.py +7 -0
  89. mima/types/graphic_state.py +22 -0
  90. mima/types/keys.py +16 -0
  91. mima/types/mode.py +15 -0
  92. mima/types/nature.py +12 -0
  93. mima/types/object.py +22 -0
  94. mima/types/start.py +7 -0
  95. mima/types/terrain.py +9 -0
  96. mima/types/weapon_slot.py +6 -0
  97. mima/usables/__init__.py +0 -0
  98. mima/usables/item.py +31 -0
  99. mima/usables/weapon.py +48 -0
  100. mima/util/__init__.py +1 -0
  101. mima/util/colors.py +45 -0
  102. mima/util/constants.py +47 -0
  103. mima/util/functions.py +13 -0
  104. mima/util/input_defaults.py +49 -0
  105. mima/util/logging.py +51 -0
  106. mima/util/property.py +8 -0
  107. mima/util/runtime_config.py +133 -0
  108. mima/view/__init__.py +0 -0
  109. mima/view/camera.py +51 -0
  110. mima/view/scene.py +350 -0
  111. mima_engine-0.1.0.dist-info/METADATA +14 -0
  112. mima_engine-0.1.0.dist-info/RECORD +114 -0
  113. mima_engine-0.1.0.dist-info/WHEEL +5 -0
  114. mima_engine-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,430 @@
1
+ import logging
2
+ import time
3
+ from typing import Dict, List, Union
4
+
5
+ import pygame
6
+
7
+ from ..types.keys import Key as K
8
+ from ..util.constants import AXIS_ACTIVATION, AXIS_DEADZONE, DOUBLE_TAP_SPEED
9
+ from ..util.input_defaults import (
10
+ BUTTONS,
11
+ DEFAULT_JOYSTICK_MAP,
12
+ DEFAULT_KEYBOARD_MAP,
13
+ )
14
+
15
+ LOG = logging.getLogger(__name__)
16
+
17
+ KEYBOARD_EVENTS = [pygame.KEYUP, pygame.KEYDOWN]
18
+ JOYSTICK_EVENTS = [
19
+ pygame.JOYDEVICEADDED,
20
+ pygame.JOYDEVICEREMOVED,
21
+ pygame.JOYBUTTONUP,
22
+ pygame.JOYBUTTONDOWN,
23
+ pygame.JOYAXISMOTION,
24
+ pygame.JOYHATMOTION,
25
+ ]
26
+ TOUCH_EVENTS = [pygame.FINGERUP, pygame.FINGERDOWN, pygame.FINGERMOTION]
27
+ SDL2_BACKGROUND_EVENTS = [259, 260, 261, 262]
28
+
29
+
30
+ class PygameUserInput:
31
+ """A class that manages keys and key events."""
32
+
33
+ def __init__(
34
+ self,
35
+ key_map: Dict[K, List[str]] = DEFAULT_KEYBOARD_MAP,
36
+ joystick_map: Dict[K, List[Union[str, int]]] = DEFAULT_JOYSTICK_MAP,
37
+ platform: str = "PC",
38
+ ):
39
+ self._last_keys: Dict[K, bool] = {}
40
+ self._new_keys: Dict[K, bool] = {but: False for but in BUTTONS}
41
+ self._left_finger_tap_pos: pygame.Vector2 = pygame.Vector2(0, 0)
42
+ self._right_finger_tap_pos: pygame.Vector2 = pygame.Vector2(0, 0)
43
+ self._left_finger_pos: pygame.Vector2 = pygame.Vector2(0, 0)
44
+ self._right_finger_pos: pygame.Vector2 = pygame.Vector2(0, 0)
45
+ self._last_left_tap: float = 0.0
46
+ self._last_right_tap: float = 0.0
47
+ self._last_left_motion: float = 0.0
48
+ self._last_right_motion: float = 0.0
49
+ self._key_map: Dict[K, int] = {}
50
+ self.vd = pygame.Vector2(0, 0)
51
+ self.joystick_input_enabled: bool = True
52
+ if platform == "android":
53
+ # Disable motion control
54
+ # FIXME allow external controllers
55
+ self.joystick_input_enabled = False
56
+ # self._fingers: List[pygame.Vector2] = []
57
+
58
+ # if key_map is None:
59
+ # self._key_map = dict()
60
+ # for key, vals in RuntimeConfig().keymap.items():
61
+ # self._key_map[key] = list()
62
+ # for val in vals:
63
+ # self._key_map[key].append(getattr(pygame, f"K_{val}"))
64
+ # else:
65
+ for key, vals in key_map.items():
66
+ self._key_map[key] = []
67
+ for val in vals:
68
+ self._key_map[key].append(getattr(pygame, f"K_{val}"))
69
+
70
+ # self._key_map = key_map
71
+ # if joystick_map is None:
72
+ # self._joystick_map = DEFAULT_JOYSTICK_MAP
73
+ # else:
74
+ self._joystick_map = joystick_map
75
+
76
+ self.joystick = None
77
+ self._init_joystick()
78
+ self.width = 0
79
+ self.height = 0
80
+
81
+ def reset(self):
82
+ self._last_keys = self._new_keys.copy()
83
+
84
+ def process(self, event):
85
+ if event.type in KEYBOARD_EVENTS:
86
+ self._handle_keyboard(event)
87
+
88
+ if event.type in JOYSTICK_EVENTS and self.joystick_input_enabled:
89
+ self._handle_joystick(event)
90
+
91
+ if event.type in TOUCH_EVENTS:
92
+ self._handle_touch(event)
93
+
94
+ def _handle_keyboard(self, event):
95
+ if event.type == pygame.KEYDOWN:
96
+ for but, keys in self._key_map.items():
97
+ if event.key in keys:
98
+ self.set_key(but)
99
+
100
+ if event.type == pygame.KEYUP:
101
+ for but, keys in self._key_map.items():
102
+ if event.key in keys:
103
+ self.unset_key(but)
104
+
105
+ def _handle_joystick(self, event):
106
+ if event.type == pygame.JOYDEVICEREMOVED:
107
+ self.joystick = None
108
+ LOG.info("Gamepad unplugged.")
109
+
110
+ if event.type == pygame.JOYDEVICEADDED:
111
+ self._init_joystick()
112
+ LOG.info(
113
+ "Detected new gamepad device %s.", self.joystick.get_name()
114
+ )
115
+
116
+ if event.type == pygame.JOYBUTTONDOWN:
117
+ if event.joy != 0:
118
+ return
119
+ for but, keys in self._joystick_map.items():
120
+ if event.button in keys:
121
+ self.set_key(but)
122
+
123
+ if event.type == pygame.JOYBUTTONUP:
124
+ if event.joy != 0:
125
+ return
126
+ for but, keys in self._joystick_map.items():
127
+ if event.button in keys:
128
+ self.unset_key(but)
129
+ if event.type == pygame.JOYHATMOTION:
130
+ if event.joy != 0 or event.hat != 0:
131
+ return
132
+
133
+ if event.value[0] == 0:
134
+ self.unset_key(K.LEFT)
135
+ self.unset_key(K.RIGHT)
136
+ elif event.value[0] == -1:
137
+ self.set_key(K.LEFT)
138
+ self.unset_key(K.RIGHT)
139
+ else:
140
+ self.unset_key(K.LEFT)
141
+ self.set_key(K.RIGHT)
142
+
143
+ if event.value[1] == 0:
144
+ self.unset_key(K.UP)
145
+ self.unset_key(K.DOWN)
146
+ elif event.value[1] == 1:
147
+ self.set_key(K.UP)
148
+ self.unset_key(K.DOWN)
149
+ else:
150
+ self.unset_key(K.UP)
151
+ self.set_key(K.DOWN)
152
+ if event.type == pygame.JOYAXISMOTION:
153
+ if event.joy != 0:
154
+ return
155
+ if event.axis == 0:
156
+ if event.value < -AXIS_ACTIVATION:
157
+ self.set_key(K.LEFT)
158
+ self.unset_key(K.RIGHT)
159
+ elif event.value > AXIS_ACTIVATION:
160
+ self.unset_key(K.LEFT)
161
+ self.set_key(K.RIGHT)
162
+ elif abs(event.value) < AXIS_DEADZONE:
163
+ self.unset_key(K.LEFT)
164
+ self.unset_key(K.RIGHT)
165
+ else:
166
+ pass
167
+ if event.axis == 1:
168
+ if event.value < -AXIS_ACTIVATION:
169
+ self.set_key(K.UP)
170
+ self.unset_key(K.DOWN)
171
+ elif event.value > AXIS_ACTIVATION:
172
+ self.unset_key(K.UP)
173
+ self.set_key(K.DOWN)
174
+ elif abs(event.value) < AXIS_DEADZONE:
175
+ self.unset_key(K.UP)
176
+ self.unset_key(K.DOWN)
177
+
178
+ def _handle_touch(self, event):
179
+ finger_pos = pygame.Vector2(
180
+ event.x * self.width, event.y * self.height
181
+ )
182
+
183
+ if event.type == pygame.FINGERDOWN:
184
+ tap = time.time()
185
+ if event.x < 0.0625 and event.y < 0.1111:
186
+ self.set_key(K.R)
187
+ elif event.x < 0.5:
188
+ # print(f"Left Finger Down: {finger_pos}")
189
+ self._left_finger_tap_pos = finger_pos
190
+
191
+ if tap - self._last_left_tap < DOUBLE_TAP_SPEED:
192
+ # print("Left Double Tap")
193
+ self.set_key(K.SELECT)
194
+ self._last_left_tap = tap
195
+ # self._left_finger_pos.x = event.x
196
+ # self._left_finger_pos.y = event.y
197
+
198
+ # if tap - self._last_left_tap < 0.2:
199
+ # print("Left Double Tap")
200
+ # # self._set_key(K.START)
201
+ # # self._unset_key(K.RIGHT)
202
+ # # self._unset_key(K.LEFT)
203
+ # # self._unset_key(K.UP)
204
+ # # self._unset_key(K.DOWN)
205
+ else:
206
+ self._right_finger_tap_pos = finger_pos
207
+
208
+ # if tap - self._last_right_tap < DOUBLE_TAP_SPEED:
209
+ # # print("Right Double Tap")
210
+ # self.set_key(K.SELECT)
211
+ self._last_right_tap = tap
212
+ if event.y < 0.3:
213
+ self.set_key(K.START)
214
+ elif event.x < 0.75:
215
+ self.set_key(K.B)
216
+ else:
217
+ self.set_key(K.A)
218
+ # self._right_finger_pos.x = event.x
219
+ # self._right_finger_pos.y = event.y
220
+ # if tap - self._last_right_tap < 0.2:
221
+ # print("Right Double Tap")
222
+
223
+ if event.type == pygame.FINGERUP:
224
+ # release = time.time()
225
+ # finger_dist = (finger_pos - self._left_finger_tap_pos).length()
226
+
227
+ if event.x < 0.5:
228
+ # print(f"Left Finger Up: {finger_pos}")
229
+ # if (
230
+ # SINGLE_TAP_MIN
231
+ # < release - self._last_left_tap
232
+ # < SINGLE_TAP_MAX
233
+ # ) and finger_dist < 2.5:
234
+ # print("Left Single Tap")
235
+ # # self.set_key(K.START)
236
+
237
+ self.unset_key(K.SELECT)
238
+ self.unset_key(K.RIGHT)
239
+ self.unset_key(K.LEFT)
240
+ self.unset_key(K.UP)
241
+ self.unset_key(K.DOWN)
242
+ self.unset_key(K.R)
243
+ # print(
244
+ # f"Left Finger moved {finger_dist} "
245
+ # f"({release - self._last_left_tap} s)"
246
+ # )
247
+ else:
248
+ self.unset_key(K.START)
249
+ self.unset_key(K.A)
250
+ self.unset_key(K.B)
251
+ self.unset_key(K.Y)
252
+ self.unset_key(K.X)
253
+ self.unset_key(K.R)
254
+ # print(f"Right Finger Up: {finger_pos}")
255
+ # if (
256
+ # SINGLE_TAP_MIN
257
+ # < release - self._last_right_tap
258
+ # < SINGLE_TAP_MAX
259
+ # ) and finger_dist < 2.5:
260
+ # print("Right Single Tap")
261
+
262
+ # print(
263
+ # f"Left Finger moved {finger_dist} "
264
+ # f"({release - self._last_left_tap} s)"
265
+ # )
266
+ #
267
+ # if event.x < 0.5:
268
+ # if 0.1 < release - self._last_left_tap < 0.25:
269
+ # print("Left Single Tap")
270
+
271
+ # self._left_finger_pos.x = 0
272
+ # self._left_finger_pos.y = 0
273
+ # self._unset_key(K.DOWN)
274
+ # self._unset_key(K.LEFT)
275
+ # self._unset_key(K.UP)
276
+ # self._unset_key(K.RIGHT)
277
+ # self._unset_key(K.START)
278
+ # else:
279
+ # if 0.1 < release - self._last_right_tap < 0.25:
280
+ # print("Right Single Tap")
281
+
282
+ # self._unset_key(K.A)
283
+ # self._unset_key(K.B)
284
+ if event.type == pygame.FINGERMOTION:
285
+ if event.x < 0.5:
286
+ vd = finger_pos - self._left_finger_tap_pos
287
+ self.unset_key(K.RIGHT)
288
+ self.unset_key(K.LEFT)
289
+ self.unset_key(K.UP)
290
+ self.unset_key(K.DOWN)
291
+ if abs(vd.x) > 2 * abs(vd.y):
292
+ # Horizontal
293
+ if vd.x > 5.0:
294
+ self.set_key(K.RIGHT)
295
+ self.unset_key(K.LEFT)
296
+ self.unset_key(K.UP)
297
+ self.unset_key(K.DOWN)
298
+ elif vd.x < -5.0:
299
+ self.set_key(K.LEFT)
300
+ self.unset_key(K.RIGHT)
301
+ self.unset_key(K.UP)
302
+ self.unset_key(K.DOWN)
303
+ elif abs(vd.x) * 2 < abs(vd.y):
304
+ # Vertical
305
+ if vd.y > 5.0:
306
+ self.unset_key(K.RIGHT)
307
+ self.unset_key(K.LEFT)
308
+ self.unset_key(K.UP)
309
+ self.set_key(K.DOWN)
310
+ elif vd.y < -5.0:
311
+ self.unset_key(K.LEFT)
312
+ self.unset_key(K.RIGHT)
313
+ self.set_key(K.UP)
314
+ self.unset_key(K.DOWN)
315
+ elif abs(vd.x) * 1.05 > abs(vd.y) or abs(vd.x) < 1.05 * abs(
316
+ vd.y
317
+ ):
318
+ if vd.x < 0:
319
+ self.set_key(K.LEFT)
320
+ elif vd.x > 0:
321
+ self.set_key(K.RIGHT)
322
+ if vd.y < 0:
323
+ self.set_key(K.UP)
324
+ elif vd.y > 0:
325
+ self.set_key(K.DOWN)
326
+ # else:
327
+ # vd = finger_pos - self._right_finger_tap_pos
328
+ # self.unset_key(K.A)
329
+ # self.unset_key(K.B)
330
+ # self.unset_key(K.Y)
331
+ # self.unset_key(K.X)
332
+ # if abs(vd.x) > 2 * abs(vd.y):
333
+ # # Horizontal
334
+ # if vd.x > 5.0:
335
+ # self.set_key(K.Y)
336
+ # elif vd.x < -5.0:
337
+ # self.set_key(K.B)
338
+ # elif abs(vd.x) * 2 < abs(vd.y):
339
+ # # Vertical
340
+ # if vd.y > 5.0:
341
+ # self.set_key(K.A)
342
+ # elif vd.y < -5.0:
343
+ # self.set_key(K.X)
344
+
345
+ self.vd = vd
346
+
347
+ def _handle_mouse(self, event):
348
+ # if event.type == pygame.MOUSEBUTTONDOWN:
349
+ # if 0 <= event.pos[0] < 16 and 80 <= event.pos[1] < 96:
350
+ # self._unset_key(K.RIGHT)
351
+ # self._set_key(K.LEFT)
352
+ # self._unset_key(K.UP)
353
+ # self._unset_key(K.DOWN)
354
+ # elif 0 <= event.pos[0] < 16 and 64 <= event.pos[1] < 80:
355
+ # self._unset_key(K.RIGHT)
356
+ # self._set_key(K.LEFT)
357
+ # self._set_key(K.UP)
358
+ # self._unset_key(K.DOWN)
359
+ # elif 16 <= event.pos[0] < 32 and 64 <= event.pos[1] < 80:
360
+ # self._unset_key(K.RIGHT)
361
+ # self._unset_key(K.LEFT)
362
+ # self._set_key(K.UP)
363
+ # self._unset_key(K.DOWN)
364
+ # elif 32 <= event.pos[0] < 48 and 64 <= event.pos[1] < 80:
365
+ # self._set_key(K.RIGHT)
366
+ # self._unset_key(K.LEFT)
367
+ # self._set_key(K.UP)
368
+ # self._unset_key(K.DOWN)
369
+ # elif 32 <= event.pos[0] < 48 and 80 <= event.pos[1] < 96:
370
+ # self._set_key(K.RIGHT)
371
+ # self._unset_key(K.LEFT)
372
+ # self._unset_key(K.UP)
373
+ # self._unset_key(K.DOWN)
374
+ # elif 32 <= event.pos[0] < 48 and 96 <= event.pos[1] < 112:
375
+ # self._set_key(K.RIGHT)
376
+ # self._unset_key(K.LEFT)
377
+ # self._unset_key(K.UP)
378
+ # self._set_key(K.DOWN)
379
+ # elif 16 <= event.pos[0] < 32 and 96 <= event.pos[1] < 112:
380
+ # self._unset_key(K.RIGHT)
381
+ # self._unset_key(K.LEFT)
382
+ # self._unset_key(K.UP)
383
+ # self._set_key(K.DOWN)
384
+ # elif 0 <= event.pos[0] < 16 and 96 <= event.pos[1] < 112:
385
+ # self._unset_key(K.RIGHT)
386
+ # self._set_key(K.LEFT)
387
+ # self._unset_key(K.UP)
388
+ # self._set_key(K.DOWN)
389
+ # if 112 <= event.pos[0] < 144 and 0 <= event.pos[1] < 32:
390
+ # self._set_key(K.START)
391
+ # self._unset_key(K.RIGHT)
392
+ # self._unset_key(K.LEFT)
393
+ # self._unset_key(K.UP)
394
+ # self._unset_key(K.DOWN)
395
+ # if 240 <= event.pos[0] < 256 and 80 <= event.pos[1] < 112:
396
+ # self._set_key(K.A)
397
+ # self._unset_key(K.RIGHT)
398
+ # self._unset_key(K.LEFT)
399
+ # self._unset_key(K.UP)
400
+ # self._unset_key(K.DOWN)
401
+
402
+ # if event.type == pygame.MOUSEBUTTONUP:
403
+ # self._unset_key(K.DOWN)
404
+ # self._unset_key(K.LEFT)
405
+ # self._unset_key(K.UP)
406
+ # self._unset_key(K.RIGHT)
407
+ # self._unset_key(K.START)
408
+ # self._unset_key(K.A)
409
+ pass
410
+
411
+ def _init_joystick(self):
412
+ if pygame.joystick.get_count() > 0:
413
+ self.joystick = pygame.joystick.Joystick(0)
414
+ self.joystick.init()
415
+ LOG.info("Initialized Joystick %s.", self.joystick.get_name())
416
+
417
+ def set_key(self, button: K):
418
+ self._new_keys[button] = True
419
+
420
+ def unset_key(self, button: K):
421
+ self._new_keys[button] = False
422
+
423
+ def new_key_press(self, button: K):
424
+ return self._new_keys[button] and not self._last_keys[button]
425
+
426
+ def key_held(self, button: K):
427
+ return self._new_keys[button]
428
+
429
+ def new_key_release(self, button: K):
430
+ return self._last_keys[button] and not self._new_keys[button]
mima/collision.py ADDED
@@ -0,0 +1,237 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Tuple
4
+
5
+ from .maps.tilemap import Tilemap
6
+ from .objects.dynamic import Dynamic
7
+ from .types.direction import Direction
8
+ from .types.graphic_state import GraphicState, Until
9
+ from .types.nature import Nature
10
+ from .types.object import ObjectType
11
+
12
+ if TYPE_CHECKING:
13
+ from .engine import MimaEngine
14
+ from .view.scene import Scene
15
+
16
+
17
+ def check_object_to_map_collision(
18
+ elapsed_time: float,
19
+ obj: Dynamic,
20
+ tilemap: Tilemap,
21
+ new_px: float,
22
+ new_py: float,
23
+ ) -> Tuple[float, float]:
24
+
25
+ left = new_px + obj.hitbox_px
26
+ right = left + obj.hitbox_width
27
+ top = obj.py + obj.hitbox_py
28
+ bottom = top + obj.hitbox_height
29
+
30
+ collided_with_map = False
31
+ if obj.solid_vs_map:
32
+ if collision_with_map(tilemap, left, right, top, bottom):
33
+ # On rare occasions, the object might be pushed towards
34
+ # the wall, i.e. old and new pos are equal
35
+ # Decide depending on the decimal part of the position
36
+ # where to push the object
37
+ if new_px == obj.px:
38
+ decimal_dif = new_px - int(new_px)
39
+ if abs(decimal_dif) > 0.5:
40
+ new_px += 0.0001
41
+ else:
42
+ new_px -= 0.0001
43
+
44
+ # Did the object move from right to left?
45
+ if new_px < obj.px:
46
+ new_px += int(left) + 1.0 - left
47
+ else:
48
+ new_px -= right - int(right) + 0.001
49
+
50
+ obj.vx = 0
51
+ collided_with_map = True
52
+ if (
53
+ obj.facing_direction
54
+ in [
55
+ Direction.WEST,
56
+ Direction.EAST,
57
+ ]
58
+ and obj.can_push
59
+ ):
60
+ obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
61
+
62
+ left = new_px + obj.hitbox_px
63
+ right = left + obj.hitbox_width
64
+ top = new_py + obj.hitbox_py
65
+ bottom = top + obj.hitbox_height
66
+
67
+ if collision_with_map(tilemap, left, right, top, bottom):
68
+ # See comment above
69
+ if new_py == obj.py:
70
+ decimal_dif = new_py - int(new_py)
71
+ if abs(decimal_dif) > 0.5:
72
+ new_py += 0.0001
73
+ else:
74
+ new_py -= 0.0001
75
+
76
+ if new_py < obj.py:
77
+ new_py += int(top) + 1.0 - top
78
+ else:
79
+ new_py -= bottom - int(bottom) + 0.001
80
+
81
+ obj.vy = 0
82
+ collided_with_map = True
83
+ if (
84
+ obj.facing_direction
85
+ in [
86
+ Direction.NORTH,
87
+ Direction.SOUTH,
88
+ ]
89
+ and obj.can_push
90
+ ):
91
+ obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
92
+
93
+ if obj.type == ObjectType.PROJECTILE and collided_with_map:
94
+ obj.kill()
95
+
96
+ return new_px, new_py
97
+
98
+
99
+ def collision_with_map(
100
+ tilemap: Tilemap,
101
+ left: float,
102
+ right: float,
103
+ top: float,
104
+ bottom: float,
105
+ layer: int = 0,
106
+ ) -> bool:
107
+ if tilemap.is_solid(left, top, layer):
108
+ return True
109
+ if tilemap.is_solid(left, bottom, layer):
110
+ return True
111
+ if tilemap.is_solid(right, top, layer):
112
+ return True
113
+ if tilemap.is_solid(right, bottom, layer):
114
+ return True
115
+
116
+ return False
117
+
118
+
119
+ def check_object_to_object_collision(
120
+ engine: MimaEngine,
121
+ scene: Scene,
122
+ obj: Dynamic,
123
+ new_px: float,
124
+ new_py: float,
125
+ other: Dynamic,
126
+ ) -> Tuple[float, float]:
127
+ pass
128
+
129
+ obj_left = new_px + obj.hitbox_px
130
+ obj_right = obj_left + obj.hitbox_width
131
+ obj_top = obj.py + obj.hitbox_py
132
+ obj_bottom = obj_top + obj.hitbox_height
133
+
134
+ other_left = other.px + other.hitbox_px
135
+ other_right = other_left + other.hitbox_width
136
+ other_top = other.py + other.hitbox_py
137
+ other_bottom = other_top + other.hitbox_height
138
+
139
+ if obj.solid_vs_dyn and other.solid_vs_dyn:
140
+ collided_with_dyn = False
141
+ if collision_with_dyn(
142
+ obj_left,
143
+ obj_right,
144
+ obj_top,
145
+ obj_bottom,
146
+ other_left,
147
+ other_right,
148
+ other_top,
149
+ other_bottom,
150
+ ):
151
+ collided_with_dyn = True
152
+ if obj_left < other_left:
153
+ new_px -= obj_right - other_left + 0.001
154
+ else:
155
+ new_px += other_right - obj_left + 0.001
156
+
157
+ obj_left = new_px + obj.hitbox_px
158
+ obj_right = obj_left + obj.hitbox_width
159
+ obj_top = new_py + obj.hitbox_py
160
+ obj_bottom = obj_top + obj.hitbox_height
161
+
162
+ if collision_with_dyn(
163
+ obj_left,
164
+ obj_right,
165
+ obj_top,
166
+ obj_bottom,
167
+ other_left,
168
+ other_right,
169
+ other_top,
170
+ other_bottom,
171
+ ):
172
+ collided_with_dyn = True
173
+ if obj_top < other_top:
174
+ new_py -= obj_bottom - other_top + 0.001
175
+ else:
176
+ new_py += other_bottom - obj_top + 0.001
177
+
178
+ if collided_with_dyn:
179
+ other.on_interaction(obj, Nature.WALK)
180
+
181
+ else:
182
+ if obj.type == ObjectType.PLAYER:
183
+ if collision_with_dyn(
184
+ obj_left,
185
+ obj_right,
186
+ obj_top,
187
+ obj_bottom,
188
+ other_left,
189
+ other_right,
190
+ other_top,
191
+ other_bottom,
192
+ ):
193
+ for quest in engine.quests:
194
+ if quest.on_interaction(
195
+ scene.dynamics, other, Nature.WALK
196
+ ):
197
+ break
198
+ scene.tilemap.on_interaction(other, Nature.WALK)
199
+ other.on_interaction(obj, Nature.WALK)
200
+ else:
201
+ if collision_with_dyn(
202
+ obj_left,
203
+ obj_right,
204
+ obj_top,
205
+ obj_bottom,
206
+ other_left,
207
+ other_right,
208
+ other_top,
209
+ other_bottom,
210
+ ):
211
+ if obj.type == ObjectType.PROJECTILE:
212
+ if other.alignment != obj.alignment:
213
+ # We know object is a projectile
214
+ if other.attackable and not other.invincible:
215
+ scene.deal_damage(obj, other)
216
+ else:
217
+ other.on_interaction(obj, Nature.WALK)
218
+ else:
219
+ other.on_interaction(obj, Nature.WALK)
220
+
221
+ return new_px, new_py
222
+
223
+
224
+ def collision_with_dyn(
225
+ left1: float,
226
+ right1: float,
227
+ top1: float,
228
+ bottom1: float,
229
+ left2: float,
230
+ right2: float,
231
+ top2: float,
232
+ bottom2: float,
233
+ ) -> bool:
234
+ if left1 < right2 and right1 > left2 and top1 < bottom2 and bottom1 > top2:
235
+ return True
236
+
237
+ return False