A wave is an attack on what the player has built, not a hazard sitting in space waiting to be flown near. Wave enemies now ignore the detection radius their components otherwise respect and close on the nearest building from wherever they arrived. The flag is per INSTANCE rather than per definition: the same chaser is a wave when the director launches it and a garrison when a map author places it, and a planet garrison that chased the player across the system would be a very different thing from what the editor is used to place. The test pins both halves - a wave chaser 6000px out closed on a crafter, and an identically built one nobody sent held its station. What it fights on the way is unchanged: EnemyTargets already prefers a vessel inside 520px over a building further off, and already searches the player_ships group, so the fleet and an outpost's garrison both count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
2.1 KiB
GDScript
56 lines
2.1 KiB
GDScript
class_name EnemyStandoffComponent
|
|
extends Node
|
|
|
|
# Holds a preferred distance from the nearest player ship instead of
|
|
# ramming: advances when too far, retreats when too close, drifts
|
|
# laterally in the comfortable band between so it still reads as alive
|
|
# rather than frozen. Never pair with a pursuit component on the same
|
|
# enemy - the two would fight over velocity every tick.
|
|
|
|
var capability_id: String = "standoff"
|
|
|
|
@export var preferred_range: float = 620.0
|
|
@export var retreat_range_factor: float = 0.75
|
|
# #69: kept comfortably beyond preferred_range so it still has room to close
|
|
# in before settling at stand-off distance, but the fleet has to actually be
|
|
# somewhere on the board first - see EnemyPursuitComponent's identical fix.
|
|
@export var detection_range: float = 1200.0
|
|
|
|
var velocity: Vector2 = Vector2.ZERO
|
|
|
|
|
|
func tick(delta: float) -> void:
|
|
var enemy: Node2D = get_parent()
|
|
# #101: holds its preferred range off a building just as off a ship.
|
|
var target: Node2D = EnemyTargets.nearest_target(self, enemy.global_position)
|
|
if target == null:
|
|
return
|
|
var to_target: Vector2 = target.global_position - enemy.global_position
|
|
var distance: float = to_target.length()
|
|
if distance < 1.0:
|
|
return
|
|
# #113: see EnemyPursuitComponent - a wave arrives with somewhere to be.
|
|
if not EnemyTargets.ignores_range(enemy) and distance > detection_range:
|
|
return
|
|
var direction: Vector2 = to_target / distance
|
|
var retreat_range: float = preferred_range * retreat_range_factor
|
|
|
|
var desired_velocity: Vector2
|
|
if distance > preferred_range:
|
|
desired_velocity = direction * enemy.speed
|
|
elif distance < retreat_range:
|
|
desired_velocity = -direction * enemy.speed
|
|
else:
|
|
var lateral: Vector2 = Vector2(-direction.y, direction.x)
|
|
desired_velocity = lateral * enemy.speed * 0.5
|
|
|
|
velocity = velocity.move_toward(desired_velocity, enemy.acceleration * delta)
|
|
enemy.global_position += velocity * delta
|
|
|
|
if velocity.length() > 5.0:
|
|
var target_angle: float = velocity.angle() + PI / 2.0
|
|
enemy.rotation = lerp_angle(enemy.rotation, target_angle, enemy.rotation_speed * delta)
|
|
|
|
|
|
func _nearest_ship(enemy: Node2D) -> Node2D:
|
|
return EnemyTargets.nearest_ship(self, enemy.global_position)
|