70 lines
2.7 KiB
GDScript
70 lines
2.7 KiB
GDScript
extends Area2D
|
|
|
|
const COMBAT_TARGET = preload("res://core/CombatTarget.gd")
|
|
|
|
# #86: faster. The bolt used to crawl far enough behind a moving target that
|
|
# leading a shot was guesswork rather than skill.
|
|
# #155: was 850, which took 1.1s to cross the gun's own range - long enough
|
|
# that a shot read as leaving the ship rather than as hitting something. The
|
|
# range below is derived from this (see set_range), so raising it makes shots
|
|
# faster without making them fall short.
|
|
@export var speed: float = 1400.0
|
|
# How long a bolt lives if nothing sets a range. VesselWeaponComponent always
|
|
# does - see set_range() - so this only covers a projectile fired by something
|
|
# that has no range of its own.
|
|
@export var lifetime: float = 1.5
|
|
@export var damage: int = 1
|
|
@export var bolt_length: float = 20.0
|
|
|
|
var velocity: Vector2 = Vector2.ZERO
|
|
var age: float = 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
add_to_group("player_projectiles")
|
|
area_entered.connect(_on_area_entered)
|
|
# Draw once. The earlier invisible-bolt bug came from _draw() deriving
|
|
# the bolt's look from `age` - drawing only at age==0 froze it at
|
|
# whatever the age==0 case rendered as. _draw() below is now a fixed
|
|
# shape with no age dependency, so one draw is safe and matches the
|
|
# Ship.gd/Asteroid.gd pattern: position/rotation are transform
|
|
# properties Godot applies at render time without re-tessellating.
|
|
queue_redraw()
|
|
|
|
|
|
# The gun's reach, in world units. Converted to a lifetime here rather than
|
|
# stored separately, so the distance a bolt COVERS and the distance the aim
|
|
# guide DRAWS are the same number rather than two that have to be kept in
|
|
# step. They were not: the guide showed fire_range (320) while a bolt actually
|
|
# flew speed * lifetime (750), so shots landed well past the line promising
|
|
# where they would stop.
|
|
func set_range(world_units: float) -> void:
|
|
if world_units > 0.0 and speed > 0.0:
|
|
lifetime = world_units / speed
|
|
|
|
|
|
func launch(direction: Vector2) -> void:
|
|
velocity = direction.normalized() * speed
|
|
rotation = direction.angle() + PI / 2.0
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
position += velocity * delta
|
|
age += delta
|
|
if age >= lifetime:
|
|
queue_free()
|
|
|
|
|
|
func _on_area_entered(area: Area2D) -> void:
|
|
# Anything that opted into the weapon-target contract answers take_damage(),
|
|
# so adding a new target composition needs no projectile type check.
|
|
if area.is_in_group(COMBAT_TARGET.GROUP):
|
|
area.take_damage(damage)
|
|
queue_free()
|
|
|
|
|
|
func _draw() -> void:
|
|
# Fixed-length, constant-brightness bolt. Drawn once and left alone.
|
|
var tip: Vector2 = Vector2(0, -bolt_length * 0.6)
|
|
var tail: Vector2 = Vector2(0, bolt_length * 0.4)
|
|
NeonDraw.draw_glowing_line(self, tail, tip, Color(1.0, 0.85, 0.3), 1.0, 0.5)
|