extends CharacterBody3D @export var mouse_sensitivity := 0.15 @onready var main = get_tree().root.get_node("Main") @onready var gui = main.get_node("GUI") @onready var settings = gui.settings @onready var hud = main.get_node("HUD") @onready var crosshair = hud.get_node("Crosshair") @onready var vignette = hud.get_node("Vignette") @onready var head = $Head @onready var camera = $Head/Camera @onready var tween = $Head/Camera/Tween @onready var ground_check = $GroundCheck @onready var climb_tween = $ClimbTween # undergoing redesign in Godot 4 @onready var climb_check = $ClimbCheck @onready var body = $Body @onready var mesh = $Mesh @onready var weapon = $Head/Camera/Hand/Weapon var gibs_vfx = preload("res://Assets/Effects/Gibs.tscn") var blood_decal = preload("res://Assets/Decals/Blood/BloodSplash.tscn") # 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 @onready var ground_check_y = ground_check.position.y var input_active = false var base_fov = 90 var view_zoom_target := 1.0 var view_zoom_direction = true var view_zoom := view_zoom_target : set(zoom): view_zoom = zoom camera.fov = base_fov / zoom crosshair.modulate.a = clamp(1 - (zoom - 1), 0, 1) vignette.modulate.a = (zoom - 1) / 3 # 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 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 := { "ground": 10, "air": 10, "water": 5 } var gravity := 28 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 var jetpack_min = 1/4 var jetpack_was_active = false var velocity := Vector3.ZERO var gravity_vec := Vector3.ZERO 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 #set_physics_process(true) dead = value @rpc(auth, nosync,unreliable) func update_movement(player_transform, head_rotation, lin_velocity, jetpack): global_transform = player_transform head.set_rotation(head_rotation) jetpack_active = jetpack linear_velocity = lin_velocity @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) func _ready() -> void: #Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) view_zoom_target = 1.0 if is_multiplayer_authority(): # prevent puppets from attempting to steer the authority - this just causes RPC errors input_active = true $Head/Camera.current = true else: input_active = false $Head/Camera.current = false $Jetpack/JetpackSound.playing = true $Jetpack/JetpackSound.stream_paused = true func aim(event) -> void: var mouse_motion = event as InputEventMouseMotion if mouse_motion: 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) var current_tilt: float = head.rotation.x current_tilt -= deg2rad(mouse_motion.relative.y * adjusted_mouse_sensitivity / view_zoom) head.rotation.x = clamp(current_tilt, deg2rad(-90), deg2rad(90)) func _input(event) -> void: if dead: return if not input_active: return #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 if Input.is_action_just_pressed("view_zoom"): # tween.remove_all() # tween.interpolate_property(self, "view_zoom", view_zoom, 4.0, 0.5, Tween.TRANS_SINE, Tween.EASE_IN_OUT) # tween.start() view_zoom_direction = true view_zoom_target = 4.0 if Input.is_action_just_released("view_zoom"): # tween.remove_all() # tween.interpolate_property(self, "view_zoom", view_zoom, 1.0, 0.25, Tween.TRANS_SINE, Tween.EASE_IN_OUT) # tween.start() view_zoom_direction = false view_zoom_target = 1.0 aim(event) var can_shoot = true if view_zoom <= 1.05 else false if can_shoot and Input.is_action_just_pressed("trigger_primary"): weapon.trigger(0, true) elif Input.is_action_just_released("trigger_primary"): weapon.trigger(0, false) if can_shoot and Input.is_action_just_pressed("trigger_secondary"): weapon.trigger(1, true) elif Input.is_action_just_released("trigger_secondary"): weapon.trigger(1, false) func _process(delta): if dead: return $Jetpack/GPUParticles3D.emitting = jetpack_active if jetpack_active: $Jetpack/JetpackSound.stream_paused = false $Jetpack/OmniLight3D.show() else: $Jetpack/JetpackSound.stream_paused = true $Jetpack/OmniLight3D.hide() if not input_active: return #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 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) hud.get_node("Stats").get_node("JetpackBar").value = (jetpack_fuel / jetpack_tank) * 100 func damage(hp: int): var target = main.player_list.players[self.get_multiplayer_authority()] target.health -= hp @rpc(any, sync, reliable) func die(killer_pid: int): var gibs = gibs_vfx.instantiate() get_tree().root.add_child(gibs) gibs.global_transform = self.global_transform var decal = blood_decal.instantiate() get_tree().root.add_child(decal) decal.global_transform = self.global_transform #if get_tree().get_rpc_sender_id() != get_multiplayer_authority(): # print ("Death requested by a non-master. Ignoring") # return #main.rpc(&'destroy_player', self.get_multiplayer_authority()) main.destroy_player(self.get_multiplayer_authority()) set_dead(true) #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 ) #queue_free() func update_player(info) -> void: update_color(info.color) func update_color(color) -> void: #change player's wolrldmodel color var player_material = mesh.mesh.surface_get_material(0).duplicate() player_material.albedo_color = color mesh.set_surface_override_material(0, player_material) func _physics_process(delta): if dead: # workaround for Godot player destruction crash linear_velocity = Vector3.ZERO return if not is_multiplayer_authority(): move_and_slide() return direction = Vector3.ZERO if is_on_floor() and ground_check.is_colliding() and not jetpack_active: snap = -get_floor_normal() medium = "ground" gravity_vec = Vector3.ZERO else: snap = Vector3.DOWN medium = "air" gravity_vec += Vector3.DOWN * gravity * delta if input_active: if Input.is_action_just_pressed("move_jump") and is_on_floor(): snap = Vector3.ZERO gravity_vec = Vector3.UP * jump 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 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 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) #print("Jetpack fuel: ", jetpack_fuel, " active: ", jetpack_active, " delta: ", delta, " gravity vec: ", gravity_vec) if direction.length() > 0: # normalized() will return a null direction = direction.normalized() speed = speed_type[medium] accel = accel_type[medium] velocity = velocity.lerp(direction * speed, accel * delta) linear_velocity = velocity + gravity_vec move_and_slide() if not is_on_floor() and not ground_check.is_colliding(): # while in mid-air collisions affect momentum velocity.x = linear_velocity.x velocity.z = linear_velocity.z gravity_vec.y = linear_velocity.y rpc(&'update_movement', global_transform, head.get_rotation(), linear_velocity, jetpack_active) # (stair) climbing # ↓ 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()