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