mima-engine 0.1.4__py3-none-any.whl → 0.2.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.
Potentially problematic release.
This version of mima-engine might be problematic. Click here for more details.
- mima/__init__.py +1 -1
- mima/backend/pygame_assets.py +15 -9
- mima/backend/pygame_audio.py +5 -2
- mima/backend/pygame_backend.py +255 -57
- mima/backend/pygame_camera.py +63 -0
- mima/backend/pygame_events.py +331 -85
- mima/collision.py +182 -111
- mima/engine.py +155 -15
- mima/maps/tiled/tiled_map.py +3 -3
- mima/maps/tiled/tiled_tileset.py +1 -0
- mima/maps/tilemap.py +78 -15
- mima/maps/tileset.py +8 -2
- mima/maps/transition_map.py +6 -8
- mima/mode_engine.py +80 -0
- mima/objects/animated_sprite.py +23 -15
- mima/objects/attributes.py +3 -0
- mima/objects/creature.py +64 -25
- mima/objects/dynamic.py +30 -8
- mima/objects/effects/colorize_screen.py +22 -6
- mima/objects/effects/debug_box.py +124 -0
- mima/objects/effects/light.py +21 -30
- mima/objects/effects/show_sprite.py +39 -0
- mima/objects/effects/walking_on_grass.py +25 -7
- mima/objects/effects/walking_on_water.py +17 -6
- mima/objects/loader.py +24 -13
- mima/objects/projectile.py +21 -6
- mima/objects/sprite.py +7 -8
- mima/objects/world/color_gate.py +5 -2
- mima/objects/world/color_switch.py +12 -6
- mima/objects/world/container.py +17 -8
- mima/objects/world/floor_switch.py +8 -4
- mima/objects/world/gate.py +8 -5
- mima/objects/world/light_source.py +11 -9
- mima/objects/world/logic_gate.py +8 -7
- mima/objects/world/movable.py +72 -28
- mima/objects/world/oneway.py +14 -9
- mima/objects/world/pickup.py +10 -5
- mima/objects/world/switch.py +28 -25
- mima/objects/world/teleport.py +76 -55
- mima/scene_engine.py +19 -20
- mima/scripts/command.py +16 -2
- mima/scripts/commands/change_map.py +23 -4
- mima/scripts/commands/equip_weapon.py +23 -0
- mima/scripts/commands/give_item.py +5 -3
- mima/scripts/commands/move_map.py +9 -9
- mima/scripts/commands/parallel.py +16 -3
- mima/scripts/commands/present_item.py +7 -5
- mima/scripts/commands/screen_fade.py +30 -12
- mima/scripts/commands/serial.py +30 -7
- mima/scripts/commands/set_spawn_map.py +6 -3
- mima/scripts/commands/show_choices.py +16 -7
- mima/scripts/commands/show_dialog.py +110 -3
- mima/scripts/script_processor.py +41 -20
- mima/states/game_state.py +2 -0
- mima/states/memory.py +28 -0
- mima/states/quest.py +2 -3
- mima/types/keys.py +48 -0
- mima/types/mode.py +4 -10
- mima/types/player.py +9 -0
- mima/types/position.py +13 -0
- mima/types/tile_collision.py +11 -0
- mima/types/window.py +44 -0
- mima/usables/item.py +1 -0
- mima/util/colors.py +5 -0
- mima/util/constants.py +6 -0
- mima/util/functions.py +27 -0
- mima/util/input_defaults.py +109 -0
- mima/util/runtime_config.py +234 -30
- mima/util/trading_item.py +20 -0
- mima/view/camera.py +160 -19
- mima/view/mima_mode.py +612 -0
- mima/view/mima_scene.py +225 -0
- mima/view/mima_view.py +12 -0
- mima/view/mima_window.py +153 -0
- {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/METADATA +4 -2
- mima_engine-0.2.0.dist-info/RECORD +128 -0
- {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/WHEEL +1 -1
- mima/view/scene.py +0 -322
- mima_engine-0.1.4.dist-info/RECORD +0 -114
- {mima_engine-0.1.4.dist-info → mima_engine-0.2.0.dist-info}/top_level.txt +0 -0
mima/maps/tilemap.py
CHANGED
|
@@ -4,11 +4,11 @@ import logging
|
|
|
4
4
|
import math
|
|
5
5
|
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
6
6
|
|
|
7
|
-
from ..
|
|
7
|
+
from ..types.tile_collision import TileCollision
|
|
8
|
+
from .template import Template
|
|
8
9
|
from .tile_info import TileInfo
|
|
9
10
|
from .tile_layer import TileLayer
|
|
10
11
|
from .tileset_info import TilesetInfo
|
|
11
|
-
from .template import Template
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
14
|
from ..objects.dynamic import Dynamic
|
|
@@ -20,17 +20,17 @@ LOG = logging.getLogger(__name__)
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class Tilemap(Template):
|
|
23
|
-
|
|
24
23
|
def __init__(self, name: str):
|
|
25
24
|
super().__init__(name)
|
|
26
25
|
self.width: int = 0
|
|
27
26
|
self.height: int = 0
|
|
28
|
-
self.tile_width =
|
|
29
|
-
self.tile_height =
|
|
27
|
+
self.tile_width = self.engine.rtc.tile_width
|
|
28
|
+
self.tile_height = self.engine.rtc.tile_height
|
|
30
29
|
|
|
31
30
|
self._layers: List[TileLayer] = []
|
|
32
31
|
self._tilesets: List[TilesetInfo] = []
|
|
33
32
|
self._cache: Dict[int, TileInfo] = {}
|
|
33
|
+
self._objects: List[Template] = []
|
|
34
34
|
|
|
35
35
|
def populate_dynamics(self, dynamics: List[Dynamic]) -> bool:
|
|
36
36
|
"""Load all map-related objects into the game."""
|
|
@@ -54,6 +54,7 @@ class Tilemap(Template):
|
|
|
54
54
|
visible_tiles_ex: int,
|
|
55
55
|
visible_tiles_ey: int,
|
|
56
56
|
layer_pos: int = 0,
|
|
57
|
+
camera_name: str = "display",
|
|
57
58
|
):
|
|
58
59
|
# Get offsets for smooth movement
|
|
59
60
|
tile_ox = (ox - math.floor(ox)) * self.tile_width
|
|
@@ -79,13 +80,12 @@ class Tilemap(Template):
|
|
|
79
80
|
layer_visible_tiles_sy -= 1
|
|
80
81
|
|
|
81
82
|
# Draw visible tiles of the map
|
|
82
|
-
for x in range(layer_visible_tiles_sx, int(visible_tiles_ex) +
|
|
83
|
+
for x in range(layer_visible_tiles_sx, int(visible_tiles_ex) + 2):
|
|
83
84
|
for y in range(
|
|
84
|
-
layer_visible_tiles_sy, int(visible_tiles_ey) +
|
|
85
|
+
layer_visible_tiles_sy, int(visible_tiles_ey) + 2
|
|
85
86
|
):
|
|
86
87
|
tile_index = layer.get_index(
|
|
87
|
-
int(x + math.floor(ox)),
|
|
88
|
-
int(y + math.floor(oy)),
|
|
88
|
+
int(x + math.floor(ox)), int(y + math.floor(oy))
|
|
89
89
|
)
|
|
90
90
|
if tile_index <= 0:
|
|
91
91
|
# Zero means the tile was not set in Tiled
|
|
@@ -108,6 +108,7 @@ class Tilemap(Template):
|
|
|
108
108
|
sy * self.tile_height,
|
|
109
109
|
self.tile_width,
|
|
110
110
|
self.tile_height,
|
|
111
|
+
camera_name,
|
|
111
112
|
)
|
|
112
113
|
return True
|
|
113
114
|
|
|
@@ -115,17 +116,56 @@ class Tilemap(Template):
|
|
|
115
116
|
return False
|
|
116
117
|
|
|
117
118
|
def is_solid(
|
|
118
|
-
self,
|
|
119
|
+
self,
|
|
120
|
+
px: int,
|
|
121
|
+
py: int,
|
|
122
|
+
layer_pos: Optional[int] = None,
|
|
123
|
+
collision: TileCollision = TileCollision.TOP,
|
|
119
124
|
) -> bool:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
if collision in (TileCollision.TOP, TileCollision.BOTTOM):
|
|
126
|
+
tile = self.get_tile(
|
|
127
|
+
px,
|
|
128
|
+
py,
|
|
129
|
+
layer_pos,
|
|
130
|
+
ascending=(collision == TileCollision.BOTTOM),
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if tile is not None and tile.solid:
|
|
134
|
+
return True
|
|
135
|
+
return False
|
|
136
|
+
|
|
137
|
+
tiles = self.get_tiles(px, py, layer_pos)
|
|
138
|
+
if collision in (TileCollision.TOP_2, TileCollision.TOP_3):
|
|
139
|
+
tiles = tiles[::-1]
|
|
140
|
+
|
|
141
|
+
for idx, tile in enumerate(self.get_tiles(px, py, layer_pos)):
|
|
142
|
+
if tile.solid:
|
|
143
|
+
return True
|
|
144
|
+
if idx == 1 and collision in (
|
|
145
|
+
TileCollision.TOP_2,
|
|
146
|
+
TileCollision.BOTTOM_2,
|
|
147
|
+
):
|
|
148
|
+
break
|
|
149
|
+
elif idx == 2 and collision in (
|
|
150
|
+
TileCollision.TOP_3,
|
|
151
|
+
TileCollision.BOTTOM_3,
|
|
152
|
+
):
|
|
153
|
+
break
|
|
123
154
|
return False
|
|
124
155
|
|
|
125
156
|
def get_tile(
|
|
126
|
-
self,
|
|
157
|
+
self,
|
|
158
|
+
px: int,
|
|
159
|
+
py: int,
|
|
160
|
+
layer_pos: Optional[int] = None,
|
|
161
|
+
ascending=False,
|
|
127
162
|
) -> Optional[Tile]:
|
|
128
|
-
|
|
163
|
+
if ascending:
|
|
164
|
+
layers = self._layers
|
|
165
|
+
else:
|
|
166
|
+
layers = self._layers[::-1]
|
|
167
|
+
|
|
168
|
+
for layer in layers:
|
|
129
169
|
if layer_pos is not None and layer_pos != layer.layer_pos:
|
|
130
170
|
continue
|
|
131
171
|
tile_index = layer.get_index(math.floor(px), math.floor(py))
|
|
@@ -139,6 +179,21 @@ class Tilemap(Template):
|
|
|
139
179
|
|
|
140
180
|
return None
|
|
141
181
|
|
|
182
|
+
def get_tiles(self, px: int, py: int, layer_pos: Optional[int] = None):
|
|
183
|
+
tiles = []
|
|
184
|
+
for layer in self._layers:
|
|
185
|
+
if layer_pos is not None and layer_pos != layer.layer_pos:
|
|
186
|
+
continue
|
|
187
|
+
tile_index = layer.get_index(math.floor(px), math.floor(py))
|
|
188
|
+
if tile_index not in self._cache:
|
|
189
|
+
if not self._load_to_cache(tile_index):
|
|
190
|
+
continue
|
|
191
|
+
info = self._cache[tile_index]
|
|
192
|
+
if info.tile is not None:
|
|
193
|
+
tiles.append(info.tile)
|
|
194
|
+
|
|
195
|
+
return tiles
|
|
196
|
+
|
|
142
197
|
def _load_to_cache(self, tile_index: int) -> bool:
|
|
143
198
|
tileset = None
|
|
144
199
|
firstgid = 0
|
|
@@ -157,3 +212,11 @@ class Tilemap(Template):
|
|
|
157
212
|
|
|
158
213
|
self._cache[tile_index] = TileInfo(tileset=tileset, tile=tile)
|
|
159
214
|
return True
|
|
215
|
+
|
|
216
|
+
def trigger_new_frame(self):
|
|
217
|
+
for tsinfo in self._tilesets:
|
|
218
|
+
tsinfo.tileset.trigger_new_frame()
|
|
219
|
+
|
|
220
|
+
@property
|
|
221
|
+
def objects(self) -> List[Template]:
|
|
222
|
+
return []
|
mima/maps/tileset.py
CHANGED
|
@@ -17,10 +17,13 @@ class Tileset:
|
|
|
17
17
|
|
|
18
18
|
self.tiles: List[Tile] = []
|
|
19
19
|
self.animated_tiles: List[Tile] = []
|
|
20
|
+
self._is_new_frame: bool = False
|
|
20
21
|
|
|
21
22
|
def update(self, elapsed_time: float) -> bool:
|
|
22
|
-
|
|
23
|
-
tile.
|
|
23
|
+
if self._is_new_frame:
|
|
24
|
+
for tile in self.animated_tiles:
|
|
25
|
+
tile.update(elapsed_time)
|
|
26
|
+
self._is_new_frame = False
|
|
24
27
|
|
|
25
28
|
return True
|
|
26
29
|
|
|
@@ -30,3 +33,6 @@ class Tileset:
|
|
|
30
33
|
return tile
|
|
31
34
|
|
|
32
35
|
return self.tiles[0]
|
|
36
|
+
|
|
37
|
+
def trigger_new_frame(self):
|
|
38
|
+
self._is_new_frame = True
|
mima/maps/transition_map.py
CHANGED
|
@@ -5,8 +5,6 @@ from typing import TYPE_CHECKING, Optional
|
|
|
5
5
|
from ..util.constants import (
|
|
6
6
|
MAP_TRANSITION_DURATION_FACTOR,
|
|
7
7
|
MOVE_MAP_DURATION,
|
|
8
|
-
TILE_HEIGHT,
|
|
9
|
-
TILE_WIDTH,
|
|
10
8
|
UI_HIGHT,
|
|
11
9
|
)
|
|
12
10
|
from .tilemap import Tilemap
|
|
@@ -35,18 +33,18 @@ class TransitionMap(Tilemap):
|
|
|
35
33
|
|
|
36
34
|
self.width: int = self.src_map.width
|
|
37
35
|
self.height: int = self.src_map.height
|
|
38
|
-
self.tiles_nx =
|
|
39
|
-
|
|
36
|
+
self.tiles_nx = (
|
|
37
|
+
self.engine.backend.render_width / self.engine.rtc.tile_width
|
|
38
|
+
)
|
|
39
|
+
self.tiles_ny = (
|
|
40
|
+
self.engine.backend.render_height / self.engine.rtc.tile_height
|
|
41
|
+
)
|
|
40
42
|
|
|
41
43
|
if self.vx != 0:
|
|
42
44
|
self.step_size = self.duration / (self.tiles_nx * 0.75)
|
|
43
45
|
elif self.vy != 0:
|
|
44
46
|
self.step_size = self.duration / ((self.tiles_ny - 1) * 0.75)
|
|
45
47
|
|
|
46
|
-
# print(
|
|
47
|
-
# f"Step Size={self.step_size}, Duration={self.duration}, Size={WIDTH, HEIGHT}, TileSize={TILE_WIDTH, TILE_HEIGHT} "
|
|
48
|
-
# )
|
|
49
|
-
|
|
50
48
|
def update(self, elapsed_time: float) -> bool:
|
|
51
49
|
self.progress = self.time_so_far / self.step_size
|
|
52
50
|
self.time_so_far += elapsed_time
|
mima/mode_engine.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from .engine import MimaEngine
|
|
5
|
+
from .states.game_state import GameState
|
|
6
|
+
from .types.keys import Key as K
|
|
7
|
+
from .types.mode import Mode
|
|
8
|
+
from .types.nature import Nature
|
|
9
|
+
from .types.player import Player
|
|
10
|
+
from .view.mima_mode import MimaMode
|
|
11
|
+
|
|
12
|
+
# from .view.mima_scene import MimaScene
|
|
13
|
+
LOG = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MimaModeEngine(MimaEngine):
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
init_file: str,
|
|
20
|
+
config_path: str,
|
|
21
|
+
default_config: Dict[str, Any],
|
|
22
|
+
platform="PC",
|
|
23
|
+
caption: str = "MimaEngine",
|
|
24
|
+
):
|
|
25
|
+
super().__init__(
|
|
26
|
+
init_file, config_path, default_config, platform, caption
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
self.modes: Dict[Mode, MimaMode] = {}
|
|
30
|
+
self.mode: Optional[MimaMode] = None
|
|
31
|
+
|
|
32
|
+
self.mode_stack: List[Mode] = []
|
|
33
|
+
|
|
34
|
+
self.draw_chunks: bool = False
|
|
35
|
+
self.draw_chunk_info: bool = False
|
|
36
|
+
self.disable_filter: bool = False
|
|
37
|
+
self._timer = 1.0
|
|
38
|
+
|
|
39
|
+
def on_user_create(self):
|
|
40
|
+
# change_mode(Mode.LOADING)
|
|
41
|
+
# TODO add example modes
|
|
42
|
+
return True
|
|
43
|
+
|
|
44
|
+
def on_user_update(self, elapsed_time: float):
|
|
45
|
+
self.audio.update(elapsed_time)
|
|
46
|
+
|
|
47
|
+
# print("Update start")
|
|
48
|
+
|
|
49
|
+
if not self.mode.update(elapsed_time):
|
|
50
|
+
LOG.critical(f"Update of mode {self.mode} failed.")
|
|
51
|
+
return False
|
|
52
|
+
self._timer -= elapsed_time
|
|
53
|
+
if self._timer <= 0.0:
|
|
54
|
+
self._timer += 1.0
|
|
55
|
+
LOG.debug(
|
|
56
|
+
f"Updated {self.mode} (Current Stack:"
|
|
57
|
+
f"{[m.name for m in self.mode_stack]})"
|
|
58
|
+
)
|
|
59
|
+
# print("Update end")
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def change_mode(self, mode: Mode):
|
|
63
|
+
if mode in self.mode_stack:
|
|
64
|
+
self.mode_stack.remove(mode)
|
|
65
|
+
self.mode_stack.append(mode)
|
|
66
|
+
if self.mode is not None:
|
|
67
|
+
self.mode.unload()
|
|
68
|
+
self.mode = self.modes[mode]
|
|
69
|
+
self.mode.load()
|
|
70
|
+
|
|
71
|
+
def return_mode(self):
|
|
72
|
+
LOG.debug(
|
|
73
|
+
"Returning to previous mode. Stack: %s", str(self.mode_stack)
|
|
74
|
+
)
|
|
75
|
+
self.mode_stack.pop()
|
|
76
|
+
self.mode = self.modes[self.mode_stack[-1]]
|
|
77
|
+
self.mode.load()
|
|
78
|
+
|
|
79
|
+
def get_view(self):
|
|
80
|
+
return self.mode
|
mima/objects/animated_sprite.py
CHANGED
|
@@ -4,7 +4,6 @@ from typing import Any, Dict, Optional
|
|
|
4
4
|
|
|
5
5
|
from ..types.direction import Direction
|
|
6
6
|
from ..types.graphic_state import GraphicState
|
|
7
|
-
from ..util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
8
7
|
|
|
9
8
|
LOG = logging.getLogger(__name__)
|
|
10
9
|
|
|
@@ -35,7 +34,7 @@ class AnimatedSprite:
|
|
|
35
34
|
"tileset": tileset_name,
|
|
36
35
|
"image": image_name,
|
|
37
36
|
"sprite": self._sprite_name,
|
|
38
|
-
}
|
|
37
|
+
}
|
|
39
38
|
)
|
|
40
39
|
tileset = self.engine.assets.get_tileset(tileset_name)
|
|
41
40
|
|
|
@@ -56,9 +55,11 @@ class AnimatedSprite:
|
|
|
56
55
|
self._timer: float = data["duration"][0]
|
|
57
56
|
else:
|
|
58
57
|
LOG.debug(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
"Sprite information uncomplete. Tileset=%s, Image=%s, Sprite="
|
|
59
|
+
"%s. Will continue without sprite.",
|
|
60
|
+
tileset_name,
|
|
61
|
+
image_name,
|
|
62
|
+
self._sprite_name,
|
|
62
63
|
)
|
|
63
64
|
self.name = self._tileset_name = self._sprite_name = ""
|
|
64
65
|
|
|
@@ -103,14 +104,20 @@ class AnimatedSprite:
|
|
|
103
104
|
self._last_direction = direction
|
|
104
105
|
self._last_graphic_state = graphic_state
|
|
105
106
|
|
|
106
|
-
def draw_self(
|
|
107
|
+
def draw_self(
|
|
108
|
+
self,
|
|
109
|
+
px: float,
|
|
110
|
+
py: float,
|
|
111
|
+
camera_name: str = "display",
|
|
112
|
+
absolute_position: bool = False,
|
|
113
|
+
):
|
|
107
114
|
if self.name == "":
|
|
108
115
|
return
|
|
109
116
|
|
|
110
117
|
data = self._get_data(self._last_graphic_state, self._last_direction)
|
|
111
118
|
if not absolute_position:
|
|
112
|
-
px *=
|
|
113
|
-
py *=
|
|
119
|
+
px *= self.engine.rtc.tile_width
|
|
120
|
+
py *= self.engine.rtc.tile_height
|
|
114
121
|
px, py = math.floor(px), math.floor(py)
|
|
115
122
|
try:
|
|
116
123
|
self.engine.backend.draw_partial_sprite(
|
|
@@ -121,8 +128,9 @@ class AnimatedSprite:
|
|
|
121
128
|
data["oy"][self._frame_index] * self.height,
|
|
122
129
|
self.width,
|
|
123
130
|
self.height,
|
|
131
|
+
camera_name,
|
|
124
132
|
)
|
|
125
|
-
except KeyError
|
|
133
|
+
except KeyError:
|
|
126
134
|
LOG.exception(
|
|
127
135
|
f"Data of {self._tileset_name, self.name} is malformed: {data}"
|
|
128
136
|
)
|
|
@@ -180,15 +188,15 @@ class AnimatedSprite:
|
|
|
180
188
|
data = self._sprites.get(
|
|
181
189
|
graphic_state, self._sprites.get(GraphicState.STANDING, {})
|
|
182
190
|
)
|
|
183
|
-
data = data.get(
|
|
184
|
-
direction,
|
|
185
|
-
data.get(Direction.SOUTH, {}),
|
|
186
|
-
)
|
|
191
|
+
data = data.get(direction, data.get(Direction.SOUTH, {}))
|
|
187
192
|
if not data:
|
|
188
193
|
try:
|
|
189
194
|
LOG.debug(
|
|
190
|
-
|
|
191
|
-
|
|
195
|
+
"Animation of sprite %s,%s is empty for %s, %s ",
|
|
196
|
+
self._tileset_name,
|
|
197
|
+
self._sprite_name,
|
|
198
|
+
graphic_state.name,
|
|
199
|
+
direction.name,
|
|
192
200
|
)
|
|
193
201
|
except Exception:
|
|
194
202
|
# print(graphic_state, direction)
|
mima/objects/attributes.py
CHANGED
mima/objects/creature.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from ..types.direction import Direction
|
|
6
6
|
from ..types.graphic_state import GraphicState, Until
|
|
7
7
|
from ..types.nature import Nature
|
|
8
8
|
from ..types.object import ObjectType
|
|
9
|
+
from ..types.player import Player
|
|
9
10
|
from ..types.terrain import Terrain
|
|
10
11
|
from ..types.weapon_slot import WeaponSlot
|
|
11
12
|
|
|
@@ -14,34 +15,43 @@ from ..util.colors import BLACK
|
|
|
14
15
|
from ..util.constants import DEFAULT_KNOCK_SPEED
|
|
15
16
|
from .animated_sprite import AnimatedSprite
|
|
16
17
|
from .dynamic import Dynamic
|
|
18
|
+
from .effects.light import Light
|
|
17
19
|
from .effects.walking_on_grass import WalkingOnGrass
|
|
18
20
|
from .effects.walking_on_water import WalkingOnWater
|
|
19
21
|
from .projectile import Projectile
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from ..maps.tilemap import Tilemap
|
|
25
|
+
from ..usables.weapon import Weapon
|
|
26
|
+
from ..util.trading_item import TradingItem
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
class Creature(Dynamic):
|
|
26
30
|
def __init__(
|
|
27
31
|
self,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
py: float,
|
|
32
|
+
px: float = 0,
|
|
33
|
+
py: float = 0,
|
|
34
|
+
name: str = "Unnamed Creature",
|
|
35
|
+
*,
|
|
36
|
+
tilemap: Tilemap = None,
|
|
34
37
|
dyn_id: int = -1,
|
|
38
|
+
tileset_name: str = "",
|
|
39
|
+
image_name: str = "",
|
|
40
|
+
sprite_name: str = "",
|
|
41
|
+
player: Player = Player.P0,
|
|
35
42
|
):
|
|
36
|
-
super().__init__(
|
|
43
|
+
super().__init__(px, py, name, tilemap, dyn_id)
|
|
37
44
|
|
|
38
45
|
self.sprite = AnimatedSprite(tileset_name, image_name, sprite_name)
|
|
46
|
+
self.player = player
|
|
39
47
|
self.type = ObjectType.CREATURE
|
|
40
48
|
self.knock_speed: float = DEFAULT_KNOCK_SPEED
|
|
41
49
|
|
|
42
50
|
# self.sprite.name = sprite_name
|
|
43
51
|
# self.sprite.num_frames = 2
|
|
44
52
|
self.attackable = True
|
|
53
|
+
self.knockable = True
|
|
54
|
+
self.moves_on_collision = True
|
|
45
55
|
|
|
46
56
|
self._knock_vx: float = 0.0
|
|
47
57
|
self._knock_vy: float = 0.0
|
|
@@ -63,9 +73,23 @@ class Creature(Dynamic):
|
|
|
63
73
|
WeaponSlot.FIRST_HAND: None,
|
|
64
74
|
WeaponSlot.SECOND_HAND: None,
|
|
65
75
|
}
|
|
76
|
+
self._light = None
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
self.attributes.speed = 5.0
|
|
79
|
+
# self.use_acceleration = True
|
|
80
|
+
# self.use_friction = True
|
|
81
|
+
# self.attributes.friction = 0.1
|
|
82
|
+
|
|
83
|
+
def start_shining(self):
|
|
84
|
+
self._light = Light(self, fixed_size=True, update_from_target=True)
|
|
85
|
+
self.engine.get_view().add_effect(self._light, self.tilemap.name)
|
|
68
86
|
|
|
87
|
+
def stop_shining(self):
|
|
88
|
+
if self._light is not None:
|
|
89
|
+
self._light.kill()
|
|
90
|
+
self._light = None
|
|
91
|
+
|
|
92
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
69
93
|
if not self.visible:
|
|
70
94
|
self.vx = self.vy = 0.0
|
|
71
95
|
return
|
|
@@ -88,6 +112,7 @@ class Creature(Dynamic):
|
|
|
88
112
|
self._invincible_timer -= elapsed_time
|
|
89
113
|
if self._invincible_timer <= 0.0:
|
|
90
114
|
self.invincible = False
|
|
115
|
+
self.knockable = True
|
|
91
116
|
self._invincible_timer = 0.0
|
|
92
117
|
|
|
93
118
|
if self._attack_timer > 0.0:
|
|
@@ -158,7 +183,7 @@ class Creature(Dynamic):
|
|
|
158
183
|
if self._gs_lock_condition == Until.NEXT_UPDATE:
|
|
159
184
|
self.graphic_state_locked = False
|
|
160
185
|
|
|
161
|
-
def draw_self(self, ox: float, oy: float):
|
|
186
|
+
def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
|
|
162
187
|
if (
|
|
163
188
|
self.sprite.name is None
|
|
164
189
|
or self.sprite.name == ""
|
|
@@ -175,9 +200,10 @@ class Creature(Dynamic):
|
|
|
175
200
|
py + 0.7 * self.sprite.height,
|
|
176
201
|
0.3125 * self.sprite.width,
|
|
177
202
|
BLACK,
|
|
203
|
+
camera_name,
|
|
178
204
|
)
|
|
179
205
|
|
|
180
|
-
self.sprite.draw_self(px, py - self.pz)
|
|
206
|
+
self.sprite.draw_self(px, py - self.pz, camera_name)
|
|
181
207
|
# for effect in self.effects:
|
|
182
208
|
# effect.draw_self(px, py - self.pz)
|
|
183
209
|
|
|
@@ -198,15 +224,18 @@ class Creature(Dynamic):
|
|
|
198
224
|
return False
|
|
199
225
|
|
|
200
226
|
def knock_back(self, vx: float, vy: float, dist: float):
|
|
201
|
-
self.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
227
|
+
if self.knockable:
|
|
228
|
+
self._knock_vx = vx
|
|
229
|
+
self._knock_vy = vy
|
|
230
|
+
self._knock_timer = dist
|
|
231
|
+
|
|
232
|
+
self.knockable = False
|
|
233
|
+
self._invincible_timer = dist + 0.5
|
|
234
|
+
# self.solid_vs_dyn = False
|
|
235
|
+
self.controllable = False
|
|
236
|
+
self.invincible = True
|
|
237
|
+
self.sprite.reset()
|
|
238
|
+
self.cancel_attack()
|
|
210
239
|
|
|
211
240
|
def can_act(self):
|
|
212
241
|
actable_states = [
|
|
@@ -271,9 +300,11 @@ class Creature(Dynamic):
|
|
|
271
300
|
if do.inherit_pos:
|
|
272
301
|
do.px = self.px
|
|
273
302
|
do.py = self.py
|
|
274
|
-
self.engine.
|
|
303
|
+
self.engine.get_view().add_projectile(
|
|
304
|
+
do, self.tilemap.name
|
|
305
|
+
)
|
|
275
306
|
else:
|
|
276
|
-
self.engine.
|
|
307
|
+
self.engine.get_view().add_dynamic(do, self.tilemap.name)
|
|
277
308
|
|
|
278
309
|
self.spawn_on_death = []
|
|
279
310
|
|
|
@@ -302,7 +333,7 @@ class Creature(Dynamic):
|
|
|
302
333
|
else:
|
|
303
334
|
eff = WalkingOnWater(self)
|
|
304
335
|
self.effects.append(eff)
|
|
305
|
-
self.engine.
|
|
336
|
+
self.engine.get_view().add_effect(eff, self.tilemap.name)
|
|
306
337
|
else:
|
|
307
338
|
self.attributes.speed_mod = 1.0
|
|
308
339
|
|
|
@@ -316,6 +347,8 @@ class Creature(Dynamic):
|
|
|
316
347
|
for s, w in self.weapons.items():
|
|
317
348
|
if slot != s and weapon == w:
|
|
318
349
|
# Weapon equipped in a different slot; change
|
|
350
|
+
if self.weapons[slot] is not None:
|
|
351
|
+
self.weapons[slot].on_unequip(self)
|
|
319
352
|
self.weapons[slot] = weapon
|
|
320
353
|
self.weapons[s] = None
|
|
321
354
|
return
|
|
@@ -330,3 +363,9 @@ class Creature(Dynamic):
|
|
|
330
363
|
def unequip_weapon(self, slot):
|
|
331
364
|
if self.weapons[slot] is not None:
|
|
332
365
|
self.weapons[slot].on_unequip(self)
|
|
366
|
+
|
|
367
|
+
def get_trading_items(self) -> List[TradingItem]:
|
|
368
|
+
return []
|
|
369
|
+
|
|
370
|
+
def __str__(self):
|
|
371
|
+
return f"C({self.name}, {self.dyn_id})"
|
mima/objects/dynamic.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional, Union
|
|
4
4
|
|
|
5
5
|
from ..types.alignment import Alignment
|
|
6
6
|
from ..types.damage import Damage
|
|
@@ -8,13 +8,15 @@ from ..types.direction import Direction
|
|
|
8
8
|
from ..types.graphic_state import GraphicState, Until
|
|
9
9
|
from ..types.nature import Nature
|
|
10
10
|
from ..types.object import ObjectType
|
|
11
|
+
from ..types.player import Player
|
|
11
12
|
from ..types.terrain import Terrain
|
|
12
|
-
from .attributes import Attributes
|
|
13
13
|
from .attribute_effect import Effect
|
|
14
|
+
from .attributes import Attributes
|
|
14
15
|
from .sprite import Sprite
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
from ..engine import MimaEngine
|
|
19
|
+
from ..maps.tilemap import Tilemap
|
|
18
20
|
from .projectile import Projectile
|
|
19
21
|
|
|
20
22
|
|
|
@@ -23,17 +25,20 @@ class Dynamic:
|
|
|
23
25
|
|
|
24
26
|
def __init__(
|
|
25
27
|
self,
|
|
26
|
-
name: str = "Unnamed Dynamic",
|
|
27
28
|
px: float = 0.0,
|
|
28
29
|
py: float = 0.0,
|
|
30
|
+
name: str = "Unnamed Dynamic",
|
|
31
|
+
tilemap: Optional[Tilemap] = None,
|
|
29
32
|
dyn_id=-1,
|
|
30
33
|
):
|
|
31
34
|
self.name: str = name
|
|
32
35
|
self.dyn_id: int = dyn_id # ID given by Tiled
|
|
33
36
|
self.layer: int = 1
|
|
37
|
+
self.tilemap = tilemap # The object's current map
|
|
38
|
+
self.player = Player.P0
|
|
34
39
|
|
|
35
|
-
self.px: float = px
|
|
36
|
-
self.py: float = py
|
|
40
|
+
self.px: float = px if px is not None else 0.0
|
|
41
|
+
self.py: float = py if py is not None else 0.0
|
|
37
42
|
self.pz: float = 0.0
|
|
38
43
|
self.vx: float = 0.0
|
|
39
44
|
self.vy: float = 0.0
|
|
@@ -52,6 +57,7 @@ class Dynamic:
|
|
|
52
57
|
|
|
53
58
|
self.solid_vs_map: bool = True
|
|
54
59
|
self.solid_vs_dyn: bool = True
|
|
60
|
+
self.solid_vs_player: bool = True
|
|
55
61
|
# self.is_player: bool = False
|
|
56
62
|
# self.is_projectile: bool = False
|
|
57
63
|
self.redundant: bool = False
|
|
@@ -64,6 +70,7 @@ class Dynamic:
|
|
|
64
70
|
self.graphic_state_locked: bool = False
|
|
65
71
|
self.use_acceleration: bool = False
|
|
66
72
|
self.use_friction: bool = False
|
|
73
|
+
self.occupied: bool = False
|
|
67
74
|
|
|
68
75
|
self.alignment: Alignment = Alignment.GOOD
|
|
69
76
|
self.walking_on: Terrain = Terrain.DEFAULT
|
|
@@ -86,7 +93,10 @@ class Dynamic:
|
|
|
86
93
|
|
|
87
94
|
self.sprite: Sprite = Sprite()
|
|
88
95
|
|
|
89
|
-
# Performance
|
|
96
|
+
# Performance
|
|
97
|
+
self.chunks: List[int] = []
|
|
98
|
+
self.moves_on_collision: bool = False
|
|
99
|
+
|
|
90
100
|
self.update_skippable: bool = False
|
|
91
101
|
"""Update may be skipped if object is offscreen."""
|
|
92
102
|
self.offscreen_collision_skippable: bool = True
|
|
@@ -100,7 +110,7 @@ class Dynamic:
|
|
|
100
110
|
"""Update this dynamic."""
|
|
101
111
|
pass
|
|
102
112
|
|
|
103
|
-
def draw_self(self, ox: float, oy: float):
|
|
113
|
+
def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
|
|
104
114
|
"""Draw self to screen"""
|
|
105
115
|
pass
|
|
106
116
|
|
|
@@ -154,7 +164,6 @@ class Dynamic:
|
|
|
154
164
|
if isinstance(eff, str):
|
|
155
165
|
obj = [e for e in self.attribute_effects if e.effect_id == eff]
|
|
156
166
|
if not obj:
|
|
157
|
-
print("Effect not active")
|
|
158
167
|
return
|
|
159
168
|
else:
|
|
160
169
|
eff = obj[0]
|
|
@@ -180,3 +189,16 @@ class Dynamic:
|
|
|
180
189
|
rad += eff.light_radius
|
|
181
190
|
|
|
182
191
|
return rad
|
|
192
|
+
|
|
193
|
+
def get_player(self):
|
|
194
|
+
return self.player
|
|
195
|
+
|
|
196
|
+
def halt(self):
|
|
197
|
+
self.vx = 0
|
|
198
|
+
self.vy = 0
|
|
199
|
+
|
|
200
|
+
def __str__(self):
|
|
201
|
+
return f"D({self.name}, {self.dyn_id})"
|
|
202
|
+
|
|
203
|
+
def __repr__(self):
|
|
204
|
+
return str(self)
|