Combat overhaul: charge/gore, bear ram, matador taunt reactions

Bull charge damage:
- Tunnel-proof swept detection (prev→now segment) for matador gore and bear
  ram, so a 66 m/s dash no longer skips clean over the thin HitArea.
- Horns-first cone (bull_gore_arc): running into an enemy only wounds it when
  charging roughly at it, not a sideways brush.
- Dash is a committed lunge: bypasses the horns cone AND the survival roll, so a
  dash connect is a guaranteed kill. Cruise charges keep a speed-scaled roll
  (mat_charge_pierce). Roll ability defers to its own pop (is_rolling guard).

Bear:
- Horns-first swept ram detection (edge-triggered: one charge = one wound);
  a tunnelling or point-blank charge now lands where body_entered wouldn't.
- Overhead smash lane extends faster (0.1s) and further (20 m).
- Leap slam launches the bull from anywhere in the circle (ring_frac 0).
- Removed stray-quote syntax errors that broke the script.

Matador:
- Collider-driven stab (BladeHitbox) with recovery beat + taunt-after-hit so it
  stops machine-gunning the bull.
- A taunting matador answers a charge: spear from range, sword up close.

Spear projectile masks the BULL layer so throws connect.

Tests: matador_charge/stab/spear/taunt_react, bear_ram/hitbox/fx (+ unified
_fake_bull stand-in). Trimmed verbose comments.

Note: bear claw-emitter/death work is still WIP (3 bear_boss_test failures).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 00:59:23 +03:00
parent 4efc6763f0
commit 5c1e881a53
39 changed files with 1555 additions and 376 deletions
+25 -7
View File
@@ -573,8 +573,20 @@ func _mat_active(mat: Node) -> bool:
return is_instance_valid(mat) and mat.has_method(&"is_active") and mat.call(&"is_active")
# End the Powerball early: brake hard (the ball "pops" and unrolls) and drop back to
# the normal locomotion path next frame.
# True while the boulder roll is live — matadors defer their charge-gore to the roll's own pop
# (is_rolling guard in matador._take_bull_charge) so the ball pops instead of quietly goring.
func is_rolling() -> bool:
return _active_ability == 3 and _ability_active
# True while the dash ability is live. A dash is a committed lunge — it gores/rams on contact
# regardless of angle (enemies bypass their horns-first cone while it's set).
func is_dashing() -> bool:
return _active_ability == 1 and _ability_active
# End the Powerball early: brake hard (the ball "pops" and unrolls) and drop back to the normal
# locomotion path next frame.
func _end_roll() -> void:
_ability_active = false
_active_ability = -1
@@ -1025,7 +1037,7 @@ func _physics_process(delta: float) -> void:
_pre_slide_vel_y = velocity.y
var pre_wall_vel := Vector3(velocity.x, 0.0, velocity.z)
move_and_slide()
# ── Wall bounce — no sliding, guaranteed exit ────────────────────────────
@@ -1042,15 +1054,15 @@ 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)
# --- ADD THIS BREAKABLE BARREL DETECTION CODE ---
var collision = get_slide_collision(i)
var collider = collision.get_collider()
# Check if the object we hit has the "destroy_barrel" function
if collider and collider.has_method("destroy_barrel"):
# Optional: Only break if moving fast (e.g., sprinting or dashing)
# if velocity.length() > 5.0:
# if velocity.length() > 5.0:
collider.destroy_barrel()
# ── Landing. Don't bounce ─────────────────────────────────────────────────────
@@ -1126,6 +1138,12 @@ func take_sword_hit(cause: String = "", hit_pos: Vector3 = Vector3.ZERO) -> void
died.emit(cause)
# Pop the bull upward — a smash hitbox knocks it off its feet. Only ever raises the
# vertical speed so a downward-moving bull still gets launched.
func apply_knock_up(v: float) -> void:
velocity.y = maxf(velocity.y, v)
# Blood spurt from a spear wound: a short-lived spray of dark-red gobs flung out of the
# entry point, biased outward from the bull's centre (so it reads as bursting from the
# hide) with a touch of lift before gravity drags it down.
@@ -1167,7 +1185,7 @@ func _spawn_blood(hit_pos: Vector3) -> void:
p.global_position = hit_pos
p.restart()
_free_after(p, 1.5)
#hit flashing
func _hit_flash() -> void: