mima-engine 0.1.5__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 +14 -8
- 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 +54 -17
- 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.5.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.5.dist-info → mima_engine-0.2.0.dist-info}/WHEEL +1 -1
- mima/view/scene.py +0 -322
- mima_engine-0.1.5.dist-info/RECORD +0 -114
- {mima_engine-0.1.5.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,28 +15,35 @@ 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
|
|
|
@@ -43,6 +51,7 @@ class Creature(Dynamic):
|
|
|
43
51
|
# self.sprite.num_frames = 2
|
|
44
52
|
self.attackable = True
|
|
45
53
|
self.knockable = True
|
|
54
|
+
self.moves_on_collision = True
|
|
46
55
|
|
|
47
56
|
self._knock_vx: float = 0.0
|
|
48
57
|
self._knock_vy: float = 0.0
|
|
@@ -64,9 +73,23 @@ class Creature(Dynamic):
|
|
|
64
73
|
WeaponSlot.FIRST_HAND: None,
|
|
65
74
|
WeaponSlot.SECOND_HAND: None,
|
|
66
75
|
}
|
|
76
|
+
self._light = None
|
|
67
77
|
|
|
68
|
-
|
|
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)
|
|
69
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):
|
|
70
93
|
if not self.visible:
|
|
71
94
|
self.vx = self.vy = 0.0
|
|
72
95
|
return
|
|
@@ -89,6 +112,7 @@ class Creature(Dynamic):
|
|
|
89
112
|
self._invincible_timer -= elapsed_time
|
|
90
113
|
if self._invincible_timer <= 0.0:
|
|
91
114
|
self.invincible = False
|
|
115
|
+
self.knockable = True
|
|
92
116
|
self._invincible_timer = 0.0
|
|
93
117
|
|
|
94
118
|
if self._attack_timer > 0.0:
|
|
@@ -159,7 +183,7 @@ class Creature(Dynamic):
|
|
|
159
183
|
if self._gs_lock_condition == Until.NEXT_UPDATE:
|
|
160
184
|
self.graphic_state_locked = False
|
|
161
185
|
|
|
162
|
-
def draw_self(self, ox: float, oy: float):
|
|
186
|
+
def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
|
|
163
187
|
if (
|
|
164
188
|
self.sprite.name is None
|
|
165
189
|
or self.sprite.name == ""
|
|
@@ -176,9 +200,10 @@ class Creature(Dynamic):
|
|
|
176
200
|
py + 0.7 * self.sprite.height,
|
|
177
201
|
0.3125 * self.sprite.width,
|
|
178
202
|
BLACK,
|
|
203
|
+
camera_name,
|
|
179
204
|
)
|
|
180
205
|
|
|
181
|
-
self.sprite.draw_self(px, py - self.pz)
|
|
206
|
+
self.sprite.draw_self(px, py - self.pz, camera_name)
|
|
182
207
|
# for effect in self.effects:
|
|
183
208
|
# effect.draw_self(px, py - self.pz)
|
|
184
209
|
|
|
@@ -203,7 +228,9 @@ class Creature(Dynamic):
|
|
|
203
228
|
self._knock_vx = vx
|
|
204
229
|
self._knock_vy = vy
|
|
205
230
|
self._knock_timer = dist
|
|
206
|
-
|
|
231
|
+
|
|
232
|
+
self.knockable = False
|
|
233
|
+
self._invincible_timer = dist + 0.5
|
|
207
234
|
# self.solid_vs_dyn = False
|
|
208
235
|
self.controllable = False
|
|
209
236
|
self.invincible = True
|
|
@@ -273,9 +300,11 @@ class Creature(Dynamic):
|
|
|
273
300
|
if do.inherit_pos:
|
|
274
301
|
do.px = self.px
|
|
275
302
|
do.py = self.py
|
|
276
|
-
self.engine.
|
|
303
|
+
self.engine.get_view().add_projectile(
|
|
304
|
+
do, self.tilemap.name
|
|
305
|
+
)
|
|
277
306
|
else:
|
|
278
|
-
self.engine.
|
|
307
|
+
self.engine.get_view().add_dynamic(do, self.tilemap.name)
|
|
279
308
|
|
|
280
309
|
self.spawn_on_death = []
|
|
281
310
|
|
|
@@ -304,7 +333,7 @@ class Creature(Dynamic):
|
|
|
304
333
|
else:
|
|
305
334
|
eff = WalkingOnWater(self)
|
|
306
335
|
self.effects.append(eff)
|
|
307
|
-
self.engine.
|
|
336
|
+
self.engine.get_view().add_effect(eff, self.tilemap.name)
|
|
308
337
|
else:
|
|
309
338
|
self.attributes.speed_mod = 1.0
|
|
310
339
|
|
|
@@ -318,6 +347,8 @@ class Creature(Dynamic):
|
|
|
318
347
|
for s, w in self.weapons.items():
|
|
319
348
|
if slot != s and weapon == w:
|
|
320
349
|
# Weapon equipped in a different slot; change
|
|
350
|
+
if self.weapons[slot] is not None:
|
|
351
|
+
self.weapons[slot].on_unequip(self)
|
|
321
352
|
self.weapons[slot] = weapon
|
|
322
353
|
self.weapons[s] = None
|
|
323
354
|
return
|
|
@@ -332,3 +363,9 @@ class Creature(Dynamic):
|
|
|
332
363
|
def unequip_weapon(self, slot):
|
|
333
364
|
if self.weapons[slot] is not None:
|
|
334
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)
|