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,163 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
|
+
|
|
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 ...util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
10
|
+
from ..animated_sprite import AnimatedSprite
|
|
11
|
+
from ..dynamic import Dynamic
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LogicFunction(Enum):
|
|
15
|
+
LOGIC_PASS = 0
|
|
16
|
+
LOGIC_NOT = 1
|
|
17
|
+
LOGIC_AND = 2
|
|
18
|
+
LOGIC_OR = 3
|
|
19
|
+
LOGIC_NAND = 4
|
|
20
|
+
LOGIC_NOR = 5
|
|
21
|
+
LOGIC_XOR = 6
|
|
22
|
+
LOGIC_IMPL = 7
|
|
23
|
+
LOGIC_EQUI = 8
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class LogicGate(Dynamic):
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
px: float,
|
|
30
|
+
py: float,
|
|
31
|
+
tileset_name: str,
|
|
32
|
+
image_name: str,
|
|
33
|
+
sprite_name: str,
|
|
34
|
+
graphic_state: GraphicState,
|
|
35
|
+
facing_direction: Direction,
|
|
36
|
+
mode=LogicFunction.LOGIC_PASS,
|
|
37
|
+
initial_signal=False,
|
|
38
|
+
visible=False,
|
|
39
|
+
dyn_id=-1,
|
|
40
|
+
name="Logic Gate",
|
|
41
|
+
):
|
|
42
|
+
super().__init__(name, px, py, dyn_id)
|
|
43
|
+
|
|
44
|
+
self.sprite = AnimatedSprite(
|
|
45
|
+
tileset_name,
|
|
46
|
+
image_name,
|
|
47
|
+
sprite_name,
|
|
48
|
+
graphic_state,
|
|
49
|
+
facing_direction,
|
|
50
|
+
)
|
|
51
|
+
self.type = ObjectType.LOGIC_GATE
|
|
52
|
+
self.alignment = Alignment.NEUTRAL
|
|
53
|
+
self.graphic_state = graphic_state
|
|
54
|
+
self.facing_direction = facing_direction
|
|
55
|
+
self.visible = visible
|
|
56
|
+
|
|
57
|
+
self.input_id1: int = -1
|
|
58
|
+
self.input_id2: int = -1
|
|
59
|
+
|
|
60
|
+
self.mode = mode
|
|
61
|
+
self.signal: bool = False
|
|
62
|
+
self.last_signal: bool = False
|
|
63
|
+
self.input1: bool = False
|
|
64
|
+
self.input2: bool = False
|
|
65
|
+
|
|
66
|
+
self.listener_ids: List[int] = []
|
|
67
|
+
self.listeners: List[Dynamic] = []
|
|
68
|
+
self.send_initial_signal: bool = initial_signal
|
|
69
|
+
self.state_changed: bool = False
|
|
70
|
+
|
|
71
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
72
|
+
if self.mode == LogicFunction.LOGIC_NOT:
|
|
73
|
+
self.signal = not self.input1
|
|
74
|
+
elif self.mode == LogicFunction.LOGIC_AND:
|
|
75
|
+
self.signal = self.input1 and self.input2
|
|
76
|
+
elif self.mode == LogicFunction.LOGIC_OR:
|
|
77
|
+
self.signal = self.input1 or self.input2
|
|
78
|
+
else:
|
|
79
|
+
self.signal = self.input1
|
|
80
|
+
|
|
81
|
+
if self.last_signal != self.signal:
|
|
82
|
+
self.state_changed = True
|
|
83
|
+
else:
|
|
84
|
+
self.state_changed = False
|
|
85
|
+
|
|
86
|
+
if self.send_initial_signal or self.state_changed:
|
|
87
|
+
self.send_signal(self.signal)
|
|
88
|
+
self.send_initial_signal = False
|
|
89
|
+
|
|
90
|
+
self.last_signal = self.signal
|
|
91
|
+
|
|
92
|
+
self.graphic_state = (
|
|
93
|
+
GraphicState.ON if self.signal else GraphicState.OFF
|
|
94
|
+
)
|
|
95
|
+
self.sprite.update(
|
|
96
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
100
|
+
if nature == Nature.SIGNAL:
|
|
101
|
+
if target.dyn_id == self.input_id1:
|
|
102
|
+
self.input1 = True
|
|
103
|
+
elif target.dyn_id == self.input_id2:
|
|
104
|
+
self.input2 = True
|
|
105
|
+
else:
|
|
106
|
+
return False
|
|
107
|
+
return True
|
|
108
|
+
elif nature == Nature.NO_SIGNAL:
|
|
109
|
+
if target.dyn_id == self.input_id1:
|
|
110
|
+
self.input1 = False
|
|
111
|
+
elif target.dyn_id == self.input_id2:
|
|
112
|
+
self.input2 = False
|
|
113
|
+
else:
|
|
114
|
+
return False
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
def draw_self(self, ox: float, oy: float):
|
|
120
|
+
if self.visible:
|
|
121
|
+
self.sprite.draw_self(self.px - ox, self.py - oy)
|
|
122
|
+
|
|
123
|
+
def send_signal(self, nature: Union[Nature, bool]):
|
|
124
|
+
if isinstance(nature, bool):
|
|
125
|
+
nature = Nature.SIGNAL if nature else Nature.NO_SIGNAL
|
|
126
|
+
|
|
127
|
+
for listener in self.listeners:
|
|
128
|
+
listener.on_interaction(self, nature)
|
|
129
|
+
|
|
130
|
+
@staticmethod
|
|
131
|
+
def load_from_tiled_object(obj, px, py, width, height):
|
|
132
|
+
logic = LogicGate(
|
|
133
|
+
px=px,
|
|
134
|
+
py=py,
|
|
135
|
+
tileset_name=obj.get_string("tileset_name"),
|
|
136
|
+
image_name=obj.get_string("tileset_name"),
|
|
137
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
138
|
+
graphic_state=GraphicState[
|
|
139
|
+
obj.get_string("graphic_state", "closed").upper()
|
|
140
|
+
],
|
|
141
|
+
facing_direction=Direction[
|
|
142
|
+
obj.get_string("facing_direction", "south").upper()
|
|
143
|
+
],
|
|
144
|
+
mode=LogicFunction[obj.get_string("mode", "logic_pass").upper()],
|
|
145
|
+
initial_signal=obj.get_bool("initial_signal", False),
|
|
146
|
+
visible=obj.get_bool("visible", True),
|
|
147
|
+
dyn_id=obj.object_id,
|
|
148
|
+
name=obj.name,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# logic.send_initial_signal =
|
|
152
|
+
logic.sprite.width = int(width * TILE_WIDTH)
|
|
153
|
+
logic.sprite.height = int(height * TILE_HEIGHT)
|
|
154
|
+
|
|
155
|
+
ctr = 1
|
|
156
|
+
while True:
|
|
157
|
+
listener_id = obj.get_int(f"output{ctr}", -1)
|
|
158
|
+
if listener_id < 0:
|
|
159
|
+
break
|
|
160
|
+
logic.listener_ids.append(listener_id)
|
|
161
|
+
ctr += 1
|
|
162
|
+
|
|
163
|
+
return [logic]
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from ...types.alignment import Alignment
|
|
5
|
+
from ...types.damage import Damage
|
|
6
|
+
from ...types.direction import Direction
|
|
7
|
+
from ...types.graphic_state import GraphicState
|
|
8
|
+
from ...types.keys import Key as K
|
|
9
|
+
from ...types.nature import Nature
|
|
10
|
+
from ...types.object import ObjectType
|
|
11
|
+
from ...util.colors import BLACK
|
|
12
|
+
from ...util.constants import TILE_HEIGHT, TILE_WIDTH
|
|
13
|
+
from ..animated_sprite import AnimatedSprite
|
|
14
|
+
from ..dynamic import Dynamic
|
|
15
|
+
from ..projectile import Projectile
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Movable(Dynamic):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
px: float,
|
|
22
|
+
py: float,
|
|
23
|
+
tileset_name: str,
|
|
24
|
+
image_name: str,
|
|
25
|
+
sprite_name: str,
|
|
26
|
+
facing_direction: Direction,
|
|
27
|
+
graphic_state: GraphicState,
|
|
28
|
+
mrange: float,
|
|
29
|
+
liftable: bool,
|
|
30
|
+
destroyable: bool,
|
|
31
|
+
movable: bool,
|
|
32
|
+
intangible: bool,
|
|
33
|
+
force_collision_check: bool,
|
|
34
|
+
dyn_id=-1,
|
|
35
|
+
name="Movable",
|
|
36
|
+
):
|
|
37
|
+
super().__init__(name, px, py, dyn_id)
|
|
38
|
+
|
|
39
|
+
self.sprite = AnimatedSprite(
|
|
40
|
+
tileset_name,
|
|
41
|
+
image_name,
|
|
42
|
+
sprite_name,
|
|
43
|
+
graphic_state,
|
|
44
|
+
facing_direction,
|
|
45
|
+
)
|
|
46
|
+
self.type = ObjectType.MOVABLE
|
|
47
|
+
self.alignment = Alignment.NEUTRAL
|
|
48
|
+
self.facing_direction = facing_direction
|
|
49
|
+
self.graphic_state = graphic_state
|
|
50
|
+
self.solid_vs_map = True
|
|
51
|
+
|
|
52
|
+
self.range = mrange
|
|
53
|
+
self.total_range = 0
|
|
54
|
+
self.spawn_px = px
|
|
55
|
+
self.spawn_py = py
|
|
56
|
+
|
|
57
|
+
self.liftable = liftable
|
|
58
|
+
self.destroyable = destroyable
|
|
59
|
+
self.movable = movable
|
|
60
|
+
self.intangible = intangible
|
|
61
|
+
self.moving = False
|
|
62
|
+
self.lift_started = False
|
|
63
|
+
self.lifted = False
|
|
64
|
+
self.thrown = False
|
|
65
|
+
self.visible = True
|
|
66
|
+
self.visible_pz = 0.0
|
|
67
|
+
self.actor: Optional[Dynamic] = None
|
|
68
|
+
self.vx_mask = 0
|
|
69
|
+
self.vy_mask = 0
|
|
70
|
+
self.move_direction: str = ""
|
|
71
|
+
self.onscreen_collision_skippable = (
|
|
72
|
+
not self.movable and not force_collision_check
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def update(self, elapsed_time: float, target: Optional[Dynamic] = None):
|
|
76
|
+
if self.intangible:
|
|
77
|
+
self.solid_vs_dyn = False
|
|
78
|
+
else:
|
|
79
|
+
self.solid_vs_dyn = (
|
|
80
|
+
self.visible and not self.lifted and not self.thrown
|
|
81
|
+
)
|
|
82
|
+
if self.pz > 1.0:
|
|
83
|
+
self.solid_vs_map = False
|
|
84
|
+
else:
|
|
85
|
+
self.solid_vs_map = True
|
|
86
|
+
|
|
87
|
+
self.sprite.update(
|
|
88
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if self.thrown:
|
|
92
|
+
return self._throw()
|
|
93
|
+
|
|
94
|
+
self.vx = self.vy = 0.0
|
|
95
|
+
|
|
96
|
+
if self.moving:
|
|
97
|
+
return self._move()
|
|
98
|
+
|
|
99
|
+
if self.lift_started or self.lifted:
|
|
100
|
+
return self._lift()
|
|
101
|
+
|
|
102
|
+
def on_interaction(self, target: Dynamic, nature: Nature):
|
|
103
|
+
if self.moving:
|
|
104
|
+
return False
|
|
105
|
+
if self.lifted:
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
if target.type == ObjectType.PLAYER:
|
|
109
|
+
if nature == Nature.TALK and self.liftable and target.can_lift:
|
|
110
|
+
self.lift_started = True
|
|
111
|
+
self.actor = target
|
|
112
|
+
self.solid_vs_dyn = False
|
|
113
|
+
target.can_attack = False
|
|
114
|
+
return True
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
self.movable
|
|
118
|
+
and self.visible
|
|
119
|
+
and self.total_range < self.range
|
|
120
|
+
and target.graphic_state
|
|
121
|
+
in [
|
|
122
|
+
GraphicState.WALKING,
|
|
123
|
+
GraphicState.PUSHING,
|
|
124
|
+
]
|
|
125
|
+
):
|
|
126
|
+
if (
|
|
127
|
+
target.facing_direction == Direction.WEST
|
|
128
|
+
and self.engine.keys.key_held(K.LEFT)
|
|
129
|
+
and target.vy == 0
|
|
130
|
+
):
|
|
131
|
+
self.move_direction = K.LEFT
|
|
132
|
+
self.vx_mask = -1
|
|
133
|
+
elif (
|
|
134
|
+
target.facing_direction == Direction.EAST
|
|
135
|
+
and self.engine.keys.key_held(K.RIGHT)
|
|
136
|
+
and target.vy == 0
|
|
137
|
+
):
|
|
138
|
+
self.move_direction = K.RIGHT
|
|
139
|
+
self.vx_mask = 1
|
|
140
|
+
elif (
|
|
141
|
+
target.facing_direction == Direction.SOUTH
|
|
142
|
+
and self.engine.keys.key_held(K.DOWN)
|
|
143
|
+
and target.vx == 0
|
|
144
|
+
):
|
|
145
|
+
self.move_direction = K.DOWN
|
|
146
|
+
self.vy_mask = 1
|
|
147
|
+
elif (
|
|
148
|
+
target.facing_direction == Direction.NORTH
|
|
149
|
+
and self.engine.keys.key_held(K.UP)
|
|
150
|
+
and target.vx == 0
|
|
151
|
+
):
|
|
152
|
+
self.move_direction = K.UP
|
|
153
|
+
self.vy_mask = -1
|
|
154
|
+
else:
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
self.actor = target
|
|
158
|
+
self.moving = True
|
|
159
|
+
self.actor.lock_graphic_state(GraphicState.PUSHING)
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
elif target.type == ObjectType.PROJECTILE:
|
|
163
|
+
if self.destroyable:
|
|
164
|
+
damage = target.damage - self.attributes.defense[target.dtype]
|
|
165
|
+
if damage > 0:
|
|
166
|
+
self.kill()
|
|
167
|
+
if target.one_hit:
|
|
168
|
+
target.kill()
|
|
169
|
+
return True
|
|
170
|
+
|
|
171
|
+
elif nature == Nature.SIGNAL:
|
|
172
|
+
self.visible = False
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
elif nature == Nature.NO_SIGNAL:
|
|
176
|
+
self.visible = True
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
def draw_self(self, ox: float, oy: float):
|
|
182
|
+
if not self.visible:
|
|
183
|
+
return
|
|
184
|
+
|
|
185
|
+
py = self.py - oy - (self.pz + self.visible_pz)
|
|
186
|
+
|
|
187
|
+
if self.pz != 0:
|
|
188
|
+
self.engine.backend.fill_circle(
|
|
189
|
+
(self.px - ox + 0.5) * self.sprite.width,
|
|
190
|
+
(self.py - oy + 0.7) * self.sprite.height,
|
|
191
|
+
0.3125 * self.sprite.width,
|
|
192
|
+
BLACK,
|
|
193
|
+
)
|
|
194
|
+
self.sprite.draw_self(self.px - ox, py)
|
|
195
|
+
|
|
196
|
+
def _throw(self):
|
|
197
|
+
if self.pz < 0.5:
|
|
198
|
+
self.solid_vs_dyn = True
|
|
199
|
+
if self.pz > 0:
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
self._create_impact()
|
|
203
|
+
|
|
204
|
+
# self.solid_vs_dyn = True
|
|
205
|
+
self.thrown = False
|
|
206
|
+
self.vx = self.vy = 0.0
|
|
207
|
+
if self.destroyable:
|
|
208
|
+
self.kill()
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
def _move(self):
|
|
212
|
+
if self.actor.graphic_state == GraphicState.PUSHING:
|
|
213
|
+
stop_moving = False
|
|
214
|
+
for button in [K.DOWN, K.LEFT, K.UP, K.RIGHT]:
|
|
215
|
+
if button == self.move_direction:
|
|
216
|
+
if self.engine.keys.key_held(button):
|
|
217
|
+
self.vx = self.vx_mask
|
|
218
|
+
self.vy = self.vy_mask
|
|
219
|
+
else:
|
|
220
|
+
if self.engine.keys.key_held(button):
|
|
221
|
+
stop_moving = True
|
|
222
|
+
self.vx = 0
|
|
223
|
+
self.vy = 0
|
|
224
|
+
break
|
|
225
|
+
if (
|
|
226
|
+
abs(self.actor.px - self.px) > 1.1
|
|
227
|
+
or abs(self.actor.py - self.py) > 1.1
|
|
228
|
+
):
|
|
229
|
+
stop_moving = True
|
|
230
|
+
|
|
231
|
+
if not stop_moving and abs(self.vx) > abs(self.vy):
|
|
232
|
+
self.vy = 0
|
|
233
|
+
elif not stop_moving and abs(self.vy) > abs(self.vx):
|
|
234
|
+
self.vx = 0
|
|
235
|
+
else:
|
|
236
|
+
self.vx = self.vy = 0.0
|
|
237
|
+
|
|
238
|
+
dx = self.px - self.spawn_px
|
|
239
|
+
dy = self.py - self.spawn_py
|
|
240
|
+
self.total_range = math.sqrt(dx * dx + dy * dy)
|
|
241
|
+
|
|
242
|
+
if self.total_range >= self.range:
|
|
243
|
+
self.vx = self.vy = 0.0
|
|
244
|
+
|
|
245
|
+
if self.vx == 0.0 and self.vy == 0.0:
|
|
246
|
+
self.moving = False
|
|
247
|
+
self.vx_mask = self.vy_mask = 0
|
|
248
|
+
self.actor.unlock_graphic_state()
|
|
249
|
+
self.engine.audio.stop_sound("move_block")
|
|
250
|
+
else:
|
|
251
|
+
self.engine.audio.play_sound("move_block")
|
|
252
|
+
return
|
|
253
|
+
|
|
254
|
+
def _lift(self):
|
|
255
|
+
if self.lifted and self.engine.keys.new_key_press(K.A):
|
|
256
|
+
# Throw away
|
|
257
|
+
self.vx = self.vy = 0
|
|
258
|
+
if self.actor.facing_direction == Direction.SOUTH:
|
|
259
|
+
self.vy = 4
|
|
260
|
+
if self.actor.facing_direction == Direction.WEST:
|
|
261
|
+
self.vx = -4
|
|
262
|
+
if self.actor.facing_direction == Direction.NORTH:
|
|
263
|
+
self.vy = -4
|
|
264
|
+
if self.actor.facing_direction == Direction.EAST:
|
|
265
|
+
self.vx = 4
|
|
266
|
+
|
|
267
|
+
self.vz = 6.0
|
|
268
|
+
self.pz = self.actor.pz + 0.9
|
|
269
|
+
self.visible_pz = 0
|
|
270
|
+
self.actor.can_attack = True
|
|
271
|
+
self.lifted = False
|
|
272
|
+
self.actor = None
|
|
273
|
+
self.thrown = True
|
|
274
|
+
|
|
275
|
+
elif self.lift_started and self.engine.keys.new_key_release(K.A):
|
|
276
|
+
self.lift_started = False
|
|
277
|
+
self.lifted = True
|
|
278
|
+
self.solid_vs_dyn = False
|
|
279
|
+
else:
|
|
280
|
+
self.solid_vs_dyn = False
|
|
281
|
+
self.px = self.actor.px
|
|
282
|
+
self.py = self.actor.py
|
|
283
|
+
self.visible_pz = self.actor.pz + 0.9
|
|
284
|
+
self.vx = self.vy = 0.0
|
|
285
|
+
|
|
286
|
+
def _create_impact(self):
|
|
287
|
+
impact: List[Projectile] = []
|
|
288
|
+
impact.append(
|
|
289
|
+
Projectile(self.px + 0.5, self.py + 0.5, 0, 0, 0.2, self.alignment)
|
|
290
|
+
)
|
|
291
|
+
impact.append(
|
|
292
|
+
Projectile(self.px - 0.5, self.py + 0.5, 0, 0, 0.2, self.alignment)
|
|
293
|
+
)
|
|
294
|
+
impact.append(
|
|
295
|
+
Projectile(self.px - 0.5, self.py - 0.5, 0, 0, 0.2, self.alignment)
|
|
296
|
+
)
|
|
297
|
+
impact.append(
|
|
298
|
+
Projectile(self.px + 0.5, self.py - 0.5, 0, 0, 0.2, self.alignment)
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
for pro in impact:
|
|
302
|
+
pro.sprite.name = "explosion"
|
|
303
|
+
pro.solid_vs_dyn = False
|
|
304
|
+
pro.solid_vs_map = False
|
|
305
|
+
pro.damage = 5
|
|
306
|
+
self.engine.scene.add_projectile(pro)
|
|
307
|
+
|
|
308
|
+
@staticmethod
|
|
309
|
+
def load_from_tiled_object(obj, px, py, width, height):
|
|
310
|
+
movable = Movable(
|
|
311
|
+
px=px,
|
|
312
|
+
py=py,
|
|
313
|
+
tileset_name=obj.get_string("tileset_name"),
|
|
314
|
+
image_name=obj.get_string("tileset_name"),
|
|
315
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
316
|
+
graphic_state=GraphicState[
|
|
317
|
+
obj.get_string("graphic_state", "standing").upper()
|
|
318
|
+
],
|
|
319
|
+
facing_direction=Direction[
|
|
320
|
+
obj.get_string("facing_direction", "south").upper()
|
|
321
|
+
],
|
|
322
|
+
mrange=obj.get_float("range"),
|
|
323
|
+
liftable=obj.get_bool("liftable"),
|
|
324
|
+
destroyable=obj.get_bool("destroyable"),
|
|
325
|
+
movable=obj.get_bool("movable"),
|
|
326
|
+
intangible=obj.get_bool("intangible"),
|
|
327
|
+
force_collision_check=obj.get_bool("force_collision_check"),
|
|
328
|
+
dyn_id=obj.object_id,
|
|
329
|
+
name=obj.name,
|
|
330
|
+
)
|
|
331
|
+
movable.sprite.width = int(width * TILE_WIDTH)
|
|
332
|
+
movable.sprite.height = int(height * TILE_HEIGHT)
|
|
333
|
+
for dt in Damage:
|
|
334
|
+
movable.attributes.defense[dt] = obj.get_int(
|
|
335
|
+
f"defense_{dt.name.lower()}"
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
return [movable]
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from ...scripts.commands.oneway_move import CommandOnewayMove
|
|
2
|
+
from ...types.direction import Direction
|
|
3
|
+
from ...types.graphic_state import GraphicState
|
|
4
|
+
from ...types.nature import Nature
|
|
5
|
+
from ...types.object import ObjectType
|
|
6
|
+
from ...util.constants import ONEWAY_ACTIVATION_DELAY, TILE_HEIGHT, TILE_WIDTH
|
|
7
|
+
from ..animated_sprite import AnimatedSprite
|
|
8
|
+
from ..dynamic import Dynamic
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Oneway(Dynamic):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
px: float,
|
|
15
|
+
py: float,
|
|
16
|
+
tileset_name: str,
|
|
17
|
+
image_name: str,
|
|
18
|
+
sprite_name: str,
|
|
19
|
+
facing_direction: Direction,
|
|
20
|
+
graphic_state: GraphicState,
|
|
21
|
+
jump_vx: float,
|
|
22
|
+
jump_vy: float,
|
|
23
|
+
width: float,
|
|
24
|
+
height: float,
|
|
25
|
+
dyn_id=-1,
|
|
26
|
+
name="Oneway",
|
|
27
|
+
):
|
|
28
|
+
super().__init__(name, px, py, dyn_id)
|
|
29
|
+
self.sprite = AnimatedSprite(
|
|
30
|
+
tileset_name,
|
|
31
|
+
image_name,
|
|
32
|
+
sprite_name,
|
|
33
|
+
graphic_state,
|
|
34
|
+
facing_direction,
|
|
35
|
+
)
|
|
36
|
+
self.type = ObjectType.ONEWAY
|
|
37
|
+
self.graphic_state = graphic_state
|
|
38
|
+
self.facing_direction = facing_direction
|
|
39
|
+
self.sprite.width = int(width * TILE_WIDTH)
|
|
40
|
+
self.sprite.height = int(height * TILE_HEIGHT)
|
|
41
|
+
|
|
42
|
+
self.hitbox_px, self.hitbox_py = 0.0, 0.0
|
|
43
|
+
self.hitbox_width, self.hitbox_height = 1.0, 1.0
|
|
44
|
+
self.solid_vs_map = False
|
|
45
|
+
|
|
46
|
+
self.width: float = width
|
|
47
|
+
self.height: float = height
|
|
48
|
+
self.jump_vx: float = 0.0
|
|
49
|
+
self.jump_vy: float = 0.0
|
|
50
|
+
self.activation_delay: float = ONEWAY_ACTIVATION_DELAY
|
|
51
|
+
self.triggered: bool = False
|
|
52
|
+
self.cooldown: float = 0.0
|
|
53
|
+
|
|
54
|
+
if jump_vx < 0:
|
|
55
|
+
self.jump_vx = jump_vx - 1
|
|
56
|
+
self.hitbox_px += 0.1
|
|
57
|
+
elif jump_vx > 0:
|
|
58
|
+
self.jump_vx = jump_vx + 1
|
|
59
|
+
self.hitbox_px -= 0.1
|
|
60
|
+
|
|
61
|
+
if jump_vy < 0:
|
|
62
|
+
self.jump_vy = jump_vy - 1
|
|
63
|
+
self.hitbox_py += 0.1
|
|
64
|
+
elif jump_vy > 0:
|
|
65
|
+
self.jump_vy = jump_vy + 1
|
|
66
|
+
self.hitbox_py -= 0.1
|
|
67
|
+
|
|
68
|
+
def update(self, elapsed_time, target=None):
|
|
69
|
+
self.sprite.update(
|
|
70
|
+
elapsed_time, self.facing_direction, self.graphic_state
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Can only be triggered again after a certain time has passed.
|
|
74
|
+
if self.cooldown >= 0.0:
|
|
75
|
+
self.cooldown -= elapsed_time
|
|
76
|
+
return
|
|
77
|
+
else:
|
|
78
|
+
self.cooldown = 0.0
|
|
79
|
+
|
|
80
|
+
# If no interaction happened in a frame, the activation timer
|
|
81
|
+
# gets resetted.
|
|
82
|
+
if not self.triggered:
|
|
83
|
+
self.timer = 0.0
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
# Activation countdown
|
|
87
|
+
if self.timer > 0.0:
|
|
88
|
+
self.timer -= elapsed_time
|
|
89
|
+
|
|
90
|
+
# Activation countdown reached 0 and the jump is initiated.
|
|
91
|
+
if self.timer <= 0.0:
|
|
92
|
+
self.engine.script.add_command(
|
|
93
|
+
CommandOnewayMove(target, self.jump_vx, self.jump_vy)
|
|
94
|
+
)
|
|
95
|
+
self.cooldown = 2.0
|
|
96
|
+
|
|
97
|
+
# Reset the triggered flag so it has to be activated again
|
|
98
|
+
# by interaction
|
|
99
|
+
self.triggered = False
|
|
100
|
+
|
|
101
|
+
def on_interaction(self, target, nature=Nature.WALK):
|
|
102
|
+
if target.type == ObjectType.PLAYER and nature == Nature.WALK:
|
|
103
|
+
# No interaction when target is higher than the oneway
|
|
104
|
+
if target.pz > 0:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
# We have to check that target is not placed "more" in the
|
|
108
|
+
# target direction than the oneway
|
|
109
|
+
if (
|
|
110
|
+
self.jump_vx < 0
|
|
111
|
+
and target.px < self.px + self.width - target.hitbox_px
|
|
112
|
+
):
|
|
113
|
+
return False
|
|
114
|
+
if self.jump_vx > 0 and target.px >= self.px:
|
|
115
|
+
return False
|
|
116
|
+
if (
|
|
117
|
+
self.jump_vy < 0
|
|
118
|
+
and target.py <= self.py + self.height - target.hitbox_py
|
|
119
|
+
):
|
|
120
|
+
return False
|
|
121
|
+
if self.jump_vy > 0 and target.py >= self.py:
|
|
122
|
+
return False
|
|
123
|
+
|
|
124
|
+
if self.jump_vx == 0:
|
|
125
|
+
if target.px >= self.px + self.width:
|
|
126
|
+
return False
|
|
127
|
+
if target.px + 1.0 <= self.px:
|
|
128
|
+
return False
|
|
129
|
+
|
|
130
|
+
if self.jump_vy == 0:
|
|
131
|
+
if target.py >= self.py + self.height:
|
|
132
|
+
return False
|
|
133
|
+
if target.py + 1.0 <= self.py:
|
|
134
|
+
return False
|
|
135
|
+
|
|
136
|
+
self.triggered = True
|
|
137
|
+
if self.timer <= 0.0:
|
|
138
|
+
self.timer = self.activation_delay
|
|
139
|
+
|
|
140
|
+
return True
|
|
141
|
+
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
def draw_self(self, ox: float, oy: float):
|
|
145
|
+
self.sprite.draw_self(self.px - ox, self.py - oy)
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def load_from_tiled_object(obj, px, py, width, height):
|
|
149
|
+
oneway = Oneway(
|
|
150
|
+
px=px,
|
|
151
|
+
py=py,
|
|
152
|
+
tileset_name=obj.get_string("tileset_name"),
|
|
153
|
+
image_name=obj.get_string("tileset_name"),
|
|
154
|
+
sprite_name=obj.get_string("sprite_name"),
|
|
155
|
+
graphic_state=GraphicState[
|
|
156
|
+
obj.get_string("graphic_state", "standing").upper()
|
|
157
|
+
],
|
|
158
|
+
facing_direction=Direction[
|
|
159
|
+
obj.get_string("facing_direction", "south").upper()
|
|
160
|
+
],
|
|
161
|
+
jump_vx=obj.get_float("jump_vx"),
|
|
162
|
+
jump_vy=obj.get_float("jump_vy"),
|
|
163
|
+
width=width,
|
|
164
|
+
height=height,
|
|
165
|
+
dyn_id=obj.object_id,
|
|
166
|
+
name=obj.name,
|
|
167
|
+
)
|
|
168
|
+
return [oneway]
|