class_name ShotSetup extends Node3D enum Phase { AIM, POWER_ADJUST, CURVE_ADJUST, SHOT, FINISHED, } const PITCH_MIN := deg_to_rad(-60.0) const PITCH_MAX := deg_to_rad(-5.0) const ZOOM_LENGTH := 0.1 const ZOOM_MIN := 1.0 const ZOOM_MAX := 12.0 const ARROW_ACCELERATION := 8.0 const FREE_CAM_RETURN_TIME := 1.0 @export var control_disabled := false var base_speed: float = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed") var x_sensitivity: float = ProjectSettings.get_setting( "game/config/controls/camera/x_axis_sensitivity" ) var x_acceleration: float = ProjectSettings.get_setting( "game/config/controls/camera/x_axis_acceleration" ) var y_sensitivity: float = ProjectSettings.get_setting( "game/config/controls/camera/y_axis_sensitivity" ) var y_acceleration: float = ProjectSettings.get_setting( "game/config/controls/camera/y_axis_acceleration" ) var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camera/invert_pitch") var phase: Phase: set(value): if value != phase: _on_phase_change(value) phase = value var _free_camera: FreeCamera var _returning_free_camera := false @onready var direction: Node3D = %Direction @onready var pitch: Node3D = %Pitch @onready var camera: Camera3D = %Camera @onready var arrow: Node3D = %Arrow @onready var power_bar: ProgressBar = %PowerBar @onready var power_animation: AnimationPlayer = %PowerAnimation @onready var curve_bar: ProgressBar = %CurveBar @onready var curve_animation: AnimationPlayer = %CurveAnimation @onready var camera_distance := camera.position.z: set = _set_camera_distance @onready var _target_rotation := Vector2(pitch.rotation.x, direction.rotation.y) func _set_camera_distance(value: float) -> void: var tween := get_tree().create_tween() tween.tween_property(camera, "position", Vector3(0, 0, value), ZOOM_LENGTH).set_trans( Tween.TRANS_SINE ) camera_distance = value func _unhandled_input(event: InputEvent) -> void: if event is InputEventMouseButton: Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) elif event is InputEventMouseMotion: if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED: camera_motion((event as InputEventMouseMotion).relative) func camera_motion(motion: Vector2) -> void: if not control_disabled and phase == Phase.AIM: # Can only control camera while aiming _target_rotation.y = _target_rotation.y - deg_to_rad(motion.x * x_sensitivity) _target_rotation.x = clampf( _target_rotation.x - deg_to_rad(motion.y * y_sensitivity) * (-1 if invert_pitch else 1), PITCH_MIN, PITCH_MAX ) func take_shot() -> void: print("WHACK!") print("Power: ", power_bar.value) print("Curve: ", curve_bar.value) func _on_phase_change(new_phase: Phase) -> void: match new_phase: Phase.AIM: print("AIM PHASE") power_bar.hide() curve_bar.hide() Phase.POWER_ADJUST: curve_bar.hide() power_bar.show() power_animation.stop() # Reset if needed Phase.CURVE_ADJUST: curve_bar.show() curve_animation.play("fill") Phase.SHOT: power_bar.hide() curve_bar.hide() take_shot() func insert_free_cam() -> void: _free_camera = FreeCamera.create(camera) add_sibling(_free_camera) control_disabled = true func return_free_cam() -> void: # TODO alter shot aim based on free camera selection _free_camera.queue_free() _free_camera = null control_disabled = false _returning_free_camera = false func _process(delta: float) -> void: # Rotation direction.rotation.y = lerp_angle( direction.rotation.y, _target_rotation.y, delta * x_acceleration ) pitch.rotation.x = lerp_angle(pitch.rotation.x, _target_rotation.x, delta * y_acceleration) arrow.rotation.y = lerp_angle(arrow.rotation.y, _target_rotation.y, delta * ARROW_ACCELERATION) # Input Handling if control_disabled: if Input.is_action_just_pressed("camera_cancel"): if is_instance_valid(_free_camera) and not _returning_free_camera: _returning_free_camera = true var tween := get_tree().create_tween() ( tween . tween_property( _free_camera, "global_transform", camera.global_transform, FREE_CAM_RETURN_TIME ) . set_trans(Tween.TRANS_SINE) ) tween.tween_callback(return_free_cam) return match phase: Phase.AIM: # Camera zoom if Input.is_action_just_pressed("shot_zoom_in"): camera_distance = max(camera_distance - 1.0, ZOOM_MIN) if Input.is_action_just_pressed("shot_zoom_out"): camera_distance = min(camera_distance + 1.0, ZOOM_MAX) # Switch to free cam if ( Input.is_action_just_pressed("camera_back") or Input.is_action_just_pressed("camera_forward") or Input.is_action_just_pressed("camera_left") or Input.is_action_just_pressed("camera_right") ): insert_free_cam() # Advance to next phase if Input.is_action_just_pressed("shot_accept"): phase = Phase.POWER_ADJUST Phase.POWER_ADJUST: if Input.is_action_just_pressed("shot_accept"): # TODO set power gauge parameters if needed power_animation.play("fill") if Input.is_action_just_released("shot_accept") and power_bar.value > 0: power_animation.pause() phase = Phase.CURVE_ADJUST Phase.CURVE_ADJUST: if Input.is_action_just_pressed("shot_accept"): curve_animation.pause() phase = Phase.SHOT Phase.SHOT: # DEBUG: reset to AIM phase phase = Phase.AIM