using System; using System.Collections; using Code.Gameplay.Camera; using UnityEngine; using Random = UnityEngine.Random; namespace Code.Gameplay.Weapons { public abstract class WeaponBase : MonoBehaviour { [SerializeField] protected WeaponType weaponType; [SerializeField] protected string weaponName; [SerializeField] protected FireMode fireMode; [SerializeField] protected float damage = 20f; [SerializeField] protected float fireRate = 0.5f; [SerializeField] protected float bulletSpeed = 50f; [SerializeField] protected int magazineSize = 10; [SerializeField] protected int currentAmmo; [SerializeField] protected int reserveAmmo = 30; [SerializeField] protected float reloadTime = 1.5f; [SerializeField] protected float spreadAngle = 1f; public Transform _firePoint; [SerializeField] protected GameObject bulletPrefab; [SerializeField] protected ParticleSystem muzzleFlash; [SerializeField] protected AudioClip shootSound; [SerializeField] protected AudioClip reloadSound; [SerializeField] protected AudioClip emptySound; protected AudioSource audioSource; protected bool isShooting; protected bool isReloading; protected float lastShotTime; protected float lastShotTryTime; public WeaponType Type => weaponType; public int CurrentAmmo => currentAmmo; public int ReserveAmmo => reserveAmmo; public int MagazineSize => magazineSize; public bool IsReloading => isReloading; public float FireRate => fireRate; public string WeaponName => weaponName; protected virtual void Awake() { audioSource = GetComponent(); if (audioSource == null) audioSource = gameObject.AddComponent(); currentAmmo = magazineSize; } protected virtual void Start() { if (!_firePoint) _firePoint = FindFirstObjectByType().transform; } public virtual void Shoot() { if (isReloading) return; if (Time.time - lastShotTime < fireRate) return; if (currentAmmo <= 0) { PlayEmptySound(); return; } switch (fireMode) { case FireMode.SemiAutomatic: if (!isShooting) return; break; case FireMode.Automatic: break; case FireMode.BoltAction: if (!isShooting) return; StartCoroutine(BoltActionDelay()); break; default: throw new ArgumentOutOfRangeException(); } PerformShot(); } protected virtual void PerformShot() { currentAmmo--; lastShotTime = Time.time; SpawnBullet(); if (muzzleFlash != null) muzzleFlash.Play(); if (shootSound != null) audioSource.PlayOneShot(shootSound); OnShoot(); } protected virtual void SpawnBullet() { if (bulletPrefab == null) return; var spread = Random.insideUnitSphere * (spreadAngle / 100f); var direction = (_firePoint.forward + spread).normalized; var bullet = Instantiate(bulletPrefab, _firePoint.position, Quaternion.LookRotation(direction)); var bulletComponent = bullet.GetComponent(); if (bulletComponent != null) { bulletComponent.Initialize(bulletSpeed, damage, direction); } } protected virtual void OnShoot() { } public virtual void StartShooting() { isShooting = true; Shoot(); } public virtual void StopShooting() { isShooting = false; } public virtual void Reload() { if (isReloading) return; if (reserveAmmo <= 0) return; if (currentAmmo == magazineSize) return; StartCoroutine(ReloadCoroutine()); } protected virtual IEnumerator ReloadCoroutine() { isReloading = true; if (reloadSound != null) audioSource.PlayOneShot(reloadSound); yield return new WaitForSeconds(reloadTime); var ammoNeeded = magazineSize - currentAmmo; var ammoToTake = Mathf.Min(ammoNeeded, reserveAmmo); currentAmmo += ammoToTake; reserveAmmo -= ammoToTake; isReloading = false; } protected virtual void PlayEmptySound() { if (Time.time - lastShotTryTime < fireRate) return; lastShotTryTime = Time.time; if (emptySound != null) audioSource.PlayOneShot(emptySound); } public virtual void AddAmmo(int amount) { reserveAmmo += amount; } protected virtual IEnumerator BoltActionDelay() { yield return new WaitForSeconds(reloadTime); } } }