extends Node3D class_name weapon @export var weapon_info : weapon_resource @export var anim_player : AnimationPlayer @export var barrel_ray : RayCast3D @export var casing_ejector : RayCast3D @export var mag_ejector : RayCast3D @export var audio_fire : AudioStreamPlayer3D @export var audio_empty : AudioStreamPlayer3D @export var audio_reload : AudioStreamPlayer3D @export_group("Barrel Smoke") @export var fire_smoke : GPUParticles3D @export var smoke_timer : Timer @export_group("Revolver") @export var casing_array : Array[MeshInstance3D] @export var bullet_array : Array[MeshInstance3D] @export var chamber : Node @export_group("Melee") @export var melee_collision_shape : Node @export_group("Tracker Gun") @export var looking_mesh : Node @onready var player = get_tree().current_scene.player # Revolver vars var revolver_chamber_rot_amt = 0 var casings_chamber_last # Tracker vars var tracker var check_track const TRACKER_ASSIGNED_COLOR: Color = Color(0, 0.853, 0, 1) const TRACKER_NULL_COLOR :Color = Color(0.743, 0.359, 0, 1) var remaining_ammo var start_position var start_rotation var cycle_count_start var cycle_count # Called when the node enters the scene tree for the first time. func _ready(): if melee_collision_shape != null: melee_collision_shape.disabled = true if weapon_info.weapon_type != 1: casings_chamber_last = weapon_info.max_ammo if weapon_info.fire_mode == 0: cycle_count_start = 1 elif weapon_info.fire_mode == 1: cycle_count_start = 1 elif weapon_info.fire_mode == 2: cycle_count_start = 3 cycle_count = cycle_count_start # Called every frame. 'delta' is the elapsed time since the previous frame. func _process(_delta): if chamber != null: var rot_amount = revolver_chamber_rot_amt * _delta * 10 chamber.rotate_object_local(Vector3(0,-1,0),deg_to_rad(rot_amount)) revolver_chamber_rot_amt -= rot_amount if Input.is_action_just_released("shoot"): cycle_count = cycle_count_start if weapon_info.weapon_type == 2: if tracker != null: weapon_info.tracker_asset.tracker_indicator_material.emission = TRACKER_ASSIGNED_COLOR if looking_mesh != null: looking_mesh.look_at(tracker.global_position) else: weapon_info.tracker_asset.tracker_indicator_material.emission = TRACKER_NULL_COLOR looking_mesh.rotation = lerp(rotation, Vector3(0,0,0), _delta * 4) if check_track: tracker_checker(_delta) func reload_finished(): match weapon_info.reload_type: 0: #MAGAZINE #calculate ammo to add from reserve --- discards ammo in previous mag var ammo_added = clamp(weapon_info.max_ammo,1,GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)]) #add to gun and remove from reserve GameGlobals.gun_ammo[weapon_info.gun_name] += ammo_added GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)] -= ammo_added 1: #REVOLVER #calculate ammo to add from reserve --- only pulls from what it is missing var ammo_needed = weapon_info.max_ammo - GameGlobals.gun_ammo[weapon_info.gun_name] var ammo_added = clamp(ammo_needed,1,GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)]) #add to gun and remove from reserve GameGlobals.gun_ammo[weapon_info.gun_name] += ammo_added GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)] -= ammo_added #make the added-back casings visible var index = 0 while index <= (ammo_added - 1): if index < bullet_array.size(): bullet_array[index].visible = true if index < casing_array.size(): casing_array[index].visible = true index += 1 func shoot(): if weapon_info.weapon_type == 0: if GameGlobals.gun_ammo[weapon_info.gun_name] > 0 and cycle_count > 0: if !anim_player.is_playing(): GameGlobals.gun_ammo[weapon_info.gun_name] -= 1 if weapon_info.fire_mode != 0: cycle_count -= 1 #audio and anims anim_player.play("shoot") elif !anim_player.is_playing() and cycle_count != 0: anim_player.play("empty") audio_empty.play() elif weapon_info.weapon_type == 1: if !anim_player.is_playing(): #audio and anims anim_player.play("shoot") vibration() player.recoil.add_recoil(Vector3(0,weapon_info.recoil_amount.y,weapon_info.recoil_amount.z),10,10) player.recoil.add_gun_recoil(weapon_info.recoil_amount.x) elif weapon_info.weapon_type == 2: if !anim_player.is_playing(): if tracker == null and Input.is_action_just_pressed("shoot"): check_track = true weapon_info.tracker_check_mesh.visible = true weapon_info.tracker_check_mesh.anim_player.play("check") if tracker != null and !check_track: anim_player.play("shoot") func reload(): match weapon_info.weapon_type: 0: match weapon_info.reload_type: 0: if GameGlobals.gun_ammo[weapon_info.gun_name] < weapon_info.max_ammo and anim_player.get_current_animation() != "reload" and GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)] > 0: anim_player.play("reload") audio_reload.play() if anim_player.is_playing() and anim_player.current_animation == "reload": if GameGlobals.gun_ammo[weapon_info.gun_name] == 0: GameGlobals.gun_ammo[weapon_info.gun_name] = 0 else: GameGlobals.gun_ammo[weapon_info.gun_name] = 1 1: if GameGlobals.gun_ammo[weapon_info.gun_name] < weapon_info.max_ammo and anim_player.get_current_animation() != "reload" and GameGlobals.ammo_reserve[str(weapon_info.bullet.ammo_type)] > 0: anim_player.play("reload") audio_reload.play() func spawn_mag(): var instance_mag = weapon_info.mag.instantiate() instance_mag.position = mag_ejector.global_position instance_mag.transform.basis = mag_ejector.global_transform.basis instance_mag.linear_velocity += transform.basis * Vector3(0, -20, 0) + player.velocity get_tree().get_root().add_child(instance_mag) func spawn_casing(): if weapon_info.casing != null and casing_ejector != null: # Casing transform var instance_casing = weapon_info.casing.instantiate() instance_casing.position = casing_ejector.global_position instance_casing.transform.basis = casing_ejector.global_transform.basis instance_casing.player_velocity = player.velocity * transform.basis get_tree().get_root().add_child(instance_casing) func spawn_revolver_casings(): if casing_array.size() > 0: var ammo_needed = weapon_info.max_ammo - GameGlobals.gun_ammo[weapon_info.gun_name] var index = 0 while index <= ammo_needed - 1: if index < bullet_array.size(): bullet_array[index].visible = false if index < casing_array.size(): casing_array[index].visible = false var instance_casing = weapon_info.casing.instantiate() instance_casing.position = casing_array[index].global_position instance_casing.random_rotation = false instance_casing.transform.basis = casing_array[index].global_transform.basis instance_casing.player_velocity = player.velocity * transform.basis instance_casing.rotation.x += deg_to_rad(90) instance_casing.linear_velocity.y = -(8 + randf_range(0,3)) get_tree().get_root().add_child(instance_casing) index += 1 func fire_tracker(): var instance_tracker = weapon_info.tracker_asset.instantiate() get_tree().current_scene.add_child(instance_tracker) if player.bullet_ray.is_colliding(): var spawn_loc = player.bullet_ray.get_collision_point() var spawn_parent = player.bullet_ray.get_collider() if spawn_parent != null: spawn_parent.add_child(instance_tracker) instance_tracker.global_transform.origin = spawn_loc instance_tracker.anim_player.play("mark") tracker = instance_tracker func tracker_checker(_delta): if player.bullet_ray.is_colliding(): var tracker_check_mesh = weapon_info.tracker_asset.instantiate() var spawn_loc = player.bullet_ray.get_collision_point() var distance_to_point = self.global_position.distance_to(spawn_loc) var scale_adjusted = distance_to_point/5 tracker_check_mesh.global_transform.origin = spawn_loc tracker_check_mesh.global_rotation = Vector3(0,0,0) tracker_check_mesh.scale = Vector3(scale_adjusted,scale_adjusted,scale_adjusted) if check_track == true and Input.is_action_just_released("shoot"): fire_tracker() check_track = false func shotgun_pellet_spawn(): SignalBus.emit_signal("shot_fired",weapon_info.crosshair_jump_amount) audio_fire.play() spawn_casing() var pellets_remaining = weapon_info.pellets_per_shot while pellets_remaining > 0: var lv_x = randf_range(-weapon_info.shotgun_spread.x,weapon_info.shotgun_spread.x) var lv_y = randf_range(-weapon_info.shotgun_spread.y,weapon_info.shotgun_spread.y) # instance bullet var instance_bullet = projectile_initialize() instance_bullet.linear_velocity += instance_bullet.transform.basis * Vector3(lv_x, lv_y, -weapon_info.bullet_speed) + player.velocity get_tree().get_root().add_child(instance_bullet) pellets_remaining -= 1 vibration() if fire_smoke != null: fire_smoke.restart() fire_smoke.emitting = true smoke_timer.start() player.recoil.add_recoil(Vector3(0,weapon_info.recoil_amount.y,weapon_info.recoil_amount.z),10,10) player.recoil.add_gun_recoil(weapon_info.recoil_amount.x) player.velocity += player.bullet_ray.global_basis * Vector3(0,0, weapon_info.kick_amount) SignalBus.emit_signal("shot_fired") func spawn_muzzle_smoke(): var instance_smoke = weapon_info.muzzle_smoke.instantiate() instance_smoke.global_transform.basis = barrel_ray.global_transform.basis add_child(instance_smoke) func projectile_initialize(): var instance_bullet = weapon_info.bullet.asset.instantiate() instance_bullet.position = player.bullet_ray.global_position #shoot bullet from real gun if gun is folded up if player.gun_folded == false: instance_bullet.transform.basis = player.bullet_ray.global_transform.basis else: instance_bullet.transform.basis = barrel_ray.global_transform.basis instance_bullet.bullet_speed = weapon_info.bullet_speed instance_bullet.player_velocity = player.velocity * transform.basis instance_bullet.bullet_damage = weapon_info.bullet_damage instance_bullet.blast_power = weapon_info.blast_power instance_bullet.bullet_force_mod = weapon_info.bullet_force_mod if weapon_info.bullet.bullet_hole != null: instance_bullet.instance_bullethole = weapon_info.bullet.bullet_hole.instantiate() instance_bullet.player_position = player.global_position return instance_bullet func bullet_fire(): SignalBus.emit_signal("shot_fired",weapon_info.crosshair_jump_amount) audio_fire.play() get_tree().current_scene.add_child(projectile_initialize()) spawn_casing() vibration() if chamber != null: revolver_chamber_rot_amt += weapon_info.chamber_rot_amount if weapon_info.smoke_enabled: spawn_muzzle_smoke() player.recoil.add_recoil(Vector3(0,weapon_info.recoil_amount.y,weapon_info.recoil_amount.z),10,10) player.recoil.add_gun_recoil(weapon_info.recoil_amount.x) SignalBus.emit_signal("shot_fired") func can_fire(): if anim_player.is_playing() and anim_player.current_animation == "reload": return false else: return true func hitscan_fire(): # Fire hitscan pass func swapped_out(): queue_free() func melee_collider_enable(): melee_collision_shape.disabled = false func melee_collider_disable(): melee_collision_shape.disabled = true func vibration(): Input.start_joy_vibration(0,weapon_info.vibration_weak_magnitude,weapon_info.vibration_strong_magnitude,weapon_info.vibration_duration)