spaceship/tests/test_energy_propulser_dash.gd

154 lines
5.9 KiB
GDScript3
Raw Normal View History

2026-09-02 13:43:56 +02:00
extends SceneTree
# Forgejo #179: an Energy Propulser in the fleet grants a hold-aim-release
# dash. Covers visibility, aimed movement, energy cost, formation integrity,
# and one cooldown per Propulser.
const DASH_COST: int = 15
const DASH_DISTANCE: float = 520.0
2026-09-03 23:14:02 +02:00
const DASH_SETTLE_TIME: float = 0.3
2026-09-02 13:43:56 +02:00
var state: Node
var main: Node
var fleet: Node
var dash: Node
var stage: int = 0
var elapsed: float = 0.0
var first_origin: Vector2
var second_origin: Vector2
var offsets_before: Array = []
var first_component: Node
func _initialize() -> void:
state = root.get_node("GameState")
state.reset_new_game()
state.intro_active = false
state.reset_quests()
state.vessel_levels["unlock_distorder"] = 1
state.fleet_inventory.total_capacity = -1
state.deposit_fleet_resource(GameState.RESOURCE_ENERGY, DASH_COST * 3)
# #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)
2026-09-02 13:43:56 +02:00
main = load("res://world/Main.tscn").instantiate()
root.add_child(main)
current_scene = main
fleet = main.get_node("Fleet")
dash = main.get_node("DashController")
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
if elapsed < 0.3:
return false
main.get_node("EnemySpawner").set_process(false)
main.get_node("AsteroidField").set_process(false)
if main.get_node("UI/DashButton").visible:
return _fail("dash button appeared without an Energy Propulser")
# The carrier keeps enough hold capacity for both dashes. Two
# Propulsers prove that cooldown belongs to a vessel, not the fleet.
fleet.spawn_ships(3, Vector2.ZERO, PackedStringArray([
GameState.VESSEL_CARRIER, GameState.VESSEL_DISTORDER,
GameState.VESSEL_DISTORDER,
]))
dash._process(0.0)
if not main.get_node("UI/DashButton").visible:
return _fail("an Energy Propulser did not reveal the dash button")
first_component = dash._next_ready_ship().get_component("distorder")
first_origin = fleet.get_centroid()
for ship in fleet.ships:
offsets_before.append(ship.position - first_origin)
_aim_and_release(Vector2.RIGHT, 11)
if not fleet.is_dashing():
return _fail("releasing a rightward aim did not begin a dash")
if state.fleet_resource(GameState.RESOURCE_ENERGY) != DASH_COST * 2:
return _fail("first dash did not consume %d energy" % DASH_COST)
if first_component.is_ready():
return _fail("the Propulser that dashed did not enter cooldown")
2026-09-04 18:40:58 +02:00
# The burst should cover substantially more than a linear quarter of
# its distance at a quarter of its duration, then ease into its stop.
2026-09-04 20:01:56 +02:00
fleet.set_physics_process(false)
2026-09-04 18:40:58 +02:00
fleet._advance_dash(fleet.dash_duration * 0.25)
var early_distance: float = fleet.get_centroid().distance_to(first_origin)
if early_distance <= DASH_DISTANCE * 0.45 \
or early_distance >= DASH_DISTANCE * 0.7:
return _fail("dash has no useful ease-out curve (quarter-time distance %.1f)" \
% early_distance)
2026-09-04 20:01:56 +02:00
fleet.set_physics_process(true)
2026-09-02 13:43:56 +02:00
dash._process(0.0)
if not main.get_node("UI/DashButton").enabled:
return _fail("one cooling Propulser disabled a second ready one")
stage = 1
elapsed = 0.0
return false
1:
2026-09-03 23:14:02 +02:00
if elapsed < DASH_SETTLE_TIME:
2026-09-02 13:43:56 +02:00
return false
var moved: Vector2 = fleet.get_centroid() - first_origin
if absf(moved.x - DASH_DISTANCE) > 8.0 or absf(moved.y) > 8.0:
return _fail("rightward aim moved the fleet by %s" % moved)
for i in range(fleet.ships.size()):
var offset: Vector2 = fleet.ships[i].position - fleet.get_centroid()
if offset.distance_to(offsets_before[i]) > 1.0:
return _fail("dash distorted formation slot %d by %.2f" \
% [i, offset.distance_to(offsets_before[i])])
second_origin = fleet.get_centroid()
_aim_and_release(Vector2.UP, 12)
if state.fleet_resource(GameState.RESOURCE_ENERGY) != DASH_COST:
return _fail("second Propulser did not pay for its own dash")
dash._process(0.0)
if main.get_node("UI/DashButton").enabled:
return _fail("dash remained enabled after both Propulsers fired")
stage = 2
elapsed = 0.0
return false
2:
2026-09-03 23:14:02 +02:00
if elapsed < DASH_SETTLE_TIME:
2026-09-02 13:43:56 +02:00
return false
var moved: Vector2 = fleet.get_centroid() - second_origin
if absf(moved.y + DASH_DISTANCE) > 8.0 or absf(moved.x) > 8.0:
return _fail("upward aim moved the fleet by %s" % moved)
first_component.tick(first_component.DASH_COOLDOWN)
dash._process(0.0)
if not main.get_node("UI/DashButton").enabled:
return _fail("button did not re-enable when one Propulser recharged")
var surface: Node = load("res://surface/PlanetSurface.tscn").instantiate()
if not surface.has_node("DashController") or not surface.has_node("UI/DashButton"):
return _fail("planet surface does not carry the dash ability")
2026-09-03 23:14:02 +02:00
if surface.has_node("UI/SpeedFader"):
return _fail("planet surface still carries the sustained speed multiplier")
2026-09-02 13:43:56 +02:00
surface.free()
print("two aimed dashes moved %.0f units, preserved formation, and cooled down per Propulser" \
% DASH_DISTANCE)
print("TEST_ENERGY_PROPULSER_DASH: PASS")
quit(0)
return true
return false
func _aim_and_release(direction: Vector2, touch_index: int) -> void:
var button: Control = main.get_node("UI/DashButton")
var press := InputEventScreenTouch.new()
press.index = touch_index
press.pressed = true
press.position = button.global_position + button.size * 0.5
main._handle_touch(press)
var drag := InputEventScreenDrag.new()
drag.index = touch_index
drag.position = press.position + direction * 240.0
main._handle_drag(drag)
var release := InputEventScreenTouch.new()
release.index = touch_index
release.pressed = false
release.position = drag.position
main._handle_touch(release)
func _fail(message: String) -> bool:
print("TEST_ENERGY_PROPULSER_DASH: FAIL (%s)" % message)
quit(1)
return true