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,126 @@
1
+ import time
2
+
3
+ import pygame
4
+
5
+ from ..types.keys import Key as K
6
+ from ..util.constants import DOUBLE_TAP_SPEED
7
+ from ..util.input_defaults import BUTTONS, DEFAULT_TOUCHSCREEN_MAP
8
+
9
+
10
+ class TouchControlSchemeA:
11
+
12
+ def __init__(self):
13
+
14
+ self._left_finger_tap_pos = 0.0
15
+ self._right_finger_tap_pos = 0.0
16
+
17
+ self._last_left_tap = 0.0
18
+ self._last_right_tap = 0.0
19
+
20
+ def handle_touch(self, event, width=1.0, height=1.0):
21
+ set_keys = []
22
+ unset_keys = []
23
+
24
+ tap_pos = pygame.Vector2(event.x * width, event.y * height)
25
+
26
+ if event.type == pygame.FINGERDOWN:
27
+ tap_time = time.time()
28
+ for key, area in DEFAULT_TOUCHSCREEN_MAP.items():
29
+
30
+ if (
31
+ area[0][0] <= event.x < area[1][0]
32
+ and area[0][1] <= event.y < area[1][1]
33
+ ):
34
+ if key == K.P1_UP:
35
+ self._left_finger_tap_pos = tap_pos
36
+ if tap_time - self._last_left_tap < DOUBLE_TAP_SPEED:
37
+ # print("Left Double Tap")
38
+ set_keys.append(K.P1_SELECT)
39
+ self._last_left_tap = tap_time
40
+ elif key == K.P1_L:
41
+ set_keys.append(K.P1_L)
42
+ else:
43
+ set_keys.append(key)
44
+ self._right_finger_tap_pos = tap_pos
45
+ self._last_right_tap = tap_time
46
+
47
+ if event.type == pygame.FINGERUP:
48
+ # release = time.time()
49
+ # finger_dist = (finger_pos - self._left_finger_tap_pos).length()
50
+
51
+ if event.x < 0.5:
52
+ # print(f"Left Finger Up: {finger_pos}")
53
+ # if (
54
+ # SINGLE_TAP_MIN
55
+ # < release - self._last_left_tap
56
+ # < SINGLE_TAP_MAX
57
+ # ) and finger_dist < 2.5:
58
+ # print("Left Single Tap")
59
+ # # set_keys.append(K.START)
60
+
61
+ unset_keys.append(K.P1_SELECT)
62
+ unset_keys.append(K.P1_RIGHT)
63
+ unset_keys.append(K.P1_LEFT)
64
+ unset_keys.append(K.P1_UP)
65
+ unset_keys.append(K.P1_DOWN)
66
+ unset_keys.append(K.P1_L)
67
+ # print(
68
+ # f"Left Finger moved {finger_dist} "
69
+ # f"({release - self._last_left_tap} s)"
70
+ # )
71
+ else:
72
+ unset_keys.append(K.P1_START)
73
+ unset_keys.append(K.P1_A)
74
+ unset_keys.append(K.P1_B)
75
+ unset_keys.append(K.P1_Y)
76
+ unset_keys.append(K.P1_X)
77
+ unset_keys.append(K.P1_R)
78
+
79
+ if event.type == pygame.FINGERMOTION:
80
+ if event.x < 0.5:
81
+ vd = tap_pos - self._left_finger_tap_pos
82
+ unset_keys.append(K.P1_RIGHT)
83
+ unset_keys.append(K.P1_LEFT)
84
+ unset_keys.append(K.P1_UP)
85
+ unset_keys.append(K.P1_DOWN)
86
+ if abs(vd.x) > 2 * abs(vd.y):
87
+ # Horizontal
88
+ if vd.x > 5.0:
89
+ set_keys.append(K.P1_RIGHT)
90
+ unset_keys.append(K.P1_LEFT)
91
+ unset_keys.append(K.P1_UP)
92
+ unset_keys.append(K.P1_DOWN)
93
+ elif vd.x < -5.0:
94
+ set_keys.append(K.P1_LEFT)
95
+ unset_keys.append(K.P1_RIGHT)
96
+ unset_keys.append(K.P1_UP)
97
+ unset_keys.append(K.P1_DOWN)
98
+ elif abs(vd.x) * 2 < abs(vd.y):
99
+ # Vertical
100
+ if vd.y > 5.0:
101
+ unset_keys.append(K.P1_RIGHT)
102
+ unset_keys.append(K.P1_LEFT)
103
+ unset_keys.append(K.P1_UP)
104
+ set_keys.append(K.P1_DOWN)
105
+ elif vd.y < -5.0:
106
+ unset_keys.append(K.P1_LEFT)
107
+ unset_keys.append(K.P1_RIGHT)
108
+ set_keys.append(K.P1_UP)
109
+ unset_keys.append(K.P1_DOWN)
110
+ elif abs(vd.x) * 1.05 > abs(vd.y) or abs(vd.x) < 1.05 * abs(
111
+ vd.y
112
+ ):
113
+ if vd.x < 0:
114
+ set_keys.append(K.P1_LEFT)
115
+ elif vd.x > 0:
116
+ set_keys.append(K.P1_RIGHT)
117
+ if vd.y < 0:
118
+ set_keys.append(K.P1_UP)
119
+ elif vd.y > 0:
120
+ set_keys.append(K.P1_DOWN)
121
+ self.vd = vd
122
+
123
+ return set_keys, unset_keys
124
+
125
+ def get_touch_state(self):
126
+ return {}
@@ -0,0 +1,132 @@
1
+ import time
2
+
3
+ import pygame
4
+
5
+ from ..types.keys import Key as K
6
+ from ..util.constants import DOUBLE_TAP_SPEED
7
+ from ..util.input_defaults import ALT_TOUCHSCREEN_MAP, BUTTONS
8
+
9
+
10
+ class TouchControlSchemeB:
11
+
12
+ def __init__(self):
13
+
14
+ self.move_start_pos = pygame.Vector2()
15
+ self.move_pos = pygame.Vector2()
16
+ self.move_sy = 0.0
17
+ self.move_px = 0.0
18
+ self.move_py = 0.0
19
+ self._left_finger_tap_pos = 0.0
20
+ self._right_finger_tap_pos = 0.0
21
+
22
+ self._last_left_tap = 0.0
23
+ self._last_right_tap = 0.0
24
+ self._keys_active = {k: False for k in K}
25
+ self._state = {}
26
+ for key, conf in ALT_TOUCHSCREEN_MAP.items():
27
+ px, py = conf["pos"]
28
+ w, h = conf["size"]
29
+ self._state[key] = {
30
+ "rpx": px,
31
+ "rpy": py,
32
+ "rwidth": w,
33
+ "rheight": h,
34
+ "active": False,
35
+ }
36
+
37
+ def handle_touch(self, event, width=1.0, height=1.0):
38
+ set_keys = []
39
+ unset_keys = []
40
+
41
+ tap_pos = pygame.Vector2(event.x * width, event.y * height)
42
+ keys_active = {k: False for k in K}
43
+ # print(f"{event.x:.2f}, {event.y:.2f}", end="")
44
+ for key, conf in ALT_TOUCHSCREEN_MAP.items():
45
+ px, py = conf["pos"]
46
+ w, h = conf["size"]
47
+ area = [[px, py], [px + w, py + h]]
48
+
49
+ if (
50
+ area[0][0] <= event.x < area[1][0]
51
+ and area[0][1] <= event.y < area[1][1]
52
+ ):
53
+ if key == K.P1_UP:
54
+ if event.type == pygame.FINGERDOWN:
55
+ self.move_start_pos = pygame.Vector2(tap_pos)
56
+ self.move_pos = pygame.Vector2(tap_pos)
57
+ self._state[key]["rsx"] = event.x
58
+ self._state[key]["rsy"] = event.y
59
+ self._state[key]["rpx"] = event.x
60
+ self._state[key]["rpy"] = event.y
61
+ elif event.type == pygame.FINGERMOTION:
62
+ self.move_pos = tap_pos
63
+ self._state[key]["rpx"] = event.x
64
+ self._state[key]["rpy"] = event.y
65
+
66
+ vd = self.move_pos - self.move_start_pos
67
+ if abs(vd.x) > 2 * abs(vd.y):
68
+ # Horizontal
69
+ if vd.x > 5.0:
70
+ keys_active[K.P1_RIGHT] = True
71
+ # print("..>", end="")
72
+ elif vd.x < -5.0:
73
+ keys_active[K.P1_LEFT] = True
74
+ # print("..<", end="")
75
+ elif abs(vd.x) * 2 < abs(vd.y):
76
+ # Vertical
77
+ if vd.y > 5.0:
78
+ keys_active[K.P1_DOWN] = True
79
+ # print("..v", end="")
80
+ elif vd.y < -5.0:
81
+ keys_active[K.P1_UP] = True
82
+ # print("..^", end="")
83
+ elif abs(vd.x) * 1.05 > abs(vd.y) or abs(
84
+ vd.x
85
+ ) < 1.05 * abs(vd.y):
86
+ # Diagonal
87
+ if vd.x < 0:
88
+ keys_active[K.P1_LEFT] = True
89
+ # print("..<", end="")
90
+ elif vd.x > 0:
91
+ keys_active[K.P1_RIGHT] = True
92
+ # print("..>", end="")
93
+ if vd.y < 0:
94
+ keys_active[K.P1_UP] = True
95
+ # print("..^", end="")
96
+ elif vd.y > 0:
97
+ keys_active[K.P1_DOWN] = True
98
+ # print("..v", end="")
99
+ # elif event.type == pygame.FINGERUP:
100
+ # unset_keys.append(K.P1_RIGHT)
101
+ # unset_keys.append(K.P1_LEFT)
102
+ # unset_keys.append(K.P1_UP)
103
+ # unset_keys.append(K.P1_DOWN)
104
+ else:
105
+ if event.type == pygame.FINGERDOWN:
106
+ keys_active[key] = True
107
+ # print(f"..{key.name}", end="")
108
+ if event.type == pygame.FINGERMOTION:
109
+ keys_active[key] = True
110
+ # print(f"..{key.name}", end="")
111
+ # print()
112
+ for k, val in keys_active.items():
113
+ if val:
114
+ set_keys.append(k)
115
+ # self._state.setdefault(k, {})["active"] = True
116
+ if k in self._state:
117
+ self._state[k]["active"] = True
118
+ if k in [K.P1_LEFT, K.P1_RIGHT, K.P1_DOWN]:
119
+ self._state[K.P1_UP]["active"] = True
120
+ else:
121
+ unset_keys.append(k)
122
+ if k in self._state:
123
+ self._state[k]["active"] = False
124
+ return set_keys, unset_keys
125
+
126
+ def get_touch_state(self):
127
+ state = {}
128
+ for key, conf in self._state.items():
129
+ nkey = K(key.value - 12)
130
+ state[nkey] = conf
131
+
132
+ return state
mima/core/__init__.py ADDED
File without changes
mima/core/collision.py ADDED
@@ -0,0 +1,325 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+ from itertools import product
5
+ from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Tuple
6
+
7
+ from ..maps.tilemap import Tilemap
8
+ from ..types.direction import Direction
9
+ from ..types.graphic_state import GraphicState, Until
10
+ from ..types.nature import Nature
11
+ from ..types.object import ObjectType
12
+ from ..types.tile_collision import TileCollision
13
+
14
+ if TYPE_CHECKING:
15
+ # from .engine import MimaEngine
16
+ from ..objects.dynamic import Dynamic
17
+ from ..states.quest import Quest
18
+
19
+ # from .view.view import View
20
+
21
+
22
+ def check_object_to_map_collision(
23
+ elapsed_time: float,
24
+ obj: Dynamic,
25
+ tilemap: Tilemap,
26
+ new_px: float,
27
+ new_py: float,
28
+ *,
29
+ layer: int = 0,
30
+ collision: TileCollision = TileCollision.TOP,
31
+ ) -> Tuple[float, float]:
32
+ left = new_px + obj.hitbox_px
33
+ right = left + obj.hitbox_width
34
+ top = obj.py + obj.hitbox_py
35
+ bottom = top + obj.hitbox_height
36
+
37
+ collided_with_map = False
38
+ if obj.solid_vs_map:
39
+ if collision_with_map(
40
+ tilemap, left, right, top, bottom, layer, collision
41
+ ):
42
+ # On rare occasions, the object might be pushed towards
43
+ # the wall, i.e. old and new pos are equal
44
+ # Decide depending on the decimal part of the position
45
+ # where to push the object
46
+ if new_px == obj.px:
47
+ decimal_dif = new_px - int(new_px)
48
+ if abs(decimal_dif) > 0.5:
49
+ new_px += 0.0001
50
+ else:
51
+ new_px -= 0.0001
52
+
53
+ # Did the object move from right to left?
54
+ if new_px < obj.px:
55
+ new_px += int(left) + 1.0 - left
56
+ else:
57
+ new_px -= right - int(right) + 0.001
58
+
59
+ obj.vx = 0
60
+ collided_with_map = True
61
+ if (
62
+ obj.facing_direction in [Direction.WEST, Direction.EAST]
63
+ and obj.can_push
64
+ ):
65
+ obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
66
+
67
+ left = new_px + obj.hitbox_px
68
+ right = left + obj.hitbox_width
69
+ top = new_py + obj.hitbox_py
70
+ bottom = top + obj.hitbox_height
71
+
72
+ if collision_with_map(
73
+ tilemap, left, right, top, bottom, layer, collision
74
+ ):
75
+ # See comment above
76
+ if new_py == obj.py:
77
+ decimal_dif = new_py - int(new_py)
78
+ if abs(decimal_dif) > 0.5:
79
+ new_py += 0.0001
80
+ else:
81
+ new_py -= 0.0001
82
+
83
+ if new_py < obj.py:
84
+ new_py += int(top) + 1.0 - top
85
+ else:
86
+ new_py -= bottom - int(bottom) + 0.001
87
+
88
+ obj.vy = 0
89
+ collided_with_map = True
90
+ if (
91
+ obj.facing_direction in [Direction.NORTH, Direction.SOUTH]
92
+ and obj.can_push
93
+ ):
94
+ obj.lock_graphic_state(GraphicState.PUSHING, Until.NEXT_UPDATE)
95
+
96
+ if obj.type == ObjectType.PROJECTILE and collided_with_map:
97
+ obj.kill()
98
+
99
+ return new_px, new_py
100
+
101
+
102
+ def collision_with_map(
103
+ tilemap: Tilemap,
104
+ left: float,
105
+ right: float,
106
+ top: float,
107
+ bottom: float,
108
+ layer: int = 0,
109
+ collision: TileCollision = TileCollision.TOP,
110
+ ) -> bool:
111
+ if tilemap.is_solid(left, top, layer, collision):
112
+ return True
113
+ if tilemap.is_solid(left, bottom, layer, collision):
114
+ return True
115
+ if tilemap.is_solid(right, top, layer, collision):
116
+ return True
117
+ if tilemap.is_solid(right, bottom, layer, collision):
118
+ return True
119
+
120
+ return False
121
+
122
+
123
+ def check_object_to_object_collision(
124
+ obj: Dynamic,
125
+ new_px: float,
126
+ new_py: float,
127
+ other: Dynamic,
128
+ deal_damage: Optional[Callable[Dynamic, Dynamic]] = None,
129
+ quests: Optional[List[Quest]] = None,
130
+ ) -> Tuple[float, float]:
131
+ quests = quests if quests is not None else []
132
+ deal_damage = deal_damage if deal_damage is not None else lambda x, y: None
133
+
134
+ pxys = {}
135
+ pxys["left1"] = new_px + obj.hitbox_px
136
+ pxys["right1"] = pxys["left1"] + obj.hitbox_width
137
+ pxys["top1"] = obj.py + obj.hitbox_py
138
+ pxys["bottom1"] = pxys["top1"] + obj.hitbox_height
139
+
140
+ pxys["left2"] = other.px + other.hitbox_px
141
+ pxys["right2"] = pxys["left2"] + other.hitbox_width
142
+ pxys["top2"] = other.py + other.hitbox_py
143
+ pxys["bottom2"] = pxys["top2"] + other.hitbox_height
144
+
145
+ if obj.solid_vs_dyn and other.solid_vs_dyn: # and obj.moves_on_collision:
146
+ new_px, new_py = _check_solid_objects(obj, other, new_px, new_py, pxys)
147
+
148
+ else:
149
+ if obj.type == ObjectType.PLAYER:
150
+ _check_player_with_non_solid(obj, other, pxys, quests)
151
+
152
+ else:
153
+ _check_non_solid_objects(obj, other, pxys, deal_damage)
154
+
155
+ return new_px, new_py
156
+
157
+
158
+ def _check_solid_objects(
159
+ obj: Dynamic,
160
+ other: Dynamic,
161
+ new_px: float,
162
+ new_py: float,
163
+ pxys: Dict[str, float],
164
+ ) -> None:
165
+ collided_with_dyn = False
166
+ if collision_with_dyn(**pxys):
167
+ collided_with_dyn = True
168
+ if pxys["left1"] < pxys["left2"]:
169
+ new_px -= pxys["right1"] - pxys["left2"] + 0.001
170
+ else:
171
+ new_px += pxys["right2"] - pxys["left1"] + 0.001
172
+
173
+ pxys["left1"] = new_px + obj.hitbox_px
174
+ pxys["right1"] = pxys["left1"] + obj.hitbox_width
175
+ pxys["top1"] = new_py + obj.hitbox_py
176
+ pxys["bottom1"] = pxys["top1"] + obj.hitbox_height
177
+
178
+ if collision_with_dyn(**pxys):
179
+ collided_with_dyn = True
180
+ if pxys["top1"] < pxys["top2"]:
181
+ new_py -= pxys["bottom1"] - pxys["top2"] + 0.001
182
+ else:
183
+ new_py += pxys["bottom2"] - pxys["top1"] + 0.001
184
+
185
+ if collided_with_dyn:
186
+ # print(f"Collision: {obj.get_player()} -> {other.dyn_id}")
187
+ other.on_interaction(obj, Nature.WALK)
188
+
189
+ return new_px, new_py
190
+
191
+
192
+ def _check_player_with_non_solid(
193
+ obj: Dynamic, other: Dynamic, pxys: Dict[str, float], quests: List[Quest]
194
+ ):
195
+ if collision_with_dyn(**pxys):
196
+ for quest in quests:
197
+ if quest.on_interaction(other, Nature.WALK, obj.get_player()):
198
+ break
199
+ obj.tilemap.on_interaction(other, Nature.WALK)
200
+ other.on_interaction(obj, Nature.WALK)
201
+
202
+
203
+ def _check_non_solid_objects(
204
+ obj: Dynamic,
205
+ other: Dynamic,
206
+ pxys: Dict[str, float],
207
+ deal_damage: Callable[Dynamic, Dynamic],
208
+ ):
209
+ if collision_with_dyn(**pxys):
210
+ if obj.type == ObjectType.PROJECTILE:
211
+ if other.alignment != obj.alignment:
212
+ # We know object is a projectile
213
+ if other.attackable and not other.invincible:
214
+ deal_damage(obj, other)
215
+ else:
216
+ other.on_interaction(obj, Nature.WALK)
217
+ else:
218
+ other.on_interaction(obj, Nature.WALK)
219
+
220
+
221
+ def collision_with_dyn(
222
+ left1: float,
223
+ right1: float,
224
+ top1: float,
225
+ bottom1: float,
226
+ left2: float,
227
+ right2: float,
228
+ top2: float,
229
+ bottom2: float,
230
+ ) -> bool:
231
+ if left1 < right2 and right1 > left2 and top1 < bottom2 and bottom1 > top2:
232
+ return True
233
+
234
+ return False
235
+
236
+
237
+ def add_to_collision_chunk(
238
+ collision_chunks: Dict[int, List[Dynamic]],
239
+ obj: Dynamic,
240
+ chunk_size: int,
241
+ chunks_per_row: int,
242
+ ) -> List[int]:
243
+ chunk_ids = []
244
+ # chidx = _chunk_index(obj.px, obj.py, chunk_size, chunks_per_row)
245
+ # collision_chunks.setdefault(chidx, [])
246
+
247
+ # if obj not in collision_chunks[chidx]:
248
+ # if obj.get_player().value > 0:
249
+ # collision_chunks[chidx].insert(0, obj)
250
+ # else:
251
+ # collision_chunks[chidx].append(obj)
252
+ centerx = obj.px + (obj.hitbox_px + obj.hitbox_width) / 2
253
+ centery = obj.py + (obj.hitbox_py + obj.hitbox_height) / 2
254
+ # chid = _test_chunk_position(
255
+ # collision_chunks, obj, centerx, centery, chunk_size, chunks_per_row
256
+ # )
257
+ offsets = [[0, 0]] + [
258
+ list(p) for p in product([-1, 0, 1], repeat=2) if p != (0, 0)
259
+ ]
260
+ for x, y in offsets:
261
+ chunk_ids.append(
262
+ _test_chunk_position(
263
+ collision_chunks,
264
+ obj,
265
+ centerx + x,
266
+ centery + y,
267
+ chunk_size,
268
+ chunks_per_row,
269
+ )
270
+ )
271
+ chunk_ids = list(dict.fromkeys(chunk_ids))
272
+ # chid_right = _test_chunk_position(
273
+ # collision_chunks, obj, obj.px + 1, obj.py, chunk_size, chunks_per_row
274
+ # )
275
+
276
+ # chid_bottom = _test_chunk_position(
277
+ # collision_chunks, obj, obj.px, obj.py + 1, chunk_size, chunks_per_row
278
+ # )
279
+
280
+ # chunk_ids.append(chid)
281
+ # if chid != chid_right:
282
+ # chunk_ids.append(chid_right)
283
+ # if chid != chid_bottom:
284
+ # chunk_ids.append(chid_bottom)
285
+ # if chid != chid_right and chid != chid_bottom:
286
+ # chunk_ids.append(
287
+ # _test_chunk_position(
288
+ # collision_chunks,
289
+ # obj,
290
+ # obj.px + 1,
291
+ # obj.py + 1,
292
+ # chunk_size,
293
+ # chunks_per_row,
294
+ # )
295
+ # )
296
+
297
+ for chid in obj.chunks:
298
+ if (
299
+ chid not in chunk_ids
300
+ and chid in collision_chunks
301
+ and obj in collision_chunks[chid]
302
+ ):
303
+ collision_chunks[chid].remove(obj)
304
+ return chunk_ids
305
+
306
+
307
+ def _test_chunk_position(
308
+ collision_chunks, obj, px, py, chunk_size, chunks_per_row
309
+ ):
310
+ chidx = _chunk_index(px, py, chunk_size, chunks_per_row)
311
+ collision_chunks.setdefault(chidx, [])
312
+
313
+ if obj not in collision_chunks[chidx]:
314
+ if obj.get_player().value > 0:
315
+ collision_chunks[chidx].insert(0, obj)
316
+ else:
317
+ collision_chunks[chidx].append(obj)
318
+ return chidx
319
+
320
+
321
+ def _chunk_index(px, py, chunk_size, chunks_per_row):
322
+ hc = chunk_size // 2
323
+ return math.floor((px + hc) / chunk_size) + chunks_per_row * math.floor(
324
+ (py + hc) / chunk_size
325
+ )
mima/core/database.py ADDED
@@ -0,0 +1,58 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from typing import TYPE_CHECKING, Any, Dict
5
+
6
+ from ..objects.animated_sprite import AnimatedSprite
7
+ from ..types.damage import Damage
8
+
9
+ if TYPE_CHECKING:
10
+ from .engine import MimaEngine
11
+
12
+ LOG = logging.getLogger(__name__)
13
+
14
+
15
+ class Database:
16
+ engine: MimaEngine
17
+
18
+ def __init__(self):
19
+ self._cache = {}
20
+ self._known_locale = ""
21
+ self._active_sprites = []
22
+
23
+ def get_usable_data(self, usable_id) -> Dict[str, Any]:
24
+ if usable_id in self._cache:
25
+ return self._cache[usable_id]
26
+
27
+ data = {}
28
+ tpl = self.engine.assets.get_template(usable_id)
29
+
30
+ data["usable_id"] = tpl.get_string("usable_id")
31
+ data["name"] = tpl.get_string(f"display_name_{self.engine.rtc.locale}")
32
+ data["description"] = tpl.get_string(f"description_{self.engine.rtc.locale}")
33
+ data["sprite_name"] = tpl.get_string("sprite_name")
34
+ data["dtype"] = Damage[tpl.get_string("dtype", "body").upper()]
35
+ for pid, prop in tpl.properties.items():
36
+ if not pid.startswith("attr_"):
37
+ continue
38
+ _, attr = pid.split("_", 1)
39
+ data[attr] = tpl.get(pid)
40
+ # data["attr_price"] = tpl.get_int("attr_price")
41
+ # data["attr_swing_timer"] = tpl.get_float("attr_swing_timer")
42
+ # data["attr_damage"] = tpl.get_int("attr_damage")
43
+ # data["attr_health_cost"] = tpl.get_int("attr_health_cost")
44
+ # data["attr_magic_cost"] = tpl.get_int("attr_magic_cost")
45
+ # data["attr_stamina_cost"] = tpl.get_int("attr_stamina_cost")
46
+ # data["attr_bomb_cost"] = tpl.get_int("attr_bomb_cost")
47
+ # data["attr_arrow_cost"] = tpl.get_int("attr_arrow_cost")
48
+
49
+ self._cache[usable_id] = data
50
+ return data
51
+
52
+ def get_object_data(self, object_id) -> Dict[str, Any]:
53
+ return {}
54
+
55
+ def get_sprite(self, sprite_id):
56
+ sprite = AnimatedSprite(sprite_id)
57
+ self._active_sprites.append(sprite)
58
+ return sprite