spaceship/CapturePoint.gd

113 lines
3.7 KiB
GDScript3
Raw Normal View History

class_name CapturePoint
extends Area2D
# One point on a landed planet's surface that must be held (fleet ship
# nearby, no enemy nearby) for capture_time seconds. Progress is polled by
# distance each frame rather than through physics signals - same idiom as
# RockField/GasVentField - since no collision response is needed, only
# proximity.
signal captured
enum State { NEUTRAL, BLOCKED, CAPTURING, CAPTURED }
@export var point_index: int = 0
@export var capture_radius: float = 220.0
@export var capture_time: float = 8.0
@export var enemy_clear_radius: float = 700.0
@export var drain_rate: float = 0.5
const NEUTRAL_COLOR: Color = Color(0.55, 0.85, 1.0)
const BLOCKED_COLOR: Color = Color(1.0, 0.3, 0.3)
const CAPTURING_COLOR: Color = Color(0.4, 1.0, 0.55)
const CAPTURED_COLOR: Color = Color(1.0, 0.85, 0.3)
const CORE_RADIUS: float = 60.0
var progress: float = 0.0
var state: int = State.NEUTRAL
func _ready() -> void:
queue_redraw()
# Called by PlanetSetupHost when this point was already captured on a
# previous visit, so re-landing restores it instead of starting the fill
# over. Permanent for the run - _process() below returns early once
# CAPTURED and never re-evaluates it.
func mark_captured() -> void:
progress = 1.0
state = State.CAPTURED
queue_redraw()
func _process(delta: float) -> void:
if state == State.CAPTURED:
return
var enemy_nearby: bool = _enemy_nearby()
var ship_in_range: bool = _ship_in_range()
if enemy_nearby:
state = State.BLOCKED
progress = maxf(progress - drain_rate * delta / capture_time, 0.0)
elif ship_in_range:
state = State.CAPTURING
progress = minf(progress + delta / capture_time, 1.0)
if progress >= 1.0:
state = State.CAPTURED
GameState.mark_point_captured(GameState.planet_id, point_index)
captured.emit()
else:
state = State.NEUTRAL
progress = maxf(progress - drain_rate * delta / capture_time, 0.0)
queue_redraw()
func _ship_in_range() -> bool:
for ship in get_tree().get_nodes_in_group("player_ships"):
if is_instance_valid(ship) and global_position.distance_to(ship.global_position) <= capture_radius:
return true
return false
func _enemy_nearby() -> bool:
for enemy in get_tree().get_nodes_in_group("enemies"):
if is_instance_valid(enemy) and global_position.distance_to(enemy.global_position) <= enemy_clear_radius:
return true
return false
func _state_color() -> Color:
match state:
State.BLOCKED:
return BLOCKED_COLOR
State.CAPTURING:
return CAPTURING_COLOR
State.CAPTURED:
return CAPTURED_COLOR
_:
return NEUTRAL_COLOR
func _draw() -> void:
var color: Color = _state_color()
# Outer ring: the actual capture radius, faint - tells the player how
# close they need to stay without dominating the screen.
_draw_ring(capture_radius, 0.0, TAU, color, 0.35)
# Inner beacon: fixed size regardless of capture_radius, so it reads
# clearly even when zoomed out. A filling arc sweeps clockwise from the
# top as progress advances, same "fill toward completion" idea as
# VesselGasBomberComponent's hull fill, adapted to a ring since a
# capture point has no hull.
_draw_ring(CORE_RADIUS, 0.0, TAU, color, 1.0)
if progress > 0.001:
var sweep: float = TAU * progress
_draw_ring(CORE_RADIUS, -PI / 2.0, -PI / 2.0 + sweep, color, 1.0, 10.0)
func _draw_ring(radius: float, from_angle: float, to_angle: float, color: Color, alpha: float,
core_width: float = 6.0) -> void:
draw_arc(Vector2.ZERO, radius, from_angle, to_angle, 64, Color(color, 0.16 * alpha), core_width * 2.6, true)
draw_arc(Vector2.ZERO, radius, from_angle, to_angle, 64, Color(color, 0.5 * alpha), core_width, true)
draw_arc(Vector2.ZERO, radius, from_angle, to_angle, 64, Color(1.0, 0.96, 0.9, 0.9 * alpha), core_width * 0.3, true)