93 lines
3.4 KiB
GDScript3
93 lines
3.4 KiB
GDScript3
|
|
extends Node2D
|
||
|
|
|
||
|
|
# Points every armed vessel's gun wherever the right-hand stick is pushed.
|
||
|
|
#
|
||
|
|
# The fleet's guns used to seek their own targets and fire on their own. That
|
||
|
|
# made combat something that happened TO the player rather than something they
|
||
|
|
# did, and left the right half of the screen with nothing on it. Aiming is now
|
||
|
|
# the second stick, and this is the only thing that tells a weapon to fire.
|
||
|
|
#
|
||
|
|
# Deliberately a scene node rather than state on the fleet or in GameState:
|
||
|
|
# it is input plumbing, it belongs to whichever scene is showing the stick,
|
||
|
|
# and a fleet that moves between scenes should arrive with its guns silent
|
||
|
|
# until the local stick says otherwise.
|
||
|
|
|
||
|
|
# The aim guide: a dotted line from the fleet along the heading being fired,
|
||
|
|
# out to the guns' own range. Deliberately faint and broken rather than a
|
||
|
|
# solid beam - it has to answer "where will this go and how far" at a glance
|
||
|
|
# without competing with the shots themselves for attention.
|
||
|
|
const GUIDE_DASH: float = 14.0
|
||
|
|
const GUIDE_GAP: float = 16.0
|
||
|
|
const GUIDE_WIDTH: float = 2.0
|
||
|
|
const GUIDE_COLOR: Color = Color(1.0, 0.55, 0.38, 0.28)
|
||
|
|
|
||
|
|
@onready var fleet: Node2D = get_node("../Fleet")
|
||
|
|
# Optional so a scene without a fire stick (a preview, a test harness) still
|
||
|
|
# runs - the guns simply stay quiet, which is the correct resting state.
|
||
|
|
@onready var stick: Node = get_node_or_null("../UI/FireStick")
|
||
|
|
|
||
|
|
var aim_direction: Vector2 = Vector2.ZERO
|
||
|
|
|
||
|
|
|
||
|
|
func _process(_delta: float) -> void:
|
||
|
|
var direction: Vector2 = stick.direction if stick != null else Vector2.ZERO
|
||
|
|
aim(direction)
|
||
|
|
# The guide starts at the fleet, which is usually moving, so this redraws
|
||
|
|
# while aiming rather than only when the heading changes. Once the stick is
|
||
|
|
# released it redraws once more to clear the line, then stops.
|
||
|
|
var was_aiming: bool = aim_direction != Vector2.ZERO
|
||
|
|
aim_direction = direction
|
||
|
|
if direction != Vector2.ZERO or was_aiming:
|
||
|
|
queue_redraw()
|
||
|
|
|
||
|
|
|
||
|
|
# Also the entry point for anything driving the guns without a stick - tests
|
||
|
|
# aim through this rather than reaching into each weapon component, so what
|
||
|
|
# they exercise is the same path the stick uses.
|
||
|
|
func aim(direction: Vector2) -> void:
|
||
|
|
if fleet == null:
|
||
|
|
return
|
||
|
|
for ship in fleet.ships:
|
||
|
|
if not is_instance_valid(ship):
|
||
|
|
continue
|
||
|
|
var weapon: Node = ship.get_component("weapon")
|
||
|
|
if weapon != null:
|
||
|
|
weapon.set_fire_direction(direction)
|
||
|
|
|
||
|
|
|
||
|
|
# How far the guide reaches: the actual range of a gun that is actually
|
||
|
|
# aboard, so the line never promises reach the fleet does not have.
|
||
|
|
func guide_range() -> float:
|
||
|
|
for ship in fleet.ships:
|
||
|
|
if not is_instance_valid(ship):
|
||
|
|
continue
|
||
|
|
var weapon: Node = ship.get_component("weapon")
|
||
|
|
if weapon != null:
|
||
|
|
return float(weapon.fire_range)
|
||
|
|
return 0.0
|
||
|
|
|
||
|
|
|
||
|
|
func _draw() -> void:
|
||
|
|
if aim_direction == Vector2.ZERO or fleet == null or fleet.ships.is_empty():
|
||
|
|
return
|
||
|
|
var reach: float = guide_range()
|
||
|
|
if reach <= 0.0:
|
||
|
|
return
|
||
|
|
var origin: Vector2 = fleet.get_centroid()
|
||
|
|
var heading: Vector2 = aim_direction.normalized()
|
||
|
|
var travelled: float = GUIDE_GAP
|
||
|
|
while travelled < reach:
|
||
|
|
var dash_end: float = minf(travelled + GUIDE_DASH, reach)
|
||
|
|
draw_line(origin + heading * travelled, origin + heading * dash_end,
|
||
|
|
GUIDE_COLOR, GUIDE_WIDTH, true)
|
||
|
|
travelled = dash_end + GUIDE_GAP
|
||
|
|
|
||
|
|
|
||
|
|
func is_firing() -> bool:
|
||
|
|
for ship in fleet.ships:
|
||
|
|
if not is_instance_valid(ship):
|
||
|
|
continue
|
||
|
|
var weapon: Node = ship.get_component("weapon")
|
||
|
|
if weapon != null and weapon.is_firing():
|
||
|
|
return true
|
||
|
|
return false
|