mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 08:23:45 +00:00
feat(*): Add bot selection, score HUD, and winner reveal to matches, replace HUD text with aircraft-style flight instruments, and fix camera judder
This commit is contained in:
@@ -1,18 +1,28 @@
|
||||
# HUD Controller - handles all display logic and label updates
|
||||
# Receives ship data via signals and updates UI accordingly
|
||||
# Simplified version focusing on labels only
|
||||
# HUD Controller - discovers the ship/camera/game mode via groups and routes
|
||||
# their signals to the display widgets. All flight data is rendered by
|
||||
# aircraft-style instruments (attitude indicator, heading tape, bar gauges) —
|
||||
# this node is only the wiring hub; the instruments are dumb displays.
|
||||
extends CanvasLayer
|
||||
class_name HUDController
|
||||
|
||||
# UI Label References
|
||||
@onready var timer_label = get_node("Control/TimerLabel")
|
||||
@onready var speed_label = get_node("Control/InfoPanel/SpeedLabel")
|
||||
@onready var altitude_label = get_node("Control/InfoPanel/AltitudeLabel")
|
||||
@onready var angular_vel_label = get_node("Control/InfoPanel/AngularVelLabel")
|
||||
@onready var camera_mode_label = get_node("Control/InfoPanel/CameraModeLabel")
|
||||
@onready var attitude_label = get_node("Control/InfoPanel/AttitudeLabel")
|
||||
@onready var heading_label = get_node("Control/InfoPanel/HeadingLabel")
|
||||
@onready var thrust_label = get_node("Control/InfoPanel/ThrustLabel")
|
||||
@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 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")
|
||||
|
||||
@onready var heading_tape: HudHeadingTape = get_node_or_null("Control/Instruments/HeadingTape")
|
||||
@onready var attitude_indicator: HudAttitudeIndicator = get_node_or_null("Control/Instruments/Cluster/Dials/AttitudeIndicator")
|
||||
@onready var speed_gauge: HudGauge = get_node_or_null("Control/Instruments/Cluster/Dials/SpeedGauge")
|
||||
@onready var altitude_gauge: HudGauge = get_node_or_null("Control/Instruments/Cluster/Dials/AltitudeGauge")
|
||||
@onready var thrust_bar: HudGauge = get_node_or_null("Control/Instruments/Cluster/ThrustBar")
|
||||
@onready var camera_mode_label = get_node_or_null("Control/Instruments/Cluster/CameraModeLabel")
|
||||
|
||||
# Team identity, mirroring TEAM_COLORS in ship.gd
|
||||
const TEAM_NAMES := {0: "Blue", 1: "Orange"}
|
||||
const TEAM_COLORS := {0: Color(0.25, 0.55, 1.0), 1: Color(1.0, 0.5, 0.15)}
|
||||
|
||||
var ship: Node
|
||||
|
||||
@@ -29,6 +39,11 @@ func _initialize_hud():
|
||||
return
|
||||
|
||||
print("HUDController: Found ship: ", ship.name)
|
||||
# Gauge ranges derive from the ship and arena rather than restating numbers
|
||||
if speed_gauge and "max_speed" in ship:
|
||||
speed_gauge.max_value = ship.max_speed
|
||||
if altitude_gauge:
|
||||
altitude_gauge.max_value = ArenaBoundary.INNER_HEIGHT
|
||||
_connect_ship_signals()
|
||||
|
||||
# Camera mode comes from the camera rig, not the ship
|
||||
@@ -46,15 +61,23 @@ func _initialize_hud():
|
||||
if timer_label and is_instance_valid(timer_label):
|
||||
timer_label.visible = has_timer
|
||||
|
||||
# Score display follows the same pattern: modes without scoring
|
||||
# (e.g. free play) just don't show it
|
||||
var has_score = game_manager and game_manager.has_signal("score_changed")
|
||||
if has_score:
|
||||
game_manager.score_changed.connect(_on_score_changed)
|
||||
if score_row and is_instance_valid(score_row):
|
||||
score_row.visible = has_score
|
||||
|
||||
if game_manager and game_manager.has_signal("match_ended"):
|
||||
game_manager.match_ended.connect(_on_match_ended)
|
||||
|
||||
func _connect_ship_signals():
|
||||
# Connect ship signals to label update methods
|
||||
# Route ship telemetry to the flight instruments
|
||||
if ship.has_signal("speed_changed"):
|
||||
ship.speed_changed.connect(_on_ship_speed_changed)
|
||||
print("HUDController: Connected to speed_changed signal")
|
||||
if ship.has_signal("altitude_changed"):
|
||||
ship.altitude_changed.connect(_on_ship_altitude_changed)
|
||||
if ship.has_signal("angular_velocity_changed"):
|
||||
ship.angular_velocity_changed.connect(_on_ship_angular_velocity_changed)
|
||||
if ship.has_signal("attitude_changed"):
|
||||
ship.attitude_changed.connect(_on_ship_attitude_changed)
|
||||
if ship.has_signal("heading_changed"):
|
||||
@@ -62,38 +85,33 @@ func _connect_ship_signals():
|
||||
if ship.has_signal("thrust_changed"):
|
||||
ship.thrust_changed.connect(_on_ship_thrust_changed)
|
||||
|
||||
# Signal handlers for ship data - these update the legacy text labels
|
||||
# All handlers include null checks for robustness
|
||||
# Instrument signal handlers - all include null checks for robustness
|
||||
func _on_ship_speed_changed(speed: float):
|
||||
if speed_label and is_instance_valid(speed_label):
|
||||
speed_label.text = "Speed: %.1f m/s" % speed
|
||||
if speed_gauge and is_instance_valid(speed_gauge):
|
||||
speed_gauge.set_value(speed)
|
||||
|
||||
func _on_ship_altitude_changed(altitude: float):
|
||||
if altitude_label and is_instance_valid(altitude_label):
|
||||
altitude_label.text = "Altitude: %.1f m" % altitude
|
||||
if altitude_gauge and is_instance_valid(altitude_gauge):
|
||||
altitude_gauge.set_value(altitude)
|
||||
|
||||
func _on_ship_angular_velocity_changed(angular_speed: float):
|
||||
if angular_vel_label and is_instance_valid(angular_vel_label):
|
||||
angular_vel_label.text = "Angular Vel: %.2f rad/s" % angular_speed
|
||||
func _on_ship_attitude_changed(pitch: float, roll: float, _yaw: float):
|
||||
if attitude_indicator and is_instance_valid(attitude_indicator):
|
||||
attitude_indicator.set_attitude(pitch, roll)
|
||||
|
||||
func _on_ship_heading_changed(heading_degrees: float):
|
||||
if heading_tape and is_instance_valid(heading_tape):
|
||||
heading_tape.set_heading(heading_degrees)
|
||||
|
||||
func _on_ship_thrust_changed(thrust_percent: float):
|
||||
if thrust_bar and is_instance_valid(thrust_bar):
|
||||
thrust_bar.set_value(thrust_percent)
|
||||
|
||||
func _on_ship_camera_mode_changed(is_ball_cam: bool):
|
||||
if camera_mode_label and is_instance_valid(camera_mode_label):
|
||||
var camera_mode = "Ball Cam" if is_ball_cam else "Ship Cam"
|
||||
camera_mode_label.text = "Camera: %s" % camera_mode
|
||||
|
||||
func _on_ship_attitude_changed(pitch: float, roll: float, _yaw: float):
|
||||
if attitude_label and is_instance_valid(attitude_label):
|
||||
attitude_label.text = "Pitch: %.0f° Roll: %.0f°" % [pitch, roll]
|
||||
|
||||
func _on_ship_heading_changed(heading_degrees: float):
|
||||
if heading_label and is_instance_valid(heading_label):
|
||||
heading_label.text = "Heading: %.0f°" % heading_degrees
|
||||
|
||||
func _on_ship_thrust_changed(thrust_percent: float):
|
||||
if thrust_label and is_instance_valid(thrust_label):
|
||||
thrust_label.text = "Thrust: %.0f%%" % thrust_percent
|
||||
|
||||
# Timer signal handler from game manager (restored original functionality)
|
||||
# Timer signal handler from game manager
|
||||
func _on_timer_updated(minutes: int, seconds: int):
|
||||
if timer_label and is_instance_valid(timer_label):
|
||||
timer_label.text = "%02d:%02d" % [minutes, seconds]
|
||||
@@ -102,3 +120,21 @@ func _on_timer_updated(minutes: int, seconds: int):
|
||||
func update_timer(time_text: String):
|
||||
if timer_label and is_instance_valid(timer_label):
|
||||
timer_label.text = time_text
|
||||
|
||||
func _on_score_changed(score: Dictionary):
|
||||
if team0_score_label and is_instance_valid(team0_score_label):
|
||||
team0_score_label.text = str(score.get(0, 0))
|
||||
if team1_score_label and is_instance_valid(team1_score_label):
|
||||
team1_score_label.text = str(score.get(1, 0))
|
||||
|
||||
func _on_match_ended(winning_team: int, score: Dictionary):
|
||||
if not result_overlay or not is_instance_valid(result_overlay):
|
||||
return
|
||||
if winning_team < 0:
|
||||
result_label.text = "Draw"
|
||||
result_label.remove_theme_color_override("font_color")
|
||||
else:
|
||||
result_label.text = "%s team wins!" % TEAM_NAMES[winning_team]
|
||||
result_label.add_theme_color_override("font_color", TEAM_COLORS[winning_team])
|
||||
final_score_label.text = "%d - %d" % [score.get(0, 0), score.get(1, 0)]
|
||||
result_overlay.visible = true
|
||||
|
||||
Reference in New Issue
Block a user