2021-06-07 19:11:42 +02:00
extends CharacterBody3D
2021-05-19 00:56:18 +02:00
2021-09-07 23:23:48 +02:00
@ export var mouse_sensitivity : = 0.15
2021-05-19 00:56:18 +02:00
2021-08-21 15:03:51 +02:00
@ onready var main = get_tree ( ) . root . get_node ( " Main " )
2021-09-11 15:52:09 +02:00
@ onready var gui = main . get_node ( " GUI " )
@ onready var settings = gui . settings
2021-08-21 15:03:51 +02:00
@ onready var hud = main . get_node ( " HUD " )
2021-07-16 02:52:18 +02:00
@ onready var crosshair = hud . get_node ( " Crosshair " )
@ onready var vignette = hud . get_node ( " Vignette " )
2021-09-26 02:18:51 +02:00
@ onready var banner_busy = preload ( " res://Assets/Effects/Busy.png " )
@ onready var banner_chat = preload ( " res://Assets/Effects/Typing.png " )
2021-05-19 00:56:18 +02:00
@ onready var head = $ Head
2021-05-24 21:57:19 +02:00
@ onready var camera = $ Head / Camera
@ onready var tween = $ Head / Camera / Tween
2021-05-22 02:10:00 +02:00
@ onready var ground_check = $ GroundCheck
2021-07-12 22:40:23 +02:00
@ onready var climb_tween = $ ClimbTween # undergoing redesign in Godot 4
2021-05-24 21:57:19 +02:00
@ onready var climb_check = $ ClimbCheck
@ onready var body = $ Body
2021-05-25 01:14:37 +02:00
@ onready var mesh = $ Mesh
2021-06-18 01:43:10 +02:00
@ onready var weapon = $ Head / Camera / Hand / Weapon
2021-09-08 23:25:52 +02:00
var gibs_vfx = preload ( " res://Assets/Effects/Gibs.tscn " )
2021-09-24 04:46:39 +02:00
var blood_decal = preload ( " res://Assets/Decals/Blood/BloodSplash.tscn " )
2021-09-08 23:25:52 +02:00
2021-09-04 22:37:13 +02:00
# climb functions - temporarily disabled
#@onready var body_height = body.shape.height
#@onready var body_y = body.position.y
#@onready var mesh_height = mesh.mesh.mid_height
#@onready var mesh_y = mesh.position.y
#@onready var climb_check_y = climb_check.position.y
2021-06-07 19:11:42 +02:00
@ onready var ground_check_y = ground_check . position . y
2021-05-24 21:57:19 +02:00
2021-06-03 17:44:25 +02:00
var input_active = false
2021-05-24 21:57:19 +02:00
var base_fov = 90
2021-07-26 20:51:54 +02:00
var view_zoom_target : = 1.0
var view_zoom_direction = true
var view_zoom : = view_zoom_target :
2021-05-24 21:57:19 +02:00
set ( zoom ) :
view_zoom = zoom
camera . fov = base_fov / zoom
2021-05-24 22:18:46 +02:00
crosshair . modulate . a = clamp ( 1 - ( zoom - 1 ) , 0 , 1 )
vignette . modulate . a = ( zoom - 1 ) / 3
2021-05-24 21:57:19 +02:00
2021-09-25 17:36:15 +02:00
var revenge_pid : int #store PID of the player who killed you recently
2021-09-04 22:37:13 +02:00
# climbing code - disabled for now
#var climb_height := 0.75
#var climb_time := 0.15
#var climb_state := 0.0 :
# set(factor):
# #print("climb_state is now ", factor)
# climb_state = factor
# body.shape.height = body_height - factor * climb_height
# body.position.y = body_y + factor * climb_height / 2
#
# mesh.mesh.mid_height = mesh_height - factor * climb_height
# mesh.position.y = mesh_y + factor * climb_height / 2
#
# ground_check.position.y = ground_check_y + factor * climb_height / 2
# climb_check.position.y = climb_check_y + factor * climb_height / 2
2021-05-19 00:56:18 +02:00
2021-05-19 01:59:58 +02:00
var direction : = Vector3 . ZERO
var accel : = 0
var speed : = 0
var medium = " ground "
var accel_type : = {
" ground " : 12 ,
" air " : 1 ,
" water " : 4
}
var speed_type : = {
2021-05-24 21:57:19 +02:00
" ground " : 10 ,
" air " : 10 ,
" water " : 5
2021-05-19 01:59:58 +02:00
}
2021-05-19 00:56:18 +02:00
var gravity : = 28
2021-09-07 23:23:48 +02:00
var jump : = 14 * 1
var jetpack_active = false
var jetpack_thrust = 48 # force applied against gravity
var jetpack_tank = 0.5 # maximum fuel (jetpack use time
var jetpack_fuel = jetpack_tank # current fuel (use time) left
var jetpack_recharge = 0.25 # how long to recharge to full
2021-09-25 20:51:20 +02:00
var jetpack_min = 1.0 / 8
2021-09-08 23:54:04 +02:00
var jetpack_was_active = false
2021-05-19 00:56:18 +02:00
2021-05-19 01:59:58 +02:00
var velocity : = Vector3 . ZERO
2021-05-19 00:56:18 +02:00
var gravity_vec : = Vector3 . ZERO
2021-09-22 01:51:21 +02:00
var dead = false : # used to workaround Godot crash when destroying player_nodes
set ( value ) :
match value :
true :
#input_active = false
self . hide ( )
$ Body . disabled = true
#set_physics_process(false)
false :
#input_active = true
self . show ( )
$ Body . disabled = false
2021-09-25 19:03:38 +02:00
$ SpawnSFX . play ( )
2021-09-25 19:24:29 +02:00
$ SpawnVFX . emitting = true
2021-09-22 01:51:21 +02:00
#set_physics_process(true)
dead = value
2021-09-26 00:46:34 +02:00
var focus_banner_alpha = 0
var focus_banner_inc = 5
var focus_banner_dec = 1
var focus_banner_show = false
var last_viewed_banner = null
func view_banner ( show : bool ) :
focus_banner_show = show
2021-09-26 02:18:51 +02:00
@ rpc ( nosync , reliable , auth ) func focus_banner ( show : bool , type : = 0 ) :
2021-09-26 00:46:34 +02:00
if show :
$ FocusBanner . show ( )
else :
$ FocusBanner . hide ( )
2021-09-26 02:18:51 +02:00
match type :
0 : $ FocusBanner . mesh . surface_get_material ( 0 ) . set ( " albedo_texture " , banner_busy )
1 : $ FocusBanner . mesh . surface_get_material ( 0 ) . set ( " albedo_texture " , banner_chat )
2021-09-22 01:51:21 +02:00
2021-09-17 01:32:00 +02:00
@ rpc ( auth , nosync , unreliable ) func update_movement ( player_transform , head_rotation , lin_velocity , jetpack ) :
2021-07-12 22:40:23 +02:00
global_transform = player_transform
head . set_rotation ( head_rotation )
2021-09-07 23:23:48 +02:00
jetpack_active = jetpack
2021-09-08 21:36:43 +02:00
linear_velocity = lin_velocity
2021-06-19 03:40:10 +02:00
2021-09-22 01:51:21 +02:00
@ rpc ( any , sync , reliable ) func set_dead ( is_dead : bool ) :
#print("Recieved RPC call for set_dead ", is_dead)
self . dead = is_dead
# if is_multiplayer_authority():
# print("Rebroadcasting RPC call for set_dead ", dead)
# rpc(&'set_dead', dead)
2021-05-19 00:56:18 +02:00
func _ready ( ) - > void :
2021-06-03 17:44:25 +02:00
#Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
2021-07-26 20:51:54 +02:00
view_zoom_target = 1.0
2021-09-26 00:46:34 +02:00
var banner_material = $ FocusBanner . mesh . surface_get_material ( 0 ) . duplicate ( )
$ FocusBanner . mesh . surface_set_material ( 0 , banner_material )
focus_banner ( false )
2021-09-10 02:04:27 +02:00
2021-09-17 01:32:00 +02:00
if is_multiplayer_authority ( ) : # prevent puppets from attempting to steer the authority - this just causes RPC errors
2021-09-10 02:04:27 +02:00
input_active = true
2021-09-11 23:00:53 +02:00
$ Head / Camera . current = true
2021-09-10 02:04:27 +02:00
else :
input_active = false
2021-09-11 23:00:53 +02:00
$ Head / Camera . current = false
2021-06-09 19:41:52 +02:00
2021-09-12 04:44:02 +02:00
$ Jetpack / JetpackSound . playing = true
$ Jetpack / JetpackSound . stream_paused = true
2021-09-25 19:24:29 +02:00
$ SpawnVFX . emitting = true
2021-09-12 04:44:02 +02:00
2021-05-19 00:56:18 +02:00
func aim ( event ) - > void :
var mouse_motion = event as InputEventMouseMotion
if mouse_motion :
2021-09-11 15:52:09 +02:00
var adjusted_mouse_sensitivity = 1.0
if " Sensitivity " in settings . keys ( ) :
adjusted_mouse_sensitivity = mouse_sensitivity * settings [ " Sensitivity " ]
rotation . y -= deg2rad ( mouse_motion . relative . x * adjusted_mouse_sensitivity / view_zoom )
2021-05-19 00:56:18 +02:00
2021-07-04 00:42:42 +02:00
var current_tilt : float = head . rotation . x
2021-09-11 15:52:09 +02:00
current_tilt -= deg2rad ( mouse_motion . relative . y * adjusted_mouse_sensitivity / view_zoom )
2021-07-04 00:42:42 +02:00
head . rotation . x = clamp ( current_tilt , deg2rad ( - 90 ) , deg2rad ( 90 ) )
2021-05-19 00:56:18 +02:00
func _input ( event ) - > void :
2021-09-22 01:51:21 +02:00
if dead :
return
2021-06-03 17:44:25 +02:00
if not input_active :
return
2021-05-19 00:56:18 +02:00
2021-09-19 20:51:47 +02:00
#assert(is_multiplayer_authority() == true, "input_active is true, even though the node is not multiplayer_authority")
if is_multiplayer_authority ( ) == false :
print ( " Input is active, but we ' re not the authority. WTF?! " )
input_active = false
return
2021-09-11 14:35:07 +02:00
2021-05-24 21:57:19 +02:00
if Input . is_action_just_pressed ( " view_zoom " ) :
2021-07-16 02:52:18 +02:00
# tween.remove_all()
# tween.interpolate_property(self, "view_zoom", view_zoom, 4.0, 0.5, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
# tween.start()
2021-07-26 20:51:54 +02:00
view_zoom_direction = true
view_zoom_target = 4.0
2021-05-24 21:57:19 +02:00
if Input . is_action_just_released ( " view_zoom " ) :
2021-07-16 02:52:18 +02:00
# tween.remove_all()
# tween.interpolate_property(self, "view_zoom", view_zoom, 1.0, 0.25, Tween.TRANS_SINE, Tween.EASE_IN_OUT)
# tween.start()
2021-07-26 20:51:54 +02:00
view_zoom_direction = false
view_zoom_target = 1.0
2021-07-16 02:52:18 +02:00
2021-07-12 22:40:23 +02:00
aim ( event )
2021-09-12 04:05:32 +02:00
var can_shoot = true if view_zoom < = 1.05 else false
if can_shoot and Input . is_action_just_pressed ( " trigger_primary " ) :
2021-08-25 12:03:35 +02:00
weapon . trigger ( 0 , true )
2021-06-18 01:43:10 +02:00
elif Input . is_action_just_released ( " trigger_primary " ) :
2021-08-25 12:03:35 +02:00
weapon . trigger ( 0 , false )
2021-09-12 04:05:32 +02:00
if can_shoot and Input . is_action_just_pressed ( " trigger_secondary " ) :
2021-08-25 12:03:35 +02:00
weapon . trigger ( 1 , true )
2021-06-18 01:43:10 +02:00
elif Input . is_action_just_released ( " trigger_secondary " ) :
2021-08-25 12:03:35 +02:00
weapon . trigger ( 1 , false )
2021-07-26 20:51:54 +02:00
func _process ( delta ) :
2021-09-22 01:51:21 +02:00
if dead :
return
2021-09-11 14:35:07 +02:00
$ Jetpack / GPUParticles3D . emitting = jetpack_active
2021-09-12 04:44:02 +02:00
if jetpack_active :
$ Jetpack / JetpackSound . stream_paused = false
2021-09-21 23:07:31 +02:00
$ Jetpack / OmniLight3D . show ( )
2021-09-25 00:29:54 +02:00
$ Jetpack / OmniLight3D . light_energy = randf_range ( 2 , 5 )
2021-09-12 04:44:02 +02:00
else :
$ Jetpack / JetpackSound . stream_paused = true
2021-09-21 23:07:31 +02:00
$ Jetpack / OmniLight3D . hide ( )
2021-09-12 04:44:02 +02:00
2021-09-26 00:46:34 +02:00
# show focus banner on demand
if focus_banner_show :
focus_banner_alpha = min ( focus_banner_alpha + focus_banner_inc * delta , 1.0 )
else :
focus_banner_alpha = max ( focus_banner_alpha - focus_banner_dec * delta , 0.0 )
$ FocusBanner . mesh . surface_get_material ( 0 ) . set ( " albedo_color " , Color ( 1 , 1 , 1 , focus_banner_alpha ) )
2021-09-26 01:12:12 +02:00
#print("PLayer: ", name, "; Focus banner alpha: ", focus_banner_alpha, "; Focus banner show: ", focus_banner_show)
2021-09-26 00:46:34 +02:00
2021-09-11 14:35:07 +02:00
if not input_active :
return
2021-09-26 00:46:34 +02:00
# demand seeing other player's banner:
2021-09-26 01:12:12 +02:00
#print("Last viewed banner is ", last_viewed_banner)
2021-09-26 00:46:34 +02:00
if $ " Head/Camera/RayCast3D " . is_colliding ( ) :
2021-09-26 01:12:12 +02:00
#print("Probe got ", $"Head/Camera/RayCast3D".get_collider().name)
2021-09-26 00:46:34 +02:00
if $ " Head/Camera/RayCast3D " . get_collider ( ) . has_method ( & " view_banner " ) :
$ " Head/Camera/RayCast3D " . get_collider ( ) . view_banner ( true )
last_viewed_banner = $ " Head/Camera/RayCast3D " . get_collider ( )
elif last_viewed_banner :
last_viewed_banner . view_banner ( false )
2021-09-11 14:35:07 +02:00
2021-09-19 20:51:47 +02:00
#assert(is_multiplayer_authority() == true, "input_active is true, even though the node is not multiplayer_authority")
if is_multiplayer_authority ( ) == false :
print ( " input_active is true, while we ' re not the authority. WTF? " )
input_active = false
return
2021-09-11 14:35:07 +02:00
2021-07-26 20:51:54 +02:00
if view_zoom_direction and view_zoom < view_zoom_target :
view_zoom = min ( view_zoom_target , view_zoom + delta * 4 )
elif not view_zoom_direction and view_zoom > view_zoom_target :
view_zoom = max ( view_zoom_target , view_zoom - delta * 4 )
2021-09-07 23:23:48 +02:00
2021-09-22 01:51:21 +02:00
hud . get_node ( " Stats " ) . get_node ( " JetpackBar " ) . value = ( jetpack_fuel / jetpack_tank ) * 100
2021-09-25 20:51:20 +02:00
# weapon spread
weapon . spread = max ( lerp ( weapon . spread , weapon . spread_min , weapon . spread_lerp ) , weapon . spread_min )
2021-07-26 20:51:54 +02:00
2021-09-29 14:08:46 +02:00
@ rpc ( sync , any , reliable ) func moan ( ) :
var anims = [ " 01 " , " 02 " , " 03 " , " 04 " ]
$ Pain . play ( anims [ randi ( ) % 4 ] )
2021-08-23 01:57:47 +02:00
func damage ( hp : int ) :
2021-09-17 01:32:00 +02:00
var target = main . player_list . players [ self . get_multiplayer_authority ( ) ]
2021-08-25 12:03:35 +02:00
target . health -= hp
2021-08-29 20:32:46 +02:00
2021-09-22 01:51:21 +02:00
@ rpc ( any , sync , reliable ) func die ( killer_pid : int ) :
2021-09-08 23:25:52 +02:00
var gibs = gibs_vfx . instantiate ( )
get_tree ( ) . root . add_child ( gibs )
2021-09-19 20:51:47 +02:00
gibs . global_transform = self . global_transform
2021-09-24 04:46:39 +02:00
var decal = blood_decal . instantiate ( )
get_tree ( ) . root . add_child ( decal )
decal . global_transform = self . global_transform
2021-09-08 23:25:52 +02:00
2021-09-17 01:32:00 +02:00
#if get_tree().get_rpc_sender_id() != get_multiplayer_authority():
2021-08-29 20:32:46 +02:00
# print ("Death requested by a non-master. Ignoring")
# return
2021-09-19 20:51:47 +02:00
#main.rpc(&'destroy_player', self.get_multiplayer_authority())
main . destroy_player ( self . get_multiplayer_authority ( ) )
2021-09-22 01:51:21 +02:00
set_dead ( true )
2021-09-19 20:51:47 +02:00
#main.chat.rpc(&'chat_notification', "Player [/i][b][color=" + main.player_list.players[self.get_multiplayer_authority()].color.to_html() + "]" + main.player_list.players[self.get_multiplayer_authority()].name + "[/color][/b][i] was killed by " + main.player_list.players[killer_pid].name )
main . chat . chat_notification ( " Player [/i][b][color= " + main . player_list . players [ self . get_multiplayer_authority ( ) ] . color . to_html ( ) + " ] " + main . player_list . players [ self . get_multiplayer_authority ( ) ] . name + " [/color][/b][i] was killed by " + main . player_list . players [ killer_pid ] . name )
2021-08-29 22:47:40 +02:00
2021-09-25 17:36:15 +02:00
revenge_pid = killer_pid
2021-08-29 20:32:46 +02:00
#queue_free()
2021-09-11 22:23:41 +02:00
func update_player ( info ) - > void :
update_color ( info . color )
2021-09-17 01:32:00 +02:00
func update_color ( color ) - > void : #change player's wolrldmodel color
2021-08-29 21:41:20 +02:00
var player_material = mesh . mesh . surface_get_material ( 0 ) . duplicate ( )
2021-09-11 22:23:41 +02:00
player_material . albedo_color = color
mesh . set_surface_override_material ( 0 , player_material )
2021-07-26 20:51:54 +02:00
2021-05-19 00:56:18 +02:00
func _physics_process ( delta ) :
2021-09-22 01:51:21 +02:00
if dead : # workaround for Godot player destruction crash
linear_velocity = Vector3 . ZERO
return
2021-09-17 01:32:00 +02:00
if not is_multiplayer_authority ( ) :
2021-09-08 21:36:43 +02:00
move_and_slide ( )
return
2021-05-19 01:59:58 +02:00
direction = Vector3 . ZERO
2021-05-19 00:56:18 +02:00
2021-09-07 23:23:48 +02:00
if is_on_floor ( ) and ground_check . is_colliding ( ) and not jetpack_active :
2021-09-26 03:07:41 +02:00
#snap = -get_floor_normal()
2021-05-19 01:59:58 +02:00
medium = " ground "
gravity_vec = Vector3 . ZERO
2021-05-19 00:56:18 +02:00
else :
2021-09-26 03:07:41 +02:00
#snap = Vector3.DOWN
2021-05-19 01:59:58 +02:00
medium = " air "
2021-05-19 00:56:18 +02:00
gravity_vec += Vector3 . DOWN * gravity * delta
2021-06-06 01:58:04 +02:00
if input_active :
if Input . is_action_just_pressed ( " move_jump " ) and is_on_floor ( ) :
2021-09-26 03:07:41 +02:00
#snap = Vector3.ZERO
2021-06-06 01:58:04 +02:00
gravity_vec = Vector3 . UP * jump
2021-09-25 18:34:02 +02:00
$ JumpSFX . rpc ( & " play " )
2021-06-06 01:58:04 +02:00
if Input . is_action_pressed ( " move_forward " ) :
direction -= transform . basis . z
if Input . is_action_pressed ( " move_backward " ) :
direction += transform . basis . z
if Input . is_action_pressed ( " move_left " ) :
direction -= transform . basis . x
if Input . is_action_pressed ( " move_right " ) :
direction += transform . basis . x
2021-09-07 23:23:48 +02:00
2021-09-08 23:54:04 +02:00
jetpack_was_active = jetpack_active
if jetpack_was_active :
jetpack_active = Input . is_action_pressed ( " move_special " ) if jetpack_fuel > 0 else false
else :
jetpack_active = Input . is_action_just_pressed ( " move_special " ) if jetpack_fuel > jetpack_min else false
2021-09-07 23:23:48 +02:00
if jetpack_active :
gravity_vec [ 1 ] += jetpack_thrust * delta
jetpack_fuel -= delta
elif jetpack_fuel < jetpack_tank :
jetpack_fuel = min ( jetpack_tank , jetpack_fuel + jetpack_recharge * delta )
2021-05-19 01:59:58 +02:00
2021-09-11 15:49:59 +02:00
#print("Jetpack fuel: ", jetpack_fuel, " active: ", jetpack_active, " delta: ", delta, " gravity vec: ", gravity_vec)
2021-09-17 01:32:00 +02:00
2021-05-19 01:59:58 +02:00
if direction . length ( ) > 0 : # normalized() will return a null
direction = direction . normalized ( )
2021-05-19 00:56:18 +02:00
2021-05-19 01:59:58 +02:00
speed = speed_type [ medium ]
accel = accel_type [ medium ]
2021-05-19 00:56:18 +02:00
2021-05-19 01:59:58 +02:00
velocity = velocity . lerp ( direction * speed , accel * delta )
2021-05-19 00:56:18 +02:00
2021-06-07 19:11:42 +02:00
linear_velocity = velocity + gravity_vec
2021-07-12 22:40:23 +02:00
move_and_slide ( )
2021-05-19 00:56:18 +02:00
2021-05-22 18:08:26 +02:00
if not is_on_floor ( ) and not ground_check . is_colliding ( ) : # while in mid-air collisions affect momentum
2021-06-07 19:11:42 +02:00
velocity . x = linear_velocity . x
velocity . z = linear_velocity . z
gravity_vec . y = linear_velocity . y
2021-07-12 22:40:23 +02:00
2021-09-08 21:36:43 +02:00
rpc ( & ' update_movement ' , global_transform , head . get_rotation ( ) , linear_velocity , jetpack_active )
2021-07-12 22:40:23 +02:00
2021-05-25 01:14:37 +02:00
# (stair) climbing
2021-09-04 22:37:13 +02:00
# ↓ disabled - Tween is undergoing redesign in Godot 4
# if get_slide_count() > 1 and climb_check.is_colliding() and false:
# #print("climb started at climb state: ", climb_state)
# var test_y = climb_height * (1 - climb_state)
# #print("test_y: ", test_y)
# var climb_test_start = global_transform.translated(Vector3(0, test_y, 0))
# var climb_test_step = Vector3(0,0,-0.1).rotated(Vector3.UP, rotation.y)
# if not test_move(climb_test_start, climb_test_step): # no collision
# var step = climb_check.get_collision_point().y
# var start = global_transform.origin.y
## print("step: ", step, " start: ", start)
# climb_state = clamp((step - start) / climb_height, 0, 1)
# global_transform.origin.y += climb_height * climb_state
# #print("climb state to start: ", climb_state)
## print("Climb height: ", step - start, " Climb state: ", climb_state)
# climb_tween.remove_all()
# climb_tween.interpolate_property(self, "climb_state", climb_state, 0.0, climb_time, Tween.TRANS_CUBIC, Tween.EASE_IN)
# climb_tween.start()