MagicNStuff/game/src/gameplay/characters/player/player_character.gd

173 lines
5.5 KiB
GDScript

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
)