mima-engine 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. mima/__init__.py +4 -0
  2. mima/backend/__init__.py +1 -0
  3. mima/backend/pygame_assets.py +401 -0
  4. mima/backend/pygame_audio.py +78 -0
  5. mima/backend/pygame_backend.py +603 -0
  6. mima/backend/pygame_camera.py +63 -0
  7. mima/backend/pygame_events.py +695 -0
  8. mima/backend/touch_control_scheme_a.py +126 -0
  9. mima/backend/touch_control_scheme_b.py +132 -0
  10. mima/core/__init__.py +0 -0
  11. mima/core/collision.py +325 -0
  12. mima/core/database.py +58 -0
  13. mima/core/engine.py +367 -0
  14. mima/core/mode_engine.py +81 -0
  15. mima/core/scene_engine.py +81 -0
  16. mima/integrated/__init__.py +0 -0
  17. mima/integrated/entity.py +183 -0
  18. mima/integrated/layered_map.py +351 -0
  19. mima/integrated/sprite.py +156 -0
  20. mima/layered/__init__.py +0 -0
  21. mima/layered/assets.py +56 -0
  22. mima/layered/scene.py +415 -0
  23. mima/layered/shape.py +99 -0
  24. mima/layered/shaped_sprite.py +78 -0
  25. mima/layered/virtual_input.py +302 -0
  26. mima/maps/__init__.py +0 -0
  27. mima/maps/template.py +71 -0
  28. mima/maps/tile.py +20 -0
  29. mima/maps/tile_animation.py +7 -0
  30. mima/maps/tile_info.py +10 -0
  31. mima/maps/tile_layer.py +52 -0
  32. mima/maps/tiled/__init__.py +0 -0
  33. mima/maps/tiled/tiled_layer.py +48 -0
  34. mima/maps/tiled/tiled_map.py +95 -0
  35. mima/maps/tiled/tiled_object.py +79 -0
  36. mima/maps/tiled/tiled_objectgroup.py +25 -0
  37. mima/maps/tiled/tiled_template.py +49 -0
  38. mima/maps/tiled/tiled_tile.py +90 -0
  39. mima/maps/tiled/tiled_tileset.py +51 -0
  40. mima/maps/tilemap.py +216 -0
  41. mima/maps/tileset.py +39 -0
  42. mima/maps/tileset_info.py +9 -0
  43. mima/maps/transition_map.py +146 -0
  44. mima/objects/__init__.py +0 -0
  45. mima/objects/animated_sprite.py +217 -0
  46. mima/objects/attribute_effect.py +26 -0
  47. mima/objects/attributes.py +126 -0
  48. mima/objects/creature.py +384 -0
  49. mima/objects/dynamic.py +206 -0
  50. mima/objects/effects/__init__.py +0 -0
  51. mima/objects/effects/colorize_screen.py +60 -0
  52. mima/objects/effects/debug_box.py +133 -0
  53. mima/objects/effects/light.py +103 -0
  54. mima/objects/effects/show_sprite.py +50 -0
  55. mima/objects/effects/walking_on_grass.py +70 -0
  56. mima/objects/effects/walking_on_water.py +57 -0
  57. mima/objects/loader.py +111 -0
  58. mima/objects/projectile.py +111 -0
  59. mima/objects/sprite.py +116 -0
  60. mima/objects/world/__init__.py +0 -0
  61. mima/objects/world/color_gate.py +67 -0
  62. mima/objects/world/color_switch.py +101 -0
  63. mima/objects/world/container.py +175 -0
  64. mima/objects/world/floor_switch.py +109 -0
  65. mima/objects/world/gate.py +178 -0
  66. mima/objects/world/light_source.py +121 -0
  67. mima/objects/world/logic_gate.py +157 -0
  68. mima/objects/world/movable.py +399 -0
  69. mima/objects/world/oneway.py +195 -0
  70. mima/objects/world/pickup.py +157 -0
  71. mima/objects/world/switch.py +179 -0
  72. mima/objects/world/teleport.py +308 -0
  73. mima/py.typed +0 -0
  74. mima/scripts/__init__.py +2 -0
  75. mima/scripts/command.py +38 -0
  76. mima/scripts/commands/__init__.py +0 -0
  77. mima/scripts/commands/add_quest.py +19 -0
  78. mima/scripts/commands/change_map.py +34 -0
  79. mima/scripts/commands/close_dialog.py +9 -0
  80. mima/scripts/commands/equip_weapon.py +23 -0
  81. mima/scripts/commands/give_item.py +26 -0
  82. mima/scripts/commands/give_resource.py +51 -0
  83. mima/scripts/commands/move_map.py +152 -0
  84. mima/scripts/commands/move_to.py +49 -0
  85. mima/scripts/commands/oneway_move.py +58 -0
  86. mima/scripts/commands/parallel.py +66 -0
  87. mima/scripts/commands/play_sound.py +13 -0
  88. mima/scripts/commands/present_item.py +53 -0
  89. mima/scripts/commands/progress_quest.py +12 -0
  90. mima/scripts/commands/quit_game.py +8 -0
  91. mima/scripts/commands/save_game.py +14 -0
  92. mima/scripts/commands/screen_fade.py +83 -0
  93. mima/scripts/commands/serial.py +69 -0
  94. mima/scripts/commands/set_facing_direction.py +21 -0
  95. mima/scripts/commands/set_spawn_map.py +17 -0
  96. mima/scripts/commands/show_choices.py +52 -0
  97. mima/scripts/commands/show_dialog.py +118 -0
  98. mima/scripts/commands/take_coins.py +23 -0
  99. mima/scripts/script_processor.py +61 -0
  100. mima/standalone/__init__.py +0 -0
  101. mima/standalone/camera.py +153 -0
  102. mima/standalone/geometry.py +1318 -0
  103. mima/standalone/multicolumn_list.py +54 -0
  104. mima/standalone/pixel_font.py +84 -0
  105. mima/standalone/scripting.py +145 -0
  106. mima/standalone/spatial.py +186 -0
  107. mima/standalone/sprite.py +158 -0
  108. mima/standalone/tiled_map.py +1247 -0
  109. mima/standalone/transformed_view.py +433 -0
  110. mima/standalone/user_input.py +563 -0
  111. mima/states/__init__.py +0 -0
  112. mima/states/game_state.py +189 -0
  113. mima/states/memory.py +28 -0
  114. mima/states/quest.py +71 -0
  115. mima/types/__init__.py +0 -0
  116. mima/types/alignment.py +7 -0
  117. mima/types/blend.py +8 -0
  118. mima/types/damage.py +42 -0
  119. mima/types/direction.py +44 -0
  120. mima/types/gate_color.py +7 -0
  121. mima/types/graphic_state.py +23 -0
  122. mima/types/keys.py +64 -0
  123. mima/types/mode.py +9 -0
  124. mima/types/nature.py +12 -0
  125. mima/types/object.py +22 -0
  126. mima/types/player.py +9 -0
  127. mima/types/position.py +13 -0
  128. mima/types/start.py +7 -0
  129. mima/types/terrain.py +9 -0
  130. mima/types/tile_collision.py +11 -0
  131. mima/types/weapon_slot.py +6 -0
  132. mima/types/window.py +44 -0
  133. mima/usables/__init__.py +0 -0
  134. mima/usables/item.py +51 -0
  135. mima/usables/weapon.py +68 -0
  136. mima/util/__init__.py +1 -0
  137. mima/util/colors.py +50 -0
  138. mima/util/constants.py +55 -0
  139. mima/util/functions.py +38 -0
  140. mima/util/input_defaults.py +170 -0
  141. mima/util/logging.py +51 -0
  142. mima/util/property.py +8 -0
  143. mima/util/runtime_config.py +327 -0
  144. mima/util/trading_item.py +23 -0
  145. mima/view/__init__.py +0 -0
  146. mima/view/camera.py +192 -0
  147. mima/view/mima_mode.py +618 -0
  148. mima/view/mima_scene.py +231 -0
  149. mima/view/mima_view.py +12 -0
  150. mima/view/mima_window.py +244 -0
  151. mima_engine-0.4.0.dist-info/METADATA +47 -0
  152. mima_engine-0.4.0.dist-info/RECORD +153 -0
  153. mima_engine-0.4.0.dist-info/WHEEL +4 -0
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
@@ -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)