From ebc47a80ddd48672b42ce9074199ba4aa8e343fb Mon Sep 17 00:00:00 2001 From: sega Date: Thu, 27 Aug 2026 11:04:40 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20PvP=20=D0=B8=20=D1=80=D0=B0=D0=BD=D0=BD?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=B2=D1=8B=D1=85=D0=BE=D0=B4=20=D0=B8=D0=B7=20?= =?UTF-8?q?=D0=B8=D0=B3=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Реализовать матч PvP до пяти очков и рабочую панель победы. Исправить возврат подготовленной фишки и оплаты при раннем выходе. Ограничить перемещения фишки бота осмысленными кандидатами. --- Assets/Scripts/Common/GameManager.cs | 54 ++++- .../Scripts/Gameplay/BackToMenuController.cs | 13 ++ Assets/Scripts/Gameplay/Bot.cs | 164 ++++++++++----- Assets/Scripts/Gameplay/EndGameHandler.cs | 18 +- Assets/Scripts/Gameplay/Panels/PvpWinPanel.cs | 195 +++++++++--------- .../Scripts/Gameplay/Panels/RevengeButton.cs | 7 + Assets/Scripts/Gameplay/Player.cs | 12 ++ Assets/Scripts/Gameplay/TurnManager.cs | 22 +- .../Scripts/Payment/System/PaymentSystem.cs | 19 +- 9 files changed, 343 insertions(+), 161 deletions(-) diff --git a/Assets/Scripts/Common/GameManager.cs b/Assets/Scripts/Common/GameManager.cs index c9c6273..d9cb983 100644 --- a/Assets/Scripts/Common/GameManager.cs +++ b/Assets/Scripts/Common/GameManager.cs @@ -68,7 +68,10 @@ public class GameManager : MonoBehaviour public QrPanel qrPanel; public TakeOrRiskPanel takeOrRiskPanel; + private const int PvpWinningScore = 5; private bool _isLeavingGameplay; + private int _paidEntryAmount; + private bool _canRefundEntry; public void Initialize() { @@ -90,11 +93,18 @@ public class GameManager : MonoBehaviour public void TryEnterGame(GameMode mode) { + int entryPrice = PaymentSystem.GetModePrice(mode); if (!PaymentSystem.TryPayForMode(mode)) return; + + _paidEntryAmount = PaymentSystem.Freeplay ? 0 : entryPrice; + _canRefundEntry = true; SetMode(mode); _isLeavingGameplay = false; + if (mode == GameMode.pvp) + ResetPvpScores(); + if (mode == GameMode.pve) CharactersController.I.ApplyFirstCharacterColorsToBoard(); @@ -132,9 +142,11 @@ public class GameManager : MonoBehaviour if (_isLeavingGameplay) return; + RefundEntryIfAvailable(); _isLeavingGameplay = true; ControlsPerformer.Instance.SetControlable(null); VideoUtility.CrossFadeToMenu(); + turnManager.CancelPlayerPreparation(); _fieldCleaner.Clear(); if (gameMode == GameMode.pve) @@ -157,7 +169,14 @@ public class GameManager : MonoBehaviour public void SetMode(GameMode mode) => gameMode = mode; - public void DisableEarlyMenuExit() => _backToMenu.Hide(); + public void EnableEarlyMenuExit() => _backToMenu.EnableExit(); + + public void DisableEarlyMenuExit() + { + _paidEntryAmount = 0; + _canRefundEntry = false; + _backToMenu.Hide(); + } public void PerformRevenge() => ContinueAfterPveLoss(); @@ -193,6 +212,18 @@ public class GameManager : MonoBehaviour public void ContinueAfterPveDraw() => StartPveRound(CharactersController.CharacterIndex); + public void ContinueAfterPvpRound() + { + _fieldCleaner.Clear(); + StartRound(); + } + + public void RestartPvpMatch() + { + ResetPvpScores(); + ContinueAfterPvpRound(); + } + public void IncreaseScore(Team player) { if (player == Team.Player1) @@ -201,6 +232,9 @@ public class GameManager : MonoBehaviour player2score += 1; } + public bool HasPvpMatchWinner(Team player) + => player == Team.Player1 ? player1score >= PvpWinningScore : player2score >= PvpWinningScore; + public void ProcessDifficultyCounter()//вызывается каждый раз, когда приходит COIN с платы или обновились настройки { string previous = PlayerPrefs.GetString("PreviousBaseBotDifficulty", "Прохождение"); @@ -247,7 +281,7 @@ public class GameManager : MonoBehaviour timerManager.SetActiveTimer(Team.Player1); } else if (gameMode == GameMode.pvp) - DualAvatarController.Instance.ShowCharacters(); + DualAvatarController.Instance?.ShowCharacters(); VitrinaView.Instance.ActivateIfNeed(); DetermineNextTurn(switchPlayer: false); @@ -300,6 +334,22 @@ public class GameManager : MonoBehaviour OnRoundStarted?.Invoke(); } + private void RefundEntryIfAvailable() + { + if (!_canRefundEntry) + return; + + PaymentSystem.ReturnCredits(_paidEntryAmount); + _paidEntryAmount = 0; + _canRefundEntry = false; + } + + private void ResetPvpScores() + { + player1score = 0; + player2score = 0; + } + public void upgradeFeeshkaDifficulty() { diff --git a/Assets/Scripts/Gameplay/BackToMenuController.cs b/Assets/Scripts/Gameplay/BackToMenuController.cs index c370445..bdd4e17 100644 --- a/Assets/Scripts/Gameplay/BackToMenuController.cs +++ b/Assets/Scripts/Gameplay/BackToMenuController.cs @@ -9,6 +9,7 @@ public class BackToMenuController : MonoBehaviour private CanvasGroup _canvasGroup; private Tween _fadeTween; private bool _canExit; + private bool _isWaitingForPreparedChip; private void Awake() { @@ -29,6 +30,7 @@ public class BackToMenuController : MonoBehaviour _fadeTween?.Kill(); _fadeTween = null; _canExit = false; + _isWaitingForPreparedChip = false; } public void Show() @@ -36,12 +38,23 @@ public class BackToMenuController : MonoBehaviour _fadeTween?.Kill(); _fadeTween = null; _canvasGroup.alpha = 1f; + _canExit = false; + _isWaitingForPreparedChip = true; + } + + public void EnableExit() + { + if (!_isWaitingForPreparedChip) + return; + + _isWaitingForPreparedChip = false; _canExit = true; } public void Hide() { _canExit = false; + _isWaitingForPreparedChip = false; _fadeTween?.Kill(); _fadeTween = _canvasGroup .DOFade(0f, _fadeDuration) diff --git a/Assets/Scripts/Gameplay/Bot.cs b/Assets/Scripts/Gameplay/Bot.cs index 03501f8..1aa8d76 100644 --- a/Assets/Scripts/Gameplay/Bot.cs +++ b/Assets/Scripts/Gameplay/Bot.cs @@ -12,6 +12,7 @@ public class Bot : BasePlayer, IGamePlayer [SerializeField] private DifficultyPreset difficultyPreset; [SerializeField] private bool useDummyBots = false; [SerializeField] private float minThinkingTime = 1f; + [SerializeField, Min(0f)] private float _candidatePauseDuration = 0.35f; public DifficultyPreset dummyPreset; @@ -34,9 +35,10 @@ public class Bot : BasePlayer, IGamePlayer private DifficultyPreset _activeDifficultyPreset; private bool _useStrongSolver; - // Random movement - private float randomMoveTimer; - private float randomMoveInterval = 0.3f; + private int[] _thinkingCandidates; + private int _thinkingCandidateIndex; + private float _candidatePauseUntil; + private bool _isVisitingCandidates; private int droppedRow, droppedColumn; @@ -94,31 +96,35 @@ public class Bot : BasePlayer, IGamePlayer private void UpdateThinking() { - // Случайные движения (инициируют переход в Moving) - TryRandomMovement(); + if (!isThinkingComplete) + return; - // Проверяем, завершены ли вычисления и прошло ли минимальное время - if (isThinkingComplete && Time.time - thinkingStartTime >= _requiredThinkingTime) + if (!_isVisitingCandidates) { - MoveToCalculatedColumn(); + StartCandidateVisit(); + return; } + + if (Time.time < _candidatePauseUntil) + return; + + if (_thinkingCandidateIndex < _thinkingCandidates.Length - 1) + { + _thinkingCandidateIndex++; + MoveToCandidate(_thinkingCandidates[_thinkingCandidateIndex]); + return; + } + + if (Time.time - thinkingStartTime >= _requiredThinkingTime) + PerformDrop(); } private void UpdateMoving() { - // Плавное горизонтальное движение к целевой позиции (Y сохраняется) if (currentChip.MoveToLinear(stickedTargetPosition, moveSpeed)) { - // Если условия для дропа выполнены - бросаем - if (isThinkingComplete && Time.time - thinkingStartTime >= _requiredThinkingTime) - { - PerformDrop(); - } - else - { - // Иначе возвращаемся в режим раздумий - SetState(PlayerState.Thinking); - } + SetState(PlayerState.Thinking); + _candidatePauseUntil = Time.time + _candidatePauseDuration; } } @@ -137,7 +143,10 @@ public class Bot : BasePlayer, IGamePlayer isThinkingComplete = false; calculatedColumn = -1; thinkingStartTime = Time.time; - randomMoveTimer = 0f; + _thinkingCandidates = null; + _thinkingCandidateIndex = 0; + _candidatePauseUntil = 0f; + _isVisitingCandidates = false; CacheDifficulty(); StopThinking(); @@ -174,21 +183,26 @@ public class Bot : BasePlayer, IGamePlayer } } - private void MoveToCalculatedColumn() + private void StartCandidateVisit() { - if (calculatedColumn == -1) return; + _thinkingCandidates = BuildThinkingCandidates(); + _thinkingCandidateIndex = 0; + _isVisitingCandidates = true; + MoveToCandidate(_thinkingCandidates[_thinkingCandidateIndex]); + } - int targetIndex = System.Array.IndexOf(availableColumns, calculatedColumn); + private void MoveToCandidate(int column) + { + int targetIndex = System.Array.IndexOf(availableColumns, column); if (targetIndex == -1) { - Debug.LogWarning($"Column {calculatedColumn} is not available!"); + Debug.LogWarning($"Column {column} is not available!"); return; } if (selectedColumnIndex == targetIndex) { - - PerformDrop(); + _candidatePauseUntil = Time.time + _candidatePauseDuration; return; } @@ -197,6 +211,82 @@ public class Bot : BasePlayer, IGamePlayer SetState(PlayerState.Moving); } + private int[] BuildThinkingCandidates() + { + int alternativeColumn = FindAlternativeCandidate(calculatedColumn); + return alternativeColumn >= 0 + ? new[] { alternativeColumn, calculatedColumn } + : new[] { calculatedColumn }; + } + + private int FindAlternativeCandidate(int finalColumn) + { + Team[,] grid = board.GridToTeam(); + Team opponent = team == Team.Player1 ? Team.Player2 : Team.Player1; + int bestColumn = -1; + float bestScore = float.NegativeInfinity; + float boardCenter = (board.Columns - 1) * 0.5f; + + foreach (int column in availableColumns) + { + if (column == finalColumn) + continue; + + int row = board.GetLowestEmptyRow(column); + int attackScore = EvaluatePlacement(grid, row, column, team); + int blockScore = EvaluatePlacement(grid, row, column, opponent); + float centerPenalty = Mathf.Abs(column - boardCenter); + float score = attackScore * 2f + blockScore * 1.5f - centerPenalty; + + if (score > bestScore) + { + bestScore = score; + bestColumn = column; + } + } + + return bestColumn; + } + + private int EvaluatePlacement(Team[,] grid, int row, int column, Team evaluatedTeam) + { + grid[row, column] = evaluatedTeam; + + int longestLine = Mathf.Max( + GetLineLength(grid, row, column, evaluatedTeam, 1, 0), + GetLineLength(grid, row, column, evaluatedTeam, 0, 1), + GetLineLength(grid, row, column, evaluatedTeam, 1, 1), + GetLineLength(grid, row, column, evaluatedTeam, 1, -1)); + + grid[row, column] = Team.None; + return longestLine >= 4 ? 100 + longestLine : longestLine * longestLine; + } + + private int GetLineLength(Team[,] grid, int row, int column, Team evaluatedTeam, int rowStep, int columnStep) + { + return 1 + + CountDirection(grid, row, column, evaluatedTeam, rowStep, columnStep) + + CountDirection(grid, row, column, evaluatedTeam, -rowStep, -columnStep); + } + + private int CountDirection(Team[,] grid, int row, int column, Team evaluatedTeam, int rowStep, int columnStep) + { + int count = 0; + int currentRow = row + rowStep; + int currentColumn = column + columnStep; + + while (currentRow >= 0 && currentRow < grid.GetLength(0) && + currentColumn >= 0 && currentColumn < grid.GetLength(1) && + grid[currentRow, currentColumn] == evaluatedTeam) + { + count++; + currentRow += rowStep; + currentColumn += columnStep; + } + + return count; + } + private void PerformDrop() { dropTargetPosition = board.GetTargetPosition(SelectedColumn); @@ -208,27 +298,6 @@ public class Bot : BasePlayer, IGamePlayer SetState(PlayerState.Dropping); } - private void TryRandomMovement() - { - if (availableColumns == null || availableColumns.Length <= 1) return; - - randomMoveTimer += Time.deltaTime; - if (randomMoveTimer >= randomMoveInterval) - { - randomMoveTimer = 0f; - int direction = Random.Range(0, 2) * 2 - 1; - int newIndex = Mathf.Clamp(selectedColumnIndex + direction, 0, availableColumns.Length - 1); - - if (newIndex != selectedColumnIndex) - { - selectedColumnIndex = newIndex; - // Сохраняем текущий Y, меняем только X - UpdateTargetPositionX(board.GetStickedPosition(SelectedColumn).x); - SetState(PlayerState.Moving); - } - } - } - private void CacheAvailableColumns() { availableColumns = board.GetNonFullColumns(); @@ -276,6 +345,7 @@ public class Bot : BasePlayer, IGamePlayer { // Начальная позиция – сверху с оффсетом stickedTargetPosition = board.GetMiddleStickedPosition(); + stickedTargetPosition.x = board.GetStickedPosition(SelectedColumn).x; currentChip.transform.position = stickedTargetPosition + Vector2.up * 5f; } diff --git a/Assets/Scripts/Gameplay/EndGameHandler.cs b/Assets/Scripts/Gameplay/EndGameHandler.cs index daee5ec..7fccd06 100644 --- a/Assets/Scripts/Gameplay/EndGameHandler.cs +++ b/Assets/Scripts/Gameplay/EndGameHandler.cs @@ -30,7 +30,13 @@ public class EndGameHandler : MonoBehaviour } else { - _pvpWinPanel.Open(); + GameManager gameManager = GameManager.Instance; + _pvpWinPanel.Open( + Team.None, + false, + gameManager.player1score, + gameManager.player2score, + gameManager.ContinueAfterPvpRound); } } @@ -54,7 +60,15 @@ public class EndGameHandler : MonoBehaviour } else { - _pvpWinPanel.Open(); + GameManager gameManager = GameManager.Instance; + gameManager.IncreaseScore(winner); + bool isMatchWin = gameManager.HasPvpMatchWinner(winner); + _pvpWinPanel.Open( + winner, + isMatchWin, + gameManager.player1score, + gameManager.player2score, + isMatchWin ? gameManager.RestartPvpMatch : gameManager.ContinueAfterPvpRound); } } diff --git a/Assets/Scripts/Gameplay/Panels/PvpWinPanel.cs b/Assets/Scripts/Gameplay/Panels/PvpWinPanel.cs index ca89d7d..b18499b 100644 --- a/Assets/Scripts/Gameplay/Panels/PvpWinPanel.cs +++ b/Assets/Scripts/Gameplay/Panels/PvpWinPanel.cs @@ -1,156 +1,159 @@ -using DG.Tweening; +using DG.Tweening; +using System; using System.Collections.Generic; using TMPro; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; public class PvpWinPanel : MonoBehaviour, IControlable { - [SerializeField] private RectTransform panel; - [SerializeField] private float startPanelX, shownPanelX, hiddenPanelX; - [SerializeField] private Transform description; - [SerializeField] private RevengeButton revengeButton; - [SerializeField] private GotoMenuButton menuButton; - [SerializeField] private Image background; - [SerializeField] private Color backgroundEnabledColor, backgroundDisabledColor; + [SerializeField, FormerlySerializedAs("panel")] private RectTransform _panel; + [SerializeField, FormerlySerializedAs("startPanelX")] private float _startPanelX; + [SerializeField, FormerlySerializedAs("shownPanelX")] private float _shownPanelX; + [SerializeField, FormerlySerializedAs("hiddenPanelX")] private float _hiddenPanelX; + [SerializeField, FormerlySerializedAs("description")] private Transform _description; + [SerializeField, FormerlySerializedAs("revengeButton")] private RevengeButton _restartButton; + [SerializeField, FormerlySerializedAs("menuButton")] private GotoMenuButton _menuButton; + [SerializeField, FormerlySerializedAs("background")] private Image _background; + [SerializeField, FormerlySerializedAs("backgroundEnabledColor")] private Color _backgroundEnabledColor; + [SerializeField, FormerlySerializedAs("backgroundDisabledColor")] private Color _backgroundDisabledColor; + [SerializeField, FormerlySerializedAs("elasticCurve")] private AnimationCurve _elasticCurve; + [SerializeField, FormerlySerializedAs("buttons")] private List _buttons; - [SerializeField] private AnimationCurve elasticCurve; private Sequence _animation; - - [Header("Работа с кнопками")] - [SerializeField] private List buttons; - private int buttonIndex; + private int _buttonIndex; + private Action _restartAction; public bool ControlsEnabled { get; set; } - + private void OnDisable() + { + _animation?.Kill(); + _animation = null; + _restartAction = null; + ControlsEnabled = false; + } public void UpdateControls() { int direction = In.GetAxis(JoystickSide.Both, InteractMode.Discrete).x; + int newButtonIndex = Mathf.Clamp(_buttonIndex + direction, 0, _buttons.Count - 1); - int newButtonIndex = Mathf.Clamp(buttonIndex + direction, 0, buttons.Count); - bool indexChanged = newButtonIndex != buttonIndex; - if (indexChanged) + if (newButtonIndex != _buttonIndex) { - buttonIndex = newButtonIndex; + _buttonIndex = newButtonIndex; RefreshButtons(); } - //interact if (In.GetAction(JoystickSide.Both, InteractMode.Discrete)) - { - var selectedButton = buttons[buttonIndex]; - selectedButton.Interact(); - } + Close(); } - public void Open() + public void Open(Team winner, bool isMatchWin, int player1Score, int player2Score, Action restartAction) { - if (_animation != null) - { - _animation.Kill(); - } + _animation?.Kill(); + _restartAction = restartAction; + Initialize(winner, isMatchWin, player1Score, player2Score); - Initialize(); _animation = GetOpenSequence(); - _animation.onComplete += () => + _animation.OnComplete(() => { ControlsEnabled = true; RefreshButtons(); - }; + }); _animation.PlayForward(); } public void Close() { - if (_animation != null) - { - _animation.Kill(); - } + ControlsEnabled = false; + ControlsPerformer.Instance.SetControlable(null); + int selectedButtonIndex = _buttonIndex; + _animation?.Kill(); _animation = GetCloseSequence(); - _animation.onComplete += () => - { - ControlsEnabled = false; - revengeButton.Select(); - menuButton.Deselect(); - }; + _animation.OnComplete(() => CompleteClose(selectedButtonIndex)); _animation.PlayForward(); } - private Sequence GetCloseSequence() + private void Initialize(Team winner, bool isMatchWin, int player1Score, int player2Score) { - Sequence seq = DOTween.Sequence(); + ControlsEnabled = false; + gameObject.SetActive(true); + ControlsPerformer.Instance.SetControlable(this); - foreach (var button in buttons) - { - seq.Join(button.transform.DOScale(0, 0.4f).SetEase(Ease.InBack)); - } + _panel.anchoredPosition = new Vector2(_startPanelX, _panel.anchoredPosition.y); + _description.localScale = Vector3.zero; + _description.localRotation = Quaternion.identity; + _description.GetComponent().text = GetDescription(winner, isMatchWin, player1Score, player2Score); - seq.Append(panel.DOAnchorPosX(hiddenPanelX, 1f).SetEase(Ease.InBack)); - seq.Join(background.DOColor(backgroundDisabledColor, 2).SetEase(Ease.InCubic)); - - return seq; - } - - private void Initialize() - { - panel.anchoredPosition = new Vector2(startPanelX, panel.anchoredPosition.y); - description.localScale = Vector3.zero; - - var text = description.GetComponent(); - int winner = GameManager.Instance.currentPlayer == Team.Player1 ? 1 : 2; - text.text = $"игрок {winner} победил"; - - revengeButton.Initialize(); - - revengeButton.transform.localScale = Vector3.zero; - menuButton.transform.localScale = Vector3.zero; - - background.color = backgroundDisabledColor; - - buttonIndex = 0; + _restartButton.InitializeForFreeRestart(); + _restartButton.transform.localScale = Vector3.zero; + _menuButton.transform.localScale = Vector3.zero; + _background.color = _backgroundDisabledColor; + _buttonIndex = 0; } private Sequence GetOpenSequence() { - Sequence seq = DOTween.Sequence(); + Sequence sequence = DOTween.Sequence(); + sequence.Append(_panel.DOAnchorPosX(_shownPanelX, 1f).SetEase(Ease.OutBack)); + sequence.Join(_background.DOColor(_backgroundEnabledColor, 1f).SetEase(Ease.OutCubic)); + sequence.Append(_description.DOScale(1f, 0.5f).SetEase(_elasticCurve)); + sequence.AppendInterval(0.2f); + sequence.Append(_description.DOLocalRotate(new Vector3(0f, 0f, 2f), 0.2f)); + sequence.Append(_description.DOLocalRotate(new Vector3(0f, 0f, -2f), 0.2f)); + sequence.Append(_description.DOLocalRotate(Vector3.zero, 0.1f)); + sequence.Join(_restartButton.transform.DOScale(1.05f, 0.5f).SetEase(Ease.OutBack)); + sequence.Append(_menuButton.transform.DOScale(0.9f, 0.5f).SetEase(Ease.OutBack)); + return sequence; + } - //приезд панели - seq.Append(panel.DOAnchorPosX(shownPanelX, 1f).SetEase(Ease.OutBack)); + private Sequence GetCloseSequence() + { + Sequence sequence = DOTween.Sequence(); - //фон - seq.Join(background.DOColor(backgroundEnabledColor, 1).SetEase(Ease.OutCubic)); + foreach (PveLossButton button in _buttons) + sequence.Join(button.transform.DOScale(0f, 0.4f).SetEase(Ease.InBack)); - //текст описания - seq.Append(description.DOScale(1, 0.5f).SetEase(elasticCurve)); - seq.AppendInterval(0.2f); + sequence.Append(_panel.DOAnchorPosX(_hiddenPanelX, 1f).SetEase(Ease.InBack)); + sequence.Join(_background.DOColor(_backgroundDisabledColor, 2f).SetEase(Ease.InCubic)); + return sequence; + } - //вращение описания - float rotationDuration = 0.2f; - seq.Append(description.DOLocalRotate(new Vector3(0, 0, 2), rotationDuration)); - seq.Append(description.DOLocalRotate(new Vector3(0, 0, -2), rotationDuration)); - seq.Append(description.DOLocalRotate(new Vector3(0, 0, 0), rotationDuration / 2)); + private void CompleteClose(int selectedButtonIndex) + { + ControlsEnabled = false; + Action restartAction = _restartAction; + _restartAction = null; + gameObject.SetActive(false); - //появление кнопок - seq.Join(revengeButton.transform.DOScale(1.05f, 0.5f).SetEase(Ease.OutBack)); - seq.Append(menuButton.transform.DOScale(0.9f, 0.5f).SetEase(Ease.OutBack)); - - return seq; + if (selectedButtonIndex == 0) + restartAction?.Invoke(); + else + GameManager.Instance.GotoMenu(); } private void RefreshButtons() { - if (buttonIndex == 0) + for (int index = 0; index < _buttons.Count; index++) { - buttons[0].Select(); - buttons[1].Deselect(); - } - else if (buttonIndex == 1) - { - buttons[1].Select(); - buttons[0].Deselect(); + if (index == _buttonIndex) + _buttons[index].Select(); + else + _buttons[index].Deselect(); } } + + private string GetDescription(Team winner, bool isMatchWin, int player1Score, int player2Score) + { + if (winner == Team.None) + return $"ничья · счёт {player1Score}:{player2Score}"; + + int playerNumber = winner == Team.Player1 ? 1 : 2; + return isMatchWin + ? $"поздравляем! игрок {playerNumber} победил {player1Score}:{player2Score}" + : $"игрок {playerNumber} победил · счёт {player1Score}:{player2Score}"; + } } diff --git a/Assets/Scripts/Gameplay/Panels/RevengeButton.cs b/Assets/Scripts/Gameplay/Panels/RevengeButton.cs index ed7da9f..8f9d740 100644 --- a/Assets/Scripts/Gameplay/Panels/RevengeButton.cs +++ b/Assets/Scripts/Gameplay/Panels/RevengeButton.cs @@ -33,6 +33,13 @@ public class RevengeButton : PveLossButton saleImage.gameObject.SetActive(revengeCheaper); } + public void InitializeForFreeRestart() + { + oldPrice.gameObject.SetActive(false); + actualPrice.gameObject.SetActive(false); + saleImage.gameObject.SetActive(false); + } + public override void Interact() { if (!interactable) return; diff --git a/Assets/Scripts/Gameplay/Player.cs b/Assets/Scripts/Gameplay/Player.cs index 8634c94..68b4f4b 100644 --- a/Assets/Scripts/Gameplay/Player.cs +++ b/Assets/Scripts/Gameplay/Player.cs @@ -46,6 +46,17 @@ public class Player : BasePlayer, IGamePlayer availableColumns = null; } + public void ReturnPreparedChipToPool() + { + if (currentChip == null || state == PlayerState.Sleep || state == PlayerState.Dropping) + return; + + ChipPool.Instance.Release(currentChip); + currentChip = null; + availableColumns = null; + SetState(PlayerState.Sleep); + } + public void UpdateControls() { // Always update state machine regardless of ControlsEnabled @@ -176,6 +187,7 @@ public class Player : BasePlayer, IGamePlayer private void SetInitialChipPosition() { stickedTargetPosition = board.GetMiddleStickedPosition(); + stickedTargetPosition.x = board.GetStickedPosition(SelectedColumn).x; currentChip.transform.position = stickedTargetPosition + Vector2.up * chipYOffset; } diff --git a/Assets/Scripts/Gameplay/TurnManager.cs b/Assets/Scripts/Gameplay/TurnManager.cs index 6ecfd65..d4b842c 100644 --- a/Assets/Scripts/Gameplay/TurnManager.cs +++ b/Assets/Scripts/Gameplay/TurnManager.cs @@ -24,6 +24,14 @@ public class TurnManager : MonoBehaviour StartTurn(_currentPlayer); } + public void CancelPlayerPreparation() + { + _player1.OnMoveCommitted -= OnPlayerMoveCommitted; + _player1.OnThinkingStarted -= OnPlayerThinkingStarted; + _player1.OnChipDropped -= OnTurnCompleted; + _player1.ReturnPreparedChipToPool(); + } + private void SetupPlayers() => _currentPlayer = _player1; @@ -38,17 +46,14 @@ public class TurnManager : MonoBehaviour { _player1.OnMoveCommitted -= OnPlayerMoveCommitted; _player1.OnMoveCommitted += OnPlayerMoveCommitted; + + _player1.OnThinkingStarted -= OnPlayerThinkingStarted; + _player1.OnThinkingStarted += OnPlayerThinkingStarted; } if (_gameMode == GameMode.pve) { GameManager.Instance.turnTracker.EnableTimer(false); - - if (isPlayerOneTurn) - { - _player1.OnThinkingStarted -= OnPlayerThinkingStarted; - _player1.OnThinkingStarted += OnPlayerThinkingStarted; - } } player.OnChipDropped -= OnTurnCompleted; @@ -127,6 +132,9 @@ public class TurnManager : MonoBehaviour private void OnPlayerThinkingStarted() { _player1.OnThinkingStarted -= OnPlayerThinkingStarted; - GameManager.Instance.turnTracker.EnableTimer(true); + GameManager.Instance.EnableEarlyMenuExit(); + + if (_gameMode == GameMode.pve) + GameManager.Instance.turnTracker.EnableTimer(true); } } diff --git a/Assets/Scripts/Payment/System/PaymentSystem.cs b/Assets/Scripts/Payment/System/PaymentSystem.cs index 41f9ee8..fddf301 100644 --- a/Assets/Scripts/Payment/System/PaymentSystem.cs +++ b/Assets/Scripts/Payment/System/PaymentSystem.cs @@ -48,8 +48,7 @@ public static class PaymentSystem public static bool TryPayForMode(GameMode mode) { - int price = mode == GameMode.pve ? PricePve : PricePvp; - return TryPay(price); + return TryPay(GetModePrice(mode)); } public static void AddCredit() @@ -69,9 +68,14 @@ public static class PaymentSystem public static void AddCreditNoCounters(int amount) => ChangeBalance(amount); public static void ReturnCredits(GameMode gameMode) + => ReturnCredits(Freeplay ? 0 : GetModePrice(gameMode)); + + public static void ReturnCredits(int amount) { - int gamemodePrice = gameMode == GameMode.pvp ? PricePvp : PricePve; - ChangeBalance(gamemodePrice); + if (amount <= 0) + return; + + ChangeBalance(amount); } public static void ResetBalance() @@ -82,6 +86,8 @@ public static class PaymentSystem public static int GetRevengePrice(GameMode gameMode) => gameMode == GameMode.pve ? PriceRevengePve : PriceRevengePvP; + public static int GetModePrice(GameMode gameMode) => gameMode == GameMode.pve ? PricePve : PricePvp; + private static void ApplySettings() { Freeplay = SettingsManager.GetBool(SettingKey.FREEPLAY); @@ -135,10 +141,9 @@ public static class PaymentSystem } else { - int price = mode == GameMode.pve ? PricePve : PricePvp; - return GetPluralizedMoney(price); + return GetPluralizedMoney(GetModePrice(mode)); } } public static string GetPluralizedMoney(int amount) => $"{amount} {PluralizeUtil.GetValutaPluralized(ValutaName, amount)}"; -} \ No newline at end of file +}