class_name VesselLaserComponent extends Node2D # Same shape as VesselGasBomberComponent (per-ship cooldown, ready/cooling # hull fill) - #58's own note: two laser vessels must give two shots, so # this lives on the SHIP, not the fleet-wide LaserController. # # Unlike the gas bomber, damage/beam_range/cooldown are resolved from the # vessel's own base_stats/upgrades (apply_stats(), the same pattern # VesselWeaponComponent uses) rather than hardcoded in the controller, so # ship-lab upgrades actually reach them. var capability_id := "laser" var damage: int = 2 var beam_range: float = 950.0 var cooldown_duration: float = 4.0 var cooldown_left: float = 0.0 const READY_COLOR := Color(0.3, 0.9, 1.0) const COOLING_COLOR := Color(0.08, 0.28, 0.34) func setup() -> void: # Keep the ship's normal neon outline above the charge fill. show_behind_parent = true queue_redraw() func apply_stats() -> void: var ship: Node = get_parent() var state: Node = ship.get_tree().root.get_node("GameState") damage = int(state.vessel_stat(ship.vessel_type, "damage")) beam_range = state.vessel_stat(ship.vessel_type, "beam_range") cooldown_duration = state.vessel_stat(ship.vessel_type, "beam_cooldown") queue_redraw() func is_ready() -> bool: return cooldown_left <= 0.0 func consume_shot() -> bool: if not is_ready(): return false cooldown_left = maxf(cooldown_duration, 0.001) queue_redraw() return true func tick(delta: float) -> void: if cooldown_left <= 0.0: return cooldown_left = maxf(cooldown_left - delta, 0.0) queue_redraw() func cooldown_progress() -> float: return 1.0 - cooldown_left / maxf(cooldown_duration, 0.001) func _filled_hull(points: PackedVector2Array, progress: float) -> PackedVector2Array: if points.size() < 3 or progress <= 0.001: return PackedVector2Array() if progress >= 0.999: return points.duplicate() var top := INF var bottom := -INF for point in points: top = minf(top, point.y) bottom = maxf(bottom, point.y) var fill_y: float = lerpf(bottom, top, clampf(progress, 0.0, 1.0)) var clipped := PackedVector2Array() var previous: Vector2 = points[-1] var previous_inside := previous.y >= fill_y for current in points: var current_inside := current.y >= fill_y if current_inside != previous_inside: var amount: float = (fill_y - previous.y) / (current.y - previous.y) clipped.append(previous.lerp(current, amount)) if current_inside: clipped.append(current) previous = current previous_inside = current_inside return clipped func _draw() -> void: var parent_ship: Node = get_parent() if parent_ship == null or parent_ship.definition == null: return var core: Color = parent_ship.core_color if parent_ship != null else Color.WHITE var hull: PackedVector2Array = parent_ship.definition.hull_points if is_ready(): draw_colored_polygon(hull, Color(READY_COLOR, 0.42)) NeonDraw.draw_glowing_polygon(self, hull, READY_COLOR, 0.75, core, 0.75) return draw_colored_polygon(hull, Color(COOLING_COLOR, 0.58)) var filled := _filled_hull(hull, cooldown_progress()) if filled.size() >= 3: draw_colored_polygon(filled, Color(READY_COLOR, 0.68))