#216: transport hubs and spacial workshops no longer conjure their crews. Transporters and repair drones are ordered at a ship crafter, assigned per building through a new Station menu, flown out from the mothership hangar, and spent by the work they do - 20 runs, 12 repairs, counted where the run ENDS so nothing retires with cargo in the air or a building half repaired. A spent vessel is gone, so the structure is short again and the player has to build another. The menu shows what each one has left, worst first. Craftability needed its own flag: "the crafter can make it" and "it can take a berth in the fleet" stopped being the same question. #217: transporters crawl at 120 rather than 150, and a Burn Drive lets them carry energy and spend it to travel far faster. Fetching it is a leg of its own, to the nearest store that has any - so a refuelling transporter cannot be dispatched mid-errand - and the leg flown to COLLECT fuel is not charged for it. Unresearched, nothing detours and nothing changes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
139 lines
5.2 KiB
GDScript
139 lines
5.2 KiB
GDScript
extends SceneTree
|
|
|
|
# Forgejo #216, the workshop half. A spacial workshop used to conjure its own
|
|
# repair drones, free and permanent. They are built at a ship crafter now,
|
|
# flown out from the mothership hangar to whichever workshop asked for them,
|
|
# and spent by the repairs they make.
|
|
#
|
|
# test_transporter_supply.gd covers the same rules on the hub side. The two
|
|
# share a model but not their wear paths - a transporter spends a run getting
|
|
# home, a drone spends a repair getting home - so the drone's own path is
|
|
# exercised here rather than assumed from the transporter's.
|
|
#
|
|
# Covers:
|
|
# A. a new workshop stands empty, and takes nothing until it is asked to
|
|
# B. the crafter accepts a repair drone order, which it used to refuse
|
|
# C. an assigned workshop is sent one from the hangar
|
|
# D. it repairs a damaged structure and is worn by doing so
|
|
# E. its last repair uses it up, and the workshop is short again
|
|
|
|
var state: Node
|
|
var workshop_id: int
|
|
var damaged_id: int
|
|
var elapsed: float = 0.0
|
|
var stage: int = 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
state = root.get_node("GameState")
|
|
state.buildings.clear()
|
|
state.extractors.clear()
|
|
state.transporters.clear()
|
|
state.repair_drones.clear()
|
|
state.stored_vessels.clear()
|
|
state.vessel_deliveries.clear()
|
|
state.reset_progression()
|
|
state.rocks = 600
|
|
state.unlocked = ["shipyards", "logistics"]
|
|
state.add_building(state.BUILDING_MOTHERSHIP, Vector2.ZERO)
|
|
workshop_id = state.add_building(GameState.BUILDING_WORKSHOP, Vector2(700.0, 0.0))
|
|
damaged_id = state.add_building(state.BUILDING_SHIP_CRAFTER, Vector2(900.0, 200.0))
|
|
state.deposit_resource_to_building(workshop_id, GameState.RESOURCE_ORE, 400)
|
|
|
|
|
|
func _drones() -> int:
|
|
return state.station_crew(workshop_id)
|
|
|
|
|
|
func _process(delta: float) -> bool:
|
|
elapsed += delta
|
|
match stage:
|
|
0:
|
|
# --- A. nothing comes with the structure ---------------------
|
|
if not state.repair_drones.is_empty():
|
|
return _fail("a new workshop arrived with %d drones" % state.repair_drones.size())
|
|
state.stored_vessels.append(GameState.REPAIR_DRONE)
|
|
state._advance_station_dispatch()
|
|
if _drones() != 0:
|
|
return _fail("a workshop that asked for nothing took a drone anyway")
|
|
print("a new workshop stands empty, and takes nothing unasked")
|
|
|
|
# --- B. the crafter takes the order --------------------------
|
|
var crafter_id: int = state.add_building(state.BUILDING_SHIP_CRAFTER, Vector2(-900.0, 0.0))
|
|
if not state.queue_order(crafter_id, GameState.REPAIR_DRONE):
|
|
return _fail("the crafter refused a repair drone order")
|
|
state.find_building(crafter_id).orders.clear()
|
|
|
|
# --- C. assigned, then sent ----------------------------------
|
|
if state.set_station_desired(workshop_id, 1) != 1:
|
|
return _fail("the workshop refused an order for one drone")
|
|
state._advance_station_dispatch()
|
|
if state.vessel_deliveries.is_empty():
|
|
return _fail("nothing was sent to the workshop")
|
|
elapsed = 0.0
|
|
stage = 1
|
|
return false
|
|
1:
|
|
if _drones() < 1:
|
|
if elapsed > 25.0:
|
|
return _fail("the drone never reached the workshop")
|
|
return false
|
|
var fresh: Array = state.station_condition(workshop_id)
|
|
if fresh.size() != 1 or int(fresh[0]) != GameState.DRONE_REPAIRS:
|
|
return _fail("the arrived drone reports %s, expected [%d]" \
|
|
% [fresh, GameState.DRONE_REPAIRS])
|
|
print("ordered and flown out: 1 drone with %d repairs in it" % GameState.DRONE_REPAIRS)
|
|
|
|
# --- D. it does the work, and is worn by it ------------------
|
|
state.damage_building(damaged_id, state.building_max_hp(state.BUILDING_SHIP_CRAFTER) / 2)
|
|
elapsed = 0.0
|
|
stage = 2
|
|
return false
|
|
2:
|
|
if state.building_health_fraction(damaged_id) >= 0.999:
|
|
print("the workshop's own drone repaired the structure")
|
|
elapsed = 0.0
|
|
stage = 3
|
|
return false
|
|
if elapsed > 40.0:
|
|
return _fail("the drone never repaired the damaged structure")
|
|
return false
|
|
3:
|
|
# Spent on getting HOME, so this waits for it to be idle again.
|
|
if state.repair_drones.is_empty():
|
|
return _fail("the drone vanished mid-run")
|
|
if String(state.repair_drones[0].state) != GameState.DRONE_IDLE:
|
|
if elapsed > 25.0:
|
|
return _fail("the drone never got home")
|
|
return false
|
|
var worn: Array = state.station_condition(workshop_id)
|
|
if worn.is_empty() or int(worn[0]) >= GameState.DRONE_REPAIRS:
|
|
return _fail("a drone that ran a repair is still unworn (%s)" % [worn])
|
|
print("a completed repair wore it to %d" % int(worn[0]))
|
|
|
|
# --- E. and its last one uses it up --------------------------
|
|
state.repair_drones[0].repairs_left = 1
|
|
state.damage_building(damaged_id, state.building_max_hp(state.BUILDING_SHIP_CRAFTER) / 2)
|
|
elapsed = 0.0
|
|
stage = 4
|
|
return false
|
|
4:
|
|
if not state.repair_drones.is_empty():
|
|
if elapsed > 40.0:
|
|
return _fail("a drone on its last repair was never retired")
|
|
return false
|
|
if state.stored_vessels.has(GameState.REPAIR_DRONE):
|
|
return _fail("a spent drone went back to the hangar instead of being used up")
|
|
if _drones() != 0:
|
|
return _fail("the workshop still counts a retired drone")
|
|
print("spent after its last repair: gone, and the workshop is short again")
|
|
print("TEST_WORKSHOP_STAFFING: PASS")
|
|
quit(0)
|
|
return true
|
|
return false
|
|
|
|
|
|
func _fail(message: String) -> bool:
|
|
print("TEST_WORKSHOP_STAFFING: FAIL (%s)" % message)
|
|
quit(1)
|
|
return true
|