using System; using System.Collections; using UnityEngine; public class GameManager : MonoBehaviour { public static event System.Action OnInviteSent; public static event System.Action OnReady; public event System.Action OnInputModeChanged; private IBotAI bot; public Transform shipPlacementCameraPosition; public Transform mainMenuCameraPosition; public Transform gameplayCameraPosition; public bool isReady = false; // Готов ли игрок (расставил ли корабли) public GameManager opponentManager; // Противник (если есть) public CannonBattery battery; public CannonBattery enemyBattery; public GameState currentState; public UIManager uiManager; public GamePole playerField; public GamePole enemyField; public GameObject targetCursorPrefab; private GameObject targetCursorInstance; public bool isPlayerTurn = false; public int playerShips = 0; public int enemyShips = 0; public int playerId = 1; // 1 - первый игрок, 2 - второй игрок и т.д. public Camera playerCamera; public Transform cameraPivot; public int currentBotLevel = 0; public EnemyManager enemyManager; public FortInitializer fortInitializer; [Header("Время бездействия")] [SerializeField] float inactionTimer = 360f; public float timerBackToMenu = 0; public enum InputMode { UI, Settings, ShipPlacement, Battle } private bool paidFroFriend = false; public InputMode currentInputMode = InputMode.UI; [Header("Настройки бота")] public bool useManualDifficulty = false; public BotDifficulty manualDifficulty = BotDifficulty.Level1; public enum BotDifficulty { Level1, Level2, Level3, Level4, Level5, Level6, } public GameMode gameMode = GameMode.PlayerVsBot; public BotDifficulty botDifficulty = BotDifficulty.Level1; public float cameraMoveDuration = 1.0f; // Время перемещения в секундах void Start() { var player = GetComponentInParent(); if (player != null) playerId = player.PlayerId; SetState(GameState.MainMenu); } private void Update() { timerBackToMenu += Time.deltaTime; if (timerBackToMenu >= inactionTimer) { ReturnToMainMenu(); timerBackToMenu = 0; } } private void OnEnable() { In.ButtonEvent += ResetTimer; } private void OnDisable() { In.ButtonEvent -= ResetTimer; } private void ResetTimer() { timerBackToMenu = 0; } void SetPlayerReady(bool ready) { isReady = ready; if (gameMode == GameMode.PlayerVsBot) { uiManager.SetupPlayButtons(ready); } else { var rez = isReady & opponentManager.isReady; uiManager.SetupPlayButtons(rez); opponentManager.uiManager.SetupPlayButtons(rez); } OnReady?.Invoke(playerId, ready, gameMode); } public void AddShipPlayer() { playerShips++; if (playerShips == 10) { SetPlayerReady(true); } } public void SetInputMode(InputMode newMode) { currentInputMode = newMode; OnInputModeChanged?.Invoke(newMode); } public void ShipDestroyed(bool isPlayer) { if (isPlayer) { enemyManager.TriggerEventReaction(DialogueEventType.PlayerShipDestroyed); playerShips--; } else { enemyManager.TriggerEventReaction(DialogueEventType.BotShipDestroyed); enemyShips--; } CheckVictoryCondition(); } private void CheckVictoryCondition() { BlinkController.StartBlinkATimes(playerId,5,0.2f); BlinkController.StartBlinkBTimes(playerId,5,0.2f); BlinkController.StartBlinkCTimes(5,0.2f); if (playerShips <= 0) { Debug.Log("Игрок проиграл!"); enemyManager.TriggerEndGameReaction(false); EndGame(); } else if (enemyShips <= 0) { Debug.Log("Игрок победил!"); enemyManager.TriggerEndGameReaction(true); EndGame(); } } public void ProcessShotResult(bool hit, bool isPlayer, bool wasShipDestroyed = false) { if (wasShipDestroyed) { BlinkController.StartBlinkATimes(playerId,3,0.2f); BlinkController.StartBlinkBTimes(playerId,3,0.2f); ShipDestroyed(!isPlayer); return; } if (!isPlayer) { if (hit) { BlinkController.StartBlinkATimes(playerId,1); BlinkController.StartBlinkBTimes(playerId,1); enemyManager.TriggerEventReaction(DialogueEventType.BotHit); } else { SoundManager.Instance.PlayMiss(playerId); enemyManager.TriggerEventReaction(DialogueEventType.BotMiss); } } else { if (hit) { BlinkController.StartBlinkATimes(playerId,1); BlinkController.StartBlinkBTimes(playerId,1); enemyManager.TriggerEventReaction(DialogueEventType.PlayerHit); } else { SoundManager.Instance.PlayMiss(playerId); enemyManager.TriggerEventReaction(DialogueEventType.PlayerMiss); } } } public void SetState(GameState newState) { currentState = newState; // UI: просто показываем панель по имени state (без switch) uiManager.ShowById(newState.ToString()); // Камера — оставляем switch только для 3 точек switch (newState) { case GameState.MainMenu: MoveCameraTo(mainMenuCameraPosition); break; case GameState.ShipPlacement: MoveCameraTo(shipPlacementCameraPosition); break; case GameState.Playing: MoveCameraTo(gameplayCameraPosition); break; } } private BotDifficulty GetDifficultyFromLevel() { if (useManualDifficulty) { return manualDifficulty; } int level = Mathf.Clamp(currentBotLevel, 0, 4); Debug.Log("шанс на победу : " + DifficultyManager.GetChance()); if (level == 4) // дошел до 5го уровня { // Система шанса на победу if (DifficultyManager.GetChance() <= 0) { Debug.Log("Шанс на победу сработал!"); DifficultyManager.ResetChance(); return BotDifficulty.Level5; // даем шанс победить } else { DifficultyManager.DecreaseChance(); return BotDifficulty.Level6; // почти непобедимый } } return (BotDifficulty)level; } public void PrepareForBattle() { if (playerShips != 10) { playerField.ArrangeShip(); playerShips = 10; } if (targetCursorInstance != null) { Destroy(targetCursorInstance); targetCursorInstance = null; } SetInputMode(InputMode.Battle); InitTargetCursor(); } public void StartBattle() { Debug.Log("Игровой этап начат"); if (gameMode == GameMode.PlayerVsPlayer) { if (!isReady || opponentManager == null || !opponentManager.isReady) { Debug.Log("Ожидаем готовности оппонента..."); return; } PrepareForBattle(); opponentManager.PrepareForBattle(); SetState(GameState.Playing); opponentManager.SetState(GameState.Playing); // копируем корабли на поля if (!enemyField.IsReady) { enemyField.CopyShipsFrom(opponentManager.playerField, true); enemyShips = 10; } if (!opponentManager.enemyField.IsReady) { opponentManager.enemyField.CopyShipsFrom(playerField, true); opponentManager.enemyShips = 10; } paidFroFriend = false; opponentManager.paidFroFriend = false; opponentManager.uiManager.ShowTimer(false); isReady = false; opponentManager.isReady = false; } else // бот { PrepareForBattle(); enemyShips = 10; enemyField.ArrangeShip(true); SetState(GameState.Playing); } StartPlayerTurn(); } public void InitTargetCursor(bool isPlacingShips = false) { if (targetCursorInstance != null) return; targetCursorInstance = Instantiate(targetCursorPrefab); TargetCursor cursorScript = targetCursorInstance.GetComponent(); cursorScript.Initialize(this, playerId, isPlacingShips); targetCursorInstance.SetActive(isPlacingShips); // по дефолту скрыт } public void EndGame() { SetState(GameState.GameOver); SetInputMode(InputMode.UI); bool isWin = enemyShips <= 0; bool isBotGame = (gameMode == GameMode.PlayerVsBot); if (gameMode == GameMode.PlayerVsBot) { if (isWin) { Prize priz = PrizeSerializer.GetPrizes()[currentBotLevel]; Debug.Log("Приз :"+ priz); if (priz.PrizeType == PrizeType.Special) { bool settingEnabled = SettingsManager.instance.GetSettingValue("Выдача призов при игре с ботом") == "Выдавать"; if (settingEnabled) { // Overlay Prize - не меняет состояние игры uiManager.ShowOverlay("Prize", true, hideOtherOverlays: true); return; } } { PrizeSystem.GivePrize(priz, playerId); } if (currentBotLevel <= 4) currentBotLevel++; else DifficultyManager.DecreaseChance(); } } uiManager.ShowGameOverMenu(isWin, isBotGame); } // ============= buttons ========================================================================== public void OnReplayPressed() // по идеее функции разные так как нужна проверка на баланс $ { if (gameMode == GameMode.PlayerVsBot) { StartBotGame(); } else { if (ComWrapper.GetPlayerCredits(opponentManager.playerId) > 0) { ComWrapper.Pay(1, playerId); ComWrapper.Pay(1, opponentManager.playerId); } else { paidFroFriend = true; ComWrapper.Pay(2, playerId); } opponentManager.StartPlayerGame(); StartPlayerGame(); } } public void OnContinuePressed() { if (gameMode == GameMode.PlayerVsBot) { ComWrapper.AddCredit(1, playerId); // возврат кредита за победу StartBotGame(); } } public void MenuGame(bool endGame = false) { // Возврат кредитов при выходе (если не завершилась игра) if (!endGame) { if (gameMode == GameMode.PlayerVsPlayer) { if (paidFroFriend) ComWrapper.AddCredit(2, playerId); else if(opponentManager.paidFroFriend) ComWrapper.AddCredit(2, opponentManager.playerId); else { ComWrapper.AddCredit(1, playerId); ComWrapper.AddCredit(1, opponentManager.playerId); } opponentManager.ReturnToMainMenu(); } else { if(currentBotLevel == 0) ComWrapper.AddCredit(1, playerId); // Возврат одному игроку если тот не играл } } if (gameMode == GameMode.PlayerVsPlayer)opponentManager.ReturnToMainMenu(); ReturnToMainMenu(); } public void ClearPole() { if (playerField != null) { playerField.ClearPole(); playerShips = 0; SetPlayerReady(false); Debug.Log("Поле очищено"); } } public void ArrangeGame() { if (playerField != null) { playerField.ArrangeShip(); playerShips = 10; SetPlayerReady(true); Debug.Log("Поле очищено и корабли расставлены заново"); } else { Debug.LogWarning("GamePole не назначен в GameManager"); } } public void OnPlayVsPlayerClicked(int playerId) // мистр кастыль { int opponentId = playerId == 1 ? 2 : 1; OnInviteSent?.Invoke(playerId, opponentId); } public void AcceptInvite() { if (ComWrapper.GetPlayerCredits(playerId) > 0) { ComWrapper.Pay(1, playerId); ComWrapper.Pay(1, opponentManager.playerId); } else { opponentManager.paidFroFriend = true; ComWrapper.Pay(2, opponentManager.playerId); } StartPlayerGame(); opponentManager.StartPlayerGame(); } public void DeclineInvite() { uiManager.HideInvite(); } public void StartPlayerGame() { PrepareNewGame(); Debug.Log("Начинаем игру с игроком"); gameMode = GameMode.PlayerVsPlayer; uiManager.ShowOverlay("PlacementChoice", true, hideOtherOverlays: true); SetInputMode(InputMode.UI); fortInitializer.FortSwitch(3); enemyManager.SetEnemyPlayer(); } public void StartBotGame() { // Если есть оппонент, который ждёт в главном меню - закрываем его приглашение if (opponentManager != null && opponentManager.currentState == GameState.MainMenu) { opponentManager.DeclineInvite(); } ComWrapper.Pay(1, playerId); PrepareNewGame(); Debug.Log("Начинаем игру с ботом"); gameMode = GameMode.PlayerVsBot; uiManager.ShowOverlay("PlacementChoice", true, hideOtherOverlays: true); SetInputMode(InputMode.UI); InitBot(); } //========================================================================================== private void InitBot() { BotDifficulty selectedDifficulty = GetDifficultyFromLevel(); Debug.Log("Выбран бот: " + selectedDifficulty); switch (selectedDifficulty) { case BotDifficulty.Level1: bot = new EasyBotAI(); enemyManager.SetEnemy(0); fortInitializer.FortSwitch(0); break; case BotDifficulty.Level2: bot = new NormalBotAI(); enemyManager.SetEnemy(1); fortInitializer.FortSwitch(1); break; case BotDifficulty.Level3: bot = new HardBotAI(); enemyManager.SetEnemy(2); fortInitializer.FortSwitch(2); break; case BotDifficulty.Level4: bot = new VeryHardBotAI(); enemyManager.SetEnemy(3); fortInitializer.FortSwitch(3); break; case BotDifficulty.Level5: bot = new CrazyBotAI(); enemyManager.SetEnemy(4); fortInitializer.FortSwitch(4); break; case BotDifficulty.Level6: bot = new UltimateBotAI(); enemyManager.SetEnemy(4); fortInitializer.FortSwitch(4); break; default: bot = new EasyBotAI(); enemyManager.SetEnemy(0); fortInitializer.FortSwitch(0); break; } } // ходы public void StartPlayerTurn() { isPlayerTurn = true; uiManager.ShowTimer(true); if (targetCursorInstance != null) targetCursorInstance.SetActive(true); } public void EndPlayerTurn() { isPlayerTurn = false; uiManager.ShowTimer(false); if (targetCursorInstance != null) targetCursorInstance.SetActive(false); if (gameMode == GameMode.PlayerVsPlayer) { opponentManager.StartPlayerTurn(); } else { // enemyManager.ShowNextDialogue(); StartCoroutine(BotTurn()); } } public IEnumerator BotTurn() { bool hit; do { yield return new WaitForSeconds(1f); Vector2Int target = bot.MakeMove(playerField); if (target.x < 0 || target.y < 0) { Debug.LogWarning("Боту нечего стрелять, пропускаем ход"); break; // или EndGame, если нет ходов } bool shotComplete = false; hit = false; enemyBattery.FireShot(playerField, target.x, target.y, (wasHit, wasShipDestroyed) => { hit = wasHit; shotComplete = true; ProcessShotResult(wasHit,false, wasShipDestroyed); // if (hit) SoundManager.Instance.PlayBotHit(playerId); // else SoundManager.Instance.PlayBotMiss(playerId); // if(wasShipDestroyed) SoundManager.Instance.PlayPlayerShipDestroyed(playerId); }); yield return new WaitUntil(() => shotComplete); } while (hit); StartPlayerTurn(); } public void MoveCameraTo(Transform target) { if (playerCamera != null && target != null) { StopAllCoroutines(); StartCoroutine(MoveCameraSmoothly(target)); } } private IEnumerator MoveCameraSmoothly(Transform target) { Transform cam = playerCamera.transform; Vector3 startPos = cam.position; Quaternion startRot = cam.rotation; Vector3 endPos = target.position; Quaternion endRot = target.rotation; float elapsed = 0f; while (elapsed < cameraMoveDuration) { float t = elapsed / cameraMoveDuration; cam.position = Vector3.Lerp(startPos, endPos, t); cam.rotation = Quaternion.Slerp(startRot, endRot, t); elapsed += Time.deltaTime; yield return null; } cam.position = endPos; cam.rotation = endRot; } /// /// Сбрасывает игровое состояние - очищает поля, удаляет курсор, сбрасывает переменные /// private void ResetGameState() { Debug.Log("Сброс состояния игры..."); // 1. Сбрасываем игровые поля if (playerField != null) { playerField.ClearPole(); // очистка своего поля (удаляем корабли и сбрасываем массивы) } if (enemyField != null) { enemyField.ClearPole(); // очистка вражеского поля } // 2. Удаляем курсор if (targetCursorInstance != null) { Destroy(targetCursorInstance); targetCursorInstance = null; } // 3. Сбрасываем переменные playerShips = 0; enemyShips = 0; SetPlayerReady(false); isPlayerTurn = false; SetInputMode(InputMode.UI); Debug.Log("Сброс завершён"); } /// /// Подготовка к новой игре - сбрасывает состояние и переводит в состояние расстановки /// private void PrepareNewGame() { ResetGameState(); // Состояние будет установлено в вызывающем методе (StartPlayerGame или StartBotGame) } /// /// Возврат в главное меню - сбрасывает состояние и показывает главное меню /// public void ReturnToMainMenu() { paidFroFriend = false; currentBotLevel = 0; ResetGameState(); SetState(GameState.MainMenu); } }