Refactor multi-resource UI and transfers
This commit is contained in:
parent
af5e08e5a1
commit
8231e165ff
20 changed files with 648 additions and 435 deletions
205
ResourceBar.gd
205
ResourceBar.gd
|
|
@ -1,146 +1,113 @@
|
|||
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.
|
||||
# Compact resource table shared by crafting, research and the ship lab.
|
||||
# Rows are resources; columns are locations. Adding another material to
|
||||
# GameState.RESOURCE_TYPES automatically gives it a row here.
|
||||
|
||||
# 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 HEADER_HEIGHT: float = 22.0
|
||||
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 _numbers: Array[NeonNumber] = []
|
||||
var _captions: Array[Label] = []
|
||||
var _gas_caption: Label
|
||||
var _gas_icon: IconButton
|
||||
var _summary: Label
|
||||
var _header_labels: Array[Label] = []
|
||||
var _rows: Dictionary = {}
|
||||
|
||||
|
||||
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)
|
||||
for heading in ["", "BANKED", "FLEET", "LOCAL"]:
|
||||
var label := _make_label(heading, 16, Color(0.62, 0.78, 0.86))
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
add_child(label)
|
||||
_header_labels.append(label)
|
||||
|
||||
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)
|
||||
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(), 18,
|
||||
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,
|
||||
}
|
||||
|
||||
_layout()
|
||||
resized.connect(_layout)
|
||||
_gas_caption = Label.new()
|
||||
_gas_caption.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_gas_caption.add_theme_font_size_override("font_size", 20)
|
||||
_gas_caption.add_theme_color_override("font_color", Color(0.85, 0.52, 1.0))
|
||||
add_child(_gas_caption)
|
||||
_gas_icon = IconButton.new()
|
||||
_gas_icon.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_gas_icon.icon = IconButton.Icon.GAS
|
||||
_gas_icon.glow_color = Color(0.85, 0.52, 1.0)
|
||||
add_child(_gas_icon)
|
||||
_summary = Label.new()
|
||||
_summary.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
_summary.add_theme_font_size_override("font_size", 18)
|
||||
_summary.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
_summary.size = size
|
||||
add_child(_summary)
|
||||
_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", 19, colour)
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
return label
|
||||
|
||||
|
||||
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)
|
||||
if _gas_caption != null:
|
||||
_gas_caption.position = Vector2(size.x - 110.0, 2.0)
|
||||
_gas_caption.size = Vector2(106.0, CAPTION_HEIGHT)
|
||||
_gas_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
if _gas_icon != null:
|
||||
_gas_icon.position = Vector2(size.x - 34.0, 0.0)
|
||||
_gas_icon.size = Vector2(32.0, 32.0)
|
||||
_gas_caption.size.x = maxf(0.0, _gas_caption.size.x - 36.0)
|
||||
if _header_labels.is_empty():
|
||||
return
|
||||
var resource_width: float = size.x * 0.25
|
||||
var value_width: float = (size.x - resource_width) / 3.0
|
||||
var columns: Array = [0.0, resource_width, resource_width + value_width,
|
||||
resource_width + value_width * 2.0]
|
||||
var widths: Array = [resource_width, value_width, value_width, value_width]
|
||||
for i in range(_header_labels.size()):
|
||||
_header_labels[i].position = Vector2(columns[i], 0.0)
|
||||
_header_labels[i].size = Vector2(widths[i], HEADER_HEIGHT)
|
||||
|
||||
var row_height: float = (size.y - HEADER_HEIGHT - 4.0) / maxf(1.0, float(_rows.size()))
|
||||
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
|
||||
row.glyph.position = Vector2(2.0, y + 2.0)
|
||||
row.glyph.size = Vector2(row_height - 4.0, row_height - 4.0)
|
||||
row.name.position = Vector2(row_height, y)
|
||||
row.name.size = Vector2(resource_width - row_height, row_height)
|
||||
for i in range(3):
|
||||
var label: Label = row[["bank", "fleet", "local"][i]]
|
||||
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:
|
||||
# Resource rows scale without inventing a new menu per resource. The old
|
||||
# ore-only columns remain behind this concise, explicit summary.
|
||||
_summary.text = "ORE Bank %d Fleet %d Local %d\nGAS Bank %d Fleet %d Local %d" % [GameState.rocks, GameState.fleet_cargo, GameState.building_holdings(building_id), GameState.gas, GameState.fleet_gas_cargo, GameState.building_gas_holdings(building_id)]
|
||||
for number in _numbers: number.visible = false
|
||||
for caption in _captions: caption.visible = false
|
||||
_gas_caption.visible = false
|
||||
_gas_icon.visible = false
|
||||
_gas_caption.text = "GAS %d / %d / %d" % [GameState.gas, GameState.fleet_gas_cargo, GameState.building_gas_holdings(building_id)]
|
||||
_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))
|
||||
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 "—"
|
||||
|
||||
|
||||
# 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
|
||||
return 84.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
|
||||
)
|
||||
NeonDraw.draw_glowing_line(self, Vector2(0.0, size.y), Vector2(size.x, size.y),
|
||||
RULE_COLOR, 0.55, 1.0)
|
||||
|
|
|
|||
Loading…
Reference in a new issue