refactor(team-colors): collapse disagreeing team palettes into one source of truth

ship.gd, HUDController.gd, and goal.gd each declared their own TEAM_COLORS,
arena_boundary.gd/arena_deck.gdshader had a third pair, and HUD.tscn baked
in a fourth (hardcoded "BLUE"/"ORANGE" labels) — nose, goal rim, end zone,
and scoreboard all rendered different blues. New scripts/team_colors.gd
(class_name TeamColors) is now the single source every one of those reads
from, and team identity moves to purple/green.
This commit is contained in:
Josh Creek
2026-08-04 19:18:45 +01:00
parent ff2e40198f
commit fa9590d9c3
9 changed files with 39 additions and 30 deletions
+17 -7
View File
@@ -24,10 +24,6 @@ class_name HUDController
@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
var _last_score := {0: 0, 1: 0}
@@ -66,6 +62,20 @@ func _initialize_hud():
if timer_label and is_instance_valid(timer_label):
timer_label.visible = has_timer
# Team identity (name + color) is fixed regardless of whether this
# mode tracks score, so set it here rather than baking it into the
# scene where it can't track TeamColors.
if team0_name_label and is_instance_valid(team0_name_label):
team0_name_label.text = TeamColors.TEAM_NAMES[0]
team0_name_label.add_theme_color_override("font_color", TeamColors.TEAM_COLORS[0])
if team0_score_label and is_instance_valid(team0_score_label):
team0_score_label.add_theme_color_override("font_color", TeamColors.TEAM_COLORS[0])
if team1_score_label and is_instance_valid(team1_score_label):
team1_score_label.add_theme_color_override("font_color", TeamColors.TEAM_COLORS[1])
if team1_name_label and is_instance_valid(team1_name_label):
team1_name_label.text = TeamColors.TEAM_NAMES[1]
team1_name_label.add_theme_color_override("font_color", TeamColors.TEAM_COLORS[1])
# 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")
@@ -177,9 +187,9 @@ func _on_match_ended(winning_team: int, score: Dictionary):
result_label.remove_theme_color_override("font_color")
_set_result_panel_accent(Color(1, 1, 1, 0.2))
else:
result_label.text = "%s team wins!" % TEAM_NAMES[winning_team]
result_label.add_theme_color_override("font_color", TEAM_COLORS[winning_team])
_set_result_panel_accent(TEAM_COLORS[winning_team])
result_label.text = "%s team wins!" % TeamColors.TEAM_NAMES[winning_team]
result_label.add_theme_color_override("font_color", TeamColors.TEAM_COLORS[winning_team])
_set_result_panel_accent(TeamColors.TEAM_COLORS[winning_team])
final_score_label.text = "%d - %d" % [score.get(0, 0), score.get(1, 0)]
result_overlay.visible = true
_animate_result_panel_in()
+2 -2
View File
@@ -91,8 +91,8 @@ const DECK_SHADER_PATH := "res://shaders/arena_deck.gdshader"
@export var field_tint := Color(0.45, 0.65, 1.0)
@export var field_intensity := 0.09
@export var team0_tint := Color(0.15, 0.45, 1.0)
@export var team1_tint := Color(1.0, 0.35, 0.25)
@export var team0_tint := TeamColors.TEAM_COLORS[0]
@export var team1_tint := TeamColors.TEAM_COLORS[1]
# The single merged surface shell and the material whose camera-side fade
# _process() drives. Both stay null in headless runs, which never render.
+1 -4
View File
@@ -10,9 +10,6 @@ extends Area3D
signal goal_scored(team: int)
# Frame tints, indexed by team.
const TEAM_COLORS := [Color(0.2, 0.55, 1.0), Color(1.0, 0.4, 0.28)]
# The pocket is sunk into the end wall, so it can be at most as deep as that
# wall is thick or it pokes out the back of the arena. ArenaBoundary cuts the
# matching aperture in the hull (see its GOAL_APERTURE_* constants).
@@ -52,7 +49,7 @@ func _on_body_entered(body):
func _build_visuals() -> void:
var mouth := ($CollisionShape3D.shape as BoxShape3D).size
var half := Vector2(mouth.x, mouth.y) / 2.0
var tint: Color = TEAM_COLORS[team % TEAM_COLORS.size()]
var tint: Color = TeamColors.TEAM_COLORS[team % TeamColors.TEAM_COLORS.size()]
# One ArrayMesh, four surfaces (pocket, net, bezel, rim). They stay separate
# surfaces rather than sharing materials because they aren't visually
+1 -8
View File
@@ -25,13 +25,6 @@ extends RigidBody3D
@export var ceiling_pull_strength = 11.5 # Ceiling grav-plating strength; nets above gravity so a ship can hold a ceiling
@export var ceiling_pull_range = 3.0 # Metres from the ceiling where pull begins
# Accent colours per team, applied to the nose and tail fin meshes so the
# two sides are tellable apart at a glance.
const TEAM_COLORS := {
0: Color(0.25, 0.55, 1.0),
1: Color(1.0, 0.5, 0.15),
}
# Non-tinted hull meshes, runtime-merged into one ArrayMesh by
# _build_merged_hull() (Nose/TailFin stay separate MeshInstance3Ds since
# _apply_team_color() retints them per-team and must keep addressing them by
@@ -73,7 +66,7 @@ static var _team_materials: Dictionary = {} # team:int -> StandardMaterial3D
static func _get_team_material(team: int) -> StandardMaterial3D:
if _team_materials.has(team):
return _team_materials[team]
var color: Color = TEAM_COLORS.get(team, TEAM_COLORS[0])
var color: Color = TeamColors.TEAM_COLORS.get(team, TeamColors.TEAM_COLORS[0])
var accent := StandardMaterial3D.new()
accent.albedo_color = color
accent.metallic = 0.3
+10
View File
@@ -0,0 +1,10 @@
class_name TeamColors
# Single source of truth for team identity. Ship nose/tailfin, HUD result
# panel, goal rim/net, and the arena end-zone floor shader all read from
# here — do not restate these values locally (see TODO.md history).
const TEAM_NAMES := {0: "Purple", 1: "Green"}
const TEAM_COLORS := {
0: Color(0.48, 0.18, 0.88),
1: Color(0.24, 0.86, 0.42),
}
+1
View File
@@ -0,0 +1 @@
uid://c6r8q1dpd8klq