Improve ship lab upgrade selection

This commit is contained in:
Loïc Wisniewski 2026-08-13 16:45:08 +02:00
commit 8219893cda
3 changed files with 208 additions and 29 deletions

View file

@ -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:

136
Main.tscn
View file

@ -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

View file

@ -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")