A ship crafter shows what can actually be ordered there: a hull whose research is done, and - for the support craft - one with somewhere to be assigned. A transporter with no hub and a drone with no workshop have nowhere to go and would sit in the hangar as spent resources, so the structure that uses one is part of its availability. Hidden rather than greyed, as the build menu already treats an unresearched structure, and gated in queue_order() too so the model refuses what the menu declines to show. The craft buttons bound their handlers to a definition captured at startup. With a list that changes length that is a live bug - a slot would order whatever hull was there when the menu was built - so they bind to the slot and resolve through the current offer. The hub and workshop crew menus now NAME what is left in each vessel: "TRANSPORTER 2 9 / 20 RUNS", worst first, with one wide bar for the vessel closest to spent. The per-vessel bars they replaced drew about seven pixels tall on a phone and carried no figure, so the number the player is deciding on could not be read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
95 lines
3.4 KiB
GDScript
95 lines
3.4 KiB
GDScript
extends SceneTree
|
|
|
|
# The hub and workshop menus name what is left in each vessel they hold.
|
|
#
|
|
# #216 gave them crews that wear out, and the whole point of that is a
|
|
# decision - "build another before this one is spent" - which the player can
|
|
# only make if they can read the figure. A bar alone said "getting low"; the
|
|
# number says how low.
|
|
#
|
|
# Covers:
|
|
# A. a hub lists every transporter it holds, worst first, with runs left
|
|
# B. a workshop says REPAIRS rather than RUNS, in its own capacity
|
|
# C. an empty structure says nothing rather than "0 / 0"
|
|
|
|
var state: Node
|
|
var scene: Node
|
|
var menu: Control
|
|
var hub_id: int
|
|
var workshop_id: int
|
|
var stage: int = 0
|
|
var elapsed: float = 0.0
|
|
|
|
|
|
func _initialize() -> void:
|
|
state = root.get_node("GameState")
|
|
state.buildings.clear()
|
|
state.extractors.clear()
|
|
state.transporters.clear()
|
|
state.repair_drones.clear()
|
|
state.reset_progression()
|
|
state.reset_new_game()
|
|
state.intro_active = false
|
|
state.reset_quests()
|
|
state.unlocked = ["shipyards", "logistics", "freight"]
|
|
hub_id = state.add_building(state.BUILDING_TRANSPORT_HUB, Vector2(900.0, 0.0))
|
|
workshop_id = state.add_building(GameState.BUILDING_WORKSHOP, Vector2(-900.0, 0.0))
|
|
scene = load("res://world/Main.tscn").instantiate()
|
|
root.add_child(scene)
|
|
current_scene = scene
|
|
menu = scene.get_node("UI/StationMenu")
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
elapsed += delta
|
|
if elapsed < 0.3:
|
|
return false
|
|
match stage:
|
|
0:
|
|
# --- C. nothing held, nothing claimed --------------------
|
|
menu.open_for(hub_id)
|
|
if menu.condition_list.text.strip_edges() != "":
|
|
return _fail("an empty hub lists a crew: '%s'" % menu.condition_list.text)
|
|
print("an empty hub lists nothing")
|
|
|
|
# --- A. worst first, with the figure ---------------------
|
|
var staff: Script = load("res://tests/_staff.gd")
|
|
staff.staff(state, hub_id, 2)
|
|
state.transporters[0].trips_left = 4
|
|
state.transporters[1].trips_left = GameState.TRANSPORTER_TRIPS
|
|
menu.refresh()
|
|
var listed: String = menu.condition_list.text
|
|
if not listed.contains("4 / %d RUNS" % GameState.TRANSPORTER_TRIPS):
|
|
return _fail("a hub holding a worn transporter does not say so: '%s'" % listed)
|
|
if not listed.contains("%d / %d RUNS" % [GameState.TRANSPORTER_TRIPS,
|
|
GameState.TRANSPORTER_TRIPS]):
|
|
return _fail("the fresh transporter is missing from '%s'" % listed)
|
|
# Worst first: the nearly-spent one is the decision.
|
|
if listed.find("4 / ") > listed.find("%d / " % GameState.TRANSPORTER_TRIPS):
|
|
return _fail("the worn transporter is listed after the fresh one")
|
|
print("hub lists: %s" % listed.replace("\n", " | "))
|
|
elapsed = 0.0
|
|
stage = 1
|
|
return false
|
|
1:
|
|
# --- B. a workshop counts repairs, in its own capacity ----
|
|
load("res://tests/_staff.gd").staff(state, workshop_id, 1)
|
|
state.repair_drones[0].repairs_left = 3
|
|
menu.open_for(workshop_id)
|
|
var drone_text: String = menu.condition_list.text
|
|
if not drone_text.contains("3 / %d REPAIRS" % GameState.DRONE_REPAIRS):
|
|
return _fail("a workshop does not report repairs left: '%s'" % drone_text)
|
|
if drone_text.contains("RUNS"):
|
|
return _fail("a workshop reports its drones in RUNS: '%s'" % drone_text)
|
|
print("workshop lists: %s" % drone_text.replace("\n", " | "))
|
|
|
|
print("TEST_STATION_CONDITION_READOUT: PASS")
|
|
quit(0)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
print("TEST_STATION_CONDITION_READOUT: FAIL (%s)" % message)
|
|
quit(1)
|
|
return true
|