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>
111 lines
4.3 KiB
GDScript
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
|
|
)
|