From 8219893cda55a3f967b58c85c783783f7273e7ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Wisniewski?= Date: Thu, 13 Aug 2026 16:45:08 +0200 Subject: [PATCH] Improve ship lab upgrade selection --- BuildController.gd | 69 ++++++++++++--------- Main.tscn | 136 +++++++++++++++++++++++++++++++++++++++++ tests/test_ship_lab.gd | 32 ++++++++++ 3 files changed, 208 insertions(+), 29 deletions(-) diff --git a/BuildController.gd b/BuildController.gd index 15f8dbc..605994a 100644 --- a/BuildController.gd +++ b/BuildController.gd @@ -35,12 +35,18 @@ extends Node2D get_node("../UI/LabMenu/VesselC"), ] @onready var lab_vessel_label: Label = get_node("../UI/LabMenu/VesselLabel") -@onready var lab_list: Label = get_node("../UI/LabMenu/UpgradeList") @onready var lab_detail: Label = get_node("../UI/LabMenu/DetailLabel") @onready var lab_status: Label = get_node("../UI/LabMenu/StatusLabel") -@onready var lab_next_button: IconButton = get_node("../UI/LabMenu/NextButton") @onready var lab_buy_button: IconButton = get_node("../UI/LabMenu/BuyButton") @onready var lab_close_button: IconButton = get_node("../UI/LabMenu/CloseButton") +@onready var lab_upgrade_buttons: Array = [ + get_node("../UI/LabMenu/Upgrade0"), get_node("../UI/LabMenu/Upgrade1"), + get_node("../UI/LabMenu/Upgrade2"), get_node("../UI/LabMenu/Upgrade3"), +] +@onready var lab_upgrade_labels: Array = [ + get_node("../UI/LabMenu/Upgrade0Label"), get_node("../UI/LabMenu/Upgrade1Label"), + get_node("../UI/LabMenu/Upgrade2Label"), get_node("../UI/LabMenu/Upgrade3Label"), +] @onready var craft_shield_button: IconButton = get_node("../UI/CraftMenu/ShieldButton") @onready var queue_slots: Array = [ get_node("../UI/CraftMenu/QueueSlot0"), get_node("../UI/CraftMenu/QueueSlot1"), @@ -86,10 +92,9 @@ extends Node2D get_node("../UI/BuildMenu/ShipLabLabel"), get_node("../UI/BuildMenu/ShipLabCost")], ] -# Which vessel the lab screen is showing, and which of its upgrades is -# selected. The screen steps through one upgrade at a time rather than -# listing tappable rows: on a phone a row of small hit targets is worse than -# one big Buy button and a Next. +# Which vessel the lab screen is showing, and which visible upgrade card is +# selected. Each vessel gets its own small set of direct cards; the detail and +# Buy controls below them only describe the one the player tapped. var lab_vessel: String = GameState.VESSEL_INTERCEPTOR var lab_choice: int = 0 @onready var planet_field: Node2D = get_node("../PlanetField") @@ -217,10 +222,11 @@ func _ready() -> void: build_lab_button.pressed.connect(_on_place_ship_lab) craft_shield_button.pressed.connect(_on_craft_shield) lab_close_button.pressed.connect(func(): lab_menu.visible = false) - lab_next_button.pressed.connect(_on_lab_next) lab_buy_button.pressed.connect(_on_lab_buy) for i in range(lab_vessel_buttons.size()): lab_vessel_buttons[i].pressed.connect(_on_lab_vessel.bind(i)) + for i in range(lab_upgrade_buttons.size()): + lab_upgrade_buttons[i].pressed.connect(_on_lab_upgrade.bind(i)) info_close_button.pressed.connect(_close_info) info_build_button.pressed.connect(_confirm_build) @@ -787,6 +793,7 @@ func _lab_options() -> Array: options.append({"unlock": true, "name": "Unlock Shield Vessel", "desc": "Lets the ship crafter build shield vessels. Its shield absorbs one lethal hit, then cycles back up.", "cost": GameState.UNLOCK_SHIELD_COST}) + return options for u in GameState.upgrades_for(lab_vessel): options.append({"unlock": false, "id": u.id, "name": u.name, "desc": u.desc}) return options @@ -799,11 +806,11 @@ func _on_lab_vessel(index: int) -> void: _refresh_lab() -func _on_lab_next() -> void: - var options: Array = _lab_options() - if options.is_empty(): +func _on_lab_upgrade(index: int) -> void: + if index < 0 or index >= _lab_options().size(): return - lab_choice = (lab_choice + 1) % options.size() + lab_choice = index + lab_status.text = "" _refresh_lab() @@ -838,33 +845,37 @@ func _refresh_lab() -> void: for i in range(lab_vessel_buttons.size()): var this_vessel: String = [GameState.VESSEL_INTERCEPTOR, GameState.VESSEL_SHIELD, GameState.VESSEL_TRANSPORTER][i] - lab_vessel_buttons[i].enabled = this_vessel != lab_vessel + lab_vessel_buttons[i].enabled = true + lab_vessel_buttons[i].glow_color = Color(0.45, 0.9, 0.55) if this_vessel == lab_vessel \ + else _vessel_colour(this_vessel) lab_vessel_buttons[i].queue_redraw() - lab_vessel_label.text = GameState.VESSEL_NAMES.get(lab_vessel, lab_vessel) + lab_vessel_label.text = "%s research" % GameState.VESSEL_NAMES.get(lab_vessel, lab_vessel) var options: Array = _lab_options() + for i in range(lab_upgrade_buttons.size()): + var visible: bool = i < options.size() + lab_upgrade_buttons[i].visible = visible + lab_upgrade_labels[i].visible = visible + if not visible: + continue + var option: Dictionary = options[i] + var price_text: String = "%d ore" % (GameState.UNLOCK_SHIELD_COST if bool(option.unlock) else GameState.upgrade_cost(option.id)) + if not bool(option.unlock) and GameState.upgrade_maxed(option.id): + price_text = "MAX" + var level_note: String = "" if bool(option.unlock) else " %d/%d" % [ + GameState.upgrade_level(option.id), int(GameState.upgrade_def(option.id).max_level)] + lab_upgrade_labels[i].text = "%s%s\n%s" % [option.name, level_note, price_text] + lab_upgrade_buttons[i].icon = IconButton.Icon.SHIP_LAB + lab_upgrade_buttons[i].glow_color = Color(1.0, 0.85, 0.4) if i == lab_choice else _vessel_colour(lab_vessel) + lab_upgrade_buttons[i].enabled = true + lab_upgrade_buttons[i].queue_redraw() if options.is_empty(): - lab_list.text = "" lab_detail.text = "" lab_buy_button.enabled = false + lab_buy_button.queue_redraw() return lab_choice = clampi(lab_choice, 0, options.size() - 1) - # The whole page at a glance, with the selected line marked - so the - # player can see what else is there without stepping through it. - var lines: Array = [] - for i in range(options.size()): - var o: Dictionary = options[i] - var marker: String = ">" if i == lab_choice else " " - if o.unlock: - lines.append("%s %s %d ore" % [marker, o.name, GameState.UNLOCK_SHIELD_COST]) - else: - var lvl: int = GameState.upgrade_level(o.id) - var max_lvl: int = int(GameState.upgrade_def(o.id).max_level) - var price: String = "MAX" if GameState.upgrade_maxed(o.id) else "%d ore" % GameState.upgrade_cost(o.id) - lines.append("%s %s %d/%d %s" % [marker, o.name, lvl, max_lvl, price]) - lab_list.text = "\n".join(lines) - var choice: Dictionary = options[lab_choice] lab_detail.text = "%s\n%s" % [choice.name, choice.desc] if choice.unlock: diff --git a/Main.tscn b/Main.tscn index 9f8af89..154467e 100644 --- a/Main.tscn +++ b/Main.tscn @@ -953,6 +953,7 @@ icon = 12 glow_color = Color(0.95, 0.55, 0.85, 1) [node name="VesselLabel" type="Label" parent="UI/LabMenu"] +visible = false mouse_filter = 2 anchor_right = 1.0 offset_left = 24 @@ -963,7 +964,44 @@ theme_override_font_sizes/font_size = 34 horizontal_alignment = 1 text = "" +[node name="VesselALabel" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -330 +offset_right = -130 +offset_top = 400 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 +horizontal_alignment = 1 +text = "Interceptor" + +[node name="VesselBLabel" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -100 +offset_right = 100 +offset_top = 400 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 +horizontal_alignment = 1 +text = "Shield" + +[node name="VesselCLabel" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = 130 +offset_right = 330 +offset_top = 400 +offset_bottom = 460 +theme_override_font_sizes/font_size = 22 +horizontal_alignment = 1 +text = "Transporter" + [node name="UpgradeList" type="Label" parent="UI/LabMenu"] +visible = false mouse_filter = 2 anchor_right = 1.0 anchor_bottom = 1.0 @@ -1004,6 +1042,7 @@ horizontal_alignment = 1 text = "" [node name="NextButton" type="Control" parent="UI/LabMenu"] +visible = false anchor_left = 0.5 anchor_top = 1.0 anchor_right = 0.5 @@ -1030,6 +1069,7 @@ icon = 4 glow_color = Color(0.45, 0.9, 0.55, 1) [node name="HintLabel" type="Label" parent="UI/LabMenu"] +visible = false mouse_filter = 2 anchor_right = 1.0 anchor_top = 1.0 @@ -1043,6 +1083,102 @@ autowrap_mode = 2 horizontal_alignment = 1 text = "Next upgrade Buy" +[node name="Upgrade0" type="Control" parent="UI/LabMenu"] +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -240 +offset_right = -120 +offset_top = 470 +offset_bottom = 580 +script = ExtResource("10") +icon = 13 +glow_color = Color(0.25, 0.85, 1, 1) + +[node name="Upgrade0Label" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -310 +offset_right = -50 +offset_top = 585 +offset_bottom = 655 +theme_override_font_sizes/font_size = 22 +autowrap_mode = 2 +horizontal_alignment = 1 +text = "" + +[node name="Upgrade1" type="Control" parent="UI/LabMenu"] +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = 120 +offset_right = 240 +offset_top = 470 +offset_bottom = 580 +script = ExtResource("10") +icon = 13 +glow_color = Color(0.25, 0.85, 1, 1) + +[node name="Upgrade1Label" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = 50 +offset_right = 310 +offset_top = 585 +offset_bottom = 655 +theme_override_font_sizes/font_size = 22 +autowrap_mode = 2 +horizontal_alignment = 1 +text = "" + +[node name="Upgrade2" type="Control" parent="UI/LabMenu"] +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -240 +offset_right = -120 +offset_top = 660 +offset_bottom = 765 +script = ExtResource("10") +icon = 13 +glow_color = Color(0.25, 0.85, 1, 1) + +[node name="Upgrade2Label" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -310 +offset_right = -50 +offset_top = 770 +offset_bottom = 815 +theme_override_font_sizes/font_size = 22 +autowrap_mode = 2 +horizontal_alignment = 1 +text = "" + +[node name="Upgrade3" type="Control" parent="UI/LabMenu"] +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = 120 +offset_right = 240 +offset_top = 660 +offset_bottom = 765 +script = ExtResource("10") +icon = 13 +glow_color = Color(0.25, 0.85, 1, 1) + +[node name="Upgrade3Label" type="Label" parent="UI/LabMenu"] +mouse_filter = 2 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = 50 +offset_right = 310 +offset_top = 770 +offset_bottom = 815 +theme_override_font_sizes/font_size = 22 +autowrap_mode = 2 +horizontal_alignment = 1 +text = "" + [node name="CloseButton" type="Control" parent="UI/LabMenu"] anchor_left = 1.0 anchor_right = 1.0 diff --git a/tests/test_ship_lab.gd b/tests/test_ship_lab.gd index 84003f7..cf879ea 100644 --- a/tests/test_ship_lab.gd +++ b/tests/test_ship_lab.gd @@ -90,6 +90,38 @@ func _process(delta: float) -> bool: 1: if elapsed < 0.3: return false + # The lab is a direct vessel -> upgrade flow: tapping a vessel + # shows its own upgrade cards, and tapping a card changes the + # purchase detail. A locked vessel must show its unlock alone. + var lab_menu: Control = controller.get_node("../UI/LabMenu") + var lab_card_0: Control = controller.get_node("../UI/LabMenu/Upgrade0") + var lab_card_1: Control = controller.get_node("../UI/LabMenu/Upgrade1") + var lab_card_3: Control = controller.get_node("../UI/LabMenu/Upgrade3") + var lab_detail: Label = controller.get_node("../UI/LabMenu/DetailLabel") + var lab_buy: IconButton = controller.get_node("../UI/LabMenu/BuyButton") + lab_menu.visible = true + controller.call("_on_lab_vessel", 1) + if not lab_card_0.visible or lab_card_1.visible: + print("TEST_SHIP_LAB: FAIL (locked shield page did not show only its unlock)") + quit(1) + return true + controller.call("_on_lab_upgrade", 0) + if not "Unlock Shield Vessel" in lab_detail.text or not lab_buy.enabled: + print("TEST_SHIP_LAB: FAIL (unlock card did not select a purchasable detail)") + quit(1) + return true + controller.call("_on_lab_vessel", 0) + if not lab_card_0.visible or not lab_card_3.visible: + print("TEST_SHIP_LAB: FAIL (interceptor upgrade cards were not shown)") + quit(1) + return true + controller.call("_on_lab_upgrade", 2) + if not "Targeting Array" in lab_detail.text: + print("TEST_SHIP_LAB: FAIL (tapping an upgrade did not update its detail)") + quit(1) + return true + print("lab cards are vessel-specific and selectable") + # --- 3. buying raises the level and charges ------------------ var before_rocks: int = game_state.rocks var first_cost: int = game_state.upgrade_cost("int_damage")