feat(*): Add 3-2-1 kickoff countdown before play and after goals

This commit is contained in:
Josh Creek
2026-07-28 19:11:36 +01:00
parent 2533827cf8
commit 93f90ca6de
4 changed files with 64 additions and 13 deletions
+11
View File
@@ -9,6 +9,7 @@ class_name HUDController
@onready var score_row = get_node_or_null("Control/ScoreRow")
@onready var team0_score_label = get_node_or_null("Control/ScoreRow/Team0Score")
@onready var team1_score_label = get_node_or_null("Control/ScoreRow/Team1Score")
@onready var kickoff_label = get_node_or_null("Control/KickoffLabel")
@onready var result_overlay = get_node_or_null("Control/ResultOverlay")
@onready var result_label = get_node_or_null("Control/ResultOverlay/Center/VBox/ResultLabel")
@onready var final_score_label = get_node_or_null("Control/ResultOverlay/Center/VBox/FinalScoreLabel")
@@ -72,6 +73,9 @@ func _initialize_hud():
if game_manager and game_manager.has_signal("match_ended"):
game_manager.match_ended.connect(_on_match_ended)
if game_manager and game_manager.has_signal("kickoff_countdown"):
game_manager.kickoff_countdown.connect(_on_kickoff_countdown)
func _connect_ship_signals():
# Route ship telemetry to the flight instruments
if ship.has_signal("speed_changed"):
@@ -127,6 +131,13 @@ func _on_score_changed(score: Dictionary):
if team1_score_label and is_instance_valid(team1_score_label):
team1_score_label.text = str(score.get(1, 0))
func _on_kickoff_countdown(count: int):
if not kickoff_label or not is_instance_valid(kickoff_label):
return
kickoff_label.visible = count > 0
if count > 0:
kickoff_label.text = str(count)
func _on_match_ended(winning_team: int, score: Dictionary):
if not result_overlay or not is_instance_valid(result_overlay):
return
+31
View File
@@ -9,9 +9,12 @@ signal timer_updated(minutes: int, seconds: int)
signal score_changed(score: Dictionary)
# winning_team is -1 for a draw.
signal match_ended(winning_team: int, score: Dictionary)
# Counts 3, 2, 1, then 0 ("go", hide the label).
signal kickoff_countdown(count: int)
# How long the result overlay stays up before returning to the menu.
const RESULT_SCREEN_SECONDS := 5.0
const KICKOFF_COUNTDOWN_SECONDS := 3
@export var match_length_seconds := 150.0
@@ -32,6 +35,8 @@ func _start() -> void:
spawn_camera_rig(player_ship)
spawn_ship(1, 0, _make_opponent_controller())
await _run_kickoff_countdown()
match_timer = Timer.new()
match_timer.one_shot = true
match_timer.wait_time = match_length_seconds
@@ -66,8 +71,34 @@ func _on_goal_scored(conceding_team: int) -> void:
score[scoring_team] += 1
score_changed.emit(score.duplicate())
print("Goal for team %d! Score: %d - %d" % [scoring_team, score[0], score[1]])
await _run_kickoff_countdown()
# Used both for the initial kickoff and after every goal. Freezes ball/ships
# via RigidBody3D.freeze — a built-in engine property — so this needs zero
# changes to ship.gd/ball.gd/ship_controller.gd, keeping training_mode.gd
# (which never calls into this file) completely untouched.
func _run_kickoff_countdown() -> void:
reset_ball()
reset_ships()
_set_frozen(true)
for count in range(KICKOFF_COUNTDOWN_SECONDS, 0, -1):
kickoff_countdown.emit(count)
# process_always=false: if full-time fires mid-countdown (see
# _on_match_timer_timeout's get_tree().paused = true), this stalls
# harmlessly in lockstep with the pause instead of ticking a
# countdown label over the results screen.
await get_tree().create_timer(1.0, false).timeout
kickoff_countdown.emit(0)
_set_frozen(false)
func _set_frozen(frozen: bool) -> void:
if is_instance_valid(ball):
ball.set_deferred("freeze", frozen)
for ship in ships:
if is_instance_valid(ship):
ship.set_deferred("freeze", frozen)
func _on_match_timer_timeout() -> void: