extends Node # ScreenShakeManager - Camera-local shake that follows the player var camera: Camera3D var shake_intensity: float = 0.0 var shake_duration: float = 0.0 var shake_timer: float = 0.0 var shake_offset: Vector3 = Vector3.ZERO # Shake presets const SHAKE_TARGETED: Dictionary = {"intensity": 0.15, "duration": 0.4} const SHAKE_GOAL_COMPLETE: Dictionary = {"intensity": 0.1, "duration": 0.3} const SHAKE_LIGHT: Dictionary = {"intensity": 0.05, "duration": 0.2} func initialize(p_camera: Camera3D): camera = p_camera func shake(intensity: float, duration: float) -> void: """Trigger a camera shake. Intensity in world units, duration in seconds.""" if intensity <= 0 or duration <= 0: return # Take the stronger shake if already active if shake_timer > 0 and intensity <= shake_intensity: return shake_intensity = intensity shake_duration = duration shake_timer = duration func _process(delta: float) -> void: if not camera or not is_instance_valid(camera): return if shake_timer > 0: shake_timer -= delta var progress = shake_timer / shake_duration # Fade out intensity as timer expires var current_intensity = shake_intensity * progress # Random offset within intensity range shake_offset = Vector3( randf_range(-current_intensity, current_intensity), randf_range(-current_intensity, current_intensity) * 0.5, randf_range(-current_intensity, current_intensity) ) camera.position += shake_offset else: shake_offset = Vector3.ZERO