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,133 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List
4
+
5
+ from ...types.alignment import Alignment
6
+ from ...util.colors import DARK_RED, TRANS_LIGHT_RED
7
+ from ...util.constants import SMALL_FONT_HEIGHT, SMALL_FONT_WIDTH
8
+ from ..projectile import Projectile
9
+
10
+ if TYPE_CHECKING:
11
+ from ...util.colors import Color
12
+ from ..dynamic import Dynamic
13
+
14
+
15
+ class DynamicDebugBox(Projectile):
16
+ def __init__(
17
+ self,
18
+ follow: Dynamic,
19
+ color: Color = TRANS_LIGHT_RED,
20
+ n_frames=1,
21
+ ids=None,
22
+ ) -> None:
23
+ super().__init__(
24
+ 0,
25
+ 0,
26
+ "DynamicDebugBox",
27
+ duration=0.0,
28
+ alignment=Alignment.NEUTRAL,
29
+ tilemap=follow.tilemap,
30
+ )
31
+ self.layer = 2
32
+ self.sprite = None
33
+
34
+ self._follow: Dynamic = follow
35
+ self._color: Color = color
36
+ self._n_frames: int = n_frames
37
+ self._ids: List[int] = ids if ids is not None else follow.chunks
38
+
39
+ def update(self, elapsed_time: float, target: Dynamic = None):
40
+ if self._n_frames >= 0:
41
+ if self._n_frames == 0:
42
+ self.kill()
43
+
44
+ self._n_frames -= 1
45
+
46
+ def draw_self(self, ox, oy, camera_name):
47
+ ppx = (
48
+ self._follow.px - ox + self._follow.hitbox_px
49
+ ) * self.engine.rtc.tile_width
50
+ ppy = (
51
+ self._follow.py - oy + self._follow.hitbox_py
52
+ ) * self.engine.rtc.tile_height
53
+ pwidth = self._follow.hitbox_width * self.engine.rtc.tile_width
54
+ pheight = self._follow.hitbox_height * self.engine.rtc.tile_height
55
+ self.engine.backend.fill_rect(
56
+ ppx, ppy, pwidth, pheight, self._color, camera_name
57
+ )
58
+ txt = ""
59
+ for i in self._ids:
60
+ txt += f"{i} "
61
+
62
+ if txt:
63
+ txt = txt.strip()
64
+ text_pw = len(txt) * SMALL_FONT_WIDTH + 1
65
+ text_ph = SMALL_FONT_HEIGHT + 1
66
+ text_ppx = ppx + pwidth / 2 - text_pw / 2
67
+ text_ppy = ppy + pheight / 2 - text_ph / 2
68
+ self.engine.backend.fill_rect(
69
+ text_ppx - 1,
70
+ text_ppy - 1,
71
+ text_pw + 1,
72
+ text_ph + 1,
73
+ DARK_RED,
74
+ camera_name,
75
+ )
76
+ self.engine.backend.draw_small_text(
77
+ txt, text_ppx, text_ppy, camera_name=camera_name
78
+ )
79
+
80
+
81
+ class StaticDebugBox(Projectile):
82
+ def __init__(self, px, py, width, height, color, n_frames=1, ids=None):
83
+ super().__init__(
84
+ px,
85
+ py,
86
+ "StaticDebugBox",
87
+ duration=0.0,
88
+ alignment=Alignment.NEUTRAL,
89
+ )
90
+ self.layer = 2
91
+ self._color = color
92
+ self._n_frames = n_frames
93
+ self._px = px
94
+ self._py = py
95
+ self._width = width
96
+ self._height = height
97
+ self._ids: List[int] = ids if ids is not None else []
98
+
99
+ def update(self, elapsed_time: float, target: Dynamic = None):
100
+ if self._n_frames <= 0:
101
+ self.kill()
102
+
103
+ self._n_frames -= 1
104
+
105
+ def draw_self(self, ox, oy, camera_name):
106
+ ppx = (self._px - ox) * self.engine.rtc.tile_width
107
+ ppy = (self._py - oy) * self.engine.rtc.tile_height
108
+ pwidth = self._width * self.engine.rtc.tile_width
109
+ pheight = self._height * self.engine.rtc.tile_height
110
+ self.engine.backend.fill_rect(
111
+ ppx, ppy, pwidth, pheight, self._color, camera_name
112
+ )
113
+ txt = ""
114
+ for i in self._ids:
115
+ txt += f"{i} "
116
+
117
+ if txt:
118
+ txt = txt.strip()
119
+ text_pw = len(txt) * self.engine.rtc.small_font_width + 1
120
+ text_ph = self.engine.rtc.small_font_height + 1
121
+ text_ppx = ppx + pwidth / 2 - text_pw / 2
122
+ text_ppy = ppy + pheight / 2 - text_ph / 2
123
+ self.engine.backend.fill_rect(
124
+ text_ppx - 1,
125
+ text_ppy - 1,
126
+ text_pw + 1,
127
+ text_ph + 1,
128
+ self.engine.rtc.color_dark_red,
129
+ camera_name,
130
+ )
131
+ self.engine.backend.draw_small_text(
132
+ txt, text_ppx, text_ppy, camera_name=camera_name
133
+ )
@@ -0,0 +1,103 @@
1
+ from typing import List
2
+
3
+ from ...types.alignment import Alignment
4
+ from ...types.blend import Blend
5
+ from ..dynamic import Dynamic
6
+ from ..projectile import Projectile
7
+
8
+
9
+ class Light(Projectile):
10
+ def __init__(
11
+ self,
12
+ follow: Dynamic,
13
+ max_size: int = 64,
14
+ fixed_size: bool = False,
15
+ update_from_target: bool = False,
16
+ ):
17
+ super().__init__(
18
+ 0,
19
+ 0,
20
+ name="Light",
21
+ tilemap=follow.tilemap,
22
+ duration=0,
23
+ alignment=Alignment.GOOD,
24
+ )
25
+ self.layer = 1
26
+ self.sprite.name = "light_small"
27
+ self.sprite.width = 48
28
+ self.sprite.height = 48
29
+ self.solid_vs_map = False
30
+ # self.moves_on_collision = False
31
+ self._follow: Dynamic = follow
32
+ self._fixed_size: bool = fixed_size
33
+ self._update_from_target: bool = update_from_target
34
+
35
+ self._timer: float = 0.2
36
+ self._timer_reset: float = 0.2
37
+ self._size_idx: int = 0
38
+
39
+ self._sizes: List[int]
40
+ self._max_size: int
41
+
42
+ self._prepare_light(max_size)
43
+
44
+ def update(self, elapsed_time: float, target: Dynamic = None):
45
+ self.px = (
46
+ self._follow.px
47
+ + self._follow.sprite.width / self.engine.rtc.tile_width * 0.5
48
+ )
49
+ self.py = (
50
+ self._follow.py
51
+ + self._follow.sprite.height / self.engine.rtc.tile_height * 0.5
52
+ )
53
+
54
+ rad = self._follow.light_radius()
55
+ if self._max_size != rad:
56
+ self._prepare_light(rad)
57
+
58
+ if self._follow.redundant:
59
+ self.kill()
60
+
61
+ if self._fixed_size:
62
+ return
63
+
64
+ self._timer -= elapsed_time
65
+ if self._timer <= 0.0:
66
+ self._timer += self._timer_reset
67
+ self._size_idx = (self._size_idx + 1) % len(self._sizes)
68
+
69
+ def draw_self(self, ox: float, oy: float, camera_name: str):
70
+ self.engine.backend.fill_circle(
71
+ (self.px - ox + self._follow.extra_ox)
72
+ * self.engine.rtc.tile_width,
73
+ (self.py - oy + self._follow.extra_oy)
74
+ * self.engine.rtc.tile_height,
75
+ self._sizes[self._size_idx] * 0.65, # 0.3125 *
76
+ self.engine.rtc.color_dark_grey,
77
+ camera_name,
78
+ blend_mode=Blend.SUB,
79
+ draw_to_filter=True,
80
+ )
81
+ self.engine.backend.fill_circle(
82
+ (self.px - ox + self._follow.extra_ox)
83
+ * self.engine.rtc.tile_width,
84
+ (self.py - oy + self._follow.extra_oy)
85
+ * self.engine.rtc.tile_height,
86
+ self._sizes[self._size_idx] * 0.5, # 0.3125 *
87
+ self.engine.rtc.color_very_light_grey,
88
+ camera_name,
89
+ blend_mode=Blend.SUB,
90
+ draw_to_filter=True,
91
+ )
92
+
93
+ def _prepare_light(self, max_size):
94
+ self._max_size = max_size
95
+ self._sizes = [max_size]
96
+ if not self._fixed_size:
97
+ self._sizes.extend(
98
+ [
99
+ int(max_size * 0.97),
100
+ int(max_size * 0.94),
101
+ int(max_size * 0.97),
102
+ ]
103
+ )
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Optional
4
+
5
+ from ...types.alignment import Alignment
6
+ from ..projectile import Projectile
7
+
8
+ if TYPE_CHECKING:
9
+ from ...maps.tilemap import Tilemap
10
+ from ..sprite import Sprite
11
+
12
+
13
+ class ShowSprite(Projectile):
14
+ def __init__(
15
+ self,
16
+ px: float,
17
+ py: float,
18
+ name: str = "ShowSprite",
19
+ *,
20
+ sprite_name: str = "",
21
+ tilemap: Optional[Tilemap] = None,
22
+ dyn_id: int = 0,
23
+ layer: int = 0,
24
+ ):
25
+ super().__init__(
26
+ px,
27
+ py,
28
+ name,
29
+ sprite_name=sprite_name,
30
+ tilemap=tilemap,
31
+ dyn_id=dyn_id,
32
+ vx=0.0,
33
+ vy=0.0,
34
+ duration=0.0,
35
+ alignment=Alignment.NEUTRAL,
36
+ )
37
+
38
+ self.layer = layer
39
+ self.draw_to_ui: bool = False
40
+
41
+ def update(self, elapsed_time: float):
42
+ self.sprite.update(
43
+ elapsed_time, self.facing_direction, self.graphic_state
44
+ )
45
+
46
+ def draw_self(self, ox: float, oy: float, camera_name: str):
47
+ if self.sprite is None:
48
+ return
49
+
50
+ self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
@@ -0,0 +1,70 @@
1
+ from ...types.direction import Direction
2
+ from ...types.graphic_state import GraphicState
3
+ from ..dynamic import Dynamic
4
+ from ..projectile import Projectile
5
+
6
+
7
+ class WalkingOnGrass(Projectile):
8
+ def __init__(
9
+ self,
10
+ follow: Dynamic,
11
+ name: str = "WalkingOnGrass",
12
+ sprite_name: str = "walking_on_grass",
13
+ ):
14
+ super().__init__(
15
+ follow.px,
16
+ follow.py,
17
+ name,
18
+ sprite_name=sprite_name,
19
+ tilemap=follow.tilemap,
20
+ vx=0,
21
+ vy=0,
22
+ duration=1.0,
23
+ alignment=follow.alignment,
24
+ )
25
+ self.layer = 0
26
+ # self.sprite.name = "simple_sheet"
27
+ # self.sprite.ox = 32
28
+ # self.sprite.oy = 10
29
+ # self.sprite.num_frames = 2
30
+ # self.sprite.timer = 0.2
31
+ # self.sprite.timer_reset = 0.2
32
+ self._follow = follow
33
+ self.renew: bool = True
34
+ self.solid_vs_map = False
35
+ # self.moves_on_collision = False
36
+
37
+ def update(self, elapsed_time: float, target: Dynamic = None):
38
+ if not self.renew:
39
+ self.kill()
40
+
41
+ self.px = (
42
+ self._follow.px
43
+ + (self._follow.sprite.width - self.sprite.width)
44
+ / 2
45
+ / self.engine.rtc.tile_width
46
+ )
47
+ self.py = (
48
+ self._follow.py
49
+ + (self._follow.sprite.height - self.sprite.height)
50
+ / self.engine.rtc.tile_height
51
+ )
52
+
53
+ if self._follow.graphic_state == GraphicState.WALKING:
54
+ self.graphic_state = GraphicState.WALKING
55
+ else:
56
+ self.graphic_state = GraphicState.STANDING
57
+ # elapsed_time = 0
58
+ self.sprite.update(elapsed_time, Direction.SOUTH, self.graphic_state)
59
+
60
+ self.renew = False
61
+
62
+ def draw_self(self, ox: float, oy: float, camera_name):
63
+ if (
64
+ self.sprite.name is None
65
+ or self.sprite.name == ""
66
+ or self.redundant
67
+ ):
68
+ return
69
+
70
+ self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
@@ -0,0 +1,57 @@
1
+ from ...types.direction import Direction
2
+ from ...types.graphic_state import GraphicState
3
+ from ..dynamic import Dynamic
4
+ from .walking_on_grass import WalkingOnGrass
5
+
6
+
7
+ class WalkingOnWater(WalkingOnGrass):
8
+ def __init__(
9
+ self,
10
+ follow: Dynamic,
11
+ name: str = "WalkingOnWater",
12
+ sprite_name: str = "walking_on_water",
13
+ ):
14
+ super().__init__(follow, name, sprite_name)
15
+ # self.sprite.name = "simple_sheet"
16
+ # self.sprite.ox = 32
17
+ # self.sprite.oy = 9
18
+ # self.sprite.num_frames = 2
19
+ # self.sprite.timer = 0.2
20
+ # self.sprite.timer_reset = 0.2
21
+ self._follow = follow
22
+ self.renew: bool = True
23
+ self.solid_vs_map = False
24
+
25
+ # def update(self, elapsed_time: float, target: Dynamic = None):
26
+ # if not self.renew:
27
+ # self.kill()
28
+
29
+ # self.px = (
30
+ # self._follow.px
31
+ # + (self._follow.sprite.width - self.sprite.width)
32
+ # / 2
33
+ # / self.engine.rtc.tile_width
34
+ # )
35
+ # self.py = (
36
+ # self._follow.py
37
+ # + (self._follow.sprite.height - self.sprite.height)
38
+ # / self.engine.rtc.tile_height
39
+ # )
40
+
41
+ # if self._follow.graphic_state == GraphicState.STANDING:
42
+ # elapsed_time = 0
43
+ # self.sprite.update(
44
+ # elapsed_time, Direction.SOUTH, GraphicState.STANDING
45
+ # )
46
+
47
+ # self.renew = False
48
+
49
+ def draw_self(self, ox: float, oy: float, camera_name: str):
50
+ if (
51
+ self.sprite.name is None
52
+ or self.sprite.name == ""
53
+ or self.redundant
54
+ ):
55
+ return
56
+
57
+ self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
mima/objects/loader.py ADDED
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import TYPE_CHECKING
5
+
6
+ from ..types.object import ObjectType
7
+ from .world.color_gate import ColorGate
8
+ from .world.color_switch import ColorSwitch
9
+ from .world.container import Container
10
+ from .world.floor_switch import FloorSwitch
11
+ from .world.gate import Gate
12
+ from .world.light_source import LightSource
13
+ from .world.logic_gate import LogicGate
14
+ from .world.movable import Movable
15
+ from .world.oneway import Oneway
16
+ from .world.pickup import Pickup
17
+
18
+ # from .world.pickup import Pickup
19
+ from .world.switch import Switch
20
+ from .world.teleport import Teleport
21
+
22
+ if TYPE_CHECKING:
23
+ from ..engine import MimaEngine
24
+
25
+ LOG = logging.getLogger(__name__)
26
+
27
+
28
+ class ObjectLoader:
29
+ engine: MimaEngine
30
+
31
+ def __init__(self, object_dispatcher, creature_dispatcher):
32
+ self._base_dispatcher = {
33
+ "color_switch": ColorSwitch.load_from_tiled_object,
34
+ "color_gate": ColorGate.load_from_tiled_object,
35
+ "container": Container.load_from_tiled_object,
36
+ "floor_switch": FloorSwitch.load_from_tiled_object,
37
+ "gate": Gate.load_from_tiled_object,
38
+ "light_source": LightSource.load_from_tiled_object,
39
+ "logic_gate": LogicGate.load_from_tiled_object,
40
+ "movable": Movable.load_from_tiled_object,
41
+ "oneway": Oneway.load_from_tiled_object,
42
+ "pickup": Pickup.load_from_tiled_object,
43
+ "switch": Switch.load_from_tiled_object,
44
+ "teleport": Teleport.load_from_tiled_object,
45
+ "creature": self.load_creature_from_tiled_object,
46
+ }
47
+ self._cstm_object_dispatcher = object_dispatcher
48
+ self._cstm_creature_dispatcher = creature_dispatcher
49
+ # self._current_map = None
50
+
51
+ def populate_dynamics(self, tilemap, dynamics):
52
+ # self._current_map = tilemap
53
+ for obj in tilemap.objects:
54
+ px = obj.px / self.engine.rtc.tile_width
55
+ py = obj.py / self.engine.rtc.tile_height
56
+ width = max(obj.width / self.engine.rtc.tile_width, 1.0)
57
+ height = max(obj.height / self.engine.rtc.tile_height, 1.0)
58
+
59
+ LOG.debug(
60
+ f"Loading object {obj.name} ({obj.object_id}): {obj.type}) "
61
+ f"at {px, py}."
62
+ )
63
+ if obj.type in self._cstm_object_dispatcher:
64
+ dispatcher = self._cstm_object_dispatcher
65
+ else:
66
+ dispatcher = self._base_dispatcher
67
+
68
+ try:
69
+ dynamics.extend(
70
+ dispatcher[obj.type](obj, px, py, width, height, tilemap)
71
+ )
72
+ except (KeyError, ValueError):
73
+ LOG.exception(
74
+ f"Failed to load '{obj.name}' ({obj.object_id}: " f"{obj.type})"
75
+ )
76
+ raise
77
+
78
+ # Connect listener IDs to actual listeners
79
+ for dyn in dynamics:
80
+ if dyn.type in [
81
+ ObjectType.SWITCH,
82
+ ObjectType.FLOOR_SWITCH,
83
+ ObjectType.LOGIC_GATE,
84
+ ]:
85
+ for listener_id in dyn.listener_ids:
86
+ for listener in dynamics:
87
+ if listener.dyn_id == listener_id:
88
+ dyn.listeners.append(listener)
89
+
90
+ if listener.type == ObjectType.LOGIC_GATE:
91
+ if listener.input_id1 == -1:
92
+ listener.input_id1 = dyn.dyn_id
93
+ elif listener.input_id2 == -1:
94
+ listener.input_id2 = dyn.dyn_id
95
+ else:
96
+ LOG.warning(
97
+ f"Object with ID {dyn.dyn_id} "
98
+ "has no free input port at "
99
+ f"Logic Gate {listener.dyn_id}"
100
+ )
101
+
102
+ def load_creature_from_tiled_object(self, obj, px, py, width, height, tilemap):
103
+ creature_type = obj.get_string("creature_type")
104
+ try:
105
+ return self._cstm_creature_dispatcher[creature_type](
106
+ obj, px, py, width, height, tilemap
107
+ )
108
+ except (KeyError, ValueError):
109
+ LOG.exception(f"Failed to load '{obj.name}' ({obj.object_id}: {obj.type})")
110
+ # raise
111
+ return []
@@ -0,0 +1,111 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from ..types.alignment import Alignment
6
+ from ..types.damage import Damage
7
+ from ..types.object import ObjectType
8
+ from .dynamic import Dynamic
9
+
10
+ if TYPE_CHECKING:
11
+ from ..maps.tilemap import Tilemap
12
+
13
+
14
+ class Projectile(Dynamic):
15
+ def __init__(
16
+ self,
17
+ px: float,
18
+ py: float,
19
+ name: str = "Projectile",
20
+ *,
21
+ sprite_name: str = "",
22
+ tilemap: Tilemap = None,
23
+ dyn_id: int = 1000,
24
+ vx: float = 0.0,
25
+ vy: float = 0.0,
26
+ duration: float,
27
+ alignment: Alignment,
28
+ ):
29
+ super().__init__(
30
+ px,
31
+ py,
32
+ name,
33
+ sprite_name=sprite_name,
34
+ tilemap=tilemap,
35
+ dyn_id=dyn_id,
36
+ )
37
+
38
+ self.type = ObjectType.PROJECTILE
39
+ self.vx = vx
40
+ self.vy = vy
41
+ self.pz = 0.5
42
+ self.duration = duration
43
+ self.alignment = alignment
44
+
45
+ self.solid_vs_dyn = False
46
+ self.solid_vs_map = True
47
+ self.is_projectile = True # redundant
48
+ self.attackable = False
49
+ self.one_hit = False
50
+ self.inherit_pos = False
51
+ self.gravity = False
52
+ self.moves_on_collision = True
53
+ self.change_solid_vs_map_timer = 0
54
+ self.dtype = Damage.BODY
55
+ self.damage = 0
56
+
57
+ def update(self, elapsed_time: float, target: Dynamic = None):
58
+ self.duration -= elapsed_time
59
+ if self.duration <= 0.0:
60
+ self.kill()
61
+
62
+ if self.change_solid_vs_map_timer > 0:
63
+ self.change_solid_vs_map_timer -= elapsed_time
64
+ if self.change_solid_vs_map_timer <= 0:
65
+ self.solid_vs_map = not self.solid_vs_map
66
+ self.change_solid_vs_map_timer = 0
67
+
68
+ self.speed = self.attributes.speed
69
+ # if "body" not in self.name:
70
+ # print(self.name, self.pz)
71
+ self.sprite.update(
72
+ elapsed_time, self.facing_direction, self.graphic_state
73
+ )
74
+
75
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
76
+ if (
77
+ self.sprite.name is None
78
+ or self.sprite.name == ""
79
+ or self.redundant
80
+ ):
81
+ return
82
+
83
+ self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
84
+
85
+ def on_death(self) -> bool:
86
+ if self.spawn_on_death:
87
+ for do in self.spawn_on_death:
88
+ if do.type == ObjectType.PROJECTILE:
89
+ if self.inherit_pos:
90
+ do.px = self.px
91
+ do.py = self.py
92
+ self.engine.get_view().add_projectile(
93
+ do, self.tilemap.name
94
+ )
95
+ else:
96
+ self.engine.get_view().add_dynamic(do, self.tilemap.name)
97
+
98
+ return False
99
+
100
+ def cancel(self):
101
+ if self.spawn_on_death:
102
+ for do in self.spawn_on_death:
103
+ if do.type == ObjectType.PROJECTILE:
104
+ do.cancel() # Projectile will kill itself
105
+ else:
106
+ do.kill()
107
+ self.kill()
108
+ return True
109
+
110
+ def __str__(self):
111
+ return f"P({self.name}, {self.dyn_id})"