Show the laser's whole reach while aiming

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019AJMdB7iuHuLFZM1aBu3GD
This commit is contained in:
Loïc Wisniewski 2026-09-04 23:20:29 +02:00
commit a902fddba0
4 changed files with 70 additions and 7 deletions

View file

@ -16,6 +16,9 @@ const BEAM_EFFECT = preload("res://vfx/LaserBeamEffect.gd")
const WEAPON_COMPONENT = preload("res://vessels/VesselWeaponComponent.gd")
const BEAM_COLOR := BEAM_EFFECT.BEAM_COLOR
const TARGET_COLOR := Color(0.6, 0.95, 1.0)
# #203's range mark and the assist's target ring, in screen pixels.
const END_TICK_PIXELS: float = 17.0
const TARGET_RING_PIXELS: float = 15.0
const BEAM_HALF_WIDTH := BEAM_EFFECT.BEAM_HALF_WIDTH
const FLASH_DURATION := BEAM_EFFECT.FLASH_DURATION
const ENERGY_PER_SHOT: int = VesselLaserComponent.ENERGY_PER_SHOT
@ -114,8 +117,48 @@ func beam_tip(beam: Node2D) -> Vector2:
return beam.tip()
# #203: the aim view frames the beam's WHOLE reach, not the point the aim
# happens to sit on. The assist can settle that point on an enemy a few
# hundred units away (#201), and fitting the camera to it would say the shot
# stops there. Fitting the far end instead is also what makes the range
# visible at all: the beam is 2900 units long and the inherited floor on the
# zoom - sized for the gas bomb's 950 - left three quarters of it off screen.
func _update_aim_zoom(origin: Vector2, target: Vector2) -> void:
super(origin, origin + _aim_heading(origin, target) * _aim_max_range())
func _aim_heading(origin: Vector2, target: Vector2) -> Vector2:
var direction: Vector2 = target - origin
return direction.normalized() if direction.length() > 0.001 else Vector2.UP
func _draw() -> void:
# Only the aim guide. Each beam draws itself, in its own node.
if aiming:
NeonDraw.draw_glowing_line(self, _aim_origin(), aim_target,
TARGET_COLOR, 0.7, 0.6)
if not aiming:
return
var origin: Vector2 = _aim_origin()
var reach: float = _aim_max_range()
var far_end: Vector2 = origin + _aim_heading(origin, aim_target) * reach
# Drawn to the far end rather than to aim_target, for the same reason the
# camera frames it: this is the line the beam will occupy (#203).
NeonDraw.draw_glowing_line(self, origin, far_end, TARGET_COLOR, 0.7, 0.6)
# A cross-tick where it stops, so the reach is a mark on the screen rather
# than a line that merely runs out. Sized in SCREEN pixels: framing 2900
# units puts the camera far enough out that a mark measured in world units
# would be a few pixels of nothing at the moment it matters most.
var across: Vector2 = far_end.direction_to(origin).orthogonal() \
* _screen_units(END_TICK_PIXELS)
NeonDraw.draw_glowing_line(self, far_end - across, far_end + across,
TARGET_COLOR, 0.7, 0.5)
# What the assist picked, when that is nearer than the far end. The line
# still runs past it - everything behind it on the same line is hit too.
if origin.distance_to(aim_target) < reach - 1.0:
NeonDraw.draw_glowing_circle(self, aim_target,
maxf(BEAM_HALF_WIDTH, _screen_units(TARGET_RING_PIXELS)),
TARGET_COLOR, 0.7)
# World units that cover a given number of screen pixels at the camera's
# current zoom.
func _screen_units(pixels: float) -> float:
return pixels / maxf(camera.zoom.x, 0.001)