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.
- mima/__init__.py +4 -0
- mima/backend/__init__.py +1 -0
- mima/backend/pygame_assets.py +401 -0
- mima/backend/pygame_audio.py +78 -0
- mima/backend/pygame_backend.py +603 -0
- mima/backend/pygame_camera.py +63 -0
- mima/backend/pygame_events.py +695 -0
- mima/backend/touch_control_scheme_a.py +126 -0
- mima/backend/touch_control_scheme_b.py +132 -0
- mima/core/__init__.py +0 -0
- mima/core/collision.py +325 -0
- mima/core/database.py +58 -0
- mima/core/engine.py +367 -0
- mima/core/mode_engine.py +81 -0
- mima/core/scene_engine.py +81 -0
- mima/integrated/__init__.py +0 -0
- mima/integrated/entity.py +183 -0
- mima/integrated/layered_map.py +351 -0
- mima/integrated/sprite.py +156 -0
- mima/layered/__init__.py +0 -0
- mima/layered/assets.py +56 -0
- mima/layered/scene.py +415 -0
- mima/layered/shape.py +99 -0
- mima/layered/shaped_sprite.py +78 -0
- mima/layered/virtual_input.py +302 -0
- mima/maps/__init__.py +0 -0
- mima/maps/template.py +71 -0
- mima/maps/tile.py +20 -0
- mima/maps/tile_animation.py +7 -0
- mima/maps/tile_info.py +10 -0
- mima/maps/tile_layer.py +52 -0
- mima/maps/tiled/__init__.py +0 -0
- mima/maps/tiled/tiled_layer.py +48 -0
- mima/maps/tiled/tiled_map.py +95 -0
- mima/maps/tiled/tiled_object.py +79 -0
- mima/maps/tiled/tiled_objectgroup.py +25 -0
- mima/maps/tiled/tiled_template.py +49 -0
- mima/maps/tiled/tiled_tile.py +90 -0
- mima/maps/tiled/tiled_tileset.py +51 -0
- mima/maps/tilemap.py +216 -0
- mima/maps/tileset.py +39 -0
- mima/maps/tileset_info.py +9 -0
- mima/maps/transition_map.py +146 -0
- mima/objects/__init__.py +0 -0
- mima/objects/animated_sprite.py +217 -0
- mima/objects/attribute_effect.py +26 -0
- mima/objects/attributes.py +126 -0
- mima/objects/creature.py +384 -0
- mima/objects/dynamic.py +206 -0
- mima/objects/effects/__init__.py +0 -0
- mima/objects/effects/colorize_screen.py +60 -0
- mima/objects/effects/debug_box.py +133 -0
- mima/objects/effects/light.py +103 -0
- mima/objects/effects/show_sprite.py +50 -0
- mima/objects/effects/walking_on_grass.py +70 -0
- mima/objects/effects/walking_on_water.py +57 -0
- mima/objects/loader.py +111 -0
- mima/objects/projectile.py +111 -0
- mima/objects/sprite.py +116 -0
- mima/objects/world/__init__.py +0 -0
- mima/objects/world/color_gate.py +67 -0
- mima/objects/world/color_switch.py +101 -0
- mima/objects/world/container.py +175 -0
- mima/objects/world/floor_switch.py +109 -0
- mima/objects/world/gate.py +178 -0
- mima/objects/world/light_source.py +121 -0
- mima/objects/world/logic_gate.py +157 -0
- mima/objects/world/movable.py +399 -0
- mima/objects/world/oneway.py +195 -0
- mima/objects/world/pickup.py +157 -0
- mima/objects/world/switch.py +179 -0
- mima/objects/world/teleport.py +308 -0
- mima/py.typed +0 -0
- mima/scripts/__init__.py +2 -0
- mima/scripts/command.py +38 -0
- mima/scripts/commands/__init__.py +0 -0
- mima/scripts/commands/add_quest.py +19 -0
- mima/scripts/commands/change_map.py +34 -0
- mima/scripts/commands/close_dialog.py +9 -0
- mima/scripts/commands/equip_weapon.py +23 -0
- mima/scripts/commands/give_item.py +26 -0
- mima/scripts/commands/give_resource.py +51 -0
- mima/scripts/commands/move_map.py +152 -0
- mima/scripts/commands/move_to.py +49 -0
- mima/scripts/commands/oneway_move.py +58 -0
- mima/scripts/commands/parallel.py +66 -0
- mima/scripts/commands/play_sound.py +13 -0
- mima/scripts/commands/present_item.py +53 -0
- mima/scripts/commands/progress_quest.py +12 -0
- mima/scripts/commands/quit_game.py +8 -0
- mima/scripts/commands/save_game.py +14 -0
- mima/scripts/commands/screen_fade.py +83 -0
- mima/scripts/commands/serial.py +69 -0
- mima/scripts/commands/set_facing_direction.py +21 -0
- mima/scripts/commands/set_spawn_map.py +17 -0
- mima/scripts/commands/show_choices.py +52 -0
- mima/scripts/commands/show_dialog.py +118 -0
- mima/scripts/commands/take_coins.py +23 -0
- mima/scripts/script_processor.py +61 -0
- mima/standalone/__init__.py +0 -0
- mima/standalone/camera.py +153 -0
- mima/standalone/geometry.py +1318 -0
- mima/standalone/multicolumn_list.py +54 -0
- mima/standalone/pixel_font.py +84 -0
- mima/standalone/scripting.py +145 -0
- mima/standalone/spatial.py +186 -0
- mima/standalone/sprite.py +158 -0
- mima/standalone/tiled_map.py +1247 -0
- mima/standalone/transformed_view.py +433 -0
- mima/standalone/user_input.py +563 -0
- mima/states/__init__.py +0 -0
- mima/states/game_state.py +189 -0
- mima/states/memory.py +28 -0
- mima/states/quest.py +71 -0
- mima/types/__init__.py +0 -0
- mima/types/alignment.py +7 -0
- mima/types/blend.py +8 -0
- mima/types/damage.py +42 -0
- mima/types/direction.py +44 -0
- mima/types/gate_color.py +7 -0
- mima/types/graphic_state.py +23 -0
- mima/types/keys.py +64 -0
- mima/types/mode.py +9 -0
- mima/types/nature.py +12 -0
- mima/types/object.py +22 -0
- mima/types/player.py +9 -0
- mima/types/position.py +13 -0
- mima/types/start.py +7 -0
- mima/types/terrain.py +9 -0
- mima/types/tile_collision.py +11 -0
- mima/types/weapon_slot.py +6 -0
- mima/types/window.py +44 -0
- mima/usables/__init__.py +0 -0
- mima/usables/item.py +51 -0
- mima/usables/weapon.py +68 -0
- mima/util/__init__.py +1 -0
- mima/util/colors.py +50 -0
- mima/util/constants.py +55 -0
- mima/util/functions.py +38 -0
- mima/util/input_defaults.py +170 -0
- mima/util/logging.py +51 -0
- mima/util/property.py +8 -0
- mima/util/runtime_config.py +327 -0
- mima/util/trading_item.py +23 -0
- mima/view/__init__.py +0 -0
- mima/view/camera.py +192 -0
- mima/view/mima_mode.py +618 -0
- mima/view/mima_scene.py +231 -0
- mima/view/mima_view.py +12 -0
- mima/view/mima_window.py +244 -0
- mima_engine-0.4.0.dist-info/METADATA +47 -0
- mima_engine-0.4.0.dist-info/RECORD +153 -0
- mima_engine-0.4.0.dist-info/WHEEL +4 -0
mima/core/engine.py
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
from ..backend.pygame_assets import PygameAssets
|
|
8
|
+
from ..backend.pygame_audio import PygameAudio
|
|
9
|
+
from ..backend.pygame_backend import PygameBackend
|
|
10
|
+
from ..backend.pygame_events import PygameUserInput
|
|
11
|
+
from ..maps.template import Template
|
|
12
|
+
from ..maps.tilemap import Tilemap
|
|
13
|
+
from ..objects.animated_sprite import AnimatedSprite
|
|
14
|
+
from ..objects.creature import Creature
|
|
15
|
+
from ..objects.dynamic import Dynamic
|
|
16
|
+
from ..objects.loader import ObjectLoader
|
|
17
|
+
from ..objects.sprite import Sprite
|
|
18
|
+
from ..scripts import Command, ScriptProcessor
|
|
19
|
+
from ..states.memory import Memory
|
|
20
|
+
from ..states.quest import Quest
|
|
21
|
+
from ..types.gate_color import GateColor
|
|
22
|
+
from ..types.mode import Mode
|
|
23
|
+
from ..types.player import Player
|
|
24
|
+
from ..usables.item import Item
|
|
25
|
+
from ..usables.weapon import Weapon
|
|
26
|
+
from ..util import RuntimeConfig
|
|
27
|
+
from ..util.logging import install_trace_logger
|
|
28
|
+
from ..view.camera import Camera
|
|
29
|
+
from ..view.mima_view import MimaView
|
|
30
|
+
from .database import Database
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from .states.game_state import GameState
|
|
34
|
+
|
|
35
|
+
LOG = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class MimaEngine(ABC):
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
init_file: str,
|
|
42
|
+
config_path: str,
|
|
43
|
+
default_config: Dict[str, Any],
|
|
44
|
+
platform: str = "PC",
|
|
45
|
+
caption: str = "MimaEngine",
|
|
46
|
+
):
|
|
47
|
+
self.rtc = RuntimeConfig(config_path, default_config)
|
|
48
|
+
install_trace_logger()
|
|
49
|
+
|
|
50
|
+
self.backend: PygameBackend = PygameBackend(
|
|
51
|
+
self.rtc, init_file, platform
|
|
52
|
+
)
|
|
53
|
+
self.db: Database
|
|
54
|
+
self._caption: str = caption
|
|
55
|
+
self.seconds_total: float = 0.0
|
|
56
|
+
self.app_fps: float = 0.0
|
|
57
|
+
self.game_fps: float = 0.0
|
|
58
|
+
self.elapsed_time: float = 0.00022
|
|
59
|
+
self._app_time: float = 0.0
|
|
60
|
+
|
|
61
|
+
self.enable_touch_controls: bool = False
|
|
62
|
+
self.show_touch_controls: bool = False
|
|
63
|
+
|
|
64
|
+
self.mode: Mode = Mode.LOADING
|
|
65
|
+
self.gate_color: GateColor = GateColor.RED
|
|
66
|
+
self.n_gate_colors = 2
|
|
67
|
+
self.script: ScriptProcessor = None
|
|
68
|
+
self.memory: Memory = Memory()
|
|
69
|
+
self.all_games: Dict[str, GameState] = {}
|
|
70
|
+
self.current_game: str = ""
|
|
71
|
+
self.cameras: List[str] = []
|
|
72
|
+
|
|
73
|
+
def construct(
|
|
74
|
+
self,
|
|
75
|
+
width: int,
|
|
76
|
+
height: int,
|
|
77
|
+
pixel_size: int,
|
|
78
|
+
fullscreen: bool = False,
|
|
79
|
+
target_fps: int = 60,
|
|
80
|
+
resizable: bool = False,
|
|
81
|
+
no_scaled_flag: bool = False,
|
|
82
|
+
kb_map=None,
|
|
83
|
+
):
|
|
84
|
+
"""Initialize backend and create a window."""
|
|
85
|
+
AnimatedSprite.engine = self
|
|
86
|
+
Camera.engine = self
|
|
87
|
+
Command.engine = self
|
|
88
|
+
Database.engine = self
|
|
89
|
+
Dynamic.engine = self
|
|
90
|
+
# GameMode.engine = self
|
|
91
|
+
Item.engine = self
|
|
92
|
+
PygameBackend.engine = self
|
|
93
|
+
ObjectLoader.engine = self
|
|
94
|
+
Quest.engine = self
|
|
95
|
+
# Scene.engine = self
|
|
96
|
+
ScriptProcessor.engine = self
|
|
97
|
+
Sprite.engine = self
|
|
98
|
+
Template.engine = self
|
|
99
|
+
Tilemap.engine = self
|
|
100
|
+
MimaView.engine = self
|
|
101
|
+
|
|
102
|
+
self.script = ScriptProcessor()
|
|
103
|
+
self.db = Database()
|
|
104
|
+
self.backend.init(
|
|
105
|
+
keyboard_map=self.rtc.get_keyboard_map(),
|
|
106
|
+
joystick_map=self.rtc.get_joystick_map(),
|
|
107
|
+
joy_to_player=self.rtc.get_joy_to_player(),
|
|
108
|
+
)
|
|
109
|
+
self.backend.construct(
|
|
110
|
+
width,
|
|
111
|
+
height,
|
|
112
|
+
pixel_size,
|
|
113
|
+
fullscreen,
|
|
114
|
+
target_fps,
|
|
115
|
+
resizable,
|
|
116
|
+
no_scaled_flag,
|
|
117
|
+
)
|
|
118
|
+
self.backend.user_input.enable_touch_controls = (
|
|
119
|
+
self.enable_touch_controls
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
def start(self):
|
|
125
|
+
"""Start the main loop"""
|
|
126
|
+
app_frames = 0
|
|
127
|
+
game_frames = 0
|
|
128
|
+
app_seconds = 0.0
|
|
129
|
+
game_seconds = 0.0
|
|
130
|
+
app_frames_total = 0
|
|
131
|
+
game_frames_total = 0
|
|
132
|
+
self.seconds_total = 0.0
|
|
133
|
+
|
|
134
|
+
if self.on_user_create():
|
|
135
|
+
while self.backend.keep_running():
|
|
136
|
+
self.backend.set_caption(
|
|
137
|
+
f"{self._caption} ({self.game_fps:.2f}/"
|
|
138
|
+
f"{self.app_fps:.2f} fps)"
|
|
139
|
+
)
|
|
140
|
+
self.backend.process_events()
|
|
141
|
+
|
|
142
|
+
if not self.backend.keep_running():
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
self.cameras = ["display"]
|
|
146
|
+
if not self.on_user_update(self.elapsed_time):
|
|
147
|
+
print("Error in on_user_update")
|
|
148
|
+
break
|
|
149
|
+
|
|
150
|
+
self.backend.update_display(*self.cameras)
|
|
151
|
+
|
|
152
|
+
self._app_time = self.backend.tick()
|
|
153
|
+
self.elapsed_time = min(self._app_time, 1.0 / 30.0)
|
|
154
|
+
|
|
155
|
+
app_seconds += self._app_time
|
|
156
|
+
game_seconds += self.elapsed_time
|
|
157
|
+
app_frames += 1
|
|
158
|
+
game_frames += 1
|
|
159
|
+
|
|
160
|
+
if game_seconds >= 1.0:
|
|
161
|
+
game_frames_total += game_frames
|
|
162
|
+
self.game_fps = game_frames
|
|
163
|
+
game_frames = 0
|
|
164
|
+
game_seconds -= 1.0
|
|
165
|
+
if app_seconds >= 1.0:
|
|
166
|
+
app_frames_total += app_frames
|
|
167
|
+
self.seconds_total += app_seconds
|
|
168
|
+
self.app_fps = app_frames
|
|
169
|
+
app_frames = 0
|
|
170
|
+
app_seconds -= 1.0
|
|
171
|
+
|
|
172
|
+
print(
|
|
173
|
+
f"App/Game Frames total: {app_frames_total}/"
|
|
174
|
+
f"{game_frames_total}"
|
|
175
|
+
)
|
|
176
|
+
print(f"Seconds total: {self.seconds_total:.3f}")
|
|
177
|
+
print(
|
|
178
|
+
"Average App/Game FPS: "
|
|
179
|
+
f"{app_frames_total/self.seconds_total:.3f}/"
|
|
180
|
+
f"{game_frames_total/self.seconds_total:.3f}"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.backend.shutdown()
|
|
184
|
+
|
|
185
|
+
@abstractmethod
|
|
186
|
+
def on_user_update(self, elapsed_time: float) -> bool:
|
|
187
|
+
"""Update."""
|
|
188
|
+
raise NotImplementedError()
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def on_user_create(self) -> bool:
|
|
192
|
+
raise NotImplementedError()
|
|
193
|
+
|
|
194
|
+
def on_user_terminate(self) -> bool:
|
|
195
|
+
self.backend.terminate = True
|
|
196
|
+
return True
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def assets(self) -> PygameAssets:
|
|
200
|
+
return self.backend.assets
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def audio(self) -> PygameAudio:
|
|
204
|
+
return self.backend.audio
|
|
205
|
+
|
|
206
|
+
@property
|
|
207
|
+
def keys(self) -> PygameUserInput:
|
|
208
|
+
return self.backend.user_input
|
|
209
|
+
|
|
210
|
+
def get_map(self, map_name: str):
|
|
211
|
+
return self.backend.assets.get_map(map_name)
|
|
212
|
+
|
|
213
|
+
def load_usable(self, item: Item, item_id) -> None:
|
|
214
|
+
LOG.debug("Loading usable %s.", item_id)
|
|
215
|
+
item.init(self.db.get_usable_data(item_id))
|
|
216
|
+
self.memory.items[item_id] = item
|
|
217
|
+
|
|
218
|
+
# def load_weapon(self, weapon: Weapon, weapon_id: str) -> None:
|
|
219
|
+
# LOG.debug("Loading weapon %s.", weapon_id)
|
|
220
|
+
# weapon.init(self.db.get_weapon_data(weapon_id))
|
|
221
|
+
# self.memory.items[weapon_id] = weapon
|
|
222
|
+
|
|
223
|
+
# def load_item(self, item: Item, item_id: str) -> None:
|
|
224
|
+
# LOG.debug("Loading item %s.", item_id)
|
|
225
|
+
# item.init(self.db.get_item_data(item_id))
|
|
226
|
+
# self.memory.items[item_id] = item
|
|
227
|
+
|
|
228
|
+
# def load_item(self, item: Item):
|
|
229
|
+
# LOG.debug(f"Loading item {item.name}.")
|
|
230
|
+
# self.memory.items[item.name] = item
|
|
231
|
+
|
|
232
|
+
def get_item(self, item_id: str):
|
|
233
|
+
try:
|
|
234
|
+
return self.memory.items[item_id]
|
|
235
|
+
except KeyError:
|
|
236
|
+
LOG.exception(f"Item '{item_id}' is not defined!")
|
|
237
|
+
raise
|
|
238
|
+
except TypeError:
|
|
239
|
+
print(type(self.memory.items), item_id)
|
|
240
|
+
raise
|
|
241
|
+
|
|
242
|
+
def give_item(self, item: Union[str, Item], player: Player = Player.P1):
|
|
243
|
+
if isinstance(item, str):
|
|
244
|
+
item = self.get_item(item)
|
|
245
|
+
self.memory.bag[player].append(item)
|
|
246
|
+
# self.items.append(item)
|
|
247
|
+
return True
|
|
248
|
+
|
|
249
|
+
def take_item(self, item: Union[str, Item], player: Player = Player.P1):
|
|
250
|
+
if isinstance(item, str):
|
|
251
|
+
item = self.get_item(item)
|
|
252
|
+
|
|
253
|
+
if item in self.memory.bag[player]:
|
|
254
|
+
self.memory.bag[player].remove(item)
|
|
255
|
+
return True
|
|
256
|
+
return False
|
|
257
|
+
|
|
258
|
+
def has_item(self, item: Union[str, Item], player: Player = Player.P1):
|
|
259
|
+
if isinstance(item, str):
|
|
260
|
+
item = self.get_item(item)
|
|
261
|
+
|
|
262
|
+
return item in self.memory.bag[player]
|
|
263
|
+
# return False
|
|
264
|
+
|
|
265
|
+
def progress_quest(self, quest_name: str, new_state: int):
|
|
266
|
+
for quest in self.quests:
|
|
267
|
+
if quest.name == quest_name:
|
|
268
|
+
quest.state = new_state
|
|
269
|
+
|
|
270
|
+
def on_enter_background(self):
|
|
271
|
+
LOG.debug("About to enter background")
|
|
272
|
+
|
|
273
|
+
def on_entered_background(self):
|
|
274
|
+
LOG.debug("Entered background")
|
|
275
|
+
|
|
276
|
+
def on_enter_foreground(self):
|
|
277
|
+
LOG.debug("About to enter foreground")
|
|
278
|
+
|
|
279
|
+
def on_entered_foreground(self):
|
|
280
|
+
LOG.debug("Entered foreground")
|
|
281
|
+
|
|
282
|
+
def get_player(self, player: Player = Player.P1):
|
|
283
|
+
if player in self.memory.player:
|
|
284
|
+
return self.memory.player[player]
|
|
285
|
+
else:
|
|
286
|
+
return None
|
|
287
|
+
|
|
288
|
+
def set_player(self, creature: Creature, player: Player = Player.P1):
|
|
289
|
+
self.memory.player[player] = creature
|
|
290
|
+
|
|
291
|
+
def trigger_teleport(
|
|
292
|
+
self, active: bool = True, player: Player = Player.P1
|
|
293
|
+
):
|
|
294
|
+
self.memory.teleport_active[player] = active
|
|
295
|
+
|
|
296
|
+
def is_teleport_active(self, player: Player = Player.P1):
|
|
297
|
+
return self.memory.teleport_active[player]
|
|
298
|
+
|
|
299
|
+
def trigger_dialog(self, active: bool = True, player=Player.P1):
|
|
300
|
+
self.memory.dialog_active[player] = active
|
|
301
|
+
|
|
302
|
+
def is_dialog_active(self, player: Player = Player.P1):
|
|
303
|
+
return self.memory.dialog_active[player]
|
|
304
|
+
|
|
305
|
+
def trigger_script(
|
|
306
|
+
self, active: bool = True, player: Player = Player.P1
|
|
307
|
+
) -> None:
|
|
308
|
+
self.memory.script_active[player] = active
|
|
309
|
+
|
|
310
|
+
def is_script_active(self, player: Player = Player.P1) -> bool:
|
|
311
|
+
return self.memory.script_active[player]
|
|
312
|
+
|
|
313
|
+
def trigger_player_collision(
|
|
314
|
+
self, active: bool = True, player: Player = Player.P1
|
|
315
|
+
) -> None:
|
|
316
|
+
self.memory.player_collision_active[player] = active
|
|
317
|
+
|
|
318
|
+
def is_player_collision_active(self, player: Player = Player.P1) -> bool:
|
|
319
|
+
return self.memory.player_collision_active[player]
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def player(self):
|
|
323
|
+
print("Deprecated access to player. Use 'get_player' instead!")
|
|
324
|
+
return self.memory.player
|
|
325
|
+
|
|
326
|
+
@player.setter
|
|
327
|
+
def player(self, val):
|
|
328
|
+
print("Deprecated access to player. Use 'get_player' instead!")
|
|
329
|
+
self.memory.player = val
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def items(self):
|
|
333
|
+
# raise TypeError()
|
|
334
|
+
return self.memory.items
|
|
335
|
+
|
|
336
|
+
@items.setter
|
|
337
|
+
def items(self, val):
|
|
338
|
+
raise TypeError(f"Items cannot be set to type {type(val)}")
|
|
339
|
+
# self.memory.items = val
|
|
340
|
+
|
|
341
|
+
@property
|
|
342
|
+
def quests(self):
|
|
343
|
+
return self.memory.quests
|
|
344
|
+
|
|
345
|
+
@quests.setter
|
|
346
|
+
def quests(self, val):
|
|
347
|
+
self.memory.quests = val
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def dialog_active(self):
|
|
351
|
+
print("dialog_active is deprecated; use 'is_dialog_active()' instead.")
|
|
352
|
+
return self.memory.dialog_active
|
|
353
|
+
|
|
354
|
+
@dialog_active.setter
|
|
355
|
+
def dialog_active(self, val):
|
|
356
|
+
print(
|
|
357
|
+
"dialog_active is deprecated; use "
|
|
358
|
+
"'trigger_dialog(active)' instead."
|
|
359
|
+
)
|
|
360
|
+
self.memory.dialog_active = val
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def game_state(self) -> GameState:
|
|
364
|
+
return self.all_games[self.current_game]
|
|
365
|
+
|
|
366
|
+
def get_view(self):
|
|
367
|
+
return None
|
mima/core/mode_engine.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from ..states.game_state import GameState
|
|
5
|
+
from ..types.keys import Key as K
|
|
6
|
+
from ..types.mode import Mode
|
|
7
|
+
from ..types.nature import Nature
|
|
8
|
+
from ..types.player import Player
|
|
9
|
+
from ..view.mima_mode import MimaMode
|
|
10
|
+
from .engine import MimaEngine
|
|
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.draw_dyn_ids: bool = False
|
|
37
|
+
self.disable_filter: bool = False
|
|
38
|
+
self._timer = 1.0
|
|
39
|
+
|
|
40
|
+
def on_user_create(self):
|
|
41
|
+
# change_mode(Mode.LOADING)
|
|
42
|
+
# TODO add example modes
|
|
43
|
+
return True
|
|
44
|
+
|
|
45
|
+
def on_user_update(self, elapsed_time: float):
|
|
46
|
+
self.audio.update(elapsed_time)
|
|
47
|
+
|
|
48
|
+
# print("Update start")
|
|
49
|
+
|
|
50
|
+
if not self.mode.update(elapsed_time):
|
|
51
|
+
LOG.critical(f"Update of mode {self.mode} failed.")
|
|
52
|
+
return False
|
|
53
|
+
self._timer -= elapsed_time
|
|
54
|
+
if self._timer <= 0.0:
|
|
55
|
+
self._timer += 1.0
|
|
56
|
+
LOG.debug(
|
|
57
|
+
f"Updated {self.mode} (Current Stack:"
|
|
58
|
+
f"{[m.name for m in self.mode_stack]})"
|
|
59
|
+
)
|
|
60
|
+
# print("Update end")
|
|
61
|
+
return True
|
|
62
|
+
|
|
63
|
+
def change_mode(self, mode: Mode):
|
|
64
|
+
if mode in self.mode_stack:
|
|
65
|
+
self.mode_stack.remove(mode)
|
|
66
|
+
self.mode_stack.append(mode)
|
|
67
|
+
if self.mode is not None:
|
|
68
|
+
self.mode.unload()
|
|
69
|
+
self.mode = self.modes[mode]
|
|
70
|
+
self.mode.load()
|
|
71
|
+
|
|
72
|
+
def return_mode(self):
|
|
73
|
+
LOG.debug(
|
|
74
|
+
"Returning to previous mode. Stack: %s", str(self.mode_stack)
|
|
75
|
+
)
|
|
76
|
+
self.mode_stack.pop()
|
|
77
|
+
self.mode = self.modes[self.mode_stack[-1]]
|
|
78
|
+
self.mode.load()
|
|
79
|
+
|
|
80
|
+
def get_view(self):
|
|
81
|
+
return self.mode
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from .engine import MimaEngine
|
|
4
|
+
from .states.game_state import GameState
|
|
5
|
+
from .types.keys import Key as K
|
|
6
|
+
from .types.mode import Mode
|
|
7
|
+
from .types.nature import Nature
|
|
8
|
+
from .types.player import Player
|
|
9
|
+
from .view.scene import Scene
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MimaSceneEngine(MimaEngine):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
init_file: str,
|
|
16
|
+
config_path: str,
|
|
17
|
+
default_config: Dict[str, Any],
|
|
18
|
+
platform: str = "PC",
|
|
19
|
+
caption: str = "MimaEngine",
|
|
20
|
+
):
|
|
21
|
+
super().__init__(init_file, config_path, default_config, platform, caption)
|
|
22
|
+
|
|
23
|
+
self.scene_stack: List[str] = []
|
|
24
|
+
self._scenes: Dict[str, Scene] = {}
|
|
25
|
+
self._current_scene: Scene
|
|
26
|
+
|
|
27
|
+
self.save_timer_reset = 1.0
|
|
28
|
+
self._save_timer = self.save_timer_reset
|
|
29
|
+
# self.teleport_triggered: bool = False
|
|
30
|
+
# self.dialog_active: bool = False
|
|
31
|
+
|
|
32
|
+
def on_user_create(self):
|
|
33
|
+
return True
|
|
34
|
+
|
|
35
|
+
def on_user_update(self, elapsed_time: float):
|
|
36
|
+
self.audio.update(elapsed_time)
|
|
37
|
+
self._current_scene = self._scenes[self.scene_stack[-1]]
|
|
38
|
+
|
|
39
|
+
# self._current_scene = self.mode.name.lower()
|
|
40
|
+
|
|
41
|
+
self._save_timer -= elapsed_time
|
|
42
|
+
if self._save_timer <= 0.0:
|
|
43
|
+
self._save_timer += self.save_timer_reset
|
|
44
|
+
if self._current_scene.autosave:
|
|
45
|
+
for quest in self.quests:
|
|
46
|
+
for obj in self.scene.dynamics:
|
|
47
|
+
quest.on_interaction(self.scene.dynamics, obj, Nature.SAVE)
|
|
48
|
+
quest.save_state()
|
|
49
|
+
if self.keys.new_key_press(K.SELECT):
|
|
50
|
+
self.game_state.save_to_disk()
|
|
51
|
+
|
|
52
|
+
self.scene.update(elapsed_time)
|
|
53
|
+
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
def load_scene(self):
|
|
57
|
+
self.scene_stack
|
|
58
|
+
|
|
59
|
+
# def load_scene(self, map_name: str, px: float, py: float):
|
|
60
|
+
# type_string = (
|
|
61
|
+
# self.get_map(map_name).get_string("type", "local").upper()
|
|
62
|
+
# )
|
|
63
|
+
# if type_string == "WORLD":
|
|
64
|
+
# type_string = "WORLD_MAP"
|
|
65
|
+
# if type_string == "LOCAL":
|
|
66
|
+
# type_string = "LOCAL_MAP"
|
|
67
|
+
# mode = Mode[type_string]
|
|
68
|
+
|
|
69
|
+
# self.scene_stack.append(mode)
|
|
70
|
+
# self._scenes[self.scene_stack[-1]].load_scene()
|
|
71
|
+
# # self._scenes[self.scene_stack[-1]].change_map(map_name, px, py)
|
|
72
|
+
# # print(self.scene_stack)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def scene(self) -> Scene:
|
|
76
|
+
# return self._scenes[self._current_scene]
|
|
77
|
+
return self._current_scene
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def previous_scene(self) -> Scene:
|
|
81
|
+
return self._scenes[self.scene_stack[-2]]
|
|
File without changes
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
from pygame import Vector2
|
|
6
|
+
from typing_extensions import Any, Generic, TypeVar
|
|
7
|
+
|
|
8
|
+
from mima.integrated.sprite import Direction, GraphicState, Until, vel_to_dir
|
|
9
|
+
from mima.layered.shape import ShapeCollection
|
|
10
|
+
from mima.layered.shaped_sprite import SpriteWithShape
|
|
11
|
+
from mima.standalone.geometry import Circle, Rect
|
|
12
|
+
from mima.standalone.transformed_view import TileTransformedView
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Nature(Enum):
|
|
16
|
+
WALK = 0
|
|
17
|
+
ENTER = 1
|
|
18
|
+
EXIT = 2
|
|
19
|
+
TALK = 3
|
|
20
|
+
ATTACK = 4
|
|
21
|
+
KILLED = 5
|
|
22
|
+
SIGNAL = 6
|
|
23
|
+
NO_SIGNAL = 7
|
|
24
|
+
SAVE = 8
|
|
25
|
+
LOAD = 9
|
|
26
|
+
RESUME = 10
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
TEntity = TypeVar("TEntity", bound="Entity")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class Entity(Generic[TEntity]):
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
pos: Vector2,
|
|
36
|
+
name: str,
|
|
37
|
+
sprite: SpriteWithShape[GraphicState, Direction],
|
|
38
|
+
dyn_id: int = -1,
|
|
39
|
+
) -> None:
|
|
40
|
+
self._pos = pos
|
|
41
|
+
self.old_pos = Vector2(pos)
|
|
42
|
+
self.elevation: float = 0.0
|
|
43
|
+
self.name = name
|
|
44
|
+
self.sprite = sprite
|
|
45
|
+
self.dyn_id = dyn_id
|
|
46
|
+
self._facing_direction = Direction.SOUTH
|
|
47
|
+
self._graphic_state = GraphicState.STANDING
|
|
48
|
+
|
|
49
|
+
self._graphic_state_locked: bool = False
|
|
50
|
+
self._gs_lock_condition: Until = Until.UNLOCK
|
|
51
|
+
|
|
52
|
+
self.is_player: bool = False
|
|
53
|
+
self.is_collider: bool = False
|
|
54
|
+
self.collides_with_map: bool = False
|
|
55
|
+
self.collides_with_dyn: bool = False
|
|
56
|
+
self.is_redundant: bool = False
|
|
57
|
+
|
|
58
|
+
self._despawn_timer: float = 0.0
|
|
59
|
+
self.vel = Vector2(0, 0)
|
|
60
|
+
|
|
61
|
+
if self.sprite.hitbox is None:
|
|
62
|
+
self._hitbox: ShapeCollection = ShapeCollection(
|
|
63
|
+
pos, Circle(Vector2(0.5, 0.5), 0.5)
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
self._hitbox = self.sprite.hitbox
|
|
67
|
+
self._hitbox.update(pos)
|
|
68
|
+
|
|
69
|
+
self.speed: float = 5.0
|
|
70
|
+
|
|
71
|
+
# Debug flags
|
|
72
|
+
self.draw_shapes: bool = False
|
|
73
|
+
|
|
74
|
+
def update(self, elapsed_time: float) -> bool:
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
def update_visible(self, elapsed_time: float, ttv: TileTransformedView) -> bool:
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def update_global(self, elapsed_time: float) -> bool:
|
|
81
|
+
self.update_despawn_timer(elapsed_time)
|
|
82
|
+
|
|
83
|
+
new_direction = vel_to_dir(self.vel)
|
|
84
|
+
if new_direction is not None:
|
|
85
|
+
self._facing_direction = new_direction
|
|
86
|
+
|
|
87
|
+
if self.vel.x == 0 and self.vel.y == 0:
|
|
88
|
+
self._graphic_state = GraphicState.STANDING
|
|
89
|
+
else:
|
|
90
|
+
self._graphic_state = GraphicState.WALKING
|
|
91
|
+
if self.sprite.update(
|
|
92
|
+
elapsed_time, self._graphic_state, self._facing_direction
|
|
93
|
+
):
|
|
94
|
+
self._hitbox = (
|
|
95
|
+
self.sprite.hitbox if self.sprite.hitbox is not None else self._hitbox
|
|
96
|
+
)
|
|
97
|
+
self._hitbox.update(self._pos)
|
|
98
|
+
|
|
99
|
+
return self.update(elapsed_time)
|
|
100
|
+
|
|
101
|
+
def update_despawn_timer(self, elapsed_time: float) -> bool:
|
|
102
|
+
if self._despawn_timer > 0:
|
|
103
|
+
self._despawn_timer -= elapsed_time
|
|
104
|
+
if self._despawn_timer <= 0.0:
|
|
105
|
+
self.is_redundant = True
|
|
106
|
+
return True
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
def draw(self, ttv: TileTransformedView, cache: bool = False) -> None:
|
|
110
|
+
self.sprite.draw(self.get_pos(), ttv, cache=cache)
|
|
111
|
+
|
|
112
|
+
if self.draw_shapes:
|
|
113
|
+
ttv.draw_rect(
|
|
114
|
+
self._hitbox.bounding_box.pos,
|
|
115
|
+
self._hitbox.bounding_box.size,
|
|
116
|
+
(255, 0, 0),
|
|
117
|
+
)
|
|
118
|
+
for i, hb in enumerate(self._hitbox.shapes):
|
|
119
|
+
if isinstance(hb, Circle):
|
|
120
|
+
ttv.draw_circle(hb.pos, hb.radius, (255 - 50 * (i + 1), 0, 0))
|
|
121
|
+
if isinstance(hb, Rect):
|
|
122
|
+
ttv.draw_rect(hb.pos, hb.size, (255 - 50 * (i + 1), 0, 0))
|
|
123
|
+
|
|
124
|
+
ttv.fill_circle(self.get_pos(), 2 / 16, (0, 0, 0))
|
|
125
|
+
ttv.fill_circle(self.get_pos(), 1 / 16, (255, 255, 255))
|
|
126
|
+
ttv.fill_circle(self._hitbox.get_tl_pos(), 1 / 16, (0, 255, 255))
|
|
127
|
+
ttv.fill_circle(self._hitbox.get_br_pos(), 1 / 16, (255, 0, 255))
|
|
128
|
+
|
|
129
|
+
def on_interaction(self, obj: TEntity, nature: Nature) -> bool:
|
|
130
|
+
return False
|
|
131
|
+
|
|
132
|
+
def on_death(self) -> None:
|
|
133
|
+
return
|
|
134
|
+
|
|
135
|
+
def get_pos(self) -> Vector2:
|
|
136
|
+
return self._hitbox.pos
|
|
137
|
+
|
|
138
|
+
def set_pos(self, pos: Vector2) -> None:
|
|
139
|
+
self._pos = pos
|
|
140
|
+
self._hitbox.update(pos)
|
|
141
|
+
|
|
142
|
+
def get_hitbox(self) -> ShapeCollection:
|
|
143
|
+
return self._hitbox
|
|
144
|
+
|
|
145
|
+
def set_facing_direction(self, direction: Direction) -> None:
|
|
146
|
+
self._facing_direction = direction
|
|
147
|
+
|
|
148
|
+
def get_facing_direction(self) -> Direction:
|
|
149
|
+
return self._facing_direction
|
|
150
|
+
|
|
151
|
+
def set_graphic_state(self, graphic_state: GraphicState) -> bool:
|
|
152
|
+
if not self._graphic_state_locked:
|
|
153
|
+
self._graphic_state = graphic_state
|
|
154
|
+
return True
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
def get_graphic_state(self) -> GraphicState:
|
|
158
|
+
return self._graphic_state
|
|
159
|
+
|
|
160
|
+
def lock_graphic_state(
|
|
161
|
+
self, graphic_state: GraphicState, until: Until = Until.UNLOCK
|
|
162
|
+
) -> None:
|
|
163
|
+
self.set_graphic_state(graphic_state)
|
|
164
|
+
self._graphic_state_locked = True
|
|
165
|
+
self._gs_lock_condition = until
|
|
166
|
+
|
|
167
|
+
def unlock_graphic_state(self) -> None:
|
|
168
|
+
self._graphic_state_locked = False
|
|
169
|
+
self.set_graphic_state(GraphicState.STANDING)
|
|
170
|
+
|
|
171
|
+
def kill(self, delay: float = 0.0) -> None:
|
|
172
|
+
self._despawn_timer = delay
|
|
173
|
+
if self._despawn_timer <= 0.0:
|
|
174
|
+
self.is_redundant = True
|
|
175
|
+
|
|
176
|
+
def __str__(self) -> str:
|
|
177
|
+
return (
|
|
178
|
+
f"{self.__class__.__name__}(name={self.name}, id={self.dyn_id}, "
|
|
179
|
+
f"pos={self._hitbox.pos})"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def __repr__(self) -> str:
|
|
183
|
+
return str(self)
|