using System.Collections; using System.Collections.Generic; using UnityEngine; public class Ship : MonoBehaviour { public Material hitMaterial; public List decks; [Header("Эффекты взрыва")] public ParticleSystem[] explosionPrefabs; public float explosionScale = 1f; public List deckCoords = new List(); [Header("Обломки")] public GameObject[] debrisPrefabs; public int debrisCount = 3; public float spawnRadius = 0.2f; public float minExplosionForce = 5f; public float maxExplosionForce = 60f; [Header("Мачты")] public Transform mastsContainer; public int mastThrowChance = 20; // % шанс выброса [SerializeField] private float mastMass = 30f; [SerializeField] private float mastDrag = 0.5f; [SerializeField] private float mastAngularDrag = 0.05f; [SerializeField] private float partsLifetime = 5f; // сек [Header("Оружие и прочее")] public Transform weaponsContainer; public Transform otherContainer; public int otherPartsCount = 10; private HashSet usedMasts = new HashSet(); private int playerId = 1; void Start() { var wobble = GetComponent(); if (wobble != null) wobble.OnSunk += ExtinguishAllDecks; var player = transform.root.GetComponent(); if (player != null) playerId = player.PlayerId; } private void ExtinguishAllDecks() { foreach (var deckObj in decks) { Deck deck = deckObj.GetComponent(); if (deck != null) deck.Extinguish(); } } public void AddDeckCoord(int x, int y) { deckCoords.Add(new Vector2Int(x, y)); } public void Destroy() { foreach (Renderer r in GetComponentsInChildren()) r.enabled = true; ShipsMuved wobble = GetComponent(); if (wobble != null) wobble.Sink(); else { Vector3 newPos = transform.localPosition; newPos.y -= 0.2f; transform.localPosition = newPos; } // место для вызова ленты } public bool IsDestroyed() { foreach (GameObject deckObj in decks) { Deck deck = deckObj.GetComponent(); if (deck != null && !deck.isHit) return false; } return true; } public ParticleSystem GetRandomExplosion() { if (explosionPrefabs != null && explosionPrefabs.Length > 0) return explosionPrefabs[Random.Range(0, explosionPrefabs.Length)]; return null; } // ========================= // ОБЛОМКИ // ========================= public void SpawnDebrisAt(Vector3 origin) { if (debrisPrefabs == null || debrisPrefabs.Length == 0) return; for (int i = 0; i < debrisCount; i++) { GameObject prefab = debrisPrefabs[Random.Range(0, debrisPrefabs.Length)]; Vector3 spawnPos = origin + Random.insideUnitSphere * spawnRadius; Quaternion spawnRot = Random.rotation; GameObject debris = Instantiate(prefab, spawnPos, spawnRot); Rigidbody rb = debris.GetComponent(); if (rb == null) rb = debris.AddComponent(); rb.isKinematic = false; rb.useGravity = true; Vector3 dir = (spawnPos - origin).normalized + Random.insideUnitSphere * 0.5f; float force = Random.Range(minExplosionForce, maxExplosionForce); rb.AddForce(dir * force, ForceMode.Impulse); Destroy(debris, 5f); } } // ========================= // МАЧТЫ // ========================= public void ThrowRandomMast() { if (mastsContainer == null || mastsContainer.childCount == 0) return; if (Random.Range(0, 100) > mastThrowChance) return; Transform mast = mastsContainer.GetChild(Random.Range(0, mastsContainer.childCount)); // Если мачта уже отрывалась/падала — пропускаем if (usedMasts.Contains(mast)) return; usedMasts.Add(mast); int mode = Random.Range(0, 2); // 0 - хаотично улетает, 1 - падение по XZ switch (mode) { case 0: // Хаотично отрываем и улетаем Rigidbody rb = mast.GetComponent(); if (rb == null) { rb = mast.gameObject.AddComponent(); rb.mass = mastMass; rb.drag = mastDrag; rb.angularDrag = mastAngularDrag; } rb.isKinematic = false; rb.useGravity = true; rb.constraints = RigidbodyConstraints.None; rb.AddForce(Vector3.up * Random.Range(15f, 55f) + Random.insideUnitSphere * 0.5f, ForceMode.Impulse); rb.AddTorque(Random.insideUnitSphere * Random.Range(1f, 3f), ForceMode.Impulse); break; case 1: // Падение в случайную сторону в плоскости XZ, но не строго по X Vector3 dirXZ; do { dirXZ = new Vector3(Random.Range(-1f, 1f), 0f, Random.Range(-1f, 1f)).normalized; } while (Vector3.Angle(dirXZ, Vector3.right) < 30f || Vector3.Angle(dirXZ, Vector3.left) < 20f); dirXZ.Normalize(); Vector3 fallAxis = Vector3.Cross(Vector3.up, dirXZ).normalized; // ось вращения перпендикулярна направлению падения StartCoroutine(RotateMastToAngle(mast, Quaternion.AngleAxis(90f, fallAxis), 1f)); break; } } private IEnumerator RotateMastToAngle(Transform mast, Quaternion localTargetRotation, float duration) { Quaternion startRot = mast.rotation; Quaternion targetRot = localTargetRotation * startRot; float t = 0f; while (t < duration) { t += Time.deltaTime; mast.rotation = Quaternion.Slerp(startRot, targetRot, t / duration); yield return null; } mast.rotation = targetRot; // чтобы точно встала на место } // ========================= // ОРУЖИЕ И ПРОЧЕЕ // ========================= private void ThrowParts(Transform container, int count, Vector3 explosionPos) { if (container == null || container.childCount == 0) return; List parts = new List(); foreach (Transform child in container) parts.Add(child); for (int i = 0; i < count && parts.Count > 0; i++) { int index = Random.Range(0, parts.Count); Transform part = parts[index]; parts.RemoveAt(index); part.SetParent(null, true); // видимость var rend = part.GetComponent(); if (rend != null) rend.enabled = true; // физика var rb = part.GetComponent(); if (rb == null) rb = part.gameObject.AddComponent(); rb.isKinematic = false; rb.useGravity = true; Vector3 dir = (part.position - explosionPos).normalized + Random.insideUnitSphere * 0.5f; rb.AddForce(dir * Random.Range(minExplosionForce, maxExplosionForce), ForceMode.Impulse); Destroy(part.gameObject, partsLifetime); } } public void ThrowWeapons(Vector3 explosionPos, int count) => ThrowParts(weaponsContainer, count, explosionPos); public void ThrowOtherParts(Vector3 explosionPos, int count) => ThrowParts(otherContainer, count, explosionPos); // ========================= // ВЗРЫВ ПАЛУБЫ // ========================= public void ExplodeDeck(Vector3 deckPos) { GetComponent()?.Shake(); SpawnDebrisAt(deckPos); ThrowWeapons(deckPos, 2); ThrowOtherParts(deckPos, 10); ThrowRandomMast(); // Эффект попадания (над палубой, чуть выше и с рандомным смещением) if (EffectManager.Instance != null) { Vector3 basePos = deckPos; // Случайное смещение: по Y от 15 до 25, по X и Z от -1 до 1 float randomY = Random.Range(15f, 25f); float randomX = Random.Range(-1f, 1f); float randomZ = Random.Range(-1f, 1f); Vector3 hitPos = basePos + new Vector3(randomX, randomY, randomZ); GameObject textFx = EffectManager.Instance.PlayHitText(hitPos); if (textFx != null) { // Масштабируем текст пропорционально взрыву textFx.transform.localScale *= explosionScale; } } ParticleSystem prefab = GetRandomExplosion(); if (prefab != null) { ParticleSystem ps = Instantiate(prefab, deckPos, Quaternion.identity); ps.transform.localScale *= explosionScale; // масштабируем тут ps.Play(); } } }