mima-engine 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mima/__init__.py +4 -0
- mima/backend/__init__.py +1 -0
- mima/backend/pygame_assets.py +401 -0
- mima/backend/pygame_audio.py +78 -0
- mima/backend/pygame_backend.py +603 -0
- mima/backend/pygame_camera.py +63 -0
- mima/backend/pygame_events.py +695 -0
- mima/backend/touch_control_scheme_a.py +126 -0
- mima/backend/touch_control_scheme_b.py +132 -0
- mima/core/__init__.py +0 -0
- mima/core/collision.py +325 -0
- mima/core/database.py +58 -0
- mima/core/engine.py +367 -0
- mima/core/mode_engine.py +81 -0
- mima/core/scene_engine.py +81 -0
- mima/integrated/__init__.py +0 -0
- mima/integrated/entity.py +183 -0
- mima/integrated/layered_map.py +351 -0
- mima/integrated/sprite.py +156 -0
- mima/layered/__init__.py +0 -0
- mima/layered/assets.py +56 -0
- mima/layered/scene.py +415 -0
- mima/layered/shape.py +99 -0
- mima/layered/shaped_sprite.py +78 -0
- mima/layered/virtual_input.py +302 -0
- mima/maps/__init__.py +0 -0
- mima/maps/template.py +71 -0
- mima/maps/tile.py +20 -0
- mima/maps/tile_animation.py +7 -0
- mima/maps/tile_info.py +10 -0
- mima/maps/tile_layer.py +52 -0
- mima/maps/tiled/__init__.py +0 -0
- mima/maps/tiled/tiled_layer.py +48 -0
- mima/maps/tiled/tiled_map.py +95 -0
- mima/maps/tiled/tiled_object.py +79 -0
- mima/maps/tiled/tiled_objectgroup.py +25 -0
- mima/maps/tiled/tiled_template.py +49 -0
- mima/maps/tiled/tiled_tile.py +90 -0
- mima/maps/tiled/tiled_tileset.py +51 -0
- mima/maps/tilemap.py +216 -0
- mima/maps/tileset.py +39 -0
- mima/maps/tileset_info.py +9 -0
- mima/maps/transition_map.py +146 -0
- mima/objects/__init__.py +0 -0
- mima/objects/animated_sprite.py +217 -0
- mima/objects/attribute_effect.py +26 -0
- mima/objects/attributes.py +126 -0
- mima/objects/creature.py +384 -0
- mima/objects/dynamic.py +206 -0
- mima/objects/effects/__init__.py +0 -0
- mima/objects/effects/colorize_screen.py +60 -0
- mima/objects/effects/debug_box.py +133 -0
- mima/objects/effects/light.py +103 -0
- mima/objects/effects/show_sprite.py +50 -0
- mima/objects/effects/walking_on_grass.py +70 -0
- mima/objects/effects/walking_on_water.py +57 -0
- mima/objects/loader.py +111 -0
- mima/objects/projectile.py +111 -0
- mima/objects/sprite.py +116 -0
- mima/objects/world/__init__.py +0 -0
- mima/objects/world/color_gate.py +67 -0
- mima/objects/world/color_switch.py +101 -0
- mima/objects/world/container.py +175 -0
- mima/objects/world/floor_switch.py +109 -0
- mima/objects/world/gate.py +178 -0
- mima/objects/world/light_source.py +121 -0
- mima/objects/world/logic_gate.py +157 -0
- mima/objects/world/movable.py +399 -0
- mima/objects/world/oneway.py +195 -0
- mima/objects/world/pickup.py +157 -0
- mima/objects/world/switch.py +179 -0
- mima/objects/world/teleport.py +308 -0
- mima/py.typed +0 -0
- mima/scripts/__init__.py +2 -0
- mima/scripts/command.py +38 -0
- mima/scripts/commands/__init__.py +0 -0
- mima/scripts/commands/add_quest.py +19 -0
- mima/scripts/commands/change_map.py +34 -0
- mima/scripts/commands/close_dialog.py +9 -0
- mima/scripts/commands/equip_weapon.py +23 -0
- mima/scripts/commands/give_item.py +26 -0
- mima/scripts/commands/give_resource.py +51 -0
- mima/scripts/commands/move_map.py +152 -0
- mima/scripts/commands/move_to.py +49 -0
- mima/scripts/commands/oneway_move.py +58 -0
- mima/scripts/commands/parallel.py +66 -0
- mima/scripts/commands/play_sound.py +13 -0
- mima/scripts/commands/present_item.py +53 -0
- mima/scripts/commands/progress_quest.py +12 -0
- mima/scripts/commands/quit_game.py +8 -0
- mima/scripts/commands/save_game.py +14 -0
- mima/scripts/commands/screen_fade.py +83 -0
- mima/scripts/commands/serial.py +69 -0
- mima/scripts/commands/set_facing_direction.py +21 -0
- mima/scripts/commands/set_spawn_map.py +17 -0
- mima/scripts/commands/show_choices.py +52 -0
- mima/scripts/commands/show_dialog.py +118 -0
- mima/scripts/commands/take_coins.py +23 -0
- mima/scripts/script_processor.py +61 -0
- mima/standalone/__init__.py +0 -0
- mima/standalone/camera.py +153 -0
- mima/standalone/geometry.py +1318 -0
- mima/standalone/multicolumn_list.py +54 -0
- mima/standalone/pixel_font.py +84 -0
- mima/standalone/scripting.py +145 -0
- mima/standalone/spatial.py +186 -0
- mima/standalone/sprite.py +158 -0
- mima/standalone/tiled_map.py +1247 -0
- mima/standalone/transformed_view.py +433 -0
- mima/standalone/user_input.py +563 -0
- mima/states/__init__.py +0 -0
- mima/states/game_state.py +189 -0
- mima/states/memory.py +28 -0
- mima/states/quest.py +71 -0
- mima/types/__init__.py +0 -0
- mima/types/alignment.py +7 -0
- mima/types/blend.py +8 -0
- mima/types/damage.py +42 -0
- mima/types/direction.py +44 -0
- mima/types/gate_color.py +7 -0
- mima/types/graphic_state.py +23 -0
- mima/types/keys.py +64 -0
- mima/types/mode.py +9 -0
- mima/types/nature.py +12 -0
- mima/types/object.py +22 -0
- mima/types/player.py +9 -0
- mima/types/position.py +13 -0
- mima/types/start.py +7 -0
- mima/types/terrain.py +9 -0
- mima/types/tile_collision.py +11 -0
- mima/types/weapon_slot.py +6 -0
- mima/types/window.py +44 -0
- mima/usables/__init__.py +0 -0
- mima/usables/item.py +51 -0
- mima/usables/weapon.py +68 -0
- mima/util/__init__.py +1 -0
- mima/util/colors.py +50 -0
- mima/util/constants.py +55 -0
- mima/util/functions.py +38 -0
- mima/util/input_defaults.py +170 -0
- mima/util/logging.py +51 -0
- mima/util/property.py +8 -0
- mima/util/runtime_config.py +327 -0
- mima/util/trading_item.py +23 -0
- mima/view/__init__.py +0 -0
- mima/view/camera.py +192 -0
- mima/view/mima_mode.py +618 -0
- mima/view/mima_scene.py +231 -0
- mima/view/mima_view.py +12 -0
- mima/view/mima_window.py +244 -0
- mima_engine-0.4.0.dist-info/METADATA +47 -0
- mima_engine-0.4.0.dist-info/RECORD +153 -0
- mima_engine-0.4.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from ...maps.tilemap import Tilemap
|
|
4
|
+
from ...objects.dynamic import Dynamic
|
|
5
|
+
from ...types.direction import Direction
|
|
6
|
+
from ...types.graphic_state import GraphicState
|
|
7
|
+
from ...types.nature import Nature
|
|
8
|
+
from ...types.object import ObjectType
|
|
9
|
+
from ...types.terrain import Terrain
|
|
10
|
+
from ...usables.item import Item
|
|
11
|
+
from ...util.colors import BLACK
|
|
12
|
+
from ..animated_sprite import AnimatedSprite
|
|
13
|
+
from ..effects.walking_on_grass import WalkingOnGrass
|
|
14
|
+
from ..effects.walking_on_water import WalkingOnWater
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Pickup(Dynamic):
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
px: float,
|
|
21
|
+
py: float,
|
|
22
|
+
item: Item,
|
|
23
|
+
*,
|
|
24
|
+
tilemap: Optional[Tilemap] = None,
|
|
25
|
+
dyn_id=0,
|
|
26
|
+
):
|
|
27
|
+
super().__init__(
|
|
28
|
+
px,
|
|
29
|
+
py,
|
|
30
|
+
f"Pickup({item.name})",
|
|
31
|
+
sprite_name=item.sprite_name,
|
|
32
|
+
tilemap=tilemap,
|
|
33
|
+
dyn_id=dyn_id,
|
|
34
|
+
)
|
|
35
|
+
self.type: ObjectType = ObjectType.PICKUP
|
|
36
|
+
self.item = item
|
|
37
|
+
# if not sprite_name:
|
|
38
|
+
# sprite_name = self.item.sprite_name
|
|
39
|
+
|
|
40
|
+
self.collected = False
|
|
41
|
+
self.solid_vs_dyn = False
|
|
42
|
+
self.moves_on_collision = True
|
|
43
|
+
# self.sprite = AnimatedSprite(
|
|
44
|
+
# self.item.tileset_name,
|
|
45
|
+
# self.item.image_name,
|
|
46
|
+
# self.item.sprite_name,
|
|
47
|
+
# graphic_state,
|
|
48
|
+
# facing_direction,
|
|
49
|
+
# )
|
|
50
|
+
|
|
51
|
+
def update(self, elapsed_time: float, target=None):
|
|
52
|
+
if self.collected:
|
|
53
|
+
self.kill()
|
|
54
|
+
else:
|
|
55
|
+
self._handle_terrain(elapsed_time)
|
|
56
|
+
self.sprite.update(
|
|
57
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
61
|
+
if self.collected:
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
pl = target.get_player()
|
|
65
|
+
if pl.value > 0:
|
|
66
|
+
if self.item.on_interaction(target):
|
|
67
|
+
if self.engine.give_item(self.item, pl):
|
|
68
|
+
self.collected = True
|
|
69
|
+
return True
|
|
70
|
+
else:
|
|
71
|
+
self.collected = True
|
|
72
|
+
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
def draw_self(
|
|
76
|
+
self,
|
|
77
|
+
ox: float,
|
|
78
|
+
oy: float,
|
|
79
|
+
camera_name: str = "display",
|
|
80
|
+
draw_to_ui: bool = False,
|
|
81
|
+
):
|
|
82
|
+
if self.collected:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
if self.pz != 0:
|
|
86
|
+
|
|
87
|
+
# print("Draw item circle")
|
|
88
|
+
self.engine.backend.fill_circle(
|
|
89
|
+
(self.px - ox + 0.5) * self.sprite.width,
|
|
90
|
+
(self.py - oy + 0.7) * self.sprite.height,
|
|
91
|
+
0.2875 * self.sprite.width,
|
|
92
|
+
BLACK,
|
|
93
|
+
camera_name,
|
|
94
|
+
)
|
|
95
|
+
self.sprite.draw_self(
|
|
96
|
+
self.px - ox,
|
|
97
|
+
self.py - oy - self.pz,
|
|
98
|
+
camera_name,
|
|
99
|
+
draw_to_ui=draw_to_ui,
|
|
100
|
+
)
|
|
101
|
+
# self.engine.backend.draw_partial_sprite(
|
|
102
|
+
# (self.px - ox) * self.item.sprite_width,
|
|
103
|
+
# (self.py - oy - self.pz) * self.item.sprite_height,
|
|
104
|
+
# self.item.sprite_name,
|
|
105
|
+
# self.item.sprite_ox * self.item.sprite_width,
|
|
106
|
+
# self.item.sprite_oy * self.item.sprite_height,
|
|
107
|
+
# self.item.sprite_width,
|
|
108
|
+
# self.item.sprite_height,
|
|
109
|
+
# camera_name,
|
|
110
|
+
# )
|
|
111
|
+
|
|
112
|
+
def _handle_terrain(self, elapsed_time: float):
|
|
113
|
+
"""Method is duplicated."""
|
|
114
|
+
e2rm = []
|
|
115
|
+
for effect in self.effects:
|
|
116
|
+
if isinstance(effect, WalkingOnGrass):
|
|
117
|
+
if self.walking_on == Terrain.DEFAULT:
|
|
118
|
+
e2rm.append(effect)
|
|
119
|
+
|
|
120
|
+
for effect in e2rm:
|
|
121
|
+
self.effects.remove(effect)
|
|
122
|
+
|
|
123
|
+
if self.walking_on in [Terrain.GRASS, Terrain.SHALLOW_WATER]:
|
|
124
|
+
# self.attributes.speed_mod = 0.7
|
|
125
|
+
effect_active = False
|
|
126
|
+
for effect in self.effects:
|
|
127
|
+
if isinstance(effect, (WalkingOnGrass, WalkingOnWater)):
|
|
128
|
+
effect_active = True
|
|
129
|
+
effect.renew = True
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
if not effect_active:
|
|
133
|
+
if self.walking_on == Terrain.GRASS:
|
|
134
|
+
eff = WalkingOnGrass(self)
|
|
135
|
+
else:
|
|
136
|
+
eff = WalkingOnWater(self)
|
|
137
|
+
self.effects.append(eff)
|
|
138
|
+
self.engine.get_view().add_effect(eff, self.tilemap.name)
|
|
139
|
+
# else:
|
|
140
|
+
# self.attributes.speed_mod = 1.0
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def load_from_tiled_object(obj, px, py, width, height, tilemap):
|
|
144
|
+
item = Pickup.engine.get_item(obj.get_string("usable_id"))
|
|
145
|
+
|
|
146
|
+
pickup = Pickup(
|
|
147
|
+
px,
|
|
148
|
+
py,
|
|
149
|
+
item,
|
|
150
|
+
tilemap=tilemap,
|
|
151
|
+
dyn_id=obj.object_id,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# pickup.sprite.width = int(width * Pickup.engine.rtc.tile_width)
|
|
155
|
+
# pickup.sprite.height = int(height * Pickup.engine.rtc.tile_height)
|
|
156
|
+
|
|
157
|
+
return [pickup]
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from typing import List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from ...maps.tilemap import Tilemap
|
|
4
|
+
from ...types.alignment import Alignment
|
|
5
|
+
from ...types.direction import Direction
|
|
6
|
+
from ...types.graphic_state import GraphicState
|
|
7
|
+
from ...types.nature import Nature
|
|
8
|
+
from ...types.object import ObjectType
|
|
9
|
+
from ..animated_sprite import AnimatedSprite
|
|
10
|
+
from ..dynamic import Dynamic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Switch(Dynamic):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
px: float,
|
|
17
|
+
py: float,
|
|
18
|
+
name="Switch",
|
|
19
|
+
*,
|
|
20
|
+
sprite_name: str = "",
|
|
21
|
+
tilemap: Optional[Tilemap] = None,
|
|
22
|
+
dyn_id=0,
|
|
23
|
+
graphic_state: GraphicState = GraphicState.OPEN,
|
|
24
|
+
initial_signal=True,
|
|
25
|
+
):
|
|
26
|
+
if graphic_state not in [GraphicState.OPEN, GraphicState.CLOSED]:
|
|
27
|
+
msg = (
|
|
28
|
+
f"graphic_state of Switch {name}{dyn_id} must be either "
|
|
29
|
+
f" 'open' or 'closed', but it is {graphic_state.name}"
|
|
30
|
+
)
|
|
31
|
+
raise ValueError(msg)
|
|
32
|
+
|
|
33
|
+
super().__init__(
|
|
34
|
+
px,
|
|
35
|
+
py,
|
|
36
|
+
name,
|
|
37
|
+
tilemap=tilemap,
|
|
38
|
+
sprite_name=sprite_name,
|
|
39
|
+
dyn_id=dyn_id,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
self.type = ObjectType.SWITCH
|
|
43
|
+
self.alignment = Alignment.NEUTRAL
|
|
44
|
+
self.graphic_state = graphic_state
|
|
45
|
+
self.signal = self.graphic_state == GraphicState.CLOSED
|
|
46
|
+
self.listener_ids: List[int] = []
|
|
47
|
+
self.listeners: List[Dynamic] = []
|
|
48
|
+
self.send_initial_signal = initial_signal
|
|
49
|
+
self.cooldown = 0.0
|
|
50
|
+
self.cooldown_reset = 0.8
|
|
51
|
+
|
|
52
|
+
# self._gs_map = {False: GraphicState.OPEN, True: GraphicState.CLOSED}
|
|
53
|
+
|
|
54
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
55
|
+
if self.cooldown > 0.0:
|
|
56
|
+
self.cooldown -= elapsed_time
|
|
57
|
+
|
|
58
|
+
if self.send_initial_signal:
|
|
59
|
+
self.send_signal(self.signal)
|
|
60
|
+
self.send_initial_signal = False
|
|
61
|
+
|
|
62
|
+
self.solid_vs_dyn = self.visible
|
|
63
|
+
self.graphic_state = (
|
|
64
|
+
GraphicState.CLOSED if self.signal else GraphicState.OPEN
|
|
65
|
+
)
|
|
66
|
+
self.sprite.update(
|
|
67
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
|
|
71
|
+
if not self.visible:
|
|
72
|
+
# print(f"{self.name} is not visible")
|
|
73
|
+
return
|
|
74
|
+
self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
|
|
75
|
+
|
|
76
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
77
|
+
# if target.is_player().value > 0:
|
|
78
|
+
# print(f"{target.is_player()} talked to me({self.name})")
|
|
79
|
+
if self.cooldown > 0.0:
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
if (
|
|
83
|
+
nature == Nature.TALK
|
|
84
|
+
and target.type == ObjectType.PLAYER
|
|
85
|
+
and self.visible
|
|
86
|
+
):
|
|
87
|
+
self.state_changed = True
|
|
88
|
+
self.engine.audio.play_sound("switch", 0.2)
|
|
89
|
+
self.cooldown = self.cooldown_reset
|
|
90
|
+
# print("Player talked")
|
|
91
|
+
|
|
92
|
+
elif nature == Nature.WALK and target.type == ObjectType.PROJECTILE:
|
|
93
|
+
if self.signal:
|
|
94
|
+
# Projectiles from the right will activate the switch
|
|
95
|
+
if target.px > self.px and target.vx < 0:
|
|
96
|
+
self.state_changed = True
|
|
97
|
+
if "body" not in target.name:
|
|
98
|
+
self.engine.audio.play_sound("switch", 0.2)
|
|
99
|
+
self.cooldown = self.cooldown_reset
|
|
100
|
+
else:
|
|
101
|
+
# Projectiles from the left will (de)activate the switch
|
|
102
|
+
if (
|
|
103
|
+
target.px <= self.px and target.vx > 0
|
|
104
|
+
): # Sword does not activate because vx=0
|
|
105
|
+
self.state_changed = True
|
|
106
|
+
if "body" not in target.name:
|
|
107
|
+
self.engine.audio.play_sound("switch", 0.2)
|
|
108
|
+
self.cooldown = self.cooldown_reset
|
|
109
|
+
|
|
110
|
+
elif nature == Nature.SIGNAL:
|
|
111
|
+
self.visible = False
|
|
112
|
+
return True
|
|
113
|
+
|
|
114
|
+
elif nature == Nature.NO_SIGNAL:
|
|
115
|
+
self.visible = True
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
if self.state_changed:
|
|
119
|
+
self.signal = not self.signal
|
|
120
|
+
self.send_signal(self.signal)
|
|
121
|
+
|
|
122
|
+
if target.type == ObjectType.PROJECTILE:
|
|
123
|
+
target.kill()
|
|
124
|
+
return True
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
def send_signal(self, nature: Union[Nature, bool]):
|
|
128
|
+
if isinstance(nature, bool):
|
|
129
|
+
nature = Nature.SIGNAL if nature else Nature.NO_SIGNAL
|
|
130
|
+
|
|
131
|
+
for listener in self.listeners:
|
|
132
|
+
listener.on_interaction(self, nature)
|
|
133
|
+
|
|
134
|
+
# if (
|
|
135
|
+
# not self.send_initial_signal
|
|
136
|
+
# and abs(self.engine.player.px - self.px)
|
|
137
|
+
# < (self.engine.backend.render_width // (TILE_WIDTH * 2))
|
|
138
|
+
# and abs(self.engine.player.py - self.py)
|
|
139
|
+
# < (self.engine.backend.render_height // (TILE_HEIGHT * 2))
|
|
140
|
+
# ):
|
|
141
|
+
# print(
|
|
142
|
+
# (
|
|
143
|
+
# self.engine.player.px - self.px,
|
|
144
|
+
# self.engine.player.py - self.py,
|
|
145
|
+
# ),
|
|
146
|
+
# (
|
|
147
|
+
# self.engine.backend.render_width // (TILE_WIDTH * 2),
|
|
148
|
+
# self.engine.backend.render_height // (TILE_HEIGHT * 2),
|
|
149
|
+
# ),
|
|
150
|
+
# )
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def load_from_tiled_object(obj, px, py, width, height, tilemap):
|
|
154
|
+
switch = Switch(
|
|
155
|
+
px=px,
|
|
156
|
+
py=py,
|
|
157
|
+
name=obj.name,
|
|
158
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
159
|
+
tilemap=tilemap,
|
|
160
|
+
dyn_id=obj.object_id,
|
|
161
|
+
graphic_state=GraphicState[
|
|
162
|
+
obj.get_string("graphic_state", "closed").upper()
|
|
163
|
+
],
|
|
164
|
+
initial_signal=obj.get_bool("initial_signal", True),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# switch.sprite.width = int(width * Switch.engine.rtc.tile_width)
|
|
168
|
+
# switch.sprite.height = int(height * Switch.engine.rtc.tile_height)
|
|
169
|
+
|
|
170
|
+
ctr = 1
|
|
171
|
+
while True:
|
|
172
|
+
key = f"output{ctr}"
|
|
173
|
+
listener_id = obj.get_int(key, -1)
|
|
174
|
+
if listener_id < 0:
|
|
175
|
+
break
|
|
176
|
+
switch.listener_ids.append(listener_id)
|
|
177
|
+
ctr += 1
|
|
178
|
+
|
|
179
|
+
return [switch]
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
|
|
6
|
+
from ...maps.tilemap import Tilemap
|
|
7
|
+
from ...scripts.commands.change_map import CommandChangeMap
|
|
8
|
+
from ...scripts.commands.move_map import CommandMoveMap
|
|
9
|
+
from ...scripts.commands.parallel import CommandParallel
|
|
10
|
+
from ...scripts.commands.screen_fade import CommandScreenFade
|
|
11
|
+
from ...scripts.commands.serial import CommandSerial
|
|
12
|
+
from ...scripts.commands.set_facing_direction import CommandSetFacingDirection
|
|
13
|
+
from ...types.direction import Direction
|
|
14
|
+
from ...types.graphic_state import GraphicState
|
|
15
|
+
from ...types.nature import Nature
|
|
16
|
+
from ...types.object import ObjectType
|
|
17
|
+
from ...types.player import Player
|
|
18
|
+
from ..animated_sprite import AnimatedSprite
|
|
19
|
+
from ..dynamic import Dynamic
|
|
20
|
+
|
|
21
|
+
LOG = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Teleport(Dynamic):
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
px: float,
|
|
28
|
+
py: float,
|
|
29
|
+
name="Teleport",
|
|
30
|
+
*,
|
|
31
|
+
sprite_name: str = "str",
|
|
32
|
+
tilemap: Optional[Tilemap] = None,
|
|
33
|
+
dyn_id: int = -1,
|
|
34
|
+
facing_direction: Direction,
|
|
35
|
+
graphic_state: GraphicState,
|
|
36
|
+
dst_map_name: str,
|
|
37
|
+
dst_px: float,
|
|
38
|
+
dst_py: float,
|
|
39
|
+
direction: Direction,
|
|
40
|
+
invert_exit_direction: bool = False,
|
|
41
|
+
relative: bool = False,
|
|
42
|
+
sliding: bool = False,
|
|
43
|
+
vertical: bool = False,
|
|
44
|
+
):
|
|
45
|
+
super().__init__(
|
|
46
|
+
px,
|
|
47
|
+
py,
|
|
48
|
+
name,
|
|
49
|
+
sprite_name=sprite_name,
|
|
50
|
+
tilemap=tilemap,
|
|
51
|
+
dyn_id=dyn_id,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# self.sprite = AnimatedSprite(
|
|
55
|
+
# tileset_name,
|
|
56
|
+
# image_name,
|
|
57
|
+
# sprite_name,
|
|
58
|
+
# graphic_state,
|
|
59
|
+
# facing_direction,
|
|
60
|
+
# )
|
|
61
|
+
|
|
62
|
+
self.type = ObjectType.TELEPORT
|
|
63
|
+
self.graphic_state = graphic_state
|
|
64
|
+
self.facing_direction = facing_direction
|
|
65
|
+
self.solid_vs_dyn = False
|
|
66
|
+
self.solid_vs_map = False
|
|
67
|
+
|
|
68
|
+
self.dst_px: float = dst_px
|
|
69
|
+
self.dst_py: float = dst_py
|
|
70
|
+
self.dst_map_name: str = dst_map_name
|
|
71
|
+
self.has_sprite = self.sprite.name != ""
|
|
72
|
+
self.teleport_direction: Direction = direction
|
|
73
|
+
|
|
74
|
+
self.visible = True
|
|
75
|
+
self.invert_exit_direction = invert_exit_direction
|
|
76
|
+
self.relative = relative
|
|
77
|
+
self.sliding = sliding
|
|
78
|
+
self.vertical = vertical
|
|
79
|
+
self.triggered = False
|
|
80
|
+
self.sfx_on_trigger: str = ""
|
|
81
|
+
|
|
82
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
83
|
+
self.triggered = False
|
|
84
|
+
self.sprite.update(
|
|
85
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
89
|
+
if nature == Nature.SIGNAL:
|
|
90
|
+
self.visible = True
|
|
91
|
+
return True
|
|
92
|
+
if nature == Nature.NO_SIGNAL:
|
|
93
|
+
self.visible = False
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
if self.has_sprite and not self.visible:
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
pt = target.get_player()
|
|
100
|
+
if (
|
|
101
|
+
nature == Nature.WALK
|
|
102
|
+
and pt != Player.P0
|
|
103
|
+
and not self.engine.is_teleport_active(pt)
|
|
104
|
+
):
|
|
105
|
+
self.engine.trigger_teleport(True, pt)
|
|
106
|
+
dst_px = self.dst_px
|
|
107
|
+
dst_py = self.dst_py
|
|
108
|
+
dst_vx = 0
|
|
109
|
+
dst_vy = 0
|
|
110
|
+
|
|
111
|
+
dst_vx, dst_vy = Direction.to_velocity(self.teleport_direction)
|
|
112
|
+
# self.sliding = False
|
|
113
|
+
if self.sliding:
|
|
114
|
+
if dst_vx != 0:
|
|
115
|
+
dst_py = target.py
|
|
116
|
+
elif dst_vy != 0:
|
|
117
|
+
dst_px = target.px
|
|
118
|
+
|
|
119
|
+
# self.engine.script.add_command(
|
|
120
|
+
# CommandMoveMap(
|
|
121
|
+
# new_map_name=self.dst_map_name,
|
|
122
|
+
# obj=target,
|
|
123
|
+
# target_px=dst_px,
|
|
124
|
+
# target_py=dst_py,
|
|
125
|
+
# vx=dst_vx,
|
|
126
|
+
# vy=dst_vy,
|
|
127
|
+
# players=[pt],
|
|
128
|
+
# )
|
|
129
|
+
# )
|
|
130
|
+
# else:
|
|
131
|
+
if self.triggered:
|
|
132
|
+
return False
|
|
133
|
+
|
|
134
|
+
new_direction = target.facing_direction
|
|
135
|
+
if self.invert_exit_direction:
|
|
136
|
+
new_direction = Direction(
|
|
137
|
+
(target.facing_direction.value + 2) % 4
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
cmd = CommandSerial(
|
|
141
|
+
[
|
|
142
|
+
CommandScreenFade(),
|
|
143
|
+
CommandParallel(
|
|
144
|
+
[
|
|
145
|
+
CommandChangeMap(
|
|
146
|
+
self.dst_map_name, dst_px, dst_py
|
|
147
|
+
),
|
|
148
|
+
CommandSetFacingDirection(target, new_direction),
|
|
149
|
+
CommandScreenFade(
|
|
150
|
+
fadein=True, map_name=self.dst_map_name
|
|
151
|
+
),
|
|
152
|
+
]
|
|
153
|
+
),
|
|
154
|
+
]
|
|
155
|
+
)
|
|
156
|
+
cmd.set_players([pt])
|
|
157
|
+
self.engine.script.add_command(cmd)
|
|
158
|
+
|
|
159
|
+
if self.sfx_on_trigger:
|
|
160
|
+
self.engine.audio.play_sound(self.sfx_on_trigger)
|
|
161
|
+
|
|
162
|
+
self.triggered = True
|
|
163
|
+
return True
|
|
164
|
+
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
def draw_self(self, ox: float, oy: float, camera_name: str = "display"):
|
|
168
|
+
if not self.visible:
|
|
169
|
+
self.engine.backend.draw_circle(
|
|
170
|
+
(self.px + 0.5 - ox) * self.engine.rtc.tile_width,
|
|
171
|
+
(self.py + 0.5 - oy) * self.engine.rtc.tile_height,
|
|
172
|
+
0.5 * self.engine.rtc.tile_width,
|
|
173
|
+
self.engine.rtc.color_red,
|
|
174
|
+
camera_name,
|
|
175
|
+
)
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
if self.has_sprite:
|
|
179
|
+
self.sprite.draw_self(self.px - ox, self.py - oy, camera_name)
|
|
180
|
+
|
|
181
|
+
@staticmethod
|
|
182
|
+
def load_from_tiled_object(
|
|
183
|
+
obj, px, py, width, height, tilemap
|
|
184
|
+
) -> List[Teleport]:
|
|
185
|
+
sprite_name = obj.get_string("sprite_name")
|
|
186
|
+
graphic_state = GraphicState[
|
|
187
|
+
obj.get_string("graphic_state", "closed").upper()
|
|
188
|
+
]
|
|
189
|
+
facing_direction = Direction[
|
|
190
|
+
obj.get_string("facing_direction", "south").upper()
|
|
191
|
+
]
|
|
192
|
+
target_map = obj.get_string("target_map", "map1_c1")
|
|
193
|
+
invert_exit_direction = obj.get_bool("invert_exit_direction")
|
|
194
|
+
relative = obj.get_bool("relative", False)
|
|
195
|
+
sliding = obj.get_bool("sliding", False)
|
|
196
|
+
vertical = obj.get_bool("vertical", False)
|
|
197
|
+
layer = obj.get_int("layer", 1)
|
|
198
|
+
target_px = obj.get_float("target_px")
|
|
199
|
+
target_py = obj.get_float("target_py")
|
|
200
|
+
direction = Direction[obj.get_string("direction", "south").upper()]
|
|
201
|
+
teleports = []
|
|
202
|
+
if width > height and int(width) > 1:
|
|
203
|
+
num_horizontal = int(width)
|
|
204
|
+
for idx in range(num_horizontal):
|
|
205
|
+
from_px = px + idx
|
|
206
|
+
from_py = py
|
|
207
|
+
to_px = target_px + idx
|
|
208
|
+
to_py = target_py
|
|
209
|
+
|
|
210
|
+
LOG.debug(
|
|
211
|
+
"Adding a teleport at (%f, %f) to map %s (%f, %f).",
|
|
212
|
+
from_px,
|
|
213
|
+
from_py,
|
|
214
|
+
target_map,
|
|
215
|
+
to_px,
|
|
216
|
+
to_py,
|
|
217
|
+
)
|
|
218
|
+
teleports.append(
|
|
219
|
+
Teleport(
|
|
220
|
+
px=from_px,
|
|
221
|
+
py=from_py,
|
|
222
|
+
sprite_name=sprite_name,
|
|
223
|
+
graphic_state=graphic_state,
|
|
224
|
+
facing_direction=facing_direction,
|
|
225
|
+
dst_map_name=target_map,
|
|
226
|
+
dst_px=to_px,
|
|
227
|
+
dst_py=to_py,
|
|
228
|
+
direction=direction,
|
|
229
|
+
invert_exit_direction=invert_exit_direction,
|
|
230
|
+
relative=relative,
|
|
231
|
+
sliding=sliding,
|
|
232
|
+
vertical=False,
|
|
233
|
+
tilemap=tilemap,
|
|
234
|
+
dyn_id=obj.object_id,
|
|
235
|
+
name=obj.name,
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
teleports[-1].layer = layer
|
|
239
|
+
elif height > width and int(height) > 1:
|
|
240
|
+
num_vertical = int(height)
|
|
241
|
+
for idx in range(num_vertical):
|
|
242
|
+
from_px = px
|
|
243
|
+
from_py = py + idx
|
|
244
|
+
to_px = target_px
|
|
245
|
+
to_py = target_py if relative else from_py
|
|
246
|
+
|
|
247
|
+
LOG.debug(
|
|
248
|
+
"Adding a teleport at (%f, %f) to map %s (%f, %f).",
|
|
249
|
+
from_px,
|
|
250
|
+
from_py,
|
|
251
|
+
target_map,
|
|
252
|
+
to_px,
|
|
253
|
+
to_py,
|
|
254
|
+
)
|
|
255
|
+
teleports.append(
|
|
256
|
+
Teleport(
|
|
257
|
+
px=from_px,
|
|
258
|
+
py=from_py,
|
|
259
|
+
sprite_name=sprite_name,
|
|
260
|
+
graphic_state=graphic_state,
|
|
261
|
+
facing_direction=facing_direction,
|
|
262
|
+
dst_map_name=target_map,
|
|
263
|
+
dst_px=to_px,
|
|
264
|
+
dst_py=to_py,
|
|
265
|
+
direction=direction,
|
|
266
|
+
invert_exit_direction=invert_exit_direction,
|
|
267
|
+
relative=relative,
|
|
268
|
+
sliding=sliding,
|
|
269
|
+
vertical=True,
|
|
270
|
+
tilemap=tilemap,
|
|
271
|
+
dyn_id=obj.object_id,
|
|
272
|
+
name=obj.name,
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
teleports[-1].layer = layer
|
|
276
|
+
else:
|
|
277
|
+
LOG.debug(
|
|
278
|
+
"Adding a teleport at (%f, %f) to map %s (%f, %f).",
|
|
279
|
+
px,
|
|
280
|
+
py,
|
|
281
|
+
target_map,
|
|
282
|
+
target_px,
|
|
283
|
+
target_py,
|
|
284
|
+
)
|
|
285
|
+
teleports.append(
|
|
286
|
+
Teleport(
|
|
287
|
+
px=px,
|
|
288
|
+
py=py,
|
|
289
|
+
sprite_name=sprite_name,
|
|
290
|
+
graphic_state=graphic_state,
|
|
291
|
+
facing_direction=facing_direction,
|
|
292
|
+
dst_map_name=target_map,
|
|
293
|
+
dst_px=target_px,
|
|
294
|
+
dst_py=target_py,
|
|
295
|
+
direction=direction,
|
|
296
|
+
invert_exit_direction=invert_exit_direction,
|
|
297
|
+
relative=relative,
|
|
298
|
+
sliding=sliding,
|
|
299
|
+
vertical=vertical,
|
|
300
|
+
tilemap=tilemap,
|
|
301
|
+
dyn_id=obj.object_id,
|
|
302
|
+
name=obj.name,
|
|
303
|
+
)
|
|
304
|
+
)
|
|
305
|
+
teleports[-1].sfx_on_trigger = obj.get_string("sfx_name")
|
|
306
|
+
teleports[-1].layer = layer
|
|
307
|
+
|
|
308
|
+
return teleports
|
mima/py.typed
ADDED
|
File without changes
|
mima/scripts/__init__.py
ADDED
mima/scripts/command.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
|
4
|
+
|
|
5
|
+
from ..types.player import Player
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ..engine import MimaEngine
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Command:
|
|
12
|
+
engine: MimaEngine
|
|
13
|
+
|
|
14
|
+
def __init__(self, players: Optional[List[Player]] = None):
|
|
15
|
+
self.started: bool = False
|
|
16
|
+
self.completed: bool = False
|
|
17
|
+
self.uninterruptible: bool = False
|
|
18
|
+
self.players: List[Player] = (
|
|
19
|
+
players if (players is not None and players) else [Player.P0]
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def start(self):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def update(self, elapsed_time: float):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def can_complete(self, force: bool = False) -> bool:
|
|
29
|
+
if self.uninterruptible and not force:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
return True
|
|
33
|
+
|
|
34
|
+
def finalize(self):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def set_players(self, players: List[Player]):
|
|
38
|
+
self.players = players
|
|
File without changes
|