extends CharacterBody3D const WALK_TRANSITION_SPEED = 7 const MAX_AIR_DASH = 1 const FLASHLIGHT_BRIGHTNESS = 30 const KICK_AMOUNT = 20 const LAND_CAMERA_TILT : Vector3 = Vector3(-1,0,0) const WALK_SPEED = 12.0 const SPRINT_SPEED = 15.0 const DASH_SPEED = 40 const SLOWSPEED = .1 const MAX_STAMINA : float = 100 const STAMINA_DRAIN = 200 var speed var double_jump = true var air_dash = MAX_AIR_DASH var gravity = 9.8 var is_climbing = false var ladder_center var default_gravity = gravity var moving_fast = false var moving_fast_top_speed = 0.0 var mouse_input : Vector2 @export_group("Game Settings") @export var AUDIO = true @export_group("Player Movement") @export var DASH_STAM_REQ = 10 @export var JUMP_VELOCITY = 6 @export var SENSITIVITY = .01 @export var sensitivity_shift = true var start_sensitivity @export_subgroup("Head Bob & Gun Sway") @export var BOB_FREQ = 2.0 @export var BOB_AMP = 0.08 @export var t_bob = 1.0 @export var weapon_holder : Node3D @export var weapon_sway_amount : float = .07 @export var weapon_rotation_amount : float = .07 @export_subgroup("FOV") @export var BASE_FOV : float = 80 @export var FOV_CHANGE = 1.5 var gun : Node @onready var dead_player : Resource = load("res://assets/dead_cam.tscn") @onready var pause_menu: Control = $Head/Recoil/Camera3D/PauseMenu @onready var gun_ray = $Head/Recoil/Camera3D/GunRay @onready var level_control = get_tree().current_scene @onready var interact_ray = $Head/Recoil/Camera3D/InteractRay @onready var bullet_ray = $Head/Recoil/Camera3D/BulletRay @onready var hitmarker = load("res://hitmarker.tscn") var instance_bullet var instance_casing var instance_mag var reloading = false var ammo var ammo_reserve var bullet_damage var def_weapon_holder_pos : Vector3 var weapon_holder_start_rot var weapon_holder_start_pos var gun_folded = false var recoiling_fade = 0 var bullet_destination var gun_fire_pitch_starting var current_weapon_index var recoiling = false var dead = false var hud_visible : bool = true var held_item : Node var held_item_linear_damp_cache var held_item_angular_damp_cache var held_item_gravity_cache var held_item_mass_cache var held_item_rotation = Vector3(0,0,0) # Slow Down Variables var remaining_stamina : float = MAX_STAMINA # Pickups var picked_up = false var picked_up_text var pickup_announce = load("res://assets/pickup_announce.tscn") var dead_announce = load("res://assets/dead_announce.tscn") var pickupmsg @onready var crosshair = $Head/Recoil/Camera3D/Crosshair @onready var head = $Head @onready var camera = $Head/Recoil/Camera3D @onready var world_environment = $"../WorldEnvironment" @onready var pickup_sound = $Audio/PickupSound @onready var ear_wind = $Audio/EarWind @onready var land_sound: AudioStreamPlayer = $Audio/LandSound @onready var hurt_audio = $Audio/Hurt @onready var health_indicator = $HealthIndicator @onready var ammo_counter = $Head/Recoil/Camera3D/AmmoCounter @onready var stamina_counter = $Head/Recoil/Camera3D/StaminaCounter @onready var recoil: Node3D = $Head/Recoil @onready var weapon_spawner = $Head/Recoil/Camera3D/WeaponHolder/WeaponSpawner @onready var pick_up_detection = $pick_up_detection @onready var item_holder: Node3D = $Head/ItemHolder @onready var flashlight: SpotLight3D = $Head/Recoil/Camera3D/WeaponHolder/Flashlight @onready var flashlight_audio: AudioStreamPlayer3D = $Head/Recoil/Camera3D/WeaponHolder/FlashlightButton @onready var kick_audio: AudioStreamPlayer3D = $Head/Recoil/Camera3D/Audio/Kick @onready var weapon_pickup_audio: AudioStreamPlayer = $Audio/WeaponPickup @onready var crt_filter: ColorRect = $Head/Recoil/Camera3D/crtFilter @onready var moveable_holder: Node3D = $Head/MoveableHolder @onready var stand_check: RayCast3D = $StandCheck @onready var r_hand_test: MeshInstance3D = $Head/Recoil/Camera3D/WeaponHolder/RHandTest @onready var l_hand_test: MeshInstance3D = $Head/Recoil/Camera3D/WeaponHolder/LHandTest func _ready(): SignalBus.enemy_hit.connect(enemy_hit) weapon_holder_start_rot = weapon_holder.rotation weapon_holder_start_pos = weapon_holder.position Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) start_sensitivity = SENSITIVITY var viewportWidth = get_viewport().size.x var viewportHeight = get_viewport().size.y health_indicator.size = Vector2(viewportWidth,viewportHeight) health_indicator.color = Color(0.471, 0, 0, 0) crt_filter.visible = false #turn off audio if unchecked in player if AUDIO == false: AudioServer.set_bus_volume_db(0,-80) func _input(event) -> void: if !level_control.paused: if event is InputEventMouseMotion: self.rotate_y(-event.relative.x * SENSITIVITY) head.rotate_x(-event.relative.y * SENSITIVITY) head.rotation.x = clamp(head.rotation.x, deg_to_rad(-90), deg_to_rad(85)) mouse_input = event.relative else: if event is InputEventMouseMotion: self.rotate_y(event.relative.x * .00001) head.rotate_x(event.relative.y * .00001) head.rotation.x = clamp(head.rotation.x, deg_to_rad(-90), deg_to_rad(85)) mouse_input = event.relative func _physics_process(delta): if !dead and !level_control.paused: # Add the gravity. if is_on_floor(): double_jump = true air_dash = MAX_AIR_DASH else: velocity.y -= gravity * delta if abs(velocity.y) >= .1: moving_fast = true # Handle jump. if Input.is_action_just_pressed("jump") and is_on_floor() and !is_climbing: velocity.y += JUMP_VELOCITY elif Input.is_action_just_pressed("jump") and double_jump == true and !is_climbing: velocity.y += JUMP_VELOCITY double_jump = false speed = WALK_SPEED #the fuck is this line doing # Get the input direction and handle the movement/deceleration. var input_dir = Input.get_vector("move_left", "move_right", "move_up", "move_down") var direction = (self.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized() # Handle Sprint if Input.is_action_just_pressed("sprint") and !is_on_floor(): if air_dash > 0: velocity.x += direction.x * DASH_SPEED velocity.z += direction.z * DASH_SPEED air_dash -= 1 print("AIR DASH " +str(air_dash)) #walking if is_on_floor() and !is_climbing: if direction: velocity.x = lerp(velocity.x, direction.x * speed, delta * WALK_TRANSITION_SPEED) velocity.z = lerp(velocity.z, direction.z * speed, delta * WALK_TRANSITION_SPEED) else: velocity.x = lerp(velocity.x, direction.x * speed, delta * 6.5) + (direction.x * DASH_SPEED) velocity.z = lerp(velocity.z, direction.z * speed, delta * 6.5) + (direction.z * DASH_SPEED) #ladder movement elif is_climbing: gravity = 0.0 if direction: velocity.y = -direction.z * speed * .75 velocity.x = direction.x * speed * .3 else: velocity.y = lerp(velocity.y, -direction.z * speed, delta * 6.5) velocity.x = lerp(velocity.x, direction.x * speed, delta * 6.5) #movement in air else: velocity.x = lerp(velocity.x, direction.x * speed, delta * 6.5) velocity.z = lerp(velocity.z, direction.z * speed, delta * 6.5) # Head bob t_bob += delta * velocity.length() * float(is_on_floor()) camera.transform.origin = _headbob(t_bob) # FOV var velocity_clamped = clamp(velocity.length(), 0.5, SPRINT_SPEED) var target_fov = BASE_FOV + FOV_CHANGE * velocity_clamped camera.fov = lerp(camera.fov, target_fov, delta * 8) # Health Indicator var health_opacity = 1.5 - level_control.health / level_control.start_health if level_control.health < (level_control.start_health/2): health_indicator.color = lerp(Color(0.471, 0, 0, 0), Color(0.471, 0, 0, .25),health_opacity) # Moving Fast Sound #initiate fast movement -- may be done with this block #change sounds with speed if moving_fast: var wind_volume = clamp( velocity.length() - 20 ,-80,10) ear_wind.volume_db = wind_volume #reset at apex #cache fastest speed if abs(velocity.y) > moving_fast_top_speed: moving_fast_top_speed = abs(velocity.y) #Land Sound if velocity.y < .1 and self.is_on_floor() and moving_fast == true: var land_volume = clamp( moving_fast_top_speed - 20 ,-10,0) var recoil_amount = Vector3(-.3,0,0) land_sound.volume_db = land_volume land_sound.play() recoil.add_recoil(recoil_amount,10,2) moving_fast_top_speed = 0.0 moving_fast = false # Game Speed if !level_control.paused: if Input.is_action_pressed("slow_down") and remaining_stamina > 0 : Engine.time_scale = lerp(Engine.time_scale, SLOWSPEED, delta * 20) gun.random_spread_amt = 0 AudioServer.set_bus_effect_enabled(0,0,true) camera.fov = lerp(camera.fov, camera.fov * gun.fov_zoom_amt, delta * 100) if sensitivity_shift == true: SENSITIVITY = lerp(SENSITIVITY, SENSITIVITY * .998, delta * 100) if remaining_stamina > 0: remaining_stamina = clamp(remaining_stamina - (delta * STAMINA_DRAIN),0,MAX_STAMINA) else: Engine.time_scale = lerp(Engine.time_scale, 1.0, delta * 50) gun.random_spread_amt = gun.random_spread_start AudioServer.set_bus_effect_enabled(0,0,false) if sensitivity_shift == true: camera.fov = lerp(camera.fov, BASE_FOV, delta * .5) SENSITIVITY = start_sensitivity if remaining_stamina < MAX_STAMINA and !Input.is_action_pressed("slow_down"): remaining_stamina = clamp(remaining_stamina + (delta * STAMINA_DRAIN/10), 0, MAX_STAMINA) # Reloading if Input.is_action_just_pressed("reload"): gun.reload() if Input.is_action_pressed("inspect") and !gun.anim_player.is_playing(): gun.anim_player.play("inspect") if Input.is_action_just_pressed("flashlight"): flashlight_audio.play() if flashlight.light_energy == 0: while flashlight.light_energy != FLASHLIGHT_BRIGHTNESS: flashlight.light_energy = FLASHLIGHT_BRIGHTNESS elif flashlight.light_energy > 0: flashlight.light_energy = 0 # Shooting & fire modes if Input.is_action_pressed("shoot"): gun.shoot(delta) if Input.is_action_just_released("shoot"): gun.cycle_count = gun.cycle_count_start # Gun folding out of the way if gun_ray.is_colliding(): weapon_holder.rotation = lerp(weapon_holder.rotation, Vector3(2, -1, -1), delta * 7) weapon_holder.position = lerp(weapon_holder.position, Vector3(-.02,-.1,.06),(delta * 7)) gun_folded = true elif !gun_ray.is_colliding(): weapon_holder.rotation = lerp(weapon_holder.rotation, weapon_holder_start_rot,delta*7) weapon_holder.position = lerp(weapon_holder.position, weapon_holder_start_pos,delta*7) gun_folded = false #Weapon Swap Up if Input.is_action_just_pressed("scroll_up") and !gun.anim_player.is_playing(): if held_item == null: if level_control.held_guns.size() > 1: gun.anim_player.play("swap_out") level_control.gun_spawn(level_control.current_gun_index + 1) else: held_item_rotation.y = clamp(held_item_rotation.y + deg_to_rad(45), deg_to_rad(0),deg_to_rad(360)) #Weapon Swap Down if Input.is_action_just_pressed("scroll_down") and !gun.anim_player.is_playing(): if held_item == null: if level_control.held_guns.size() > 1: gun.anim_player.play("swap_out") level_control.gun_spawn(level_control.current_gun_index - 1) else: held_item_rotation.y = clamp(held_item_rotation.y - deg_to_rad(45), deg_to_rad(0),deg_to_rad(360)) for i in range(10): if Input.is_action_just_pressed("numb_" + str(i)): weapon_select((i - 1)) #interact button if Input.is_action_just_pressed("interact"): if held_item != null: release_moveable() elif interact_ray.is_colliding(): var body = interact_ray.get_collider() if interact_ray.get_collider().is_in_group("interact"): body.get_parent().interact() if interact_ray.get_collider().is_in_group("moveable"): grab_moveable(body) #kick if Input.is_action_just_pressed("kick"): if interact_ray.is_colliding(): if interact_ray.get_collider().is_in_group("scene_rigidbody"): kick_audio.play() interact_ray.get_collider().linear_velocity += interact_ray.global_transform.basis * Vector3(0,0, -KICK_AMOUNT) if held_item != null: release_moveable() elif held_item != null: kick_audio.play() held_item.linear_velocity += interact_ray.global_transform.basis * Vector3(0,0, -KICK_AMOUNT) release_moveable() if level_control.health <= 0: level_control.die() hold_item() move_and_slide() weapon_tilt(input_dir.x, delta) weapon_sway(delta) weapon_bob(velocity.length(), delta) func _headbob(time) -> Vector3: var pos = Vector3.ZERO pos.y = sin(time * BOB_FREQ) * BOB_AMP pos.x = cos(time * BOB_FREQ / 2) * BOB_AMP return pos func _on_pick_up_detection_body_entered(body): if body.is_in_group("pickup"): if body.pickupable: body.picked_up() if body.is_in_group("weapon_pickup"): weapon_pickup_audio.play() func ladder_collide(is_climbing): if is_climbing == true: gravity = 0.0 else: gravity = default_gravity func _on_pick_up_magnet_body_entered(body): if body.is_in_group("pickup") and body.is_in_group("magnet"): body.player_follow = self body.collision_shape.disabled = true func weapon_tilt(input_x, delta): if weapon_holder: weapon_holder.rotation.z = lerp(weapon_holder.rotation.z, -input_x * weapon_rotation_amount * 10, 4 * delta) if camera: camera.rotation.z = lerp(camera.rotation.z, -input_x * .075, 5 * delta) func weapon_sway(delta): mouse_input = lerp(mouse_input, Vector2.ZERO, 10 * delta) weapon_holder.rotation.x = lerp(weapon_holder.rotation.x, mouse_input.y * weapon_sway_amount, 5 * delta) weapon_holder.rotation.y = lerp(weapon_holder.rotation.y, mouse_input.x * weapon_sway_amount, 5 * delta) func weapon_bob(vel : float, delta): if weapon_holder: if vel > 2 and is_on_floor(): var bob_amount : float = 0.05 var bob_freq : float = 0.01 weapon_holder.position.y = lerp(weapon_holder.position.y, def_weapon_holder_pos.y + sin(Time.get_ticks_msec() * bob_freq) * bob_amount, 10 * delta) weapon_holder.position.x = lerp(weapon_holder.position.x, def_weapon_holder_pos.x + sin(Time.get_ticks_msec() * bob_freq) * bob_amount, 10 * delta) else: weapon_holder.position.y = lerp(weapon_holder.position.y, def_weapon_holder_pos.y, .1 * delta) weapon_holder.position.x = lerp(weapon_holder.position.x, def_weapon_holder_pos.x, .1 * delta) func weapon_select(gun_id): if !gun.anim_player.is_playing(): if level_control.held_guns.size() >= (gun_id + 1) and level_control.current_gun_index != gun_id: gun.anim_player.play("swap_out") level_control.gun_spawn(gun_id) func enemy_hit(): var hitmarker_spawn = hitmarker.instantiate() camera.add_child(hitmarker_spawn) func toggle_hud(hud_on): if dead: crt_filter.visible = hud_on else: crosshair.visible = hud_on ammo_counter.visible = hud_on stamina_counter.visible = hud_on func grab_moveable(body): moveable_holder.global_position = body.global_position held_item = body held_item_rotation = body.rotation #cache rigidbody settings held_item_linear_damp_cache = body.linear_damp held_item_angular_damp_cache = body.angular_damp held_item_gravity_cache = body.gravity_scale held_item_mass_cache = body.mass #change rigidbody settings body.linear_damp = 0 #5 body.angular_damp = 0 #5 body.mass = 1 held_item.gravity_scale = 0 func hold_item(): # Move Held Items if held_item != null: var held_dir = moveable_holder.global_position - held_item.global_position var held_av = held_item_rotation - held_item.rotation held_item.linear_velocity = held_dir * 30 held_item.angular_velocity = held_av * 10 #break when moved too far away var distance_from_player = abs(self.global_position - held_item.global_position) if distance_from_player.length() > 5: release_moveable() if stand_check.is_colliding(): if stand_check.get_collider() == held_item: release_moveable() func release_moveable(): held_item_rotation = Vector3(0,0,0) held_item.gravity_scale = held_item_gravity_cache held_item.linear_damp = held_item_linear_damp_cache held_item.angular_damp = held_item_angular_damp_cache held_item.mass = held_item_mass_cache held_item = null func hit(damage, fired_by, target_type): level_control.health -= damage level_control.last_hit = fired_by level_control.target_type = target_type recoil.add_recoil(Vector3(.5,.1,.1),10,10) hurt_audio.play() health_indicator.color = Color(0.471, 0, 0, .25) await get_tree().create_timer(.15).timeout health_indicator.color = Color(0.471, 0, 0, 0)