150 lines
5.4 KiB
GDScript3
150 lines
5.4 KiB
GDScript3
|
|
extends Control
|
||
|
|
|
||
|
|
# #106 (#98a): where the player says how many of each vessel should always
|
||
|
|
# exist.
|
||
|
|
#
|
||
|
|
# Like GarrisonPanel it builds its own rows from the catalog, so a new
|
||
|
|
# fleet-eligible vessel gets a row the day it is added without touching the
|
||
|
|
# scene. Unlike GarrisonPanel the targets are GLOBAL - there is one plan for
|
||
|
|
# the whole game, and a second planning centre is another place to read the
|
||
|
|
# same plan rather than a competing one.
|
||
|
|
#
|
||
|
|
# Each row reads "have / want" with what is being built alongside it, so a
|
||
|
|
# plan that is merely waiting on a production queue is distinguishable from
|
||
|
|
# one nothing is being done about. That distinction is the whole readout:
|
||
|
|
# without it a player looking at "2 / 5" cannot tell whether the system is
|
||
|
|
# working.
|
||
|
|
|
||
|
|
signal changed
|
||
|
|
|
||
|
|
const ROW_HEIGHT: float = 84.0
|
||
|
|
const ROW_GAP: float = 8.0
|
||
|
|
const BUTTON_SIZE: float = 68.0
|
||
|
|
const NAME_FONT_SIZE: int = 28
|
||
|
|
const COUNT_FONT_SIZE: int = 30
|
||
|
|
const NOTE_FONT_SIZE: int = 20
|
||
|
|
const HEADER_FONT_SIZE: int = 22
|
||
|
|
|
||
|
|
var _rows: Array = []
|
||
|
|
var _header: Label
|
||
|
|
|
||
|
|
|
||
|
|
func _ready() -> void:
|
||
|
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
|
|
_header = _make_label("", HEADER_FONT_SIZE, Color(0.62, 0.78, 0.86))
|
||
|
|
_header.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
|
|
|
||
|
|
for vessel_type in GameState.planned_vessel_types():
|
||
|
|
_rows.append(_make_row(String(vessel_type)))
|
||
|
|
resized.connect(_layout)
|
||
|
|
_layout()
|
||
|
|
|
||
|
|
|
||
|
|
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
|
||
|
|
add_child(label)
|
||
|
|
return label
|
||
|
|
|
||
|
|
|
||
|
|
func _make_row(vessel_type: String) -> Dictionary:
|
||
|
|
var definition: Resource = GameState.vessel_definition(vessel_type)
|
||
|
|
var colour: Color = definition.glow_color if definition != null else Color.WHITE
|
||
|
|
|
||
|
|
var name_label: Label = _make_label(GameState.vessel_name(vessel_type),
|
||
|
|
NAME_FONT_SIZE, colour)
|
||
|
|
var note_label: Label = _make_label("", NOTE_FONT_SIZE, Color(0.62, 0.78, 0.86))
|
||
|
|
var count_label: Label = _make_label("0/0", COUNT_FONT_SIZE, Color(0.94, 0.98, 1.0))
|
||
|
|
count_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
|
|
|
||
|
|
var minus := IconButton.new()
|
||
|
|
minus.icon = IconButton.Icon.MINUS
|
||
|
|
minus.glow_color = colour
|
||
|
|
minus.pressed.connect(_on_adjust.bind(vessel_type, -1))
|
||
|
|
add_child(minus)
|
||
|
|
|
||
|
|
var plus := IconButton.new()
|
||
|
|
plus.icon = IconButton.Icon.PLUS
|
||
|
|
plus.glow_color = colour
|
||
|
|
plus.pressed.connect(_on_adjust.bind(vessel_type, 1))
|
||
|
|
add_child(plus)
|
||
|
|
|
||
|
|
return {"type": vessel_type, "name": name_label, "note": note_label,
|
||
|
|
"count": count_label, "minus": minus, "plus": plus}
|
||
|
|
|
||
|
|
|
||
|
|
func _on_adjust(vessel_type: String, delta: int) -> void:
|
||
|
|
GameState.adjust_vessel_target(vessel_type, delta)
|
||
|
|
refresh()
|
||
|
|
changed.emit()
|
||
|
|
|
||
|
|
|
||
|
|
func _layout() -> void:
|
||
|
|
if _rows.is_empty():
|
||
|
|
return
|
||
|
|
_header.position = Vector2.ZERO
|
||
|
|
_header.size = Vector2(size.x, 40.0)
|
||
|
|
var y: float = 46.0
|
||
|
|
for row in _rows:
|
||
|
|
row.name.position = Vector2(0.0, y)
|
||
|
|
row.name.size = Vector2(size.x * 0.44, ROW_HEIGHT * 0.58)
|
||
|
|
row.note.position = Vector2(0.0, y + ROW_HEIGHT * 0.54)
|
||
|
|
row.note.size = Vector2(size.x * 0.52, ROW_HEIGHT * 0.42)
|
||
|
|
var right: float = size.x
|
||
|
|
row.plus.position = Vector2(right - BUTTON_SIZE, y + (ROW_HEIGHT - BUTTON_SIZE) * 0.5)
|
||
|
|
row.plus.size = Vector2(BUTTON_SIZE, BUTTON_SIZE)
|
||
|
|
row.count.position = Vector2(right - BUTTON_SIZE * 3.2, y)
|
||
|
|
row.count.size = Vector2(BUTTON_SIZE * 2.2, ROW_HEIGHT)
|
||
|
|
row.minus.position = Vector2(right - BUTTON_SIZE * 4.2,
|
||
|
|
y + (ROW_HEIGHT - BUTTON_SIZE) * 0.5)
|
||
|
|
row.minus.size = Vector2(BUTTON_SIZE, BUTTON_SIZE)
|
||
|
|
y += ROW_HEIGHT + ROW_GAP
|
||
|
|
|
||
|
|
|
||
|
|
func natural_height() -> float:
|
||
|
|
return 46.0 + float(_rows.size()) * (ROW_HEIGHT + ROW_GAP)
|
||
|
|
|
||
|
|
|
||
|
|
func refresh() -> void:
|
||
|
|
if _rows.is_empty():
|
||
|
|
return
|
||
|
|
var planned: int = 0
|
||
|
|
for vessel_type in GameState.vessel_targets:
|
||
|
|
planned += int(GameState.vessel_targets[vessel_type])
|
||
|
|
_header.text = "STANDING ORDERS %d VESSELS" % planned
|
||
|
|
for row in _rows:
|
||
|
|
var vessel_type: String = String(row.type)
|
||
|
|
var want: int = GameState.vessel_target(vessel_type)
|
||
|
|
var have: int = GameState.vessel_census(vessel_type)
|
||
|
|
var building: int = GameState.vessel_count_on_order(vessel_type)
|
||
|
|
row.count.text = "%d/%d" % [have, want]
|
||
|
|
# Where they are, not merely how many: a plan can look unmet while
|
||
|
|
# every vessel it asked for is sitting at an outpost.
|
||
|
|
var parts: PackedStringArray = PackedStringArray()
|
||
|
|
var flying: int = GameState.vessel_count_in_fleet(vessel_type)
|
||
|
|
var stored: int = GameState.vessel_count_in_hangar(vessel_type)
|
||
|
|
var stationed: int = GameState.vessel_count_in_garrisons(vessel_type)
|
||
|
|
var moving: int = GameState.vessel_count_in_transit(vessel_type)
|
||
|
|
if flying > 0:
|
||
|
|
parts.append("%d flying" % flying)
|
||
|
|
if stored > 0:
|
||
|
|
parts.append("%d stored" % stored)
|
||
|
|
if stationed > 0:
|
||
|
|
parts.append("%d stationed" % stationed)
|
||
|
|
if moving > 0:
|
||
|
|
parts.append("%d moving" % moving)
|
||
|
|
if building > 0:
|
||
|
|
parts.append("%d building" % building)
|
||
|
|
row.note.text = ", ".join(parts)
|
||
|
|
var unlocked: bool = GameState.vessel_is_unlocked(vessel_type)
|
||
|
|
row.name.modulate.a = 1.0 if unlocked else 0.45
|
||
|
|
row.minus.enabled = unlocked and want > 0
|
||
|
|
row.plus.enabled = unlocked
|
||
|
|
row.minus.queue_redraw()
|
||
|
|
row.plus.queue_redraw()
|