134 lines
4.8 KiB
GDScript3
134 lines
4.8 KiB
GDScript3
|
|
extends Control
|
||
|
|
|
||
|
|
# #103 (#97e): "A dedicated menu on each outpost should let you compose how
|
||
|
|
# you want the outpost to be filled."
|
||
|
|
#
|
||
|
|
# Builds its own rows from GameState.garrison_vessel_types() rather than
|
||
|
|
# from nodes placed in Main.tscn. Which vessels can hold a position is
|
||
|
|
# decided by capability, not by a list of types, so the day an armed vessel
|
||
|
|
# is added it gets a row here and an unarmed one never does by accident -
|
||
|
|
# and neither change touches the scene.
|
||
|
|
#
|
||
|
|
# The panel edits the ORDER, not the crew. What the player sets here is what
|
||
|
|
# they want stationed; #104 is what actually gets vessels there. The two are
|
||
|
|
# shown side by side ("1/2") so an outpost that is waiting on a hull it does
|
||
|
|
# not have yet reads as waiting rather than as broken.
|
||
|
|
|
||
|
|
# Emitted when the player changes the order, so whatever hosts the panel can
|
||
|
|
# update its own readout rather than polling.
|
||
|
|
signal changed
|
||
|
|
|
||
|
|
const ROW_HEIGHT: float = 96.0
|
||
|
|
const ROW_GAP: float = 12.0
|
||
|
|
const BUTTON_SIZE: float = 76.0
|
||
|
|
const NAME_FONT_SIZE: int = 30
|
||
|
|
const COUNT_FONT_SIZE: int = 34
|
||
|
|
const HEADER_FONT_SIZE: int = 22
|
||
|
|
|
||
|
|
var building_id: int = 0
|
||
|
|
|
||
|
|
var _rows: Array = []
|
||
|
|
var _capacity_label: Label
|
||
|
|
|
||
|
|
|
||
|
|
func _ready() -> void:
|
||
|
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
|
|
# _make_label already parents what it builds.
|
||
|
|
_capacity_label = _make_label("", HEADER_FONT_SIZE, Color(0.62, 0.78, 0.86))
|
||
|
|
_capacity_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
|
|
|
||
|
|
for vessel_type in GameState.garrison_vessel_types():
|
||
|
|
var row: Dictionary = _make_row(vessel_type)
|
||
|
|
_rows.append(row)
|
||
|
|
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 count_label: Label = _make_label("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, "count": count_label,
|
||
|
|
"minus": minus, "plus": plus}
|
||
|
|
|
||
|
|
|
||
|
|
func _on_adjust(vessel_type: String, delta: int) -> void:
|
||
|
|
if building_id == 0:
|
||
|
|
return
|
||
|
|
GameState.adjust_garrison_desired(building_id, vessel_type, delta)
|
||
|
|
refresh()
|
||
|
|
changed.emit()
|
||
|
|
|
||
|
|
|
||
|
|
func _layout() -> void:
|
||
|
|
if _rows.is_empty():
|
||
|
|
return
|
||
|
|
_capacity_label.position = Vector2.ZERO
|
||
|
|
_capacity_label.size = Vector2(size.x, 44.0)
|
||
|
|
var y: float = 52.0
|
||
|
|
for row in _rows:
|
||
|
|
row.name.position = Vector2(0.0, y)
|
||
|
|
row.name.size = Vector2(size.x * 0.46, ROW_HEIGHT)
|
||
|
|
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.0, y)
|
||
|
|
row.count.size = Vector2(BUTTON_SIZE * 2.0, ROW_HEIGHT)
|
||
|
|
row.minus.position = Vector2(right - BUTTON_SIZE * 4.0,
|
||
|
|
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 52.0 + float(_rows.size()) * (ROW_HEIGHT + ROW_GAP)
|
||
|
|
|
||
|
|
|
||
|
|
func refresh() -> void:
|
||
|
|
if _rows.is_empty():
|
||
|
|
return
|
||
|
|
var capacity: int = GameState.outpost_slot_capacity()
|
||
|
|
var ordered: int = GameState.garrison_desired_total(building_id)
|
||
|
|
_capacity_label.text = "BERTHS %d / %d" % [ordered, capacity]
|
||
|
|
for row in _rows:
|
||
|
|
var vessel_type: String = String(row.type)
|
||
|
|
var want: int = GameState.garrison_desired_of(building_id, vessel_type)
|
||
|
|
var here: int = GameState.garrison_crew_of(building_id, vessel_type)
|
||
|
|
# "here / ordered", so an outpost waiting on a hull nobody has built
|
||
|
|
# yet reads as waiting rather than as having lost one.
|
||
|
|
row.count.text = "%d/%d" % [here, want]
|
||
|
|
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 and ordered < capacity
|
||
|
|
row.minus.queue_redraw()
|
||
|
|
row.plus.queue_redraw()
|