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,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
|