mima-engine 0.1.5__py3-none-any.whl → 0.2.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 (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 +331 -85
  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.0.dist-info}/METADATA +4 -2
  76. mima_engine-0.2.0.dist-info/RECORD +128 -0
  77. {mima_engine-0.1.5.dist-info → mima_engine-0.2.0.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.0.dist-info}/top_level.txt +0 -0
mima/collision.py CHANGED
@@ -1,17 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Tuple
3
+ import math
4
+ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
4
5
 
5
6
  from .maps.tilemap import Tilemap
6
- from .objects.dynamic import Dynamic
7
7
  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.tile_collision import TileCollision
11
12
 
12
13
  if TYPE_CHECKING:
13
- from .engine import MimaEngine
14
- from .view.scene import Scene
14
+ # from .engine import MimaEngine
15
+ from .objects.dynamic import Dynamic
16
+ from .states.quest import Quest
17
+
18
+ # from .view.view import View
15
19
 
16
20
 
17
21
  def check_object_to_map_collision(
@@ -20,8 +24,10 @@ def check_object_to_map_collision(
20
24
  tilemap: Tilemap,
21
25
  new_px: float,
22
26
  new_py: float,
27
+ *,
28
+ layer: int = 0,
29
+ collision: TileCollision = TileCollision.TOP,
23
30
  ) -> Tuple[float, float]:
24
-
25
31
  left = new_px + obj.hitbox_px
26
32
  right = left + obj.hitbox_width
27
33
  top = obj.py + obj.hitbox_py
@@ -29,7 +35,9 @@ def check_object_to_map_collision(
29
35
 
30
36
  collided_with_map = False
31
37
  if obj.solid_vs_map:
32
- if collision_with_map(tilemap, left, right, top, bottom):
38
+ if collision_with_map(
39
+ tilemap, left, right, top, bottom, layer, collision
40
+ ):
33
41
  # On rare occasions, the object might be pushed towards
34
42
  # the wall, i.e. old and new pos are equal
35
43
  # Decide depending on the decimal part of the position
@@ -50,11 +58,7 @@ def check_object_to_map_collision(
50
58
  obj.vx = 0
51
59
  collided_with_map = True
52
60
  if (
53
- obj.facing_direction
54
- in [
55
- Direction.WEST,
56
- Direction.EAST,
57
- ]
61
+ obj.facing_direction in [Direction.WEST, Direction.EAST]
58
62
  and obj.can_push
59
63
  ):
60
64
  obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
@@ -64,7 +68,9 @@ def check_object_to_map_collision(
64
68
  top = new_py + obj.hitbox_py
65
69
  bottom = top + obj.hitbox_height
66
70
 
67
- if collision_with_map(tilemap, left, right, top, bottom):
71
+ if collision_with_map(
72
+ tilemap, left, right, top, bottom, layer, collision
73
+ ):
68
74
  # See comment above
69
75
  if new_py == obj.py:
70
76
  decimal_dif = new_py - int(new_py)
@@ -81,11 +87,7 @@ def check_object_to_map_collision(
81
87
  obj.vy = 0
82
88
  collided_with_map = True
83
89
  if (
84
- obj.facing_direction
85
- in [
86
- Direction.NORTH,
87
- Direction.SOUTH,
88
- ]
90
+ obj.facing_direction in [Direction.NORTH, Direction.SOUTH]
89
91
  and obj.can_push
90
92
  ):
91
93
  obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
@@ -103,124 +105,118 @@ def collision_with_map(
103
105
  top: float,
104
106
  bottom: float,
105
107
  layer: int = 0,
108
+ collision: TileCollision = TileCollision.TOP,
106
109
  ) -> bool:
107
- if tilemap.is_solid(left, top, layer):
110
+ if tilemap.is_solid(left, top, layer, collision):
108
111
  return True
109
- if tilemap.is_solid(left, bottom, layer):
112
+ if tilemap.is_solid(left, bottom, layer, collision):
110
113
  return True
111
- if tilemap.is_solid(right, top, layer):
114
+ if tilemap.is_solid(right, top, layer, collision):
112
115
  return True
113
- if tilemap.is_solid(right, bottom, layer):
116
+ if tilemap.is_solid(right, bottom, layer, collision):
114
117
  return True
115
118
 
116
119
  return False
117
120
 
118
121
 
119
122
  def check_object_to_object_collision(
120
- engine: MimaEngine,
121
- scene: Scene,
122
123
  obj: Dynamic,
123
124
  new_px: float,
124
125
  new_py: float,
125
126
  other: Dynamic,
127
+ deal_damage: Optional[Callable[Dynamic, Dynamic]] = None,
128
+ quests: Optional[List[Quest]] = None,
126
129
  ) -> Tuple[float, float]:
127
- pass
128
-
129
- obj_left = new_px + obj.hitbox_px
130
- obj_right = obj_left + obj.hitbox_width
131
- obj_top = obj.py + obj.hitbox_py
132
- obj_bottom = obj_top + obj.hitbox_height
133
-
134
- other_left = other.px + other.hitbox_px
135
- other_right = other_left + other.hitbox_width
136
- other_top = other.py + other.hitbox_py
137
- other_bottom = other_top + other.hitbox_height
138
-
139
- if obj.solid_vs_dyn and other.solid_vs_dyn:
140
- collided_with_dyn = False
141
- if collision_with_dyn(
142
- obj_left,
143
- obj_right,
144
- obj_top,
145
- obj_bottom,
146
- other_left,
147
- other_right,
148
- other_top,
149
- other_bottom,
150
- ):
151
- collided_with_dyn = True
152
- if obj_left < other_left:
153
- new_px -= obj_right - other_left + 0.001
154
- else:
155
- new_px += other_right - obj_left + 0.001
156
-
157
- obj_left = new_px + obj.hitbox_px
158
- obj_right = obj_left + obj.hitbox_width
159
- obj_top = new_py + obj.hitbox_py
160
- obj_bottom = obj_top + obj.hitbox_height
161
-
162
- if collision_with_dyn(
163
- obj_left,
164
- obj_right,
165
- obj_top,
166
- obj_bottom,
167
- other_left,
168
- other_right,
169
- other_top,
170
- other_bottom,
171
- ):
172
- collided_with_dyn = True
173
- if obj_top < other_top:
174
- new_py -= obj_bottom - other_top + 0.001
175
- else:
176
- new_py += other_bottom - obj_top + 0.001
130
+ quests = quests if quests is not None else []
131
+ deal_damage = deal_damage if deal_damage is not None else lambda x, y: None
177
132
 
178
- if collided_with_dyn:
179
- other.on_interaction(obj, Nature.WALK)
133
+ pxys = {}
134
+ pxys["left1"] = new_px + obj.hitbox_px
135
+ pxys["right1"] = pxys["left1"] + obj.hitbox_width
136
+ pxys["top1"] = obj.py + obj.hitbox_py
137
+ pxys["bottom1"] = pxys["top1"] + obj.hitbox_height
138
+
139
+ pxys["left2"] = other.px + other.hitbox_px
140
+ pxys["right2"] = pxys["left2"] + other.hitbox_width
141
+ pxys["top2"] = other.py + other.hitbox_py
142
+ pxys["bottom2"] = pxys["top2"] + other.hitbox_height
143
+
144
+ if obj.solid_vs_dyn and other.solid_vs_dyn: # and obj.moves_on_collision:
145
+ new_px, new_py = _check_solid_objects(obj, other, new_px, new_py, pxys)
180
146
 
181
147
  else:
182
148
  if obj.type == ObjectType.PLAYER:
183
- if collision_with_dyn(
184
- obj_left,
185
- obj_right,
186
- obj_top,
187
- obj_bottom,
188
- other_left,
189
- other_right,
190
- other_top,
191
- other_bottom,
192
- ):
193
- for quest in engine.quests:
194
- if quest.on_interaction(
195
- scene.dynamics, other, Nature.WALK
196
- ):
197
- break
198
- scene.tilemap.on_interaction(other, Nature.WALK)
199
- other.on_interaction(obj, Nature.WALK)
149
+ _check_player_with_non_solid(obj, other, pxys, quests)
150
+
200
151
  else:
201
- if collision_with_dyn(
202
- obj_left,
203
- obj_right,
204
- obj_top,
205
- obj_bottom,
206
- other_left,
207
- other_right,
208
- other_top,
209
- other_bottom,
210
- ):
211
- if obj.type == ObjectType.PROJECTILE:
212
- if other.alignment != obj.alignment:
213
- # We know object is a projectile
214
- if other.attackable and not other.invincible:
215
- scene.deal_damage(obj, other)
216
- else:
217
- other.on_interaction(obj, Nature.WALK)
218
- else:
219
- other.on_interaction(obj, Nature.WALK)
152
+ _check_non_solid_objects(obj, other, pxys, deal_damage)
220
153
 
221
154
  return new_px, new_py
222
155
 
223
156
 
157
+ def _check_solid_objects(
158
+ obj: Dynamic,
159
+ other: Dynamic,
160
+ new_px: float,
161
+ new_py: float,
162
+ pxys: Dict[str, float],
163
+ ) -> None:
164
+ collided_with_dyn = False
165
+ if collision_with_dyn(**pxys):
166
+ collided_with_dyn = True
167
+ if pxys["left1"] < pxys["left2"]:
168
+ new_px -= pxys["right1"] - pxys["left2"] + 0.001
169
+ else:
170
+ new_px += pxys["right2"] - pxys["left1"] + 0.001
171
+
172
+ pxys["left1"] = new_px + obj.hitbox_px
173
+ pxys["right1"] = pxys["left1"] + obj.hitbox_width
174
+ pxys["top1"] = new_py + obj.hitbox_py
175
+ pxys["bottom1"] = pxys["top1"] + obj.hitbox_height
176
+
177
+ if collision_with_dyn(**pxys):
178
+ collided_with_dyn = True
179
+ if pxys["top1"] < pxys["top2"]:
180
+ new_py -= pxys["bottom1"] - pxys["top2"] + 0.001
181
+ else:
182
+ new_py += pxys["bottom2"] - pxys["top1"] + 0.001
183
+
184
+ if collided_with_dyn:
185
+ # print(f"Collision: {obj.get_player()} -> {other.dyn_id}")
186
+ other.on_interaction(obj, Nature.WALK)
187
+
188
+ return new_px, new_py
189
+
190
+
191
+ def _check_player_with_non_solid(
192
+ obj: Dynamic, other: Dynamic, pxys: Dict[str, float], quests: List[Quest]
193
+ ):
194
+ if collision_with_dyn(**pxys):
195
+ for quest in quests:
196
+ if quest.on_interaction(other, Nature.WALK, obj.get_player()):
197
+ break
198
+ obj.tilemap.on_interaction(other, Nature.WALK)
199
+ other.on_interaction(obj, Nature.WALK)
200
+
201
+
202
+ def _check_non_solid_objects(
203
+ obj: Dynamic,
204
+ other: Dynamic,
205
+ pxys: Dict[str, float],
206
+ deal_damage: Callable[Dynamic, Dynamic],
207
+ ):
208
+ if collision_with_dyn(**pxys):
209
+ if obj.type == ObjectType.PROJECTILE:
210
+ if other.alignment != obj.alignment:
211
+ # We know object is a projectile
212
+ if other.attackable and not other.invincible:
213
+ deal_damage(obj, other)
214
+ else:
215
+ other.on_interaction(obj, Nature.WALK)
216
+ else:
217
+ other.on_interaction(obj, Nature.WALK)
218
+
219
+
224
220
  def collision_with_dyn(
225
221
  left1: float,
226
222
  right1: float,
@@ -235,3 +231,78 @@ def collision_with_dyn(
235
231
  return True
236
232
 
237
233
  return False
234
+
235
+
236
+ def add_to_collision_chunk(
237
+ collision_chunks: Dict[int, List[Dynamic]],
238
+ obj: Dynamic,
239
+ chunk_size: int,
240
+ chunks_per_row: int,
241
+ ) -> List[int]:
242
+ chunk_ids = []
243
+ # chidx = _chunk_index(obj.px, obj.py, chunk_size, chunks_per_row)
244
+ # collision_chunks.setdefault(chidx, [])
245
+
246
+ # if obj not in collision_chunks[chidx]:
247
+ # if obj.get_player().value > 0:
248
+ # collision_chunks[chidx].insert(0, obj)
249
+ # else:
250
+ # collision_chunks[chidx].append(obj)
251
+
252
+ chid = _test_chunk_position(
253
+ collision_chunks, obj, obj.px, obj.py, chunk_size, chunks_per_row
254
+ )
255
+ chid_right = _test_chunk_position(
256
+ collision_chunks, obj, obj.px + 1, obj.py, chunk_size, chunks_per_row
257
+ )
258
+
259
+ chid_bottom = _test_chunk_position(
260
+ collision_chunks, obj, obj.px, obj.py + 1, chunk_size, chunks_per_row
261
+ )
262
+
263
+ chunk_ids.append(chid)
264
+ if chid != chid_right:
265
+ chunk_ids.append(chid_right)
266
+ if chid != chid_bottom:
267
+ chunk_ids.append(chid_bottom)
268
+ if chid != chid_right and chid != chid_bottom:
269
+ chunk_ids.append(
270
+ _test_chunk_position(
271
+ collision_chunks,
272
+ obj,
273
+ obj.px + 1,
274
+ obj.py + 1,
275
+ chunk_size,
276
+ chunks_per_row,
277
+ )
278
+ )
279
+
280
+ for chid in obj.chunks:
281
+ if (
282
+ chid not in chunk_ids
283
+ and chid in collision_chunks
284
+ and obj in collision_chunks[chid]
285
+ ):
286
+ collision_chunks[chid].remove(obj)
287
+ return chunk_ids
288
+
289
+
290
+ def _test_chunk_position(
291
+ collision_chunks, obj, px, py, chunk_size, chunks_per_row
292
+ ):
293
+ chidx = _chunk_index(px, py, chunk_size, chunks_per_row)
294
+ collision_chunks.setdefault(chidx, [])
295
+
296
+ if obj not in collision_chunks[chidx]:
297
+ if obj.get_player().value > 0:
298
+ collision_chunks[chidx].insert(0, obj)
299
+ else:
300
+ collision_chunks[chidx].append(obj)
301
+ return chidx
302
+
303
+
304
+ def _chunk_index(px, py, chunk_size, chunks_per_row):
305
+ hc = chunk_size // 2
306
+ return math.floor((px + hc) / chunk_size) + chunks_per_row * math.floor(
307
+ (py + hc) / chunk_size
308
+ )
mima/engine.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import logging
4
4
  from abc import ABC, abstractmethod
5
- from typing import Dict
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
6
6
 
7
7
  from .backend.pygame_assets import PygameAssets
8
8
  from .backend.pygame_audio import PygameAudio
@@ -13,16 +13,26 @@ from .maps.tilemap import Tilemap
13
13
  from .objects.animated_sprite import AnimatedSprite
14
14
  from .objects.creature import Creature
15
15
  from .objects.dynamic import Dynamic
16
+ from .objects.loader import ObjectLoader
16
17
  from .objects.sprite import Sprite
17
18
  from .scripts import Command, ScriptProcessor
19
+ from .states.memory import Memory
18
20
  from .states.quest import Quest
19
21
  from .types.gate_color import GateColor
20
22
  from .types.mode import Mode
23
+ from .types.player import Player
21
24
  from .usables.item import Item
22
25
  from .util import RuntimeConfig
23
26
  from .util.logging import install_trace_logger
24
27
  from .view.camera import Camera
25
- from .view.scene import Scene
28
+
29
+ # from .view.game_mode import GameMode
30
+ from .view.mima_view import MimaView
31
+
32
+ # from .view.scene import Scene
33
+
34
+ if TYPE_CHECKING:
35
+ from .states.game_state import GameState
26
36
 
27
37
  LOG = logging.getLogger(__name__)
28
38
 
@@ -54,9 +64,13 @@ class MimaEngine(ABC):
54
64
  self.gate_color: GateColor = GateColor.RED
55
65
  self.n_gate_colors = 2
56
66
  self.script: ScriptProcessor = None
57
- self.player: Creature
58
- self.quests: List[Quest] = []
59
- self._items: Dict[str, Item] = {}
67
+ self.memory: Memory = Memory()
68
+ self.all_games: Dict[str, GameState] = {}
69
+ self.current_game: str = ""
70
+ # self.player: Creature
71
+ # self.quests: List[Quest] = []
72
+ # self._items: Dict[str, Item] = {}
73
+ self.cameras: List[str] = []
60
74
 
61
75
  def construct(
62
76
  self,
@@ -66,26 +80,35 @@ class MimaEngine(ABC):
66
80
  fullscreen: bool = False,
67
81
  target_fps: int = 60,
68
82
  resizable: bool = False,
83
+ kb_map=None,
69
84
  ):
70
85
  """Initialize backend and create a window."""
71
86
  AnimatedSprite.engine = self
72
87
  Camera.engine = self
73
88
  Command.engine = self
74
89
  Dynamic.engine = self
90
+ # GameMode.engine = self
91
+ Item.engine = self
75
92
  PygameBackend.engine = self
93
+ ObjectLoader.engine = self
76
94
  Quest.engine = self
77
- Scene.engine = self
95
+ # Scene.engine = self
78
96
  ScriptProcessor.engine = self
79
97
  Sprite.engine = self
80
98
  Template.engine = self
81
99
  Tilemap.engine = self
82
- Item.engine = self
100
+ MimaView.engine = self
83
101
 
84
102
  self.script = ScriptProcessor()
85
- self.backend.init()
103
+ self.backend.init(
104
+ keyboard_map=self.rtc.get_keyboard_map(),
105
+ joystick_map=self.rtc.get_joystick_map(),
106
+ joy_to_player=self.rtc.get_joy_to_player(),
107
+ )
86
108
  self.backend.construct(
87
109
  width, height, pixel_size, fullscreen, target_fps, resizable
88
110
  )
111
+
89
112
  return True
90
113
 
91
114
  def start(self):
@@ -101,18 +124,20 @@ class MimaEngine(ABC):
101
124
  if self.on_user_create():
102
125
  while self.backend.keep_running():
103
126
  self.backend.set_caption(
104
- f"{self._caption} ({self.game_fps:.2f}/{self.app_fps:.2f} fps)"
127
+ f"{self._caption} ({self.game_fps:.2f}/"
128
+ f"{self.app_fps:.2f} fps)"
105
129
  )
106
130
  self.backend.process_events()
107
131
 
108
132
  if not self.backend.keep_running():
109
133
  break
110
134
 
135
+ self.cameras = ["display"]
111
136
  if not self.on_user_update(self.elapsed_time):
112
137
  print("Error in on_user_update")
113
138
  break
114
139
 
115
- self.backend.update_display()
140
+ self.backend.update_display(*self.cameras)
116
141
 
117
142
  self._app_time = self.backend.tick()
118
143
  self.elapsed_time = min(self._app_time, 1.0 / 30.0)
@@ -135,11 +160,13 @@ class MimaEngine(ABC):
135
160
  app_seconds -= 1.0
136
161
 
137
162
  print(
138
- f"App/Game Frames total: {app_frames_total}/{game_frames_total}"
163
+ f"App/Game Frames total: {app_frames_total}/"
164
+ f"{game_frames_total}"
139
165
  )
140
166
  print(f"Seconds total: {self.seconds_total:.3f}")
141
167
  print(
142
- f"Average App/Game FPS: {app_frames_total/self.seconds_total:.3f}/"
168
+ "Average App/Game FPS: "
169
+ f"{app_frames_total/self.seconds_total:.3f}/"
143
170
  f"{game_frames_total/self.seconds_total:.3f}"
144
171
  )
145
172
 
@@ -175,15 +202,41 @@ class MimaEngine(ABC):
175
202
 
176
203
  def load_item(self, item: Item):
177
204
  LOG.debug(f"Loading item {item.name}.")
178
- self._items[item.name] = item
205
+ self.memory.items[item.name] = item
179
206
 
180
207
  def get_item(self, item_id: str):
181
208
  try:
182
- return self._items[item_id]
209
+ return self.memory.items[item_id]
183
210
  except KeyError:
184
- LOG.error(f"Item '{item_id}' is not defined!")
211
+ LOG.exception(f"Item '{item_id}' is not defined!")
212
+ raise
213
+ except TypeError:
214
+ print(type(self.memory.items), item_id)
185
215
  raise
186
216
 
217
+ def give_item(self, item: Union[str, Item], player: Player = Player.P1):
218
+ if isinstance(item, str):
219
+ item = self.get_item(item)
220
+ self.memory.bag[player].append(item)
221
+ # self.items.append(item)
222
+ return True
223
+
224
+ def take_item(self, item: Union[str, Item], player: Player = Player.P1):
225
+ if isinstance(item, str):
226
+ item = self.get_item(item)
227
+
228
+ if item in self.memory.bag[player]:
229
+ self.memory.bag[player].remove(item)
230
+ return True
231
+ return False
232
+
233
+ def has_item(self, item: Union[str, Item], player: Player = Player.P1):
234
+ if isinstance(item, str):
235
+ item = self.get_item(item)
236
+
237
+ return item in self.memory.bag[player]
238
+ # return False
239
+
187
240
  def progress_quest(self, quest_name: str, new_state: int):
188
241
  for quest in self.quests:
189
242
  if quest.name == quest_name:
@@ -200,3 +253,90 @@ class MimaEngine(ABC):
200
253
 
201
254
  def on_entered_foreground(self):
202
255
  LOG.debug("Entered foreground")
256
+
257
+ def get_player(self, player: Player = Player.P1):
258
+ if player in self.memory.player:
259
+ return self.memory.player[player]
260
+ else:
261
+ return None
262
+
263
+ def set_player(self, creature: Creature, player: Player = Player.P1):
264
+ self.memory.player[player] = creature
265
+
266
+ def trigger_teleport(
267
+ self, active: bool = True, player: Player = Player.P1
268
+ ):
269
+ self.memory.teleport_active[player] = active
270
+
271
+ def is_teleport_active(self, player: Player = Player.P1):
272
+ return self.memory.teleport_active[player]
273
+
274
+ def trigger_dialog(self, active: bool = True, player=Player.P1):
275
+ self.memory.dialog_active[player] = active
276
+
277
+ def is_dialog_active(self, player: Player = Player.P1):
278
+ return self.memory.dialog_active[player]
279
+
280
+ def trigger_script(
281
+ self, active: bool = True, player: Player = Player.P1
282
+ ) -> None:
283
+ self.memory.script_active[player] = active
284
+
285
+ def is_script_active(self, player: Player = Player.P1) -> bool:
286
+ return self.memory.script_active[player]
287
+
288
+ def trigger_player_collision(
289
+ self, active: bool = True, player: Player = Player.P1
290
+ ) -> None:
291
+ self.memory.player_collision_active[player] = active
292
+
293
+ def is_player_collision_active(self, player: Player = Player.P1) -> bool:
294
+ return self.memory.player_collision_active[player]
295
+
296
+ @property
297
+ def player(self):
298
+ print("Deprecated access to player. Use 'get_player' instead!")
299
+ return self.memory.player
300
+
301
+ @player.setter
302
+ def player(self, val):
303
+ print("Deprecated access to player. Use 'get_player' instead!")
304
+ self.memory.player = val
305
+
306
+ @property
307
+ def items(self):
308
+ # raise TypeError()
309
+ return self.memory.items
310
+
311
+ @items.setter
312
+ def items(self, val):
313
+ raise TypeError(f"Items cannot be set to type {type(val)}")
314
+ # self.memory.items = val
315
+
316
+ @property
317
+ def quests(self):
318
+ return self.memory.quests
319
+
320
+ @quests.setter
321
+ def quests(self, val):
322
+ self.memory.quests = val
323
+
324
+ @property
325
+ def dialog_active(self):
326
+ print("dialog_active is deprecated; use 'is_dialog_active()' instead.")
327
+ return self.memory.dialog_active
328
+
329
+ @dialog_active.setter
330
+ def dialog_active(self, val):
331
+ print(
332
+ "dialog_active is deprecated; use "
333
+ "'trigger_dialog(active)' instead."
334
+ )
335
+ self.memory.dialog_active = val
336
+
337
+ @property
338
+ def game_state(self) -> GameState:
339
+ return self.all_games[self.current_game]
340
+
341
+ def get_view(self):
342
+ return None
@@ -5,11 +5,11 @@ import os
5
5
  from typing import List
6
6
  from xml.etree import ElementTree
7
7
 
8
+ from ...util.property import Property
8
9
  from ..tilemap import Tilemap
9
10
  from ..tileset_info import TilesetInfo
10
11
  from .tiled_layer import TiledLayer
11
12
  from .tiled_objectgroup import TiledObjectgroup
12
- from ...util.property import Property
13
13
 
14
14
  # if TYPE_CHECKING:
15
15
  # from ..engine import Avare
@@ -59,7 +59,7 @@ class TiledMap(Tilemap):
59
59
  self.properties[pname] = Property(
60
60
  name=pname,
61
61
  dtype=p.attrib.get("type", "str"),
62
- value=p.attrib["value"]
62
+ value=p.attrib["value"],
63
63
  )
64
64
 
65
65
  LOG.debug("Loading tilesets ...")
@@ -92,4 +92,4 @@ class TiledMap(Tilemap):
92
92
  for group in self._objects:
93
93
  all_objects += group.objects
94
94
 
95
- return all_objects
95
+ return all_objects
@@ -39,6 +39,7 @@ class TiledTileset(Tileset):
39
39
 
40
40
  LOG.debug("Loading tiles ...")
41
41
  tiles = root.findall("tile")
42
+
42
43
  for tile in tiles:
43
44
  self.tiles.append(TiledTile(tile))
44
45
  if self.tiles[-1].animated: