mima-engine 0.1.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 -0
- mima/backend/__init__.py +1 -0
- mima/backend/pygame_assets.py +345 -0
- mima/backend/pygame_audio.py +75 -0
- mima/backend/pygame_backend.py +399 -0
- mima/backend/pygame_events.py +430 -0
- mima/collision.py +237 -0
- mima/engine.py +197 -0
- mima/maps/__init__.py +0 -0
- mima/maps/template.py +41 -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 +45 -0
- mima/maps/tilemap.py +159 -0
- mima/maps/tileset.py +32 -0
- mima/maps/tileset_info.py +9 -0
- mima/maps/transition_map.py +148 -0
- mima/objects/__init__.py +0 -0
- mima/objects/animated_sprite.py +198 -0
- mima/objects/attribute_effect.py +26 -0
- mima/objects/attributes.py +123 -0
- mima/objects/creature.py +332 -0
- mima/objects/dynamic.py +182 -0
- mima/objects/effects/__init__.py +0 -0
- mima/objects/effects/colorize_screen.py +36 -0
- mima/objects/effects/light.py +107 -0
- mima/objects/effects/walking_on_grass.py +38 -0
- mima/objects/effects/walking_on_water.py +41 -0
- mima/objects/loader.py +103 -0
- mima/objects/projectile.py +86 -0
- mima/objects/sprite.py +110 -0
- mima/objects/world/__init__.py +0 -0
- mima/objects/world/color_gate.py +68 -0
- mima/objects/world/color_switch.py +105 -0
- mima/objects/world/container.py +171 -0
- mima/objects/world/floor_switch.py +111 -0
- mima/objects/world/gate.py +174 -0
- mima/objects/world/light_source.py +124 -0
- mima/objects/world/logic_gate.py +163 -0
- mima/objects/world/movable.py +338 -0
- mima/objects/world/oneway.py +168 -0
- mima/objects/world/pickup.py +88 -0
- mima/objects/world/switch.py +165 -0
- mima/objects/world/teleport.py +288 -0
- mima/scene_engine.py +79 -0
- mima/scripts/__init__.py +2 -0
- mima/scripts/command.py +24 -0
- mima/scripts/commands/__init__.py +0 -0
- mima/scripts/commands/add_quest.py +19 -0
- mima/scripts/commands/change_map.py +15 -0
- mima/scripts/commands/close_dialog.py +8 -0
- mima/scripts/commands/give_item.py +24 -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 +57 -0
- mima/scripts/commands/parallel.py +53 -0
- mima/scripts/commands/play_sound.py +13 -0
- mima/scripts/commands/present_item.py +51 -0
- mima/scripts/commands/progress_quest.py +12 -0
- mima/scripts/commands/quit_game.py +8 -0
- mima/scripts/commands/save_game.py +13 -0
- mima/scripts/commands/screen_fade.py +65 -0
- mima/scripts/commands/serial.py +46 -0
- mima/scripts/commands/set_facing_direction.py +21 -0
- mima/scripts/commands/set_spawn_map.py +14 -0
- mima/scripts/commands/show_choices.py +43 -0
- mima/scripts/commands/show_dialog.py +11 -0
- mima/scripts/commands/take_coins.py +23 -0
- mima/scripts/script_processor.py +40 -0
- mima/states/__init__.py +0 -0
- mima/states/game_state.py +162 -0
- mima/states/quest.py +72 -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 +22 -0
- mima/types/keys.py +16 -0
- mima/types/mode.py +15 -0
- mima/types/nature.py +12 -0
- mima/types/object.py +22 -0
- mima/types/start.py +7 -0
- mima/types/terrain.py +9 -0
- mima/types/weapon_slot.py +6 -0
- mima/usables/__init__.py +0 -0
- mima/usables/item.py +31 -0
- mima/usables/weapon.py +48 -0
- mima/util/__init__.py +1 -0
- mima/util/colors.py +45 -0
- mima/util/constants.py +47 -0
- mima/util/functions.py +13 -0
- mima/util/input_defaults.py +49 -0
- mima/util/logging.py +51 -0
- mima/util/property.py +8 -0
- mima/util/runtime_config.py +133 -0
- mima/view/__init__.py +0 -0
- mima/view/camera.py +51 -0
- mima/view/scene.py +350 -0
- mima_engine-0.1.0.dist-info/METADATA +14 -0
- mima_engine-0.1.0.dist-info/RECORD +114 -0
- mima_engine-0.1.0.dist-info/WHEEL +5 -0
- mima_engine-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, List
|
|
4
|
+
|
|
5
|
+
from ...types.direction import Direction
|
|
6
|
+
from ...types.graphic_state import GraphicState
|
|
7
|
+
from ...types.terrain import Terrain
|
|
8
|
+
from ...util.functions import strtobool
|
|
9
|
+
from ..tile import Tile
|
|
10
|
+
from ..tile_animation import TileAnimation
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from xml.etree.ElementTree import Element
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class TiledTile(Tile):
|
|
17
|
+
def __init__(self, t_xtree: Element):
|
|
18
|
+
super().__init__()
|
|
19
|
+
|
|
20
|
+
self.tile_type: str = t_xtree.attrib.get(
|
|
21
|
+
"class", t_xtree.attrib.get("type", "tile")
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
self.basic_tile_id: int = int(t_xtree.attrib["id"])
|
|
25
|
+
self.tile_id: int = self.basic_tile_id
|
|
26
|
+
|
|
27
|
+
self._frames: List[TileAnimation] = []
|
|
28
|
+
self._frame: int = 0
|
|
29
|
+
self._num_frames: int = 0
|
|
30
|
+
self._frame_timer: float = 0.0
|
|
31
|
+
|
|
32
|
+
animation = t_xtree.findall("animation")
|
|
33
|
+
|
|
34
|
+
if animation:
|
|
35
|
+
frames = animation[0].findall("frame")
|
|
36
|
+
for frame in frames:
|
|
37
|
+
self._frames.append(
|
|
38
|
+
TileAnimation(
|
|
39
|
+
frame_id=int(frame.attrib["tileid"]),
|
|
40
|
+
duration=int(frame.attrib["duration"]) / 1000.0,
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
self.animated = True
|
|
44
|
+
else:
|
|
45
|
+
self._frames.append(
|
|
46
|
+
TileAnimation(frame_id=self.basic_tile_id, duration=0.0)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self._num_frames = len(self._frames)
|
|
50
|
+
|
|
51
|
+
self.tile_id = self._frames[0].frame_id
|
|
52
|
+
self._frame_timer = self._frames[0].duration
|
|
53
|
+
|
|
54
|
+
properties = t_xtree.findall("properties")
|
|
55
|
+
if properties:
|
|
56
|
+
properties = properties[0].findall("property")
|
|
57
|
+
for prop in properties:
|
|
58
|
+
if prop.attrib["name"] == "solid":
|
|
59
|
+
self.solid = strtobool(prop.attrib["value"])
|
|
60
|
+
if prop.attrib["name"] == "ground_type":
|
|
61
|
+
try:
|
|
62
|
+
self.terrain = Terrain[prop.attrib["value"].upper()]
|
|
63
|
+
except:
|
|
64
|
+
self.terrain = Terrain.DEFAULT
|
|
65
|
+
if prop.attrib["name"] == "z_height":
|
|
66
|
+
self.z_height = float(prop.attrib["value"])
|
|
67
|
+
if prop.attrib["name"] == "facing_direction":
|
|
68
|
+
self.facing_direction = Direction[
|
|
69
|
+
prop.attrib.get("value", "south").upper()
|
|
70
|
+
]
|
|
71
|
+
if prop.attrib["name"] == "graphic_state":
|
|
72
|
+
self.graphic_state = GraphicState[
|
|
73
|
+
prop.attrib.get("value", "standing").upper()
|
|
74
|
+
]
|
|
75
|
+
if prop.attrib["name"] == "sprite_name":
|
|
76
|
+
self.sprite_name = prop.attrib.get("value", "")
|
|
77
|
+
|
|
78
|
+
def update(self, elapsed_time: float) -> bool:
|
|
79
|
+
if self._num_frames <= 1:
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
self._frame_timer -= elapsed_time
|
|
83
|
+
if self._frame_timer <= 0:
|
|
84
|
+
self._frame = (self._frame + 1) % self._num_frames
|
|
85
|
+
self.tile_id = self._frames[self._frame].frame_id
|
|
86
|
+
self._frame_timer += self._frames[self._frame].duration
|
|
87
|
+
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
return False
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from xml.etree import ElementTree
|
|
6
|
+
|
|
7
|
+
from ..tileset import Tileset
|
|
8
|
+
from .tiled_tile import TiledTile
|
|
9
|
+
|
|
10
|
+
LOG = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TiledTileset(Tileset):
|
|
14
|
+
def __init__(self, name: str, filename: str):
|
|
15
|
+
super().__init__()
|
|
16
|
+
|
|
17
|
+
self.name = name
|
|
18
|
+
|
|
19
|
+
LOG.info(
|
|
20
|
+
"Loading tileset %s from TSX file at '%s' ...",
|
|
21
|
+
name,
|
|
22
|
+
filename,
|
|
23
|
+
)
|
|
24
|
+
tree = ElementTree.parse(filename)
|
|
25
|
+
LOG.debug("Loaded file %s successfully.", filename)
|
|
26
|
+
|
|
27
|
+
root = tree.getroot()
|
|
28
|
+
LOG.debug("Loading tileset properties ...")
|
|
29
|
+
self.tile_width: int = int(root.attrib["tilewidth"])
|
|
30
|
+
self.tile_height: int = int(root.attrib["tileheight"])
|
|
31
|
+
self.tile_count: int = int(root.attrib["tilecount"])
|
|
32
|
+
self.columns: int = int(root.attrib["columns"])
|
|
33
|
+
|
|
34
|
+
LOG.debug("Loading image properties ...")
|
|
35
|
+
image = root.findall("image")[0]
|
|
36
|
+
self.sprite_name: str = os.path.split(image.attrib["source"])[-1][:-4]
|
|
37
|
+
self.sprite_width: int = int(image.attrib["width"])
|
|
38
|
+
self.sprite_height: int = int(image.attrib["height"])
|
|
39
|
+
|
|
40
|
+
LOG.debug("Loading tiles ...")
|
|
41
|
+
tiles = root.findall("tile")
|
|
42
|
+
for tile in tiles:
|
|
43
|
+
self.tiles.append(TiledTile(tile))
|
|
44
|
+
if self.tiles[-1].animated:
|
|
45
|
+
self.animated_tiles.append(self.tiles[-1])
|
mima/maps/tilemap.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import math
|
|
5
|
+
from typing import TYPE_CHECKING, Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
from ..util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
8
|
+
from .tile_info import TileInfo
|
|
9
|
+
from .tile_layer import TileLayer
|
|
10
|
+
from .tileset_info import TilesetInfo
|
|
11
|
+
from .template import Template
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ..objects.dynamic import Dynamic
|
|
15
|
+
from ..types.nature import Nature
|
|
16
|
+
from .tile import Tile
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
LOG = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Tilemap(Template):
|
|
23
|
+
|
|
24
|
+
def __init__(self, name: str):
|
|
25
|
+
super().__init__(name)
|
|
26
|
+
self.width: int = 0
|
|
27
|
+
self.height: int = 0
|
|
28
|
+
self.tile_width = TILE_WIDTH
|
|
29
|
+
self.tile_height = TILE_HEIGHT
|
|
30
|
+
|
|
31
|
+
self._layers: List[TileLayer] = []
|
|
32
|
+
self._tilesets: List[TilesetInfo] = []
|
|
33
|
+
self._cache: Dict[int, TileInfo] = {}
|
|
34
|
+
|
|
35
|
+
def populate_dynamics(self, dynamics: List[Dynamic]) -> bool:
|
|
36
|
+
"""Load all map-related objects into the game."""
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
def update(self, elapsed_time: float) -> bool:
|
|
40
|
+
for info in self._tilesets:
|
|
41
|
+
info.tileset.update(elapsed_time)
|
|
42
|
+
|
|
43
|
+
for layer in self._layers:
|
|
44
|
+
layer.update(elapsed_time)
|
|
45
|
+
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
def draw_self(
|
|
49
|
+
self,
|
|
50
|
+
ox: float,
|
|
51
|
+
oy: float,
|
|
52
|
+
visible_tiles_sx: int,
|
|
53
|
+
visible_tiles_sy: int,
|
|
54
|
+
visible_tiles_ex: int,
|
|
55
|
+
visible_tiles_ey: int,
|
|
56
|
+
layer_pos: int = 0,
|
|
57
|
+
):
|
|
58
|
+
# Get offsets for smooth movement
|
|
59
|
+
tile_ox = (ox - math.floor(ox)) * self.tile_width
|
|
60
|
+
tile_oy = (oy - math.floor(oy)) * self.tile_height
|
|
61
|
+
|
|
62
|
+
for layer in self._layers:
|
|
63
|
+
if layer.layer_pos != layer_pos:
|
|
64
|
+
continue
|
|
65
|
+
|
|
66
|
+
layer_ox = (
|
|
67
|
+
layer.layer_ox - math.floor(layer.layer_ox)
|
|
68
|
+
) * self.tile_width
|
|
69
|
+
layer_oy = (
|
|
70
|
+
layer.layer_oy - math.floor(layer.layer_oy)
|
|
71
|
+
) * self.tile_height
|
|
72
|
+
|
|
73
|
+
layer_visible_tiles_sx = int(visible_tiles_sx)
|
|
74
|
+
layer_visible_tiles_sy = int(visible_tiles_sy)
|
|
75
|
+
|
|
76
|
+
if layer.speed_x != 0.0:
|
|
77
|
+
layer_visible_tiles_sx -= 1
|
|
78
|
+
if layer.speed_y != 0.0:
|
|
79
|
+
layer_visible_tiles_sy -= 1
|
|
80
|
+
|
|
81
|
+
# Draw visible tiles of the map
|
|
82
|
+
for x in range(layer_visible_tiles_sx, int(visible_tiles_ex) + 1):
|
|
83
|
+
for y in range(
|
|
84
|
+
layer_visible_tiles_sy, int(visible_tiles_ey) + 1
|
|
85
|
+
):
|
|
86
|
+
tile_index = layer.get_index(
|
|
87
|
+
int(x + math.floor(ox)),
|
|
88
|
+
int(y + math.floor(oy)),
|
|
89
|
+
)
|
|
90
|
+
if tile_index <= 0:
|
|
91
|
+
# Zero means the tile was not set in Tiled
|
|
92
|
+
continue
|
|
93
|
+
|
|
94
|
+
if tile_index not in self._cache:
|
|
95
|
+
if not self._load_to_cache(tile_index):
|
|
96
|
+
continue
|
|
97
|
+
|
|
98
|
+
info = self._cache[tile_index]
|
|
99
|
+
|
|
100
|
+
sx = info.tile.tile_id % info.tileset.columns
|
|
101
|
+
sy = info.tile.tile_id // info.tileset.columns
|
|
102
|
+
|
|
103
|
+
self.engine.backend.draw_partial_sprite(
|
|
104
|
+
math.floor(x * self.tile_width - tile_ox + layer_ox),
|
|
105
|
+
math.floor(y * self.tile_height - tile_oy + layer_oy),
|
|
106
|
+
info.tileset.sprite_name,
|
|
107
|
+
sx * self.tile_width,
|
|
108
|
+
sy * self.tile_height,
|
|
109
|
+
self.tile_width,
|
|
110
|
+
self.tile_height,
|
|
111
|
+
)
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
def on_interaction(self, target: Dynamic, nature: Nature) -> bool:
|
|
115
|
+
return False
|
|
116
|
+
|
|
117
|
+
def is_solid(
|
|
118
|
+
self, px: int, py: int, layer_pos: Optional[int] = None
|
|
119
|
+
) -> bool:
|
|
120
|
+
tile = self.get_tile(px, py, layer_pos)
|
|
121
|
+
if tile is not None and tile.solid:
|
|
122
|
+
return True
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
def get_tile(
|
|
126
|
+
self, px: int, py: int, layer_pos: Optional[int] = None
|
|
127
|
+
) -> Optional[Tile]:
|
|
128
|
+
for layer in self._layers[::-1]:
|
|
129
|
+
if layer_pos is not None and layer_pos != layer.layer_pos:
|
|
130
|
+
continue
|
|
131
|
+
tile_index = layer.get_index(math.floor(px), math.floor(py))
|
|
132
|
+
if tile_index not in self._cache:
|
|
133
|
+
if not self._load_to_cache(tile_index):
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
info = self._cache[tile_index]
|
|
137
|
+
if info.tile is not None:
|
|
138
|
+
return info.tile
|
|
139
|
+
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
def _load_to_cache(self, tile_index: int) -> bool:
|
|
143
|
+
tileset = None
|
|
144
|
+
firstgid = 0
|
|
145
|
+
for tsinfo in self._tilesets:
|
|
146
|
+
if tile_index < tsinfo.first_gid:
|
|
147
|
+
break
|
|
148
|
+
|
|
149
|
+
firstgid = tsinfo.first_gid
|
|
150
|
+
tileset = tsinfo.tileset
|
|
151
|
+
|
|
152
|
+
if tileset is None:
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
tidx = tile_index - firstgid
|
|
156
|
+
tile = tileset.get_tile(tidx)
|
|
157
|
+
|
|
158
|
+
self._cache[tile_index] = TileInfo(tileset=tileset, tile=tile)
|
|
159
|
+
return True
|
mima/maps/tileset.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from .tile import Tile
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Tileset:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.name: str = "Unnamed Tileset"
|
|
9
|
+
self.sprite_name: str = ""
|
|
10
|
+
# self.filename: str = ""
|
|
11
|
+
self.sprite_width: int = 0
|
|
12
|
+
self.sprite_height: int = 0
|
|
13
|
+
self.tile_width: int = 0
|
|
14
|
+
self.tile_height: int = 0
|
|
15
|
+
self.tile_count: int = 0
|
|
16
|
+
self.columns: int = 0
|
|
17
|
+
|
|
18
|
+
self.tiles: List[Tile] = []
|
|
19
|
+
self.animated_tiles: List[Tile] = []
|
|
20
|
+
|
|
21
|
+
def update(self, elapsed_time: float) -> bool:
|
|
22
|
+
for tile in self.animated_tiles:
|
|
23
|
+
tile.update(elapsed_time)
|
|
24
|
+
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
def get_tile(self, tile_id: int) -> Tile:
|
|
28
|
+
for tile in self.tiles:
|
|
29
|
+
if tile.basic_tile_id == tile_id:
|
|
30
|
+
return tile
|
|
31
|
+
|
|
32
|
+
return self.tiles[0]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from ..util.constants import (
|
|
6
|
+
MAP_TRANSITION_DURATION_FACTOR,
|
|
7
|
+
MOVE_MAP_DURATION,
|
|
8
|
+
TILE_HEIGHT,
|
|
9
|
+
TILE_WIDTH,
|
|
10
|
+
UI_HIGHT,
|
|
11
|
+
)
|
|
12
|
+
from .tilemap import Tilemap
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from .tile import Tile
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TransitionMap(Tilemap):
|
|
19
|
+
def __init__(
|
|
20
|
+
self, src_map: Tilemap, dst_map: Tilemap, dst_vx: int, dst_vy: int
|
|
21
|
+
):
|
|
22
|
+
super().__init__(src_map.name)
|
|
23
|
+
|
|
24
|
+
self.src_map: Tilemap = src_map
|
|
25
|
+
self.dst_map: Tilemap = dst_map
|
|
26
|
+
self.vx: int = int(dst_vx)
|
|
27
|
+
self.vy: int = int(dst_vy)
|
|
28
|
+
self.progress: float = 0.0
|
|
29
|
+
self.duration: float = (
|
|
30
|
+
MOVE_MAP_DURATION * MAP_TRANSITION_DURATION_FACTOR
|
|
31
|
+
)
|
|
32
|
+
self.time_so_far: float = 0.0
|
|
33
|
+
self.step_size: float = 1.0
|
|
34
|
+
self._extra_oy = UI_HIGHT
|
|
35
|
+
|
|
36
|
+
self.width: int = self.src_map.width
|
|
37
|
+
self.height: int = self.src_map.height
|
|
38
|
+
self.tiles_nx = self.engine.backend.render_width / TILE_WIDTH
|
|
39
|
+
self.tiles_ny = self.engine.backend.render_height / TILE_HEIGHT
|
|
40
|
+
|
|
41
|
+
if self.vx != 0:
|
|
42
|
+
self.step_size = self.duration / (self.tiles_nx * 0.75)
|
|
43
|
+
elif self.vy != 0:
|
|
44
|
+
self.step_size = self.duration / ((self.tiles_ny - 1) * 0.75)
|
|
45
|
+
|
|
46
|
+
# print(
|
|
47
|
+
# f"Step Size={self.step_size}, Duration={self.duration}, Size={WIDTH, HEIGHT}, TileSize={TILE_WIDTH, TILE_HEIGHT} "
|
|
48
|
+
# )
|
|
49
|
+
|
|
50
|
+
def update(self, elapsed_time: float) -> bool:
|
|
51
|
+
self.progress = self.time_so_far / self.step_size
|
|
52
|
+
self.time_so_far += elapsed_time
|
|
53
|
+
|
|
54
|
+
return self.src_map.update(elapsed_time)
|
|
55
|
+
|
|
56
|
+
def draw_self(
|
|
57
|
+
self,
|
|
58
|
+
ox: float,
|
|
59
|
+
oy: float,
|
|
60
|
+
visible_tiles_sx: int,
|
|
61
|
+
visible_tiles_sy: int,
|
|
62
|
+
visible_tiles_ex: int,
|
|
63
|
+
visible_tiles_ey: int,
|
|
64
|
+
layer_pos: int = 0,
|
|
65
|
+
) -> bool:
|
|
66
|
+
src_tiles_sx = dst_tiles_sx = int(visible_tiles_sx)
|
|
67
|
+
src_tiles_sy = dst_tiles_sy = int(visible_tiles_sy)
|
|
68
|
+
src_tiles_ex = dst_tiles_ex = int(visible_tiles_ex)
|
|
69
|
+
src_tiles_ey = dst_tiles_ey = int(visible_tiles_ey)
|
|
70
|
+
src_ox = dst_ox = ox
|
|
71
|
+
src_oy = dst_oy = oy
|
|
72
|
+
|
|
73
|
+
if self.vx < 0: # Transition west
|
|
74
|
+
src_tiles_sx += int(self.progress)
|
|
75
|
+
src_tiles_ex += int(self.progress)
|
|
76
|
+
dst_tiles_ex = src_tiles_sx + 1
|
|
77
|
+
src_ox = max(-self.tiles_nx, -self.progress)
|
|
78
|
+
dst_ox = (
|
|
79
|
+
self.dst_map.width - self.tiles_nx + visible_tiles_ex + src_ox
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
elif self.vx > 0: # Transition east
|
|
83
|
+
src_tiles_sx -= int(self.progress)
|
|
84
|
+
src_tiles_ex -= int(self.progress)
|
|
85
|
+
dst_tiles_sx = src_tiles_ex
|
|
86
|
+
src_ox = self.src_map.width + min(0, self.progress - self.tiles_nx)
|
|
87
|
+
dst_ox = src_ox - self.src_map.width
|
|
88
|
+
|
|
89
|
+
elif self.vy < 0: # Transition north
|
|
90
|
+
src_tiles_sy += int(self.progress)
|
|
91
|
+
src_tiles_ey += int(self.progress)
|
|
92
|
+
dst_tiles_ey = src_tiles_sy + 1
|
|
93
|
+
src_oy = max(-self.tiles_ny, -self.progress - self._extra_oy)
|
|
94
|
+
dst_oy = (
|
|
95
|
+
self.dst_map.height - self.tiles_ny + visible_tiles_ey + src_oy
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
elif self.vy > 0: # Transition south
|
|
99
|
+
src_tiles_sy -= int(self.progress)
|
|
100
|
+
src_tiles_ey -= int(self.progress)
|
|
101
|
+
dst_tiles_sy = src_tiles_ey
|
|
102
|
+
src_oy = self.src_map.height + min(
|
|
103
|
+
0, self.progress - self.tiles_ny
|
|
104
|
+
)
|
|
105
|
+
dst_oy = -visible_tiles_ey + self.progress
|
|
106
|
+
|
|
107
|
+
# if layer_pos == 0:
|
|
108
|
+
# print(
|
|
109
|
+
# f"o({ox:.2f}),t({self.tiles_nx}),"
|
|
110
|
+
# f"ss({self.src_map.width}),so({src_ox:.2f}),"
|
|
111
|
+
# f"ts({src_tiles_sx}),te({src_tiles_ex}) -> "
|
|
112
|
+
# f"ds({self.dst_map.width}),do({dst_ox:.2f}),"
|
|
113
|
+
# f"ts({dst_tiles_sx}),te({dst_tiles_ex})"
|
|
114
|
+
# )
|
|
115
|
+
# print(
|
|
116
|
+
# f"o({ox:.2f},{oy:.2f}),"
|
|
117
|
+
# f"ss({self.src_map.width},{self.src_map.height}),so({src_ox:.2f},{src_oy:.2f}),"
|
|
118
|
+
# f"ts({src_tiles_sx},{src_tiles_sy}),te{src_tiles_ex,src_tiles_ey} -> "
|
|
119
|
+
# f"ds({self.dst_map.width},{self.dst_map.height}),do({dst_ox:.2f},{dst_oy:.2f}),"
|
|
120
|
+
# f"ts({dst_tiles_sx},{dst_tiles_sy}),te({dst_tiles_ex},{dst_tiles_ey})"
|
|
121
|
+
# )
|
|
122
|
+
# print(src_oy, dst_oy, self.map_screen_oy)
|
|
123
|
+
self.src_map.draw_self(
|
|
124
|
+
src_ox,
|
|
125
|
+
src_oy,
|
|
126
|
+
src_tiles_sx,
|
|
127
|
+
src_tiles_sy,
|
|
128
|
+
src_tiles_ex,
|
|
129
|
+
src_tiles_ey,
|
|
130
|
+
layer_pos,
|
|
131
|
+
)
|
|
132
|
+
self.dst_map.draw_self(
|
|
133
|
+
dst_ox,
|
|
134
|
+
dst_oy,
|
|
135
|
+
dst_tiles_sx,
|
|
136
|
+
dst_tiles_sy,
|
|
137
|
+
dst_tiles_ex,
|
|
138
|
+
dst_tiles_ey,
|
|
139
|
+
layer_pos,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return True
|
|
143
|
+
|
|
144
|
+
def is_solid(self, px: int, py: int) -> bool:
|
|
145
|
+
return False
|
|
146
|
+
|
|
147
|
+
def get_tile(self, px: int, py: int) -> Optional[Tile]:
|
|
148
|
+
return None
|
mima/objects/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import math
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from ..types.direction import Direction
|
|
6
|
+
from ..types.graphic_state import GraphicState
|
|
7
|
+
from ..util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
8
|
+
|
|
9
|
+
LOG = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AnimatedSprite:
|
|
13
|
+
engine = None
|
|
14
|
+
sprite_sets = {}
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
tileset_name: str,
|
|
19
|
+
image_name: str,
|
|
20
|
+
sprite_name: Optional[str] = None,
|
|
21
|
+
graphic_state: GraphicState = GraphicState.STANDING,
|
|
22
|
+
facing_direction: Direction = Direction.SOUTH,
|
|
23
|
+
):
|
|
24
|
+
self._sprite_name = (
|
|
25
|
+
tileset_name if sprite_name is None else sprite_name
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
self._tileset_name = tileset_name
|
|
29
|
+
self.name = image_name
|
|
30
|
+
# TODO: Handle ""
|
|
31
|
+
if tileset_name and image_name and self._sprite_name:
|
|
32
|
+
LOG.info(
|
|
33
|
+
{
|
|
34
|
+
"operation": "load sprite",
|
|
35
|
+
"tileset": tileset_name,
|
|
36
|
+
"image": image_name,
|
|
37
|
+
"sprite": self._sprite_name,
|
|
38
|
+
},
|
|
39
|
+
)
|
|
40
|
+
tileset = self.engine.assets.get_tileset(tileset_name)
|
|
41
|
+
|
|
42
|
+
self.width = tileset.tile_width
|
|
43
|
+
self.height = tileset.tile_height
|
|
44
|
+
|
|
45
|
+
self._sprites: Dict[
|
|
46
|
+
GraphicState, Dict[Direction, Dict[str, Any]]
|
|
47
|
+
] = self._load_sprites_from_tileset(tileset, self._sprite_name)
|
|
48
|
+
|
|
49
|
+
self._last_direction: Direction = facing_direction
|
|
50
|
+
self._last_graphic_state: GraphicState = graphic_state
|
|
51
|
+
data = self._get_data(
|
|
52
|
+
self._last_graphic_state, self._last_direction
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self._frame_index: int = 0
|
|
56
|
+
self._timer: float = data["duration"][0]
|
|
57
|
+
else:
|
|
58
|
+
LOG.debug(
|
|
59
|
+
f"Sprite information uncomplete. Tileset={tileset_name}, Image"
|
|
60
|
+
f"={image_name}, Sprite={self._sprite_name}. Will continue without "
|
|
61
|
+
"sprite."
|
|
62
|
+
)
|
|
63
|
+
self.name = self._tileset_name = self._sprite_name = ""
|
|
64
|
+
|
|
65
|
+
def update(
|
|
66
|
+
self,
|
|
67
|
+
elapsed_time: float,
|
|
68
|
+
direction: Direction = Direction.SOUTH,
|
|
69
|
+
graphic_state: GraphicState = GraphicState.STANDING,
|
|
70
|
+
):
|
|
71
|
+
if not self.name:
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
data = self._get_data(graphic_state, direction)
|
|
75
|
+
# try:
|
|
76
|
+
# self._timer_reset = data["duration"][self._frame_index]
|
|
77
|
+
# except KeyError as err:
|
|
78
|
+
# LOG.exception(
|
|
79
|
+
# f"Data of {self._tileset_name, self.name} is "
|
|
80
|
+
# f"malformed: {data}"
|
|
81
|
+
# )
|
|
82
|
+
|
|
83
|
+
if (
|
|
84
|
+
direction == self._last_direction
|
|
85
|
+
and graphic_state == self._last_graphic_state
|
|
86
|
+
):
|
|
87
|
+
# No changes, normal case
|
|
88
|
+
self._timer -= elapsed_time
|
|
89
|
+
if self._timer <= 0.0:
|
|
90
|
+
self._frame_index = (self._frame_index + 1) % len(
|
|
91
|
+
data["duration"]
|
|
92
|
+
)
|
|
93
|
+
self._timer += data["duration"][self._frame_index]
|
|
94
|
+
|
|
95
|
+
else:
|
|
96
|
+
self._frame_index = 0
|
|
97
|
+
# Something changed
|
|
98
|
+
# if graphic_state != self._last_graphic_state:
|
|
99
|
+
# State changed
|
|
100
|
+
|
|
101
|
+
self._timer = data["duration"][0]
|
|
102
|
+
|
|
103
|
+
self._last_direction = direction
|
|
104
|
+
self._last_graphic_state = graphic_state
|
|
105
|
+
|
|
106
|
+
def draw_self(self, px: float, py: float, absolute_position: bool = False):
|
|
107
|
+
if self.name == "":
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
data = self._get_data(self._last_graphic_state, self._last_direction)
|
|
111
|
+
if not absolute_position:
|
|
112
|
+
px *= TILE_WIDTH
|
|
113
|
+
py *= TILE_HEIGHT
|
|
114
|
+
px, py = math.floor(px), math.floor(py)
|
|
115
|
+
try:
|
|
116
|
+
self.engine.backend.draw_partial_sprite(
|
|
117
|
+
px,
|
|
118
|
+
py,
|
|
119
|
+
self.name,
|
|
120
|
+
data["ox"][self._frame_index] * self.width,
|
|
121
|
+
data["oy"][self._frame_index] * self.height,
|
|
122
|
+
self.width,
|
|
123
|
+
self.height,
|
|
124
|
+
)
|
|
125
|
+
except KeyError as err:
|
|
126
|
+
LOG.exception(
|
|
127
|
+
f"Data of {self._tileset_name, self.name} is malformed: {data}"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def _load_sprites_from_tileset(self, tileset, sprite_name):
|
|
131
|
+
if sprite_name in AnimatedSprite.sprite_sets:
|
|
132
|
+
# Caching
|
|
133
|
+
return AnimatedSprite.sprite_sets[sprite_name]
|
|
134
|
+
|
|
135
|
+
sprites = {}
|
|
136
|
+
|
|
137
|
+
for tile in tileset.tiles:
|
|
138
|
+
if tile.sprite_name != sprite_name:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
if tile.animated:
|
|
142
|
+
data = {"duration": [], "ox": [], "oy": []}
|
|
143
|
+
for frame in tile._frames:
|
|
144
|
+
data["duration"].append(frame.duration)
|
|
145
|
+
data["ox"].append(frame.frame_id % tileset.columns)
|
|
146
|
+
data["oy"].append(frame.frame_id // tileset.columns)
|
|
147
|
+
else:
|
|
148
|
+
data = {
|
|
149
|
+
"duration": [1000],
|
|
150
|
+
"ox": [tile.tile_id % tileset.columns],
|
|
151
|
+
"oy": [tile.tile_id // tileset.columns],
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
sprites.setdefault(tile.graphic_state, {})
|
|
155
|
+
sprites[tile.graphic_state][tile.facing_direction] = data
|
|
156
|
+
LOG.debug(
|
|
157
|
+
{
|
|
158
|
+
"operation": "add frames",
|
|
159
|
+
"image": self.name,
|
|
160
|
+
"sprite": sprite_name,
|
|
161
|
+
"graphic_state": tile.graphic_state.name,
|
|
162
|
+
"direction": tile.facing_direction.name,
|
|
163
|
+
"frame_data": data,
|
|
164
|
+
}
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
AnimatedSprite.sprite_sets[sprite_name] = sprites
|
|
168
|
+
return sprites
|
|
169
|
+
# for tile in tileset.tiles
|
|
170
|
+
# Check non-animated tiles if necessary
|
|
171
|
+
|
|
172
|
+
def reset(self):
|
|
173
|
+
self._frame_index = 0
|
|
174
|
+
self._timer = 0.0
|
|
175
|
+
|
|
176
|
+
def _get_data(self, graphic_state, direction):
|
|
177
|
+
if graphic_state == GraphicState.DEFEATED:
|
|
178
|
+
graphic_state = graphic_state.DEAD
|
|
179
|
+
|
|
180
|
+
data = self._sprites.get(
|
|
181
|
+
graphic_state, self._sprites.get(GraphicState.STANDING, {})
|
|
182
|
+
)
|
|
183
|
+
data = data.get(
|
|
184
|
+
direction,
|
|
185
|
+
data.get(Direction.SOUTH, {}),
|
|
186
|
+
)
|
|
187
|
+
if not data:
|
|
188
|
+
try:
|
|
189
|
+
LOG.debug(
|
|
190
|
+
f"Animation of sprite {self._tileset_name,self._sprite_name}"
|
|
191
|
+
f" is empty for {graphic_state.name, direction.name} "
|
|
192
|
+
)
|
|
193
|
+
except Exception:
|
|
194
|
+
# print(graphic_state, direction)
|
|
195
|
+
LOG.exception(graphic_state, direction)
|
|
196
|
+
raise
|
|
197
|
+
data = {"ox": [0], "oy": [0], "duration": [1.0]}
|
|
198
|
+
return data
|