abilities first draft
This commit is contained in:
@@ -17,13 +17,18 @@ var _legs: Node
|
||||
var _kick_timer: float = 0.0
|
||||
var _was_on_floor: bool = false
|
||||
var _pre_slide_vel_y: float = 0.0
|
||||
var _charge_dir: Vector3 = Vector3.ZERO
|
||||
var _charge_hold_time: float = 0.0
|
||||
var _both_held: bool = false
|
||||
var _thrust_left_charge: float = 0.0
|
||||
var _thrust_right_charge: float = 0.0
|
||||
var _charge_was_full: bool = false
|
||||
|
||||
# Ability system — kick=0, slam=1, dash=2
|
||||
var ability_cd: Array[float] = [0.0, 0.0, 0.0]
|
||||
var _ability_active: bool = false
|
||||
var _ability_timer: float = 0.0
|
||||
var _active_ability: int = -1
|
||||
var _dash_dir: Vector3 = Vector3.ZERO
|
||||
var _bull_anim: AnimationPlayer = null
|
||||
|
||||
var _charge_overlay: ColorRect = null
|
||||
var _charge_ready_emitter: CPUParticles3D = null
|
||||
|
||||
@@ -42,6 +47,7 @@ func _ready() -> void:
|
||||
_setup_legs()
|
||||
_setup_charge_indicators()
|
||||
_setup_audio()
|
||||
_bull_anim = _find_anim_player(cube_guy)
|
||||
DP.any_changed.connect(_on_dp_changed)
|
||||
_roll_damping = DP.f("roll_damping")
|
||||
|
||||
@@ -117,7 +123,7 @@ func _setup_charge_indicators() -> void:
|
||||
var ui := CanvasLayer.new()
|
||||
add_child(ui)
|
||||
_charge_overlay = ColorRect.new()
|
||||
_charge_overlay.color = Color(0.7, 0.05, 0.0, 0.0)
|
||||
_charge_overlay.color = Color(0.9, 0.55, 0.0, 0.0)
|
||||
_charge_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_charge_overlay.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
ui.add_child(_charge_overlay)
|
||||
@@ -204,6 +210,98 @@ func _on_dp_changed(_key: String, _val: Variant) -> void:
|
||||
_set_dust_state(prev)
|
||||
|
||||
|
||||
func _find_anim_player(node: Node) -> AnimationPlayer:
|
||||
if node is AnimationPlayer:
|
||||
return node as AnimationPlayer
|
||||
for child: Node in node.get_children():
|
||||
var r := _find_anim_player(child)
|
||||
if r:
|
||||
return r
|
||||
return null
|
||||
|
||||
|
||||
func _unhandled_input(event: InputEvent) -> void:
|
||||
if _ability_active:
|
||||
return
|
||||
if event.is_action_pressed(&"ability_kick") and ability_cd[0] <= 0.0:
|
||||
_activate_kick()
|
||||
elif event.is_action_pressed(&"ability_slam") and ability_cd[1] <= 0.0:
|
||||
_activate_slam()
|
||||
elif event.is_action_pressed(&"ability_dash") and ability_cd[2] <= 0.0:
|
||||
_activate_dash()
|
||||
|
||||
|
||||
func ability_cooldown_fraction(slot: int) -> float:
|
||||
var maxcd := 0.0
|
||||
match slot:
|
||||
0: maxcd = DP.f("kick_cooldown")
|
||||
1: maxcd = DP.f("slam_cooldown")
|
||||
2: maxcd = DP.f("dash_cooldown")
|
||||
return ability_cd[slot] / maxcd if maxcd > 0.0 else 0.0
|
||||
|
||||
|
||||
func _activate_kick() -> void:
|
||||
ability_cd[0] = DP.f("kick_cooldown")
|
||||
_ability_active = true
|
||||
_active_ability = 0
|
||||
_ability_timer = 0.6
|
||||
if _bull_anim and _bull_anim.has_animation(&"Armature|FOOTKICK"):
|
||||
_bull_anim.play(&"Armature|FOOTKICK")
|
||||
_ability_timer = _bull_anim.get_animation(&"Armature|FOOTKICK").length
|
||||
_hit_matadors_cone(DP.f("kick_range"), deg_to_rad(60.0), DP.f("kick_strength"))
|
||||
|
||||
|
||||
func _activate_slam() -> void:
|
||||
ability_cd[1] = DP.f("slam_cooldown")
|
||||
_ability_active = true
|
||||
_active_ability = 1
|
||||
_ability_timer = 0.9
|
||||
if _bull_anim and _bull_anim.has_animation(&"Armature|SLAM"):
|
||||
_bull_anim.play(&"Armature|SLAM")
|
||||
_ability_timer = _bull_anim.get_animation(&"Armature|SLAM").length
|
||||
_hit_matadors_radius(DP.f("slam_range"), DP.f("slam_strength"))
|
||||
|
||||
|
||||
func _activate_dash() -> void:
|
||||
ability_cd[2] = DP.f("dash_cooldown")
|
||||
_ability_active = true
|
||||
_active_ability = 2
|
||||
_ability_timer = 0.45
|
||||
if _bull_anim and _bull_anim.has_animation(&"Armature|DASH"):
|
||||
_bull_anim.play(&"Armature|DASH")
|
||||
_ability_timer = _bull_anim.get_animation(&"Armature|DASH").length
|
||||
var raw := cube_guy.global_transform.basis.z
|
||||
_dash_dir = Vector3(raw.x, 0.0, raw.z).normalized()
|
||||
velocity.x = _dash_dir.x * DP.f("dash_speed")
|
||||
velocity.z = _dash_dir.z * DP.f("dash_speed")
|
||||
_legs.set(&"charge_pitch_target", DP.f("charge_head_pitch"))
|
||||
if _huff_player:
|
||||
_huff_player.pitch_scale = randf_range(0.9, 1.1)
|
||||
_huff_player.play()
|
||||
|
||||
|
||||
func _hit_matadors_cone(range_m: float, half_angle_rad: float, strength: float) -> void:
|
||||
var raw := cube_guy.global_transform.basis.z
|
||||
var forward := Vector3(raw.x, 0.0, raw.z).normalized()
|
||||
for mat: Node in get_tree().get_nodes_in_group(&"matador"):
|
||||
var to_mat: Vector3 = (mat as Node3D).global_position - global_position
|
||||
to_mat.y = 0.0
|
||||
if to_mat.length() > range_m:
|
||||
continue
|
||||
if to_mat.length() > 0.01 and forward.dot(to_mat.normalized()) < cos(half_angle_rad):
|
||||
continue
|
||||
mat.call(&"apply_ability_hit", (forward + Vector3.UP * 0.4).normalized(), strength)
|
||||
|
||||
|
||||
func _hit_matadors_radius(range_m: float, strength: float) -> void:
|
||||
for mat: Node in get_tree().get_nodes_in_group(&"matador"):
|
||||
var to_mat: Vector3 = (mat as Node3D).global_position - global_position
|
||||
to_mat.y = 0.0
|
||||
if to_mat.length() > range_m:
|
||||
continue
|
||||
var away := to_mat.normalized() if to_mat.length() > 0.01 \
|
||||
else Vector3(randf() - 0.5, 0.0, randf() - 0.5).normalized()
|
||||
mat.call(&"apply_ability_hit", (away + Vector3.UP * 0.6).normalized(), strength)
|
||||
|
||||
|
||||
func _apply_thrust(dir: Vector3) -> void:
|
||||
@@ -250,46 +348,27 @@ func _physics_process(delta: float) -> void:
|
||||
|
||||
var on_floor := is_on_floor()
|
||||
|
||||
# ── Mouse button actions ──────────────────────────────────────────────────
|
||||
# Both held → charge: lock direction, ramp speed, lower head
|
||||
# Left only → thrust NE (forward-right diagonal)
|
||||
# Right only → thrust NW (forward-left diagonal)
|
||||
# ── Ability cooldowns ────────────────────────────────────────────────────────
|
||||
for i: int in 3:
|
||||
ability_cd[i] = maxf(0.0, ability_cd[i] - delta)
|
||||
if _ability_active:
|
||||
_ability_timer -= delta
|
||||
if _ability_timer <= 0.0:
|
||||
_ability_active = false
|
||||
_active_ability = -1
|
||||
_legs.set(&"charge_pitch_target", 0.0)
|
||||
|
||||
# ── Mouse button single-thrust actions ───────────────────────────────────────
|
||||
# Left only → thrust NE (forward-right diagonal)
|
||||
# Right only → thrust NW (forward-left diagonal)
|
||||
var left_held := Input.is_action_pressed(&"thrust_left")
|
||||
var right_held := Input.is_action_pressed(&"thrust_right")
|
||||
var both_held := left_held and right_held
|
||||
|
||||
if both_held and not _both_held:
|
||||
var raw := cube_guy.global_transform.basis.z
|
||||
_charge_dir = Vector3(raw.x, 0.0, raw.z).normalized()
|
||||
if _huff_player:
|
||||
_huff_player.pitch_scale = randf_range(0.9, 1.1)
|
||||
_huff_player.play()
|
||||
_huff_timer = randf_range(1.4, 2.2)
|
||||
|
||||
if both_held:
|
||||
if left_held and right_held:
|
||||
_thrust_left_charge = 0.0
|
||||
_thrust_right_charge = 0.0
|
||||
_charge_hold_time += delta
|
||||
_huff_timer -= delta
|
||||
if _huff_timer <= 0.0 and _huff_player and not _huff_player.playing:
|
||||
_huff_player.pitch_scale = randf_range(0.9, 1.1)
|
||||
_huff_player.play()
|
||||
_huff_timer = randf_range(1.4, 2.2)
|
||||
var ramp := minf(_charge_hold_time / DP.f("charge_ramp_time"), 1.0)
|
||||
var accel := DP.f("bull_charge_accel") * lerpf(0.2, 1.0, ramp)
|
||||
velocity.x += _charge_dir.x * accel * delta
|
||||
velocity.z += _charge_dir.z * accel * delta
|
||||
var flat_c := Vector2(velocity.x, velocity.z)
|
||||
var spd_c := flat_c.length()
|
||||
if spd_c > DP.f("bull_charge_max_speed"):
|
||||
velocity.x *= DP.f("bull_charge_max_speed") / spd_c
|
||||
velocity.z *= DP.f("bull_charge_max_speed") / spd_c
|
||||
_legs.set(&"charge_pitch_target", DP.f("charge_head_pitch"))
|
||||
else:
|
||||
_charge_hold_time = 0.0
|
||||
_legs.set(&"charge_pitch_target", 0.0)
|
||||
|
||||
if left_held and not right_held:
|
||||
if left_held:
|
||||
_thrust_left_charge = minf(_thrust_left_charge + delta, DP.f("thrust_charge_time"))
|
||||
elif Input.is_action_just_released(&"thrust_left") and _thrust_left_charge > 0.0:
|
||||
var t := _thrust_left_charge / DP.f("thrust_charge_time")
|
||||
@@ -298,7 +377,7 @@ func _physics_process(delta: float) -> void:
|
||||
_apply_thrust(Vector3(raw.x, 0.0, raw.z).normalized().rotated(Vector3.UP, -angle))
|
||||
_thrust_left_charge = 0.0
|
||||
|
||||
if right_held and not left_held:
|
||||
if right_held:
|
||||
_thrust_right_charge = minf(_thrust_right_charge + delta, DP.f("thrust_charge_time"))
|
||||
elif Input.is_action_just_released(&"thrust_right") and _thrust_right_charge > 0.0:
|
||||
var t := _thrust_right_charge / DP.f("thrust_charge_time")
|
||||
@@ -314,12 +393,10 @@ func _physics_process(delta: float) -> void:
|
||||
_charge_ready_emitter.restart()
|
||||
_charge_was_full = either_full
|
||||
|
||||
# Fade red overlay in while charge-ahead, out when released
|
||||
var target_alpha := 0.30 if both_held else 0.0
|
||||
# Briefly flash overlay when dash is active
|
||||
var target_alpha := 0.20 if (_active_ability == 2 and _ability_active) else 0.0
|
||||
var oc := _charge_overlay.color
|
||||
_charge_overlay.color = Color(oc.r, oc.g, oc.b, lerpf(oc.a, target_alpha, delta * 10.0))
|
||||
|
||||
_both_held = both_held
|
||||
_charge_overlay.color = Color(oc.r, oc.g, oc.b, lerpf(oc.a, target_alpha, delta * 12.0))
|
||||
|
||||
# Reset kick timer on landing so the first kick after a bounce is immediate
|
||||
if on_floor and not _was_on_floor and direction:
|
||||
@@ -328,7 +405,7 @@ func _physics_process(delta: float) -> void:
|
||||
|
||||
# ── Periodic kick ─────────────────────────────────────────────────────────
|
||||
_kick_timer = maxf(0.0, _kick_timer - delta)
|
||||
if direction and _kick_timer <= 0.0 and not both_held:
|
||||
if direction and _kick_timer <= 0.0:
|
||||
_apply_kick(direction)
|
||||
_kick_timer = DP.f("kick_interval")
|
||||
|
||||
@@ -362,8 +439,6 @@ func _physics_process(delta: float) -> void:
|
||||
velocity.x = flat_n.x * exit_spd
|
||||
velocity.z = flat_n.z * exit_spd
|
||||
pre_wall_vel = Vector3(velocity.x, 0.0, velocity.z)
|
||||
if both_held and _charge_dir.dot(flat_n) < 0.0:
|
||||
_charge_dir = _charge_dir.reflect(flat_n)
|
||||
|
||||
# ── Bounce on landing ─────────────────────────────────────────────────────
|
||||
if is_on_floor() and not _was_on_floor:
|
||||
|
||||
Reference in New Issue
Block a user