mima-engine 0.1.5__py3-none-any.whl → 0.2.1__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 (80) hide show
  1. mima/__init__.py +1 -1
  2. mima/backend/pygame_assets.py +14 -8
  3. mima/backend/pygame_audio.py +5 -2
  4. mima/backend/pygame_backend.py +255 -57
  5. mima/backend/pygame_camera.py +63 -0
  6. mima/backend/pygame_events.py +369 -120
  7. mima/collision.py +182 -111
  8. mima/engine.py +155 -15
  9. mima/maps/tiled/tiled_map.py +3 -3
  10. mima/maps/tiled/tiled_tileset.py +1 -0
  11. mima/maps/tilemap.py +78 -15
  12. mima/maps/tileset.py +8 -2
  13. mima/maps/transition_map.py +6 -8
  14. mima/mode_engine.py +80 -0
  15. mima/objects/animated_sprite.py +23 -15
  16. mima/objects/attributes.py +3 -0
  17. mima/objects/creature.py +54 -17
  18. mima/objects/dynamic.py +30 -8
  19. mima/objects/effects/colorize_screen.py +22 -6
  20. mima/objects/effects/debug_box.py +124 -0
  21. mima/objects/effects/light.py +21 -30
  22. mima/objects/effects/show_sprite.py +39 -0
  23. mima/objects/effects/walking_on_grass.py +25 -7
  24. mima/objects/effects/walking_on_water.py +17 -6
  25. mima/objects/loader.py +24 -13
  26. mima/objects/projectile.py +21 -6
  27. mima/objects/sprite.py +7 -8
  28. mima/objects/world/color_gate.py +5 -2
  29. mima/objects/world/color_switch.py +12 -6
  30. mima/objects/world/container.py +17 -8
  31. mima/objects/world/floor_switch.py +8 -4
  32. mima/objects/world/gate.py +8 -5
  33. mima/objects/world/light_source.py +11 -9
  34. mima/objects/world/logic_gate.py +8 -7
  35. mima/objects/world/movable.py +72 -28
  36. mima/objects/world/oneway.py +14 -9
  37. mima/objects/world/pickup.py +10 -5
  38. mima/objects/world/switch.py +28 -25
  39. mima/objects/world/teleport.py +76 -55
  40. mima/scene_engine.py +19 -20
  41. mima/scripts/command.py +16 -2
  42. mima/scripts/commands/change_map.py +23 -4
  43. mima/scripts/commands/equip_weapon.py +23 -0
  44. mima/scripts/commands/give_item.py +5 -3
  45. mima/scripts/commands/move_map.py +9 -9
  46. mima/scripts/commands/parallel.py +16 -3
  47. mima/scripts/commands/present_item.py +7 -5
  48. mima/scripts/commands/screen_fade.py +30 -12
  49. mima/scripts/commands/serial.py +30 -7
  50. mima/scripts/commands/set_spawn_map.py +6 -3
  51. mima/scripts/commands/show_choices.py +16 -7
  52. mima/scripts/commands/show_dialog.py +110 -3
  53. mima/scripts/script_processor.py +41 -20
  54. mima/states/game_state.py +2 -0
  55. mima/states/memory.py +28 -0
  56. mima/states/quest.py +2 -3
  57. mima/types/keys.py +48 -0
  58. mima/types/mode.py +4 -10
  59. mima/types/player.py +9 -0
  60. mima/types/position.py +13 -0
  61. mima/types/tile_collision.py +11 -0
  62. mima/types/window.py +44 -0
  63. mima/usables/item.py +1 -0
  64. mima/util/colors.py +5 -0
  65. mima/util/constants.py +6 -0
  66. mima/util/functions.py +27 -0
  67. mima/util/input_defaults.py +109 -0
  68. mima/util/runtime_config.py +234 -30
  69. mima/util/trading_item.py +20 -0
  70. mima/view/camera.py +160 -19
  71. mima/view/mima_mode.py +612 -0
  72. mima/view/mima_scene.py +225 -0
  73. mima/view/mima_view.py +12 -0
  74. mima/view/mima_window.py +153 -0
  75. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/METADATA +4 -2
  76. mima_engine-0.2.1.dist-info/RECORD +128 -0
  77. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/WHEEL +1 -1
  78. mima/view/scene.py +0 -322
  79. mima_engine-0.1.5.dist-info/RECORD +0 -114
  80. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/top_level.txt +0 -0
mima/maps/tilemap.py CHANGED
@@ -4,11 +4,11 @@ import logging
4
4
  import math
5
5
  from typing import TYPE_CHECKING, Dict, List, Optional
6
6
 
7
- from ..util.constants import TILE_HEIGHT, TILE_WIDTH
7
+ from ..types.tile_collision import TileCollision
8
+ from .template import Template
8
9
  from .tile_info import TileInfo
9
10
  from .tile_layer import TileLayer
10
11
  from .tileset_info import TilesetInfo
11
- from .template import Template
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from ..objects.dynamic import Dynamic
@@ -20,17 +20,17 @@ LOG = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  class Tilemap(Template):
23
-
24
23
  def __init__(self, name: str):
25
24
  super().__init__(name)
26
25
  self.width: int = 0
27
26
  self.height: int = 0
28
- self.tile_width = TILE_WIDTH
29
- self.tile_height = TILE_HEIGHT
27
+ self.tile_width = self.engine.rtc.tile_width
28
+ self.tile_height = self.engine.rtc.tile_height
30
29
 
31
30
  self._layers: List[TileLayer] = []
32
31
  self._tilesets: List[TilesetInfo] = []
33
32
  self._cache: Dict[int, TileInfo] = {}
33
+ self._objects: List[Template] = []
34
34
 
35
35
  def populate_dynamics(self, dynamics: List[Dynamic]) -> bool:
36
36
  """Load all map-related objects into the game."""
@@ -54,6 +54,7 @@ class Tilemap(Template):
54
54
  visible_tiles_ex: int,
55
55
  visible_tiles_ey: int,
56
56
  layer_pos: int = 0,
57
+ camera_name: str = "display",
57
58
  ):
58
59
  # Get offsets for smooth movement
59
60
  tile_ox = (ox - math.floor(ox)) * self.tile_width
@@ -79,13 +80,12 @@ class Tilemap(Template):
79
80
  layer_visible_tiles_sy -= 1
80
81
 
81
82
  # Draw visible tiles of the map
82
- for x in range(layer_visible_tiles_sx, int(visible_tiles_ex) + 1):
83
+ for x in range(layer_visible_tiles_sx, int(visible_tiles_ex) + 2):
83
84
  for y in range(
84
- layer_visible_tiles_sy, int(visible_tiles_ey) + 1
85
+ layer_visible_tiles_sy, int(visible_tiles_ey) + 2
85
86
  ):
86
87
  tile_index = layer.get_index(
87
- int(x + math.floor(ox)),
88
- int(y + math.floor(oy)),
88
+ int(x + math.floor(ox)), int(y + math.floor(oy))
89
89
  )
90
90
  if tile_index <= 0:
91
91
  # Zero means the tile was not set in Tiled
@@ -108,6 +108,7 @@ class Tilemap(Template):
108
108
  sy * self.tile_height,
109
109
  self.tile_width,
110
110
  self.tile_height,
111
+ camera_name,
111
112
  )
112
113
  return True
113
114
 
@@ -115,17 +116,56 @@ class Tilemap(Template):
115
116
  return False
116
117
 
117
118
  def is_solid(
118
- self, px: int, py: int, layer_pos: Optional[int] = None
119
+ self,
120
+ px: int,
121
+ py: int,
122
+ layer_pos: Optional[int] = None,
123
+ collision: TileCollision = TileCollision.TOP,
119
124
  ) -> bool:
120
- tile = self.get_tile(px, py, layer_pos)
121
- if tile is not None and tile.solid:
122
- return True
125
+ if collision in (TileCollision.TOP, TileCollision.BOTTOM):
126
+ tile = self.get_tile(
127
+ px,
128
+ py,
129
+ layer_pos,
130
+ ascending=(collision == TileCollision.BOTTOM),
131
+ )
132
+
133
+ if tile is not None and tile.solid:
134
+ return True
135
+ return False
136
+
137
+ tiles = self.get_tiles(px, py, layer_pos)
138
+ if collision in (TileCollision.TOP_2, TileCollision.TOP_3):
139
+ tiles = tiles[::-1]
140
+
141
+ for idx, tile in enumerate(self.get_tiles(px, py, layer_pos)):
142
+ if tile.solid:
143
+ return True
144
+ if idx == 1 and collision in (
145
+ TileCollision.TOP_2,
146
+ TileCollision.BOTTOM_2,
147
+ ):
148
+ break
149
+ elif idx == 2 and collision in (
150
+ TileCollision.TOP_3,
151
+ TileCollision.BOTTOM_3,
152
+ ):
153
+ break
123
154
  return False
124
155
 
125
156
  def get_tile(
126
- self, px: int, py: int, layer_pos: Optional[int] = None
157
+ self,
158
+ px: int,
159
+ py: int,
160
+ layer_pos: Optional[int] = None,
161
+ ascending=False,
127
162
  ) -> Optional[Tile]:
128
- for layer in self._layers[::-1]:
163
+ if ascending:
164
+ layers = self._layers
165
+ else:
166
+ layers = self._layers[::-1]
167
+
168
+ for layer in layers:
129
169
  if layer_pos is not None and layer_pos != layer.layer_pos:
130
170
  continue
131
171
  tile_index = layer.get_index(math.floor(px), math.floor(py))
@@ -139,6 +179,21 @@ class Tilemap(Template):
139
179
 
140
180
  return None
141
181
 
182
+ def get_tiles(self, px: int, py: int, layer_pos: Optional[int] = None):
183
+ tiles = []
184
+ for layer in self._layers:
185
+ if layer_pos is not None and layer_pos != layer.layer_pos:
186
+ continue
187
+ tile_index = layer.get_index(math.floor(px), math.floor(py))
188
+ if tile_index not in self._cache:
189
+ if not self._load_to_cache(tile_index):
190
+ continue
191
+ info = self._cache[tile_index]
192
+ if info.tile is not None:
193
+ tiles.append(info.tile)
194
+
195
+ return tiles
196
+
142
197
  def _load_to_cache(self, tile_index: int) -> bool:
143
198
  tileset = None
144
199
  firstgid = 0
@@ -157,3 +212,11 @@ class Tilemap(Template):
157
212
 
158
213
  self._cache[tile_index] = TileInfo(tileset=tileset, tile=tile)
159
214
  return True
215
+
216
+ def trigger_new_frame(self):
217
+ for tsinfo in self._tilesets:
218
+ tsinfo.tileset.trigger_new_frame()
219
+
220
+ @property
221
+ def objects(self) -> List[Template]:
222
+ return []
mima/maps/tileset.py CHANGED
@@ -17,10 +17,13 @@ class Tileset:
17
17
 
18
18
  self.tiles: List[Tile] = []
19
19
  self.animated_tiles: List[Tile] = []
20
+ self._is_new_frame: bool = False
20
21
 
21
22
  def update(self, elapsed_time: float) -> bool:
22
- for tile in self.animated_tiles:
23
- tile.update(elapsed_time)
23
+ if self._is_new_frame:
24
+ for tile in self.animated_tiles:
25
+ tile.update(elapsed_time)
26
+ self._is_new_frame = False
24
27
 
25
28
  return True
26
29
 
@@ -30,3 +33,6 @@ class Tileset:
30
33
  return tile
31
34
 
32
35
  return self.tiles[0]
36
+
37
+ def trigger_new_frame(self):
38
+ self._is_new_frame = True
@@ -5,8 +5,6 @@ from typing import TYPE_CHECKING, Optional
5
5
  from ..util.constants import (
6
6
  MAP_TRANSITION_DURATION_FACTOR,
7
7
  MOVE_MAP_DURATION,
8
- TILE_HEIGHT,
9
- TILE_WIDTH,
10
8
  UI_HIGHT,
11
9
  )
12
10
  from .tilemap import Tilemap
@@ -35,18 +33,18 @@ class TransitionMap(Tilemap):
35
33
 
36
34
  self.width: int = self.src_map.width
37
35
  self.height: int = self.src_map.height
38
- self.tiles_nx = self.engine.backend.render_width / TILE_WIDTH
39
- self.tiles_ny = self.engine.backend.render_height / TILE_HEIGHT
36
+ self.tiles_nx = (
37
+ self.engine.backend.render_width / self.engine.rtc.tile_width
38
+ )
39
+ self.tiles_ny = (
40
+ self.engine.backend.render_height / self.engine.rtc.tile_height
41
+ )
40
42
 
41
43
  if self.vx != 0:
42
44
  self.step_size = self.duration / (self.tiles_nx * 0.75)
43
45
  elif self.vy != 0:
44
46
  self.step_size = self.duration / ((self.tiles_ny - 1) * 0.75)
45
47
 
46
- # print(
47
- # f"Step Size={self.step_size}, Duration={self.duration}, Size={WIDTH, HEIGHT}, TileSize={TILE_WIDTH, TILE_HEIGHT} "
48
- # )
49
-
50
48
  def update(self, elapsed_time: float) -> bool:
51
49
  self.progress = self.time_so_far / self.step_size
52
50
  self.time_so_far += elapsed_time
mima/mode_engine.py ADDED
@@ -0,0 +1,80 @@
1
+ import logging
2
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
3
+
4
+ from .engine import MimaEngine
5
+ from .states.game_state import GameState
6
+ from .types.keys import Key as K
7
+ from .types.mode import Mode
8
+ from .types.nature import Nature
9
+ from .types.player import Player
10
+ from .view.mima_mode import MimaMode
11
+
12
+ # from .view.mima_scene import MimaScene
13
+ LOG = logging.getLogger(__name__)
14
+
15
+
16
+ class MimaModeEngine(MimaEngine):
17
+ def __init__(
18
+ self,
19
+ init_file: str,
20
+ config_path: str,
21
+ default_config: Dict[str, Any],
22
+ platform="PC",
23
+ caption: str = "MimaEngine",
24
+ ):
25
+ super().__init__(
26
+ init_file, config_path, default_config, platform, caption
27
+ )
28
+
29
+ self.modes: Dict[Mode, MimaMode] = {}
30
+ self.mode: Optional[MimaMode] = None
31
+
32
+ self.mode_stack: List[Mode] = []
33
+
34
+ self.draw_chunks: bool = False
35
+ self.draw_chunk_info: bool = False
36
+ self.disable_filter: bool = False
37
+ self._timer = 1.0
38
+
39
+ def on_user_create(self):
40
+ # change_mode(Mode.LOADING)
41
+ # TODO add example modes
42
+ return True
43
+
44
+ def on_user_update(self, elapsed_time: float):
45
+ self.audio.update(elapsed_time)
46
+
47
+ # print("Update start")
48
+
49
+ if not self.mode.update(elapsed_time):
50
+ LOG.critical(f"Update of mode {self.mode} failed.")
51
+ return False
52
+ self._timer -= elapsed_time
53
+ if self._timer <= 0.0:
54
+ self._timer += 1.0
55
+ LOG.debug(
56
+ f"Updated {self.mode} (Current Stack:"
57
+ f"{[m.name for m in self.mode_stack]})"
58
+ )
59
+ # print("Update end")
60
+ return True
61
+
62
+ def change_mode(self, mode: Mode):
63
+ if mode in self.mode_stack:
64
+ self.mode_stack.remove(mode)
65
+ self.mode_stack.append(mode)
66
+ if self.mode is not None:
67
+ self.mode.unload()
68
+ self.mode = self.modes[mode]
69
+ self.mode.load()
70
+
71
+ def return_mode(self):
72
+ LOG.debug(
73
+ "Returning to previous mode. Stack: %s", str(self.mode_stack)
74
+ )
75
+ self.mode_stack.pop()
76
+ self.mode = self.modes[self.mode_stack[-1]]
77
+ self.mode.load()
78
+
79
+ def get_view(self):
80
+ return self.mode
@@ -4,7 +4,6 @@ from typing import Any, Dict, Optional
4
4
 
5
5
  from ..types.direction import Direction
6
6
  from ..types.graphic_state import GraphicState
7
- from ..util.constants import TILE_HEIGHT, TILE_WIDTH
8
7
 
9
8
  LOG = logging.getLogger(__name__)
10
9
 
@@ -35,7 +34,7 @@ class AnimatedSprite:
35
34
  "tileset": tileset_name,
36
35
  "image": image_name,
37
36
  "sprite": self._sprite_name,
38
- },
37
+ }
39
38
  )
40
39
  tileset = self.engine.assets.get_tileset(tileset_name)
41
40
 
@@ -56,9 +55,11 @@ class AnimatedSprite:
56
55
  self._timer: float = data["duration"][0]
57
56
  else:
58
57
  LOG.debug(
59
- f"Sprite information uncomplete. Tileset={tileset_name}, Image"
60
- f"={image_name}, Sprite={self._sprite_name}. Will continue without "
61
- "sprite."
58
+ "Sprite information uncomplete. Tileset=%s, Image=%s, Sprite="
59
+ "%s. Will continue without sprite.",
60
+ tileset_name,
61
+ image_name,
62
+ self._sprite_name,
62
63
  )
63
64
  self.name = self._tileset_name = self._sprite_name = ""
64
65
 
@@ -103,14 +104,20 @@ class AnimatedSprite:
103
104
  self._last_direction = direction
104
105
  self._last_graphic_state = graphic_state
105
106
 
106
- def draw_self(self, px: float, py: float, absolute_position: bool = False):
107
+ def draw_self(
108
+ self,
109
+ px: float,
110
+ py: float,
111
+ camera_name: str = "display",
112
+ absolute_position: bool = False,
113
+ ):
107
114
  if self.name == "":
108
115
  return
109
116
 
110
117
  data = self._get_data(self._last_graphic_state, self._last_direction)
111
118
  if not absolute_position:
112
- px *= TILE_WIDTH
113
- py *= TILE_HEIGHT
119
+ px *= self.engine.rtc.tile_width
120
+ py *= self.engine.rtc.tile_height
114
121
  px, py = math.floor(px), math.floor(py)
115
122
  try:
116
123
  self.engine.backend.draw_partial_sprite(
@@ -121,8 +128,9 @@ class AnimatedSprite:
121
128
  data["oy"][self._frame_index] * self.height,
122
129
  self.width,
123
130
  self.height,
131
+ camera_name,
124
132
  )
125
- except KeyError as err:
133
+ except KeyError:
126
134
  LOG.exception(
127
135
  f"Data of {self._tileset_name, self.name} is malformed: {data}"
128
136
  )
@@ -180,15 +188,15 @@ class AnimatedSprite:
180
188
  data = self._sprites.get(
181
189
  graphic_state, self._sprites.get(GraphicState.STANDING, {})
182
190
  )
183
- data = data.get(
184
- direction,
185
- data.get(Direction.SOUTH, {}),
186
- )
191
+ data = data.get(direction, data.get(Direction.SOUTH, {}))
187
192
  if not data:
188
193
  try:
189
194
  LOG.debug(
190
- f"Animation of sprite {self._tileset_name,self._sprite_name}"
191
- f" is empty for {graphic_state.name, direction.name} "
195
+ "Animation of sprite %s,%s is empty for %s, %s ",
196
+ self._tileset_name,
197
+ self._sprite_name,
198
+ graphic_state.name,
199
+ direction.name,
192
200
  )
193
201
  except Exception:
194
202
  # print(graphic_state, direction)
@@ -77,6 +77,9 @@ class Attributes:
77
77
  self.wisdom: int = 0
78
78
  self.initiative: int = 0
79
79
 
80
+ self.sell_factor: float = 1.0
81
+ self.buy_factor: float = 1.0
82
+
80
83
  self.experience: int = 0
81
84
  self.defense: Dict[Damage, int] = {dt: 0 for dt in Damage}
82
85
 
mima/objects/creature.py CHANGED
@@ -1,11 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, List, Optional
3
+ from typing import TYPE_CHECKING, Dict, List, Optional
4
4
 
5
5
  from ..types.direction import Direction
6
6
  from ..types.graphic_state import GraphicState, Until
7
7
  from ..types.nature import Nature
8
8
  from ..types.object import ObjectType
9
+ from ..types.player import Player
9
10
  from ..types.terrain import Terrain
10
11
  from ..types.weapon_slot import WeaponSlot
11
12
 
@@ -14,28 +15,35 @@ from ..util.colors import BLACK
14
15
  from ..util.constants import DEFAULT_KNOCK_SPEED
15
16
  from .animated_sprite import AnimatedSprite
16
17
  from .dynamic import Dynamic
18
+ from .effects.light import Light
17
19
  from .effects.walking_on_grass import WalkingOnGrass
18
20
  from .effects.walking_on_water import WalkingOnWater
19
21
  from .projectile import Projectile
20
22
 
21
- # if TYPE_CHECKING:
22
- # from ..usables.weapon import Weapon
23
+ if TYPE_CHECKING:
24
+ from ..maps.tilemap import Tilemap
25
+ from ..usables.weapon import Weapon
26
+ from ..util.trading_item import TradingItem
23
27
 
24
28
 
25
29
  class Creature(Dynamic):
26
30
  def __init__(
27
31
  self,
28
- name: str,
29
- tileset_name: str,
30
- image_name: str,
31
- sprite_name: str,
32
- px: float,
33
- py: float,
32
+ px: float = 0,
33
+ py: float = 0,
34
+ name: str = "Unnamed Creature",
35
+ *,
36
+ tilemap: Tilemap = None,
34
37
  dyn_id: int = -1,
38
+ tileset_name: str = "",
39
+ image_name: str = "",
40
+ sprite_name: str = "",
41
+ player: Player = Player.P0,
35
42
  ):
36
- super().__init__(name, px, py, dyn_id)
43
+ super().__init__(px, py, name, tilemap, dyn_id)
37
44
 
38
45
  self.sprite = AnimatedSprite(tileset_name, image_name, sprite_name)
46
+ self.player = player
39
47
  self.type = ObjectType.CREATURE
40
48
  self.knock_speed: float = DEFAULT_KNOCK_SPEED
41
49
 
@@ -43,6 +51,7 @@ class Creature(Dynamic):
43
51
  # self.sprite.num_frames = 2
44
52
  self.attackable = True
45
53
  self.knockable = True
54
+ self.moves_on_collision = True
46
55
 
47
56
  self._knock_vx: float = 0.0
48
57
  self._knock_vy: float = 0.0
@@ -64,9 +73,23 @@ class Creature(Dynamic):
64
73
  WeaponSlot.FIRST_HAND: None,
65
74
  WeaponSlot.SECOND_HAND: None,
66
75
  }
76
+ self._light = None
67
77
 
68
- def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
78
+ # self.attributes.speed = 1.0
79
+ # self.use_acceleration = True
80
+ # self.use_friction = True
81
+ # self.attributes.friction = 0.1
82
+
83
+ def start_shining(self):
84
+ self._light = Light(self, fixed_size=True, update_from_target=True)
85
+ self.engine.get_view().add_effect(self._light, self.tilemap.name)
69
86
 
87
+ def stop_shining(self):
88
+ if self._light is not None:
89
+ self._light.kill()
90
+ self._light = None
91
+
92
+ def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
70
93
  if not self.visible:
71
94
  self.vx = self.vy = 0.0
72
95
  return
@@ -89,6 +112,7 @@ class Creature(Dynamic):
89
112
  self._invincible_timer -= elapsed_time
90
113
  if self._invincible_timer <= 0.0:
91
114
  self.invincible = False
115
+ self.knockable = True
92
116
  self._invincible_timer = 0.0
93
117
 
94
118
  if self._attack_timer > 0.0:
@@ -159,7 +183,7 @@ class Creature(Dynamic):
159
183
  if self._gs_lock_condition == Until.NEXT_UPDATE:
160
184
  self.graphic_state_locked = False
161
185
 
162
- def draw_self(self, ox: float, oy: float):
186
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
163
187
  if (
164
188
  self.sprite.name is None
165
189
  or self.sprite.name == ""
@@ -176,9 +200,10 @@ class Creature(Dynamic):
176
200
  py + 0.7 * self.sprite.height,
177
201
  0.3125 * self.sprite.width,
178
202
  BLACK,
203
+ camera_name,
179
204
  )
180
205
 
181
- self.sprite.draw_self(px, py - self.pz)
206
+ self.sprite.draw_self(px, py - self.pz, camera_name)
182
207
  # for effect in self.effects:
183
208
  # effect.draw_self(px, py - self.pz)
184
209
 
@@ -203,7 +228,9 @@ class Creature(Dynamic):
203
228
  self._knock_vx = vx
204
229
  self._knock_vy = vy
205
230
  self._knock_timer = dist
206
- self.invincible_timer = dist + 0.2
231
+
232
+ self.knockable = False
233
+ self._invincible_timer = dist + 0.5
207
234
  # self.solid_vs_dyn = False
208
235
  self.controllable = False
209
236
  self.invincible = True
@@ -273,9 +300,11 @@ class Creature(Dynamic):
273
300
  if do.inherit_pos:
274
301
  do.px = self.px
275
302
  do.py = self.py
276
- self.engine.scene.add_projectile(do)
303
+ self.engine.get_view().add_projectile(
304
+ do, self.tilemap.name
305
+ )
277
306
  else:
278
- self.engine.scene.add_dynamic(do)
307
+ self.engine.get_view().add_dynamic(do, self.tilemap.name)
279
308
 
280
309
  self.spawn_on_death = []
281
310
 
@@ -304,7 +333,7 @@ class Creature(Dynamic):
304
333
  else:
305
334
  eff = WalkingOnWater(self)
306
335
  self.effects.append(eff)
307
- self.engine.scene.add_effect(eff)
336
+ self.engine.get_view().add_effect(eff, self.tilemap.name)
308
337
  else:
309
338
  self.attributes.speed_mod = 1.0
310
339
 
@@ -318,6 +347,8 @@ class Creature(Dynamic):
318
347
  for s, w in self.weapons.items():
319
348
  if slot != s and weapon == w:
320
349
  # Weapon equipped in a different slot; change
350
+ if self.weapons[slot] is not None:
351
+ self.weapons[slot].on_unequip(self)
321
352
  self.weapons[slot] = weapon
322
353
  self.weapons[s] = None
323
354
  return
@@ -332,3 +363,9 @@ class Creature(Dynamic):
332
363
  def unequip_weapon(self, slot):
333
364
  if self.weapons[slot] is not None:
334
365
  self.weapons[slot].on_unequip(self)
366
+
367
+ def get_trading_items(self) -> List[TradingItem]:
368
+ return []
369
+
370
+ def __str__(self):
371
+ return f"C({self.name}, {self.dyn_id})"
mima/objects/dynamic.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, List, Optional
3
+ from typing import TYPE_CHECKING, List, Optional, Union
4
4
 
5
5
  from ..types.alignment import Alignment
6
6
  from ..types.damage import Damage
@@ -8,13 +8,15 @@ from ..types.direction import Direction
8
8
  from ..types.graphic_state import GraphicState, Until
9
9
  from ..types.nature import Nature
10
10
  from ..types.object import ObjectType
11
+ from ..types.player import Player
11
12
  from ..types.terrain import Terrain
12
- from .attributes import Attributes
13
13
  from .attribute_effect import Effect
14
+ from .attributes import Attributes
14
15
  from .sprite import Sprite
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from ..engine import MimaEngine
19
+ from ..maps.tilemap import Tilemap
18
20
  from .projectile import Projectile
19
21
 
20
22
 
@@ -23,17 +25,20 @@ class Dynamic:
23
25
 
24
26
  def __init__(
25
27
  self,
26
- name: str = "Unnamed Dynamic",
27
28
  px: float = 0.0,
28
29
  py: float = 0.0,
30
+ name: str = "Unnamed Dynamic",
31
+ tilemap: Optional[Tilemap] = None,
29
32
  dyn_id=-1,
30
33
  ):
31
34
  self.name: str = name
32
35
  self.dyn_id: int = dyn_id # ID given by Tiled
33
36
  self.layer: int = 1
37
+ self.tilemap = tilemap # The object's current map
38
+ self.player = Player.P0
34
39
 
35
- self.px: float = px
36
- self.py: float = py
40
+ self.px: float = px if px is not None else 0.0
41
+ self.py: float = py if py is not None else 0.0
37
42
  self.pz: float = 0.0
38
43
  self.vx: float = 0.0
39
44
  self.vy: float = 0.0
@@ -52,6 +57,7 @@ class Dynamic:
52
57
 
53
58
  self.solid_vs_map: bool = True
54
59
  self.solid_vs_dyn: bool = True
60
+ self.solid_vs_player: bool = True
55
61
  # self.is_player: bool = False
56
62
  # self.is_projectile: bool = False
57
63
  self.redundant: bool = False
@@ -64,6 +70,7 @@ class Dynamic:
64
70
  self.graphic_state_locked: bool = False
65
71
  self.use_acceleration: bool = False
66
72
  self.use_friction: bool = False
73
+ self.occupied: bool = False
67
74
 
68
75
  self.alignment: Alignment = Alignment.GOOD
69
76
  self.walking_on: Terrain = Terrain.DEFAULT
@@ -86,7 +93,10 @@ class Dynamic:
86
93
 
87
94
  self.sprite: Sprite = Sprite()
88
95
 
89
- # Performance flags
96
+ # Performance
97
+ self.chunks: List[int] = []
98
+ self.moves_on_collision: bool = False
99
+
90
100
  self.update_skippable: bool = False
91
101
  """Update may be skipped if object is offscreen."""
92
102
  self.offscreen_collision_skippable: bool = True
@@ -100,7 +110,7 @@ class Dynamic:
100
110
  """Update this dynamic."""
101
111
  pass
102
112
 
103
- def draw_self(self, ox: float, oy: float):
113
+ def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
104
114
  """Draw self to screen"""
105
115
  pass
106
116
 
@@ -154,7 +164,6 @@ class Dynamic:
154
164
  if isinstance(eff, str):
155
165
  obj = [e for e in self.attribute_effects if e.effect_id == eff]
156
166
  if not obj:
157
- print("Effect not active")
158
167
  return
159
168
  else:
160
169
  eff = obj[0]
@@ -180,3 +189,16 @@ class Dynamic:
180
189
  rad += eff.light_radius
181
190
 
182
191
  return rad
192
+
193
+ def get_player(self):
194
+ return self.player
195
+
196
+ def halt(self):
197
+ self.vx = 0
198
+ self.vy = 0
199
+
200
+ def __str__(self):
201
+ return f"D({self.name}, {self.dyn_id})"
202
+
203
+ def __repr__(self):
204
+ return str(self)