Win, lose screen. Game version info. Matador a lot more aggressive/wins easier (tweaks needed). No more score/combo/waves

This commit is contained in:
2026-07-30 12:39:51 +03:00
parent ebb6ec6335
commit bf7a454ad0
28 changed files with 1229 additions and 451 deletions
+55 -45
View File
@@ -38,6 +38,7 @@ var _ability_timer: float = 0.0
var _active_ability: int = -1
var _dash_dir: Vector3 = Vector3.ZERO
var _roll_dir: Vector3 = Vector3.ZERO
var _roll_ramp: float = 0.0
var _bull_anim: AnimationPlayer = null
var _charge_ready_emitter: CPUParticles3D = null
@@ -54,9 +55,11 @@ var _charge_bar_ramp: Gradient = null
var _charge_bar_time: float = 0.0
var _charge_bar_shown: float = 0.0
const MAX_HEALTH: int = 10
var health: int = MAX_HEALTH
signal health_changed(new_health: int)
# One clean hit ends the run — see take_sword_hit(). The HUD listens for `died`
# to raise the lose screen (the `cause` tags how it happened, for the death notice);
# _dead latches so a flurry of hits fires it only once.
signal died(cause: String)
var _dead: bool = false
var _huff_player: AudioStreamPlayer = null
var _crowd_player: AudioStreamPlayer = null
@@ -460,15 +463,16 @@ func _activate_roll() -> void:
_ability_active = true
_active_ability = 3
_ability_timer = DP.f("roll_duration")
# Combine with any charge already underway: keep the current heading + speed,
# floored to a launch minimum so even a standing roll shoves off like a boulder.
_roll_ramp = 0.0
# Curl into the ball along the current heading (or facing when standing still)
# and launch at the base speed — it ramps up from here the longer you roll.
var flat := Vector3(velocity.x, 0.0, velocity.z)
if flat.length() > 1.0:
_roll_dir = flat.normalized()
else:
var f := cube_guy.global_transform.basis.z
_roll_dir = Vector3(f.x, 0.0, f.z).normalized()
var launch := maxf(flat.length(), DP.f("roll_min_speed"))
var launch := DP.f("roll_base_speed")
velocity.x = _roll_dir.x * launch
velocity.z = _roll_dir.z * launch
if _bull_anim and _bull_anim.has_animation(_ANIM_ROLL):
@@ -483,23 +487,21 @@ func _activate_roll() -> void:
_huff_player.play()
# One rolling frame: steer, ramp/retain speed, move, then ricochet off walls and
# bowl through matadors. Fully replaces the normal locomotion path while active.
# One rolling frame (Rammus Powerball): steer with momentum, accelerate the longer
# the roll lasts, move (walls just slide), then pop the ball on the first matador we
# ram. Fully replaces the normal locomotion path while active.
func _tick_roll(delta: float, steer: Vector3) -> void:
var steer_flat := Vector3(steer.x, 0.0, steer.z)
if steer_flat.length() > 0.1:
var t := clampf(DP.f("roll_turn") * delta, 0.0, 1.0)
_roll_dir = _roll_dir.slerp(steer_flat.normalized(), t).normalized()
# Ramp toward the cruise top speed; anything above it (from wall boosts) bleeds
# off slowly via roll_momentum instead of snapping back — that's the fun carry.
var speed := Vector2(velocity.x, velocity.z).length()
# Speed builds over time: base → max across roll_rampup_time, so a long roll
# winds up into a runaway boulder while a quick tap barely gets going.
_roll_ramp += delta
var ramp := clampf(_roll_ramp / maxf(DP.f("roll_rampup_time"), 0.01), 0.0, 1.0)
var top := DP.f("roll_max_speed")
if speed < top:
speed = move_toward(speed, top, DP.f("roll_accel") * delta)
else:
speed = maxf(top, speed * pow(DP.f("roll_momentum"), delta))
speed = minf(speed, DP.f("roll_wall_cap"))
var speed := lerpf(DP.f("roll_base_speed"), top, ramp)
velocity.x = _roll_dir.x * speed
velocity.z = _roll_dir.z * speed
@@ -511,40 +513,43 @@ func _tick_roll(delta: float, steer: Vector3) -> void:
if _charge_trail_emitter:
_charge_trail_emitter.direction = (-_roll_dir + Vector3(0.0, 0.25, 0.0)).normalized()
var pre_wall := Vector3(velocity.x, 0.0, velocity.z)
move_and_slide()
_was_on_floor = is_on_floor()
_roll_wall_ricochet(pre_wall)
# Bowling strike: fling matadors we plow into up and outward like pins (and the
# resulting kills feed the HUD combo meter).
_hit_matadors_radius(DP.f("roll_hit_radius"), DP.f("roll_hit_strength"), DP.f("roll_hit_up"))
_roll_try_pop(speed)
# On hitting a wall mid-roll, mirror the heading and BOOST speed (capped) instead of
# bleeding it — banking off walls is how you build ludicrous momentum.
func _roll_wall_ricochet(pre_wall: Vector3) -> void:
for i in get_slide_collision_count():
var col := get_slide_collision(i)
var normal := col.get_normal()
if absf(normal.y) >= 0.5:
# Pop the ball on ramming a matador: launch every matador in the hit radius up and
# outward, then end the roll (Rammus stops when Powerball connects). Returns true if
# it popped so the caller can stop touching roll state this frame.
func _roll_try_pop(speed: float) -> bool:
var radius := DP.f("roll_hit_radius")
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() > radius:
continue
var flat_n := Vector3(normal.x, 0.0, normal.z).normalized()
var refl := pre_wall - 2.0 * pre_wall.dot(flat_n) * flat_n
refl.y = 0.0
if refl.length() < 0.01:
return
_roll_dir = refl.normalized()
var boosted := minf(pre_wall.length() * DP.f("roll_wall_boost"), DP.f("roll_wall_cap"))
velocity.x = _roll_dir.x * boosted
velocity.z = _roll_dir.z * boosted
var hit := col.get_position()
_spawn_kick_sparks(hit, flat_n)
_spawn_kick_flash(hit)
camera_pivot.call(&"trigger_hit", clampf(boosted / 90.0, 0.25, 1.0))
_hit_matadors_radius(radius, DP.f("roll_hit_strength"), DP.f("roll_knockup"))
camera_pivot.call(&"trigger_hit", clampf(speed / 90.0, 0.35, 1.0))
if _huff_player:
_huff_player.pitch_scale = randf_range(1.05, 1.35)
_huff_player.play()
return
_end_roll()
return true
return false
# 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
_ability_timer = 0.0
_roll_ramp = 0.0
_legs.set(&"charge_pitch_target", 0.0)
_legs.set(&"tail_ragdoll", false)
velocity.x *= 0.15
velocity.z *= 0.15
_play_idle()
func _hit_matadors_cone(range_m: float, half_angle_rad: float, strength: float,
@@ -1065,6 +1070,11 @@ func _physics_process(delta: float) -> void:
# ── Hoof sounds ───────────────────────────────────────────────────────────
func take_sword_hit() -> void:
health = maxi(0, health - 1)
health_changed.emit(health)
# A single clean hit — thrown or swung blade — ends the run. `cause` ("gored" /
# "thrown") tags how it happened for the death notice. Latches so the lose screen is
# raised exactly once even if several blades land on the same frame.
func take_sword_hit(cause: String = "") -> void:
if _dead:
return
_dead = true
died.emit(cause)