Refine combo charge controls

This commit is contained in:
wxm
2026-07-02 05:46:33 -07:00
parent 67db812de4
commit fc941cf08d
7 changed files with 354 additions and 42 deletions

View File

@@ -24,8 +24,14 @@ var charge_ready := false
var charge_active := false
var _pending_combo_clear_reason := ""
var _charge_effect_time := 0.0
var _charge_animation_time := 0.0
var _charge_hold_symbol := ""
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:
@@ -61,9 +67,11 @@ func _handle_combo_key_event(event: InputEvent) -> bool:
return false
if not key_event.pressed:
if _event_matches_key(key_event, KEY_A):
_set_movement_action_suppressed("player_a", false)
_finish_charge_hold("A")
return true
elif _event_matches_key(key_event, KEY_D):
_set_movement_action_suppressed("player_d", false)
_finish_charge_hold("D")
return true
return false
@@ -71,15 +79,17 @@ func _handle_combo_key_event(event: InputEvent) -> bool:
_submit_combo_input_from_event("W")
return true
elif _event_matches_key(key_event, KEY_A):
_set_movement_action_suppressed("player_a", true)
heading = Vector2.LEFT
var skill_id := _submit_combo_input_from_event("A")
if skill_id == "skill_a":
_submit_combo_input_from_event("A")
if _last_combo_input_accepted:
_begin_charge_hold("A", Vector2.LEFT)
return true
elif _event_matches_key(key_event, KEY_D):
_set_movement_action_suppressed("player_d", true)
heading = Vector2.RIGHT
var skill_id := _submit_combo_input_from_event("D")
if skill_id == "skill_d":
_submit_combo_input_from_event("D")
if _last_combo_input_accepted:
_begin_charge_hold("D", Vector2.RIGHT)
return true
elif _event_matches_key(key_event, KEY_S):
@@ -96,7 +106,7 @@ func handle_input() -> void:
velocity = Vector2.ZERO
return
_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")
if can_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:
_last_combo_input_accepted = false
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 symbol == "A" or symbol == "D":
_cancel_missed_direction_action()
return ""
_last_combo_input_accepted = true
var resolved := InputResolver.resolve(combo_window)
if resolved.is_empty() and _pending_combo_clear_reason == "full":
resolved = _resolve_full_window_fallback(symbol)
if not resolved.is_empty():
if not _execute_combo_skill(resolved):
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():
_schedule_combo_clear("space")
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
func _apply_energy_reward(rating_label: String) -> void:
match rating_label:
"perfect":
_change_energy(2)
"good":
func _apply_skill_energy_reward(skill_id: String) -> void:
match skill_id:
"skill_a", "skill_aa", "skill_aaa", "skill_d", "skill_dd", "skill_ddd":
_change_energy(1)
@@ -332,9 +342,7 @@ func _update_charge(delta: float) -> void:
attack_time_left = 0.0
attack_lunge_time_left = 0.0
velocity = Vector2.ZERO
var player_animation := _get_animation_player()
if player_animation != null and player_animation.has_animation("warrior_idle") and player_animation.current_animation != "warrior_idle":
player_animation.play("warrior_idle")
_update_charge_animation(delta)
charge_value = minf(charge_duration, charge_value + delta)
charge_ready = charge_value >= charge_duration
_update_charge_effect(delta)
@@ -346,6 +354,8 @@ func _start_charge() -> void:
charge_value = 0.0
charge_ready = false
_charge_effect_time = 0.0
_charge_animation_time = 0.0
_play_charge_animation("warrior_charge_intro")
_update_charge_effect(0.0)
_emit_charge_changed()
@@ -356,6 +366,7 @@ func _cancel_charge() -> void:
charge_active = false
charge_value = 0.0
charge_ready = false
_charge_animation_time = 0.0
_set_charge_effect_visible(false)
_emit_charge_changed()
@@ -378,6 +389,21 @@ func _update_charge_effect(delta: float) -> void:
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:
var sprite := _get_charge_effect_sprite()
if sprite != null:
@@ -419,9 +445,9 @@ func _apply_horizontal_movement() -> void:
if state != State.IDLE and state != State.WALK:
return
var direction := 0.0
if Input.is_action_pressed("player_a"):
if _is_movement_action_pressed("player_a"):
direction -= 1.0
if Input.is_action_pressed("player_d"):
if _is_movement_action_pressed("player_d"):
direction += 1.0
if direction < 0.0:
heading = Vector2.LEFT
@@ -430,6 +456,14 @@ func _apply_horizontal_movement() -> void:
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:
var player_animation := _get_animation_player()
if player_animation != null and player_animation.has_animation(animation_name):

View File

@@ -332,9 +332,9 @@ tracks/4/keys = {
"values": [176, 177, 178, 179, 180, 181, 182, 183]
}
[sub_resource type="Animation" id="Animation_jk2m4"]
resource_name = "warrior_charge_release"
length = 1.3333334
[sub_resource type="Animation" id="Animation_charge_intro"]
resource_name = "warrior_charge_intro"
length = 0.6666667
step = 0.083333336
tracks/0/type = "value"
tracks/0/imported = false
@@ -391,10 +391,141 @@ 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, 0.6666667, 0.75, 0.8333333, 0.9166667, 1, 1.0833334, 1.1666666, 1.25),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"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": [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"]
@@ -730,6 +861,8 @@ _data = {
&"warrior_a_space_space": SubResource("Animation_2l4js"),
&"warrior_aa": SubResource("Animation_dyp2m"),
&"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_idle": SubResource("Animation_kqtwu"),
&"warrior_s": SubResource("Animation_6eyoc"),

View File

@@ -2,6 +2,12 @@ class_name InputResolver
extends RefCounted
const SKILLS := {
"W": {
"type": "skill",
"id": "skill_w",
"animation": "warrior_w",
"clear_window": false,
},
"A": {
"type": "skill",
"id": "skill_a",