361 lines
11 KiB
C#
361 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Minesweeper.Config;
|
|
using Minesweeper.Core;
|
|
using Minesweeper.Presentation.Factories;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Minesweeper.Presentation.Views
|
|
{
|
|
public sealed class GameView : MonoBehaviour, IGameView
|
|
{
|
|
[SerializeField] private GameObject gameRoot;
|
|
[SerializeField] private GameObject pauseRoot;
|
|
[SerializeField] private GameObject resultRoot;
|
|
[SerializeField] private RectTransform boardPanel;
|
|
[SerializeField] private GridLayoutGroup gridLayoutGroup;
|
|
[SerializeField] private Button pauseButton;
|
|
[SerializeField] private Button restartButton;
|
|
[SerializeField] private Button resumeButton;
|
|
[SerializeField] private Button mainMenuButton;
|
|
[SerializeField] private Button resultRestartButton;
|
|
[SerializeField] private Button resultMainMenuButton;
|
|
[SerializeField] private TMP_Text resultText;
|
|
[SerializeField] private TMP_Text timerText;
|
|
[SerializeField] private TMP_Text mineText;
|
|
[SerializeField] private MinesweeperUiConfig uiConfig;
|
|
|
|
private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
|
|
private bool boardInputEnabled = true;
|
|
private float currentPixelsPerUnitMultiplier = 1f;
|
|
|
|
public event Action RestartRequested;
|
|
public event Action GoToMenuRequested;
|
|
public event Action PauseRequested;
|
|
public event Action ResumeRequested;
|
|
public event Action CellPressStarted;
|
|
public event Action CellPressEnded;
|
|
public event Action<int, int> CellOpenRequested;
|
|
public event Action<int, int> CellFlagRequested;
|
|
|
|
private void Awake()
|
|
{
|
|
if (gameRoot == null)
|
|
{
|
|
gameRoot = gameObject;
|
|
}
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
AddButtonListeners();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
RemoveButtonListeners();
|
|
}
|
|
|
|
public void ShowGame()
|
|
{
|
|
gameRoot.SetActive(true);
|
|
SetBoardRootActive(true);
|
|
}
|
|
|
|
public void HideGame()
|
|
{
|
|
gameRoot.SetActive(true);
|
|
SetBoardRootActive(false);
|
|
}
|
|
|
|
public void ShowPause()
|
|
{
|
|
if (pauseRoot != null)
|
|
{
|
|
pauseRoot.SetActive(true);
|
|
}
|
|
}
|
|
|
|
public void HidePause()
|
|
{
|
|
if (pauseRoot != null)
|
|
{
|
|
pauseRoot.SetActive(false);
|
|
}
|
|
}
|
|
|
|
public void ShowResult(GameState state)
|
|
{
|
|
if (resultRoot != null)
|
|
{
|
|
resultRoot.SetActive(true);
|
|
}
|
|
|
|
if (resultText != null)
|
|
{
|
|
resultText.text = state == GameState.Won ? "YOU WIN" : "GAME OVER";
|
|
}
|
|
}
|
|
|
|
public void HideResult()
|
|
{
|
|
if (resultRoot != null)
|
|
{
|
|
resultRoot.SetActive(false);
|
|
}
|
|
}
|
|
|
|
public void SetTimer(float seconds)
|
|
{
|
|
if (timerText != null)
|
|
{
|
|
timerText.text = Mathf.FloorToInt(seconds).ToString("00000");
|
|
}
|
|
}
|
|
|
|
public void SetMineCount(int minesCount)
|
|
{
|
|
if (mineText != null)
|
|
{
|
|
mineText.text = minesCount.ToString("00000");
|
|
}
|
|
}
|
|
|
|
public void BindConfig(MinesweeperUiConfig config)
|
|
{
|
|
uiConfig = config;
|
|
}
|
|
|
|
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory)
|
|
{
|
|
ClearBoard();
|
|
ConfigureGrid(width, height);
|
|
|
|
for (var i = 0; i < cells.Count; i++)
|
|
{
|
|
CreateCell(cells[i], cellViewFactory);
|
|
}
|
|
|
|
RefreshBoard(cells);
|
|
}
|
|
|
|
public void RefreshBoard(IReadOnlyList<BoardCellData> cells)
|
|
{
|
|
for (var i = 0; i < cells.Count; i++)
|
|
{
|
|
var cell = cells[i];
|
|
if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
|
|
{
|
|
view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetBoardInputEnabled(bool enabled)
|
|
{
|
|
boardInputEnabled = enabled;
|
|
foreach (var cell in cellsByCoordinate.Values)
|
|
{
|
|
cell.SetInputEnabled(enabled);
|
|
}
|
|
}
|
|
|
|
private void AddButtonListeners()
|
|
{
|
|
if (pauseButton != null)
|
|
{
|
|
pauseButton.onClick.AddListener(OnPauseClicked);
|
|
}
|
|
|
|
if (restartButton != null)
|
|
{
|
|
restartButton.onClick.AddListener(OnRestartClicked);
|
|
}
|
|
|
|
if (resumeButton != null)
|
|
{
|
|
resumeButton.onClick.AddListener(OnResumeClicked);
|
|
}
|
|
|
|
if (mainMenuButton != null)
|
|
{
|
|
mainMenuButton.onClick.AddListener(OnMainMenuClicked);
|
|
}
|
|
|
|
if (resultRestartButton != null)
|
|
{
|
|
resultRestartButton.onClick.AddListener(OnRestartClicked);
|
|
}
|
|
|
|
if (resultMainMenuButton != null)
|
|
{
|
|
resultMainMenuButton.onClick.AddListener(OnMainMenuClicked);
|
|
}
|
|
}
|
|
|
|
private void RemoveButtonListeners()
|
|
{
|
|
if (pauseButton != null)
|
|
{
|
|
pauseButton.onClick.RemoveListener(OnPauseClicked);
|
|
}
|
|
|
|
if (restartButton != null)
|
|
{
|
|
restartButton.onClick.RemoveListener(OnRestartClicked);
|
|
}
|
|
|
|
if (resumeButton != null)
|
|
{
|
|
resumeButton.onClick.RemoveListener(OnResumeClicked);
|
|
}
|
|
|
|
if (mainMenuButton != null)
|
|
{
|
|
mainMenuButton.onClick.RemoveListener(OnMainMenuClicked);
|
|
}
|
|
|
|
if (resultRestartButton != null)
|
|
{
|
|
resultRestartButton.onClick.RemoveListener(OnRestartClicked);
|
|
}
|
|
|
|
if (resultMainMenuButton != null)
|
|
{
|
|
resultMainMenuButton.onClick.RemoveListener(OnMainMenuClicked);
|
|
}
|
|
}
|
|
|
|
private void SetBoardRootActive(bool active)
|
|
{
|
|
if (boardPanel != null)
|
|
{
|
|
boardPanel.gameObject.SetActive(active);
|
|
}
|
|
}
|
|
|
|
private void ConfigureGrid(int width, int height)
|
|
{
|
|
if (gridLayoutGroup == null || boardPanel == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Canvas.ForceUpdateCanvases();
|
|
|
|
var parentRect = boardPanel.parent as RectTransform;
|
|
var rect = parentRect != null ? parentRect.rect : boardPanel.rect;
|
|
var panelWidth = rect.width > 0f ? rect.width : uiConfig.ReferenceCellSize * width;
|
|
var panelHeight = rect.height > 0f ? rect.height : uiConfig.ReferenceCellSize * height;
|
|
var cellSize = CalculateCellSize(panelWidth, panelHeight, width, height);
|
|
var padding = cellSize * uiConfig.BoardPaddingRatio;
|
|
var spacing = cellSize * uiConfig.GridSpacingRatio;
|
|
|
|
boardPanel.anchorMin = Vector2.zero;
|
|
boardPanel.anchorMax = Vector2.one;
|
|
boardPanel.offsetMin = new Vector2(padding, padding);
|
|
boardPanel.offsetMax = new Vector2(-padding, -padding);
|
|
|
|
gridLayoutGroup.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
|
|
gridLayoutGroup.constraintCount = width;
|
|
gridLayoutGroup.padding = new RectOffset();
|
|
gridLayoutGroup.spacing = new Vector2(spacing, spacing);
|
|
gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
|
|
currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / cellSize;
|
|
}
|
|
|
|
private float CalculateCellSize(float panelWidth, float panelHeight, int width, int height)
|
|
{
|
|
var widthUnits = width + 2f * uiConfig.BoardPaddingRatio + Mathf.Max(0, width - 1) * uiConfig.GridSpacingRatio;
|
|
var heightUnits = height + 2f * uiConfig.BoardPaddingRatio + Mathf.Max(0, height - 1) * uiConfig.GridSpacingRatio;
|
|
var cellByWidth = panelWidth / widthUnits;
|
|
var cellByHeight = panelHeight / heightUnits;
|
|
return Mathf.Max(uiConfig.MinimumCellSize, Mathf.Floor(Mathf.Min(cellByWidth, cellByHeight)));
|
|
}
|
|
|
|
private void CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
|
|
{
|
|
var view = cellViewFactory.CreateCell(cell, gridLayoutGroup.transform);
|
|
view.SetInputEnabled(boardInputEnabled);
|
|
view.OpenRequested += OnCellOpenRequested;
|
|
view.FlagRequested += OnCellFlagRequested;
|
|
view.PressStarted += OnCellPressStarted;
|
|
view.PressEnded += OnCellPressEnded;
|
|
cellsByCoordinate[ToKey(cell.X, cell.Y)] = view;
|
|
}
|
|
|
|
private void ClearBoard()
|
|
{
|
|
foreach (var cell in cellsByCoordinate.Values)
|
|
{
|
|
if (cell != null)
|
|
{
|
|
cell.OpenRequested -= OnCellOpenRequested;
|
|
cell.FlagRequested -= OnCellFlagRequested;
|
|
cell.PressStarted -= OnCellPressStarted;
|
|
cell.PressEnded -= OnCellPressEnded;
|
|
}
|
|
}
|
|
|
|
cellsByCoordinate.Clear();
|
|
|
|
if (gridLayoutGroup == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (var i = gridLayoutGroup.transform.childCount - 1; i >= 0; i--)
|
|
{
|
|
Destroy(gridLayoutGroup.transform.GetChild(i).gameObject);
|
|
}
|
|
}
|
|
|
|
private void OnCellOpenRequested(int x, int y)
|
|
{
|
|
CellOpenRequested?.Invoke(x, y);
|
|
}
|
|
|
|
private void OnCellFlagRequested(int x, int y)
|
|
{
|
|
CellFlagRequested?.Invoke(x, y);
|
|
}
|
|
|
|
private void OnCellPressStarted()
|
|
{
|
|
CellPressStarted?.Invoke();
|
|
}
|
|
|
|
private void OnCellPressEnded()
|
|
{
|
|
CellPressEnded?.Invoke();
|
|
}
|
|
|
|
private void OnPauseClicked()
|
|
{
|
|
PauseRequested?.Invoke();
|
|
}
|
|
|
|
private void OnRestartClicked()
|
|
{
|
|
RestartRequested?.Invoke();
|
|
}
|
|
|
|
private void OnResumeClicked()
|
|
{
|
|
ResumeRequested?.Invoke();
|
|
}
|
|
|
|
private void OnMainMenuClicked()
|
|
{
|
|
GoToMenuRequested?.Invoke();
|
|
}
|
|
|
|
private static int ToKey(int x, int y)
|
|
{
|
|
return (y << 16) ^ x;
|
|
}
|
|
}
|
|
}
|