EnemyStandoffComponent holds a preferred distance from the nearest player ship instead of ramming (advance beyond it, retreat inside retreat_range_factor of it, drift laterally in the comfortable band between) - never paired with a pursuit component. EnemyRangedWeaponComponent mirrors VesselWeaponComponent for an enemy: fires an EnemyProjectile aimed at the target's CURRENT position rather than led, so a moving ship can dodge it (projectile_speed 260 vs the interceptor's 460 max_speed). Registered in EnemyDefinitions so it's placeable from a planet setup by enemy_id. test_sniper_enemy.gd went through a few iterations to get its own timing right: physics ticks can run ahead of a SceneTree test's own _process() clock under this device's load, so the cooldown-violation check reads the component's own fire_timer directly (a rising value means it refired) instead of comparing projectile counts against a wall-clock wait, and manually repositioned ships need their move_target re-pinned or Ship's own steering silently drives them back to the stale target. Part of the #40 conquest-mechanic breakdown.
76 lines
2.8 KiB
GDScript
76 lines
2.8 KiB
GDScript
extends Node2D
|
|
|
|
# Setup is deferred to the first _process() tick rather than done in
|
|
# _ready(): PlanetAtmosphereController auto-spawns a 5-ship default fleet
|
|
# in ITS _ready() on any fresh (non-returning) Main.tscn load, and since
|
|
# this scene is itself added to the tree from inside _screenshot_tool.gd's
|
|
# own _initialize() (a nested, not-yet-settled context), there's no
|
|
# guaranteed ordering between the two _ready() calls - clearing the fleet
|
|
# here could run BEFORE that auto-spawn and then get silently overwritten
|
|
# by it. Waiting one frame guarantees it has already run first.
|
|
#
|
|
# Even after clearing, physics ticks can run several times before this
|
|
# script's own next _process() tick (softpipe rendering is slow, and
|
|
# physics is decoupled from it), so an auto-spawned ship can still get one
|
|
# shot off in flight toward these enemies before the clearing code above
|
|
# ever runs. Pausing the whole tree once everything is placed (last thing
|
|
# this script does) freezes any such shot in transit for good, rather than
|
|
# letting it arrive a few physics ticks later while the screenshot is
|
|
# still waiting out its frame count.
|
|
|
|
var main: Node
|
|
var setup_done: bool = false
|
|
|
|
|
|
func _ready() -> void:
|
|
GameState.buildings.clear()
|
|
GameState.extractors.clear()
|
|
GameState.transporters.clear()
|
|
main = load("res://Main.tscn").instantiate()
|
|
add_child(main)
|
|
main.get_node("BackgroundLayer").visible = false
|
|
main.get_node("AsteroidField").visible = false
|
|
main.get_node("PlanetField").visible = false
|
|
main.get_node("BuildingField").visible = false
|
|
main.get_node("EnemySpawner").set_process(false)
|
|
main.get_node("AsteroidField").set_process(false)
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
if setup_done:
|
|
return
|
|
setup_done = true
|
|
|
|
var fleet: Node = main.get_node("Fleet")
|
|
for ship in fleet.ships:
|
|
if is_instance_valid(ship):
|
|
ship.set_physics_process(false)
|
|
fleet.spawn_ships(0, Vector2.ZERO)
|
|
# spawn_ships(0, ...) still isn't a truly empty fleet: it falls back to
|
|
# one default interceptor whenever the requested composition would
|
|
# otherwise be empty (Fleet.gd's own "the fleet is never really empty"
|
|
# rule). Clear that forced ship too, and halt it the same way.
|
|
for ship in fleet.ships:
|
|
if is_instance_valid(ship):
|
|
ship.set_physics_process(false)
|
|
ship.queue_free()
|
|
fleet.ships.clear()
|
|
for projectile in get_tree().get_nodes_in_group("player_projectiles"):
|
|
projectile.queue_free()
|
|
|
|
var chaser: Node = load("res://Enemy.tscn").instantiate()
|
|
chaser.enemy_id = "chaser"
|
|
chaser.position = Vector2(-160, -80)
|
|
main.add_child(chaser)
|
|
chaser.speed = 0.0
|
|
|
|
var sniper: Node = load("res://Enemy.tscn").instantiate()
|
|
sniper.enemy_id = "sniper"
|
|
sniper.position = Vector2(160, -80)
|
|
main.add_child(sniper)
|
|
sniper.speed = 0.0
|
|
|
|
var camera: Camera2D = main.get_node("Camera2D")
|
|
camera.zoom = Vector2(1.4, 1.4)
|
|
|
|
get_tree().paused = true
|