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
@@ -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))