mima-engine 0.1.5__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mima-engine might be problematic. Click here for more details.

Files changed (80) hide show
  1. mima/__init__.py +1 -1
  2. mima/backend/pygame_assets.py +14 -8
  3. mima/backend/pygame_audio.py +5 -2
  4. mima/backend/pygame_backend.py +255 -57
  5. mima/backend/pygame_camera.py +63 -0
  6. mima/backend/pygame_events.py +369 -120
  7. mima/collision.py +182 -111
  8. mima/engine.py +155 -15
  9. mima/maps/tiled/tiled_map.py +3 -3
  10. mima/maps/tiled/tiled_tileset.py +1 -0
  11. mima/maps/tilemap.py +78 -15
  12. mima/maps/tileset.py +8 -2
  13. mima/maps/transition_map.py +6 -8
  14. mima/mode_engine.py +80 -0
  15. mima/objects/animated_sprite.py +23 -15
  16. mima/objects/attributes.py +3 -0
  17. mima/objects/creature.py +54 -17
  18. mima/objects/dynamic.py +30 -8
  19. mima/objects/effects/colorize_screen.py +22 -6
  20. mima/objects/effects/debug_box.py +124 -0
  21. mima/objects/effects/light.py +21 -30
  22. mima/objects/effects/show_sprite.py +39 -0
  23. mima/objects/effects/walking_on_grass.py +25 -7
  24. mima/objects/effects/walking_on_water.py +17 -6
  25. mima/objects/loader.py +24 -13
  26. mima/objects/projectile.py +21 -6
  27. mima/objects/sprite.py +7 -8
  28. mima/objects/world/color_gate.py +5 -2
  29. mima/objects/world/color_switch.py +12 -6
  30. mima/objects/world/container.py +17 -8
  31. mima/objects/world/floor_switch.py +8 -4
  32. mima/objects/world/gate.py +8 -5
  33. mima/objects/world/light_source.py +11 -9
  34. mima/objects/world/logic_gate.py +8 -7
  35. mima/objects/world/movable.py +72 -28
  36. mima/objects/world/oneway.py +14 -9
  37. mima/objects/world/pickup.py +10 -5
  38. mima/objects/world/switch.py +28 -25
  39. mima/objects/world/teleport.py +76 -55
  40. mima/scene_engine.py +19 -20
  41. mima/scripts/command.py +16 -2
  42. mima/scripts/commands/change_map.py +23 -4
  43. mima/scripts/commands/equip_weapon.py +23 -0
  44. mima/scripts/commands/give_item.py +5 -3
  45. mima/scripts/commands/move_map.py +9 -9
  46. mima/scripts/commands/parallel.py +16 -3
  47. mima/scripts/commands/present_item.py +7 -5
  48. mima/scripts/commands/screen_fade.py +30 -12
  49. mima/scripts/commands/serial.py +30 -7
  50. mima/scripts/commands/set_spawn_map.py +6 -3
  51. mima/scripts/commands/show_choices.py +16 -7
  52. mima/scripts/commands/show_dialog.py +110 -3
  53. mima/scripts/script_processor.py +41 -20
  54. mima/states/game_state.py +2 -0
  55. mima/states/memory.py +28 -0
  56. mima/states/quest.py +2 -3
  57. mima/types/keys.py +48 -0
  58. mima/types/mode.py +4 -10
  59. mima/types/player.py +9 -0
  60. mima/types/position.py +13 -0
  61. mima/types/tile_collision.py +11 -0
  62. mima/types/window.py +44 -0
  63. mima/usables/item.py +1 -0
  64. mima/util/colors.py +5 -0
  65. mima/util/constants.py +6 -0
  66. mima/util/functions.py +27 -0
  67. mima/util/input_defaults.py +109 -0
  68. mima/util/runtime_config.py +234 -30
  69. mima/util/trading_item.py +20 -0
  70. mima/view/camera.py +160 -19
  71. mima/view/mima_mode.py +612 -0
  72. mima/view/mima_scene.py +225 -0
  73. mima/view/mima_view.py +12 -0
  74. mima/view/mima_window.py +153 -0
  75. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/METADATA +4 -2
  76. mima_engine-0.2.1.dist-info/RECORD +128 -0
  77. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/WHEEL +1 -1
  78. mima/view/scene.py +0 -322
  79. mima_engine-0.1.5.dist-info/RECORD +0 -114
  80. {mima_engine-0.1.5.dist-info → mima_engine-0.2.1.dist-info}/top_level.txt +0 -0
@@ -1,27 +1,27 @@
1
+ from typing import List, Optional
2
+
1
3
  from ...maps.transition_map import TransitionMap
2
4
  from ...objects.dynamic import Dynamic
3
- from ...util.constants import (
4
- HEIGHT,
5
- MOVE_MAP_DURATION,
6
- TILE_HEIGHT,
7
- TILE_WIDTH,
8
- WIDTH,
9
- )
5
+ from ...types.player import Player
6
+ from ...util.constants import HEIGHT, MOVE_MAP_DURATION, WIDTH
10
7
  from ..command import Command
11
8
 
12
9
 
13
10
  class CommandMoveMap(Command):
14
11
  def __init__(
15
12
  self,
13
+ *,
16
14
  new_map_name: str,
17
15
  obj: Dynamic,
18
16
  target_px: float,
19
17
  target_py: float,
20
18
  vx: float,
21
19
  vy: float,
20
+ players: Optional[List[Player]] = None,
22
21
  ):
23
22
  super().__init__()
24
23
 
24
+ self.players = [obj.is_player()]
25
25
  self.src_map = self.engine.scene.tilemap
26
26
  self.dst_map = self.engine.assets.get_map(new_map_name)
27
27
  self.obj: Dynamic = obj
@@ -129,7 +129,7 @@ class CommandMoveMap(Command):
129
129
  )
130
130
 
131
131
  def _get_offset_x(self, px: float) -> float:
132
- visible_tiles = WIDTH / TILE_WIDTH
132
+ visible_tiles = WIDTH / self.engine.rtc.tile_width
133
133
  offset = px - visible_tiles / 2.0
134
134
 
135
135
  if offset < 0:
@@ -140,7 +140,7 @@ class CommandMoveMap(Command):
140
140
  return offset
141
141
 
142
142
  def _get_offset_y(self, py: float) -> float:
143
- visible_tiles = HEIGHT / TILE_HEIGHT - 1
143
+ visible_tiles = HEIGHT / self.engine.rtc.tile_width - 1
144
144
  offset = py - visible_tiles / 2.0
145
145
 
146
146
  if offset < 0:
@@ -1,4 +1,6 @@
1
- from typing import List
1
+ from typing import List, Optional
2
+
3
+ from ...types.player import Player
2
4
  from ..command import Command
3
5
 
4
6
 
@@ -7,7 +9,12 @@ class CommandParallel(Command):
7
9
  ANY_COMPLETED: int = 1
8
10
  FIRST_COMPLETED: int = 2
9
11
 
10
- def __init__(self, cmds: List[Command], completed_when: int = ALL_COMPLETED):
12
+ def __init__(
13
+ self,
14
+ cmds: List[Command],
15
+ completed_when: int = ALL_COMPLETED,
16
+ players: Optional[List[Player]] = None,
17
+ ):
11
18
  super().__init__()
12
19
  self._cmds: List[Command] = cmds
13
20
  self._completed_when: int = completed_when
@@ -28,7 +35,8 @@ class CommandParallel(Command):
28
35
  self._check_for_first()
29
36
  else:
30
37
  raise ValueError(
31
- f"Unknown value {self._completed_when} for " "attribute _completed_when"
38
+ f"Unknown value {self._completed_when} for "
39
+ "attribute _completed_when"
32
40
  )
33
41
 
34
42
  def finalize(self):
@@ -51,3 +59,8 @@ class CommandParallel(Command):
51
59
 
52
60
  def _check_for_first(self):
53
61
  self.completed = self._cmds[0].completed
62
+
63
+ def set_players(self, players: List[Player]):
64
+ self.players = players
65
+ for cmd in self._cmds:
66
+ cmd.set_players(players)
@@ -14,14 +14,13 @@ class CommandPresentItem(Command):
14
14
  def __init__(self, item_name: str, dynamic: Optional[Dynamic] = None):
15
15
  super().__init__()
16
16
 
17
- if dynamic is None:
18
- dynamic = self.engine.player
19
-
20
- self._dynamic: Dynamic = dynamic
17
+ self._dynamic: Optional[Dynamic] = dynamic
21
18
  self._item_name: str = item_name
22
19
  self._item_sprite: Optional[Projectile] = None
23
20
 
24
21
  def start(self):
22
+ if self._dynamic is None:
23
+ self._dynamic = self.engine.get_player(self.players[0])
25
24
  item = self.engine.get_item(self._item_name)
26
25
  self._item_sprite = Projectile(
27
26
  self._dynamic.px,
@@ -30,6 +29,7 @@ class CommandPresentItem(Command):
30
29
  0,
31
30
  3600,
32
31
  self._dynamic.alignment,
32
+ self._dynamic.tilemap,
33
33
  )
34
34
  self._item_sprite.layer = 2
35
35
  self._item_sprite.sprite.name = item.sprite_name
@@ -42,7 +42,9 @@ class CommandPresentItem(Command):
42
42
  self._item_sprite.one_hit = False
43
43
  self._item_sprite.damage = 0
44
44
 
45
- self.engine.scene.add_projectile(self._item_sprite)
45
+ self.engine.get_view().add_projectile(
46
+ self._item_sprite, self._dynamic.tilemap.name
47
+ )
46
48
  self._dynamic.lock_graphic_state(GraphicState.CELEBRATING)
47
49
  self.completed = True
48
50
 
@@ -1,8 +1,8 @@
1
1
  from typing import Optional
2
2
 
3
3
  from ...objects.effects.colorize_screen import ColorizeScreen
4
+ from ...util.colors import BLACK, Color
4
5
  from ..command import Command
5
- from ...util.colors import BLACK,Color
6
6
 
7
7
 
8
8
  class CommandScreenFade(Command):
@@ -11,12 +11,13 @@ class CommandScreenFade(Command):
11
11
  duration: float = 0.5,
12
12
  color: Optional[Color] = None,
13
13
  fadein: bool = False,
14
+ map_name: Optional[str] = None,
14
15
  ):
15
16
  super().__init__()
16
17
 
17
18
  self.duration: float = duration
18
19
  if color is None:
19
- color = self.engine.rtc.colors["gb_dark"]
20
+ color = self.engine.rtc.color_black
20
21
 
21
22
  self.color: Color = color
22
23
  self.fadein: bool = fadein
@@ -30,16 +31,34 @@ class CommandScreenFade(Command):
30
31
  self._end = 0
31
32
  self._iter = -self._iter
32
33
 
33
- # self._alpha = chain([i for i in range(self._start, self._end, self._iter)])
34
+ self._map_name = map_name
34
35
 
35
- self._effect = ColorizeScreen()
36
+ self._effect: Optional[ColorizeScreen] = None
36
37
  # self.duration -= 0.1
37
- self._effect.alpha = self._start
38
38
 
39
39
  def start(self):
40
- self.engine.scene.add_effect(self._effect)
41
- self.engine.player.vx = 0
42
- self.engine.player.vy = 0
40
+
41
+ self._effect = ColorizeScreen(
42
+ cameras=[
43
+ self.engine.get_view().get_camera_name(p) for p in self.players
44
+ ]
45
+ )
46
+ self._effect.alpha = self._start
47
+ if self._map_name is None:
48
+ map_names = list(
49
+ set(
50
+ [
51
+ self.engine.get_player(p).tilemap.name
52
+ for p in self.players
53
+ ]
54
+ )
55
+ )
56
+ for map_name in map_names:
57
+ self.engine.get_view().add_effect(self._effect, map_name)
58
+ else:
59
+ self.engine.get_view().add_effect(self._effect, self._map_name)
60
+ for p in self.players:
61
+ self.engine.get_player(p).halt()
43
62
 
44
63
  def update(self, elapsed_time: float):
45
64
  self.time_so_far += elapsed_time
@@ -51,10 +70,9 @@ class CommandScreenFade(Command):
51
70
  alpha = 255 - alpha
52
71
 
53
72
  self._effect.alpha = min(255, max(0, alpha))
54
- # print(self, self._effect.alpha)
55
73
 
56
- self.engine.player.vx = 0
57
- self.engine.player.vy = 0
74
+ for p in self.players:
75
+ self.engine.get_player(p).halt()
58
76
 
59
77
  if self.time_so_far >= self.duration:
60
78
  self._effect.alpha = self._end
@@ -62,4 +80,4 @@ class CommandScreenFade(Command):
62
80
 
63
81
  def finalize(self):
64
82
  self.completed = True
65
- self._effect.kill()
83
+ self._effect.kill()
@@ -1,25 +1,35 @@
1
- from typing import List, Union
2
1
  import logging
2
+ from typing import List, Optional, Union
3
+
4
+ from ...types.player import Player
3
5
  from ..command import Command
4
6
 
5
7
  LOG = logging.getLogger(__name__)
6
8
 
9
+
7
10
  class CommandSerial(Command):
8
11
  START_AND_UPDATE: int = 0
9
12
  START_WITHOUT_UPDATE: int = 1
10
13
 
11
- def __init__(self, cmds: List[Command], update_when: Union[int, List[int]] = START_AND_UPDATE):
12
- super().__init__()
14
+ def __init__(
15
+ self,
16
+ cmds: List[Command],
17
+ update_when: Union[int, List[int]] = START_AND_UPDATE,
18
+ players: Optional[List[Player]] = None,
19
+ ):
20
+ super().__init__(players)
13
21
  self._cmds: List[Command] = cmds
22
+
14
23
  self._current: int = 0
15
24
  self._update_when: List[int]
16
-
17
25
  if not isinstance(update_when, list):
18
- self._update_when = [update_when for _ in range(len(self._cmds)-1)]
26
+ self._update_when = [
27
+ update_when for _ in range(len(self._cmds) - 1)
28
+ ]
19
29
  else:
20
30
  self._update_when = update_when
21
31
 
22
- if len(self._update_when) < len(self._cmds):
32
+ if len(self._update_when) + 1 < len(self._cmds):
23
33
  LOG.warning(
24
34
  "Unsufficient information for 'update_when' of serial command:"
25
35
  f"cmds={self._cmds}, update_when={self._update_when}. Using "
@@ -31,16 +41,29 @@ class CommandSerial(Command):
31
41
 
32
42
  def update(self, delta_time):
33
43
  if not self._cmds[self._current].completed:
44
+ # Not completed -> Update current command
34
45
  self._cmds[self._current].update(delta_time)
35
46
  else:
47
+ # Current command completed -> progress to next
36
48
  self._current += 1
37
49
  if self._current == len(self._cmds):
50
+ # Reached end? -> Finish serial command
38
51
  self.completed = True
39
52
  else:
53
+ # End not reached -> call start of next command
40
54
  self._cmds[self._current].start()
41
- if self._update_when[self._current-1] == self.START_AND_UPDATE:
55
+ if (
56
+ self._update_when[self._current - 1]
57
+ == self.START_AND_UPDATE
58
+ ):
59
+ # Start the command already with update
42
60
  self._cmds[self._current].update(delta_time)
43
61
 
44
62
  def finalize(self):
45
63
  for cmd in self._cmds:
46
64
  cmd.finalize()
65
+
66
+ def set_players(self, players: List[Player]):
67
+ self.players = players
68
+ for cmd in self._cmds:
69
+ cmd.set_players(players)
@@ -8,7 +8,10 @@ class CommandSetSavePosition(Command):
8
8
  self._py = py
9
9
 
10
10
  def start(self):
11
- self.engine.player_state.save_map = self._map_name
12
- self.engine.player_state.save_px = self._px
13
- self.engine.player_state.save_py = self._py
11
+
12
+ self.engine.memory.player_state[self.players[0]].save_map = (
13
+ self._map_name
14
+ )
15
+ self.engine.memory.player_state[self.players[0]].save_px = self._px
16
+ self.engine.memory.player_state[self.players[0]].save_py = self._py
14
17
  self.completed = True
@@ -1,6 +1,8 @@
1
1
  from typing import List
2
+
2
3
  from ...scripts.command import Command
3
4
  from ...types.keys import Key as K
5
+ from ...types.player import Player
4
6
 
5
7
 
6
8
  class CommandShowChoices(Command):
@@ -20,13 +22,13 @@ class CommandShowChoices(Command):
20
22
  self._cursor_pos: int = 0
21
23
 
22
24
  def update(self, elapsed_time: float):
23
- if self.engine.keys.new_key_release(K.UP):
25
+ if self.engine.keys.new_key_release(K.UP, self.players[0]):
24
26
  self._cursor_pos = (self._cursor_pos - 1) % len(self._options)
25
- elif self.engine.keys.new_key_release(K.DOWN):
27
+ elif self.engine.keys.new_key_release(K.DOWN, self.players[0]):
26
28
  self._cursor_pos = (self._cursor_pos + 1) % len(self._options)
27
- elif self.engine.keys.new_key_press(K.A):
29
+ elif self.engine.keys.new_key_press(K.A, self.players[0]):
28
30
  self.completed = True
29
- elif self.engine.keys.new_key_release(K.B):
31
+ elif self.engine.keys.new_key_release(K.B, self.players[0]):
30
32
  self._cursor_pos = self._default_option
31
33
  self.completed = True
32
34
 
@@ -36,8 +38,15 @@ class CommandShowChoices(Command):
36
38
  lines.append(f"> {opt}")
37
39
  else:
38
40
  lines.append(f" {opt}")
39
-
40
- self.engine.scene.show_dialog(lines)
41
+ # for p in self.players:
42
+ self.engine.get_view().show_dialog(lines, self.players[0])
41
43
 
42
44
  def finalize(self):
43
- self.engine.script.add_command(self._replies[self._cursor_pos])
45
+ self.engine.script.add_command(
46
+ self._replies[self._cursor_pos], players=self.players
47
+ )
48
+
49
+ def set_players(self, players: List[Player]):
50
+ for cmd in self._replies:
51
+ cmd.set_players(players)
52
+ self.players = players
@@ -1,11 +1,118 @@
1
- from typing import List
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List, Optional
4
+
2
5
  from ...scripts.command import Command
6
+ from ...util.constants import DIALOG_CHARS_PER_LINE, DIALOG_N_LINES
7
+ from ...util.functions import wrap_text
8
+
9
+ if TYPE_CHECKING:
10
+ from ...types.player import Player
3
11
 
4
12
 
5
13
  class CommandShowDialog(Command):
6
- def __init__(self, lines: List[str]):
14
+ def __init__(
15
+ self,
16
+ lines: List[str],
17
+ *,
18
+ no_auto_wrap: bool = False,
19
+ chars_per_line: int = DIALOG_CHARS_PER_LINE,
20
+ n_lines: int = DIALOG_N_LINES,
21
+ ):
7
22
  super().__init__()
23
+
8
24
  self.lines: List[str] = lines
25
+ self._current_lines: List[str] = []
26
+ self._no_auto_wrap: bool = no_auto_wrap
27
+ self._chars_per_line: int = chars_per_line
28
+ self._n_lines: int = n_lines
29
+ self._row: int = 0
30
+ self._char: int = 0
31
+ self._first_row: int = 0
32
+ self._last_row: int = self._n_lines
33
+
34
+ self._timer_reset = 0.01
35
+ self._timer = self._timer_reset
36
+ self._auto_scroll: bool = True
37
+ self._progress: bool = False
38
+ self._lines_changed: bool = True
9
39
 
10
40
  def start(self):
11
- self.engine.scene.show_dialog(self.lines)
41
+ if not self._no_auto_wrap:
42
+ one_liner = ""
43
+ for line in self.lines:
44
+ one_liner += f"{line.strip()} "
45
+
46
+ self.lines = wrap_text(one_liner, self._chars_per_line)
47
+ self._current_lines = [self.lines[0][0]]
48
+ self._char += 1
49
+ for p in self.players:
50
+ self.engine.get_view().show_dialog(self._current_lines, p)
51
+
52
+ def update(self, elapsed_time: float):
53
+ if self._auto_scroll:
54
+ self._timer -= elapsed_time
55
+ if self._timer <= 0.0:
56
+ self._timer += self._timer_reset
57
+
58
+ self._current_lines = []
59
+ for r in range(self._first_row, self._last_row):
60
+ if r < self._row:
61
+ self._current_lines.append(self.lines[r])
62
+ continue
63
+
64
+ line = ""
65
+
66
+ for c in range(self._char + 1):
67
+ if c + 1 >= self._char or c + 1 >= len(self.lines[r]):
68
+ break
69
+ line += self.lines[r][c]
70
+
71
+ self._current_lines.append(line)
72
+
73
+ if r + 1 >= self._row:
74
+ break
75
+
76
+ self._char += 1
77
+ if self._char >= self._chars_per_line or self._char >= len(
78
+ self.lines[r]
79
+ ):
80
+ self._char = 0
81
+ self._row += 1
82
+
83
+ if self._row >= self._n_lines + self._first_row:
84
+ self._first_row += 1
85
+ self._last_row += 1
86
+ if self._row >= len(self.lines):
87
+ self._auto_scroll = False
88
+
89
+ # print(f"'{self._current_lines}'")
90
+ for p in self.players:
91
+ self.engine.get_view().show_dialog(self._current_lines, p)
92
+ else:
93
+ if self._lines_changed:
94
+ self._current_lines = []
95
+ for r in range(self._first_row, self._last_row):
96
+ if r >= len(self.lines):
97
+ break
98
+ self._current_lines.append(self.lines[r])
99
+ for p in self.players:
100
+ self.engine.get_view().show_dialog(self._current_lines, p)
101
+ self._lines_changed = False
102
+
103
+ if self._progress:
104
+ self._first_row += self._n_lines
105
+ self._last_row += self._n_lines
106
+ self._progress = False
107
+ self._lines_changed = True
108
+
109
+ def can_complete(self, force: bool = False) -> bool:
110
+ if force:
111
+ return True
112
+ if self._auto_scroll:
113
+ self._auto_scroll = False
114
+ elif self._last_row >= len(self.lines):
115
+ return True
116
+ else:
117
+ self._progress = True
118
+ return False
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, List
3
+ from typing import TYPE_CHECKING, Dict, List, Optional
4
4
 
5
+ from ..types.player import Player
5
6
  from .command import Command
6
7
 
7
8
  if TYPE_CHECKING:
@@ -12,29 +13,49 @@ class ScriptProcessor:
12
13
  engine: MimaEngine
13
14
 
14
15
  def __init__(self):
15
- self.commands: List[Command] = []
16
- self.user_control_enabled: bool = True
16
+ self.commands: Dict[Player : List[Command]] = {}
17
17
 
18
- def add_command(self, cmd: Command):
19
- self.commands.append(cmd)
18
+ def add_command(
19
+ self, cmd: Command, *, players: Optional[List[Player]] = None
20
+ ):
21
+ if players is not None and players:
22
+ cmd.set_players(players)
23
+
24
+ for p in cmd.players:
25
+ self.commands.setdefault(p, []).append(cmd)
26
+ # self.commands.append(cmd)
20
27
 
21
28
  def process_command(self, elapsed_time: float):
22
- self.user_control_enabled = len(self.commands) == 0
23
- if self.commands:
24
- if not self.commands[0].completed:
25
- if not self.commands[0].started:
26
- self.commands[0].start()
27
- self.commands[0].started = True
29
+ # for p in Player:
30
+ # self.engine.trigger_script(False, p)
31
+ # for cmd in self.commands:
32
+ # if p in cmd.players or Player.P0 in cmd.players:
33
+ # self.engine.trigger_script(True, p)
34
+ # break
35
+ for p in self.commands:
36
+ if self.commands[p]:
37
+ self.engine.trigger_script(True, p)
38
+ if not self.commands[p][0].completed:
39
+ if not self.commands[p][0].started:
40
+ self.commands[p][0].start()
41
+ self.commands[p][0].started = True
42
+ else:
43
+ self.commands[p][0].update(elapsed_time)
28
44
  else:
29
- self.commands[0].update(elapsed_time)
45
+ self.commands[p][0].finalize()
46
+ self.commands[p].pop(0)
47
+ self.engine.trigger_script(False, p)
30
48
  else:
31
- self.commands[0].finalize()
32
- self.commands.pop(0)
49
+ self.engine.trigger_script(False, p)
33
50
 
34
- def complete_command(self, force: bool = False):
35
- if self.commands:
36
- if self.commands[0].uninterruptible and not force:
37
- return False
38
- else:
39
- self.commands[0].completed = True
51
+ def complete_command(self, player: Player, force: bool = False):
52
+ if self.commands[player]:
53
+ # for cmd in self.commands:
54
+ # if player in cmd.players or Player.P0 in cmd.players:
55
+
56
+ if self.commands[player][0].can_complete(force):
57
+ self.commands[player][0].completed = True
40
58
  return True
59
+ else:
60
+ return False
61
+ return True
mima/states/game_state.py CHANGED
@@ -82,6 +82,7 @@ class GameState:
82
82
  filename = os.path.join(
83
83
  os.path.split(filename)[0], "autosave.json"
84
84
  )
85
+ self.save_value("savefile_name", "autosave")
85
86
  self.save_value("player__pos_x", 5.0)
86
87
  self.save_value("player__pos_y", 5.0)
87
88
  self.save_value(
@@ -131,6 +132,7 @@ def convert(value, astype):
131
132
 
132
133
 
133
134
  def load_saved_games(save_path, save_file_name):
135
+ os.makedirs(save_path, exist_ok=True)
134
136
  all_games = {}
135
137
  if not os.path.isdir(save_path):
136
138
  LOG.warning(f"Save folder does not exist: {save_path}")
mima/states/memory.py ADDED
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Dict, List
4
+
5
+ from ..types.player import Player
6
+
7
+ if TYPE_CHECKING:
8
+ from ..objects.creature import Creature
9
+ from ..usables.item import Item
10
+ from .quest import Quest
11
+
12
+
13
+ class Memory:
14
+ def __init__(self):
15
+ self.player: Dict[Player, Creature] = {}
16
+ self.items: Dict[str, Any] = {}
17
+ self.quests: List[Quest] = []
18
+ self.teleport_active: Dict[Player, bool] = {p: False for p in Player}
19
+ self.dialog_active: Dict[Player, bool] = {p: False for p in Player}
20
+ self.script_active: Dict[Player, bool] = {p: False for p in Player}
21
+ self.player_collision_active: Dict[Player, bool] = {
22
+ p: False for p in Player
23
+ }
24
+ self.bag: Dict[Player, List[Item]] = {p: [] for p in Player}
25
+ self.map_name: Dict[Player, str] = {}
26
+
27
+ self.last_spawn_px: Dict[Player, float] = {p: 0.0 for p in Player}
28
+ self.last_spawn_py: Dict[Player, float] = {p: 0.0 for p in Player}
mima/states/quest.py CHANGED
@@ -8,6 +8,7 @@ if TYPE_CHECKING:
8
8
  from ..engine.mima_engine import MimaEngine
9
9
  from ..objects.dynamic import Dynamic
10
10
  from ..types.nature import Nature
11
+ from ..types.player import Player
11
12
 
12
13
 
13
14
  class Quest:
@@ -21,9 +22,7 @@ class Quest:
21
22
  self.completed: bool = False
22
23
  self.state: int = 0
23
24
 
24
- def on_interaction(
25
- self, dynamics: List[Dynamic], target: Dynamic, nature: Nature
26
- ):
25
+ def on_interaction(self, target: Dynamic, nature: Nature, player: Player):
27
26
  return False
28
27
 
29
28
  def populate_dynamics(self, dynamics: List[Dynamic], map_name: str):
mima/types/keys.py CHANGED
@@ -14,3 +14,51 @@ class Key(Enum):
14
14
  L = 9
15
15
  START = 10
16
16
  SELECT = 11
17
+ P1_UP = 12
18
+ P1_DOWN = 13
19
+ P1_LEFT = 14
20
+ P1_RIGHT = 15
21
+ P1_A = 16
22
+ P1_B = 17
23
+ P1_X = 18
24
+ P1_Y = 19
25
+ P1_R = 20
26
+ P1_L = 21
27
+ P1_START = 22
28
+ P1_SELECT = 23
29
+ P2_UP = 24
30
+ P2_DOWN = 25
31
+ P2_LEFT = 26
32
+ P2_RIGHT = 27
33
+ P2_A = 28
34
+ P2_B = 29
35
+ P2_X = 30
36
+ P2_Y = 31
37
+ P2_R = 32
38
+ P2_L = 33
39
+ P2_START = 34
40
+ P2_SELECT = 35
41
+ P3_UP = 36
42
+ P3_DOWN = 37
43
+ P3_LEFT = 38
44
+ P3_RIGHT = 39
45
+ P3_A = 40
46
+ P3_B = 41
47
+ P3_X = 42
48
+ P3_Y = 43
49
+ P3_R = 44
50
+ P3_L = 45
51
+ P3_START = 46
52
+ P3_SELECT = 47
53
+ P4_UP = 48
54
+ P4_DOWN = 49
55
+ P4_LEFT = 50
56
+ P4_RIGHT = 51
57
+ P4_A = 52
58
+ P4_B = 53
59
+ P4_X = 54
60
+ P4_Y = 55
61
+ P4_R = 56
62
+ P4_L = 57
63
+ P4_START = 58
64
+ P4_SELECT = 59
mima/types/mode.py CHANGED
@@ -3,13 +3,7 @@ from enum import Enum
3
3
 
4
4
  class Mode(Enum):
5
5
  LOADING = 0
6
- TITLE = 1
7
- SELECT_GAME = 2
8
- LOCAL_MAP = 3
9
- WORLD_MAP = 4
10
- INVENTORY = 5
11
- SHOP = 6
12
- GAME_OVER = 7
13
- TITLE_SETTINGS = 8
14
- CONTROLS = 9
15
- COMBAT = 10
6
+ MENU = 1
7
+ SESSION = 2
8
+ COMBAT = 3
9
+ INVENTORY = 4