Refine combo charge controls
This commit is contained in:
@@ -24,8 +24,14 @@ var charge_ready := false
|
|||||||
var charge_active := false
|
var charge_active := false
|
||||||
var _pending_combo_clear_reason := ""
|
var _pending_combo_clear_reason := ""
|
||||||
var _charge_effect_time := 0.0
|
var _charge_effect_time := 0.0
|
||||||
|
var _charge_animation_time := 0.0
|
||||||
var _charge_hold_symbol := ""
|
var _charge_hold_symbol := ""
|
||||||
var _charge_hold_direction := Vector2.ZERO
|
var _charge_hold_direction := Vector2.ZERO
|
||||||
|
var _suppressed_movement_actions := {
|
||||||
|
"player_a": false,
|
||||||
|
"player_d": false,
|
||||||
|
}
|
||||||
|
var _last_combo_input_accepted := false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -61,9 +67,11 @@ func _handle_combo_key_event(event: InputEvent) -> bool:
|
|||||||
return false
|
return false
|
||||||
if not key_event.pressed:
|
if not key_event.pressed:
|
||||||
if _event_matches_key(key_event, KEY_A):
|
if _event_matches_key(key_event, KEY_A):
|
||||||
|
_set_movement_action_suppressed("player_a", false)
|
||||||
_finish_charge_hold("A")
|
_finish_charge_hold("A")
|
||||||
return true
|
return true
|
||||||
elif _event_matches_key(key_event, KEY_D):
|
elif _event_matches_key(key_event, KEY_D):
|
||||||
|
_set_movement_action_suppressed("player_d", false)
|
||||||
_finish_charge_hold("D")
|
_finish_charge_hold("D")
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
@@ -71,15 +79,17 @@ func _handle_combo_key_event(event: InputEvent) -> bool:
|
|||||||
_submit_combo_input_from_event("W")
|
_submit_combo_input_from_event("W")
|
||||||
return true
|
return true
|
||||||
elif _event_matches_key(key_event, KEY_A):
|
elif _event_matches_key(key_event, KEY_A):
|
||||||
|
_set_movement_action_suppressed("player_a", true)
|
||||||
heading = Vector2.LEFT
|
heading = Vector2.LEFT
|
||||||
var skill_id := _submit_combo_input_from_event("A")
|
_submit_combo_input_from_event("A")
|
||||||
if skill_id == "skill_a":
|
if _last_combo_input_accepted:
|
||||||
_begin_charge_hold("A", Vector2.LEFT)
|
_begin_charge_hold("A", Vector2.LEFT)
|
||||||
return true
|
return true
|
||||||
elif _event_matches_key(key_event, KEY_D):
|
elif _event_matches_key(key_event, KEY_D):
|
||||||
|
_set_movement_action_suppressed("player_d", true)
|
||||||
heading = Vector2.RIGHT
|
heading = Vector2.RIGHT
|
||||||
var skill_id := _submit_combo_input_from_event("D")
|
_submit_combo_input_from_event("D")
|
||||||
if skill_id == "skill_d":
|
if _last_combo_input_accepted:
|
||||||
_begin_charge_hold("D", Vector2.RIGHT)
|
_begin_charge_hold("D", Vector2.RIGHT)
|
||||||
return true
|
return true
|
||||||
elif _event_matches_key(key_event, KEY_S):
|
elif _event_matches_key(key_event, KEY_S):
|
||||||
@@ -96,7 +106,7 @@ func handle_input() -> void:
|
|||||||
velocity = Vector2.ZERO
|
velocity = Vector2.ZERO
|
||||||
return
|
return
|
||||||
_apply_horizontal_movement()
|
_apply_horizontal_movement()
|
||||||
if Input.is_action_just_pressed("jump"):
|
if Input.is_action_just_pressed("jump") and not Input.is_action_just_pressed("player_space"):
|
||||||
judge_rhythm_action("jump")
|
judge_rhythm_action("jump")
|
||||||
if can_jump():
|
if can_jump():
|
||||||
start_jump()
|
start_jump()
|
||||||
@@ -161,18 +171,20 @@ func submit_combo_input(symbol: String, forced_rating := "") -> String:
|
|||||||
|
|
||||||
|
|
||||||
func _record_combo_symbol(symbol: String, rhythm_action: String, forced_rating := "") -> String:
|
func _record_combo_symbol(symbol: String, rhythm_action: String, forced_rating := "") -> String:
|
||||||
|
_last_combo_input_accepted = false
|
||||||
var rating := _rating_or_forced(judge_rhythm_action(rhythm_action), forced_rating)
|
var rating := _rating_or_forced(judge_rhythm_action(rhythm_action), forced_rating)
|
||||||
_apply_energy_reward(str(rating.get("label", "perfect")))
|
|
||||||
if not _record_rated_combo_input(symbol, rating):
|
if not _record_rated_combo_input(symbol, rating):
|
||||||
if symbol == "A" or symbol == "D":
|
if symbol == "A" or symbol == "D":
|
||||||
_cancel_missed_direction_action()
|
_cancel_missed_direction_action()
|
||||||
return ""
|
return ""
|
||||||
|
_last_combo_input_accepted = true
|
||||||
var resolved := InputResolver.resolve(combo_window)
|
var resolved := InputResolver.resolve(combo_window)
|
||||||
if resolved.is_empty() and _pending_combo_clear_reason == "full":
|
if resolved.is_empty() and _pending_combo_clear_reason == "full":
|
||||||
resolved = _resolve_full_window_fallback(symbol)
|
resolved = _resolve_full_window_fallback(symbol)
|
||||||
if not resolved.is_empty():
|
if not resolved.is_empty():
|
||||||
if not _execute_combo_skill(resolved):
|
if not _execute_combo_skill(resolved):
|
||||||
return ""
|
return ""
|
||||||
|
_apply_skill_energy_reward(last_requested_skill_id)
|
||||||
if symbol == "SP" and not _is_projectile_space_chain() and _pending_combo_clear_reason.is_empty():
|
if symbol == "SP" and not _is_projectile_space_chain() and _pending_combo_clear_reason.is_empty():
|
||||||
_schedule_combo_clear("space")
|
_schedule_combo_clear("space")
|
||||||
return last_requested_skill_id if not resolved.is_empty() else ""
|
return last_requested_skill_id if not resolved.is_empty() else ""
|
||||||
@@ -277,11 +289,9 @@ func _skill_displacement_direction(skill: Dictionary) -> Vector2:
|
|||||||
return Vector2.ZERO
|
return Vector2.ZERO
|
||||||
|
|
||||||
|
|
||||||
func _apply_energy_reward(rating_label: String) -> void:
|
func _apply_skill_energy_reward(skill_id: String) -> void:
|
||||||
match rating_label:
|
match skill_id:
|
||||||
"perfect":
|
"skill_a", "skill_aa", "skill_aaa", "skill_d", "skill_dd", "skill_ddd":
|
||||||
_change_energy(2)
|
|
||||||
"good":
|
|
||||||
_change_energy(1)
|
_change_energy(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -332,9 +342,7 @@ func _update_charge(delta: float) -> void:
|
|||||||
attack_time_left = 0.0
|
attack_time_left = 0.0
|
||||||
attack_lunge_time_left = 0.0
|
attack_lunge_time_left = 0.0
|
||||||
velocity = Vector2.ZERO
|
velocity = Vector2.ZERO
|
||||||
var player_animation := _get_animation_player()
|
_update_charge_animation(delta)
|
||||||
if player_animation != null and player_animation.has_animation("warrior_idle") and player_animation.current_animation != "warrior_idle":
|
|
||||||
player_animation.play("warrior_idle")
|
|
||||||
charge_value = minf(charge_duration, charge_value + delta)
|
charge_value = minf(charge_duration, charge_value + delta)
|
||||||
charge_ready = charge_value >= charge_duration
|
charge_ready = charge_value >= charge_duration
|
||||||
_update_charge_effect(delta)
|
_update_charge_effect(delta)
|
||||||
@@ -346,6 +354,8 @@ func _start_charge() -> void:
|
|||||||
charge_value = 0.0
|
charge_value = 0.0
|
||||||
charge_ready = false
|
charge_ready = false
|
||||||
_charge_effect_time = 0.0
|
_charge_effect_time = 0.0
|
||||||
|
_charge_animation_time = 0.0
|
||||||
|
_play_charge_animation("warrior_charge_intro")
|
||||||
_update_charge_effect(0.0)
|
_update_charge_effect(0.0)
|
||||||
_emit_charge_changed()
|
_emit_charge_changed()
|
||||||
|
|
||||||
@@ -356,6 +366,7 @@ func _cancel_charge() -> void:
|
|||||||
charge_active = false
|
charge_active = false
|
||||||
charge_value = 0.0
|
charge_value = 0.0
|
||||||
charge_ready = false
|
charge_ready = false
|
||||||
|
_charge_animation_time = 0.0
|
||||||
_set_charge_effect_visible(false)
|
_set_charge_effect_visible(false)
|
||||||
_emit_charge_changed()
|
_emit_charge_changed()
|
||||||
|
|
||||||
@@ -378,6 +389,21 @@ func _update_charge_effect(delta: float) -> void:
|
|||||||
sprite.frame = int(_charge_effect_time * 12.0) % 5
|
sprite.frame = int(_charge_effect_time * 12.0) % 5
|
||||||
|
|
||||||
|
|
||||||
|
func _update_charge_animation(delta: float) -> void:
|
||||||
|
_charge_animation_time += delta
|
||||||
|
var intro_length := _animation_length("warrior_charge_intro")
|
||||||
|
if _charge_animation_time < intro_length:
|
||||||
|
_play_charge_animation("warrior_charge_intro")
|
||||||
|
else:
|
||||||
|
_play_charge_animation("warrior_charge_loop")
|
||||||
|
|
||||||
|
|
||||||
|
func _play_charge_animation(animation_name: String) -> void:
|
||||||
|
var player_animation := _get_animation_player()
|
||||||
|
if player_animation != null and player_animation.has_animation(animation_name) and player_animation.current_animation != animation_name:
|
||||||
|
player_animation.play(animation_name)
|
||||||
|
|
||||||
|
|
||||||
func _set_charge_effect_visible(is_visible: bool) -> void:
|
func _set_charge_effect_visible(is_visible: bool) -> void:
|
||||||
var sprite := _get_charge_effect_sprite()
|
var sprite := _get_charge_effect_sprite()
|
||||||
if sprite != null:
|
if sprite != null:
|
||||||
@@ -419,9 +445,9 @@ func _apply_horizontal_movement() -> void:
|
|||||||
if state != State.IDLE and state != State.WALK:
|
if state != State.IDLE and state != State.WALK:
|
||||||
return
|
return
|
||||||
var direction := 0.0
|
var direction := 0.0
|
||||||
if Input.is_action_pressed("player_a"):
|
if _is_movement_action_pressed("player_a"):
|
||||||
direction -= 1.0
|
direction -= 1.0
|
||||||
if Input.is_action_pressed("player_d"):
|
if _is_movement_action_pressed("player_d"):
|
||||||
direction += 1.0
|
direction += 1.0
|
||||||
if direction < 0.0:
|
if direction < 0.0:
|
||||||
heading = Vector2.LEFT
|
heading = Vector2.LEFT
|
||||||
@@ -430,6 +456,14 @@ func _apply_horizontal_movement() -> void:
|
|||||||
velocity.x = direction * speed
|
velocity.x = direction * speed
|
||||||
|
|
||||||
|
|
||||||
|
func _set_movement_action_suppressed(action_name: String, suppressed: bool) -> void:
|
||||||
|
_suppressed_movement_actions[action_name] = suppressed
|
||||||
|
|
||||||
|
|
||||||
|
func _is_movement_action_pressed(action_name: String) -> bool:
|
||||||
|
return Input.is_action_pressed(action_name) and not bool(_suppressed_movement_actions.get(action_name, false))
|
||||||
|
|
||||||
|
|
||||||
func _animation_length(animation_name: String) -> float:
|
func _animation_length(animation_name: String) -> float:
|
||||||
var player_animation := _get_animation_player()
|
var player_animation := _get_animation_player()
|
||||||
if player_animation != null and player_animation.has_animation(animation_name):
|
if player_animation != null and player_animation.has_animation(animation_name):
|
||||||
|
|||||||
@@ -332,9 +332,9 @@ tracks/4/keys = {
|
|||||||
"values": [176, 177, 178, 179, 180, 181, 182, 183]
|
"values": [176, 177, 178, 179, 180, 181, 182, 183]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_jk2m4"]
|
[sub_resource type="Animation" id="Animation_charge_intro"]
|
||||||
resource_name = "warrior_charge_release"
|
resource_name = "warrior_charge_intro"
|
||||||
length = 1.3333334
|
length = 0.6666667
|
||||||
step = 0.083333336
|
step = 0.083333336
|
||||||
tracks/0/type = "value"
|
tracks/0/type = "value"
|
||||||
tracks/0/imported = false
|
tracks/0/imported = false
|
||||||
@@ -391,10 +391,141 @@ tracks/4/path = NodePath("CharacterSprite:frame")
|
|||||||
tracks/4/interp = 1
|
tracks/4/interp = 1
|
||||||
tracks/4/loop_wrap = true
|
tracks/4/loop_wrap = true
|
||||||
tracks/4/keys = {
|
tracks/4/keys = {
|
||||||
"times": PackedFloat32Array(0, 0.083333336, 0.16666667, 0.25, 0.33333334, 0.41666666, 0.5, 0.5833333, 0.6666667, 0.75, 0.8333333, 0.9166667, 1, 1.0833334, 1.1666666, 1.25),
|
"times": PackedFloat32Array(0, 0.083333336, 0.16666667, 0.25, 0.33333334, 0.41666666, 0.5, 0.5833333),
|
||||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1),
|
||||||
"update": 1,
|
"update": 1,
|
||||||
"values": [192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207]
|
"values": [192, 193, 194, 195, 196, 197, 198, 199]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_charge_loop"]
|
||||||
|
resource_name = "warrior_charge_loop"
|
||||||
|
length = 0.33333334
|
||||||
|
loop_mode = 1
|
||||||
|
step = 0.083333336
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("CharacterSprite:texture")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [ExtResource("3_dyp2m")]
|
||||||
|
}
|
||||||
|
tracks/1/type = "value"
|
||||||
|
tracks/1/imported = false
|
||||||
|
tracks/1/enabled = true
|
||||||
|
tracks/1/path = NodePath("CharacterSprite:hframes")
|
||||||
|
tracks/1/interp = 1
|
||||||
|
tracks/1/loop_wrap = true
|
||||||
|
tracks/1/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [16]
|
||||||
|
}
|
||||||
|
tracks/2/type = "value"
|
||||||
|
tracks/2/imported = false
|
||||||
|
tracks/2/enabled = true
|
||||||
|
tracks/2/path = NodePath("CharacterSprite:vframes")
|
||||||
|
tracks/2/interp = 1
|
||||||
|
tracks/2/loop_wrap = true
|
||||||
|
tracks/2/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [25]
|
||||||
|
}
|
||||||
|
tracks/3/type = "value"
|
||||||
|
tracks/3/imported = false
|
||||||
|
tracks/3/enabled = true
|
||||||
|
tracks/3/path = NodePath("CharacterSprite:offset")
|
||||||
|
tracks/3/interp = 1
|
||||||
|
tracks/3/loop_wrap = true
|
||||||
|
tracks/3/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [Vector2(-40, -48)]
|
||||||
|
}
|
||||||
|
tracks/4/type = "value"
|
||||||
|
tracks/4/imported = false
|
||||||
|
tracks/4/enabled = true
|
||||||
|
tracks/4/path = NodePath("CharacterSprite:frame")
|
||||||
|
tracks/4/interp = 1
|
||||||
|
tracks/4/loop_wrap = true
|
||||||
|
tracks/4/keys = {
|
||||||
|
"times": PackedFloat32Array(0, 0.083333336, 0.16666667, 0.25),
|
||||||
|
"transitions": PackedFloat32Array(1, 1, 1, 1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [196, 197, 198, 199]
|
||||||
|
}
|
||||||
|
|
||||||
|
[sub_resource type="Animation" id="Animation_jk2m4"]
|
||||||
|
resource_name = "warrior_charge_release"
|
||||||
|
length = 0.6666667
|
||||||
|
step = 0.083333336
|
||||||
|
tracks/0/type = "value"
|
||||||
|
tracks/0/imported = false
|
||||||
|
tracks/0/enabled = true
|
||||||
|
tracks/0/path = NodePath("CharacterSprite:texture")
|
||||||
|
tracks/0/interp = 1
|
||||||
|
tracks/0/loop_wrap = true
|
||||||
|
tracks/0/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [ExtResource("3_dyp2m")]
|
||||||
|
}
|
||||||
|
tracks/1/type = "value"
|
||||||
|
tracks/1/imported = false
|
||||||
|
tracks/1/enabled = true
|
||||||
|
tracks/1/path = NodePath("CharacterSprite:hframes")
|
||||||
|
tracks/1/interp = 1
|
||||||
|
tracks/1/loop_wrap = true
|
||||||
|
tracks/1/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [16]
|
||||||
|
}
|
||||||
|
tracks/2/type = "value"
|
||||||
|
tracks/2/imported = false
|
||||||
|
tracks/2/enabled = true
|
||||||
|
tracks/2/path = NodePath("CharacterSprite:vframes")
|
||||||
|
tracks/2/interp = 1
|
||||||
|
tracks/2/loop_wrap = true
|
||||||
|
tracks/2/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [25]
|
||||||
|
}
|
||||||
|
tracks/3/type = "value"
|
||||||
|
tracks/3/imported = false
|
||||||
|
tracks/3/enabled = true
|
||||||
|
tracks/3/path = NodePath("CharacterSprite:offset")
|
||||||
|
tracks/3/interp = 1
|
||||||
|
tracks/3/loop_wrap = true
|
||||||
|
tracks/3/keys = {
|
||||||
|
"times": PackedFloat32Array(0),
|
||||||
|
"transitions": PackedFloat32Array(1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [Vector2(-40, -48)]
|
||||||
|
}
|
||||||
|
tracks/4/type = "value"
|
||||||
|
tracks/4/imported = false
|
||||||
|
tracks/4/enabled = true
|
||||||
|
tracks/4/path = NodePath("CharacterSprite:frame")
|
||||||
|
tracks/4/interp = 1
|
||||||
|
tracks/4/loop_wrap = true
|
||||||
|
tracks/4/keys = {
|
||||||
|
"times": PackedFloat32Array(0, 0.083333336, 0.16666667, 0.25, 0.33333334, 0.41666666, 0.5, 0.5833333),
|
||||||
|
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1),
|
||||||
|
"update": 1,
|
||||||
|
"values": [200, 201, 202, 203, 204, 205, 206, 207]
|
||||||
}
|
}
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_kqtwu"]
|
[sub_resource type="Animation" id="Animation_kqtwu"]
|
||||||
@@ -730,6 +861,8 @@ _data = {
|
|||||||
&"warrior_a_space_space": SubResource("Animation_2l4js"),
|
&"warrior_a_space_space": SubResource("Animation_2l4js"),
|
||||||
&"warrior_aa": SubResource("Animation_dyp2m"),
|
&"warrior_aa": SubResource("Animation_dyp2m"),
|
||||||
&"warrior_aaa": SubResource("Animation_atpat"),
|
&"warrior_aaa": SubResource("Animation_atpat"),
|
||||||
|
&"warrior_charge_intro": SubResource("Animation_charge_intro"),
|
||||||
|
&"warrior_charge_loop": SubResource("Animation_charge_loop"),
|
||||||
&"warrior_charge_release": SubResource("Animation_jk2m4"),
|
&"warrior_charge_release": SubResource("Animation_jk2m4"),
|
||||||
&"warrior_idle": SubResource("Animation_kqtwu"),
|
&"warrior_idle": SubResource("Animation_kqtwu"),
|
||||||
&"warrior_s": SubResource("Animation_6eyoc"),
|
&"warrior_s": SubResource("Animation_6eyoc"),
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ class_name InputResolver
|
|||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
||||||
const SKILLS := {
|
const SKILLS := {
|
||||||
|
"W": {
|
||||||
|
"type": "skill",
|
||||||
|
"id": "skill_w",
|
||||||
|
"animation": "warrior_w",
|
||||||
|
"clear_window": false,
|
||||||
|
},
|
||||||
"A": {
|
"A": {
|
||||||
"type": "skill",
|
"type": "skill",
|
||||||
"id": "skill_a",
|
"id": "skill_a",
|
||||||
|
|||||||
@@ -34,7 +34,9 @@ func _init() -> void:
|
|||||||
|
|
||||||
window.record("W")
|
window.record("W")
|
||||||
var resolved: Dictionary = resolver_script.resolve(window)
|
var resolved: Dictionary = resolver_script.resolve(window)
|
||||||
_expect_bool(resolved.is_empty(), true, "W alone should not resolve a skill")
|
_expect_string(str(resolved.get("id", "")), "skill_w", "W alone skill")
|
||||||
|
_expect_string(str(resolved.get("animation", "")), "warrior_w", "W should play row 6 animation")
|
||||||
|
_expect_bool(bool(resolved.get("clear_window", true)), false, "W skill should not clear window")
|
||||||
window.record("A")
|
window.record("A")
|
||||||
resolved = resolver_script.resolve(window)
|
resolved = resolver_script.resolve(window)
|
||||||
_expect_string(str(resolved.get("id", "")), "skill_wa", "W+A skill")
|
_expect_string(str(resolved.get("id", "")), "skill_wa", "W+A skill")
|
||||||
@@ -50,7 +52,7 @@ func _init() -> void:
|
|||||||
|
|
||||||
window.record("W")
|
window.record("W")
|
||||||
resolved = resolver_script.resolve(window)
|
resolved = resolver_script.resolve(window)
|
||||||
_expect_bool(resolved.is_empty(), true, "W alone should not resolve before mirrored W+D")
|
_expect_string(str(resolved.get("id", "")), "skill_w", "W alone before mirrored W+D")
|
||||||
window.record("D")
|
window.record("D")
|
||||||
resolved = resolver_script.resolve(window)
|
resolved = resolver_script.resolve(window)
|
||||||
_expect_string(str(resolved.get("id", "")), "skill_wd", "W+D should mirror W+A skill")
|
_expect_string(str(resolved.get("id", "")), "skill_wd", "W+D should mirror W+A skill")
|
||||||
|
|||||||
@@ -35,7 +35,10 @@ func _init() -> void:
|
|||||||
_expect_warrior_animation(animation_player, "warrior_aa", 11, 5)
|
_expect_warrior_animation(animation_player, "warrior_aa", 11, 5)
|
||||||
_expect_warrior_animation(animation_player, "warrior_aaa", 12, 8)
|
_expect_warrior_animation(animation_player, "warrior_aaa", 12, 8)
|
||||||
_expect_warrior_animation(animation_player, "warrior_s_projectile", 14, 13)
|
_expect_warrior_animation(animation_player, "warrior_s_projectile", 14, 13)
|
||||||
_expect_warrior_animation(animation_player, "warrior_charge_release", 13, 16, WARRIOR_WOMAN_TEXTURE)
|
_expect_warrior_animation(animation_player, "warrior_charge_intro", 13, 8, WARRIOR_WOMAN_TEXTURE)
|
||||||
|
_expect_warrior_animation(animation_player, "warrior_charge_loop", 13, 4, WARRIOR_WOMAN_TEXTURE, 4)
|
||||||
|
_expect_animation_loops(animation_player, "warrior_charge_loop")
|
||||||
|
_expect_warrior_animation(animation_player, "warrior_charge_release", 13, 8, WARRIOR_WOMAN_TEXTURE, 8)
|
||||||
_expect_warrior_animation(animation_player, "warrior_a_space_space", 15, 12)
|
_expect_warrior_animation(animation_player, "warrior_a_space_space", 15, 12)
|
||||||
_expect_warrior_animation(animation_player, "warrior_a_space", 17, 10)
|
_expect_warrior_animation(animation_player, "warrior_a_space", 17, 10)
|
||||||
_expect_charge_effect(player)
|
_expect_charge_effect(player)
|
||||||
@@ -46,7 +49,8 @@ func _init() -> void:
|
|||||||
failures.append("Old slash animation should be removed")
|
failures.append("Old slash animation should be removed")
|
||||||
|
|
||||||
player.call("submit_combo_input", "W")
|
player.call("submit_combo_input", "W")
|
||||||
_expect_string(str(player.get("last_requested_skill_id")), "", "W alone should not request a skill")
|
_expect_string(str(player.get("last_requested_skill_id")), "skill_w", "W alone should request row 6 skill")
|
||||||
|
_expect_string(str(player.get("current_skill_animation")), "warrior_w", "W alone should play warrior_w")
|
||||||
player.call("submit_combo_input", "A")
|
player.call("submit_combo_input", "A")
|
||||||
_expect_string(str(player.get("last_requested_skill_id")), "skill_wa", "W+A should request row 7 skill")
|
_expect_string(str(player.get("last_requested_skill_id")), "skill_wa", "W+A should request row 7 skill")
|
||||||
_expect_string(str(player.get("current_skill_animation")), "warrior_wa", "W+A should play warrior_wa")
|
_expect_string(str(player.get("current_skill_animation")), "warrior_wa", "W+A should play warrior_wa")
|
||||||
@@ -71,7 +75,7 @@ func _expect_action_has_key(action_name: String, key: Key) -> void:
|
|||||||
failures.append("Input action %s should be bound to key %s" % [action_name, OS.get_keycode_string(key)])
|
failures.append("Input action %s should be bound to key %s" % [action_name, OS.get_keycode_string(key)])
|
||||||
|
|
||||||
|
|
||||||
func _expect_warrior_animation(animation_player: AnimationPlayer, animation_name: String, row: int, expected_frames: int, texture_path := WARRIOR_TEXTURE) -> void:
|
func _expect_warrior_animation(animation_player: AnimationPlayer, animation_name: String, row: int, expected_frames: int, texture_path := WARRIOR_TEXTURE, start_column := 0) -> void:
|
||||||
if not animation_player.has_animation(animation_name):
|
if not animation_player.has_animation(animation_name):
|
||||||
failures.append("Missing animation: %s" % animation_name)
|
failures.append("Missing animation: %s" % animation_name)
|
||||||
return
|
return
|
||||||
@@ -106,7 +110,7 @@ func _expect_warrior_animation(animation_player: AnimationPlayer, animation_name
|
|||||||
failures.append("Missing vframes track: %s" % animation_name)
|
failures.append("Missing vframes track: %s" % animation_name)
|
||||||
if frame_values.size() != expected_frames:
|
if frame_values.size() != expected_frames:
|
||||||
failures.append("%s should key %d frames, got %d" % [animation_name, expected_frames, frame_values.size()])
|
failures.append("%s should key %d frames, got %d" % [animation_name, expected_frames, frame_values.size()])
|
||||||
var first_frame := (row - 1) * WARRIOR_COLUMNS
|
var first_frame := (row - 1) * WARRIOR_COLUMNS + start_column
|
||||||
for index: int in range(frame_values.size()):
|
for index: int in range(frame_values.size()):
|
||||||
var expected := first_frame + index
|
var expected := first_frame + index
|
||||||
if frame_values[index] != expected:
|
if frame_values[index] != expected:
|
||||||
@@ -123,6 +127,15 @@ func _expect_string(actual: String, expected: String, label: String) -> void:
|
|||||||
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
||||||
|
|
||||||
|
|
||||||
|
func _expect_animation_loops(animation_player: AnimationPlayer, animation_name: String) -> void:
|
||||||
|
if not animation_player.has_animation(animation_name):
|
||||||
|
failures.append("Missing animation for loop check: %s" % animation_name)
|
||||||
|
return
|
||||||
|
var animation: Animation = animation_player.get_animation(animation_name)
|
||||||
|
if int(animation.loop_mode) != 1:
|
||||||
|
failures.append("%s should loop, got loop_mode %d" % [animation_name, int(animation.loop_mode)])
|
||||||
|
|
||||||
|
|
||||||
func _expect_projectile_animation(projectile: Node) -> void:
|
func _expect_projectile_animation(projectile: Node) -> void:
|
||||||
if projectile.get_child_count() == 0:
|
if projectile.get_child_count() == 0:
|
||||||
failures.append("Projectile should create a Sprite2D child")
|
failures.append("Projectile should create a Sprite2D child")
|
||||||
|
|||||||
@@ -54,12 +54,17 @@ func _init() -> void:
|
|||||||
w_event.echo = true
|
w_event.echo = true
|
||||||
player.call("_input", w_event)
|
player.call("_input", w_event)
|
||||||
_expect_array(player.call("get_combo_slots"), ["W"], "W key press should enter once and ignore echo repeat")
|
_expect_array(player.call("get_combo_slots"), ["W"], "W key press should enter once and ignore echo repeat")
|
||||||
_expect_no_skill_requested("W alone should not request a skill")
|
_expect_last_skill("skill_w", "W alone should request row 6 skill")
|
||||||
|
_expect_string(str(player.get("current_skill_animation")), "warrior_w", "W should play row 6 animation")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
var a_event := InputEventKey.new()
|
var a_event := InputEventKey.new()
|
||||||
a_event.pressed = true
|
a_event.pressed = true
|
||||||
a_event.physical_keycode = KEY_A
|
a_event.physical_keycode = KEY_A
|
||||||
|
var a_release_event := InputEventKey.new()
|
||||||
|
a_release_event.pressed = false
|
||||||
|
a_release_event.physical_keycode = KEY_A
|
||||||
player.call("_input", a_event)
|
player.call("_input", a_event)
|
||||||
_expect_array(player.call("get_combo_slots"), ["A"], "A alone should stay visible in the combo window")
|
_expect_array(player.call("get_combo_slots"), ["A"], "A alone should stay visible in the combo window")
|
||||||
_expect_last_skill("skill_a", "A should request row 10 skill")
|
_expect_last_skill("skill_a", "A should request row 10 skill")
|
||||||
@@ -74,7 +79,7 @@ func _init() -> void:
|
|||||||
player.set("attack_time_left", 0.0)
|
player.set("attack_time_left", 0.0)
|
||||||
player.call("_process", 0.2)
|
player.call("_process", 0.2)
|
||||||
_expect_bool(player.call("is_charge_active"), true, "holding A after its animation should enter charge state")
|
_expect_bool(player.call("is_charge_active"), true, "holding A after its animation should enter charge state")
|
||||||
_expect_string(animation_player.current_animation, "warrior_idle", "holding A charge should keep idle animation")
|
_expect_string(animation_player.current_animation, "warrior_charge_intro", "holding A charge should start with charge intro animation")
|
||||||
_expect_positive(player.call("get_charge"), "holding A should grow charge")
|
_expect_positive(player.call("get_charge"), "holding A should grow charge")
|
||||||
var charge_effect := player.get_node_or_null("ChargeEffectSprite") as Sprite2D
|
var charge_effect := player.get_node_or_null("ChargeEffectSprite") as Sprite2D
|
||||||
if charge_effect == null:
|
if charge_effect == null:
|
||||||
@@ -82,9 +87,6 @@ func _init() -> void:
|
|||||||
else:
|
else:
|
||||||
_expect_bool(charge_effect.visible, true, "holding A should show charge effect")
|
_expect_bool(charge_effect.visible, true, "holding A should show charge effect")
|
||||||
requested_skills.clear()
|
requested_skills.clear()
|
||||||
var a_release_event := InputEventKey.new()
|
|
||||||
a_release_event.pressed = false
|
|
||||||
a_release_event.physical_keycode = KEY_A
|
|
||||||
player.call("_input", a_release_event)
|
player.call("_input", a_release_event)
|
||||||
_expect_bool(player.call("is_charge_active"), false, "early A release should cancel charge")
|
_expect_bool(player.call("is_charge_active"), false, "early A release should cancel charge")
|
||||||
_expect_bool(player.call("is_charge_ready"), false, "early A release should not be ready")
|
_expect_bool(player.call("is_charge_ready"), false, "early A release should not be ready")
|
||||||
@@ -97,6 +99,7 @@ func _init() -> void:
|
|||||||
player.set("attack_time_left", 0.0)
|
player.set("attack_time_left", 0.0)
|
||||||
player.call("_process", player.call("get_max_charge") + 0.1)
|
player.call("_process", player.call("get_max_charge") + 0.1)
|
||||||
_expect_bool(player.call("is_charge_ready"), true, "held A should become ready when charge is full")
|
_expect_bool(player.call("is_charge_ready"), true, "held A should become ready when charge is full")
|
||||||
|
_expect_string(animation_player.current_animation, "warrior_charge_loop", "full A hold should keep charge loop animation")
|
||||||
requested_skills.clear()
|
requested_skills.clear()
|
||||||
player.call("_input", a_release_event)
|
player.call("_input", a_release_event)
|
||||||
_expect_last_skill("skill_a_charge_release", "full A release should request charge release skill")
|
_expect_last_skill("skill_a_charge_release", "full A release should request charge release skill")
|
||||||
@@ -115,10 +118,17 @@ func _init() -> void:
|
|||||||
|
|
||||||
player.call("_input", a_event)
|
player.call("_input", a_event)
|
||||||
Input.action_press("player_a")
|
Input.action_press("player_a")
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("velocity", Vector2.ZERO)
|
||||||
player.call("handle_input")
|
player.call("handle_input")
|
||||||
Input.action_release("player_a")
|
Input.action_release("player_a")
|
||||||
_expect_array(player.call("get_combo_slots"), ["A"], "one A key event should not be recorded again by physics polling")
|
_expect_array(player.call("get_combo_slots"), ["A"], "one A key event should not be recorded again by physics polling")
|
||||||
_expect_last_skill("skill_a", "single A key event should still be the last requested skill after physics polling")
|
_expect_last_skill("skill_a", "single A key event should still be the last requested skill after physics polling")
|
||||||
|
_expect_zero((player as CharacterBody2D).velocity.x, "holding consumed A key should not keep sliding after combo input")
|
||||||
|
var a_release_after_single_hold := InputEventKey.new()
|
||||||
|
a_release_after_single_hold.pressed = false
|
||||||
|
a_release_after_single_hold.physical_keycode = KEY_A
|
||||||
|
player.call("_input", a_release_after_single_hold)
|
||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
requested_skills.clear()
|
requested_skills.clear()
|
||||||
@@ -140,6 +150,7 @@ func _init() -> void:
|
|||||||
player.set("state", Character.State.IDLE)
|
player.set("state", Character.State.IDLE)
|
||||||
player.set("attack_time_left", 0.0)
|
player.set("attack_time_left", 0.0)
|
||||||
player.call("_process", player.call("get_max_charge") + 0.1)
|
player.call("_process", player.call("get_max_charge") + 0.1)
|
||||||
|
_expect_string(animation_player.current_animation, "warrior_charge_loop", "full D hold should keep charge loop animation")
|
||||||
var d_release_event := InputEventKey.new()
|
var d_release_event := InputEventKey.new()
|
||||||
d_release_event.pressed = false
|
d_release_event.pressed = false
|
||||||
d_release_event.physical_keycode = KEY_D
|
d_release_event.physical_keycode = KEY_D
|
||||||
@@ -150,6 +161,74 @@ func _init() -> void:
|
|||||||
_expect_positive((player as CharacterBody2D).velocity.x, "full D release should lunge right")
|
_expect_positive((player as CharacterBody2D).velocity.x, "full D release should lunge right")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
requested_skills.clear()
|
requested_skills.clear()
|
||||||
|
|
||||||
|
player.call("_input", d_event)
|
||||||
|
player.call("_input", d_release_event)
|
||||||
|
player.call("_input", d_event)
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("attack_time_left", 0.0)
|
||||||
|
player.call("_process", 0.2)
|
||||||
|
_expect_array(player.call("get_combo_slots"), ["D", "D"], "second held D should keep D+D in the combo window")
|
||||||
|
_expect_last_skill("skill_dd", "second held D should trigger D+D skill before charging")
|
||||||
|
_expect_bool(player.call("is_charge_active"), true, "holding second D after D+D animation should enter charge state")
|
||||||
|
_expect_string(animation_player.current_animation, "warrior_charge_intro", "holding second D should start with charge intro animation")
|
||||||
|
_expect_positive(player.call("get_charge"), "holding second D should grow charge after D+D")
|
||||||
|
requested_skills.clear()
|
||||||
|
player.call("_input", d_release_event)
|
||||||
|
_expect_bool(player.call("is_charge_active"), false, "releasing held second D should cancel D+D charge")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
|
player.call("_input", a_event)
|
||||||
|
player.call("_input", a_release_event)
|
||||||
|
player.call("_input", a_event)
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("attack_time_left", 0.0)
|
||||||
|
player.call("_process", 0.2)
|
||||||
|
_expect_array(player.call("get_combo_slots"), ["A", "A"], "second held A should keep A+A in the combo window")
|
||||||
|
_expect_last_skill("skill_aa", "second held A should trigger A+A skill before charging")
|
||||||
|
_expect_bool(player.call("is_charge_active"), true, "holding second A after A+A animation should enter charge state")
|
||||||
|
_expect_string(animation_player.current_animation, "warrior_charge_intro", "holding second A should start with charge intro animation")
|
||||||
|
_expect_positive(player.call("get_charge"), "holding second A should grow charge after A+A")
|
||||||
|
requested_skills.clear()
|
||||||
|
player.call("_input", a_release_event)
|
||||||
|
_expect_bool(player.call("is_charge_active"), false, "releasing held second A should cancel A+A charge")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
|
player.call("_input", d_event)
|
||||||
|
player.call("_input", d_event)
|
||||||
|
Input.action_press("player_d")
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("velocity", Vector2.ZERO)
|
||||||
|
player.call("handle_input")
|
||||||
|
Input.action_release("player_d")
|
||||||
|
_expect_array(player.call("get_combo_slots"), ["D", "D"], "held second D should still record D+D once")
|
||||||
|
_expect_last_skill("skill_dd", "held second D should request D+D skill")
|
||||||
|
_expect_zero((player as CharacterBody2D).velocity.x, "holding consumed second D should not slide in idle")
|
||||||
|
var d_release_after_hold := InputEventKey.new()
|
||||||
|
d_release_after_hold.pressed = false
|
||||||
|
d_release_after_hold.physical_keycode = KEY_D
|
||||||
|
player.call("_input", d_release_after_hold)
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
|
player.call("_input", a_event)
|
||||||
|
player.call("_input", a_event)
|
||||||
|
Input.action_press("player_a")
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("velocity", Vector2.ZERO)
|
||||||
|
player.call("handle_input")
|
||||||
|
Input.action_release("player_a")
|
||||||
|
_expect_array(player.call("get_combo_slots"), ["A", "A"], "held second A should still record A+A once")
|
||||||
|
_expect_last_skill("skill_aa", "held second A should request A+A skill")
|
||||||
|
_expect_zero((player as CharacterBody2D).velocity.x, "holding consumed second A should not slide in idle")
|
||||||
|
var a_release_after_hold := InputEventKey.new()
|
||||||
|
a_release_after_hold.pressed = false
|
||||||
|
a_release_after_hold.physical_keycode = KEY_A
|
||||||
|
player.call("_input", a_release_after_hold)
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
Input.action_press("player_a")
|
Input.action_press("player_a")
|
||||||
player.set("state", Character.State.IDLE)
|
player.set("state", Character.State.IDLE)
|
||||||
@@ -186,20 +265,39 @@ func _init() -> void:
|
|||||||
if supports_energy:
|
if supports_energy:
|
||||||
player.set("current_energy", 0)
|
player.set("current_energy", 0)
|
||||||
player.call("submit_combo_input", "W", "perfect")
|
player.call("submit_combo_input", "W", "perfect")
|
||||||
_expect_int(player.call("get_energy"), 2, "perfect input should add two energy segments")
|
_expect_int(player.call("get_energy"), 0, "W should not add energy")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
player.call("submit_combo_input", "A", "good")
|
player.call("submit_combo_input", "A", "good")
|
||||||
_expect_int(player.call("get_energy"), 3, "good input should add one energy segment")
|
_expect_int(player.call("get_energy"), 1, "A skill should add one energy segment")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
player.call("submit_combo_input", "D", "bad")
|
player.call("submit_combo_input", "D", "bad")
|
||||||
_expect_int(player.call("get_energy"), 3, "bad input should not add energy")
|
_expect_int(player.call("get_energy"), 2, "D skill should add one energy segment")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
player.call("submit_combo_input", "S", "miss")
|
player.call("submit_combo_input", "S", "miss")
|
||||||
_expect_int(player.call("get_energy"), 3, "miss input should not add energy")
|
_expect_int(player.call("get_energy"), 2, "miss input should not add energy")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
player.call("submit_combo_input", "A", "perfect")
|
||||||
|
player.call("submit_combo_input", "A", "perfect")
|
||||||
|
_expect_int(player.call("get_energy"), 4, "A+A skill should add one energy segment")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
player.call("submit_combo_input", "D", "perfect")
|
||||||
|
player.call("submit_combo_input", "D", "perfect")
|
||||||
|
player.call("submit_combo_input", "D", "perfect")
|
||||||
|
_expect_int(player.call("get_energy"), 7, "D, D+D, and D+D+D skills should each add one energy segment")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
player.call("submit_combo_input", "A", "miss")
|
||||||
|
_expect_int(player.call("get_energy"), 7, "missed A should not add energy")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
player.call("submit_combo_input", "SP", "perfect")
|
||||||
|
_expect_int(player.call("get_energy"), 7, "Space should not add energy")
|
||||||
|
player.call("flush_pending_combo_clear")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
player.set("current_energy", 9)
|
player.set("current_energy", 9)
|
||||||
player.call("submit_combo_input", "W", "perfect")
|
player.call("submit_combo_input", "W", "perfect")
|
||||||
_expect_int(player.call("get_energy"), 10, "energy should cap at ten segments")
|
_expect_int(player.call("get_energy"), 9, "W should not change energy near cap")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
player.call("submit_combo_input", "A", "perfect")
|
||||||
|
_expect_int(player.call("get_energy"), 10, "A energy reward should cap at ten segments")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
|
|
||||||
requested_skills.clear()
|
requested_skills.clear()
|
||||||
@@ -349,34 +447,58 @@ func _init() -> void:
|
|||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
_expect_array(player.call("get_combo_slots"), [], "D+D+Space should clear combo window")
|
_expect_array(player.call("get_combo_slots"), [], "D+D+Space should clear combo window")
|
||||||
player.get("combo_window").clear("test-reset")
|
player.get("combo_window").clear("test-reset")
|
||||||
|
requested_skills.clear()
|
||||||
|
|
||||||
player.call("submit_combo_input", "SP")
|
player.call("submit_combo_input", "SP")
|
||||||
_expect_array(player.call("get_combo_slots"), ["SP"], "Space should be visible before space clear")
|
_expect_array(player.call("get_combo_slots"), ["SP"], "Space should be visible before space clear")
|
||||||
|
_expect_no_skill_requested("Space alone should not request a skill")
|
||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
_expect_array(player.call("get_combo_slots"), [], "Space should clear combo window")
|
_expect_array(player.call("get_combo_slots"), [], "Space should clear combo window")
|
||||||
|
requested_skills.clear()
|
||||||
|
var space_event := InputEventKey.new()
|
||||||
|
space_event.pressed = true
|
||||||
|
space_event.physical_keycode = KEY_SPACE
|
||||||
|
player.set("state", Character.State.IDLE)
|
||||||
|
player.set("current_skill_animation", "")
|
||||||
|
animation_player.play("warrior_idle")
|
||||||
|
player.call("_input", space_event)
|
||||||
|
Input.action_press("player_space")
|
||||||
|
Input.action_press("jump")
|
||||||
|
player.call("handle_input")
|
||||||
|
Input.action_release("jump")
|
||||||
|
Input.action_release("player_space")
|
||||||
|
_expect_int(int(player.get("state")), Character.State.IDLE, "direct Space should keep idle state")
|
||||||
|
_expect_string(animation_player.current_animation, "warrior_idle", "direct Space should keep idle animation")
|
||||||
|
_expect_no_skill_requested("direct Space key should not request a skill")
|
||||||
|
player.call("flush_pending_combo_clear")
|
||||||
|
player.get("combo_window").clear("test-reset")
|
||||||
|
|
||||||
if supports_energy:
|
if supports_energy:
|
||||||
player.set("current_energy", 0)
|
player.set("current_energy", 3)
|
||||||
player.call("submit_combo_input", "S", "perfect")
|
player.call("submit_combo_input", "S", "perfect")
|
||||||
player.call("submit_combo_input", "SP", "perfect")
|
player.call("submit_combo_input", "SP", "perfect")
|
||||||
_expect_last_skill("skill_s_projectile_1", "S+Space should request projectile skill")
|
_expect_last_skill("skill_s_projectile_1", "S+Space should request projectile skill")
|
||||||
_expect_projectile_count(1, "S+Space should fire one projectile")
|
_expect_projectile_count(1, "S+Space should fire one projectile")
|
||||||
if supports_energy:
|
if supports_energy:
|
||||||
_expect_int(player.call("get_energy"), 1, "S+Space should spend three energy after two perfect inputs")
|
_expect_int(player.call("get_energy"), 0, "S+Space should spend three energy without input rewards")
|
||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
_expect_array(player.call("get_combo_slots"), ["S", "SP"], "S+Space should not clear combo window")
|
_expect_array(player.call("get_combo_slots"), ["S", "SP"], "S+Space should not clear combo window")
|
||||||
|
if supports_energy:
|
||||||
|
player.set("current_energy", 2)
|
||||||
player.call("submit_combo_input", "SP", "perfect")
|
player.call("submit_combo_input", "SP", "perfect")
|
||||||
_expect_last_skill("skill_s_projectile_2", "S+Space+Space should request projectile skill")
|
_expect_last_skill("skill_s_projectile_2", "S+Space+Space should request projectile skill")
|
||||||
_expect_projectile_count(2, "Second Space should fire another projectile")
|
_expect_projectile_count(2, "Second Space should fire another projectile")
|
||||||
if supports_energy:
|
if supports_energy:
|
||||||
_expect_int(player.call("get_energy"), 1, "S+Space+Space should spend two energy after the next perfect input")
|
_expect_int(player.call("get_energy"), 0, "S+Space+Space should spend two energy without Space reward")
|
||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
_expect_array(player.call("get_combo_slots"), ["S", "SP", "SP"], "S+Space+Space should not clear combo window")
|
_expect_array(player.call("get_combo_slots"), ["S", "SP", "SP"], "S+Space+Space should not clear combo window")
|
||||||
|
if supports_energy:
|
||||||
|
player.set("current_energy", 1)
|
||||||
player.call("submit_combo_input", "SP", "perfect")
|
player.call("submit_combo_input", "SP", "perfect")
|
||||||
_expect_last_skill("skill_s_projectile_3", "S+Space+Space+Space should request projectile skill")
|
_expect_last_skill("skill_s_projectile_3", "S+Space+Space+Space should request projectile skill")
|
||||||
_expect_projectile_count(3, "Third Space should fire another projectile")
|
_expect_projectile_count(3, "Third Space should fire another projectile")
|
||||||
if supports_energy:
|
if supports_energy:
|
||||||
_expect_int(player.call("get_energy"), 2, "S+Space+Space+Space should spend one energy after the next perfect input")
|
_expect_int(player.call("get_energy"), 0, "S+Space+Space+Space should spend one energy without Space reward")
|
||||||
_expect_array(player.call("get_combo_slots"), ["S", "SP", "SP", "SP"], "projectile chain should fill four slots before clear")
|
_expect_array(player.call("get_combo_slots"), ["S", "SP", "SP", "SP"], "projectile chain should fill four slots before clear")
|
||||||
player.call("flush_pending_combo_clear")
|
player.call("flush_pending_combo_clear")
|
||||||
_expect_array(player.call("get_combo_slots"), [], "projectile chain should clear combo window because four slots are full")
|
_expect_array(player.call("get_combo_slots"), [], "projectile chain should clear combo window because four slots are full")
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ const WARRIOR_ANIMATIONS := [
|
|||||||
"warrior_a",
|
"warrior_a",
|
||||||
"warrior_aa",
|
"warrior_aa",
|
||||||
"warrior_aaa",
|
"warrior_aaa",
|
||||||
|
"warrior_charge_intro",
|
||||||
|
"warrior_charge_loop",
|
||||||
"warrior_charge_release",
|
"warrior_charge_release",
|
||||||
"warrior_s_projectile",
|
"warrior_s_projectile",
|
||||||
"warrior_a_space_space",
|
"warrior_a_space_space",
|
||||||
|
|||||||
Reference in New Issue
Block a user