mima-engine 0.4.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.
Files changed (153) hide show
  1. mima/__init__.py +4 -0
  2. mima/backend/__init__.py +1 -0
  3. mima/backend/pygame_assets.py +401 -0
  4. mima/backend/pygame_audio.py +78 -0
  5. mima/backend/pygame_backend.py +603 -0
  6. mima/backend/pygame_camera.py +63 -0
  7. mima/backend/pygame_events.py +695 -0
  8. mima/backend/touch_control_scheme_a.py +126 -0
  9. mima/backend/touch_control_scheme_b.py +132 -0
  10. mima/core/__init__.py +0 -0
  11. mima/core/collision.py +325 -0
  12. mima/core/database.py +58 -0
  13. mima/core/engine.py +367 -0
  14. mima/core/mode_engine.py +81 -0
  15. mima/core/scene_engine.py +81 -0
  16. mima/integrated/__init__.py +0 -0
  17. mima/integrated/entity.py +183 -0
  18. mima/integrated/layered_map.py +351 -0
  19. mima/integrated/sprite.py +156 -0
  20. mima/layered/__init__.py +0 -0
  21. mima/layered/assets.py +56 -0
  22. mima/layered/scene.py +415 -0
  23. mima/layered/shape.py +99 -0
  24. mima/layered/shaped_sprite.py +78 -0
  25. mima/layered/virtual_input.py +302 -0
  26. mima/maps/__init__.py +0 -0
  27. mima/maps/template.py +71 -0
  28. mima/maps/tile.py +20 -0
  29. mima/maps/tile_animation.py +7 -0
  30. mima/maps/tile_info.py +10 -0
  31. mima/maps/tile_layer.py +52 -0
  32. mima/maps/tiled/__init__.py +0 -0
  33. mima/maps/tiled/tiled_layer.py +48 -0
  34. mima/maps/tiled/tiled_map.py +95 -0
  35. mima/maps/tiled/tiled_object.py +79 -0
  36. mima/maps/tiled/tiled_objectgroup.py +25 -0
  37. mima/maps/tiled/tiled_template.py +49 -0
  38. mima/maps/tiled/tiled_tile.py +90 -0
  39. mima/maps/tiled/tiled_tileset.py +51 -0
  40. mima/maps/tilemap.py +216 -0
  41. mima/maps/tileset.py +39 -0
  42. mima/maps/tileset_info.py +9 -0
  43. mima/maps/transition_map.py +146 -0
  44. mima/objects/__init__.py +0 -0
  45. mima/objects/animated_sprite.py +217 -0
  46. mima/objects/attribute_effect.py +26 -0
  47. mima/objects/attributes.py +126 -0
  48. mima/objects/creature.py +384 -0
  49. mima/objects/dynamic.py +206 -0
  50. mima/objects/effects/__init__.py +0 -0
  51. mima/objects/effects/colorize_screen.py +60 -0
  52. mima/objects/effects/debug_box.py +133 -0
  53. mima/objects/effects/light.py +103 -0
  54. mima/objects/effects/show_sprite.py +50 -0
  55. mima/objects/effects/walking_on_grass.py +70 -0
  56. mima/objects/effects/walking_on_water.py +57 -0
  57. mima/objects/loader.py +111 -0
  58. mima/objects/projectile.py +111 -0
  59. mima/objects/sprite.py +116 -0
  60. mima/objects/world/__init__.py +0 -0
  61. mima/objects/world/color_gate.py +67 -0
  62. mima/objects/world/color_switch.py +101 -0
  63. mima/objects/world/container.py +175 -0
  64. mima/objects/world/floor_switch.py +109 -0
  65. mima/objects/world/gate.py +178 -0
  66. mima/objects/world/light_source.py +121 -0
  67. mima/objects/world/logic_gate.py +157 -0
  68. mima/objects/world/movable.py +399 -0
  69. mima/objects/world/oneway.py +195 -0
  70. mima/objects/world/pickup.py +157 -0
  71. mima/objects/world/switch.py +179 -0
  72. mima/objects/world/teleport.py +308 -0
  73. mima/py.typed +0 -0
  74. mima/scripts/__init__.py +2 -0
  75. mima/scripts/command.py +38 -0
  76. mima/scripts/commands/__init__.py +0 -0
  77. mima/scripts/commands/add_quest.py +19 -0
  78. mima/scripts/commands/change_map.py +34 -0
  79. mima/scripts/commands/close_dialog.py +9 -0
  80. mima/scripts/commands/equip_weapon.py +23 -0
  81. mima/scripts/commands/give_item.py +26 -0
  82. mima/scripts/commands/give_resource.py +51 -0
  83. mima/scripts/commands/move_map.py +152 -0
  84. mima/scripts/commands/move_to.py +49 -0
  85. mima/scripts/commands/oneway_move.py +58 -0
  86. mima/scripts/commands/parallel.py +66 -0
  87. mima/scripts/commands/play_sound.py +13 -0
  88. mima/scripts/commands/present_item.py +53 -0
  89. mima/scripts/commands/progress_quest.py +12 -0
  90. mima/scripts/commands/quit_game.py +8 -0
  91. mima/scripts/commands/save_game.py +14 -0
  92. mima/scripts/commands/screen_fade.py +83 -0
  93. mima/scripts/commands/serial.py +69 -0
  94. mima/scripts/commands/set_facing_direction.py +21 -0
  95. mima/scripts/commands/set_spawn_map.py +17 -0
  96. mima/scripts/commands/show_choices.py +52 -0
  97. mima/scripts/commands/show_dialog.py +118 -0
  98. mima/scripts/commands/take_coins.py +23 -0
  99. mima/scripts/script_processor.py +61 -0
  100. mima/standalone/__init__.py +0 -0
  101. mima/standalone/camera.py +153 -0
  102. mima/standalone/geometry.py +1318 -0
  103. mima/standalone/multicolumn_list.py +54 -0
  104. mima/standalone/pixel_font.py +84 -0
  105. mima/standalone/scripting.py +145 -0
  106. mima/standalone/spatial.py +186 -0
  107. mima/standalone/sprite.py +158 -0
  108. mima/standalone/tiled_map.py +1247 -0
  109. mima/standalone/transformed_view.py +433 -0
  110. mima/standalone/user_input.py +563 -0
  111. mima/states/__init__.py +0 -0
  112. mima/states/game_state.py +189 -0
  113. mima/states/memory.py +28 -0
  114. mima/states/quest.py +71 -0
  115. mima/types/__init__.py +0 -0
  116. mima/types/alignment.py +7 -0
  117. mima/types/blend.py +8 -0
  118. mima/types/damage.py +42 -0
  119. mima/types/direction.py +44 -0
  120. mima/types/gate_color.py +7 -0
  121. mima/types/graphic_state.py +23 -0
  122. mima/types/keys.py +64 -0
  123. mima/types/mode.py +9 -0
  124. mima/types/nature.py +12 -0
  125. mima/types/object.py +22 -0
  126. mima/types/player.py +9 -0
  127. mima/types/position.py +13 -0
  128. mima/types/start.py +7 -0
  129. mima/types/terrain.py +9 -0
  130. mima/types/tile_collision.py +11 -0
  131. mima/types/weapon_slot.py +6 -0
  132. mima/types/window.py +44 -0
  133. mima/usables/__init__.py +0 -0
  134. mima/usables/item.py +51 -0
  135. mima/usables/weapon.py +68 -0
  136. mima/util/__init__.py +1 -0
  137. mima/util/colors.py +50 -0
  138. mima/util/constants.py +55 -0
  139. mima/util/functions.py +38 -0
  140. mima/util/input_defaults.py +170 -0
  141. mima/util/logging.py +51 -0
  142. mima/util/property.py +8 -0
  143. mima/util/runtime_config.py +327 -0
  144. mima/util/trading_item.py +23 -0
  145. mima/view/__init__.py +0 -0
  146. mima/view/camera.py +192 -0
  147. mima/view/mima_mode.py +618 -0
  148. mima/view/mima_scene.py +231 -0
  149. mima/view/mima_view.py +12 -0
  150. mima/view/mima_window.py +244 -0
  151. mima_engine-0.4.0.dist-info/METADATA +47 -0
  152. mima_engine-0.4.0.dist-info/RECORD +153 -0
  153. mima_engine-0.4.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,399 @@
1
+ import math
2
+ from typing import List, Optional
3
+
4
+ from ...maps.tilemap import Tilemap
5
+ from ...types.alignment import Alignment
6
+ from ...types.damage import Damage
7
+ from ...types.direction import Direction
8
+ from ...types.graphic_state import GraphicState
9
+ from ...types.keys import Key as K
10
+ from ...types.nature import Nature
11
+ from ...types.object import ObjectType
12
+ from ...util.colors import BLACK
13
+ from ..animated_sprite import AnimatedSprite
14
+ from ..dynamic import Dynamic
15
+ from ..projectile import Projectile
16
+
17
+
18
+ class Movable(Dynamic):
19
+ def __init__(
20
+ self,
21
+ px: float,
22
+ py: float,
23
+ name="Movable",
24
+ *,
25
+ sprite_name: str,
26
+ tilemap: Optional[Tilemap] = None,
27
+ dyn_id=-1,
28
+ mrange: float = 0.0,
29
+ liftable: bool = False,
30
+ destroyable: bool = False,
31
+ movable: bool = False,
32
+ intangible: bool = False,
33
+ force_collision_check: bool = False,
34
+ ):
35
+ super().__init__(
36
+ px,
37
+ py,
38
+ name,
39
+ tilemap=tilemap,
40
+ sprite_name=sprite_name,
41
+ dyn_id=dyn_id,
42
+ )
43
+
44
+ self.type = ObjectType.MOVABLE
45
+ self.alignment = Alignment.NEUTRAL
46
+ self.solid_vs_map = True
47
+
48
+ self.range = mrange
49
+ self.total_range = 0
50
+ self.spawn_px = px
51
+ self.spawn_py = py
52
+
53
+ self.liftable = liftable
54
+ self.destroyable = destroyable
55
+ self.movable = movable
56
+ self.intangible = intangible
57
+ self.moving = False
58
+ self.lift_started = False
59
+ self.lifted = False
60
+ self.thrown = False
61
+ self.visible = True
62
+ self.visible_pz = 0.0
63
+ self.actor: Optional[Dynamic] = None
64
+ self.vx_mask = 0
65
+ self.vy_mask = 0
66
+ self._vx_throw = 0
67
+ self._vy_throw = 0
68
+ self._speed_throw = 4.0
69
+ self.move_direction: str = ""
70
+ self.moves_on_collision = self.movable or self.destroyable
71
+ # self.onscreen_collision_skippable = (
72
+ # not self.movable and not force_collision_check
73
+ # )
74
+
75
+ self._impact_offsets = [
76
+ [0.5, 0.5],
77
+ [-0.5, 0.5],
78
+ [-0.5, -0.5],
79
+ [0.5, -0.5],
80
+ ]
81
+
82
+ def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
83
+ if self.intangible:
84
+ self.solid_vs_dyn = False
85
+ else:
86
+ self.solid_vs_dyn = (
87
+ self.visible and not self.lifted and not self.thrown
88
+ )
89
+ if self.pz > 1.0:
90
+ self.solid_vs_map = False
91
+ else:
92
+ self.solid_vs_map = True
93
+
94
+ self.sprite.update(
95
+ elapsed_time, self.facing_direction, self.graphic_state
96
+ )
97
+
98
+ if self.thrown:
99
+ return self._throw()
100
+
101
+ self.vx = self.vy = 0.0
102
+
103
+ if self.moving:
104
+ return self._move()
105
+
106
+ if self.lift_started or self.lifted:
107
+ self.moves_on_collision = True
108
+ return self._lift()
109
+ self.moves_on_collision = self.movable or self.destroyable
110
+
111
+ def on_interaction(self, target: Dynamic, nature: Nature):
112
+ if self.moving:
113
+ return False
114
+ if self.lifted:
115
+ return False
116
+
117
+ pt = target.get_player()
118
+ if pt.value > 0:
119
+ if nature == Nature.TALK and self.liftable and target.can_lift:
120
+ self.lift_started = True
121
+ self.actor = target
122
+ self.solid_vs_dyn = False
123
+ target.can_attack = False
124
+ return True
125
+
126
+ if (
127
+ self.movable
128
+ and self.visible
129
+ and self.total_range < self.range
130
+ and target.graphic_state
131
+ in [GraphicState.WALKING, GraphicState.PUSHING]
132
+ ):
133
+ if (
134
+ target.facing_direction == Direction.WEST
135
+ and self.engine.keys.key_held(K.LEFT, pt)
136
+ and target.vy == 0
137
+ ):
138
+ self.move_direction = K.LEFT
139
+ self.vx_mask = -1
140
+ elif (
141
+ target.facing_direction == Direction.EAST
142
+ and self.engine.keys.key_held(K.RIGHT, pt)
143
+ and target.vy == 0
144
+ ):
145
+ self.move_direction = K.RIGHT
146
+ self.vx_mask = 1
147
+ elif (
148
+ target.facing_direction == Direction.SOUTH
149
+ and self.engine.keys.key_held(K.DOWN, pt)
150
+ and target.vx == 0
151
+ ):
152
+ self.move_direction = K.DOWN
153
+ self.vy_mask = 1
154
+ elif (
155
+ target.facing_direction == Direction.NORTH
156
+ and self.engine.keys.key_held(K.UP, pt)
157
+ and target.vx == 0
158
+ ):
159
+ self.move_direction = K.UP
160
+ self.vy_mask = -1
161
+ else:
162
+ return False
163
+
164
+ self.actor = target
165
+ self.moving = True
166
+ self.actor.lock_graphic_state(GraphicState.PUSHING)
167
+
168
+ return True
169
+
170
+ elif target.type == ObjectType.PROJECTILE:
171
+ if self.destroyable:
172
+ damage = target.damage - self.attributes.defense[target.dtype]
173
+ if damage > 0:
174
+ self.kill()
175
+ if target.one_hit:
176
+ target.kill()
177
+ return True
178
+
179
+ elif nature == Nature.SIGNAL:
180
+ self.visible = False
181
+ return True
182
+
183
+ elif nature == Nature.NO_SIGNAL:
184
+ self.visible = True
185
+ return True
186
+
187
+ return False
188
+
189
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
190
+ if not self.visible:
191
+ return
192
+
193
+ py = self.py - oy - (self.pz + self.visible_pz)
194
+
195
+ if self.pz != 0:
196
+ self.engine.backend.fill_circle(
197
+ (self.px - ox + 0.5) * self.sprite.width,
198
+ (self.py - oy + 0.7) * self.sprite.height,
199
+ 0.3125 * self.sprite.width,
200
+ BLACK,
201
+ camera_name,
202
+ )
203
+ self.sprite.draw_self(self.px - ox, py, camera_name)
204
+
205
+ def _throw(self):
206
+ if self.pz < 0.5:
207
+ self.solid_vs_dyn = True
208
+ self.speed = 1.0
209
+ if self.pz > 0:
210
+ self.vx = self._vx_throw
211
+ self.vy = self._vy_throw
212
+ self.speed = self._speed_throw
213
+ return
214
+
215
+ self._create_impact()
216
+
217
+ # self.solid_vs_dyn = True
218
+ self.thrown = False
219
+ self.vx = self.vy = 0.0
220
+ if self.destroyable:
221
+ self.kill()
222
+ return
223
+
224
+ def _move(self):
225
+ if self.actor.graphic_state == GraphicState.PUSHING:
226
+ stop_moving = False
227
+ for button in [K.DOWN, K.LEFT, K.UP, K.RIGHT]:
228
+ if button == self.move_direction:
229
+ if self.engine.keys.key_held(
230
+ button, self.actor.get_player()
231
+ ):
232
+ self.vx = self.vx_mask
233
+ self.vy = self.vy_mask
234
+ else:
235
+ if self.engine.keys.key_held(
236
+ button, self.actor.get_player()
237
+ ):
238
+ stop_moving = True
239
+ self.vx = 0
240
+ self.vy = 0
241
+ break
242
+ if (
243
+ abs(self.actor.px - self.px) > 1.1
244
+ or abs(self.actor.py - self.py) > 1.1
245
+ ):
246
+ stop_moving = True
247
+
248
+ if not stop_moving and abs(self.vx) > abs(self.vy):
249
+ self.vy = 0
250
+ elif not stop_moving and abs(self.vy) > abs(self.vx):
251
+ self.vx = 0
252
+ else:
253
+ self.vx = self.vy = 0.0
254
+
255
+ dx = self.px - self.spawn_px
256
+ dy = self.py - self.spawn_py
257
+ self.total_range = math.sqrt(dx * dx + dy * dy)
258
+
259
+ if self.total_range >= self.range:
260
+ self.vx = self.vy = 0.0
261
+
262
+ if self.vx == 0.0 and self.vy == 0.0:
263
+ self.moving = False
264
+ self.vx_mask = self.vy_mask = 0
265
+ self.actor.unlock_graphic_state()
266
+ self.engine.audio.stop_sound("move_block")
267
+ else:
268
+ self.engine.audio.play_sound("move_block")
269
+ return
270
+
271
+ def _lift(self):
272
+ if self.lifted and self.engine.keys.new_key_press(
273
+ K.A, self.actor.get_player()
274
+ ):
275
+ # Throw away
276
+ self.vx = self.vy = 0
277
+ if self.actor.facing_direction == Direction.SOUTH:
278
+ self.vy = self._vy_throw = 1
279
+ if self.actor.facing_direction == Direction.WEST:
280
+ self.vx = self._vx_throw = -1
281
+ if self.actor.facing_direction == Direction.NORTH:
282
+ self.vy = self._vy_throw = -1
283
+ if self.actor.facing_direction == Direction.EAST:
284
+ self.vx = self._vx_throw = 1
285
+
286
+ self.vz = 6.0
287
+ self.pz = self.actor.pz + 0.9
288
+ self.visible_pz = 0
289
+ self.actor.can_attack = True
290
+ self.lifted = False
291
+ self.actor = None
292
+ self.thrown = True
293
+
294
+ elif self.lift_started and self.engine.keys.new_key_release(
295
+ K.A, self.actor.get_player()
296
+ ):
297
+ self.lift_started = False
298
+ self.lifted = True
299
+ self.solid_vs_dyn = False
300
+ else:
301
+ self.solid_vs_dyn = False
302
+ self.px = self.actor.px
303
+ self.py = self.actor.py
304
+ self.visible_pz = self.actor.pz + 0.9
305
+ self.vx = self.vy = 0.0
306
+
307
+ def _create_impact(self):
308
+ # impact: List[Projectile] = []
309
+ # impact.append(
310
+ # Projectile(
311
+ # self.px + 0.5,
312
+ # self.py + 0.5,
313
+ # 0,
314
+ # 0,
315
+ # 0.2,
316
+ # self.alignment,
317
+ # self.tilemap,
318
+ # )
319
+ # )
320
+ for idx, offsets in enumerate(self._impact_offsets):
321
+ p = Projectile(
322
+ self.px + offsets[0],
323
+ self.py + offsets[1],
324
+ f"Movable Impact {idx}",
325
+ sprite_name="small_explosion",
326
+ tilemap=self.tilemap,
327
+ alignment=self.alignment,
328
+ duration=0.2,
329
+ )
330
+ p.solid_vs_dyn = False
331
+ p.solid_vs_map = False
332
+ p.damage = 5
333
+ # impact.append(p)
334
+ self.engine.get_view().add_projectile(p, self.tilemap.name)
335
+
336
+ # impact.append(
337
+ # Projectile(
338
+ # self.px - 0.5,
339
+ # self.py + 0.5,
340
+ # 0,
341
+ # 0,
342
+ # 0.2,
343
+ # self.alignment,
344
+ # self.tilemap,
345
+ # )
346
+ # )
347
+ # impact.append(
348
+ # Projectile(
349
+ # self.px - 0.5,
350
+ # self.py - 0.5,
351
+ # 0,
352
+ # 0,
353
+ # 0.2,
354
+ # self.alignment,
355
+ # self.tilemap,
356
+ # )
357
+ # )
358
+ # impact.append(
359
+ # Projectile(
360
+ # self.px + 0.5,
361
+ # self.py - 0.5,
362
+ # 0,
363
+ # 0,
364
+ # 0.2,
365
+ # self.alignment,
366
+ # self.tilemap,
367
+ # )
368
+ # )
369
+
370
+ # for pro in impact:
371
+ # pro.sprite.name = "explosion"
372
+ # pro.solid_vs_dyn = False
373
+ # pro.solid_vs_map = False
374
+ # pro.damage = 5
375
+
376
+ @staticmethod
377
+ def load_from_tiled_object(obj, px, py, width, height, tilemap):
378
+ movable = Movable(
379
+ px=px,
380
+ py=py,
381
+ name=obj.name,
382
+ sprite_name=obj.get_string("sprite_name"),
383
+ tilemap=tilemap,
384
+ dyn_id=obj.object_id,
385
+ mrange=obj.get_float("range"),
386
+ liftable=obj.get_bool("liftable"),
387
+ destroyable=obj.get_bool("destroyable"),
388
+ movable=obj.get_bool("movable"),
389
+ intangible=obj.get_bool("intangible"),
390
+ force_collision_check=obj.get_bool("force_collision_check"),
391
+ )
392
+ # movable.sprite.width = int(width * Movable.engine.rtc.tile_width)
393
+ # movable.sprite.height = int(height * Movable.engine.rtc.tile_height)
394
+ for dt in Damage:
395
+ movable.attributes.defense[dt] = obj.get_int(
396
+ f"defense_{dt.name.lower()}"
397
+ )
398
+
399
+ return [movable]
@@ -0,0 +1,195 @@
1
+ from typing import List, Optional, Union
2
+
3
+ from ...maps.tilemap import Tilemap
4
+ from ...scripts.commands.oneway_move import CommandOnewayMove
5
+ from ...types.direction import Direction
6
+ from ...types.graphic_state import GraphicState
7
+ from ...types.nature import Nature
8
+ from ...types.object import ObjectType
9
+ from ...util.constants import ONEWAY_ACTIVATION_DELAY
10
+ from ..animated_sprite import AnimatedSprite
11
+ from ..dynamic import Dynamic
12
+
13
+
14
+ class Oneway(Dynamic):
15
+ def __init__(
16
+ self,
17
+ px: float,
18
+ py: float,
19
+ name="Oneway",
20
+ *,
21
+ sprite_name: str = "",
22
+ tilemap: Optional[Tilemap] = None,
23
+ dyn_id=-1,
24
+ jump_vx: float = 0.0,
25
+ jump_vy: float = 0.0,
26
+ width: float = 0.0,
27
+ height: float = 0.0,
28
+ ):
29
+ super().__init__(
30
+ px,
31
+ py,
32
+ name,
33
+ tilemap=tilemap,
34
+ sprite_name=sprite_name,
35
+ dyn_id=dyn_id,
36
+ )
37
+ self.type = ObjectType.ONEWAY
38
+ self.layer = 0
39
+ self.sprite.width = int(width * self.engine.rtc.tile_width)
40
+ self.sprite.height = int(height * self.engine.rtc.tile_height)
41
+
42
+ self.hitbox_px, self.hitbox_py = 0.0, 0.0
43
+ self.hitbox_width, self.hitbox_height = 1.0, 1.0
44
+ self.solid_vs_map = False
45
+
46
+ self.width: float = width
47
+ self.height: float = height
48
+ self.jump_vx: float = 0.0
49
+ self.jump_vy: float = 0.0
50
+ self.jump_direction = Direction.from_velocity(jump_vx, jump_vy)
51
+ self.activation_delay: float = ONEWAY_ACTIVATION_DELAY
52
+ self.triggered: bool = False
53
+ self.is_active: bool = False
54
+ self.cooldown: float = 0.0
55
+ self.target = None
56
+
57
+ if jump_vx < 0:
58
+ self.jump_vx = jump_vx - 1
59
+ # self.hitbox_px += 0.1
60
+ elif jump_vx > 0:
61
+ self.jump_vx = jump_vx + 1
62
+ # self.hitbox_px -= 0.1
63
+
64
+ if jump_vy < 0:
65
+ self.jump_vy = jump_vy - 1
66
+ # self.hitbox_py += 0.1
67
+ elif jump_vy > 0:
68
+ self.jump_vy = jump_vy + 1
69
+ # self.hitbox_py -= 0.1
70
+
71
+ def update(self, elapsed_time, target=None):
72
+ self.sprite.update(
73
+ elapsed_time, self.facing_direction, self.graphic_state
74
+ )
75
+
76
+ # Can only be triggered again after a certain time has passed.
77
+ if self.cooldown >= 0.0:
78
+ self.cooldown -= elapsed_time
79
+ return
80
+ else:
81
+ self.cooldown = 0.0
82
+
83
+ # If no interaction happened in a frame, the activation timer
84
+ # gets resetted.
85
+ if not self.triggered:
86
+ self.timer = 0.0
87
+ return
88
+
89
+ # Activation countdown
90
+ if self.timer > 0.0:
91
+ self.timer -= elapsed_time
92
+
93
+ # Activation countdown reached 0 and the jump is initiated.
94
+ if self.timer <= 0.0 and self.target is not None:
95
+ self.engine.script.add_command(
96
+ CommandOnewayMove(
97
+ self.target,
98
+ self.jump_vx,
99
+ self.jump_vy,
100
+ target.get_player(),
101
+ )
102
+ )
103
+ self.cooldown = 2.0
104
+ self.is_active
105
+
106
+ # Reset the triggered flag so it has to be activated again
107
+ # by interaction
108
+ self.triggered = False
109
+ self.target = None
110
+
111
+ def on_interaction(self, target, nature=Nature.WALK):
112
+ if (
113
+ target.type == ObjectType.PLAYER
114
+ and nature == Nature.WALK
115
+ and self.cooldown <= 0.0
116
+ ):
117
+ # No interaction when target is higher than the oneway
118
+ if target.pz > 0:
119
+ return False
120
+
121
+ if self.jump_direction != target.facing_direction:
122
+ return False
123
+
124
+ tcenterx = target.px + (target.hitbox_px + target.hitbox_width) / 2
125
+ tbottomy = target.py + target.hitbox_py + target.hitbox_height
126
+ ttopy = target.py + target.hitbox_py
127
+ tleftx = target.px + target.hitbox_px
128
+ trightx = tleftx + target.hitbox_width
129
+
130
+ # print(tcenterx, tbottomy)
131
+ # We have to check that target is not placed "more" in the
132
+ # target direction than the oneway
133
+ if (
134
+ self.jump_vx < 0
135
+ and target.px < self.px + self.width - target.hitbox_px
136
+ ):
137
+ return False
138
+ if self.jump_vx > 0 and target.px >= self.px:
139
+ return False
140
+ if (
141
+ self.jump_vy < 0
142
+ and target.py <= self.py + self.height - target.hitbox_py
143
+ ):
144
+ return False
145
+ if self.jump_vy > 0 and tbottomy >= self.py: # FIXME
146
+ return False
147
+
148
+ if self.jump_vx == 0:
149
+ if target.px >= self.px + self.width:
150
+ return False
151
+ if target.px + 1.0 <= self.px:
152
+ return False
153
+
154
+ if self.jump_vy == 0:
155
+ if target.py >= self.py + self.height:
156
+ return False
157
+ if target.py + 1.0 <= self.py:
158
+ return False
159
+ self.triggered = True
160
+ self.target = target
161
+ if self.timer <= 0.0:
162
+ self.timer = self.activation_delay
163
+ # return False
164
+
165
+ # print(
166
+ # f"activated {self.timer:.3f}, {self.px, self.py}, {target.px, target.py}, {target.sprite.width, target.sprite.height}"
167
+ # )
168
+ return True
169
+
170
+ return False
171
+
172
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
173
+ self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
174
+
175
+ @staticmethod
176
+ def load_from_tiled_object(obj, px, py, width, height, tilemap):
177
+ oneway = Oneway(
178
+ px,
179
+ py,
180
+ obj.name,
181
+ sprite_name=obj.get_string("sprite_name"),
182
+ tilemap=tilemap,
183
+ dyn_id=obj.object_id,
184
+ jump_vx=obj.get_float("jump_vx"),
185
+ jump_vy=obj.get_float("jump_vy"),
186
+ width=width,
187
+ height=height,
188
+ )
189
+ oneway.graphic_state = (
190
+ GraphicState[obj.get_string("graphic_state", "standing").upper()],
191
+ )
192
+ oneway.facing_direction = (
193
+ Direction[obj.get_string("facing_direction", "south").upper()],
194
+ )
195
+ return [oneway]