using System.Collections; using UnityEngine; using UnityEngine.InputSystem; public class PlayerGun : MonoBehaviour { [Header("Параметры стрельбы")] public float damage = 35f; // Урон за одно попадание public float fireRate = 0.25f; // Минимальная задержка между выстрелами (сек) public float maxRange = 60f; // Максимальная дистанция поражения public float noiseRadius = 30f; // Радиус шума выстрела (слепые мутанты услышат) public bool isAutomatic = false; // Одиночный или автоматический огонь [Header("Боезапас")] public int currentAmmo = 7; // Патронов в магазине сейчас public int magSize = 7; // Вместимость одного магазина public int reserveAmmo = 21; // Патронов в резерве public int maxReserveAmmo = 56; // Максимум патронов в резерве public float reloadDuration = 1.6f; // Длительность перезарядки (сек) [Header("Ссылки на точки и визуал")] public Transform muzzlePoint; // Точка вылета пули / вспышки public GameObject hitEffectPrefab; // Префаб искр / пыли при попадании public Light muzzleFlashLight; // Свет вспышки (мигает при выстреле) public Transform gunModelTransform; // Модель оружия (для анимации отдачи) [Header("Анимация и эффекты оружия (Nokobot M1911)")] public Animator gunAnimator; // Аниматор самого пистолета (затвор M1911) public Animator playerAnimator; // Аниматор персонажа (стойка, выстрел, перезарядка) public GameObject muzzleFlashPrefab; // Эффект дульной вспышки (Nokobot) public GameObject casingPrefab; // Префаб гильзы (45ACP) public Transform casingExitPoint; // Точка выброса гильзы [Header("Экипировка оружия")] [Tooltip("Находится ли оружие сейчас в руках")] public bool isGunEquipped = false; // По умолчанию убрано в кобуру (в руках кулаки) public bool hasGun = true; // Есть ли у игрока огнестрел public AudioClip equipSound; // Звук доставания оружия public AudioClip holsterSound; // Звук убирания оружия [Header("Аудио")] public AudioSource audioSource; public AudioClip shootSound; public AudioClip reloadSound; public AudioClip emptyClickSound; [Header("Состояние")] private bool isReloading = false; private float nextFireTime = 0f; private Camera mainCamera; private Vector3 originalGunLocalPos; // Свойства для UI и других скриптов public int CurrentAmmo => currentAmmo; public int ReserveAmmo => reserveAmmo; public int MagSize => magSize; public bool IsReloading => isReloading; public bool IsEquipped => isGunEquipped; public bool HasGun => hasGun; void Awake() { if (playerAnimator == null) { playerAnimator = GetComponentInChildren(); } } void Start() { mainCamera = Camera.main; if (playerAnimator == null) { playerAnimator = GetComponentInChildren(); } if (audioSource == null) { audioSource = GetComponent(); if (audioSource == null) { audioSource = gameObject.AddComponent(); audioSource.playOnAwake = false; audioSource.spatialBlend = 0.3f; } } if (gunModelTransform != null) { originalGunLocalPos = gunModelTransform.localPosition; } if (muzzleFlashLight != null) { muzzleFlashLight.enabled = false; } // Применяем начальную видимость оружия (убрано или в руках) UpdateGunVisibility(); } void Update() { if (mainCamera == null) mainCamera = Camera.main; HandleInput(); } private void HandleInput() { if (Keyboard.current == null || Mouse.current == null) return; // 1. Клавиша [1] - Убрать огнестрел (перейти на кулаки / рукопашный бой) if (Keyboard.current.digit1Key.wasPressedThisFrame) { SetEquipped(false); } // 2. Клавиша [2] - Достать огнестрел (если уже в руках — убрать как переключатель) if (Keyboard.current.digit2Key.wasPressedThisFrame) { if (hasGun) { SetEquipped(!isGunEquipped); } else { Debug.Log("[Оружие] Огнестрельное оружие еще не найдено!"); } } // 3. Клавиша [X] - Быстро убрать / достать оружие (Toggle) if (Keyboard.current.xKey.wasPressedThisFrame) { if (hasGun) { ToggleEquip(); } } // Если оружие не в руках (убрано), огонь и перезарядка блокируются! // (ЛКМ в этом случае используется скриптом PlayerAttack для рукопашного удара) if (!isGunEquipped) return; // 4. Выстрел (ЛКМ) bool shootInput = isAutomatic ? Mouse.current.leftButton.isPressed : Mouse.current.leftButton.wasPressedThisFrame; if (shootInput) { TryShoot(); } // 5. Перезарядка (Клавиша R) if (Keyboard.current.rKey.wasPressedThisFrame) { TryStartReload(); } } /// /// Переключение взятия/убирания оружия /// public void ToggleEquip() { SetEquipped(!isGunEquipped); } /// /// Взять огнестрел в руки или убрать в кобуру /// public void SetEquipped(bool equip) { if (!hasGun && equip) { Debug.Log("[Оружие] Огнестрел недоступен!"); return; } if (isGunEquipped == equip) return; // Если шла перезарядка и мы убираем оружие — прерываем перезарядку if (!equip && isReloading) { StopAllCoroutines(); isReloading = false; if (muzzleFlashLight != null) muzzleFlashLight.enabled = false; if (gunModelTransform != null) gunModelTransform.localPosition = originalGunLocalPos; } isGunEquipped = equip; UpdateGunVisibility(); // Проигрываем звук экипировки/убирания, если назначен if (audioSource != null) { if (isGunEquipped && equipSound != null) audioSource.PlayOneShot(equipSound); else if (!isGunEquipped && holsterSound != null) audioSource.PlayOneShot(holsterSound); } Debug.Log(isGunEquipped ? "[Оружие] Огнестрел взят в руки! (ЛКМ - огонь, R - перезарядка, [1]/[X] - убрать)" : "[Оружие] Огнестрел убран в кобуру. В руках кулаки (ЛКМ / F - рукопашный бой, [2] - достать оружие)"); } /// /// Включение/выключение визуала модели оружия /// public void UpdateGunVisibility() { if (gunModelTransform != null) { gunModelTransform.gameObject.SetActive(isGunEquipped); } if (playerAnimator != null) { playerAnimator.SetBool("HasGun", isGunEquipped); if (playerAnimator.layerCount > 1) { playerAnimator.SetLayerWeight(1, isGunEquipped ? 1f : 0f); } } } public void TryShoot() { if (!isGunEquipped) return; if (isReloading) return; if (Time.time < nextFireTime) return; // Если магазин пуст if (currentAmmo <= 0) { PlayEmptySound(); nextFireTime = Time.time + 0.3f; // Автоматически предложить перезарядку, если есть патроны в запасе if (reserveAmmo > 0) { TryStartReload(); } return; } // Производим выстрел nextFireTime = Time.time + fireRate; currentAmmo--; ExecuteShot(); } private void ExecuteShot() { // Звук выстрела if (audioSource != null && shootSound != null) { audioSource.PlayOneShot(shootSound); } // Вспышка и отдача StartCoroutine(MuzzleFlashRoutine()); StartCoroutine(RecoilRoutine()); // Анимация затвора пистолета M1911 (Nokobot) if (gunAnimator != null) { gunAnimator.SetTrigger("Fire"); } // Анимация персонажа (отдача при выстреле) if (playerAnimator != null) { playerAnimator.SetTrigger("Shoot"); } // Спавн визуальной дульной вспышки if (muzzleFlashPrefab != null && muzzlePoint != null) { GameObject flash = Instantiate(muzzleFlashPrefab, muzzlePoint.position, muzzlePoint.rotation); Destroy(flash, 0.4f); } // Выброс стреляной гильзы 45ACP if (casingPrefab != null && casingExitPoint != null) { GameObject casing = Instantiate(casingPrefab, casingExitPoint.position, casingExitPoint.rotation); Rigidbody rb = casing.GetComponent(); if (rb != null) { Vector3 ejectDir = casingExitPoint.right * 1.5f + casingExitPoint.up * 1.2f + Random.insideUnitSphere * 0.3f; rb.AddForce(ejectDir, ForceMode.Impulse); rb.AddTorque(Random.insideUnitSphere * 15f, ForceMode.Impulse); } Destroy(casing, 3f); } // Луч выстрела из камеры вперед через перекрестие Vector3 rayOrigin = mainCamera != null ? mainCamera.transform.position : transform.position + Vector3.up * 1.5f; Vector3 rayDir = mainCamera != null ? mainCamera.transform.forward : transform.forward; Vector3 hitPoint = rayOrigin + rayDir * maxRange; if (Physics.Raycast(rayOrigin, rayDir, out RaycastHit hit, maxRange)) { hitPoint = hit.point; // Нанесение урона врагу MutantHealth mutant = hit.collider.GetComponentInParent(); if (mutant != null) { mutant.TakeDamage(damage, transform); Debug.Log($"[Оружие] Попадание по {mutant.name}! Нанесено {damage} урона."); } // Эффект попадания if (hitEffectPrefab != null) { Instantiate(hitEffectPrefab, hit.point, Quaternion.LookRotation(hit.normal)); } } // Отрисовка трассера выстрела DrawTracer(muzzlePoint != null ? muzzlePoint.position : transform.position + transform.forward * 0.5f + Vector3.up * 1.2f, hitPoint); // Шум выстрела привлекает всех ближайших мутантов! AlertMutants(noiseRadius); // Также порождаем эхо-волну (если на игроке есть EchoTrigger / визуализатор звука) TriggerEchoWave(); } private void AlertMutants(float radius) { Collider[] colliders = Physics.OverlapSphere(transform.position, radius); foreach (Collider col in colliders) { BlindMutantAI mutant = col.GetComponentInParent(); if (mutant != null) { mutant.HearSound(transform.position); } } } private void TriggerEchoWave() { EchoTrigger echo = GetComponent(); if (echo != null) { // Вызов генерации волны эхолокации при громком выстреле echo.TriggerExternalWave(transform.position, noiseRadius, 2.5f); } } public void TryStartReload() { if (!isGunEquipped) return; // Нельзя перезаряжать убранное оружие if (isReloading) return; if (currentAmmo >= magSize) return; // Магазин уже полон if (reserveAmmo <= 0) { Debug.Log("[Оружие] Нет запасных патронов!"); return; } StartCoroutine(ReloadRoutine()); } private IEnumerator ReloadRoutine() { isReloading = true; Debug.Log("[Оружие] Перезарядка..."); if (audioSource != null && reloadSound != null) { audioSource.PlayOneShot(reloadSound); } // Анимация перезарядки персонажа if (playerAnimator != null) { playerAnimator.SetTrigger("Reload"); } yield return new WaitForSeconds(reloadDuration); int needed = magSize - currentAmmo; int toLoad = Mathf.Min(needed, reserveAmmo); currentAmmo += toLoad; reserveAmmo -= toLoad; isReloading = false; Debug.Log($"[Оружие] Перезарядка завершена! Патроны: {currentAmmo}/{reserveAmmo}"); } public bool AddAmmo(int amount) { if (amount <= 0) return false; if (reserveAmmo >= maxReserveAmmo) return false; reserveAmmo = Mathf.Min(reserveAmmo + amount, maxReserveAmmo); Debug.Log($"[Патроны] Подобрано {amount} патронов. Всего в запасе: {reserveAmmo}"); return true; } private void PlayEmptySound() { if (audioSource != null && emptyClickSound != null) { audioSource.PlayOneShot(emptyClickSound); } else { Debug.Log("[Оружие] *Сухой щелчок* Магазин пуст!"); } } private IEnumerator MuzzleFlashRoutine() { if (muzzleFlashLight != null) { muzzleFlashLight.enabled = true; yield return new WaitForSeconds(0.04f); muzzleFlashLight.enabled = false; } } private IEnumerator RecoilRoutine() { if (gunModelTransform != null) { gunModelTransform.localPosition = originalGunLocalPos - Vector3.forward * 0.08f + Vector3.up * 0.02f; float elapsed = 0f; float duration = 0.12f; while (elapsed < duration) { elapsed += Time.deltaTime; gunModelTransform.localPosition = Vector3.Lerp(gunModelTransform.localPosition, originalGunLocalPos, elapsed / duration); yield return null; } gunModelTransform.localPosition = originalGunLocalPos; } } private void DrawTracer(Vector3 start, Vector3 end) { GameObject tracer = new GameObject("BulletTracer"); LineRenderer lr = tracer.AddComponent(); lr.startWidth = 0.035f; lr.endWidth = 0.01f; lr.positionCount = 2; lr.SetPosition(0, start); lr.SetPosition(1, end); Material mat = new Material(Shader.Find("Sprites/Default")); mat.color = new Color(1f, 0.9f, 0.4f, 0.9f); lr.material = mat; Destroy(tracer, 0.06f); } }