extends Area2D # #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: # Asteroids answer take_damage() exactly as enemies do, so both are hit by # the same branch rather than by a second special case. if area.is_in_group("enemies") or area.is_in_group("asteroids"): 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)