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,107 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from ...types.alignment import Alignment
|
|
4
|
+
from ...types.blend import Blend
|
|
5
|
+
from ...util.colors import BLACK, DARK_GREY, VERY_LIGHT_GREY, WHITE
|
|
6
|
+
from ...util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
7
|
+
from ..dynamic import Dynamic
|
|
8
|
+
from ..projectile import Projectile
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Light(Projectile):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
follow: Dynamic,
|
|
15
|
+
max_size: int = 64,
|
|
16
|
+
fixed_size: bool = False,
|
|
17
|
+
update_from_target: bool = False,
|
|
18
|
+
):
|
|
19
|
+
super().__init__(0, 0, 0, 0, 0, Alignment.GOOD)
|
|
20
|
+
self.layer = 1
|
|
21
|
+
self.sprite.name = "light_small"
|
|
22
|
+
self.sprite.width = 48
|
|
23
|
+
self.sprite.height = 48
|
|
24
|
+
self.solid_vs_map = False
|
|
25
|
+
self._follow: Dynamic = follow
|
|
26
|
+
self._fixed_size: bool = fixed_size
|
|
27
|
+
self._update_from_target: bool = update_from_target
|
|
28
|
+
|
|
29
|
+
self._timer: float = 0.2
|
|
30
|
+
self._timer_reset: float = 0.2
|
|
31
|
+
self._size_idx: int = 0
|
|
32
|
+
|
|
33
|
+
self._sizes: List[int]
|
|
34
|
+
self._max_size: int
|
|
35
|
+
|
|
36
|
+
self._prepare_light(max_size)
|
|
37
|
+
|
|
38
|
+
# print(f"Light({id(self)}, {self.layer}, {self.sprite.name}, {self.sprite.width, self.sprite.height}, {self._follow}, {self._max_size}, {self._fixed_size}, {self._sizes}, {self._size_idx})")
|
|
39
|
+
|
|
40
|
+
def update(self, elapsed_time: float, target: Dynamic = None):
|
|
41
|
+
self.px = (
|
|
42
|
+
self._follow.px + self._follow.sprite.width / TILE_WIDTH * 0.5
|
|
43
|
+
)
|
|
44
|
+
self.py = (
|
|
45
|
+
self._follow.py + self._follow.sprite.height / TILE_HEIGHT * 0.5
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
rad = self._follow.light_radius()
|
|
49
|
+
if self._max_size != rad:
|
|
50
|
+
self._prepare_light(rad)
|
|
51
|
+
|
|
52
|
+
if self._follow.redundant:
|
|
53
|
+
self.kill()
|
|
54
|
+
|
|
55
|
+
if self._fixed_size:
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
self._timer -= elapsed_time
|
|
59
|
+
if self._timer <= 0.0:
|
|
60
|
+
self._timer += self._timer_reset
|
|
61
|
+
self._size_idx = (self._size_idx + 1) % len(self._sizes)
|
|
62
|
+
|
|
63
|
+
# self.sprite.update(elapsed_time, self.facing_direction, self.graphic_state)
|
|
64
|
+
|
|
65
|
+
def draw_self(self, ox: float, oy: float):
|
|
66
|
+
# color = Color(self.color.red, self.color.green, self.color.blue, self.alpha)
|
|
67
|
+
# self.engine.backend.fill_rect(self.px, self.py, WIDTH, HEIGHT, color)
|
|
68
|
+
# self.engine.backend.draw_partial_sprite(
|
|
69
|
+
# (self.px - ox) * TILE_WIDTH,
|
|
70
|
+
# (self.py - oy) * TILE_WIDTH,
|
|
71
|
+
# self.sprite.name,
|
|
72
|
+
# 0,
|
|
73
|
+
# 0,
|
|
74
|
+
# self.sprite.width,
|
|
75
|
+
# self.sprite.height,
|
|
76
|
+
# draw_to_filter=True,
|
|
77
|
+
# )
|
|
78
|
+
# print(f"Drawing light {self} of {self._follow} at {(self.px - ox + self._follow.extra_ox) * TILE_WIDTH,(self.py - oy + self._follow.extra_oy) * TILE_HEIGHT}")
|
|
79
|
+
self.engine.backend.fill_circle(
|
|
80
|
+
(self.px - ox + self._follow.extra_ox) * TILE_WIDTH,
|
|
81
|
+
(self.py - oy + self._follow.extra_oy) * TILE_HEIGHT,
|
|
82
|
+
self._sizes[self._size_idx] * 0.65, # 0.3125 *
|
|
83
|
+
DARK_GREY,
|
|
84
|
+
blend_mode=Blend.SUB,
|
|
85
|
+
draw_to_filter=True,
|
|
86
|
+
)
|
|
87
|
+
self.engine.backend.fill_circle(
|
|
88
|
+
(self.px - ox + self._follow.extra_ox) * TILE_WIDTH,
|
|
89
|
+
(self.py - oy + self._follow.extra_oy) * TILE_HEIGHT,
|
|
90
|
+
self._sizes[self._size_idx] * 0.5, # 0.3125 *
|
|
91
|
+
VERY_LIGHT_GREY,
|
|
92
|
+
blend_mode=Blend.SUB,
|
|
93
|
+
draw_to_filter=True,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def _prepare_light(self, max_size):
|
|
97
|
+
|
|
98
|
+
self._max_size = max_size
|
|
99
|
+
self._sizes = [max_size]
|
|
100
|
+
if not self._fixed_size:
|
|
101
|
+
self._sizes.extend(
|
|
102
|
+
[
|
|
103
|
+
int(max_size * 0.97),
|
|
104
|
+
int(max_size * 0.94),
|
|
105
|
+
int(max_size * 0.97),
|
|
106
|
+
]
|
|
107
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from ...types.direction import Direction
|
|
2
|
+
from ...types.graphic_state import GraphicState
|
|
3
|
+
from ..dynamic import Dynamic
|
|
4
|
+
from ..projectile import Projectile
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WalkingOnGrass(Projectile):
|
|
8
|
+
def __init__(self, follow: Dynamic):
|
|
9
|
+
super().__init__(follow.px, follow.py, 0, 0, 1.0, follow.alignment)
|
|
10
|
+
self.layer = 0
|
|
11
|
+
self.sprite.name = "simple_sheet"
|
|
12
|
+
self.sprite.ox = 32
|
|
13
|
+
self.sprite.oy = 10
|
|
14
|
+
self.sprite.num_frames = 2
|
|
15
|
+
self.sprite.timer = 0.2
|
|
16
|
+
self.sprite.timer_reset = 0.2
|
|
17
|
+
self._follow = follow
|
|
18
|
+
self.renew: bool = True
|
|
19
|
+
self.solid_vs_map = False
|
|
20
|
+
|
|
21
|
+
def update(self, elapsed_time: float, target: Dynamic = None):
|
|
22
|
+
if not self.renew:
|
|
23
|
+
self.kill()
|
|
24
|
+
|
|
25
|
+
self.px = self._follow.px
|
|
26
|
+
self.py = self._follow.py
|
|
27
|
+
|
|
28
|
+
if self._follow.graphic_state == GraphicState.STANDING:
|
|
29
|
+
elapsed_time = 0
|
|
30
|
+
self.sprite.update(elapsed_time, Direction.SOUTH, GraphicState.STANDING)
|
|
31
|
+
|
|
32
|
+
self.renew = False
|
|
33
|
+
|
|
34
|
+
def draw_self(self, ox: float, oy: float):
|
|
35
|
+
if self.sprite.name is None or self.sprite.name == "" or self.redundant:
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
self.sprite.draw_self(self.px - ox, self.py - oy)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from ...types.direction import Direction
|
|
2
|
+
from ...types.graphic_state import GraphicState
|
|
3
|
+
from ..dynamic import Dynamic
|
|
4
|
+
from .walking_on_grass import WalkingOnGrass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class WalkingOnWater(WalkingOnGrass):
|
|
8
|
+
def __init__(self, follow: Dynamic):
|
|
9
|
+
super().__init__(follow)
|
|
10
|
+
self.sprite.name = "simple_sheet"
|
|
11
|
+
self.sprite.ox = 32
|
|
12
|
+
self.sprite.oy = 9
|
|
13
|
+
self.sprite.num_frames = 2
|
|
14
|
+
self.sprite.timer = 0.2
|
|
15
|
+
self.sprite.timer_reset = 0.2
|
|
16
|
+
self._follow = follow
|
|
17
|
+
self.renew: bool = True
|
|
18
|
+
self.solid_vs_map = False
|
|
19
|
+
|
|
20
|
+
def update(self, elapsed_time: float, target: Dynamic = None):
|
|
21
|
+
if not self.renew:
|
|
22
|
+
self.kill()
|
|
23
|
+
|
|
24
|
+
self.px = (
|
|
25
|
+
self._follow.px + self._follow.sprite.width / 2 - self.sprite.width / 2
|
|
26
|
+
)
|
|
27
|
+
self.py = (
|
|
28
|
+
self._follow.py + self._follow.sprite.height / 2 - self.sprite.width / 2
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
if self._follow.graphic_state == GraphicState.STANDING:
|
|
32
|
+
elapsed_time = 0
|
|
33
|
+
self.sprite.update(elapsed_time, Direction.SOUTH, GraphicState.STANDING)
|
|
34
|
+
|
|
35
|
+
self.renew = False
|
|
36
|
+
|
|
37
|
+
def draw_self(self, ox: float, oy: float):
|
|
38
|
+
if self.sprite.name is None or self.sprite.name == "" or self.redundant:
|
|
39
|
+
return
|
|
40
|
+
|
|
41
|
+
self.sprite.draw_self(self.px - ox, self.py - oy)
|
mima/objects/loader.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from ..types.object import ObjectType
|
|
4
|
+
from ..util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
5
|
+
from .world.container import Container
|
|
6
|
+
from .world.floor_switch import FloorSwitch
|
|
7
|
+
from .world.gate import Gate
|
|
8
|
+
from .world.light_source import LightSource
|
|
9
|
+
from .world.logic_gate import LogicGate
|
|
10
|
+
from .world.movable import Movable
|
|
11
|
+
from .world.oneway import Oneway
|
|
12
|
+
from .world.color_switch import ColorSwitch
|
|
13
|
+
from .world.color_gate import ColorGate
|
|
14
|
+
|
|
15
|
+
# from .world.pickup import Pickup
|
|
16
|
+
from .world.switch import Switch
|
|
17
|
+
from .world.teleport import Teleport
|
|
18
|
+
|
|
19
|
+
LOG = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ObjectLoader:
|
|
23
|
+
def __init__(self, object_dispatcher, creature_dispatcher):
|
|
24
|
+
self._base_dispatcher = {
|
|
25
|
+
"color_switch": ColorSwitch.load_from_tiled_object,
|
|
26
|
+
"color_gate": ColorGate.load_from_tiled_object,
|
|
27
|
+
"container": Container.load_from_tiled_object,
|
|
28
|
+
"floor_switch": FloorSwitch.load_from_tiled_object,
|
|
29
|
+
"gate": Gate.load_from_tiled_object,
|
|
30
|
+
"light_source": LightSource.load_from_tiled_object,
|
|
31
|
+
"logic_gate": LogicGate.load_from_tiled_object,
|
|
32
|
+
"movable": Movable.load_from_tiled_object,
|
|
33
|
+
"oneway": Oneway.load_from_tiled_object,
|
|
34
|
+
"switch": Switch.load_from_tiled_object,
|
|
35
|
+
"teleport": Teleport.load_from_tiled_object,
|
|
36
|
+
"creature": self.load_creature_from_tiled_object,
|
|
37
|
+
}
|
|
38
|
+
self._cstm_object_dispatcher = object_dispatcher
|
|
39
|
+
self._cstm_creature_dispatcher = creature_dispatcher
|
|
40
|
+
# self._current_map = None
|
|
41
|
+
|
|
42
|
+
def populate_dynamics(self, tilemap, dynamics):
|
|
43
|
+
# self._current_map = tilemap
|
|
44
|
+
for obj in tilemap.objects:
|
|
45
|
+
px = obj.px / TILE_WIDTH
|
|
46
|
+
py = obj.py / TILE_HEIGHT
|
|
47
|
+
width = max(obj.width / TILE_WIDTH, 1.0)
|
|
48
|
+
height = max(obj.height / TILE_HEIGHT, 1.0)
|
|
49
|
+
|
|
50
|
+
LOG.debug(
|
|
51
|
+
f"Loading object {obj.name} ({obj.object_id}): {obj.type}) "
|
|
52
|
+
f"at {px, py}."
|
|
53
|
+
)
|
|
54
|
+
if obj.type in self._cstm_object_dispatcher:
|
|
55
|
+
dispatcher = self._cstm_object_dispatcher
|
|
56
|
+
else:
|
|
57
|
+
dispatcher = self._base_dispatcher
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
dynamics.extend(
|
|
61
|
+
dispatcher[obj.type](obj, px, py, width, height)
|
|
62
|
+
)
|
|
63
|
+
except (KeyError, ValueError):
|
|
64
|
+
LOG.exception(
|
|
65
|
+
f"Failed to load '{obj.name}' ({obj.object_id}: {obj.type})"
|
|
66
|
+
)
|
|
67
|
+
raise
|
|
68
|
+
|
|
69
|
+
# Connect listener IDs to actual listeners
|
|
70
|
+
for dyn in dynamics:
|
|
71
|
+
if dyn.type in [
|
|
72
|
+
ObjectType.SWITCH,
|
|
73
|
+
ObjectType.FLOOR_SWITCH,
|
|
74
|
+
ObjectType.LOGIC_GATE,
|
|
75
|
+
]:
|
|
76
|
+
for listener_id in dyn.listener_ids:
|
|
77
|
+
for listener in dynamics:
|
|
78
|
+
if listener.dyn_id == listener_id:
|
|
79
|
+
dyn.listeners.append(listener)
|
|
80
|
+
|
|
81
|
+
if listener.type == ObjectType.LOGIC_GATE:
|
|
82
|
+
if listener.input_id1 == -1:
|
|
83
|
+
listener.input_id1 = dyn.dyn_id
|
|
84
|
+
elif listener.input_id2 == -1:
|
|
85
|
+
listener.input_id2 = dyn.dyn_id
|
|
86
|
+
else:
|
|
87
|
+
LOG.warning(
|
|
88
|
+
f"Object with ID {dyn.dyn_id} "
|
|
89
|
+
"has no free input port at "
|
|
90
|
+
f"Logic Gate {listener.dyn_id}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def load_creature_from_tiled_object(self, obj, px, py, width, height):
|
|
94
|
+
creature_type = obj.get_string("creature_type")
|
|
95
|
+
try:
|
|
96
|
+
return self._cstm_creature_dispatcher[creature_type](
|
|
97
|
+
obj, px, py, width, height
|
|
98
|
+
)
|
|
99
|
+
except (KeyError, ValueError):
|
|
100
|
+
LOG.exception(
|
|
101
|
+
f"Failed to load '{obj.name}' ({obj.object_id}: {obj.type})"
|
|
102
|
+
)
|
|
103
|
+
raise
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from ..types.alignment import Alignment
|
|
2
|
+
from ..types.damage import Damage
|
|
3
|
+
from ..types.object import ObjectType
|
|
4
|
+
from .dynamic import Dynamic
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Projectile(Dynamic):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
px: float,
|
|
11
|
+
py: float,
|
|
12
|
+
vx: float,
|
|
13
|
+
vy: float,
|
|
14
|
+
duration: float,
|
|
15
|
+
alignment: Alignment,
|
|
16
|
+
name: str = "Projectile",
|
|
17
|
+
):
|
|
18
|
+
super().__init__(name, px, py)
|
|
19
|
+
|
|
20
|
+
self.type = ObjectType.PROJECTILE
|
|
21
|
+
self.vx = vx
|
|
22
|
+
self.vy = vy
|
|
23
|
+
self.pz = 0.5
|
|
24
|
+
self.duration = duration
|
|
25
|
+
self.alignment = alignment
|
|
26
|
+
|
|
27
|
+
self.solid_vs_dyn = False
|
|
28
|
+
self.solid_vs_map = True
|
|
29
|
+
self.is_projectile = True # redundant
|
|
30
|
+
self.attackable = False
|
|
31
|
+
self.one_hit = False
|
|
32
|
+
self.inherit_pos = False
|
|
33
|
+
self.gravity = False
|
|
34
|
+
self.change_solid_vs_map_timer = 0
|
|
35
|
+
self.dtype = Damage.BODY
|
|
36
|
+
self.damage = 0
|
|
37
|
+
|
|
38
|
+
def update(self, elapsed_time: float, target: Dynamic = None):
|
|
39
|
+
self.duration -= elapsed_time
|
|
40
|
+
if self.duration <= 0.0:
|
|
41
|
+
self.kill()
|
|
42
|
+
|
|
43
|
+
if self.change_solid_vs_map_timer > 0:
|
|
44
|
+
self.change_solid_vs_map_timer -= elapsed_time
|
|
45
|
+
if self.change_solid_vs_map_timer <= 0:
|
|
46
|
+
self.solid_vs_map = not self.solid_vs_map
|
|
47
|
+
self.change_solid_vs_map_timer = 0
|
|
48
|
+
|
|
49
|
+
self.speed = self.attributes.speed
|
|
50
|
+
|
|
51
|
+
self.sprite.update(
|
|
52
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def draw_self(self, ox: float, oy: float):
|
|
56
|
+
if (
|
|
57
|
+
self.sprite.name is None
|
|
58
|
+
or self.sprite.name == ""
|
|
59
|
+
or self.redundant
|
|
60
|
+
):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
self.sprite.draw_self(self.px - ox, self.py - oy)
|
|
64
|
+
|
|
65
|
+
def on_death(self) -> bool:
|
|
66
|
+
if self.spawn_on_death:
|
|
67
|
+
for do in self.spawn_on_death:
|
|
68
|
+
if do.type == ObjectType.PROJECTILE:
|
|
69
|
+
if self.inherit_pos:
|
|
70
|
+
do.px = self.px
|
|
71
|
+
do.py = self.py
|
|
72
|
+
self.engine.scene.add_projectile(do)
|
|
73
|
+
else:
|
|
74
|
+
self.engine.scene.add_dynamic(do)
|
|
75
|
+
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def cancel(self):
|
|
79
|
+
if self.spawn_on_death:
|
|
80
|
+
for do in self.spawn_on_death:
|
|
81
|
+
if do.type == ObjectType.PROJECTILE:
|
|
82
|
+
do.cancel() # Projectile will kill itself
|
|
83
|
+
else:
|
|
84
|
+
do.kill()
|
|
85
|
+
self.kill()
|
|
86
|
+
return True
|
mima/objects/sprite.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
from ..types.direction import Direction
|
|
2
|
+
from ..types.graphic_state import GraphicState
|
|
3
|
+
from ..util.constants import (
|
|
4
|
+
DEFAULT_GRAPHIC_TIMER,
|
|
5
|
+
DEFAULT_GRAPHIC_TIMER_DAMAGED,
|
|
6
|
+
DEFAULT_GRAPHIC_TIMER_DEAD,
|
|
7
|
+
DEFAULT_GRAPHIC_TIMER_STANDING,
|
|
8
|
+
DEFAULT_GRAPHIC_TIMER_WALKING,
|
|
9
|
+
DEFAULT_SPRITE_HEIGHT,
|
|
10
|
+
DEFAULT_SPRITE_WIDTH,
|
|
11
|
+
TILE_HEIGHT,
|
|
12
|
+
TILE_WIDTH,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Sprite:
|
|
17
|
+
engine = None
|
|
18
|
+
|
|
19
|
+
def __init__(self, name: str = ""):
|
|
20
|
+
self.name = name
|
|
21
|
+
self.ox: int = 0
|
|
22
|
+
self.oy: int = 0
|
|
23
|
+
self.width: int = DEFAULT_SPRITE_WIDTH
|
|
24
|
+
self.height: int = DEFAULT_SPRITE_HEIGHT
|
|
25
|
+
self.num_frames: int = 1
|
|
26
|
+
self.frame_index: int = 0
|
|
27
|
+
|
|
28
|
+
self.timer: float = 0.25
|
|
29
|
+
self.timer_reset: float = 0.5
|
|
30
|
+
|
|
31
|
+
self.last_direction: Direction = Direction.SOUTH
|
|
32
|
+
self.last_graphic_state: GraphicState = GraphicState.STANDING
|
|
33
|
+
|
|
34
|
+
def update(
|
|
35
|
+
self,
|
|
36
|
+
elapsed_time: float,
|
|
37
|
+
direction: Direction = Direction.SOUTH,
|
|
38
|
+
graphic_state: GraphicState = GraphicState.STANDING,
|
|
39
|
+
):
|
|
40
|
+
if (
|
|
41
|
+
direction == self.last_direction
|
|
42
|
+
and graphic_state == self.last_graphic_state
|
|
43
|
+
):
|
|
44
|
+
# Nothing has changed, normal case
|
|
45
|
+
self.timer -= elapsed_time
|
|
46
|
+
|
|
47
|
+
if self.timer <= 0.0:
|
|
48
|
+
self.timer += self.timer_reset
|
|
49
|
+
self.frame_index = (self.frame_index + 1) % self.num_frames
|
|
50
|
+
|
|
51
|
+
else:
|
|
52
|
+
# Something changed
|
|
53
|
+
if graphic_state != self.last_graphic_state:
|
|
54
|
+
# State changed
|
|
55
|
+
if graphic_state == GraphicState.STANDING:
|
|
56
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER_STANDING
|
|
57
|
+
elif graphic_state == GraphicState.WALKING:
|
|
58
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER_WALKING
|
|
59
|
+
elif graphic_state == GraphicState.DAMAGED:
|
|
60
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER_DAMAGED
|
|
61
|
+
elif graphic_state == GraphicState.DEAD:
|
|
62
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER_DEAD
|
|
63
|
+
elif graphic_state == GraphicState.PUSHING:
|
|
64
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER_WALKING
|
|
65
|
+
else:
|
|
66
|
+
self.timer_reset = DEFAULT_GRAPHIC_TIMER
|
|
67
|
+
|
|
68
|
+
# self.timer = self.timer_reset
|
|
69
|
+
# self.frame_index = 0
|
|
70
|
+
|
|
71
|
+
self.timer = self.timer_reset
|
|
72
|
+
self.frame_index = 0
|
|
73
|
+
|
|
74
|
+
self.last_direction = direction
|
|
75
|
+
self.last_graphic_state = graphic_state
|
|
76
|
+
|
|
77
|
+
def draw_self(self, px: float, py: float):
|
|
78
|
+
if self.name == "":
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
sheet_ox = sheet_oy = 0
|
|
82
|
+
state_value = self.last_graphic_state.value
|
|
83
|
+
|
|
84
|
+
if self.last_graphic_state == GraphicState.CELEBRATING:
|
|
85
|
+
sheet_ox = (self.ox + self.frame_index) * self.width
|
|
86
|
+
sheet_oy = (self.oy + state_value) * self.height
|
|
87
|
+
elif self.last_graphic_state == GraphicState.DEAD:
|
|
88
|
+
sheet_ox = (self.ox + 2 + self.frame_index) * self.width
|
|
89
|
+
sheet_oy = (self.oy + state_value) * self.height
|
|
90
|
+
else:
|
|
91
|
+
if self.last_graphic_state == GraphicState.PUSHING:
|
|
92
|
+
state_value -= 1
|
|
93
|
+
sheet_ox = (
|
|
94
|
+
self.ox + 2 * self.last_direction.value + self.frame_index
|
|
95
|
+
) * self.width
|
|
96
|
+
sheet_oy = (self.oy + state_value) * self.height
|
|
97
|
+
|
|
98
|
+
self.engine.backend.draw_partial_sprite(
|
|
99
|
+
px * TILE_WIDTH,
|
|
100
|
+
py * TILE_HEIGHT,
|
|
101
|
+
self.name,
|
|
102
|
+
sheet_ox,
|
|
103
|
+
sheet_oy,
|
|
104
|
+
self.width,
|
|
105
|
+
self.height,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def reset(self):
|
|
109
|
+
self.frame_index = 0
|
|
110
|
+
self.timer = 0.0
|
|
File without changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from ...types.direction import Direction
|
|
4
|
+
from ...types.gate_color import GateColor
|
|
5
|
+
from ...types.graphic_state import GraphicState
|
|
6
|
+
from ...types.nature import Nature
|
|
7
|
+
from ...types.object import ObjectType
|
|
8
|
+
from ..dynamic import Dynamic
|
|
9
|
+
from .gate import Gate
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ColorGate(Gate):
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
px: float,
|
|
17
|
+
py: float,
|
|
18
|
+
tileset_name: str,
|
|
19
|
+
image_name: str,
|
|
20
|
+
sprite_name: str,
|
|
21
|
+
graphic_state: GraphicState,
|
|
22
|
+
facing_direction: Direction,
|
|
23
|
+
color: GateColor,
|
|
24
|
+
dyn_id: int = -1,
|
|
25
|
+
name: str = "ColorGate",
|
|
26
|
+
):
|
|
27
|
+
super().__init__(
|
|
28
|
+
px,
|
|
29
|
+
py,
|
|
30
|
+
tileset_name,
|
|
31
|
+
image_name,
|
|
32
|
+
sprite_name,
|
|
33
|
+
graphic_state,
|
|
34
|
+
facing_direction,
|
|
35
|
+
False,
|
|
36
|
+
dyn_id,
|
|
37
|
+
name,
|
|
38
|
+
)
|
|
39
|
+
self.color = color
|
|
40
|
+
self.type = ObjectType.COLOR_GATE
|
|
41
|
+
|
|
42
|
+
def update(self, elapsed_time, float, target: Optional[Dynamic] = None):
|
|
43
|
+
self.open = self.engine.gate_color == self.color
|
|
44
|
+
super().update(elapsed_time, target)
|
|
45
|
+
|
|
46
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
47
|
+
return False
|
|
48
|
+
|
|
49
|
+
@staticmethod
|
|
50
|
+
def load_from_tiled_object(obj, px, py, width, height):
|
|
51
|
+
gate = ColorGate(
|
|
52
|
+
px=px,
|
|
53
|
+
py=py,
|
|
54
|
+
tileset_name=obj.get_string("tileset_name"),
|
|
55
|
+
image_name=obj.get_string("tileset_name"),
|
|
56
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
57
|
+
graphic_state=GraphicState[
|
|
58
|
+
obj.get_string("graphic_state", "closed").upper()
|
|
59
|
+
],
|
|
60
|
+
facing_direction=Direction[
|
|
61
|
+
obj.get_string("facing_direction", "south").upper()
|
|
62
|
+
],
|
|
63
|
+
color=GateColor[obj.get_string("color", "red").upper()],
|
|
64
|
+
dyn_id=obj.object_id,
|
|
65
|
+
name=obj.name,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return [gate]
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from ...types.direction import Direction
|
|
4
|
+
from ...types.gate_color import GateColor
|
|
5
|
+
from ...types.graphic_state import GraphicState
|
|
6
|
+
from ...types.nature import Nature
|
|
7
|
+
from ...types.object import ObjectType
|
|
8
|
+
from ...util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
9
|
+
from ..dynamic import Dynamic
|
|
10
|
+
from .switch import Switch
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ColorSwitch(Switch):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
px: float,
|
|
17
|
+
py: float,
|
|
18
|
+
tileset_name: str,
|
|
19
|
+
image_name: str,
|
|
20
|
+
sprite_name: str,
|
|
21
|
+
facing_direction: Direction,
|
|
22
|
+
graphic_state: GraphicState,
|
|
23
|
+
color: GateColor,
|
|
24
|
+
initial_signal=True,
|
|
25
|
+
dyn_id=-1,
|
|
26
|
+
name="ColorSwitch",
|
|
27
|
+
):
|
|
28
|
+
|
|
29
|
+
super().__init__(
|
|
30
|
+
px,
|
|
31
|
+
py,
|
|
32
|
+
tileset_name,
|
|
33
|
+
image_name,
|
|
34
|
+
sprite_name,
|
|
35
|
+
facing_direction,
|
|
36
|
+
graphic_state,
|
|
37
|
+
initial_signal,
|
|
38
|
+
dyn_id,
|
|
39
|
+
name,
|
|
40
|
+
)
|
|
41
|
+
self.type = ObjectType.COLOR_SWITCH
|
|
42
|
+
|
|
43
|
+
self.color = color
|
|
44
|
+
self.graphic_state = (
|
|
45
|
+
GraphicState.CLOSED
|
|
46
|
+
if color == self.engine.gate_color
|
|
47
|
+
else GraphicState.OPEN
|
|
48
|
+
)
|
|
49
|
+
# self.signal = self.graphic_state == GraphicState.CLOSED
|
|
50
|
+
self.send_initial_signal = False
|
|
51
|
+
|
|
52
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
53
|
+
self.graphic_state = (
|
|
54
|
+
GraphicState.CLOSED
|
|
55
|
+
if self.color == self.engine.gate_color
|
|
56
|
+
else GraphicState.OPEN
|
|
57
|
+
)
|
|
58
|
+
# self.signal = self.graphic_state == GraphicState.CLOSED
|
|
59
|
+
#
|
|
60
|
+
self.sprite.update(
|
|
61
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
65
|
+
if (
|
|
66
|
+
nature == Nature.TALK
|
|
67
|
+
and target.type == ObjectType.PLAYER
|
|
68
|
+
and self.visible
|
|
69
|
+
):
|
|
70
|
+
if self.engine.gate_color != self.color:
|
|
71
|
+
self.engine.gate_color = self.color
|
|
72
|
+
else:
|
|
73
|
+
self.engine.gate_color = GateColor(
|
|
74
|
+
(self.color.value + 1) % self.engine.n_gate_colors
|
|
75
|
+
)
|
|
76
|
+
self.state_changed = True
|
|
77
|
+
self.engine.audio.play_sound("switch")
|
|
78
|
+
return True
|
|
79
|
+
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def load_from_tiled_object(obj, px, py, width, height):
|
|
84
|
+
switch = ColorSwitch(
|
|
85
|
+
px=px,
|
|
86
|
+
py=py,
|
|
87
|
+
tileset_name=obj.get_string("tileset_name"),
|
|
88
|
+
image_name=obj.get_string("tileset_name"),
|
|
89
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
90
|
+
graphic_state=GraphicState[
|
|
91
|
+
obj.get_string("graphic_state", "closed").upper()
|
|
92
|
+
],
|
|
93
|
+
facing_direction=Direction[
|
|
94
|
+
obj.get_string("facing_direction", "south").upper()
|
|
95
|
+
],
|
|
96
|
+
color=GateColor[obj.get_string("color", "red").upper()],
|
|
97
|
+
initial_signal=False,
|
|
98
|
+
dyn_id=obj.object_id,
|
|
99
|
+
name=obj.name,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
switch.sprite.width = int(width * TILE_WIDTH)
|
|
103
|
+
switch.sprite.height = int(height * TILE_HEIGHT)
|
|
104
|
+
|
|
105
|
+
return [switch]
|