class_name ResourceReadout extends Control # Generalizes ResourceCounter/GasCounter/CargoCounter/GasCargoCounter - one # hardcoded node plus one script per resource, times two scenes - into one # row per GameState.resource_types() entry, built at _ready(). A new # resource added to ResourceDefinitions.ORDER/DEFINITIONS gets a HUD row # with no scene edit and no new script, the same way ResourceBar and # ResourceTransferList already pick up a new resource automatically. # # Layout mirrors what it replaces: a BANKED column (left) and a FLEET HOLD # column (right), one row per resource, each row a resource glyph plus a # digit readout. Banked rows are always visible, even at zero (matching # ResourceCounter/GasCounter today); fleet rows hide at zero, since that # hold isn't the player's yet (see CargoCounter's own note on why). const FLEET_GLOW: Color = Color(1.0, 0.78, 0.25) const FLEET_CORE: Color = Color(0.22, 0.12, 0.0) const ROW_HEIGHT: float = 58.0 const COLUMN_GAP: float = 328.0 const GLYPH_GAP: float = 8.0 @export var digit_height: float = 38.0 # The bright rocky/gas surfaces need a darker banked glow than deep space # for the same contrast reason PlanetSurface.tscn's old ResourceCounter # node overrode its colour - set true on the surface-scene instance only. @export var bright_background: bool = false var _banked_numbers: Dictionary = {} var _fleet_numbers: Dictionary = {} # #56: the fleet hold now has a finite total capacity. Rather than a new # node, this reuses the existing "FLEET HOLD" caption Label that both Main # and PlanetSurface already place as a sibling - get_node_or_null so a # scene that happens to embed ResourceReadout without that sibling (none # does today) degrades to just not showing a limit, instead of erroring. @onready var _fleet_caption: Label = get_node_or_null("../FleetCaption") var _fleet_caption_base_text: String = "" func _ready() -> void: mouse_filter = Control.MOUSE_FILTER_IGNORE if _fleet_caption != null: _fleet_caption_base_text = _fleet_caption.text var y: float = 0.0 for resource_type in GameState.resource_types(): var banked_color: Color = GameState.resource_color(resource_type) if bright_background: banked_color = banked_color.darkened(0.4) _add_glyph(resource_type, Vector2(0.0, y)) var banked: NeonNumber = _add_number(Vector2(digit_height + GLYPH_GAP, y), banked_color, banked_color.darkened(0.8)) _banked_numbers[resource_type] = banked _add_glyph(resource_type, Vector2(COLUMN_GAP, y)) var fleet: NeonNumber = _add_number(Vector2(COLUMN_GAP + digit_height + GLYPH_GAP, y), FLEET_GLOW, FLEET_CORE) _fleet_numbers[resource_type] = fleet y += ROW_HEIGHT # Stop understating our own extent. The rows are drawn by child nodes, # which happily paint outside this Control's rect, so the scene-authored # height stayed at its original guess no matter how many rows appeared. size.y = rows_height() # How tall the rows are, derived from the live resource list rather than from # whatever _ready() happened to have built. That matters: _ready() runs in # scene-tree order, so a sibling laying itself out relative to this readout # (BuildController._seat_hud_columns) can legitimately ask BEFORE this node # has built anything, and a cached height would answer zero. func rows_height() -> float: return float(GameState.resource_types().size()) * ROW_HEIGHT # Bottom edge of the rows in this Control's parent space - the line anything # stacked below the readout has to clear. func content_bottom() -> float: return position.y + rows_height() func _add_glyph(resource_type: String, at: Vector2) -> void: var glyph: ResourceGlyph = ResourceGlyph.new() glyph.resource_type = resource_type glyph.position = at glyph.size = Vector2(digit_height, digit_height) add_child(glyph) func _add_number(at: Vector2, glow: Color, core: Color) -> NeonNumber: var number: NeonNumber = NeonNumber.new() number.show_icon = false number.position = at number.size = Vector2(0.0, digit_height) number.glow_color = glow number.core_color = core number.digit_height = digit_height add_child(number) return number func banked_number(resource_type: String) -> NeonNumber: return _banked_numbers.get(resource_type) func fleet_number(resource_type: String) -> NeonNumber: return _fleet_numbers.get(resource_type) func _process(_delta: float) -> void: if _fleet_caption != null: var capacity: int = GameState.fleet_cargo_total_capacity() _fleet_caption.text = "%s %d/%d" % [_fleet_caption_base_text, GameState.fleet_cargo_used(), capacity] \ if capacity >= 0 else _fleet_caption_base_text for resource_type in GameState.resource_types(): var banked: NeonNumber = _banked_numbers.get(resource_type) if banked != null: banked.set_amount(GameState.banked_resource(resource_type)) var fleet: NeonNumber = _fleet_numbers.get(resource_type) if fleet == null: continue var held: int = GameState.fleet_resource(resource_type) fleet.visible = held > 0 if fleet.visible: fleet.set_amount(held)