```cs
using Unity.Netcode;
using UnityEngine;
namespace JebScripts.Game.Combat
{
public class GunController : NetworkBehaviour
{
[Header("Basic Information")]
public string gunName;
[Header("Firing Information")]
[Tooltip("Time between shots (seconds)")]
[SerializeField] private float fireRate;
[SerializeField] private float bulletSpeed;
[SerializeField] private int maxMagAmmo;
[SerializeField] private int maxReserveAmmo;
[SerializeField] private FireType fireType;
[Header("Projectiles")]
[SerializeField] private GameObject serverBulletPrefab;
[SerializeField] private GameObject clientBulletPrefab;
[Header("Muzzle")]
[SerializeField] private Transform muzzle;
private Vector3 MuzzlePosition => muzzle.position;
private Vector3 MuzzleDirection => muzzle.up;
[Header("Visuals")]
[SerializeField] private Transform visualTransform;
public bool bIsFiring = false;
private float currentFireDelay = 0f;
private NetworkVariable<int> currentMagAmmo = new NetworkVariable<int>();
private NetworkVariable<int> currentReserveAmmo = new NetworkVariable<int>();
public override void OnNetworkSpawn()
{
if (IsServer)
{
RequestFillAmmo();
}
currentMagAmmo.OnValueChanged += CurrentMagAmmoChanged;
currentReserveAmmo.OnValueChanged += CurrentReserveAmmoChanged;
}
public override void OnNetworkDespawn()
{
currentMagAmmo.OnValueChanged -= CurrentMagAmmoChanged;
currentReserveAmmo.OnValueChanged -= CurrentReserveAmmoChanged;
}
private void CurrentReserveAmmoChanged(int previousvalue, int newvalue)
{
Debug.Log($"Reserve Ammo Ammo {newvalue}");
}
private void CurrentMagAmmoChanged(int previousvalue, int newvalue)
{
Debug.Log($"Mag Ammo {newvalue}");
}
// Tick Functions
private void Update()
{
if (!IsOwner)
return;
if (currentFireDelay > 0f)
currentFireDelay -= Time.deltaTime;
fireType?.UpdateFiring(this);
}
// Shooting
public void StartShooting()
{
fireType?.StartFiring(this);
}
public void StopShooting()
{
fireType?.StopFiring(this);
}
public bool CanFire()
{
return currentFireDelay <= 0f && currentMagAmmo.Value > 0;
}
public void ResetFireDelay()
{
currentFireDelay = fireRate;
}
public void FireProjectile()
{
if (!IsOwner)
return;
Vector3 pos = MuzzlePosition;
Vector3 direction = MuzzleDirection;
SpawnProjectileServerRPC(pos, direction);
SpawnDummyProjectile(pos, direction);
}
private void SpawnDummyProjectile(Vector3 position, Vector3 direction)
{
GameObject projectileInstance = Instantiate(
clientBulletPrefab,
position,
Quaternion.identity);
projectileInstance.transform.up = direction;
if (!projectileInstance.TryGetComponent<ProjectilePropulsion>(out ProjectilePropulsion propulsion))
return;
float speed = bulletSpeed;
propulsion.AddPropulsion(speed);
}
[ServerRpc]
private void SpawnProjectileServerRPC(Vector3 position, Vector3 direction)
{
ReduceAmmo(1);
GameObject projectileInstance = Instantiate(
serverBulletPrefab,
position,
Quaternion.identity);
projectileInstance.transform.up = direction;
if (projectileInstance.TryGetComponent<ProjectilePropulsion>(out ProjectilePropulsion propulsion))
{
float speed = bulletSpeed;
propulsion.AddPropulsion(speed);
}
SpawnDummyProjectileClientRPC(position, direction);
}
[ClientRpc]
private void SpawnDummyProjectileClientRPC(Vector3 position, Vector3 direction)
{
if(IsOwner)
return;
SpawnDummyProjectile(position, direction);
}
// Ammo
public void Reload()
{
RequestReloadMag();
}
public void RequestFillAmmo()
{
if (IsServer)
{
FillAmmo();
return;
}
FillAmmoServerRPC();
}
[ServerRpc]
private void FillAmmoServerRPC()
{
FillAmmo();
}
private void FillAmmo()
{
currentMagAmmo.Value = maxMagAmmo;
currentReserveAmmo.Value = maxReserveAmmo;
}
private void RequestReloadMag()
{
if (IsServer)
{
ReloadMag();
return;
}
ReloadMagServerRPC();
}
[ServerRpc]
private void ReloadMagServerRPC()
{
ReloadMag();
}
private void ReloadMag()
{
int bulletsNeeded = maxMagAmmo - currentMagAmmo.Value;
if (bulletsNeeded <= 0)
return;
int bulletsAvailable = Mathf.Min(bulletsNeeded, currentReserveAmmo.Value);
if (bulletsAvailable <= 0)
return;
currentReserveAmmo.Value -= bulletsAvailable;
currentMagAmmo.Value += bulletsAvailable;
}
private void ReduceAmmo(int ammo)
{
// Only Called on server
currentMagAmmo.Value = Mathf.Clamp(
currentMagAmmo.Value - 1,
0,
maxMagAmmo
);
}
// Visuals
public void ShowVisuals(bool show)
{
visualTransform.gameObject.SetActive(show);
}
}
}
```