extends Camera3D ## Nausea screen-tilt effect. ## ## Rolls the camera slightly on its local Z axis, oscillating over time, with ## amplitude proportional to nausea intensity. Attach to the player `Camera3D`. ## The first-person controller only writes camera rotation.x (pitch), so writing ## rotation.z here does not conflict. ## Maximum tilt in degrees at full nausea (100). @export var max_tilt_deg: float = 5.0 ## Oscillation frequency in Hz. @export var frequency_hz: float = 0.3 ## How fast the effective intensity follows the target, in intensity units/sec. ## At 200, a full 0->100 change resolves in ~0.5s. @export var intensity_follow_speed: float = 200.0 var _target_intensity: float = 0.0 var _current_intensity: float = 0.0 var _time: float = 0.0 func _ready() -> void: # Resolve the SymptomManager autoload via the scene tree root when possible, # falling back to the global autoload identifier. var sm: Node = null if is_inside_tree(): sm = get_tree().root.get_node_or_null("SymptomManager") if sm == null: sm = SymptomManager if sm: sm.nausea_intensity_changed.connect(_on_nausea_changed) _target_intensity = sm.get_intensity(sm.NAUSEA) _current_intensity = _target_intensity func _on_nausea_changed(value: float) -> void: _target_intensity = value func _process(delta: float) -> void: _time += delta _current_intensity = move_toward( _current_intensity, _target_intensity, intensity_follow_speed * delta ) var amplitude := deg_to_rad(max_tilt_deg) * (_current_intensity / 100.0) var roll := sin(_time * TAU * frequency_hz) * amplitude rotation.z = roll