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()