The shot was one flat line that appeared at full length and faded. Nothing happened at the ship, nothing moved, and a hit looked exactly like a miss. The blade now runs out of the hull over the first 30% of the flash on a cubic ease-out, holds white-hot, then falls away, with a hum in its width, a bright cap at the leading end, a flare at the emitter, and sparks off the hull that fired it. MuzzleSparkVFX takes an optional colour so the laser throws cyan where the ordinary gun (#87) keeps its gold. The extra light is a white-hot rod down the middle at 42% of the width, deliberately not a wider halo: the note at BEAM_WIDTH_SCALE records that the drawn beam spans exactly what the beam hits, and a halo past that would put the "it visibly missed" complaint back the other way round. The damage is untouched - dealt in full, along the whole beam, on the frame of the shot. A beam that had to arrive would change how the weapon plays, which the ticket did not ask for, so the test pins it down: a target at the far end of the range must be hit before the drawn blade has visibly reached it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
73 lines
2.5 KiB
GDScript
73 lines
2.5 KiB
GDScript
extends Node2D
|
|
|
|
# Forgejo #87: a brief spray of sparks off the hull that just fired.
|
|
#
|
|
# Firing was silent at the ship's end - a bolt simply appeared in front of it.
|
|
# With a whole fleet shooting there was nothing tying a shot to the vessel it
|
|
# came from, which mattered more once the player started aiming: you could not
|
|
# see which of your ships were actually in the fight.
|
|
#
|
|
# Deliberately tiny and short. This fires several times a second per vessel,
|
|
# so it has to be legible at a glance and gone before the next one - anything
|
|
# with weight would turn a firing fleet into a smear.
|
|
#
|
|
# No class_name, and it loads itself by path: a global class-name identifier
|
|
# is not reliably resolvable from a script reached through a preload chain,
|
|
# which is what happens here. Same reason as ResourcePickupVFX.
|
|
|
|
const SELF_PATH: String = "res://vfx/MuzzleSparkVFX.gd"
|
|
const SPARK_COUNT: int = 4
|
|
const DURATION: float = 0.13
|
|
const SPREAD: float = 0.5
|
|
const LENGTH: float = 9.0
|
|
const COLOR: Color = Color(1.0, 0.88, 0.42)
|
|
|
|
var age: float = 0.0
|
|
var color: Color = COLOR
|
|
var sparks: Array = []
|
|
|
|
|
|
# The colour is optional and defaults to the gun's own: #90 wanted the laser
|
|
# to do something at the hull too, and a cyan weapon throwing gold sparks
|
|
# would read as a different weapon firing from the same ship.
|
|
static func spawn(parent: Node, world_position: Vector2, direction: Vector2,
|
|
spark_color: Color = COLOR) -> void:
|
|
if direction.length() < 0.001:
|
|
return
|
|
var vfx: Node2D = (load(SELF_PATH) as GDScript).new()
|
|
vfx.position = world_position
|
|
vfx.rotation = direction.angle()
|
|
vfx.color = spark_color
|
|
parent.add_child(vfx)
|
|
|
|
|
|
func _ready() -> void:
|
|
# Built once here rather than per frame: the streaks never change shape,
|
|
# only how far along their own line they have travelled, so _draw reads
|
|
# fixed values. Same discipline as the explosion particles.
|
|
for i in range(SPARK_COUNT):
|
|
sparks.append({
|
|
"angle": randf_range(-SPREAD, SPREAD),
|
|
"speed": randf_range(190.0, 340.0),
|
|
"length": randf_range(LENGTH * 0.6, LENGTH),
|
|
})
|
|
queue_redraw()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
age += delta
|
|
if age >= DURATION:
|
|
queue_free()
|
|
return
|
|
queue_redraw()
|
|
|
|
|
|
func _draw() -> void:
|
|
var t: float = clampf(age / DURATION, 0.0, 1.0)
|
|
var fade: float = 1.0 - t
|
|
for spark in sparks:
|
|
var heading: Vector2 = Vector2.RIGHT.rotated(float(spark.angle))
|
|
var start: Vector2 = heading * float(spark.speed) * age
|
|
var end: Vector2 = start + heading * float(spark.length)
|
|
NeonDraw.draw_glowing_line(self, start, end, color, fade, 0.35,
|
|
color.darkened(0.7))
|