extends CharacterBody3D
# NOTICE all comments for work:
#Critical comments: ALERT,ATTENTION,CAUTION,CRITICAL,DANGER,SECURITY
#Warning comments: BUG,DEPRECATED,FIXME,HACK,TASK,TBD,TODO,WARNING
#Notices: INFO,NOTE,NOTICE,TEST,TESTING
@onready var title = "Prometheus Pre-Alpha"
@onready var neck: Node3D = $Neck
@onready var head: Node3D = $Neck/Head
@onready var eye: Node3D = $Neck/Head/Eye
@onready var camera: Camera3D = $Neck/Head/Eye/Camera
@onready var shoulder: Node3D = $Shoulder
@onready var hand: Node3D = $Shoulder/Hand
@onready var flashlight: SpotLight3D = $Shoulder/Hand/Flashlight
@onready var collision_shape: CollisionShape3D = $CollisionShape
@onready var top_cast: ShapeCast3D = $TopCast
@onready var interaction_cast: RayCast3D = $"Neck/Head/Eye/Camera/Interaction cast"
@onready var fps: Label = $"Neck/Head/Eye/Camera/2D overlays/Control/FPS"
@onready var animation_player: AnimationPlayer = $"Neck/Head/Eye/Camera/2D
overlays/CanvasLayer/AnimationPlayer"
@export_category("Movement")
@export var walkSpeed = 5.0
@export var sprintSpeed = 7.0
@export var staminaMax = 6.0
@export var staminaPenalty = 10.0
@export var crouchspeed = 3.0
@export var playerAcceleration = 8.0
@export var airFriction = 1.0
@export var jumpForce = 7.0
@export_category("Visuals")
@export var crouchHeight = 1.0
@export var crouchTransition = 8.0
@export_exp_easing("attenuation") var lerpSpd = 10.0
@export var cameraBaseFOV = 60.0
@export var mouseSens = 0.4
@export var camAcceleration = 10.0
@export_group("Head Bob")
@export var head_bobbing_crouch_speed = 9.0
@export var head_bobbing_walk_speed = 12.0
@export var head_bobbing_sprint_speed = 15.0
@export var head_bobbing_crouch_intensity = 0.1
@export var head_bobbing_sprint_intensity = 0.3
@export var head_bobbing_walk_intensity = 0.2
@export_group("flashlight")
@export_subgroup("Inside")
@export var flashlightInnerEnergy = 2.0
@export var flashlightInnerFogEnergy = 16.0
@export var flashlightInnerRange = 20.0
@export var flashlightInnerAngle = 15.0
@export_subgroup("Outside")
@export var flashlightOuterEnergy = 0.5
@export var flashlightOuterFogEnergy = 8.0
@export var flashlightOuterRange = 20.0
@export var flashlightOuterAngle = 30.0
@export var inventory : Array
@export var inventorySize : int = 5
enum MovementState {
Crouching,
Sprinting,
Walking
}
var i = 0
var isReady : bool
var standHeight : float
var CurrentState: MovementState = [Link]
var local_velocity
var gravity : Vector3
var headYaxis : float
var camXaxis : float
var currentSpeed : float
var stamina : float
var head_bobbing_vector = [Link]
var head_bobbing_rotation = 0.0
var head_bobbing_index = 0.0
var head_bobbing_current_intensity = 0.0
var direction := [Link]
const maxStepHeight = 0.5
var snappedToStairLastFrame := false
var lastFrameWasOnFloor = -INF
func _ready() -> void:
standHeight = collision_shape.[Link]
stamina = staminaMax
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _input(event: InputEvent) -> void:
handleCameraMovementEvents(event)
func isSurfaceTooSteep(normal : Vector3) -> bool:
return normal.angle_to([Link]) > self.floor_max_angle
func runBodyTestMotion(from : Transform3D, motion : Vector3, result = null) ->
bool:
if not result : result = [Link]()
var params = [Link]()
[Link] = from
[Link] = motion
return PhysicsServer3D.body_test_motion(self.get_rid(), params, result)
func snapUpStairsCheck(delta) -> bool:
if not is_on_floor() and not snappedToStairLastFrame: return false
var expectedMoveMotion = [Link] * Vector3(1,0,1) * delta
var stepPosWithClearance =
self.global_transform.translated(expectedMoveMotion + Vector3(0, maxStepHeight * 2,
0))
var downCheckResult = [Link]()
if (runBodyTestMotion(stepPosWithClearance, Vector3(0,-maxStepHeight * 2,0),
downCheckResult)
and (downCheckResult.get_collider().is_class("StaticBody3D") or
downCheckResult.get_collider().is_class("CSGShape3D"))):
var stepHeight = (([Link] +
downCheckResult.get_travel()) - self.global_position).y
if stepHeight > maxStepHeight or stepHeight <= 0.01 or
(downCheckResult.get_collision_point() - self.global_position).y > maxStepHeight:
return false
%StairAheadRayCast.global_position =
downCheckResult.get_collision_point() + Vector3(0, maxStepHeight, 0) +
[Link]() * 0.1
%StairAheadRayCast.force_raycast_update()
if %StairAheadRayCast.is_colliding() and not
isSurfaceTooSteep(%StairAheadRayCast.get_collision_normal()):
self.global_position = [Link] +
downCheckResult.get_travel()
apply_floor_snap()
snappedToStairLastFrame = true
saveCameraGlobalPosForsmoothing()
return true
return false
func snapDownToStairCheck() -> void:
var didSnap = false
var floorBelow : bool = %StairBelowRayCast.is_colliding() and not
isSurfaceTooSteep(%StairBelowRayCast.get_collision_normal())
var wasOnFloorLastFrame = Engine.get_physics_frames() - lastFrameWasOnFloor
== 1
if not is_on_floor() and velocity.y <= 0 and (wasOnFloorLastFrame or
snappedToStairLastFrame) and floorBelow:
var bodyTestResult = [Link]()
if runBodyTestMotion(self.global_transform, Vector3(0, -maxStepHeight,
0), bodyTestResult):
var translateY = bodyTestResult.get_travel().y
[Link].y += translateY
apply_floor_snap()
saveCameraGlobalPosForsmoothing()
didSnap = true
snappedToStairLastFrame = didSnap
var savedCameraGlobalPos = null
func saveCameraGlobalPosForsmoothing():
if savedCameraGlobalPos == null:
savedCameraGlobalPos = %Eye.global_position
func slideEyeBackToOrigin(delta):
if savedCameraGlobalPos == null: return
%Eye.global_position.y = savedCameraGlobalPos.y
%[Link].y = clampf(%[Link].y, -0.7, 0.7)
var moveAmount = max([Link]() * delta, walkSpeed/2 * delta)
%[Link].y = move_toward(%[Link].y, 0.0, moveAmount)
savedCameraGlobalPos = %Eye.global_position
if %[Link].y == 0:
savedCameraGlobalPos = null
func _physics_process(delta: float) -> void:
handleMouseModes()
displayFPS()
[Link](inventorySize)
#local velocity calculation
local_velocity = Vector3([Link]([Link].x),
[Link]([Link].y), [Link]([Link].z))
if is_on_floor:
lastFrameWasOnFloor = Engine.get_physics_frames()
#get gravity
gravity = get_gravity()
handleInteraction()
handleCameraMovement(delta)
handlePlayerInput(delta)
handleCurrentPlayerState(delta)
dynamicHeadTilt(local_velocity, delta)
dynamicFOV(local_velocity, delta)
movement(delta)
headBob(delta)
if not snapUpStairsCheck(delta):
move_and_slide()
snapDownToStairCheck()
slideEyeBackToOrigin(delta)
func handleInventory():
pass # TODO
func displayFPS():
[Link] = "fps: " + str(Engine.get_frames_per_second())
func crouch(delta : float, reverse = false):
var targetHeight : float = crouchHeight if not reverse else standHeight
collision_shape.[Link] = lerp(collision_shape.[Link],
targetHeight, delta * crouchTransition)
collision_shape.position.y = lerp(collision_shape.position.y, targetHeight *
0.5, delta * crouchTransition)
[Link].y = lerp([Link].y, targetHeight - 1, delta *
crouchTransition)
[Link].y = lerp([Link].y, targetHeight - 0.5, delta *
crouchTransition)
func dynamicHeadTilt(local_velocity : Vector3, delta : float):
#head tilt on strafe
if local_velocity.x != 0 && is_on_floor():#2.85
[Link].z = clamp(lerp([Link].z, deg_to_rad(-
local_velocity.x/2.85), delta * 15.0), deg_to_rad(-10), deg_to_rad(10))
else:
[Link].z = lerp([Link].z, 0.0, delta * 5.0)
#head tilt on jump and fall
if local_velocity.y != 0 && !snappedToStairLastFrame: #1.3
[Link].x = clamp(lerp([Link].x, deg_to_rad(-
local_velocity.y/1.3), delta * lerpSpd), deg_to_rad(-30), deg_to_rad(30))
else:
[Link].x = lerp([Link].x, 0.0, delta * lerpSpd)
func dynamicFOV(local_velocity : Vector3, delta : float):
#dynamic fov
if local_velocity.z < 0 && direction != [Link]: #1.2
[Link] = lerp([Link], cameraBaseFOV - local_velocity.z *
1.2,delta * 5.0)
else:
[Link] = lerp([Link], cameraBaseFOV, delta * 5.0)
func movement(delta : float):
#input management
direction = (Input.get_axis("left","right") * [Link].x +
Input.get_axis("up", "down") * [Link].z).normalized()
#movement
if is_on_floor() or snappedToStairLastFrame == true:
velocity = [Link](direction * currentSpeed + velocity.y *
[Link], playerAcceleration * delta)
else:
velocity = [Link](direction * currentSpeed + velocity.y *
[Link], airFriction * delta)
func headBob(delta : float):
if (is_on_floor() or snappedToStairLastFrame and is_on_floor()) &&
direction != [Link]:
head_bobbing_vector.y = sin(head_bobbing_index)
head_bobbing_vector.x = sin(head_bobbing_index/2)+0.5
head_bobbing_rotation = sin(head_bobbing_index/2)
[Link].z = lerp([Link].z,
deg_to_rad(head_bobbing_rotation + head_bobbing_current_intensity), delta *
lerpSpd)
[Link].y = lerp([Link].y,
head_bobbing_vector.y*(head_bobbing_current_intensity/2.0), delta * lerpSpd)
[Link].x = lerp([Link].x,
head_bobbing_vector.x*head_bobbing_current_intensity, delta * lerpSpd)
else:
[Link].z = lerp([Link].z, 0.0, delta * lerpSpd)
[Link].y = lerp([Link].y, 0.0, delta * lerpSpd)
[Link].x = lerp([Link].x, 0.0, delta * lerpSpd)
func handlePlayerInput(delta : float):
#Movement State Management
if Input.is_action_pressed("jump") and (is_on_floor()):
velocity.y = jumpForce
else:
velocity += gravity * delta
if Input.is_action_pressed("crouch"):
CurrentState = [Link]
elif not top_cast.is_colliding():
if Input.is_action_pressed("sprint") && isReady:
CurrentState = [Link]
else:
CurrentState = [Link]
func handleCurrentPlayerState(delta : float):
#State Machine For Movement
match CurrentState:
[Link]:
currentSpeed = lerp(currentSpeed, crouchspeed, delta *
playerAcceleration)
head_bobbing_current_intensity = head_bobbing_crouch_intensity
head_bobbing_index += head_bobbing_crouch_speed*delta
crouch(delta)
[Link]:
currentSpeed = lerp(currentSpeed, sprintSpeed, delta *
playerAcceleration)
head_bobbing_current_intensity = head_bobbing_sprint_intensity
head_bobbing_index += head_bobbing_sprint_speed*delta
crouch(delta, true)
[Link]:
currentSpeed = lerp(currentSpeed, walkSpeed, delta *
playerAcceleration)
head_bobbing_current_intensity = head_bobbing_walk_intensity
head_bobbing_index += head_bobbing_walk_speed*delta
crouch(delta, true)
func handleCameraMovementEvents(event):
if event is InputEventMouseMotion:
headYaxis += [Link].x * mouseSens
camXaxis += [Link].y * mouseSens
camXaxis = clamp(camXaxis, -85, 85)
func handleMouseModes():
if Input.is_action_just_pressed("show mouse"):
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
Engine.time_scale = 0.0
elif Input.mouse_mode == Input.MOUSE_MODE_VISIBLE &&
Input.is_action_just_pressed("hide mouse"):
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func handleCameraMovement(delta : float):
#Camera Movement
[Link].y = lerp([Link].y, -deg_to_rad(headYaxis),
camAcceleration * delta)
[Link].x = lerp([Link].x, -deg_to_rad(camXaxis),
camAcceleration * delta)
[Link].x = clamp([Link].x, deg_to_rad(-80), deg_to_rad(80))
#flashlight movement
[Link].y = -deg_to_rad(headYaxis)
[Link].x = -deg_to_rad(camXaxis)
var Obj : Node
var interacting = false
func handleInteraction():
if interaction_cast.is_colliding() &&
Input.is_action_just_pressed("interact"):
var object = interaction_cast.get_collider()
Obj = object
print(object)
print(interacting)
if object is interactable:
if object is Lever:
[Link](self)
interacting = true
elif object is button:
[Link](self)
interacting = false
elif object is Pickupable:
[Link](self)
interacting = false
else:
interacting = false
elif Input.is_action_just_released("interact"):
interacting = false
var pressed : bool