spaceship/ui/ResourceBar.gd
Loïc Wisniewski 5f1b5a9b29 #95: stop printing the bank twice in the research menu
The research table's LOCAL column was not merely equal to BANKED, it was the
same store: research lives on the mothership, the mothership is also the
bank, and building_inventory() redirects any bank-capable building to
bank_inventory itself. ResourceBar now drops LOCAL wherever it resolves to
the bank and lets the remaining two columns share the width. Anywhere else -
a crafter, a lab, the transfer panel - the column is untouched.

The "Research" heading went with it. It named a panel you can only reach by
pressing the research button, and it cost the tree canvas 128px, which the
table and the tree have now taken back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 11:35:14 +02:00

165 lines
6.2 KiB
GDScript

class_name ResourceBar
extends Control
# Compact resource table shared by crafting, research and the ship lab.
# Rows are resources; columns are locations. Adding another material to
# The resource-definition catalog automatically gives each material a row.
var building_id: int = 0
# Header band and font sizes were all set for a 84px-tall box, which made the
# table read as a dense strip of small numbers. The hosting menus now give it
# 136px, so the rows have somewhere to breathe and the text can be legible at
# arm's length on a phone.
const HEADER_HEIGHT: float = 30.0
# Vertical gap between rows. Previously rows were flush against each other and
# the only separation was colour.
const ROW_GAP: float = 4.0
const HEADER_FONT_SIZE: int = 20
const NAME_FONT_SIZE: int = 23
const VALUE_FONT_SIZE: int = 24
const RULE_COLOR: Color = Color(0.25, 0.55, 0.7)
const FLEET_COLOR: Color = Color(1.0, 0.78, 0.25)
const LOCAL_COLOR: Color = Color(0.45, 0.9, 0.55)
var _header_labels: Array[Label] = []
var _rows: Dictionary = {}
# The value columns, in the order they are drawn. LOCAL is dropped from this
# list at a building that IS the bank - see _local_is_banked().
const VALUE_KEYS: Array = ["bank", "fleet", "local"]
var _local_shown: bool = true
func _ready() -> void:
mouse_filter = Control.MOUSE_FILTER_IGNORE
for heading in ["", "BANKED", "FLEET", "LOCAL"]:
var label := _make_label(heading, HEADER_FONT_SIZE, Color(0.62, 0.78, 0.86))
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
add_child(label)
_header_labels.append(label)
for resource_type in GameState.resource_types():
var glyph := ResourceGlyph.new()
glyph.resource_type = resource_type
add_child(glyph)
var name_label := _make_label(GameState.resource_name(resource_type).to_upper(),
NAME_FONT_SIZE, GameState.resource_color(resource_type))
add_child(name_label)
var bank_label := _make_value_label(GameState.resource_color(resource_type))
var fleet_label := _make_value_label(FLEET_COLOR)
var local_label := _make_value_label(LOCAL_COLOR)
for label in [bank_label, fleet_label, local_label]:
add_child(label)
_rows[resource_type] = {
"glyph": glyph, "name": name_label, "bank": bank_label,
"fleet": fleet_label, "local": local_label,
}
resized.connect(_layout)
_layout()
queue_redraw()
func _make_label(text_value: String, font_size: int, colour: Color) -> Label:
var label := Label.new()
label.text = text_value
label.mouse_filter = Control.MOUSE_FILTER_IGNORE
label.add_theme_font_size_override("font_size", font_size)
label.add_theme_color_override("font_color", colour)
label.add_theme_color_override("font_outline_color", Color(0.01, 0.01, 0.035, 0.95))
label.add_theme_constant_override("outline_size", 2)
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
return label
func _make_value_label(colour: Color) -> Label:
var label := _make_label("0", VALUE_FONT_SIZE, colour)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
return label
# #95: at a building that IS the bank - the mothership carries both the "bank"
# and the "research" capability, so the research menu is always opened from one
# - building_inventory() redirects to bank_inventory itself, and LOCAL is not
# merely equal to BANKED but the very same store read twice. Printing the
# number again teaches nothing and costs a third of the table, so the column
# goes away there and the remaining two share the width.
func _local_is_banked() -> bool:
if building_id == 0:
return false
return GameState.building_inventory(building_id) == GameState.bank_inventory
func _visible_value_keys() -> Array:
return VALUE_KEYS if _local_shown else VALUE_KEYS.slice(0, 2)
func _layout() -> void:
if _header_labels.is_empty():
return
var keys: Array = _visible_value_keys()
var resource_width: float = size.x * 0.25
var value_width: float = (size.x - resource_width) / float(keys.size())
var columns: Array = [0.0]
var widths: Array = [resource_width]
for i in range(keys.size()):
columns.append(resource_width + value_width * float(i))
widths.append(value_width)
for i in range(_header_labels.size()):
var shown: bool = i < columns.size()
_header_labels[i].visible = shown
if not shown:
continue
_header_labels[i].position = Vector2(columns[i], 0.0)
_header_labels[i].size = Vector2(widths[i], HEADER_HEIGHT)
var rows: float = maxf(1.0, float(_rows.size()))
# The gap is taken OUT of the available height rather than added on top of
# each row, so the table still ends exactly where its box does however many
# resources the catalog grows to.
var row_height: float = (size.y - HEADER_HEIGHT - ROW_GAP) / rows
var row_index: int = 0
for resource_type in GameState.resource_types():
var row: Dictionary = _rows[resource_type]
var y: float = HEADER_HEIGHT + float(row_index) * row_height
var content_height: float = row_height - ROW_GAP
row.glyph.position = Vector2(2.0, y + ROW_GAP * 0.5)
row.glyph.size = Vector2(content_height, content_height)
row.name.position = Vector2(content_height + 10.0, y)
row.name.size = Vector2(resource_width - content_height - 10.0, row_height)
for i in range(VALUE_KEYS.size()):
var label: Label = row[VALUE_KEYS[i]]
label.visible = i < keys.size()
if not label.visible:
continue
label.position = Vector2(columns[i + 1], y)
label.size = Vector2(widths[i + 1], row_height)
row_index += 1
queue_redraw()
func _process(_delta: float) -> void:
# building_id is assigned by whichever panel opens, so the column count can
# change between frames; re-lay out only when it actually flips.
var local_shown: bool = not _local_is_banked()
if local_shown != _local_shown:
_local_shown = local_shown
_layout()
for resource_type in GameState.resource_types():
var row: Dictionary = _rows.get(resource_type, {})
if row.is_empty():
continue
row.bank.text = str(GameState.banked_resource(resource_type))
row.fleet.text = str(GameState.fleet_resource(resource_type))
row.local.text = str(GameState.building_resource(building_id, resource_type)) \
if building_id != 0 else "—"
static func natural_height() -> float:
return 84.0
func _draw() -> void:
NeonDraw.draw_glowing_line(self, Vector2(0.0, size.y), Vector2(size.x, size.y),
RULE_COLOR, 0.55, 1.0)