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,26 @@
1
+ from .attributes import Attributes
2
+
3
+
4
+ class Effect(Attributes):
5
+ def __init__(self):
6
+ super().__init__()
7
+
8
+ self.effect_id: str = ""
9
+ self.duration: float = 0.0
10
+ self.redundant: bool = False
11
+ self.health_cost: float = 0.0
12
+ self.magic_cost: float = 0.0
13
+ self.stamina_cost: float = 0.0
14
+
15
+ def update(self, elapsed_time: float):
16
+ self.duration -= self.elapsed_time
17
+ if self.duration < 0.0:
18
+ self.redundant = True
19
+
20
+ @staticmethod
21
+ def from_dict(data):
22
+ attr = Effect()
23
+ for key, val in data.items():
24
+ setattr(attr, key, val)
25
+
26
+ return attr
@@ -0,0 +1,123 @@
1
+ from typing import Dict
2
+
3
+ from ..types.damage import Damage
4
+ from ..util.constants import ATTRIBUTE_TIMER
5
+
6
+
7
+ class Attributes:
8
+ """A class holding all the attributes of an object.
9
+
10
+ Attributes
11
+ ----------
12
+
13
+ health: float
14
+ The current health of the object.
15
+ health_max: float
16
+ The maximum health of the object.
17
+ speed: float
18
+ The current speed value of the object. If different run speeds
19
+ are not relevant for the object, this is the only value to
20
+ consider. Otherwise, this value might be changed by objects
21
+ itself to reflect different states.
22
+ speed_mod: float
23
+ A modification of the speed used by the game engine, e.g., when
24
+ walking in shallow water
25
+ walk_speed: float
26
+ The speed value when the objects moves slow, e.g., when it is
27
+ walking.
28
+ run_speed: float
29
+ The speed value when the object moves fast, e.g., when it is
30
+ running.
31
+ acceleration: float
32
+ The acceleration of the object when it starts to move. Ranges
33
+ between 0 and 1
34
+ friction: float
35
+ The friction of the object when it stops moving. Ranges between
36
+ 0 and 1.
37
+
38
+ """
39
+
40
+ def __init__(self):
41
+ self.health: float = 10.0
42
+ self.health_max: float = 10.0
43
+ self.health_per_second: float = 0.0
44
+ self.magic: float = 10.0
45
+ self.magic_max: float = 10.0
46
+ self.magic_per_second: float = 0.1
47
+ self.stamina: float = 10.0
48
+ self.stamina_max: float = 10.0
49
+ self.stamina_per_second: float = 2.0
50
+ self.speed: float = 1.0
51
+ self.speed_mod: float = 1.0
52
+ self.walk_speed: float = 1.0
53
+ self.run_speed: float = 1.0
54
+ # self.current_speed: float = 0.0 # Acceleration and Friction
55
+ self.knock_speed: float = 5.0
56
+ self.acceleration: float = 15.0
57
+ self.friction: float = 15.0
58
+
59
+ self.timer: float = 0.25
60
+ self.gravity_vz: float = 40.0
61
+ self.light_radius: float = 32
62
+
63
+ self.coins: int = 0
64
+ self.coins_max: int = 100_000
65
+ self.keys: int = 0
66
+ self.keys_max: int = 100_000
67
+ self.bombs: int = 0
68
+ self.bombs_max: int = 5
69
+ self.arrows: int = 0
70
+ self.arrows_max: int = 15
71
+
72
+ self.body_damage: int = 0
73
+
74
+ self.strength: int = 0
75
+ self.dexterity: int = 0
76
+ self.intelligence: int = 0
77
+ self.wisdom: int = 0
78
+ self.initiative: int = 0
79
+
80
+ self.experience: int = 0
81
+ self.defense: Dict[Damage, int] = {dt: 0 for dt in Damage}
82
+
83
+ def update(self, elapsed_time: float):
84
+ self.timer -= elapsed_time
85
+ if self.timer <= 0.0:
86
+ self.timer += ATTRIBUTE_TIMER
87
+
88
+ self.health = min(
89
+ self.health + self.health_per_second * ATTRIBUTE_TIMER,
90
+ self.health_max,
91
+ )
92
+ self.magic = min(
93
+ self.magic + self.magic_per_second * ATTRIBUTE_TIMER,
94
+ self.magic_max,
95
+ )
96
+ self.stamina = min(
97
+ self.stamina + self.stamina_per_second * ATTRIBUTE_TIMER,
98
+ self.stamina_max,
99
+ )
100
+
101
+ @staticmethod
102
+ def from_dict(data):
103
+ attr = Attributes()
104
+
105
+ for key, val in data.items():
106
+ if "defense" in key:
107
+ def_key = key.split("_", 1)[1]
108
+ attr.defense[def_key] = val
109
+ setattr(attr, key, val)
110
+
111
+ return attr
112
+
113
+ @property
114
+ def health_percent(self):
115
+ return self.health / self.health_max
116
+
117
+ @property
118
+ def magic_percent(self):
119
+ return self.magic / self.magic_max
120
+
121
+ @property
122
+ def stamina_percent(self):
123
+ return self.stamina / self.stamina_max
@@ -0,0 +1,332 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, 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.terrain import Terrain
10
+ from ..types.weapon_slot import WeaponSlot
11
+
12
+ # from ..types.weapon_slot import WeaponSlot
13
+ from ..util.colors import BLACK
14
+ from ..util.constants import DEFAULT_KNOCK_SPEED
15
+ from .animated_sprite import AnimatedSprite
16
+ from .dynamic import Dynamic
17
+ from .effects.walking_on_grass import WalkingOnGrass
18
+ from .effects.walking_on_water import WalkingOnWater
19
+ from .projectile import Projectile
20
+
21
+ # if TYPE_CHECKING:
22
+ # from ..usables.weapon import Weapon
23
+
24
+
25
+ class Creature(Dynamic):
26
+ def __init__(
27
+ self,
28
+ name: str,
29
+ tileset_name: str,
30
+ image_name: str,
31
+ sprite_name: str,
32
+ px: float,
33
+ py: float,
34
+ dyn_id: int = -1,
35
+ ):
36
+ super().__init__(name, px, py, dyn_id)
37
+
38
+ self.sprite = AnimatedSprite(tileset_name, image_name, sprite_name)
39
+ self.type = ObjectType.CREATURE
40
+ self.knock_speed: float = DEFAULT_KNOCK_SPEED
41
+
42
+ # self.sprite.name = sprite_name
43
+ # self.sprite.num_frames = 2
44
+ self.attackable = True
45
+
46
+ self._knock_vx: float = 0.0
47
+ self._knock_vy: float = 0.0
48
+ self.real_vx: float = 0.0
49
+ self.real_vy: float = 0.0
50
+ self.last_vx: float = 0.0
51
+ self.last_vy: float = 0.0
52
+ self._attack_timer: float = 0.0
53
+ self._invincible_timer: float = 0.0
54
+ self._knock_timer: float = 0.0
55
+ self._state_timer: float = 0.0
56
+
57
+ self.invincible: bool = False
58
+ # self.use_acceleration: bool = True
59
+ # self.use_friction: bool = True
60
+
61
+ self.projectiles: List[Projectile] = []
62
+ self.weapons: Dict[WeaponSlot, Optional[Weapon]] = {
63
+ WeaponSlot.FIRST_HAND: None,
64
+ WeaponSlot.SECOND_HAND: None,
65
+ }
66
+
67
+ def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
68
+
69
+ if not self.visible:
70
+ self.vx = self.vy = 0.0
71
+ return
72
+
73
+ if self._knock_timer > 0.0: # Handle knocking
74
+ self._knock_timer -= elapsed_time
75
+ self._invincible_timer -= elapsed_time
76
+ self.vx, self.vy = self._knock_vx, self._knock_vy
77
+ self.speed = self.knock_speed
78
+ self.change_graphic_state(GraphicState.DAMAGED)
79
+
80
+ if self._knock_timer <= 0.0: # Knocking is over
81
+ self._state_timer = 0.0
82
+ self.controllable = True
83
+ self.solid_vs_dyn = True
84
+ self.sprite.reset()
85
+
86
+ else: # Usual update
87
+ if self._invincible_timer > 0.0:
88
+ self._invincible_timer -= elapsed_time
89
+ if self._invincible_timer <= 0.0:
90
+ self.invincible = False
91
+ self._invincible_timer = 0.0
92
+
93
+ if self._attack_timer > 0.0:
94
+ self._attack_timer -= elapsed_time
95
+ self.change_graphic_state(GraphicState.ATTACKING)
96
+
97
+ if self._attack_timer <= 0:
98
+ self._attack_timer = 0.0
99
+ if abs(self.vx) > 0 or abs(self.vy) > 0:
100
+ self.change_graphic_state(GraphicState.WALKING)
101
+ else:
102
+ self.change_graphic_state(GraphicState.STANDING)
103
+
104
+ if self.attributes.health <= 0:
105
+ self.change_graphic_state(GraphicState.DEAD)
106
+
107
+ if self.vx < -0.01:
108
+ self.facing_direction = Direction.WEST
109
+ if self.vx > 0.01:
110
+ self.facing_direction = Direction.EAST
111
+ if self.vy < -0.01:
112
+ self.facing_direction = Direction.NORTH
113
+ if self.vy > 0.01:
114
+ self.facing_direction = Direction.SOUTH
115
+
116
+ self.speed = self.attributes.speed
117
+
118
+ if self.can_act():
119
+ self.behavior(elapsed_time, target)
120
+
121
+ self.sprite.update(
122
+ elapsed_time, self.facing_direction, self.graphic_state
123
+ )
124
+
125
+ self._handle_terrain(elapsed_time)
126
+
127
+ if self.graphic_state == GraphicState.DEAD:
128
+ self.attackable = False
129
+ self.vx = self.vy = 0
130
+ if self.despawn_timer is None:
131
+ self.despawn_timer = self.despawn_duration
132
+ self.engine.play_sound("enemy_killed")
133
+ else:
134
+ self.despawn_timer -= elapsed_time
135
+
136
+ if self.despawn_timer <= 0.0:
137
+ self.kill()
138
+ else:
139
+ eff2r = []
140
+ for eff in self.attribute_effects:
141
+ if (
142
+ self.attributes.health < eff.health_cost
143
+ or self.attributes.magic < eff.magic_cost
144
+ or self.attributes.stamina < eff.stamina_cost
145
+ # and self.attributes.arrows >= weapon.arrow_cost
146
+ # and self.attributes.bombs >= weapon.bomb_cost
147
+ ):
148
+ eff.redundant = True
149
+ self.attributes.health_per_second += eff.health_cost
150
+ self.attributes.magic_per_second += eff.magic_cost
151
+ self.attributes.stamina_per_second += eff.stamina_cost
152
+ eff2r.append(eff)
153
+ for e in eff2r:
154
+ self.attribute_effects.remove(e)
155
+
156
+ self.attributes.update(elapsed_time)
157
+
158
+ if self._gs_lock_condition == Until.NEXT_UPDATE:
159
+ self.graphic_state_locked = False
160
+
161
+ def draw_self(self, ox: float, oy: float):
162
+ if (
163
+ self.sprite.name is None
164
+ or self.sprite.name == ""
165
+ or not self.visible
166
+ ):
167
+ return
168
+
169
+ px = self.px - ox + self.extra_ox
170
+ py = self.py - oy + self.extra_oy
171
+
172
+ if self.pz != 0:
173
+ self.engine.backend.fill_circle(
174
+ px + 0.5 * self.sprite.width,
175
+ py + 0.7 * self.sprite.height,
176
+ 0.3125 * self.sprite.width,
177
+ BLACK,
178
+ )
179
+
180
+ self.sprite.draw_self(px, py - self.pz)
181
+ # for effect in self.effects:
182
+ # effect.draw_self(px, py - self.pz)
183
+
184
+ def behavior(self, elapsed_time: float, target: Optional[Dynamic] = None):
185
+ # No default behavior
186
+ pass
187
+
188
+ def on_interaction(self, target=None, nature=Nature.WALK):
189
+ if nature == Nature.SIGNAL:
190
+ self.solid_vs_dyn = False
191
+ self.visible = False
192
+ return True
193
+
194
+ elif nature == Nature.NO_SIGNAL:
195
+ self.visible = True
196
+ self.solid_vs_dyn = True
197
+ return True
198
+ return False
199
+
200
+ def knock_back(self, vx: float, vy: float, dist: float):
201
+ self._knock_vx = vx
202
+ self._knock_vy = vy
203
+ self._knock_timer = dist
204
+ self.invincible_timer = dist + 0.2
205
+ # self.solid_vs_dyn = False
206
+ self.controllable = False
207
+ self.invincible = True
208
+ self.sprite.reset()
209
+ self.cancel_attack()
210
+
211
+ def can_act(self):
212
+ actable_states = [
213
+ GraphicState.STANDING,
214
+ GraphicState.WALKING,
215
+ GraphicState.CELEBRATING,
216
+ # GraphicState.DAMAGED,
217
+ GraphicState.PUSHING,
218
+ ]
219
+ if self.graphic_state in actable_states:
220
+ return True
221
+
222
+ return False
223
+
224
+ def perform_attack(self, slot: WeaponSlot) -> bool:
225
+ weapon = self.weapons.get(slot, None)
226
+
227
+ if weapon is None or not self.can_attack:
228
+ # print(f"Cannot attack. Weapon={weapon}")
229
+ return False
230
+
231
+ # Clean up all previous projectiles
232
+ self.cancel_attack()
233
+
234
+ if (
235
+ self.attributes.health >= weapon.health_cost
236
+ and self.attributes.magic >= weapon.magic_cost
237
+ and self.attributes.stamina >= weapon.stamina_cost
238
+ and self.attributes.arrows >= weapon.arrow_cost
239
+ and self.attributes.bombs >= weapon.bomb_cost
240
+ ):
241
+ self.attributes.health -= weapon.health_cost
242
+ self.attributes.magic -= weapon.magic_cost
243
+ self.attributes.stamina -= weapon.stamina_cost
244
+ self.attributes.arrows -= weapon.arrow_cost
245
+ self.attributes.bombs -= weapon.bomb_cost
246
+ else:
247
+ return False
248
+
249
+ if weapon.on_use(self):
250
+ self.vx = self.vy = 0.0
251
+ self._attack_timer = weapon.swing_timer
252
+ return True
253
+
254
+ return False
255
+
256
+ def cancel_attack(self):
257
+ # if self.projectiles:
258
+ for projectile in self.projectiles:
259
+ if projectile is not None:
260
+ projectile.spawn_on_death = []
261
+ projectile.kill()
262
+
263
+ self.projectiles = []
264
+
265
+ def on_death(self):
266
+ if self.spawn_on_death:
267
+ for do in self.spawn_on_death:
268
+ if do.redundant:
269
+ continue
270
+ if do.type == ObjectType.PROJECTILE:
271
+ if do.inherit_pos:
272
+ do.px = self.px
273
+ do.py = self.py
274
+ self.engine.scene.add_projectile(do)
275
+ else:
276
+ self.engine.scene.add_dynamic(do)
277
+
278
+ self.spawn_on_death = []
279
+
280
+ def _handle_terrain(self, elapsed_time: float):
281
+ e2rm = []
282
+ for effect in self.effects:
283
+ if isinstance(effect, WalkingOnGrass):
284
+ if self.walking_on == Terrain.DEFAULT:
285
+ e2rm.append(effect)
286
+
287
+ for effect in e2rm:
288
+ self.effects.remove(effect)
289
+
290
+ if self.walking_on in [Terrain.GRASS, Terrain.SHALLOW_WATER]:
291
+ self.attributes.speed_mod = 0.7
292
+ effect_active = False
293
+ for effect in self.effects:
294
+ if isinstance(effect, (WalkingOnGrass, WalkingOnWater)):
295
+ effect_active = True
296
+ effect.renew = True
297
+ break
298
+
299
+ if not effect_active:
300
+ if self.walking_on == Terrain.GRASS:
301
+ eff = WalkingOnGrass(self)
302
+ else:
303
+ eff = WalkingOnWater(self)
304
+ self.effects.append(eff)
305
+ self.engine.scene.add_effect(eff)
306
+ else:
307
+ self.attributes.speed_mod = 1.0
308
+
309
+ def equip_weapon(self, slot, weapon):
310
+ if self.weapons[slot] == weapon:
311
+ # Weapon already equipped; unequip
312
+ self.weapons[slot] = None
313
+ weapon.on_unequip(self)
314
+ return
315
+
316
+ for s, w in self.weapons.items():
317
+ if slot != s and weapon == w:
318
+ # Weapon equipped in a different slot; change
319
+ self.weapons[slot] = weapon
320
+ self.weapons[s] = None
321
+ return
322
+
323
+ if self.weapons[slot] is not None:
324
+ # Other weapon equipped; unequip that
325
+ self.weapons[slot].on_unequip(self)
326
+
327
+ self.weapons[slot] = weapon
328
+ weapon.on_equip(self)
329
+
330
+ def unequip_weapon(self, slot):
331
+ if self.weapons[slot] is not None:
332
+ self.weapons[slot].on_unequip(self)
@@ -0,0 +1,182 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List, Optional
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.terrain import Terrain
12
+ from .attributes import Attributes
13
+ from .attribute_effect import Effect
14
+ from .sprite import Sprite
15
+
16
+ if TYPE_CHECKING:
17
+ from ..engine import MimaEngine
18
+ from .projectile import Projectile
19
+
20
+
21
+ class Dynamic:
22
+ engine: MimaEngine
23
+
24
+ def __init__(
25
+ self,
26
+ name: str = "Unnamed Dynamic",
27
+ px: float = 0.0,
28
+ py: float = 0.0,
29
+ dyn_id=-1,
30
+ ):
31
+ self.name: str = name
32
+ self.dyn_id: int = dyn_id # ID given by Tiled
33
+ self.layer: int = 1
34
+
35
+ self.px: float = px
36
+ self.py: float = py
37
+ self.pz: float = 0.0
38
+ self.vx: float = 0.0
39
+ self.vy: float = 0.0
40
+ self.vz: float = 0.0
41
+ self.real_vx: float = 0.0
42
+ self.real_vy: float = 0.0
43
+ self.hitbox_px: float = 0.1
44
+ self.hitbox_py: float = 0.1
45
+ self.hitbox_width: float = 0.8
46
+ self.hitbox_height: float = 0.8
47
+ self.attribute_timer: float = 0.25
48
+ self.despawn_timer: float = 0.5
49
+ self.despawn_duration: float = 0.2
50
+ self.timer: float = 0.0
51
+ self.speed: float = 1.0
52
+
53
+ self.solid_vs_map: bool = True
54
+ self.solid_vs_dyn: bool = True
55
+ # self.is_player: bool = False
56
+ # self.is_projectile: bool = False
57
+ self.redundant: bool = False
58
+ self.persistent: bool = False
59
+ self.attackable: bool = False
60
+ self.controllable: bool = True
61
+ self.state_changed: bool = False
62
+ self.visible: bool = True
63
+ self.gravity: bool = True
64
+ self.graphic_state_locked: bool = False
65
+ self.use_acceleration: bool = False
66
+ self.use_friction: bool = False
67
+
68
+ self.alignment: Alignment = Alignment.GOOD
69
+ self.walking_on: Terrain = Terrain.DEFAULT
70
+ self.attributes: Attributes = Attributes()
71
+ self.facing_direction: Direction = Direction.SOUTH
72
+ self.graphic_state: GraphicState = GraphicState.STANDING
73
+ self._gs_lock_condition: Until = Until.UNLOCK
74
+ self.type: ObjectType = ObjectType.UNDEFINED
75
+ self.spawn_on_death: List[Dynamic] = []
76
+ self.childs: List[Dynamic] = []
77
+ self.effects: List[Projectile] = []
78
+ self.attribute_effects: List[Effect] = []
79
+
80
+ self.extra_ox: float = 0.0
81
+ self.extra_oy: float = 0.0
82
+
83
+ self.can_attack: bool = True
84
+ self.can_lift: bool = False
85
+ self.can_push: bool = False
86
+
87
+ self.sprite: Sprite = Sprite()
88
+
89
+ # Performance flags
90
+ self.update_skippable: bool = False
91
+ """Update may be skipped if object is offscreen."""
92
+ self.offscreen_collision_skippable: bool = True
93
+ """Collision checks may be skipped if object is offscreen."""
94
+ self.onscreen_collision_skippable: bool = False
95
+ """Collision checks may be skipped if object is onscreen."""
96
+ self.draw_skippable: bool = True
97
+ """Drawing the unit may be skipped if object is offscreen."""
98
+
99
+ def update(self, elapsed_time: float, target: Optional[Dynamic]):
100
+ """Update this dynamic."""
101
+ pass
102
+
103
+ def draw_self(self, ox: float, oy: float):
104
+ """Draw self to screen"""
105
+ pass
106
+
107
+ def on_interaction(
108
+ self, target: Dynamic = None, nature: Nature = Nature.WALK
109
+ ) -> bool:
110
+ """Handle interaction with other dynamic objects."""
111
+ return False
112
+
113
+ def on_death(self) -> bool:
114
+ return False
115
+
116
+ def kill(self):
117
+ self.redundant = True
118
+
119
+ def change_graphic_state(self, new_state: GraphicState) -> bool:
120
+ if self.graphic_state_locked or new_state == self.graphic_state:
121
+ return False
122
+
123
+ self.graphic_state = new_state
124
+ self.sprite.update(0.0, self.facing_direction, self.graphic_state)
125
+
126
+ return True
127
+
128
+ def lock_graphic_state(
129
+ self,
130
+ new_state: GraphicState = GraphicState.STANDING,
131
+ until: Until = Until.UNLOCK,
132
+ ):
133
+ self.change_graphic_state(new_state)
134
+ self.graphic_state_locked = True
135
+ self._gs_lock_condition = until
136
+
137
+ def unlock_graphic_state(self):
138
+ self.graphic_state_locked = False
139
+ self.change_graphic_state(GraphicState.STANDING)
140
+
141
+ def get_defense_value(self, dtype: Damage) -> int:
142
+ return self.attributes.defense[dtype]
143
+
144
+ def is_py_lower(self, other: Dynamic) -> bool:
145
+ return self.py < other.py
146
+
147
+ def get_alignment(self) -> Alignment:
148
+ return self.alignment
149
+
150
+ def add_effect(self, eff: Effect):
151
+ self.attribute_effects.append(eff)
152
+
153
+ def remove_effect(self, eff: Union[str, Effect]):
154
+ if isinstance(eff, str):
155
+ obj = [e for e in self.attribute_effects if e.effect_id == eff]
156
+ if not obj:
157
+ print("Effect not active")
158
+ return
159
+ else:
160
+ eff = obj[0]
161
+
162
+ self.attribute_effects.remove(eff)
163
+
164
+ def has_effect(self, eff: Union[str, Effect]):
165
+ if isinstance(eff, str):
166
+ for e in self.attribute_effects:
167
+ if e.effect_id == eff:
168
+ return True
169
+ return False
170
+ else:
171
+ if eff in self.attribute_effects:
172
+ return True
173
+ else:
174
+ return False
175
+
176
+ def light_radius(self):
177
+ rad = self.attributes.light_radius
178
+
179
+ for eff in self.attribute_effects:
180
+ rad += eff.light_radius
181
+
182
+ return rad
File without changes
@@ -0,0 +1,36 @@
1
+ from ...types.alignment import Alignment
2
+ from ...util.colors import Color, BLACK
3
+ from ...util.constants import HEIGHT, WIDTH
4
+ from ..dynamic import Dynamic
5
+ from ..projectile import Projectile
6
+
7
+
8
+ class ColorizeScreen(Projectile):
9
+ def __init__(
10
+ self,
11
+ color: Color = BLACK,
12
+ alpha=BLACK.alpha,
13
+ duration: float = -1.0,
14
+ to_filter: bool = False,
15
+ ):
16
+ super().__init__(0, 0, 0, 0, duration, Alignment.GOOD)
17
+ self.layer = 1
18
+ self.solid_vs_map = False
19
+ self.color = color
20
+ self.alpha = alpha
21
+ self.vanishs_after_time: bool = duration > 0
22
+ self.to_filter = to_filter
23
+
24
+ def update(self, elapsed_time: float, target: Dynamic = None):
25
+ if self.vanishs_after_time:
26
+ self.duration -= elapsed_time
27
+ if self.duration <= 0.0:
28
+ self.kill()
29
+
30
+ # self.sprite.update(elapsed_time, self.facing_direction, self.graphic_state)
31
+
32
+ def draw_self(self, ox: float, oy: float):
33
+ color = Color(self.color.red, self.color.green, self.color.blue, self.alpha)
34
+ self.engine.backend.fill_rect(
35
+ self.px, self.py, self.engine.backend.render_width, self.engine.backend.render_height, color, draw_to_filter=self.to_filter
36
+ )