class_name RunnerController extends RunnerObserver const BASE_ZOOM_SPEED: float = 0.125 const MOUSE_WHEEL_BIAS: float = 8.0 const MAX_VERTICAL_ALIGNMENT: float = 0.75 const LOOK_IMPETUS_LERP_WEIGHT: float = 0.125 var control_enabled: bool var look_impetus := Vector3.ZERO var look_impetus_target := Vector3.ZERO func _ready() -> void: super() control_enabled = true func switch_runner(whom: Runner, distance: float = DEFAULT_DISTANCE) -> void: if runner: runner.remove_from_group(&'player') super(whom, distance) if runner: runner.add_to_group(&'player') func _would_be_responsive_if_current() -> bool: return runner and control_enabled and not target_override func _is_responsive() -> bool: return current and _would_be_responsive_if_current() func _unhandled_input(event: InputEvent) -> void: if _is_responsive(): var iemm := event as InputEventMouseMotion var iemb := event as InputEventMouseButton if iemm and Options.mouselook: var dy: float = iemm.relative.y if Options.invert_mouselook: dy = -dy var align: float = maintain_direction.dot(up_normal) if ( (dy > 0.0 and align < -MAX_VERTICAL_ALIGNMENT) or (dy < 0.0 and align > MAX_VERTICAL_ALIGNMENT/2.0) ): dy = 0.0 var dx: float = iemm.relative.x look_impetus_target += ( basis.y*dy - basis.x*dx ).limit_length()*Options.mouse_sensitivity*( maintain_distance/FX.DEFAULT_CAMERA_DISTANCE ) if iemb and Options.mouselook: match iemb.button_index: MOUSE_BUTTON_WHEEL_UP: maintain_distance = max( maintain_distance - ( BASE_ZOOM_SPEED * MOUSE_WHEEL_BIAS * Options.mouse_wheel_sensitivity )*( -1.0 if Options.invert_mouse_wheel else 1.0 ), 0.0 ) MOUSE_BUTTON_WHEEL_DOWN: maintain_distance = max( maintain_distance + ( BASE_ZOOM_SPEED * MOUSE_WHEEL_BIAS * Options.mouse_wheel_sensitivity )*( -1.0 if Options.invert_mouse_wheel else 1.0 ), 0.0 ) _: pass if event.is_action_released(&'pause'): UI.Call(load("res://ui/OptionsMenu/OptionsMenu.tscn")) get_viewport().set_input_as_handled() elif runner.state != &'knockback' and runner.state != &'taunt': if event.is_action_pressed(&'jump'): runner.jump() get_viewport().set_input_as_handled() elif event.is_action_released(&'jump'): runner.cancel_jump() get_viewport().set_input_as_handled() elif event.is_action_pressed(&'ability'): runner.do_ability() get_viewport().set_input_as_handled() func _accept_analog_input() -> void: if _is_responsive(): var dy: float = Input.get_axis(&'look_down', &'look_up') if Options.invert_right_stick: dy = -dy var align: float = maintain_direction.dot(up_normal) if ( (dy > 0.0 and align < -MAX_VERTICAL_ALIGNMENT) or (dy < 0.0 and align > MAX_VERTICAL_ALIGNMENT/2.0) ): dy = 0.0 var dx: float = Input.get_axis(&'look_left', &'look_right') look_impetus_target += ( basis.y*dy - basis.x*dx ).limit_length()*Options.right_stick_sensitivity*( maintain_distance/FX.DEFAULT_CAMERA_DISTANCE ) look_impetus = look_impetus.lerp( look_impetus_target, LOOK_IMPETUS_LERP_WEIGHT ) _try_go(global_position + look_impetus) if Input.is_action_pressed(&'zoom_in'): maintain_distance = max(maintain_distance - BASE_ZOOM_SPEED, 0.0) if Input.is_action_pressed(&'zoom_out'): maintain_distance += BASE_ZOOM_SPEED if runner.state != &'knockback' and runner.state != &'taunt': var raw_impetus := Vector3( Input.get_axis(&'move_left', &'move_right'), 0.0, Input.get_axis(&'move_forward', &'move_back') ).limit_length() if not raw_impetus.is_zero_approx(): runner.impetus = ( (basis*raw_impetus).slide(runner.up_normal).normalized() )*raw_impetus.length() look_impetus_target = Vector3.ZERO func _update_mouselook_requested() -> void: if current: Options.mouselook_requested = _would_be_responsive_if_current() func _physics_process(delta: float) -> void: _update_mouselook_requested() _accept_analog_input() super(delta)