sea-battle/Assets/Scripts/GameManager.cs

1196 lines
40 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static event System.Action<int, int> OnInviteSent;
public static event System.Action <int, bool, GameMode> OnReady;
public event System.Action<InputMode> OnInputModeChanged;
private IBotAI bot;
public Transform shipPlacementCameraPosition;
public Transform mainMenuCameraPosition;
public Transform gameplayCameraPosition;
[Tooltip("Центр вращения камеры во время расстановки кораблей (пустой GO у поля игрока)")]
[SerializeField] private Transform shipPlacementPivotPoint;
/// <summary>Исходная мировая позиция cameraPivot — восстанавливается вне режима расстановки.</summary>
private Vector3 _defaultPivotPosition;
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;
[SerializeField] float mainMenuVideoIdleDelay = 30f;
public float timerBackToMenu = 0;
private bool idleVideoRequested;
[SerializeField] private ShipPlacementTimer shipPlacementTimer;
private bool autoStartBattlePending;
[Header("Таймер бездействия в бою")]
[SerializeField] private float battleIdleTimeout = 45f;
private float battleIdleTimer = 0f;
private bool battlePauseShown = false;
/// <summary>True после EndGame() — блокирует таймер паузы и вызов StartPlayerTurn() из BotTurn().</summary>
private bool isGameEnded;
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;
[Header("Конфиг уровней (режимы игры)")]
[SerializeField] private BossLevelConfig bossLevelConfig;
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()
{
// Запоминаем исходную позицию пивота (центр карты)
if (cameraPivot != null)
_defaultPivotPosition = cameraPivot.position;
var player = GetComponentInParent<Player>();
if (player != null)
playerId = player.PlayerId;
SetState(GameState.MainMenu);
}
private void Update()
{
timerBackToMenu += Time.deltaTime;
bool idleVideoPlaying = IdleMainMenuVideoController.Instance != null &&
IdleMainMenuVideoController.Instance.IsPlaying();
if (currentState == GameState.MainMenu)
{
if (!idleVideoPlaying && timerBackToMenu >= mainMenuVideoIdleDelay)
{
IdleMainMenuVideoController.Instance?.TryStartVideo();
idleVideoRequested = true;
}
}
else
{
idleVideoRequested = false;
if (timerBackToMenu >= inactionTimer)
{
ReturnToMainMenu();
timerBackToMenu = 0;
}
}
// --- Таймер бездействия во время боя ---
if (currentState == GameState.Playing && !battlePauseShown && !isGameEnded)
{
battleIdleTimer += Time.deltaTime;
if (battleIdleTimer >= battleIdleTimeout)
{
ShowBattlePause();
}
}
}
private void OnEnable()
{
In.ButtonEvent += ResetTimer;
if (shipPlacementTimer != null)
shipPlacementTimer.TimerExpired += HandleShipPlacementTimeout;
}
private void OnDisable()
{
In.ButtonEvent -= ResetTimer;
if (shipPlacementTimer != null)
shipPlacementTimer.TimerExpired -= HandleShipPlacementTimeout;
}
private void ResetTimer()
{
ResetIdleTimer();
IdleMainMenuVideoController.Instance?.StopVideo();
}
public void ResetIdleTimer()
{
timerBackToMenu = 0;
idleVideoRequested = false;
}
// ===== Таймер бездействия в бою =====
public void ResetBattleIdleTimer()
{
battleIdleTimer = 0f;
}
public void ShowBattlePause()
{
if (battlePauseShown) return;
battlePauseShown = true;
SetInputMode(InputMode.UI);
uiManager.ShowOverlay("BattlePause", true, hideOtherOverlays: true);
}
public void HideBattlePause()
{
if (!battlePauseShown) return;
battlePauseShown = false;
battleIdleTimer = 0f;
uiManager.ShowOverlay("BattlePause", false);
// Восстанавливаем режим ввода: если ход игрока — Battle, иначе UI
SetInputMode(isPlayerTurn ? InputMode.Battle : InputMode.UI);
}
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);
TryStartBattleAfterPlacementTimeout();
}
public void AddShipPlayer()
{
playerShips++;
if (playerShips == playerField.ShipCount)
{
SetPlayerReady(true);
}
}
public void SetInputMode(InputMode newMode)
{
currentInputMode = newMode;
OnInputModeChanged?.Invoke(newMode);
if (newMode == InputMode.ShipPlacement && targetCursorInstance == null)
InitTargetCursor(true);
}
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.StartBlinkCTimes(5,0.2f);
if (playerShips <= 0)
{
Debug.Log("Игрок проиграл!");
BlinkController.LEDRedAll(playerId); // Красный LED при поражении (статический)
enemyManager.TriggerEndGameReaction(false);
EndGame();
}
else if (enemyShips <= 0)
{
Debug.Log("Игрок победил!");
BlinkController.SetLEDBaseCustomColor(playerId, 255, 215, 0); // Устанавливаем золотой как базовый
enemyManager.TriggerEndGameReaction(true);
EndGame();
}
}
public void ProcessShotResult(bool hit, bool isPlayer, bool wasShipDestroyed = false)
{
if (wasShipDestroyed)
{
BlinkController.StartBlinkATimes(playerId,3,0.2f);
// Мигание LED 3 раза при потоплении
if (!isPlayer)
{
// Бот потопил корабль игрока - красный 3 раза
BlinkController.BlinkLEDRed(playerId, 3, 0.4f);
}
else
{
// Игрок потопил корабль противника - зеленый 3 раза
BlinkController.BlinkLEDGreen(playerId, 3, 0.4f);
}
ShipDestroyed(!isPlayer);
return;
}
if (!isPlayer)
{
if (hit)
{
BlinkController.StartBlinkATimes(playerId,1);
BlinkController.BlinkLEDRed(playerId, 1, 0.6f);
enemyManager.TriggerEventReaction(DialogueEventType.BotHit);
}
else
{
SoundManager.Instance.PlayMiss(playerId);
enemyManager.TriggerEventReaction(DialogueEventType.BotMiss);
}
}
else
{
if (hit)
{
BlinkController.StartBlinkATimes(playerId,1);
BlinkController.BlinkLEDGreen(playerId, 1, 0.6f);
enemyManager.TriggerEventReaction(DialogueEventType.PlayerHit);
}
else
{
SoundManager.Instance.PlayMiss(playerId);
enemyManager.TriggerEventReaction(DialogueEventType.PlayerMiss);
}
}
}
public void SetState(GameState newState)
{
currentState = newState;
// Сброс таймера бездействия в бою при выходе из боя
if (newState != GameState.Playing)
{
battleIdleTimer = 0f;
battlePauseShown = false;
}
if (newState != GameState.ShipPlacement)
{
autoStartBattlePending = false;
shipPlacementTimer?.StopTimer();
}
// UI: просто показываем панель по имени state (без switch)
uiManager.ShowById(newState.ToString());
// LED подсветка в зависимости от состояния
if (newState == GameState.MainMenu)
{
// В главном меню: канал 1 - волна, канал 2 - радуга
BlinkController.LEDWave(1, playerId); // Канал 1 -> волна (команда 8111)
BlinkController.LEDRainbow(2, playerId); // Канал 2 -> радуга (команда 8209)
}
// Камера — оставляем switch только для 3 точек
switch (newState)
{
case GameState.MainMenu:
MoveCameraTo(mainMenuCameraPosition);
break;
case GameState.ShipPlacement:
MoveCameraTo(shipPlacementCameraPosition);
break;
case GameState.Playing:
MoveCameraTo(gameplayCameraPosition);
break;
}
}
public void StartShipPlacementCountdown()
{
autoStartBattlePending = false;
shipPlacementTimer?.StartTimer();
}
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)
{
// Счётчик НЕ сбрасываем — он сбросится только после победы игрока.
// Пока не победил — каждая игра остаётся шанс-игрой (Level5).
Debug.Log("Шанс на победу! (сбросится при победе)");
return BotDifficulty.Level5; // даем шанс победить
}
else
{
DifficultyManager.DecreaseChance();
return BotDifficulty.Level6; // почти непобедимый
}
}
return (BotDifficulty)level;
}
public void PrepareForBattle()
{
if (playerShips != playerField.ShipCount)
{
playerField.ArrangeShip();
playerShips = playerField.ShipCount;
}
if (targetCursorInstance != null)
{
Destroy(targetCursorInstance);
targetCursorInstance = null;
}
SetInputMode(InputMode.Battle);
InitTargetCursor();
// Устанавливаем синий цвет LED при входе в бой
BlinkController.SetLEDBaseColor(playerId, 5); // 5 = синий
}
public void StartBattle()
{
Debug.Log("Игровой этап начат");
autoStartBattlePending = false;
shipPlacementTimer?.StopTimer();
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 = opponentManager.playerField.ShipCount;
}
if (!opponentManager.enemyField.IsReady)
{
opponentManager.enemyField.CopyShipsFrom(playerField, true);
opponentManager.enemyShips = playerField.ShipCount;
}
paidFroFriend = false;
opponentManager.paidFroFriend = false;
opponentManager.uiManager.ShowTimer(false);
isReady = false;
opponentManager.isReady = false;
}
else // бот
{
PrepareForBattle();
enemyShips = enemyField.ShipCount;
enemyField.ArrangeShip(true);
SetState(GameState.Playing);
}
StartPlayerTurn();
}
public void InitTargetCursor(bool isPlacingShips = false)
{
if (targetCursorInstance != null)
return;
targetCursorInstance = Instantiate(targetCursorPrefab);
TargetCursor cursorScript = targetCursorInstance.GetComponent<TargetCursor>();
cursorScript.Initialize(this, playerId, isPlacingShips);
targetCursorInstance.SetActive(isPlacingShips); // по дефолту скрыт
}
public void EndGame()
{
isGameEnded = true;
SetInputMode(InputMode.UI);
autoStartBattlePending = false;
shipPlacementTimer?.StopTimer();
StartCoroutine(EndGameSequence());
}
private IEnumerator EndGameSequence()
{
bool isWin = enemyShips <= 0;
bool isBotGame = (gameMode == GameMode.PlayerVsBot);
// Задержка перед показом GameOver экрана
yield return new WaitForSeconds(0.5f);
// Определяем приз ДО повышения уровня
Prize pendingPrize = null;
bool isAtFinalBoss = (currentBotLevel >= 4);
if (gameMode == GameMode.PlayerVsBot && isWin)
{
Prize priz = PrizeSerializer.GetPrizes()[currentBotLevel];
Debug.Log("Приз :" + priz);
bool settingEnabled = SettingsManager.instance.GetSettingValue("Выдача призов при игре с ботом") == "Выдавать";
if (settingEnabled && priz.PrizeType != PrizeType.None)
pendingPrize = priz;
// Повышаем уровень бота ПЕРЕД выдачей приза.
if (currentBotLevel < 4)
currentBotLevel++;
else
DifficultyManager.ResetChance();
}
// Показываем GameOver; если есть приз и не финальный босс — кнопка "Меню" станет "Забрать приз"
bool showClaimPrize = (pendingPrize != null && !isAtFinalBoss);
uiManager.ShowGameOverMenu(isWin, isBotGame, showClaimPrize ? pendingPrize : null);
// Финальный босс — приз выдаём сразу как раньше (кнопки "Продолжить" нет)
if (pendingPrize != null && isAtFinalBoss)
{
yield return new WaitForSeconds(1.5f);
if (pendingPrize.PrizeType == PrizeType.Special)
{
uiManager.ShowOverlayAnimated("QRCode", 0f);
yield break;
}
if (pendingPrize.PrizeType == PrizeType.Showcase)
{
uiManager.ShowOverlayAnimated("PrizeSelection", 0f);
yield break;
}
uiManager.ShowOverlayAnimated("PrizeReminder", 0f);
PrizeSystem.GivePrize(pendingPrize, playerId);
}
}
// ============= buttons ==========================================================================
public void OnReplayPressed() // по идеее функции разные так как нужна проверка на баланс $
{
if (gameMode == GameMode.PlayerVsBot)
{
StartBotGame(isReplay: true); // скидка за реванш обрабатывается в CreditSystem.PayBotGame
}
else
{
int price = CreditSystem.PlayerGamePrice;
if (CreditSystem.IsSharedBalance())
{
// Общий пул — платим ровно цену один раз
CreditSystem.Pay(price, 1);
}
else if (CreditSystem.GetPlayerCredits(opponentManager.playerId) >= price)
{
CreditSystem.Pay(price, playerId);
CreditSystem.Pay(price, opponentManager.playerId);
}
else
{
paidFroFriend = true;
CreditSystem.Pay(price * 2, playerId);
}
opponentManager.StartPlayerGame();
StartPlayerGame();
}
}
public void OnContinuePressed()
{
if (gameMode == GameMode.PlayerVsBot)
{
// Возврат кредита за победу только в платном режиме (следующий уровень бесплатный)
bool isFreeMode = SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл";
if (!isFreeMode)
{
CreditSystem.AddCredit(CreditSystem.BotGamePrice, playerId);
}
StartBotGame();
}
}
public void MenuGame(bool endGame = false)
{
bool isFreeMode = SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл";
if (!endGame && !isFreeMode)
{
if (gameMode == GameMode.PlayerVsPlayer)
{
int price = CreditSystem.PlayerGamePrice;
if (CreditSystem.IsSharedBalance())
{
// Общий пул — возвращаем ровно цену один раз
CreditSystem.AddCredit(price, 1);
}
else if (paidFroFriend) CreditSystem.AddCredit(price * 2, playerId);
else if (opponentManager.paidFroFriend) CreditSystem.AddCredit(price * 2, opponentManager.playerId);
else
{
CreditSystem.AddCredit(price, playerId);
CreditSystem.AddCredit(price, opponentManager.playerId);
}
opponentManager.ReturnToMainMenu();
}
else
{
// Бот: рефанд только если игрок ещё не выиграл ни разу (вышел с первой игры)
if (currentBotLevel == 0) CreditSystem.AddCredit(CreditSystem.BotGamePrice, 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 = playerField.ShipCount;
SetPlayerReady(true);
Debug.Log("Поле очищено и корабли расставлены заново");
}
else
{
Debug.LogWarning("GamePole не назначен в GameManager");
}
}
public void OnPlayVsPlayerClicked(int playerId) // мистр кастыль
{
bool freeMode = SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл";
if (!freeMode)
{
int opponentId = playerId == 1 ? 2 : 1;
int price = CreditSystem.PlayerGamePrice;
bool canStart;
if (CreditSystem.IsSharedBalance())
{
// Единый пул — достаточно одной цены
canStart = CreditSystem.GetPlayerCredits(1) >= price;
}
else
{
int myCoins = CreditSystem.GetPlayerCredits(playerId);
int opponentCoins = CreditSystem.GetPlayerCredits(opponentId);
canStart = (myCoins >= price && opponentCoins >= price) || myCoins >= price * 2;
}
if (!canStart)
{
uiManager?.ShowOverlay("InsufficientFunds", true, hideOtherOverlays: false);
return;
}
}
int oppId = playerId == 1 ? 2 : 1;
OnInviteSent?.Invoke(playerId, oppId);
}
public void AcceptInvite()
{
int price = CreditSystem.PlayerGamePrice;
if (CreditSystem.IsSharedBalance())
{
// Общий пул — платим ровно цену один раз
CreditSystem.Pay(price, 1);
}
else if (CreditSystem.GetPlayerCredits(playerId) >= price)
{
CreditSystem.Pay(price, playerId);
CreditSystem.Pay(price, opponentManager.playerId);
}
else
{
opponentManager.paidFroFriend = true;
CreditSystem.Pay(price * 2, opponentManager.playerId);
}
StartPlayerGame();
opponentManager.StartPlayerGame();
}
public void DeclineInvite()
{
uiManager.HideInvite();
}
public void StartPlayerGame()
{
// PvP всегда использует классический режим 10x10
// Конфигурируем оба поля — это сбрасывает сохранённые рифы (если были в режиме "Рифы")
playerField.Configure(10, 10);
enemyField.Configure(10, 10);
PrepareNewGame();
Debug.Log("Начинаем игру с игроком");
gameMode = GameMode.PlayerVsPlayer;
if (playerId == 1)
{
SoundManager.Instance?.SetPvPMode(true);
}
SetState(GameState.ShipPlacement);
SetInputMode(InputMode.UI);
uiManager.ShowOverlay("PlacementChoice", true, hideOtherOverlays: true);
SetInputMode(InputMode.UI);
fortInitializer.FortSwitch(3);
enemyManager.SetEnemyPlayer();
}
private string _modeOverride = null;
/// <summary>Режим, применённый в текущей игре (определяется в ConfigureFields).</summary>
private string _effectiveMode = "Классика";
/// <summary>Вызывается кнопкой «Играть с ботом».
/// Если в настройках включён «Выбор режима игроком» — показывает панель выбора,
/// иначе сразу запускает игру с режимом из настроек.</summary>
public void TryStartBotGame()
{
bool playerChooses = SettingsManager.instance?.GetSettingValue("Выбор режима игроком") == "Вкл";
if (playerChooses)
{
// Проверяем баланс ДО показа панели, чтобы не открывать её зря
bool isFreeMode = SettingsManager.instance?.GetSettingValue("Бесплатная игра") == "Вкл";
if (!isFreeMode)
{
int price = CreditSystem.GetEffectiveBotPrice(isReplay: false);
int credits = CreditSystem.GetPlayerCredits(playerId);
if (credits < price)
{
uiManager?.ShowOverlay("InsufficientFunds", true, hideOtherOverlays: false);
return;
}
}
uiManager.ShowOverlay("GameModeSelect", true, hideOtherOverlays: true);
SetInputMode(InputMode.UI);
}
else
{
// Баланс проверит сам StartBotGame через PayBotGame
StartBotGame();
}
}
/// <summary>Кнопка «Классика» на панели выбора режима.</summary>
public void StartBotClassic()
{
_modeOverride = "Классика";
StartBotGame();
}
/// <summary>Кнопка «Растущие поля» на панели выбора режима.</summary>
public void StartBotGrowing()
{
_modeOverride = "Растущие поля";
StartBotGame();
}
/// <summary>Кнопка «Рифы» на панели выбора режима.</summary>
public void StartBotReefs()
{
_modeOverride = "Рифы";
StartBotGame();
}
public void StartBotGame(bool isReplay = false)
{
// Сбрасываем скидку Виндисты — начинается новая игра
VindistaController.Instance?.SetDiscountMode(false, playerId);
// Если есть оппонент, который ждёт в главном меню - закрываем его приглашение
if (opponentManager != null &&
opponentManager.currentState == GameState.MainMenu)
{
opponentManager.DeclineInvite();
}
// PayBotGame сам считает скидку если isReplay=true и она включена в настройках
if (!CreditSystem.PayBotGame(isReplay, playerId)) return;
// Конфигурируем поле под текущий режим игры и уровень босса
ConfigureFields();
PrepareNewGame();
// Рифы применяем ПОСЛЕ ClearPole (чтобы массивы не были обнулены)
ApplyModeReefs();
Debug.Log("Начинаем игру с ботом");
gameMode = GameMode.PlayerVsBot;
if (playerId == 1)
SoundManager.Instance?.SetPvPMode(false);
SetState(GameState.ShipPlacement);
SetInputMode(InputMode.UI);
// Затем показать overlay выбора расстановки
uiManager.ShowOverlay("PlacementChoice", true, hideOtherOverlays: true);
InitBot();
}
//==========================================================================================
/// <summary>
/// Читает режим игры из настроек и конфигурирует оба поля (размер + кол-во кораблей).
/// Вызывается перед PrepareNewGame() в StartBotGame.
/// </summary>
private void ConfigureFields()
{
string mode = _modeOverride ?? SettingsManager.instance?.GetSettingValue("Режим игры") ?? "Классика";
_modeOverride = null; // сбрасываем override
_effectiveMode = mode; // сохраняем для ApplyModeReefs
int fieldSize = 10;
int shipCount = 10;
if (mode != "Классика" && bossLevelConfig != null)
{
var data = bossLevelConfig.GetLevel(currentBotLevel);
if (mode == "Растущие поля")
{
fieldSize = data.fieldSize;
shipCount = data.shipCountGame1;
}
else if (mode == "Рифы")
{
fieldSize = 10;
shipCount = data.shipCountGame2;
}
}
playerField.Configure(fieldSize, shipCount);
enemyField.Configure(fieldSize, shipCount);
Debug.Log($"[GameManager] Режим игры: '{mode}', поле: {fieldSize}x{fieldSize}, кораблей: {shipCount}");
}
private void ApplyModeReefs()
{
if (bossLevelConfig == null) return;
if (_effectiveMode != "Рифы") return;
var data = bossLevelConfig.GetLevel(currentBotLevel);
playerField.ApplyRandomReefs(data.reefs);
enemyField.ApplyRandomReefs(data.reefs);
Debug.Log($"[GameManager] Рифы применены для уровня {currentBotLevel}");
}
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);
SetInputMode(InputMode.Battle);
}
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);
// Не восстанавливаем ход игрока, если игра уже завершена (EndGame был вызван из колбэка выстрела)
if (!isGameEnded)
{
// Если пауза активна — ждём её снятия, иначе StartPlayerTurn() перезатрёт InputMode.UI
if (battlePauseShown)
yield return new WaitUntil(() => !battlePauseShown);
StartPlayerTurn();
}
}
public void MoveCameraTo(Transform target)
{
if (playerCamera != null && target != null)
{
StopAllCoroutines();
StartCoroutine(MoveCameraSmoothly(target));
}
}
private IEnumerator MoveCameraSmoothly(Transform target)
{
Transform cam = playerCamera.transform;
// Отсоединяем камеру от пивота на время перемещения —
// иначе поворот пивота кнопками во время полёта камеры сдвигает её конечную позицию
cam.SetParent(null);
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;
// Сбрасываем пивот и возвращаем камеру обратно как дочерний объект
if (cameraPivot != null)
{
cameraPivot.rotation = Quaternion.identity;
// Перемещаем пивот: во время расстановки кораблей — к полю игрока,
// в остальных состояниях — обратно на дефолтную позицию
bool isShipPlacement = (target == shipPlacementCameraPosition);
if (isShipPlacement && shipPlacementPivotPoint != null)
cameraPivot.position = shipPlacementPivotPoint.position;
else
cameraPivot.position = _defaultPivotPosition;
// worldPositionStays: true — локальные координаты пересчитаются под новый пивот
cam.SetParent(cameraPivot, true);
// Фиксируем новый "дом" и синхронизируем квадрант курсора
var camCtrl = cameraPivot.GetComponent<CameraPivotController>();
if (camCtrl != null)
{
camCtrl.CaptureHomeNow();
camCtrl.TargetCursor?.SwitchButton(cameraPivot.rotation.eulerAngles);
}
}
}
/// <summary>
/// Сбрасывает игровое состояние - очищает поля, удаляет курсор, сбрасывает переменные
/// </summary>
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);
autoStartBattlePending = false;
shipPlacementTimer?.StopTimer();
Debug.Log("Сброс завершён");
}
/// <summary>
/// Подготовка к новой игре - сбрасывает состояние и переводит в состояние расстановки
/// </summary>
private void PrepareNewGame()
{
isGameEnded = false;
ResetGameState();
// Состояние будет установлено в вызывающем методе (StartPlayerGame или StartBotGame)
}
/// <summary>
/// Возврат в главное меню - сбрасывает состояние и показывает главное меню
/// </summary>
public void ReturnToMainMenu()
{
// Сбрасываем скидку Виндисты
VindistaController.Instance?.SetDiscountMode(false, playerId);
paidFroFriend = false;
currentBotLevel = 0;
ResetGameState();
if (playerId == 1)
SoundManager.Instance?.SetPvPMode(false);
idleVideoRequested = false;
SetState(GameState.MainMenu);
}
private void HandleShipPlacementTimeout()
{
autoStartBattlePending = true;
uiManager.HideAllOverlays();
if (playerShips != playerField.ShipCount)
{
playerField.ArrangeShip();
playerShips = playerField.ShipCount;
}
SetPlayerReady(true);
TryStartBattleAfterPlacementTimeout();
}
private void TryStartBattleAfterPlacementTimeout()
{
if (!autoStartBattlePending || currentState != GameState.ShipPlacement || !isReady)
return;
if (gameMode == GameMode.PlayerVsPlayer)
{
if (opponentManager == null || !opponentManager.isReady)
return;
autoStartBattlePending = false;
opponentManager.autoStartBattlePending = false;
StartBattle();
return;
}
autoStartBattlePending = false;
StartBattle();
}
}