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,384 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Dict, List, Optional
4
+
5
+ from ..types.direction import Direction
6
+ from ..types.graphic_state import GraphicState, Until
7
+ from ..types.nature import Nature
8
+ from ..types.object import ObjectType
9
+ from ..types.player import Player
10
+ from ..types.terrain import Terrain
11
+ from ..types.weapon_slot import WeaponSlot
12
+
13
+ # from ..types.weapon_slot import WeaponSlot
14
+ from ..util.colors import BLACK
15
+ from ..util.constants import DEFAULT_KNOCK_SPEED
16
+ from .animated_sprite import AnimatedSprite
17
+ from .dynamic import Dynamic
18
+ from .effects.light import Light
19
+ from .effects.walking_on_grass import WalkingOnGrass
20
+ from .effects.walking_on_water import WalkingOnWater
21
+ from .projectile import Projectile
22
+
23
+ if TYPE_CHECKING:
24
+ from ..maps.tilemap import Tilemap
25
+ from ..usables.weapon import Weapon
26
+ from ..util.trading_item import TradingItem
27
+
28
+
29
+ class Creature(Dynamic):
30
+ def __init__(
31
+ self,
32
+ px: float = 0,
33
+ py: float = 0,
34
+ name: str = "Unnamed Creature",
35
+ *,
36
+ sprite_name: str = "",
37
+ tilemap: Tilemap = None,
38
+ dyn_id: int = -1,
39
+ player: Player = Player.P0,
40
+ ):
41
+ super().__init__(
42
+ px,
43
+ py,
44
+ name,
45
+ sprite_name=sprite_name,
46
+ tilemap=tilemap,
47
+ dyn_id=dyn_id,
48
+ )
49
+
50
+ # self.sprite = self.engine.db.get_sprite(
51
+ # sprite_name
52
+ # ) # AnimatedSprite(tileset_name, image_name, sprite_name)
53
+ self.player = player
54
+ self.type = ObjectType.CREATURE
55
+ self.knock_speed: float = DEFAULT_KNOCK_SPEED
56
+
57
+ # self.sprite.name = sprite_name
58
+ # self.sprite.num_frames = 2
59
+ self.attackable = True
60
+ self.knockable = True
61
+ self.moves_on_collision = True
62
+
63
+ self._knock_vx: float = 0.0
64
+ self._knock_vy: float = 0.0
65
+ self.real_vx: float = 0.0
66
+ self.real_vy: float = 0.0
67
+ self.last_vx: float = 0.0
68
+ self.last_vy: float = 0.0
69
+ self._attack_timer: float = 0.0
70
+ self._invincible_timer: float = 0.0
71
+ self._knock_timer: float = 0.0
72
+ self._state_timer: float = 0.0
73
+
74
+ self.invincible: bool = False
75
+ # self.use_acceleration: bool = True
76
+ # self.use_friction: bool = True
77
+
78
+ self.projectiles: List[Projectile] = []
79
+ self.weapons: Dict[WeaponSlot, Optional[Weapon]] = {
80
+ WeaponSlot.FIRST_HAND: None,
81
+ WeaponSlot.SECOND_HAND: None,
82
+ }
83
+ self._light = None
84
+
85
+ # self.attributes.speed = 1.0
86
+ # self.use_acceleration = True
87
+ # self.use_friction = True
88
+ # self.attributes.friction = 0.1
89
+
90
+ def start_shining(self):
91
+ self._light = Light(self, fixed_size=True, update_from_target=True)
92
+ self.engine.get_view().add_effect(self._light, self.tilemap.name)
93
+
94
+ def stop_shining(self):
95
+ if self._light is not None:
96
+ self._light.kill()
97
+ self._light = None
98
+
99
+ def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
100
+ if not self.visible:
101
+ self.vx = self.vy = 0.0
102
+ return
103
+
104
+ if self._knock_timer > 0.0: # Handle knocking
105
+ self._knock_timer -= elapsed_time
106
+ self._invincible_timer -= elapsed_time
107
+ self.vx, self.vy = self._knock_vx, self._knock_vy
108
+ self.speed = self.knock_speed
109
+ self.change_graphic_state(GraphicState.DAMAGED)
110
+
111
+ if self._knock_timer <= 0.0: # Knocking is over
112
+ self._state_timer = 0.0
113
+ self.controllable = True
114
+ self.solid_vs_dyn = True
115
+ self.sprite.reset()
116
+
117
+ else: # Usual update
118
+ if self._invincible_timer > 0.0:
119
+ self._invincible_timer -= elapsed_time
120
+ if self._invincible_timer <= 0.0:
121
+ self.invincible = False
122
+ self.knockable = True
123
+ self._invincible_timer = 0.0
124
+
125
+ if self._attack_timer > 0.0:
126
+ self._attack_timer -= elapsed_time
127
+ self.change_graphic_state(GraphicState.ATTACKING)
128
+
129
+ if self._attack_timer <= 0:
130
+ self._attack_timer = 0.0
131
+ if abs(self.vx) > 0 or abs(self.vy) > 0:
132
+ self.change_graphic_state(GraphicState.WALKING)
133
+ else:
134
+ self.change_graphic_state(GraphicState.STANDING)
135
+
136
+ if self.attributes.health <= 0:
137
+ self.change_graphic_state(GraphicState.DEAD)
138
+
139
+ if self.can_act():
140
+ self.behavior(elapsed_time, target)
141
+
142
+ if self.vx < -0.01:
143
+ self.facing_direction = Direction.WEST
144
+ if self.vx > 0.01:
145
+ self.facing_direction = Direction.EAST
146
+ if self.vy < -0.01:
147
+ self.facing_direction = Direction.NORTH
148
+ if self.vy > 0.01:
149
+ self.facing_direction = Direction.SOUTH
150
+
151
+ self.speed = self.attributes.speed
152
+
153
+ self.sprite.update(
154
+ elapsed_time, self.facing_direction, self.graphic_state
155
+ )
156
+
157
+ self._handle_terrain(elapsed_time)
158
+
159
+ if self.graphic_state == GraphicState.DEAD:
160
+ self.attackable = False
161
+ self.vx = self.vy = 0
162
+ if self.despawn_timer is None:
163
+ self.despawn_timer = self.despawn_duration
164
+ self.engine.play_sound("enemy_killed")
165
+ else:
166
+ self.despawn_timer -= elapsed_time
167
+
168
+ if self.despawn_timer <= 0.0:
169
+ self.kill()
170
+ else:
171
+ eff2r = []
172
+ for eff in self.attribute_effects:
173
+ if (
174
+ self.attributes.health < eff.health_cost
175
+ or self.attributes.magic < eff.magic_cost
176
+ or self.attributes.stamina < eff.stamina_cost
177
+ # and self.attributes.arrows >= weapon.arrow_cost
178
+ # and self.attributes.bombs >= weapon.bomb_cost
179
+ ):
180
+ eff.redundant = True
181
+ self.attributes.health_per_second += eff.health_cost
182
+ self.attributes.magic_per_second += eff.magic_cost
183
+ self.attributes.stamina_per_second += eff.stamina_cost
184
+ eff2r.append(eff)
185
+ for e in eff2r:
186
+ self.attribute_effects.remove(e)
187
+
188
+ self.attributes.update(elapsed_time)
189
+
190
+ if self._gs_lock_condition == Until.NEXT_UPDATE:
191
+ self.graphic_state_locked = False
192
+
193
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
194
+ if (
195
+ self.sprite.name is None
196
+ or self.sprite.name == ""
197
+ or not self.visible
198
+ ):
199
+ return
200
+
201
+ px = self.px - ox + self.extra_ox
202
+ py = self.py - oy + self.extra_oy
203
+
204
+ if self.pz != 0:
205
+ self.engine.backend.fill_circle(
206
+ (px + 0.5) * self.sprite.width,
207
+ (py + 0.7) * self.sprite.height,
208
+ 0.3125 * self.sprite.width,
209
+ BLACK,
210
+ camera_name,
211
+ )
212
+
213
+ self.sprite.draw_self(px, py - self.pz, camera_name)
214
+ # for effect in self.effects:
215
+ # effect.draw_self(px, py - self.pz)
216
+
217
+ def behavior(self, elapsed_time: float, target: Optional[Dynamic] = None):
218
+ # No default behavior
219
+ pass
220
+
221
+ def on_interaction(self, target=None, nature=Nature.WALK):
222
+ if nature == Nature.SIGNAL:
223
+ self.solid_vs_dyn = False
224
+ self.visible = False
225
+ return True
226
+
227
+ elif nature == Nature.NO_SIGNAL:
228
+ self.visible = True
229
+ self.solid_vs_dyn = True
230
+ return True
231
+ return False
232
+
233
+ def knock_back(self, vx: float, vy: float, dist: float):
234
+ if self.knockable:
235
+ self._knock_vx = vx
236
+ self._knock_vy = vy
237
+ self._knock_timer = dist
238
+
239
+ self.knockable = False
240
+ self._invincible_timer = dist + 0.5
241
+ # self.solid_vs_dyn = False
242
+ self.controllable = False
243
+ self.invincible = True
244
+ self.sprite.reset()
245
+ self.cancel_attack()
246
+
247
+ def can_act(self):
248
+ actable_states = [
249
+ GraphicState.STANDING,
250
+ GraphicState.WALKING,
251
+ GraphicState.CELEBRATING,
252
+ # GraphicState.DAMAGED,
253
+ GraphicState.PUSHING,
254
+ ]
255
+ if self.graphic_state in actable_states:
256
+ return True
257
+
258
+ return False
259
+
260
+ def perform_attack(self, slot: WeaponSlot) -> bool:
261
+ weapon = self.weapons.get(slot, None)
262
+
263
+ if weapon is None or not self.can_attack:
264
+ # print(f"Cannot attack. Weapon={weapon}")
265
+ return False
266
+
267
+ # Clean up all previous projectiles
268
+ self.cancel_attack()
269
+
270
+ if (
271
+ self.attributes.health >= weapon.health_cost
272
+ and self.attributes.magic >= weapon.magic_cost
273
+ and self.attributes.stamina >= weapon.stamina_cost
274
+ and self.attributes.arrows >= weapon.arrow_cost
275
+ and self.attributes.bombs >= weapon.bomb_cost
276
+ ):
277
+ self.attributes.health -= weapon.health_cost
278
+ self.attributes.magic -= weapon.magic_cost
279
+ self.attributes.stamina -= weapon.stamina_cost
280
+ self.attributes.arrows -= weapon.arrow_cost
281
+ self.attributes.bombs -= weapon.bomb_cost
282
+ else:
283
+ return False
284
+
285
+ if weapon.on_use(self):
286
+ self.vx = self.vy = 0.0
287
+ self._attack_timer = weapon.swing_timer
288
+ return True
289
+
290
+ return False
291
+
292
+ def cancel_attack(self):
293
+ # if self.projectiles:
294
+ for projectile in self.projectiles:
295
+ if projectile is not None:
296
+ projectile.spawn_on_death = []
297
+ projectile.kill()
298
+
299
+ self.projectiles = []
300
+
301
+ def on_enter_map(self):
302
+ pass
303
+
304
+ def on_exit_map(self):
305
+ pass
306
+
307
+ def on_death(self):
308
+ if self.spawn_on_death:
309
+ for do in self.spawn_on_death:
310
+ if do.redundant:
311
+ continue
312
+ if do.type == ObjectType.PROJECTILE:
313
+ if do.inherit_pos:
314
+ do.px = self.px
315
+ do.py = self.py
316
+ self.engine.get_view().add_projectile(
317
+ do, self.tilemap.name
318
+ )
319
+ else:
320
+ self.engine.get_view().add_dynamic(do, self.tilemap.name)
321
+
322
+ self.spawn_on_death = []
323
+
324
+ def _handle_terrain(self, elapsed_time: float):
325
+ e2rm = []
326
+ for effect in self.effects:
327
+ if isinstance(effect, WalkingOnGrass):
328
+ if self.walking_on == Terrain.DEFAULT:
329
+ e2rm.append(effect)
330
+
331
+ for effect in e2rm:
332
+ self.effects.remove(effect)
333
+
334
+ if self.walking_on in [Terrain.GRASS, Terrain.SHALLOW_WATER]:
335
+ self.attributes.speed_mod = 0.7
336
+ effect_active = False
337
+ for effect in self.effects:
338
+ if isinstance(effect, (WalkingOnGrass, WalkingOnWater)):
339
+ effect_active = True
340
+ effect.renew = True
341
+ break
342
+
343
+ if not effect_active:
344
+ if self.walking_on == Terrain.GRASS:
345
+ eff = WalkingOnGrass(self)
346
+ else:
347
+ eff = WalkingOnWater(self)
348
+ self.effects.append(eff)
349
+ self.engine.get_view().add_effect(eff, self.tilemap.name)
350
+ else:
351
+ self.attributes.speed_mod = 1.0
352
+
353
+ def equip_weapon(self, slot, weapon):
354
+ if self.weapons[slot] == weapon:
355
+ # Weapon already equipped; unequip
356
+ self.weapons[slot] = None
357
+ weapon.on_unequip(self)
358
+ return
359
+
360
+ for s, w in self.weapons.items():
361
+ if slot != s and weapon == w:
362
+ # Weapon equipped in a different slot; change
363
+ if self.weapons[slot] is not None:
364
+ self.weapons[slot].on_unequip(self)
365
+ self.weapons[slot] = weapon
366
+ self.weapons[s] = None
367
+ return
368
+
369
+ if self.weapons[slot] is not None:
370
+ # Other weapon equipped; unequip that
371
+ self.weapons[slot].on_unequip(self)
372
+
373
+ self.weapons[slot] = weapon
374
+ weapon.on_equip(self)
375
+
376
+ def unequip_weapon(self, slot):
377
+ if self.weapons[slot] is not None:
378
+ self.weapons[slot].on_unequip(self)
379
+
380
+ def get_trading_items(self) -> List[TradingItem]:
381
+ return []
382
+
383
+ def __str__(self):
384
+ return f"C({self.name}, {self.dyn_id})"
@@ -0,0 +1,206 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List, Optional, Union
4
+
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, Until
9
+ from ..types.nature import Nature
10
+ from ..types.object import ObjectType
11
+ from ..types.player import Player
12
+ from ..types.terrain import Terrain
13
+ from .attribute_effect import Effect
14
+ from .attributes import Attributes
15
+ from .sprite import Sprite
16
+
17
+ if TYPE_CHECKING:
18
+ from ..engine import MimaEngine
19
+ from ..maps.tilemap import Tilemap
20
+ from .projectile import Projectile
21
+
22
+
23
+ class Dynamic:
24
+ engine: MimaEngine
25
+
26
+ def __init__(
27
+ self,
28
+ px: float = 0.0,
29
+ py: float = 0.0,
30
+ name: str = "Unnamed Dynamic",
31
+ *,
32
+ sprite_name: str = "",
33
+ tilemap: Optional[Tilemap] = None,
34
+ dyn_id=-1,
35
+ ):
36
+ self.name: str = name
37
+ self.dyn_id: int = dyn_id # ID given by Tiled
38
+ self.layer: int = 1
39
+ self.tilemap = tilemap # The object's current map
40
+ self.player = Player.P0
41
+
42
+ self.px: float = px if px is not None else 0.0
43
+ self.py: float = py if py is not None else 0.0
44
+ self.pz: float = 0.0
45
+ self.vx: float = 0.0
46
+ self.vy: float = 0.0
47
+ self.vz: float = 0.0
48
+ self.real_vx: float = 0.0
49
+ self.real_vy: float = 0.0
50
+ self.hitbox_px: float = 0.1
51
+ self.hitbox_py: float = 0.1
52
+ self.hitbox_width: float = 0.8
53
+ self.hitbox_height: float = 0.8
54
+ self.attribute_timer: float = 0.25
55
+ self.despawn_timer: float = 0.5
56
+ self.despawn_duration: float = 0.2
57
+ self.timer: float = 0.0
58
+ self.speed: float = 1.0
59
+
60
+ self.solid_vs_map: bool = True
61
+ self.solid_vs_dyn: bool = True
62
+ self.solid_vs_player: bool = True
63
+ # self.is_player: bool = False
64
+ # self.is_projectile: bool = False
65
+ self.redundant: bool = False
66
+ self.persistent: bool = False
67
+ self.attackable: bool = False
68
+ self.controllable: bool = True
69
+ self.state_changed: bool = False
70
+ self.visible: bool = True
71
+ self.gravity: bool = True
72
+ self.graphic_state_locked: bool = False
73
+ self.use_acceleration: bool = False
74
+ self.use_friction: bool = False
75
+ self.occupied: bool = False
76
+
77
+ self.alignment: Alignment = Alignment.GOOD
78
+ self.walking_on: Terrain = Terrain.DEFAULT
79
+ self.attributes: Attributes = Attributes()
80
+ self.facing_direction: Direction = Direction.SOUTH
81
+ self.graphic_state: GraphicState = GraphicState.STANDING
82
+ self._gs_lock_condition: Until = Until.UNLOCK
83
+ self.type: ObjectType = ObjectType.UNDEFINED
84
+ self.spawn_on_death: List[Dynamic] = []
85
+ self.childs: List[Dynamic] = []
86
+ self.effects: List[Projectile] = []
87
+ self.attribute_effects: List[Effect] = []
88
+
89
+ self.extra_ox: float = 0.0
90
+ self.extra_oy: float = 0.0
91
+
92
+ self.can_attack: bool = True
93
+ self.can_lift: bool = False
94
+ self.can_push: bool = False
95
+
96
+ self.sprite: Sprite = self.engine.db.get_sprite(sprite_name)
97
+
98
+ # Performance
99
+ self.chunks: List[int] = []
100
+ self.moves_on_collision: bool = False
101
+
102
+ self.update_skippable: bool = False
103
+ """Update may be skipped if object is offscreen."""
104
+ self.offscreen_collision_skippable: bool = True
105
+ """Collision checks may be skipped if object is offscreen."""
106
+ self.onscreen_collision_skippable: bool = False
107
+ """Collision checks may be skipped if object is onscreen."""
108
+ self.draw_skippable: bool = True
109
+ """Drawing the unit may be skipped if object is offscreen."""
110
+
111
+ def update(self, elapsed_time: float, target: Optional[Dynamic]):
112
+ """Update this dynamic."""
113
+ pass
114
+
115
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
116
+ """Draw self to screen"""
117
+ pass
118
+
119
+ def on_interaction(
120
+ self, target: Dynamic = None, nature: Nature = Nature.WALK
121
+ ) -> bool:
122
+ """Handle interaction with other dynamic objects."""
123
+ return False
124
+
125
+ def on_death(self) -> bool:
126
+ return False
127
+
128
+ def kill(self):
129
+ self.redundant = True
130
+
131
+ def change_graphic_state(self, new_state: GraphicState) -> bool:
132
+ if self.graphic_state_locked or new_state == self.graphic_state:
133
+ return False
134
+
135
+ self.graphic_state = new_state
136
+ self.sprite.update(0.0, self.facing_direction, self.graphic_state)
137
+
138
+ return True
139
+
140
+ def lock_graphic_state(
141
+ self,
142
+ new_state: GraphicState = GraphicState.STANDING,
143
+ until: Until = Until.UNLOCK,
144
+ ):
145
+ self.change_graphic_state(new_state)
146
+ self.graphic_state_locked = True
147
+ self._gs_lock_condition = until
148
+
149
+ def unlock_graphic_state(self):
150
+ self.graphic_state_locked = False
151
+ self.change_graphic_state(GraphicState.STANDING)
152
+
153
+ def get_defense_value(self, dtype: Damage) -> int:
154
+ return self.attributes.defense[dtype]
155
+
156
+ def is_py_lower(self, other: Dynamic) -> bool:
157
+ return self.py < other.py
158
+
159
+ def get_alignment(self) -> Alignment:
160
+ return self.alignment
161
+
162
+ def add_effect(self, eff: Effect):
163
+ self.attribute_effects.append(eff)
164
+
165
+ def remove_effect(self, eff: Union[str, Effect]):
166
+ if isinstance(eff, str):
167
+ obj = [e for e in self.attribute_effects if e.effect_id == eff]
168
+ if not obj:
169
+ return
170
+ else:
171
+ eff = obj[0]
172
+
173
+ self.attribute_effects.remove(eff)
174
+
175
+ def has_effect(self, eff: Union[str, Effect]):
176
+ if isinstance(eff, str):
177
+ for e in self.attribute_effects:
178
+ if e.effect_id == eff:
179
+ return True
180
+ return False
181
+ else:
182
+ if eff in self.attribute_effects:
183
+ return True
184
+ else:
185
+ return False
186
+
187
+ def light_radius(self):
188
+ rad = self.attributes.light_radius
189
+
190
+ for eff in self.attribute_effects:
191
+ rad += eff.light_radius
192
+
193
+ return rad
194
+
195
+ def get_player(self):
196
+ return self.player
197
+
198
+ def halt(self):
199
+ self.vx = 0
200
+ self.vy = 0
201
+
202
+ def __str__(self):
203
+ return f"D({self.name}, {self.dyn_id})"
204
+
205
+ def __repr__(self):
206
+ return str(self)
File without changes
@@ -0,0 +1,60 @@
1
+ from typing import List, Optional
2
+
3
+ from ...types.alignment import Alignment
4
+ from ...types.player import Player
5
+ from ...util.colors import BLACK, Color
6
+ from ...util.constants import HEIGHT, WIDTH
7
+ from ..dynamic import Dynamic
8
+ from ..projectile import Projectile
9
+
10
+
11
+ class ColorizeScreen(Projectile):
12
+ def __init__(
13
+ self,
14
+ color: Color = BLACK,
15
+ alpha=BLACK.alpha,
16
+ duration: float = -1.0,
17
+ to_filter: bool = False,
18
+ cameras: Optional[List[str]] = None,
19
+ ):
20
+ super().__init__(
21
+ 0,
22
+ 0,
23
+ "ColorizeScreen",
24
+ vx=0,
25
+ vy=0,
26
+ duration=duration,
27
+ alignment=Alignment.GOOD,
28
+ )
29
+ self.layer = 1
30
+ self.solid_vs_map = False
31
+ self.color = color
32
+ self.alpha = alpha
33
+ self.vanishs_after_time: bool = duration > 0
34
+ self.to_filter = to_filter
35
+ self._cameras = (
36
+ cameras if (cameras is not None and cameras) else ["C_DISPLAY"]
37
+ )
38
+
39
+ def update(self, elapsed_time: float, target: Dynamic = None):
40
+ if self.vanishs_after_time:
41
+ self.duration -= elapsed_time
42
+ if self.duration <= 0.0:
43
+ self.kill()
44
+
45
+ # self.sprite.update(elapsed_time, self.facing_direction, self.graphic_state)
46
+
47
+ def draw_self(self, ox: float, oy: float, camera_name=None):
48
+ color = Color(
49
+ self.color.red, self.color.green, self.color.blue, self.alpha
50
+ )
51
+ if camera_name in self._cameras:
52
+ self.engine.backend.fill_rect(
53
+ self.px,
54
+ self.py,
55
+ self.engine.backend.render_width,
56
+ self.engine.backend.render_height,
57
+ color,
58
+ camera_name,
59
+ draw_to_filter=self.to_filter,
60
+ )