extends CharacterBody3D
var hyperGossip : HyperGossip
# TODO : These probably should not be in player_core, though I am uncertain yet if they belong elsewhere
const EVENT_PLAYER_SNAPSHOT = 'player_snapshot'
const EVENT_PLAYER_WANTSTOJUMP = 'player_wantstojump'
const EVENT_PLAYER_DIRECTION = 'player_direction'
const EVENT_PLAYER_RESPAWNPLAYER = 'player_respawnplayer'
const EVENT_PLAYER_SHOOT_GRAPPLINGHOOK = 'player_shoot_grapplinghook'
const EVENT_PLAYER_RELEASE_GRAPPLINGHOOK = 'player_release_grapplinghook'
const EVENT_PLAYER_TOGGLE_LIGHT = 'player_toggle_light'
@export var mouseSensitivity : float = 0.3
@export var movementSpeed : float = 14
@export var fallAcceleration : float = 27
@export var movementAcceleration : float = 4
@export var canJumpHeight : float = 10
@export var jumpHeight : float = 15
@export var turn_velocity : float = 15
@export var cameraLerpAmount : float = 40
@export var currentSpeed : float = 0
@onready var meshNode : Node3D = $Model
@onready var meshSkeletonNode : Skeleton3D = $Model/Armature/Skeleton3D
@onready var meshSkeletalIKNode : SkeletonIK3D = $Model/Armature/Skeleton3D/SkeletonIK3D
@onready var meshSkeletonHiddenHand : MeshInstance3D = $Model/Armature/Skeleton3D/player001
@onready var meshHandBone : int = meshSkeletonNode.find_bone("DEF-hand.R")
@onready var meshHandBonePos : Transform3D = meshSkeletonNode.get_bone_pose(meshHandBone)
@onready var clippedCamera : Node3D = $CameraHead/CameraPivot/ClippedCamera
@onready var clippedCameraHead : Node3D = $CameraHead
@onready var clippedCameraPivot : Node3D = $CameraHead/CameraPivot
@onready var meshCollisionShape : CollisionShape3D = $CollisionShape
@onready var grappleHookCast : RayCast3D = $CameraHead/CameraPivot/GrappleHookCast
@onready var grappleVisualPoint_1 = $GrapplingHook/GrappleVisualPoint_1
# onready var grappleVisualPoint_2 = $GrapplingHook/GrappleVisualPoint_2
@onready var grappleVisualPoint = grappleVisualPoint_1
@onready var grappleLineHelper : Node3D = $Model/GrappleLineHelper
@onready var grappleVisualLine : CSGCylinder3D = $Model/GrappleLineHelper/GrappleVisualLine_1
# onready var grappleVisualLine : = $Model/GrappleLineHelper/GrappleVisualLine_2
@onready var grappleIKTarget : Position3D = $GrapplingHook/IKTarget
@onready var grapplingHandOriginalScale : Vector3 = Vector3.ZERO
@onready var omniLight : OmniLight3D = $OmniLight
var grapplingHook_GrapplePosition : Vector3 = Vector3.ZERO
var grapplingHook_IsHooked : bool = false
var playerWantsToShootGrapplingHook : bool = false
var playerWantsToReleaseGrapplingHook : bool = false
# TODO : Make Debug UI dynamic and not hardcoded in the core Hyper scripts.
#onready var hyperdebugui : Control = $HyperGodotDebugUI
#onready var hyperdebugui_gateway_startstop_button : Button = $HyperGodotDebugUI/HypercoreDebugPanel/HypercoreDebugContainer/GatewayStartStopButton
#onready var hyperdebugui_gossipid_list : ItemList = get_tree().get_current_scene().get_node("HyperGodot").get_node("HyperGateway")
@onready var currentMap = get_tree().get_current_scene().get_node("Maps").get_child(0)
var originalOrigin : Vector3 = Vector3.ZERO
var currentSpawnLocation : Vector3 = Vector3.ZERO
var currentDirection : Vector3 = Vector3.ZERO
var moveNetworkUpdateAllowed : bool = true
var playerWantsToRespawn : bool = false
var jumpFloorDirection : Vector3 = Vector3(0,1,0)
var playerWantsToJump : bool = false
var playerWantsNewWorldEnvironment : bool = true
var playerWantsToToggleLight : bool = false
var f_input : float
var h_input : float
var snapVector : float
var collisions : Dictionary = {}
# TODO : Godot 4 cannot parse this particle format
# var Particles_Land = preload("res://game/player/particles.tscn")
func _ready():
# Sanitize Velocity
velocity = Vector3.ZERO
# Backup Origin
originalOrigin = self.position
# Spawn into Map
currentSpawnLocation = getSpawnLocationForMapName(currentMap.map_name)
playerWantsToRespawn = true
# Get HyperGossip
hyperGossip = get_tree().get_current_scene().get_node("HyperGodot").get_node("HyperGossip")
func snapShotUpdate(_position : Vector3, _meshDirection : Vector3, _lookingDirection : Vector3):
self.position = _position
self.meshNode.rotation = _meshDirection
self.currentDirection = _lookingDirection
func directionUpdate(_direction : Vector3):
self.currentDirection = _direction
func getSpawnLocationForMapName(mapName : String) -> Vector3:
# Get Map Node
var mapNodes = get_tree().get_current_scene().get_node("Maps").find_children(mapName, "", true, false)
if(mapNodes == null or mapNodes.size() == 0):
return get_tree().get_current_scene().get_node("Maps").get_node("map_test").getSpawnLocation()
else:
return mapNodes[0].getSpawnLocation()
func _process(_delta):
if(currentDirection != Vector3.ZERO):
meshNode.rotation.y = lerp_angle(meshNode.rotation.y, atan2(-currentDirection.x, -currentDirection.z), turn_velocity * _delta)
func grapplingHook_Process():
grapplingHook_CheckActivation()
var length : float = grapplingHook_UpdatePlayerVelocityAndReturnHookLength()
grapplingHook_UpdateVisualLine(length)
# TODO : BIG Hack to prevent running on remote players
var _name = self.name
if(self.name == "PlayerCoreLocal"):
grapplingHook_UpdateVisualPoint()
func grapplingHook_CheckActivation():
# Activate hook
if(playerWantsToShootGrapplingHook):
playerWantsToShootGrapplingHook = false
playerWantsToReleaseGrapplingHook = false
grapplingHook_IsHooked = true
grappleVisualLine.show()
$Model/Sound_Shoot_GrapplingHook_2.play()
grappleIKTarget.position = grapplingHook_GrapplePosition
grapplingHook_DisappearHand()
elif(playerWantsToReleaseGrapplingHook):
# Stop grappling
if(grapplingHook_IsHooked):
grapplingHook_IsHooked = false
playerWantsToReleaseGrapplingHook = false
grappleVisualLine.hide()
$Model/Sound_Release_GrapplingHook.play()
grapplingHook_ReappearHand()
func grapplingHook_UpdateVisualPoint():
if grappleHookCast.is_colliding():
grappleVisualPoint.visible = true
grappleVisualPoint.position = grappleHookCast.get_collision_point()
else:
grappleVisualPoint.visible = false
func grapplingHook_UpdateVisualLine(length : float):
if(grapplingHook_IsHooked):
grappleLineHelper.look_at(grapplingHook_GrapplePosition, Vector3.UP)
grappleVisualLine.height = length
grappleVisualLine.position.z = length / -1.80
func grapplingHook_UpdatePlayerVelocityAndReturnHookLength() -> float:
var grapple_speed : float = 0.5
var rest_length : float = 1
var max_grapple_speed : float = 2.75
var player2hook := grapplingHook_GrapplePosition - position # vector from player to hook
var length := player2hook.length()
if(grapplingHook_IsHooked):
# if we more than 4 away from line, don't dampen speed as much
if(length > 4):
velocity *= .999
# Otherwise dampen speed more
else:
velocity *= .9
# Hook's law equation
var force := grapple_speed * (length - rest_length)
# Clamp force to be less than max_grapple_speed
if abs(force) > max_grapple_speed:
force = max_grapple_speed
# Preserve direction, but scale by force
velocity += player2hook.normalized() * force
return length
func grapplingHook_DisappearHand():
meshSkeletonHiddenHand.visible = false
#var bone = meshSkeletonNode.find_bone("DEF-hand.R")
#var bonePos = meshSkeletonNode.get_bone_pose(bone)
#bonePos = bonePos.scaled( Vector3(0.1, 0.1, 0.1) )
#meshSkeletonNode.set_bone_custom_pose(bone, bonePos)
func grapplingHook_ReappearHand():
meshSkeletonHiddenHand.visible = true
#var bone = meshSkeletonNode.find_bone("DEF-hand.R")
#var bonePos = meshSkeletonNode.get_bone_pose(bone)
#bonePos = bonePos.( Vector3(1, 1, 1) )
#meshSkeletonNode.set_bone_custom_pose(bone, bonePos)
var jumpingUp : bool
func _physics_process(_delta):
# Respawn Player here first
if(playerWantsToRespawn):
playerWantsToRespawn = false
respawnPlayer()
return
# Check for New World Environment
if(playerWantsNewWorldEnvironment):
playerWantsNewWorldEnvironment = false
currentMap.updateMapWorldEnvironmentScene()
# Check for Toggle Light
if(playerWantsToToggleLight):
playerWantsToToggleLight = false
omniLight.visible = !omniLight.visible
$Model/Sound_ToggleLight_1.play()
# Moving the character
# Do NOT interpolate gravity / jumping / falling, so backup the y velocity
var y_cache : float = velocity.y
velocity = velocity.lerp(currentDirection * movementSpeed, movementAcceleration * _delta)
# Restore the y velocity after the interpolation, since we don't interpolate gravity / jumping / falling
velocity.y = y_cache
# Vertical velocity
if(!grapplingHook_IsHooked):
# Apply Gravity
velocity.y -= fallAcceleration * _delta
# Check for a Jump
if(playerWantsToJump):
playerWantsToJump = false
if( playerCanJump() ):
velocity.y = jumpHeight
jumpingUp = true
$Model/Sound_Jump.play()
var bPrevOnFloor = is_on_floor()
y_cache = velocity.y
move_and_slide()
if( !bPrevOnFloor and is_on_floor() and y_cache < -17.0):
# var particles = Particles_Land.instance()
#$Model.add_child(particles)
$Model/Sound_Land.play()
# Calculate Potential Jumping Animation
if( !is_on_floor() ):
if(velocity.y > 0.1):
$AnimationTree.set("parameters/air_transition/current", 0)
$AnimationTree.set("parameters/air_blend/blend_amount", -1)
elif(velocity.y < -0.1):
$AnimationTree.set("parameters/air_transition/current", 0)
$AnimationTree.set("parameters/air_blend/blend_amount", 0)
else:
$AnimationTree.set("parameters/air_transition/current", 1)
# Calculate Running Animation
currentSpeed = ( (abs(velocity.x) + abs(velocity.z) - 7) / 7)
$AnimationTree.set("parameters/iwr_blend/blend_amount", currentSpeed)
# Update Grappling Hook
grapplingHook_Process()
# Check for Hand IK from Grappling Hook
if(grapplingHook_IsHooked):
meshSkeletalIKNode.start()
else:
meshSkeletalIKNode.stop()
# self.linear_velocity.y += jumpHeight
# playerWantsToJump = false
#var y_cache = linear_velocity.y
#linear_velocity = linear_velocity.linear_interpolate(currentDirection * movementSpeed, movementAcceleration * _delta)
#linear_velocity.y = y_cache
#linear_velocity = Vector3(currentDirection.x, 0, currentDirection.z).normalized() * movementSpeed * _delta
#linear_velocity.y = y_cache
#linear_velocity.x += currentDirection.x * movementSpeed * _delta
#linear_velocity.x = clamp(linear_velocity.x, -3, 3)
#linear_velocity.z += currentDirection.z * movementSpeed * _delta
#linear_velocity.z = clamp(linear_velocity.z, -3, 3)
# Handle Jumping / Gravity
#if is_on_floor():
#if true:
# snapVector = -get_floor_normal()
# gravity_vec = Vector3.ZERO
#else:
# snap = Vector3.DOWN
# accel = ACCEL_AIR
# gravity_vec += Vector3.DOWN * gravity * delta
#if Input.is_action_just_pressed("jump") and is_on_floor():
# snap = Vector3.ZERO
# gravity_vec = Vector3.UP * jump
# Actually Movement
#velocityAmount = velocityAmount.linear_interpolate(currentDirection * movementSpeed, accelerationDefault * delta)
# finalMovement = velocityAmount + finalgravityVelociy
func respawnPlayer():
velocity = Vector3.ZERO
grapplingHook_IsHooked = false
playerWantsToShootGrapplingHook = false
playerWantsToReleaseGrapplingHook = true
$Model/Sound_Teleport_1.play()
global_transform.origin = currentSpawnLocation
func playerCanJump() -> bool:
if( self.is_on_floor() ):
return true
else:
return false
func _on_input_player_mousemotion_event(event):
clippedCameraHead.rotate_y(deg2rad(-event.relative.x * mouseSensitivity))
clippedCameraPivot.rotate_x(deg2rad(-event.relative.y * mouseSensitivity))
clippedCameraPivot.rotation.x = clamp(clippedCameraPivot.rotation.x, deg2rad(-89), deg2rad(89))
func _on_input_player_move(direction : Vector3):
currentDirection = direction
if(moveNetworkUpdateAllowed):
hyperGossip.broadcast_event(EVENT_PLAYER_DIRECTION, getPlayerLocalCoreNetworkData() )
moveNetworkUpdateAllowed = false
#var h_rot = clippedCameraHead.global_transform.basis.get_euler().y
# Adjust Current Direction based on Mouse Direction
#currentDirection = Vector3(direction.x, 0, direction.z).rotated(Vector3.UP, h_rot).normalized()
func _on_input_player_respawn_player() -> void:
playerWantsToRespawn = true
var spawnLocation : Vector3 = getSpawnLocationForMapName(currentMap.map_name)
currentSpawnLocation = spawnLocation
var data : Dictionary = {
#"profile": profile,
"spawnLocation": {
"x": spawnLocation.x,
"y": spawnLocation.y,
"z": spawnLocation.z
}
}
hyperGossip.broadcast_event(EVENT_PLAYER_RESPAWNPLAYER, data)
func _on_input_player_jump():
playerWantsToJump = true
hyperGossip.broadcast_event(EVENT_PLAYER_WANTSTOJUMP, getPlayerLocalCoreNetworkData() )
func _on_input_player_shoot_grapplinghook():
if( Input.get_mouse_mode() != Input.MOUSE_MODE_VISIBLE ):
if( grappleHookCast.is_colliding() ):
playerWantsToShootGrapplingHook = true
grapplingHook_GrapplePosition = grappleHookCast.get_collision_point()
hyperGossip.broadcast_event(EVENT_PLAYER_SHOOT_GRAPPLINGHOOK, getPlayerLocalShootGrapplingHookData() )
func _on_input_player_release_grapplinghook():
if( Input.get_mouse_mode() != Input.MOUSE_MODE_VISIBLE ):
if(grapplingHook_IsHooked):
playerWantsToReleaseGrapplingHook = true
hyperGossip.broadcast_event(EVENT_PLAYER_RELEASE_GRAPPLINGHOOK, getPlayerLocalCoreNetworkData() )
func _on_input_player_toggle_light():
playerWantsToToggleLight = true
hyperGossip.broadcast_event(EVENT_PLAYER_TOGGLE_LIGHT, getPlayerLocalCoreNetworkData() )
func _on_Player_body_entered(body : Node) -> void:
if(!collisions.has(body.get_instance_id()) ):
collisions[body.get_instance_id()] = body.name
$CollisionUI.onNewollision(body, collisions)
func _on_Player_body_exited(body : Node) -> void:
if(collisions.has(body.get_instance_id()) ):
collisions.erase(body.get_instance_id())
$CollisionUI.onRemovedCollision(body, collisions)
func _on_MoveNetworkTimer_timeout():
moveNetworkUpdateAllowed = true
func getPlayerLocalCoreNetworkData() -> Dictionary:
# TODO : Fix finding the local player, and get it out of player_core into player_core_local
var localPlayer : CharacterBody3D = get_tree().get_current_scene().get_node("Players").get_node("PlayerCoreLocal")
var _position : Vector3 = localPlayer.position
var direction : Vector3 = localPlayer.currentDirection
var data : Dictionary = {
#"profile": profile,
"position": {
"x": _position.x,
"y": _position.y,
"z": _position.z
},
"direction": {
"x": direction.x,
"y": direction.y,
"z": direction.z
},
"velocity": {
"x": velocity.x,
"y": velocity.y,
"z": velocity.z
}
}
return data
func getPlayerLocalShootGrapplingHookData() -> Dictionary:
# TODO : Fix finding the local player, and get it out of player_core into player_core_local
var localPlayer : CharacterBody3D = get_tree().get_current_scene().get_node("Players").get_node("PlayerCoreLocal")
var _position : Vector3 = localPlayer.position
var direction : Vector3 = localPlayer.currentDirection
var data : Dictionary = {
#"profile": profile,
"position": {
"x": _position.x,
"y": _position.y,
"z": _position.z
},
"direction": {
"x": direction.x,
"y": direction.y,
"z": direction.z
},
"velocity": {
"x": velocity.x,
"y": velocity.y,
"z": velocity.z
},
"grapple_position": {
"x": grapplingHook_GrapplePosition.x,
"y": grapplingHook_GrapplePosition.y,
"z": grapplingHook_GrapplePosition.z
}
}
return data
func playerCoreNetworkDataUpdate(data : Dictionary):
self.position = Vector3(data.position.x, data.position.y, data.position.z)
self.currentDirection = Vector3(data.direction.x, data.direction.y, data.direction.z)
self.velocity = Vector3(data.velocity.x, data.velocity.y, data.velocity.z)
func playerCoreNetworkDataUpdate_Types(_position : Vector3, _currentDirection : Vector3, _kinematicVelocity : Vector3):
self.position = _position
self.currentDirection = _currentDirection
self.velocity = _kinematicVelocity