spaceship/tests/test_gas_bomber_cooldowns.gd
Loïc Wisniewski 1f5fecd371 Buy fleet berths with Fleet Offices
#219: fleet capacity is a built, supplied thing rather than a researched
one. The eight fleet-size doctrines are gone; capacity is one berth free
plus one per Fleet Office standing. An office charges every 25s for the
vessel in its berth, priced off that vessel's own build cost so an expensive
hull is expensive to keep, and drawn from its own store the way a gas burner
draws gas. An office that cannot pay goes dark, its vessel is scuttled, and
the player is told through the same queue quest lines use - which reaches
them on a planet surface too.

An empty berth is idle rather than starved: an office is only failing while
there is something in it to fail, and holding it disabled would leave a
berth that could never be crewed and so could never be paid for again.

Ships have no hit points and a shield absorbs a hit wholesale, so the lost
vessel is scuttled rather than damaged - otherwise a shielded hull would
survive its own eviction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
2026-09-05 17:09:27 +02:00

87 lines
3.2 KiB
GDScript

extends SceneTree
var main: Node
var fleet: Node
var skill: Node
var elapsed := 0.0
func _initialize() -> void:
var state: Node = root.get_node("GameState")
state.buildings.clear()
state.extractors.clear()
state.transporters.clear()
state.vessel_levels["unlock_gas_bomber"] = 1
# #60: shots draw from the fleet hold now - stock enough for both fires.
state.fleet_gas_cargo = 20
# #219: fleet capacity is built now - one berth free, one per Fleet
# Office. This test flies more than one vessel, so it needs the offices
# standing before the fleet spawns into them.
load("res://tests/_berths.gd").grant(root.get_node("GameState"), 6)
main = load("res://world/Main.tscn").instantiate()
root.add_child(main)
fleet = main.get_node("Fleet")
skill = main.get_node("GasBombController")
func _process(delta: float) -> bool:
elapsed += delta
if elapsed < 0.2:
return false
main.get_node("EnemySpawner").set_process(false)
main.get_node("AsteroidField").set_process(false)
# Asteroids take damage now, so anything the field already placed
# would soak a bomb meant for the enemies under test.
for hazard in get_nodes_in_group("asteroids"):
if is_instance_valid(hazard):
hazard.free()
fleet.spawn_ships(2, Vector2.ZERO, PackedStringArray([
GameState.VESSEL_GAS_BOMBER, GameState.VESSEL_GAS_BOMBER]))
skill._process(0.0)
var first: Node = fleet.ships[0].get_component("gas_bomber")
var second: Node = fleet.ships[1].get_component("gas_bomber")
if not first.is_ready() or not second.is_ready() or not skill.button.enabled:
return _fail("two fresh bombers did not each begin ready")
skill.aim_target = Vector2(500, 0)
skill._fire()
skill._process(0.0)
if first.is_ready() or not second.is_ready() or not skill.button.enabled:
return _fail("the first shot disabled more than its firing bomber")
# _filled_hull returns a LIST of polygons: a notched hull fills as two
# separate puddles until the level rises past the notch, and the gas
# bomber's tail is exactly such a notch.
var hull: PackedVector2Array = fleet.ships[0].definition.hull_points
var empty: Array = first._filled_hull(hull, 0.0)
var half: Array = first._filled_hull(hull, 0.5)
var half_points: int = 0
for piece in half:
half_points += piece.size()
if not empty.is_empty() or half_points < 3:
return _fail("bomber cooldown did not produce an empty-to-filling hull")
if skill.projectiles.size() != 1:
return _fail("the first bomber did not launch exactly one projectile")
skill.aim_target = Vector2(-500, 0)
skill._fire()
skill._process(0.0)
if first.is_ready() or second.is_ready() or skill.button.enabled:
return _fail("the skill stayed available after both bombers fired")
if skill.projectiles.size() != 2:
return _fail("independent bombers could not have shots in flight together")
# Rearming one bomber restores one available shot without touching the
# other bomber's cooldown.
first.tick(5.0) # matches GasBombController.COOLDOWN
skill._process(0.0)
if not first.is_ready() or second.is_ready() or not skill.button.enabled:
return _fail("one completed cooldown did not restore exactly one shot")
print("TEST_GAS_BOMBER_COOLDOWNS: PASS")
quit(0)
return true
func _fail(message: String) -> bool:
print("TEST_GAS_BOMBER_COOLDOWNS: FAIL (%s)" % message)
quit(1)
return true