extends CharacterBody3D var speed var double_jump = true var gravity = 9.8 var mouse_input : Vector2 var rng = RandomNumberGenerator.new() @export_group("Game Settings") @export var AUDIO = true @export var dead_player : Resource @export_group("Player Movement") @export var WALK_SPEED = 7.0 @export var SPRINT_SPEED = 15.0 @export var JUMP_VELOCITY = 10 @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 = .1 @export var weapon_rotation_amount : float = .1 @export_subgroup("FOV") @export var BASE_FOV : float = 80 @export var FOV_CHANGE = 1.5 @export_group("Gun") @export var gun : Node @onready var gun_ray = $Head/Camera3D/GunRay @onready var level_control = $".." @onready var interact_ray = $Head/Camera3D/InteractRay @onready var bullet_ray = $Head/Camera3D/BulletRay 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 gun_folded = false var recoiling_fade = 0 var bullet_destination var gun_fire_pitch_starting var current_weapon_index var recoiling = false # Slow Down Variables const SLOWSPEED = .2 const MAX_STAMINA = 1800 const STAMINA_DRAIN = 100 var remaining_stamina = 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/Camera3D/Crosshair @onready var head = $Head @onready var camera = $Head/Camera3D @onready var world_environment = $"../WorldEnvironment" @onready var pickup_sound = $Audio/PickupSound @onready var ear_wind = $Audio/EarWind @onready var hurt_audio = $Audio/Hurt @onready var weapon_spawner = $Head/Camera3D/WeaponHolder/WeaponSpawner @onready var pick_up_detection = $pick_up_detection func _ready(): Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) start_sensitivity = SENSITIVITY #turn off audio if unchecked in player if AUDIO == false: AudioServer.set_bus_volume_db(0,-80) func _unhandled_input(event): if event is InputEventMouseMotion: self.rotate_y(-event.relative.x * SENSITIVITY) camera.rotate_x(-event.relative.y * SENSITIVITY) camera.rotation.x = clamp(camera.rotation.x, deg_to_rad(-90), deg_to_rad(60)) mouse_input = event.relative func _physics_process(delta): # Add the gravity. if not is_on_floor(): velocity.y -= gravity * delta else: double_jump = true # Handle jump. if Input.is_action_just_pressed("jump") and is_on_floor(): velocity.y += JUMP_VELOCITY elif Input.is_action_just_pressed("jump") and double_jump == true: velocity.y += JUMP_VELOCITY double_jump = false # Handle Sprint if Input.is_action_pressed("sprint") and is_on_floor(): speed = SPRINT_SPEED else: speed = WALK_SPEED # 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() if is_on_floor(): if direction: velocity.x = direction.x * speed velocity.z = direction.z * speed else: velocity.x = lerp(velocity.x, direction.x * speed, delta * 6.5) velocity.z = lerp(velocity.z, direction.z * speed, delta * 6.5) 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 * 2) var target_fov = BASE_FOV + FOV_CHANGE * velocity_clamped camera.fov = lerp(camera.fov, target_fov, delta * 8) # Land sound # Game Speed if Input.is_action_pressed("slow_down") and remaining_stamina >0: Engine.time_scale = lerp(0, 1, SLOWSPEED) 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 -= 1000 * delta else: Engine.time_scale = 1 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: remaining_stamina += STAMINA_DRAIN * delta elif remaining_stamina > MAX_STAMINA * 1.01: remaining_stamina -= (STAMINA_DRAIN)/2 * delta # Reloading if Input.is_action_just_pressed("reload"): gun.reload(delta) if Input.is_action_pressed("inspect") and !gun.anim_player.is_playing(): gun.anim_player.play("inspect") # 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() and !gun_ray.get_collider().is_in_group("player"): #check distance to wall later ? gun.rotation = lerp(gun.rotation, Vector3(1, -1, -.25), delta * 10) gun.position = lerp(gun.position, Vector3(gun.start_position.x+.25,gun.start_position.y +.2,gun.start_position.z +.6),(delta*10)) gun_folded = true elif !gun_ray.is_colliding(): gun.rotation = lerp(gun.rotation, gun.start_rotation,delta*7) gun.position = lerp(gun.position, gun.start_position,delta*7) gun_folded = false #Weapon Swap Up if Input.is_action_just_pressed("scroll_up") and !gun.anim_player.is_playing(): if level_control.held_guns.size() > 1: gun.anim_player.play("swap_out") level_control.gun_spawn(level_control.current_gun_index + 1) #Weapon Swap Down if Input.is_action_just_pressed("scroll_down") and !gun.anim_player.is_playing(): if level_control.held_guns.size() > 1: gun.anim_player.play("swap_out") level_control.gun_spawn(level_control.current_gun_index - 1) #interact button if Input.is_action_just_pressed("interact"): if interact_ray.is_colliding(): # and !interact_ray.get_collider().is_in_group("interact"): var body = interact_ray.get_collider() body.get_parent().interact() #quit game and eventually go to menu if Input.is_action_just_pressed("escape"): get_tree().quit() if level_control.health <= 0: die() 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.pickupType == "stamina": pickupmsg = pickup_announce.instantiate() pickupmsg.pickuptext = "stamina" get_parent().add_child(pickupmsg) remaining_stamina += (body.rand_amt/100) * MAX_STAMINA pickup_sound.pitch_scale = 1 + rng.randf_range(-.3,.3) pickup_sound.play() body.queue_free() elif body.pickupType == "ammo": pickupmsg = pickup_announce.instantiate() pickupmsg.pickuptext = "ammo" get_parent().add_child(pickupmsg) level_control.ammo_reserve[level_control.current_gun_index] += int((body.rand_amt/100) * gun.max_ammo) picked_up = true picked_up_text = "ammo" pickup_sound.pitch_scale = 1 + rng.randf_range(-.3,.3) pickup_sound.play() body.queue_free() elif body.pickupType == "money": pickupmsg = pickup_announce.instantiate() pickupmsg.pickuptext = "$" + str(int(body.rand_amt)) get_parent().add_child(pickupmsg) level_control.money += int(body.rand_amt) picked_up = true picked_up_text = "ammo" pickup_sound.pitch_scale = 1 + rng.randf_range(-.3,.3) pickup_sound.play() body.queue_free() elif body.pickupType == "jump": pickupmsg = pickup_announce.instantiate() pickupmsg.pickuptext = "jump" get_parent().add_child(pickupmsg) double_jump = true picked_up = true picked_up_text = "double jump" pickup_sound.pitch_scale = 1 + rng.randf_range(-.3,.3) pickup_sound.play() body.queue_free() elif body.pickupType == "weapon": pickup_sound.pitch_scale = 1 + rng.randf_range(-.3,.3) pickup_sound.play() level_control.held_guns.append(body.gun_resource) var instance_gun = body.gun_resource.instantiate() level_control.ammo_current.append(instance_gun.max_ammo) level_control.ammo_reserve.append(instance_gun.max_ammo * instance_gun.start_mags) body.queue_free() var weapon_id = level_control.held_guns.size() - 1 gun.anim_player.play("swap_out") level_control.gun_spawn(weapon_id) func _on_pick_up_magnet_body_entered(body): if body.is_in_group("pickup") and body.is_in_group("magnet"): body.player = self func weapon_tilt(input_x, delta): if weapon_holder: weapon_holder.rotation.z = lerp(weapon_holder.rotation.z, -input_x * weapon_rotation_amount * 10, 10 * 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_rotation_amount, 5 * delta) weapon_holder.rotation.y = lerp(weapon_holder.rotation.y, mouse_input.x * weapon_rotation_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_recoil(delta): var recoil_to = camera.rotation.x + gun.recoil_amount camera.rotation.x = clamp(lerp(camera.rotation.x,recoil_to, .05), deg_to_rad(-90), deg_to_rad(60)) func die(): Engine.time_scale = .05 var deadmsg = dead_announce.instantiate() get_parent().add_child(deadmsg) await get_tree().create_timer(.3).timeout get_tree().reload_current_scene() #var instance_dead = dead_player.instantiate() #instance_dead.position = head.global_position #instance_dead.transform.basis = head.global_transform.basis #get_tree().get_root().add_child(instance_dead)