extends "res://ui/menus/ModalMenu.gd" signal craft_requested(vessel_type: String) signal queue_cancel_requested(index: int) const VESSEL_DEFINITIONS = preload("res://vessels/VesselDefinitions.gd") const MENU_LAYOUT = preload("res://ui/menus/MenuLayout.gd") const PRICE_TAG = preload("res://ui/PriceTag.gd") const COLUMNS := 3 const ROW_PITCH := 260.0 const COLUMN_X := 190.0 const BUTTON_HALF := 56.0 const BUTTON_HEIGHT := 100.0 const LABEL_HALF := 92.0 const LABEL_HEIGHT := 44.0 const LABEL_OFFSET := 106.0 const PRICE_HALF := 92.0 const PRICE_HEIGHT := 86.0 const PRICE_OFFSET := 154.0 @onready var status: Label = $StatusLabel @onready var queue_detail: Label = $QueueDetail @onready var resource_bar: Control = $Bar # #216 added the two support craft at the end, in the order # VESSEL_DEFINITIONS.craftable() returns them - fleet hulls first, so none of # the rows above moved. @onready var vessel_buttons: Array = [$InterceptorButton, $ShieldButton, $GasBomberButton, $CarrierButton, $LaserButton, $DistorderButton, $TransporterButton, $RepairDroneButton] @onready var vessel_labels: Array = [$InterceptorLabel, $ShieldLabel, $GasBomberLabel, $CarrierLabel, $LaserLabel, $DistorderLabel, $TransporterLabel, $RepairDroneLabel] @onready var queue_slots: Array = [$QueueSlot0, $QueueSlot1, $QueueSlot2, $QueueSlot3] var building_id: int = 0 # Which vessel each visible slot is offering, in order. Empty slots are hidden. var shown_types: PackedStringArray = PackedStringArray() var message: String = "" var message_until: int = 0 var price_tags: Array = [] func _ready() -> void: super._ready() _build_price_tags() # Bound to the SLOT, not to a definition: which vessel sits in slot i # changes as things become available, so a handler that captured one at # startup would order the wrong hull the moment the list shortened. for i in range(vessel_buttons.size()): vessel_buttons[i].pressed.connect(func(): _on_vessel_pressed(i)) for i in range(queue_slots.size()): queue_slots[i].pressed.connect(func(): queue_cancel_requested.emit(i)) _layout_entries() func open_for(next_building_id: int) -> void: building_id = next_building_id resource_bar.building_id = building_id open_menu() refresh() func close_menu() -> void: super.close_menu() building_id = 0 func say(next_message: String) -> void: message = next_message message_until = Time.get_ticks_msec() + 3000 refresh() func refresh() -> void: status.text = "" # Only what can actually be ordered here today: a hull whose research is # done, and - for the support craft - one with somewhere to be assigned. # Hidden rather than greyed, the way the build menu treats a structure # that has not been researched; a row nobody can press is clutter that # says nothing the absence does not. shown_types.clear() for definition in VESSEL_DEFINITIONS.craftable(): if GameState.vessel_can_be_crafted(String(definition.definition_id)): shown_types.append(String(definition.definition_id)) for i in range(vessel_buttons.size()): var on_offer: bool = i < shown_types.size() vessel_buttons[i].visible = on_offer vessel_labels[i].visible = on_offer if i < price_tags.size(): price_tags[i].visible = on_offer if not on_offer: continue var definition: Resource = VESSEL_DEFINITIONS.get_definition(String(shown_types[i])) var button: Control = vessel_buttons[i] vessel_labels[i].text = String(definition.display_name) if i < price_tags.size(): price_tags[i].set_costs(GameState.product_costs(definition.definition_id)) button.icon = definition.icon button.glow_color = definition.glow_color button.enabled = true button.queue_redraw() _layout_entries() refresh_queue() if message != "": if Time.get_ticks_msec() < message_until: status.text = "%s\n%s" % [message, status.text] else: message = "" func refresh_queue() -> void: var orders: Array = GameState.order_list(building_id) for i in range(queue_slots.size()): var slot: Control = queue_slots[i] slot.visible = i < orders.size() if not slot.visible: continue var order: Dictionary = orders[i] var definition: Resource = VESSEL_DEFINITIONS.get_definition(String(order.product)) slot.icon = definition.icon slot.glow_color = vessel_colour(String(order.product)) slot.enabled = bool(order.started) slot.queue_redraw() if orders.is_empty(): queue_detail.text = "Nothing queued" return var head: Dictionary = orders[0] if bool(head.started): queue_detail.text = "Building %s %d%%\nTap to cancel" % [ GameState.vessel_name(String(head.product)), int(round(float(head.progress) * 100.0))] else: queue_detail.text = "%s waiting for resources\nTap to cancel" \ % GameState.vessel_name(String(head.product)) func vessel_colour(vessel_type: String) -> Color: var definition: Resource = VESSEL_DEFINITIONS.get_definition(vessel_type) return definition.glow_color if definition != null else Color(0.25, 0.85, 1.0) func _build_price_tags() -> void: for i in range(vessel_buttons.size()): var tag: Control = PRICE_TAG.new() tag.name = "CraftPrice%d" % i tag.set_anchors_preset(Control.PRESET_CENTER) add_child(tag) price_tags.append(tag) func row_height() -> float: return PRICE_OFFSET + PRICE_HEIGHT func _on_vessel_pressed(slot: int) -> void: if slot < 0 or slot >= shown_types.size(): return craft_requested.emit(String(shown_types[slot])) func _layout_entries() -> void: # Laid out over what is ON OFFER, so a short list is centred and packed # rather than leaving holes where a hidden hull used to be. var offered: int = maxi(shown_types.size(), 1) var rows: int = maxi(int(ceil(float(offered) / float(COLUMNS))), 1) var band: Vector2 = MENU_LAYOUT.grid_band(status.offset_bottom, size.y) for i in range(vessel_buttons.size()): var row: int = i / COLUMNS var column: int = i % COLUMNS var cx: float = (float(column) - 1.0) * COLUMN_X var top: float = MENU_LAYOUT.grid_row_top(row, rows, row_height(), band, ROW_PITCH) MENU_LAYOUT.place_entry(vessel_buttons[i], cx, top, BUTTON_HALF, BUTTON_HEIGHT) MENU_LAYOUT.place_entry(vessel_labels[i], cx, top + LABEL_OFFSET, LABEL_HALF, LABEL_HEIGHT) if i < price_tags.size(): MENU_LAYOUT.place_entry(price_tags[i], cx, top + PRICE_OFFSET, PRICE_HALF, PRICE_HEIGHT)