extends CharacterBody3D class_name PlayerCharacter var aceleration: float = 4.32 #Current aceleration var maxSpeed: float = 6.2 #Maximum Speed var sprintSpeed: float = 3.34 #Additional Speed gained while Sprinting var slowdownSpeedLimit: float = 0.8 #How fast the player can go when slowed var gear: String = "neutral" #Weather the player is going forward, backward or standing still var isMoving: bool = false var slowed: bool = false var momentum: float = 0 var newMoveDirection: Vector3 = Vector3(0,0,0) var moveDirection: Vector3 = Vector3(0,0,0) var deceleration: float = 5.56 var turnSpeed: float = PI/2 #Speed at wich body turn sideways var handling: float = 0.67 var facingDirection = Vector2(0,1) #Where the legs are facing var sprinting = false #While Player is sprinting var sprintEnergy: float = 100 #Energy used for Sprinting var sprintEnergyInitialUse: float = 4.5 #How much % sprint energy is used immediatly when initiating a sprint var sprintEnergyUse: float = 17.5 #How much % energy is used per 1 sec of sprint var sprintEnergyGain: float = 30 #How much % energy is gained per 1 sec of recovery var maxDriftAngle = 0.12 #Quaternion(0,0,0.06,0.998).normalized() #How much the playesr turns to the side while drifting (radians) var rightLeftLean: float = 0.0 #How much the player leans right or left from -1 to 1 with negative numbers being left and 0 being no lean var currentControlState: int = 0 enum controls {DEFAULT,STANDARD_MINIGAME} var health: float = 100.0 var invoulnerable: bool = false var alive: bool = true @onready var camera: Camera3D = $CameraPivot/Camera3D @onready var cameraPivot: Marker3D = $CameraPivot @onready var flashlight: SpotLight3D = $CameraPivot/Camera3D/flashlight @onready var backlightR := $pivot/BodyPivot/body/BacklightR @onready var backlightL := $pivot/BodyPivot/body/BacklightL @onready var pivot: = $pivot @onready var headPivot: = $pivot/HeadBinoculars/HeadPivot @onready var headBinoculars: = $pivot/HeadBinoculars @onready var legs := $pivot/BodyPivot/CaterpillarLegs @onready var body: = $pivot/BodyPivot/body @onready var hud = $/root/Main/Hud @onready var interactCross := $/root/Main/Hud/InteractCross @onready var sprintBar := $/root/Main/Hud/TextureProgressBar @onready var grabRaycast: = $CameraPivot/Camera3D/GrabDetector @onready var grabPivot: = $pivot/BodyPivot/body/GrabPivot @onready var grabBox: = $pivot/BodyPivot/body/GrabBox var grabbedObject: GrabableObject @onready var interactRaycast = $CameraPivot/Camera3D/InteractDetector @onready var VoiceChat = $VoiceChat @onready var bodyPivot: Node3D = $pivot/BodyPivot var mouseSensetivity := 0.1 #How much mouse movement affects ingame camera movement var spectatorScene: PackedScene = preload("res://actors/Player/Spectator.tscn") var spectatorParent: Node3D var mapLogic: MapLogic #Camera Shake Stuff var traumaReductionRate:float = 0.34 var trauma: float = 0 @export var maxX = 12 @export var maxY = 12 @export var maxZ = 7 @export var shakeIntensety : float = 7.0 var time: float = 0 @export var noise: Noise var noiseSpeed: float = 50 var initialRotation = rotation_degrees as Vector3 # Camera Shake Stuff end func _enter_tree() -> void: set_multiplayer_authority(name.to_int()) if is_multiplayer_authority(): $CameraPivot/Camera3D.make_current() $pivot/HeadBinoculars.hide() $/root/Main/Hud/TextureProgressBar.show() func _ready() -> void: Multiplayer.player_ready.emit(int(name)) Input.mouse_mode = Input.MOUSE_MODE_CAPTURED mapLogic = Multiplayer.currentMapLogic spectatorParent = get_node("/root/Main/Spectators") if mapLogic: mapLogic.onCollision.connect(onCollision) if is_multiplayer_authority(): position.y += 2 return func _process(delta: float) -> void: if not is_multiplayer_authority(): return if not alive: return if invoulnerable and trauma == 0.0: invoulnerable = false if Input.is_action_just_pressed("debug"): ##Debug die() match currentControlState: controls.DEFAULT: regularControlsIdle(delta) controls.STANDARD_MINIGAME: standardMinigameControlsIdle(delta) func _physics_process(delta: float) -> void: if not is_multiplayer_authority(): return if not alive: return match currentControlState: controls.DEFAULT: regularControlsPhysics(delta) controls.STANDARD_MINIGAME: endOfPhysicsCleanup(delta) func standardMinigameControlsIdle(_delta:float): if Input.is_action_just_pressed("moveDown") or Input.is_action_just_pressed("interact"): currentControlState = controls.DEFAULT func regularControlsIdle(_delta:float): #Interacting Logic var InteractCollider: InteractBox = interactRaycast.get_collider() if InteractCollider and Input.is_action_just_pressed("interact"): InteractCollider.playerRef = self InteractCollider.interact() if InteractCollider.type == "minigame": currentControlState = controls.STANDARD_MINIGAME momentum = 0 #Grabbing Logic var GrabCollider: GrabBox = grabRaycast.get_collider() var grabBoxCollider: Array[Area3D] = grabBox.get_overlapping_areas() if(grabBoxCollider and Input.is_action_just_pressed("interact") and !grabbedObject): if grabBoxCollider.has(GrabCollider): #If the player is looking at an object and it is in the grab box grabbedObject = GrabCollider.grab() else: #If the player is not looking at an object, grab the closet one in the grab box var closestObj: GrabBox var distanceToClosestObj: float = 100 for grabObj in grabBoxCollider: if (getDistance(self,grabObj) < distanceToClosestObj): closestObj = grabObj distanceToClosestObj = getDistance(self, grabObj) grabbedObject = closestObj.grab() if InteractCollider or grabBoxCollider: interactCross.show() else: interactCross.hide() if Input.is_action_just_pressed("flashlight"): flashlight.visible = !flashlight.visible if grabbedObject: grabbedObject.global_position = grabPivot.global_position# + grabbedObject.grabPositionPositionOffset grabbedObject.rotation = grabPivot.global_rotation + grabbedObject.grabPositionRotationOffset if grabbedObject.grabBox.heavy: slowed = true if Input.is_action_just_pressed("drop"): grabbedObject.release.rpc() grabbedObject = null slowed = false momentum = 0 if Input.is_action_just_pressed("throw"): grabbedObject.release.rpc() grabbedObject.throw.rpc_id(1,facingDirection.x,facingDirection.y,camera.rotation.x) grabbedObject = null slowed = false momentum = 0 func regularControlsPhysics(delta: float): isMoving = false #Drain energy on sprint start to prevent the player from pressing sprint every other frame to get infinity energy if Input.is_action_just_pressed("Sprint"): sprintEnergy = clamp(sprintEnergy - sprintEnergyInitialUse, -(sprintEnergyInitialUse*2) , 100) # Rest of sprint energy logic if Input.is_action_pressed("Sprint"): sprintEnergy = clamp(sprintEnergy - sprintEnergyUse * delta, 0 , 100) # Drain sprint when holding the button if sprintEnergy > 0: sprinting = true else: sprinting = false else: sprinting = false sprintEnergy = clamp(sprintEnergy + sprintEnergyGain * delta, -(sprintEnergyInitialUse*2) , 100) # Gain sprint when not holding the button if not is_on_floor(): velocity += get_gravity() newMoveDirection = Vector3(0,0,0) # Set moveDirection based on input if Input.is_action_pressed("moveUp") and !Input.is_action_pressed("moveDown"): isMoving = true newMoveDirection += Vector3(facingDirection.x,0,facingDirection.y) if Input.is_action_pressed("moveRight") and !Input.is_action_pressed("moveLeft"): newMoveDirection -= Vector3(facingDirection.x,0,facingDirection.y).rotated(up_direction,PI/2) if Input.is_action_pressed("moveLeft") and !Input.is_action_pressed("moveRight"): newMoveDirection += Vector3(facingDirection.x,0,facingDirection.y).rotated(up_direction,PI/2) if Input.is_action_pressed("moveDown") and !Input.is_action_pressed("moveUp"): isMoving = true newMoveDirection += Vector3(facingDirection.x,0,facingDirection.y) showBacklights() else: hideBacklights() if newMoveDirection != Vector3(0,0,0): moveDirection = clampVectorLength(moveDirection + newMoveDirection*delta,0,handling) endOfPhysicsCleanup(delta) #Momentum stuff if isMoving: if Input.is_action_pressed("moveDown"): momentum = clamp(momentum + -aceleration*2 * delta,-maxSpeed/2,maxSpeed) else: momentum = clamp(momentum + aceleration * delta,-maxSpeed/2,maxSpeed) else: if momentum >= 0: momentum = clamp(momentum - deceleration * delta,-maxSpeed/2,maxSpeed) if momentum < 0: momentum = clamp(momentum + deceleration * delta,-maxSpeed/2,maxSpeed) if sprinting and isMoving and !Input.is_action_pressed("moveDown"): velocity = clampVectorLength(moveDirection.normalized() * (momentum + sprintSpeed),0,maxSpeed+sprintSpeed) else: velocity = clampVectorLength(moveDirection.normalized() * momentum,0,maxSpeed) bodyPivot.rotation.y = rotate_toward(bodyPivot.rotation.y, -atan2(moveDirection.z,moveDirection.x) + PI/2,delta*turnSpeed) #print(facingDirection.length()) move_and_slide() #applies movement func endOfPhysicsCleanup(delta: float): trauma = max(trauma-delta*traumaReductionRate,0) time += delta rotation_degrees.x = initialRotation.x + maxX * getShakeIntensity() * getNoiseFromSeed(343) rotation_degrees.y = initialRotation.y + maxY * getShakeIntensity() * getNoiseFromSeed(123) rotation_degrees.z = initialRotation.z + maxZ * getShakeIntensity() * getNoiseFromSeed(234) #Rotate head based on camera movement headPivot.rotation.y = cameraPivot.rotation.y headPivot.rotation.x = -camera.rotation.x if trauma == 0.0: headPivot.rotation.z = 0.0 headBinoculars.rotation.z = 0.0 sprintBar.value = sprintEnergy func showBacklights() -> void: backlightL.show() backlightR.show() func hideBacklights() -> void: backlightL.hide() backlightR.hide() func useSprintEnergy(delta: float): sprintEnergy = clamp(sprintEnergy - sprintEnergyUse * delta, 0, 100) func setRightLeftLean(direction: int, strength: float, delta: float) -> void: if direction > 0 and rightLeftLean < 0: rightLeftLean = 0 elif direction < 0 and rightLeftLean > 0: rightLeftLean = 0 rightLeftLean = clamp(rightLeftLean + strength * delta * direction,-1,1) func getAngleBetweenVectors(a: Vector3, b: Vector3) -> float: if a.length() == 0 or b. length() == 0: return 0 return acos((a.dot(b)/(a.length()*b.length()))) func getDistance(a: Node3D, b: Node3D) -> float: var distanceVector: Vector3 distanceVector = b.global_position - a.global_position return distanceVector.length() func clampVectorLength(Vector: Vector3, minLength: float, maxLength: float) -> Vector3: #scales Vector up/ down to the max/ min length givin. If the Vector has a length of 0 it will be returned without being scaled. if Vector.length() == 0: return Vector if Vector.length() < minLength: return Vector * minLength / Vector.length() elif Vector.length() > maxLength: return Vector * maxLength / Vector.length() return Vector func onCollision() -> void: trauma = 1 func addTrauma(traumaAmount: float): trauma = clamp(trauma+traumaAmount,0,1) func getShakeIntensity() -> float: return shakeIntensety * trauma * trauma func getNoiseFromSeed(seed_: int) -> float: noise.seed = seed_ return noise.get_noise_1d(time*noiseSpeed) func _input(event: InputEvent) -> void: #Camera movement with mouse if event is InputEventMouseMotion && Input.mouse_mode == 2: camera.rotation.x -= clamp(deg_to_rad(event.relative.y * mouseSensetivity),-180,180) cameraPivot.rotation.y -= deg_to_rad(event.relative.x * mouseSensetivity) facingDirection = facingDirection.rotated(deg_to_rad(event.relative.x * mouseSensetivity)) camera.rotation_degrees.x = clamp(camera.rotation_degrees.x, -70, 70) cameraPivot.rotation.z = 0 func _on_hurt_box_hit_taken(attack: Attack) -> void: if invoulnerable: return trauma = attack.trauma health -= attack.damage if health <= 0.0: die() invoulnerable = true func die() -> void: Multiplayer.alivePlayerDict.erase(int(name)) headBinoculars.show() headPivot.position = Vector3(0,0.382,0.063) headPivot.rotation = Vector3(0,0,0) $"pivot/pivotRightLeg/pivotLeftLeg/body/Grabby Arms_L".rotation.x = deg_to_rad(90) $"pivot/pivotRightLeg/pivotLeftLeg/body/Grabby Arms_R".rotation.x = deg_to_rad(90) var spectator = spectatorScene.instantiate() spectatorParent.add_child(spectator) camera.current = false alive = false