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.
- mima/__init__.py +1 -1
- mima/backend/pygame_assets.py +14 -8
- mima/backend/pygame_audio.py +5 -2
- mima/backend/pygame_backend.py +255 -57
- mima/backend/pygame_camera.py +63 -0
- mima/backend/pygame_events.py +369 -120
- mima/collision.py +182 -111
- mima/engine.py +155 -15
- mima/maps/tiled/tiled_map.py +3 -3
- mima/maps/tiled/tiled_tileset.py +1 -0
- mima/maps/tilemap.py +78 -15
- mima/maps/tileset.py +8 -2
- mima/maps/transition_map.py +6 -8
- mima/mode_engine.py +80 -0
- mima/objects/animated_sprite.py +23 -15
- mima/objects/attributes.py +3 -0
- mima/objects/creature.py +54 -17
- mima/objects/dynamic.py +30 -8
- mima/objects/effects/colorize_screen.py +22 -6
- mima/objects/effects/debug_box.py +124 -0
- mima/objects/effects/light.py +21 -30
- mima/objects/effects/show_sprite.py +39 -0
- mima/objects/effects/walking_on_grass.py +25 -7
- mima/objects/effects/walking_on_water.py +17 -6
- mima/objects/loader.py +24 -13
- mima/objects/projectile.py +21 -6
- mima/objects/sprite.py +7 -8
- mima/objects/world/color_gate.py +5 -2
- mima/objects/world/color_switch.py +12 -6
- mima/objects/world/container.py +17 -8
- mima/objects/world/floor_switch.py +8 -4
- mima/objects/world/gate.py +8 -5
- mima/objects/world/light_source.py +11 -9
- mima/objects/world/logic_gate.py +8 -7
- mima/objects/world/movable.py +72 -28
- mima/objects/world/oneway.py +14 -9
- mima/objects/world/pickup.py +10 -5
- mima/objects/world/switch.py +28 -25
- mima/objects/world/teleport.py +76 -55
- mima/scene_engine.py +19 -20
- mima/scripts/command.py +16 -2
- mima/scripts/commands/change_map.py +23 -4
- mima/scripts/commands/equip_weapon.py +23 -0
- mima/scripts/commands/give_item.py +5 -3
- mima/scripts/commands/move_map.py +9 -9
- mima/scripts/commands/parallel.py +16 -3
- mima/scripts/commands/present_item.py +7 -5
- mima/scripts/commands/screen_fade.py +30 -12
- mima/scripts/commands/serial.py +30 -7
- mima/scripts/commands/set_spawn_map.py +6 -3
- mima/scripts/commands/show_choices.py +16 -7
- mima/scripts/commands/show_dialog.py +110 -3
- mima/scripts/script_processor.py +41 -20
- mima/states/game_state.py +2 -0
- mima/states/memory.py +28 -0
- mima/states/quest.py +2 -3
- mima/types/keys.py +48 -0
- mima/types/mode.py +4 -10
- mima/types/player.py +9 -0
- mima/types/position.py +13 -0
- mima/types/tile_collision.py +11 -0
- mima/types/window.py +44 -0
- mima/usables/item.py +1 -0
- mima/util/colors.py +5 -0
- mima/util/constants.py +6 -0
- mima/util/functions.py +27 -0
- mima/util/input_defaults.py +109 -0
- mima/util/runtime_config.py +234 -30
- mima/util/trading_item.py +20 -0
- mima/view/camera.py +160 -19
- mima/view/mima_mode.py +612 -0
- mima/view/mima_scene.py +225 -0
- mima/view/mima_view.py +12 -0
- mima/view/mima_window.py +153 -0
- {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/METADATA +4 -2
- mima_engine-0.2.1.dist-info/RECORD +128 -0
- {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/WHEEL +1 -1
- mima/view/scene.py +0 -322
- mima_engine-0.1.5.dist-info/RECORD +0 -114
- {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/top_level.txt +0 -0
mima/collision.py
CHANGED
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
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 .
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
179
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
58
|
-
self.
|
|
59
|
-
self.
|
|
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
|
-
|
|
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}/
|
|
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}/
|
|
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
|
-
|
|
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.
|
|
205
|
+
self.memory.items[item.name] = item
|
|
179
206
|
|
|
180
207
|
def get_item(self, item_id: str):
|
|
181
208
|
try:
|
|
182
|
-
return self.
|
|
209
|
+
return self.memory.items[item_id]
|
|
183
210
|
except KeyError:
|
|
184
|
-
LOG.
|
|
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
|
mima/maps/tiled/tiled_map.py
CHANGED
|
@@ -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
|