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
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from ...types.player import Player
|
|
5
|
+
from ..command import Command
|
|
6
|
+
|
|
7
|
+
LOG = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CommandSerial(Command):
|
|
11
|
+
START_AND_UPDATE: int = 0
|
|
12
|
+
START_WITHOUT_UPDATE: int = 1
|
|
13
|
+
|
|
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)
|
|
21
|
+
self._cmds: List[Command] = cmds
|
|
22
|
+
|
|
23
|
+
self._current: int = 0
|
|
24
|
+
self._update_when: List[int]
|
|
25
|
+
if not isinstance(update_when, list):
|
|
26
|
+
self._update_when = [
|
|
27
|
+
update_when for _ in range(len(self._cmds) - 1)
|
|
28
|
+
]
|
|
29
|
+
else:
|
|
30
|
+
self._update_when = update_when
|
|
31
|
+
|
|
32
|
+
if len(self._update_when) + 1 < len(self._cmds):
|
|
33
|
+
LOG.warning(
|
|
34
|
+
"Unsufficient information for 'update_when' of serial command:"
|
|
35
|
+
f"cmds={self._cmds}, update_when={self._update_when}. Using "
|
|
36
|
+
"default value!"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def start(self):
|
|
40
|
+
self._cmds[self._current].start()
|
|
41
|
+
|
|
42
|
+
def update(self, delta_time):
|
|
43
|
+
if not self._cmds[self._current].completed:
|
|
44
|
+
# Not completed -> Update current command
|
|
45
|
+
self._cmds[self._current].update(delta_time)
|
|
46
|
+
else:
|
|
47
|
+
# Current command completed -> progress to next
|
|
48
|
+
self._current += 1
|
|
49
|
+
if self._current == len(self._cmds):
|
|
50
|
+
# Reached end? -> Finish serial command
|
|
51
|
+
self.completed = True
|
|
52
|
+
else:
|
|
53
|
+
# End not reached -> call start of next command
|
|
54
|
+
self._cmds[self._current].start()
|
|
55
|
+
if (
|
|
56
|
+
self._update_when[self._current - 1]
|
|
57
|
+
== self.START_AND_UPDATE
|
|
58
|
+
):
|
|
59
|
+
# Start the command already with update
|
|
60
|
+
self._cmds[self._current].update(delta_time)
|
|
61
|
+
|
|
62
|
+
def finalize(self):
|
|
63
|
+
for cmd in self._cmds:
|
|
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)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from ...types.direction import Direction
|
|
6
|
+
from ..command import Command
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ...objects.dynamic import Dynamic
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CommandSetFacingDirection(Command):
|
|
13
|
+
def __init__(self, dynamic: Dynamic, facing_direction: Direction):
|
|
14
|
+
super().__init__()
|
|
15
|
+
|
|
16
|
+
self._dynamic: Dynamic = dynamic
|
|
17
|
+
self._new_direction: Direction = facing_direction
|
|
18
|
+
|
|
19
|
+
def start(self):
|
|
20
|
+
self._dynamic.facing_direction = self._new_direction
|
|
21
|
+
self.completed = True
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from ...scripts.command import Command
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CommandSetSavePosition(Command):
|
|
5
|
+
def __init__(self, map_name: str, px: float, py: float):
|
|
6
|
+
self._map_name = map_name
|
|
7
|
+
self._px = px
|
|
8
|
+
self._py = py
|
|
9
|
+
|
|
10
|
+
def start(self):
|
|
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
|
|
17
|
+
self.completed = True
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from ...scripts.command import Command
|
|
4
|
+
from ...types.keys import Key as K
|
|
5
|
+
from ...types.player import Player
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CommandShowChoices(Command):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
options: List[str],
|
|
12
|
+
replies: List[Command],
|
|
13
|
+
default_option: int = 0,
|
|
14
|
+
):
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
self.uninterruptible = True
|
|
18
|
+
self._options: List[str] = options
|
|
19
|
+
self._replies: List[Command] = replies
|
|
20
|
+
self._default_option: int = default_option
|
|
21
|
+
|
|
22
|
+
self._cursor_pos: int = 0
|
|
23
|
+
|
|
24
|
+
def update(self, elapsed_time: float):
|
|
25
|
+
if self.engine.keys.new_key_release(K.UP, self.players[0]):
|
|
26
|
+
self._cursor_pos = (self._cursor_pos - 1) % len(self._options)
|
|
27
|
+
elif self.engine.keys.new_key_release(K.DOWN, self.players[0]):
|
|
28
|
+
self._cursor_pos = (self._cursor_pos + 1) % len(self._options)
|
|
29
|
+
elif self.engine.keys.new_key_press(K.A, self.players[0]):
|
|
30
|
+
self.completed = True
|
|
31
|
+
elif self.engine.keys.new_key_release(K.B, self.players[0]):
|
|
32
|
+
self._cursor_pos = self._default_option
|
|
33
|
+
self.completed = True
|
|
34
|
+
|
|
35
|
+
lines = []
|
|
36
|
+
for idx, opt in enumerate(self._options):
|
|
37
|
+
if idx == self._cursor_pos:
|
|
38
|
+
lines.append(f"> {opt}")
|
|
39
|
+
else:
|
|
40
|
+
lines.append(f" {opt}")
|
|
41
|
+
# for p in self.players:
|
|
42
|
+
self.engine.get_view().show_dialog(lines, self.players[0])
|
|
43
|
+
|
|
44
|
+
def finalize(self):
|
|
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
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
4
|
+
|
|
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
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CommandShowDialog(Command):
|
|
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
|
+
):
|
|
22
|
+
super().__init__()
|
|
23
|
+
|
|
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
|
|
39
|
+
|
|
40
|
+
def start(self):
|
|
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
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from ...scripts.command import Command
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ...objects.dynamic import Dynamic
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CommandTakeCoins(Command):
|
|
12
|
+
def __init__(self, amount: int = 0, dynamic: Optional[Dynamic] = None):
|
|
13
|
+
super().__init__()
|
|
14
|
+
|
|
15
|
+
if dynamic is None:
|
|
16
|
+
dynamic = self.engine.player
|
|
17
|
+
|
|
18
|
+
self._amount: int = amount
|
|
19
|
+
self._dynamic: Dynamic = dynamic
|
|
20
|
+
|
|
21
|
+
def start(self):
|
|
22
|
+
self._dynamic.attributes.coins -= self._amount
|
|
23
|
+
self.completed = True
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..types.player import Player
|
|
6
|
+
from .command import Command
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from ..engine import MimaEngine
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ScriptProcessor:
|
|
13
|
+
engine: MimaEngine
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.commands: Dict[Player : List[Command]] = {}
|
|
17
|
+
|
|
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)
|
|
27
|
+
|
|
28
|
+
def process_command(self, elapsed_time: float):
|
|
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)
|
|
44
|
+
else:
|
|
45
|
+
self.commands[p][0].finalize()
|
|
46
|
+
self.commands[p].pop(0)
|
|
47
|
+
self.engine.trigger_script(False, p)
|
|
48
|
+
else:
|
|
49
|
+
self.engine.trigger_script(False, p)
|
|
50
|
+
|
|
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
|
|
58
|
+
return True
|
|
59
|
+
else:
|
|
60
|
+
return False
|
|
61
|
+
return True
|
|
File without changes
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from pygame import Vector2
|
|
4
|
+
from typing_extensions import Generic, Protocol, TypeVar
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CameraTarget(Protocol):
|
|
8
|
+
def get_pos(self) -> Vector2: ...
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
T = TypeVar("T", bound=CameraTarget)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CameraMode(Enum):
|
|
15
|
+
SIMPLE = 0 # No motion, just directly settable
|
|
16
|
+
EDGE_MOVE = 1 # Moves as target crosses boundary
|
|
17
|
+
LAZY_FOLLOW = 2 # Lazily follows the target
|
|
18
|
+
FIXED_SCREENS = 3 # Moves statically between screens
|
|
19
|
+
SLIDE_SCREENS = 4 # Moves statically between screens but with a fast transition
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Camera(Generic[T]):
|
|
23
|
+
def __init__(self, view_size: Vector2, view_pos: Vector2 | None = None) -> None:
|
|
24
|
+
self._view_size = view_size
|
|
25
|
+
self._view_pos = Vector2(0, 0) if view_pos is None else view_pos
|
|
26
|
+
self._mode = CameraMode.SIMPLE
|
|
27
|
+
self._pos = Vector2(0, 0)
|
|
28
|
+
self._edge_trigger_dist = Vector2(1.0, 1.0)
|
|
29
|
+
self._world_boundary_pos = Vector2(0, 0)
|
|
30
|
+
self._world_boundary_size = Vector2(256, 240)
|
|
31
|
+
self._world_boundary_enabled: bool = False
|
|
32
|
+
self._screen_size = Vector2(16, 15)
|
|
33
|
+
|
|
34
|
+
self._lazy_follow_rate: float = 4.0
|
|
35
|
+
self._target: T | None = None
|
|
36
|
+
|
|
37
|
+
def set_mode(self, mode: CameraMode) -> None:
|
|
38
|
+
self._mode = mode
|
|
39
|
+
|
|
40
|
+
def get_mode(self) -> CameraMode:
|
|
41
|
+
return self._mode
|
|
42
|
+
|
|
43
|
+
def get_target(self) -> T | None:
|
|
44
|
+
return self._target
|
|
45
|
+
|
|
46
|
+
def get_pos(self) -> Vector2:
|
|
47
|
+
return self._pos
|
|
48
|
+
|
|
49
|
+
def get_view_pos(self) -> Vector2:
|
|
50
|
+
return self._view_pos
|
|
51
|
+
|
|
52
|
+
def get_view_size(self) -> Vector2:
|
|
53
|
+
return self._view_size
|
|
54
|
+
|
|
55
|
+
def set_view_size(self, size: Vector2) -> None:
|
|
56
|
+
self._view_size = size
|
|
57
|
+
|
|
58
|
+
def set_screen_size(self, size: Vector2):
|
|
59
|
+
self._screen_size = size
|
|
60
|
+
|
|
61
|
+
def get_screen_size(self) -> Vector2:
|
|
62
|
+
return self._screen_size
|
|
63
|
+
|
|
64
|
+
def set_target(self, target: T | None = None) -> None:
|
|
65
|
+
self._target = target
|
|
66
|
+
|
|
67
|
+
def set_world_boundary(self, pos: Vector2, size: Vector2) -> None:
|
|
68
|
+
self._world_boundary_pos = pos
|
|
69
|
+
self._world_boundary_size = size
|
|
70
|
+
|
|
71
|
+
def enable_world_boundary(self, enable: bool = True) -> None:
|
|
72
|
+
self._world_boundary_enabled = enable
|
|
73
|
+
|
|
74
|
+
def is_world_boundary_enabled(self) -> bool:
|
|
75
|
+
return self._world_boundary_enabled
|
|
76
|
+
|
|
77
|
+
def get_world_boundary_pos(self) -> Vector2:
|
|
78
|
+
return self._world_boundary_pos
|
|
79
|
+
|
|
80
|
+
def get_world_boundary_size(self) -> Vector2:
|
|
81
|
+
return self._world_boundary_size
|
|
82
|
+
|
|
83
|
+
def get_lazy_follow_rate(self) -> float:
|
|
84
|
+
return self._lazy_follow_rate
|
|
85
|
+
|
|
86
|
+
def set_lazy_follow_rate(self, rate: float) -> None:
|
|
87
|
+
self._lazy_follow_rate = rate
|
|
88
|
+
|
|
89
|
+
def set_edge_trigger_dist(self, edge: Vector2) -> None:
|
|
90
|
+
self._edge_trigger_dist = edge
|
|
91
|
+
|
|
92
|
+
def get_edge_trigger_dist(self) -> Vector2:
|
|
93
|
+
return self._edge_trigger_dist
|
|
94
|
+
|
|
95
|
+
def update(self, elapsed_time: float) -> bool:
|
|
96
|
+
if self._target is None:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
tpos = self._target.get_pos()
|
|
100
|
+
if self._mode == CameraMode.SIMPLE:
|
|
101
|
+
self._pos = tpos
|
|
102
|
+
elif self._mode == CameraMode.EDGE_MOVE:
|
|
103
|
+
overlap = tpos - self._pos
|
|
104
|
+
if overlap.x > self._edge_trigger_dist.x:
|
|
105
|
+
self._pos.x += overlap.x - self._edge_trigger_dist.x
|
|
106
|
+
if overlap.x < self._edge_trigger_dist.x:
|
|
107
|
+
self._pos.x += overlap.x + self._edge_trigger_dist.x
|
|
108
|
+
if overlap.y > self._edge_trigger_dist.y:
|
|
109
|
+
self._pos.y += overlap.y - self._edge_trigger_dist.y
|
|
110
|
+
if overlap.y < self._edge_trigger_dist.y:
|
|
111
|
+
self._pos.y += overlap.y + self._edge_trigger_dist.y
|
|
112
|
+
elif self._mode == CameraMode.LAZY_FOLLOW:
|
|
113
|
+
self._pos += (tpos - self._pos) * self._lazy_follow_rate * elapsed_time
|
|
114
|
+
elif self._mode == CameraMode.FIXED_SCREENS:
|
|
115
|
+
self._pos = Vector2(
|
|
116
|
+
int(tpos.x // self._screen_size.x) * int(self._screen_size.x),
|
|
117
|
+
int(tpos.y // self._screen_size.y) * int(self._screen_size.y),
|
|
118
|
+
) + (self._view_size * 0.5)
|
|
119
|
+
elif self._mode == CameraMode.SLIDE_SCREENS:
|
|
120
|
+
screen = Vector2(
|
|
121
|
+
int(tpos.x // self._screen_size.x) * int(self._screen_size.x),
|
|
122
|
+
int(tpos.y // self._screen_size.y) * int(self._screen_size.y),
|
|
123
|
+
) + (self._view_size * 0.5)
|
|
124
|
+
self._pos = (
|
|
125
|
+
(screen - self._pos) * self._lazy_follow_rate * 2.0 * elapsed_time
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
self._view_pos = self._pos - (self._view_size * 0.5)
|
|
129
|
+
if self._world_boundary_enabled:
|
|
130
|
+
self._view_pos = vclamp(
|
|
131
|
+
self._view_pos,
|
|
132
|
+
self._world_boundary_pos,
|
|
133
|
+
self._world_boundary_pos + self._world_boundary_size - self._view_size,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
tpos.x >= self._view_pos.x
|
|
138
|
+
and tpos.x < (self._view_pos.x + self._view_size.x)
|
|
139
|
+
and tpos.y >= self._view_pos.y
|
|
140
|
+
and tpos.y < (self._view_pos.y + self._view_size.y)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def vclamp(val: Vector2, low: Vector2, high: Vector2) -> Vector2:
|
|
145
|
+
return Vector2(max(low.x, min(high.x, val.x)), max(low.y, min(high.y, val.y)))
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def vmax(val: Vector2, other: Vector2) -> Vector2:
|
|
149
|
+
return Vector2(max(val.x, other.x), max(val.y, other.y))
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def vmin(val: Vector2, other: Vector2) -> Vector2:
|
|
153
|
+
return Vector2(min(val.x, other.x), min(val.y, other.y))
|