spaceship/ResourceBar.gd
Loïc Wisniewski b6427b080d Remove the one-way ore API superseded by the transfer menu
The loose end from #24. withdraw_to_fleet, unload_into_building and
deliver_cargo were the old one-way moves behind "Load ore" and "Store
ore here"; every one of them is a special case of transfer_to_building /
transfer_to_fleet, and none had a caller left outside tests. The
cargo_delivered signal had no listeners at all.

Not just tidying: unload_into_building routed through
deposit_to_building, which does NOT special-case the mothership - it
writes b.stock, while building_holdings() returns `rocks` for a
mothership and ignores stock entirely. So unload_into_building(
mothership_id) would have parked ore in a field nothing reads, with no
error. Nothing called it that way, but it is a plausibly-named function
sitting there to be reached for later. transfer_to_building handles it.

CARGO_LOAD_BATCH stays - the transfer menu's batch buttons use it; only
its role as withdraw_to_fleet's default argument is gone.

The #24 note expected folding these in to be costly for
test_cargo_readout, which places no buildings. It is not: the transfer
primitives work off GameState records rather than spawned nodes, so a
single add_building() in setup is enough and it stays a display test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 13:07:52 +02:00

111 lines
4.3 KiB
GDScript

class_name ResourceBar
extends Control
# The header every building menu now carries (#24): banked ore, what the
# fleet is holding, and what THIS building has. Before this, each panel
# improvised its own - the crafter spelled the same three figures into a
# run-on StatusLabel string, the research screen printed a bank total inside
# a button caption - so the same numbers looked different in every room.
#
# Three columns rather than a sentence, because the point of the bar is that
# a figure stays in the same place as the player moves between menus.
#
# Reads GameState directly every frame, like ResourceCounter and CargoCounter
# do, so it stays correct across the space <-> planet-surface scene swap with
# nobody having to push updates into it. Only `building_id` is pushed in,
# because "here" is the one thing the bar cannot work out for itself.
# Which building the HERE column is reporting. 0 means none, which greys the
# column rather than showing a confident zero.
var building_id: int = 0
# The caption box has to be a full line tall, not just the font size: a Label
# renders its line height, not its point size, and the first version sat the
# digits 26px down from the top straight through the middle of the caption.
const CAPTION_HEIGHT: float = 34.0
const CAPTION_GAP: float = 4.0
const CAPTION_FONT_SIZE: int = 24
const DIGIT_HEIGHT: float = 34.0
# Bank keeps NeonNumber's own ore-teal, and the hold keeps the amber
# CargoCounter established for ore in transit. Only HERE needed a new colour;
# it borrows the mothership's green, which already reads as "landed
# somewhere and staying there" everywhere else in the game.
const BANK_GLOW: Color = Color(0.0, 0.62, 0.7)
const BANK_CORE: Color = Color(0.0, 0.12, 0.15)
const HOLD_GLOW: Color = Color(1.0, 0.78, 0.25)
const HOLD_CORE: Color = Color(0.22, 0.12, 0.0)
const HERE_GLOW: Color = Color(0.45, 0.9, 0.55)
const HERE_CORE: Color = Color(0.05, 0.16, 0.08)
const RULE_COLOR: Color = Color(0.25, 0.55, 0.7)
var _numbers: Array[NeonNumber] = []
var _captions: Array[Label] = []
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
var columns: Array = [
["BANK", BANK_GLOW, BANK_CORE],
["HOLD", HOLD_GLOW, HOLD_CORE],
["HERE", HERE_GLOW, HERE_CORE],
]
for column in columns:
var caption: Label = Label.new()
caption.text = column[0]
caption.mouse_filter = Control.MOUSE_FILTER_IGNORE
caption.add_theme_font_size_override("font_size", CAPTION_FONT_SIZE)
caption.add_theme_color_override("font_color", column[1])
add_child(caption)
_captions.append(caption)
var number: NeonNumber = NeonNumber.new()
number.mouse_filter = Control.MOUSE_FILTER_IGNORE
number.glow_color = column[1]
number.core_color = column[2]
number.digit_height = DIGIT_HEIGHT
add_child(number)
_numbers.append(number)
_layout()
resized.connect(_layout)
func _layout() -> void:
var column_width: float = size.x / float(_numbers.size())
for i in _numbers.size():
var x: float = column_width * float(i)
_captions[i].position = Vector2(x, 0.0)
_captions[i].size = Vector2(column_width, CAPTION_HEIGHT)
# The hexagon and digits are drawn from the node's own origin, so the
# number is positioned rather than sized.
_numbers[i].position = Vector2(x, CAPTION_HEIGHT + CAPTION_GAP)
func _process(_delta: float) -> void:
_numbers[0].set_amount(GameState.rocks)
_numbers[1].set_amount(GameState.fleet_cargo)
# A building with no id is not a building: showing "0" there would claim
# the store is empty rather than that there is no store to look at.
var known: bool = building_id != 0
_numbers[2].visible = known
_captions[2].visible = known
if known:
_numbers[2].set_amount(GameState.building_holdings(building_id))
# The height the bar needs for a caption line and a row of digits. The scene
# sizes the node, so this is what those offsets have to add up to; anything
# less and the rule is drawn back through the digits.
static func natural_height() -> float:
return CAPTION_HEIGHT + CAPTION_GAP + DIGIT_HEIGHT + 8.0
func _draw() -> void:
# One rule under the whole bar, so the header reads as a band separating
# the standing figures from whatever the panel below them does.
var rule_y: float = maxf(size.y, natural_height())
NeonDraw.draw_glowing_line(
self, Vector2(0.0, rule_y), Vector2(size.x, rule_y), RULE_COLOR, 0.55, 1.0
)