class_name Player extends Unit const PITCH_MIN_DEG := -90.0 const PITCH_MAX_DEG := 30.0 const JOY_DEADZONE := 0.10 @export var mouse_sensitivity: float = 0.006 @export var rotation_speed: float = 24.0 @export var joypad_sensitivity: float = 2.0 @export var spring_arm: SpringArm3D signal gold_changed(current_gold: int) signal player_loaded() var _gold: int = 0 var gold: int: get: return _gold set(value): if _gold == value: return _gold = value gold_changed.emit(_gold) func _ready() -> void: health = maximum_health stamina = maximum_stamina player_loaded.emit() func _physics_process(delta: float) -> void: # Gravity velocity.y += -gravity * delta # Movementw get_move_input(delta) move_and_slide() # Smooth face direction to camera if velocity.length() > 1.0: model.rotation.y = lerp_angle(model.rotation.y, spring_arm.rotation.y, rotation_speed * delta) # Joypad camera var axis_x := Input.get_joy_axis(0, JOY_AXIS_RIGHT_X) var axis_y := Input.get_joy_axis(0, JOY_AXIS_RIGHT_Y) if absf(axis_x) > JOY_DEADZONE or absf(axis_y) > JOY_DEADZONE: move_camera_joypad(axis_x, axis_y, delta) func rotate_camera(pitch_delta: float, yaw_delta: float) -> void: # Apply deltas and clamp pitch; yaw is unbounded spring_arm.rotation.x += pitch_delta spring_arm.rotation_degrees.x = clampf(spring_arm.rotation_degrees.x, PITCH_MIN_DEG, PITCH_MAX_DEG) spring_arm.rotation.y += yaw_delta func move_camera_joypad(x: float, y: float, delta: float) -> void: # Scale by sensitivity and frame time; invert to match mouse feel var pitch_delta := -y * joypad_sensitivity * delta var yaw_delta := -x * joypad_sensitivity * delta rotate_camera(pitch_delta, yaw_delta) func player_game_over(): GameManager.game_over() queue_free() func _input(event: InputEvent) -> void: # Move camera via mouse if event is InputEventMouseMotion: var motion := event as InputEventMouseMotion var pitch_delta := -motion.relative.y * mouse_sensitivity var yaw_delta := -motion.relative.x * mouse_sensitivity rotate_camera(pitch_delta, yaw_delta) var on_floor := is_on_floor() # Jump if on_floor and Input.is_action_just_pressed("jump"): if enough_stamina_available(jump_cost): use_stamina(jump_cost) velocity.y = jump_speed state = States.jumping anim_tree.set("parameters/conditions/grounded", false) # We just hit the floor after being in the air if on_floor and not last_floor: if state != States.idle: state = States.idle anim_tree.set("parameters/conditions/grounded", true) # We're in the air, but we didn't jump if not on_floor and state != States.jumping: anim_state.travel("Jump_Idle") anim_tree.set("parameters/conditions/grounded", false) anim_tree.set("parameters/conditions/jumping", state == States.jumping) last_floor = on_floor # Menu if event.is_action_pressed("Menue"): get_tree().quit() func get_move_input(delta: float) -> void: var vy := velocity.y velocity.y = 0.0 var input_vector := Input.get_vector("left", "right", "forward", "back") var direction := Vector3(input_vector.x, 0.0, input_vector.y).rotated(Vector3.UP, spring_arm.rotation.y) if state == States.blocking: direction = Vector3.ZERO # TODO: Walk while blocking velocity = velocity.lerp(direction * speed, acceleration * delta) var v_local := velocity * model.transform.basis anim_tree.set("parameters/IWR/blend_position", Vector2(v_local.x, -v_local.z) / speed) velocity.y = vy