85 lines
2.9 KiB
GDScript
85 lines
2.9 KiB
GDScript
class_name LaserController
|
|
extends "res://AimedFleetAbilityController.gd"
|
|
|
|
const BEAM_COLOR := Color(0.3, 0.9, 1.0)
|
|
const TARGET_COLOR := Color(0.6, 0.95, 1.0)
|
|
const BEAM_HALF_WIDTH := 22.0
|
|
const FLASH_DURATION := 0.22
|
|
const ENERGY_PER_SHOT: int = 8
|
|
|
|
var beams: Array = []
|
|
var last_hit_count: int = 0
|
|
|
|
|
|
func _button_path() -> NodePath: return "../UI/LaserButton"
|
|
func _status_path() -> NodePath: return "../UI/LaserStatus"
|
|
func _component_id() -> String: return "laser"
|
|
func _ammo_resource() -> String: return GameState.RESOURCE_ENERGY
|
|
func _ammo_cost() -> int: return ENERGY_PER_SHOT
|
|
func _aim_padding() -> float: return BEAM_HALF_WIDTH
|
|
func _has_effect_visuals() -> bool: return not beams.is_empty()
|
|
|
|
|
|
func _aim_max_range() -> float:
|
|
var ship: Node2D = _next_ready_ship()
|
|
if ship == null:
|
|
return 950.0
|
|
var component: Node = ship.get_component(_component_id())
|
|
return component.beam_range if component != null else 950.0
|
|
|
|
|
|
func _calculate_aim_target(drag: Vector2, origin: Vector2) -> Vector2:
|
|
# A beam always reaches full range; dragging chooses its direction only.
|
|
var direction: Vector2 = drag.normalized() if drag.length() > 0.001 else Vector2.UP
|
|
return origin + direction * _aim_max_range()
|
|
|
|
|
|
func _consume_shot(component: Node) -> bool:
|
|
return component.consume_shot()
|
|
|
|
|
|
func _perform_shot(ship: Node2D, component: Node) -> void:
|
|
var direction: Vector2 = aim_target - _aim_origin()
|
|
direction = direction.normalized() if direction.length() > 0.001 else Vector2.UP
|
|
var beam_to: Vector2 = ship.global_position + direction * component.beam_range
|
|
_strike(ship.global_position, beam_to, direction, int(component.damage))
|
|
|
|
|
|
func _next_ready_laser_ship() -> Node2D:
|
|
return _next_ready_ship()
|
|
|
|
|
|
func _advance_effects(delta: float) -> void:
|
|
for i in range(beams.size() - 1, -1, -1):
|
|
var beam: Dictionary = beams[i]
|
|
beam.time = maxf(float(beam.time) - delta, 0.0)
|
|
if float(beam.time) <= 0.0:
|
|
beams.remove_at(i)
|
|
|
|
|
|
func _strike(from: Vector2, to: Vector2, direction: Vector2, damage: int) -> void:
|
|
beams.append({"from": from, "to": to, "time": FLASH_DURATION})
|
|
last_hit_count = 0
|
|
var range_along: float = from.distance_to(to)
|
|
for enemy in get_tree().get_nodes_in_group("enemies"):
|
|
if not is_instance_valid(enemy) or not (enemy is Node2D):
|
|
continue
|
|
var to_enemy: Vector2 = enemy.global_position - from
|
|
var along: float = to_enemy.dot(direction)
|
|
if along < 0.0 or along > range_along:
|
|
continue
|
|
var perpendicular: float = (to_enemy - direction * along).length()
|
|
if perpendicular <= BEAM_HALF_WIDTH:
|
|
enemy.take_damage(damage)
|
|
last_hit_count += 1
|
|
|
|
|
|
func _draw() -> void:
|
|
if aiming:
|
|
NeonDraw.draw_glowing_line(self, _aim_origin(), aim_target,
|
|
TARGET_COLOR, 0.7, 0.6)
|
|
for beam in beams:
|
|
var progress: float = 1.0 - float(beam.time) / FLASH_DURATION
|
|
var alpha: float = 1.0 - progress
|
|
NeonDraw.draw_glowing_line(self, beam.from, beam.to,
|
|
BEAM_COLOR, alpha, 1.2 - progress * 0.6)
|