class_name PlayerCharacter extends Character3D const ACTION_RUN: StringName = &"run" const ACTION_JUMP: StringName = &"jump" const ACTION_CROUCH: StringName = &"crouch" const ACTION_MOVE_LEFT: StringName = &"move_left" const ACTION_MOVE_RIGHT: StringName = &"move_right" const ACTION_MOVE_FORWARD: StringName = &"move_forward" const ACTION_MOVE_BACKWARD: StringName = &"move_backward" const ACTION_INTERACT: StringName = &"interact" static var player: PlayerCharacter @export var flashlight_manager: FlashlightManager @export_group("Crouching") @export_subgroup("Speed") @export var crouch_walk_speed_multiplier: float = 0.4 @export var crouch_speed: float = 7.25 @export var standup_speed: float = 5.3 @export var standup_speed_affection: float = 0.5 @export var standup_speed_affection_speed_multiplier: float = 0.25 @export_subgroup("Height") @export var standing_collision_height: float = 1.8 @export var standing_camera_height: float = 1.6 @export var crouch_collision_height: float = 0.9 @export var crouch_camera_height: float = 0.81 var has_control: bool = true var can_run: bool = true var is_crouching: bool = false var crouch_speed_affection: float = 0.0 @onready var head: PlayerHead = %Head @onready var collision_shape: CollisionShape3D = %CylinderCollider @onready var standup_checker: Area3D = %StandupChecker @onready var interaction_ray: InteractionRay = %InteractionRay @onready var flashlight: Flashlight = %Flashlight func _init() -> void: player = self func _ready() -> void: head.rotation.y = global_rotation.y global_rotation.y = 0.0 flashlight.manager = flashlight_manager func _physics_process(delta: float) -> void: #var lagspike: bool = randf() < 0.005 #if lagspike: #var i: int = 0 #var max_count: int = randi_range(4687488, 23437488) #while i < max_count: #i += 1 #var i: int = 0 #var max_count: int = randi_range(65104*1.5, 325520*1.5) #while i < max_count: #i += 1 # Add the gravity. if not is_on_floor(): apply_gravity(delta) elif not was_on_floor: if previous_velocity.y <= -2.0: head.camera.shake(0.025, 0.175) if not has_control: move(Vector3.ZERO, delta) move_and_stair_step() return # Handle jump. if Input.is_action_just_pressed(ACTION_JUMP) and is_on_floor() and not is_crouching: jump() handle_crouching(delta) var input_dir: Vector3 = get_input_direction() movement_direction = input_dir.normalized() # Rotate the movement direction based on the cameras orientation. movement_direction = movement_direction.rotated(Vector3.UP, head.global_rotation.y) speed = get_desired_speed() * input_dir.length() speed *= crouch_walk_speed_multiplier if is_crouching else 1.0 if not is_crouching: speed *= remap(crouch_speed_affection, 0.0, 1.0, 1.0, standup_speed_affection) move(movement_direction, delta) move_and_stair_step() if is_on_floor() and not was_on_floor: landed.emit(previous_velocity) SPrint.print_msgf( "Player on floor: %s (was on floor: %s)\nPlayer Horizontal-Velocity: %s\nPlayer Vertical-Velocity: %s" % [is_on_floor(), was_on_floor, (velocity * Utils.VEC3_HOR).length(), velocity.y], true, ) func align(with_node: Node3D, reset_velocity: bool = true) -> void: assert(is_instance_valid(with_node), "with_node is invalid.") apply_orientation(with_node.global_transform) if reset_velocity: velocity = Vector3.ZERO func apply_orientation(orientation_transform: Transform3D) -> void: head.global_basis = orientation_transform.basis global_position = orientation_transform.origin func get_orientation() -> Transform3D: return Transform3D(head.global_basis, global_position) func apply_gravity(delta: float) -> void: super(delta) func get_input_direction() -> Vector3: if not InputManager.is_window_focused(): return Vector3.ZERO var input_dir := Input.get_vector( ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT, ACTION_MOVE_FORWARD, ACTION_MOVE_BACKWARD ) var direction: Vector3 = (transform.basis * Vector3(input_dir.x, 1, input_dir.y)) #.normalized() direction.y = 0.0 return direction func get_desired_speed() -> float: if Input.is_action_pressed(ACTION_RUN) and can_run and not is_crouching: return movement.running_speed if is_on_floor() else movement.air_running_speed return movement.walking_speed if is_on_floor() else movement.air_walking_speed func handle_crouching(delta: float) -> void: var should_crouch: bool = Input.is_action_pressed(ACTION_CROUCH) and (is_on_floor() or is_crouching) var would_ceiling_collide: bool = standup_checker.has_overlapping_bodies() is_crouching = should_crouch or would_ceiling_collide var target_collision_height: float = crouch_collision_height if is_crouching else standing_collision_height collision_shape.shape.height = target_collision_height #lerp(collision_shape.shape.height, target_collision_height, crouching_speed * delta * 5.0) collision_shape.position.y = collision_shape.shape.height / 2.0 var crouching_speed: float = crouch_speed if is_crouching else standup_speed var target_camera_height: float = crouch_camera_height if is_crouching else standing_camera_height var weight: float = 1.0 - pow(0.5, delta * crouching_speed) head.position.y = lerp(head.position.y, target_camera_height, weight) var _speed: float = standup_speed_affection_speed_multiplier if not is_crouching else 1.0 crouch_speed_affection = lerp(crouch_speed_affection, float(is_crouching), weight * _speed) if crouch_speed_affection <= 0.05: crouch_speed_affection = 0.0 SPrint.print_msgf( "Crouching Speed Affection: %s\nIs Crouching: %s" % [crouch_speed_affection, is_crouching], true )