[Fix] UI Logic
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 648cba2aa826df04aa6de2236d532ff7
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,12 @@
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.Adapters
|
||||
{
|
||||
public sealed class GameStateViewAdapter : IGameStateViewAdapter
|
||||
{
|
||||
public string GetDisplayName(GameState state)
|
||||
{
|
||||
return state.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a10e635e4da16d24db457ead4d139896
|
||||
@@ -0,0 +1,9 @@
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.Adapters
|
||||
{
|
||||
public interface IGameStateViewAdapter
|
||||
{
|
||||
string GetDisplayName(GameState state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 058bab5bc0f87f64dbbf668e5e390216
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec6192ca6506ac64c8aa089466c86db5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using Minesweeper.Config;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Views;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minesweeper.Presentation.Factories
|
||||
{
|
||||
public sealed class CellViewFactory : ICellViewFactory
|
||||
{
|
||||
private const string ContentImagePath = "Content/Image";
|
||||
private const string ContentLabelPath = "Content/Text (TMP)";
|
||||
|
||||
private readonly MinesweeperUiConfig uiConfig;
|
||||
|
||||
public CellViewFactory(MinesweeperUiConfig uiConfig)
|
||||
{
|
||||
this.uiConfig = uiConfig;
|
||||
}
|
||||
|
||||
public CellView CreateCell(BoardCellData cell, Transform parent)
|
||||
{
|
||||
var prefab = uiConfig.CellButtonPrefab;
|
||||
var go = prefab != null ? Object.Instantiate(prefab, parent) : CreateFallbackCell(parent);
|
||||
go.name = BuildCellName(cell.X, cell.Y, cell.DisplayValue);
|
||||
|
||||
var view = go.GetComponent<CellView>();
|
||||
if (view == null)
|
||||
{
|
||||
view = go.AddComponent<CellView>();
|
||||
}
|
||||
|
||||
var button = go.GetComponent<Button>();
|
||||
var backgroundImage = go.GetComponent<Image>();
|
||||
var contentImage = FindComponent<Image>(go.transform, ContentImagePath);
|
||||
var label = FindComponent<TMP_Text>(go.transform, ContentLabelPath);
|
||||
view.Bind(button, backgroundImage, contentImage, label);
|
||||
view.AutoBind();
|
||||
view.Initialize(cell.X, cell.Y);
|
||||
return view;
|
||||
}
|
||||
|
||||
public string BuildCellName(int x, int y, int value, bool isMine)
|
||||
{
|
||||
return $"bt_{x}_{y}_{(isMine ? "M" : value.ToString())}";
|
||||
}
|
||||
|
||||
public string BuildCellName(int x, int y, string displayValue)
|
||||
{
|
||||
return $"bt_{x}_{y}_{displayValue}";
|
||||
}
|
||||
|
||||
private static T FindComponent<T>(Transform root, string path) where T : Component
|
||||
{
|
||||
var child = root.Find(path);
|
||||
return child != null ? child.GetComponent<T>() : null;
|
||||
}
|
||||
|
||||
private static GameObject CreateFallbackCell(Transform parent)
|
||||
{
|
||||
var go = new GameObject("Cell", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image), typeof(Button));
|
||||
go.transform.SetParent(parent, false);
|
||||
|
||||
var content = new GameObject("Content", typeof(RectTransform));
|
||||
content.transform.SetParent(go.transform, false);
|
||||
Stretch(content.GetComponent<RectTransform>());
|
||||
|
||||
var image = new GameObject("Image", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
|
||||
image.transform.SetParent(content.transform, false);
|
||||
Stretch(image.GetComponent<RectTransform>());
|
||||
|
||||
var text = new GameObject("Text (TMP)", typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
|
||||
text.transform.SetParent(content.transform, false);
|
||||
Stretch(text.GetComponent<RectTransform>());
|
||||
|
||||
var label = text.GetComponent<TextMeshProUGUI>();
|
||||
label.alignment = TextAlignmentOptions.Center;
|
||||
label.enableAutoSizing = true;
|
||||
return go;
|
||||
}
|
||||
|
||||
private static void Stretch(RectTransform rectTransform)
|
||||
{
|
||||
rectTransform.anchorMin = Vector2.zero;
|
||||
rectTransform.anchorMax = Vector2.one;
|
||||
rectTransform.offsetMin = Vector2.zero;
|
||||
rectTransform.offsetMax = Vector2.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 681a16a970ddf3546bb4f99d93a184ca
|
||||
@@ -0,0 +1,13 @@
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Views;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minesweeper.Presentation.Factories
|
||||
{
|
||||
public interface ICellViewFactory
|
||||
{
|
||||
CellView CreateCell(BoardCellData cell, Transform parent);
|
||||
string BuildCellName(int x, int y, int value, bool isMine);
|
||||
string BuildCellName(int x, int y, string displayValue);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 34c31a0f01c730440a2bcdac0f775dd8
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3895ae14930e10841a7562a478c871ec
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,196 @@
|
||||
using Minesweeper.Commands;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Factories;
|
||||
using Minesweeper.Presentation.ReadModels;
|
||||
using Minesweeper.Presentation.Views;
|
||||
|
||||
namespace Minesweeper.Presentation.Presenters
|
||||
{
|
||||
public sealed class GamePresenter : IPresenter
|
||||
{
|
||||
private readonly IGameCommandDispatcher commandDispatcher;
|
||||
private readonly ICellViewFactory cellViewFactory;
|
||||
private readonly IGamePauseService pauseService;
|
||||
private readonly IGameReadModel readModel;
|
||||
private readonly IGameStateService gameStateService;
|
||||
private readonly IGameTimerService timerService;
|
||||
private readonly TopPanelPresenter topPanelPresenter;
|
||||
private readonly IGameView view;
|
||||
private bool boardBuilt;
|
||||
|
||||
public GamePresenter(IGameCommandDispatcher commandDispatcher, ICellViewFactory cellViewFactory, IGamePauseService pauseService, IGameReadModel readModel, IGameStateService gameStateService, IGameTimerService timerService, TopPanelPresenter topPanelPresenter, IGameView view = null)
|
||||
{
|
||||
this.commandDispatcher = commandDispatcher;
|
||||
this.cellViewFactory = cellViewFactory;
|
||||
this.pauseService = pauseService;
|
||||
this.readModel = readModel;
|
||||
this.gameStateService = gameStateService;
|
||||
this.timerService = timerService;
|
||||
this.topPanelPresenter = topPanelPresenter;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.RestartRequested += OnRestartRequested;
|
||||
view.GoToMenuRequested += OnGoToMenuRequested;
|
||||
view.PauseRequested += OnPauseRequested;
|
||||
view.ResumeRequested += OnResumeRequested;
|
||||
view.CellPressStarted += OnCellPressStarted;
|
||||
view.CellPressEnded += OnCellPressEnded;
|
||||
view.CellOpenRequested += OnCellOpenRequested;
|
||||
view.CellFlagRequested += OnCellFlagRequested;
|
||||
gameStateService.StateChanged += OnStateChanged;
|
||||
pauseService.PauseChanged += OnPauseChanged;
|
||||
timerService.TimeChanged += OnTimeChanged;
|
||||
OnStateChanged(gameStateService.Current);
|
||||
OnPauseChanged(pauseService.IsPaused);
|
||||
OnTimeChanged(timerService.ElapsedSeconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.RestartRequested -= OnRestartRequested;
|
||||
view.GoToMenuRequested -= OnGoToMenuRequested;
|
||||
view.PauseRequested -= OnPauseRequested;
|
||||
view.ResumeRequested -= OnResumeRequested;
|
||||
view.CellPressStarted -= OnCellPressStarted;
|
||||
view.CellPressEnded -= OnCellPressEnded;
|
||||
view.CellOpenRequested -= OnCellOpenRequested;
|
||||
view.CellFlagRequested -= OnCellFlagRequested;
|
||||
gameStateService.StateChanged -= OnStateChanged;
|
||||
pauseService.PauseChanged -= OnPauseChanged;
|
||||
timerService.TimeChanged -= OnTimeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRestartRequested()
|
||||
{
|
||||
commandDispatcher.Dispatch(new RestartCommand());
|
||||
RebuildBoard();
|
||||
topPanelPresenter.RefreshCounters();
|
||||
}
|
||||
|
||||
private void OnGoToMenuRequested()
|
||||
{
|
||||
commandDispatcher.Dispatch(new GoToMenuCommand());
|
||||
}
|
||||
|
||||
private void OnPauseRequested()
|
||||
{
|
||||
commandDispatcher.Dispatch(new PauseCommand());
|
||||
}
|
||||
|
||||
private void OnResumeRequested()
|
||||
{
|
||||
commandDispatcher.Dispatch(new ResumeCommand());
|
||||
}
|
||||
|
||||
private void OnCellOpenRequested(int x, int y)
|
||||
{
|
||||
topPanelPresenter.SetCellPressActive(false);
|
||||
commandDispatcher.Dispatch(new OpenCellCommand(x, y));
|
||||
RefreshBoard();
|
||||
topPanelPresenter.RefreshCounters();
|
||||
UpdateBoardInput();
|
||||
}
|
||||
|
||||
private void OnCellFlagRequested(int x, int y)
|
||||
{
|
||||
commandDispatcher.Dispatch(new ToggleFlagCommand(x, y));
|
||||
RefreshBoard();
|
||||
topPanelPresenter.RefreshCounters();
|
||||
UpdateBoardInput();
|
||||
}
|
||||
|
||||
private void OnCellPressStarted()
|
||||
{
|
||||
topPanelPresenter.SetCellPressActive(true);
|
||||
}
|
||||
|
||||
private void OnCellPressEnded()
|
||||
{
|
||||
topPanelPresenter.SetCellPressActive(false);
|
||||
}
|
||||
|
||||
private void OnStateChanged(GameState state)
|
||||
{
|
||||
if (state == GameState.FieldSelection)
|
||||
{
|
||||
boardBuilt = false;
|
||||
view.HideGame();
|
||||
view.HidePause();
|
||||
view.HideResult();
|
||||
return;
|
||||
}
|
||||
|
||||
view.ShowGame();
|
||||
|
||||
if (state == GameState.Won || state == GameState.Lost)
|
||||
{
|
||||
view.HidePause();
|
||||
view.ShowResult(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
view.HideResult();
|
||||
}
|
||||
|
||||
if (!boardBuilt || state == GameState.Preparing)
|
||||
{
|
||||
RebuildBoard();
|
||||
}
|
||||
else
|
||||
{
|
||||
RefreshBoard();
|
||||
}
|
||||
|
||||
UpdateBoardInput();
|
||||
}
|
||||
|
||||
private void OnPauseChanged(bool isPaused)
|
||||
{
|
||||
if (isPaused)
|
||||
{
|
||||
view.ShowPause();
|
||||
}
|
||||
else
|
||||
{
|
||||
view.HidePause();
|
||||
}
|
||||
|
||||
UpdateBoardInput();
|
||||
}
|
||||
|
||||
private void OnTimeChanged(float seconds)
|
||||
{
|
||||
view.SetTimer(seconds);
|
||||
}
|
||||
|
||||
private void RebuildBoard()
|
||||
{
|
||||
var cells = readModel.GetCells();
|
||||
view.SetMineCount(readModel.MinesCount);
|
||||
view.RebuildBoard(cells, readModel.Width, readModel.Height, cellViewFactory);
|
||||
boardBuilt = true;
|
||||
topPanelPresenter.RefreshCounters();
|
||||
UpdateBoardInput();
|
||||
}
|
||||
|
||||
private void RefreshBoard()
|
||||
{
|
||||
view.RefreshBoard(readModel.GetCells());
|
||||
}
|
||||
|
||||
private void UpdateBoardInput()
|
||||
{
|
||||
var state = gameStateService.Current;
|
||||
view.SetBoardInputEnabled(!pauseService.IsPaused && (state == GameState.Preparing || state == GameState.Playing));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f85646fc4851054e9efffa3ab1f6853
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Minesweeper.Presentation.Presenters
|
||||
{
|
||||
public interface IPresenter : IDisposable
|
||||
{
|
||||
void Initialize();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0fcee9aa500b62429eb4ffa2249de9c
|
||||
@@ -0,0 +1,56 @@
|
||||
using Minesweeper.Commands;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Views;
|
||||
|
||||
namespace Minesweeper.Presentation.Presenters
|
||||
{
|
||||
public sealed class MainMenuPresenter : IPresenter
|
||||
{
|
||||
private readonly IGameCommandDispatcher commandDispatcher;
|
||||
private readonly IGameStateService gameStateService;
|
||||
private readonly IMainMenuView view;
|
||||
|
||||
public MainMenuPresenter(IGameCommandDispatcher commandDispatcher, IGameStateService gameStateService, IMainMenuView view = null)
|
||||
{
|
||||
this.commandDispatcher = commandDispatcher;
|
||||
this.gameStateService = gameStateService;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.StartClicked += OnStartClicked;
|
||||
gameStateService.StateChanged += OnStateChanged;
|
||||
OnStateChanged(gameStateService.Current);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (view != null)
|
||||
{
|
||||
view.StartClicked -= OnStartClicked;
|
||||
gameStateService.StateChanged -= OnStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStartClicked()
|
||||
{
|
||||
commandDispatcher.Dispatch(new StartGameCommand());
|
||||
}
|
||||
|
||||
private void OnStateChanged(GameState state)
|
||||
{
|
||||
if (state == GameState.FieldSelection)
|
||||
{
|
||||
view.Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
view.Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 835a3b63609e9864fa6154be8b8283ad
|
||||
@@ -0,0 +1,130 @@
|
||||
using Minesweeper.Commands;
|
||||
using Minesweeper.Config;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.ReadModels;
|
||||
using Minesweeper.Presentation.Views;
|
||||
|
||||
namespace Minesweeper.Presentation.Presenters
|
||||
{
|
||||
public sealed class TopPanelPresenter : IPresenter
|
||||
{
|
||||
private readonly IGameCommandDispatcher commandDispatcher;
|
||||
private readonly IGamePauseService pauseService;
|
||||
private readonly IGameReadModel readModel;
|
||||
private readonly IGameStateService gameStateService;
|
||||
private readonly IGameTimerService timerService;
|
||||
private readonly ITopPanelView view;
|
||||
|
||||
public TopPanelPresenter(IGameCommandDispatcher commandDispatcher, IGamePauseService pauseService, IGameReadModel readModel, IGameStateService gameStateService, IGameTimerService timerService, ITopPanelView view)
|
||||
{
|
||||
this.commandDispatcher = commandDispatcher;
|
||||
this.pauseService = pauseService;
|
||||
this.readModel = readModel;
|
||||
this.gameStateService = gameStateService;
|
||||
this.timerService = timerService;
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
view.SmileClicked += OnSmileClicked;
|
||||
gameStateService.StateChanged += OnStateChanged;
|
||||
pauseService.PauseChanged += OnPauseChanged;
|
||||
timerService.TimeChanged += OnTimeChanged;
|
||||
|
||||
RefreshAll();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
view.SmileClicked -= OnSmileClicked;
|
||||
gameStateService.StateChanged -= OnStateChanged;
|
||||
pauseService.PauseChanged -= OnPauseChanged;
|
||||
timerService.TimeChanged -= OnTimeChanged;
|
||||
}
|
||||
|
||||
public void RefreshCounters()
|
||||
{
|
||||
view.SetRemainingMines(readModel.RemainingMinesCount);
|
||||
}
|
||||
|
||||
public void SetCellPressActive(bool active)
|
||||
{
|
||||
var state = gameStateService.Current;
|
||||
if (active && !pauseService.IsPaused && (state == GameState.Preparing || state == GameState.Playing))
|
||||
{
|
||||
view.SetSmile(SmileFaceState.Surprised);
|
||||
return;
|
||||
}
|
||||
|
||||
view.SetSmile(GetSmileState(state));
|
||||
}
|
||||
|
||||
private void RefreshAll()
|
||||
{
|
||||
view.SetActive(true);
|
||||
view.SetRemainingMines(readModel.RemainingMinesCount);
|
||||
view.SetTimer(timerService.ElapsedSeconds);
|
||||
view.SetSmile(GetSmileState(gameStateService.Current));
|
||||
}
|
||||
|
||||
private void OnSmileClicked()
|
||||
{
|
||||
var state = gameStateService.Current;
|
||||
if (state == GameState.FieldSelection)
|
||||
{
|
||||
commandDispatcher.Dispatch(new StartGameCommand());
|
||||
}
|
||||
else if (state == GameState.Playing && !pauseService.IsPaused)
|
||||
{
|
||||
commandDispatcher.Dispatch(new PauseCommand());
|
||||
}
|
||||
else if (state == GameState.Playing && pauseService.IsPaused)
|
||||
{
|
||||
commandDispatcher.Dispatch(new ResumeCommand());
|
||||
}
|
||||
else
|
||||
{
|
||||
commandDispatcher.Dispatch(new RestartCommand());
|
||||
}
|
||||
|
||||
RefreshAll();
|
||||
}
|
||||
|
||||
private void OnStateChanged(GameState state)
|
||||
{
|
||||
if (state == GameState.FieldSelection || state == GameState.Preparing)
|
||||
{
|
||||
view.SetTimer(0f);
|
||||
}
|
||||
|
||||
view.SetSmile(GetSmileState(state));
|
||||
view.SetRemainingMines(readModel.RemainingMinesCount);
|
||||
}
|
||||
|
||||
private void OnPauseChanged(bool isPaused)
|
||||
{
|
||||
view.SetSmile(GetSmileState(gameStateService.Current));
|
||||
}
|
||||
|
||||
private void OnTimeChanged(float seconds)
|
||||
{
|
||||
view.SetTimer(seconds);
|
||||
}
|
||||
|
||||
private SmileFaceState GetSmileState(GameState state)
|
||||
{
|
||||
if (state == GameState.Won)
|
||||
{
|
||||
return SmileFaceState.Cool;
|
||||
}
|
||||
|
||||
if (state == GameState.Lost)
|
||||
{
|
||||
return SmileFaceState.Dead;
|
||||
}
|
||||
|
||||
return SmileFaceState.Smile;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3ac57cd2e63497d9d0d7e23659a4d45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 590e96d08af41e346b49a7ce43641afb
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using Minesweeper.Config;
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.ReadModels
|
||||
{
|
||||
public sealed class GameReadModel : IGameReadModel
|
||||
{
|
||||
private readonly MinesweeperGameConfig config;
|
||||
private readonly IBoardService boardService;
|
||||
private readonly IGameStateService gameStateService;
|
||||
|
||||
public GameReadModel(MinesweeperGameConfig config, IBoardService boardService, IGameStateService gameStateService)
|
||||
{
|
||||
this.config = config;
|
||||
this.boardService = boardService;
|
||||
this.gameStateService = gameStateService;
|
||||
}
|
||||
|
||||
public GameState State => gameStateService.Current;
|
||||
public int Width => boardService.Width > 0 ? boardService.Width : config.Width;
|
||||
public int Height => boardService.Height > 0 ? boardService.Height : config.Height;
|
||||
public int MinesCount => boardService.MinesCount > 0 ? boardService.MinesCount : config.MinesCount;
|
||||
public int FlaggedCellsCount => CountFlaggedCells();
|
||||
public int RemainingMinesCount => MinesCount - FlaggedCellsCount;
|
||||
|
||||
public bool TryGetCell(int x, int y, out BoardCellData cell)
|
||||
{
|
||||
return boardService.TryGetCell(x, y, out cell);
|
||||
}
|
||||
|
||||
public IReadOnlyList<BoardCellData> GetCells()
|
||||
{
|
||||
return boardService.GetCells();
|
||||
}
|
||||
|
||||
private int CountFlaggedCells()
|
||||
{
|
||||
var cells = boardService.GetCells();
|
||||
var count = 0;
|
||||
for (var i = 0; i < cells.Count; i++)
|
||||
{
|
||||
if (cells[i].IsFlagged)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b2a816449e034a4f9635960881ec852
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using Minesweeper.Core;
|
||||
|
||||
namespace Minesweeper.Presentation.ReadModels
|
||||
{
|
||||
public interface IGameReadModel
|
||||
{
|
||||
GameState State { get; }
|
||||
int Width { get; }
|
||||
int Height { get; }
|
||||
int MinesCount { get; }
|
||||
int FlaggedCellsCount { get; }
|
||||
int RemainingMinesCount { get; }
|
||||
|
||||
bool TryGetCell(int x, int y, out BoardCellData cell);
|
||||
IReadOnlyList<BoardCellData> GetCells();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e5c921f20dd6f1b40a1f73746265a63d
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e2357d56da983b478c1cebe1e3ac363
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Config;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class CellView : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
|
||||
{
|
||||
private const string ContentImagePath = "Content/Image";
|
||||
private const string ContentLabelPath = "Content/Text (TMP)";
|
||||
|
||||
[SerializeField] private Button button;
|
||||
[SerializeField] private Image backgroundImage;
|
||||
[SerializeField] private Image contentImage;
|
||||
[SerializeField] private TMP_Text label;
|
||||
|
||||
private int x;
|
||||
private int y;
|
||||
private bool inputEnabled = true;
|
||||
private bool isOpened;
|
||||
|
||||
public event Action<int, int> OpenRequested;
|
||||
public event Action<int, int> FlagRequested;
|
||||
public event Action PressStarted;
|
||||
public event Action PressEnded;
|
||||
|
||||
public void Bind(Button button, Image backgroundImage, Image contentImage, TMP_Text label)
|
||||
{
|
||||
this.button = button;
|
||||
this.backgroundImage = backgroundImage;
|
||||
this.contentImage = contentImage;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public void AutoBind()
|
||||
{
|
||||
if (button == null)
|
||||
{
|
||||
button = GetComponent<Button>();
|
||||
}
|
||||
|
||||
if (backgroundImage == null)
|
||||
{
|
||||
backgroundImage = GetComponent<Image>();
|
||||
}
|
||||
|
||||
if (contentImage == null)
|
||||
{
|
||||
var contentImageTransform = transform.Find(ContentImagePath);
|
||||
if (contentImageTransform != null)
|
||||
{
|
||||
contentImage = contentImageTransform.GetComponent<Image>();
|
||||
}
|
||||
}
|
||||
|
||||
if (label == null)
|
||||
{
|
||||
var labelTransform = transform.Find(ContentLabelPath);
|
||||
if (labelTransform != null)
|
||||
{
|
||||
label = labelTransform.GetComponent<TMP_Text>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize(int x, int y)
|
||||
{
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public void SetInputEnabled(bool enabled)
|
||||
{
|
||||
inputEnabled = enabled;
|
||||
if (button != null)
|
||||
{
|
||||
button.interactable = enabled && !isOpened;
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier)
|
||||
{
|
||||
gameObject.name = $"bt_{cell.X}_{cell.Y}_{cell.DisplayValue}";
|
||||
isOpened = cell.IsOpened;
|
||||
|
||||
if (backgroundImage != null)
|
||||
{
|
||||
backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
||||
backgroundImage.sprite = cell.IsOpened ? config.OpenedCellSprite : config.ClosedCellSprite;
|
||||
backgroundImage.color = Color.white;
|
||||
}
|
||||
|
||||
if (contentImage != null)
|
||||
{
|
||||
contentImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
|
||||
}
|
||||
|
||||
RenderContent(cell, config);
|
||||
|
||||
if (button != null)
|
||||
{
|
||||
button.interactable = inputEnabled && !cell.IsOpened;
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderContent(BoardCellData cell, MinesweeperUiConfig config)
|
||||
{
|
||||
var showFlag = cell.IsFlagged && !cell.IsOpened;
|
||||
var showMine = cell.IsOpened && cell.IsMine;
|
||||
var showNumber = cell.IsOpened && !cell.IsMine && cell.NeighborMines > 0;
|
||||
|
||||
if (contentImage != null)
|
||||
{
|
||||
contentImage.gameObject.SetActive(showFlag || showMine);
|
||||
if (showFlag)
|
||||
{
|
||||
contentImage.sprite = config.FlagSprite;
|
||||
}
|
||||
else if (showMine)
|
||||
{
|
||||
contentImage.sprite = config.MineSprite;
|
||||
}
|
||||
|
||||
contentImage.color = Color.white;
|
||||
}
|
||||
|
||||
if (label != null)
|
||||
{
|
||||
label.gameObject.SetActive(showNumber);
|
||||
label.text = showNumber ? cell.NeighborMines.ToString() : string.Empty;
|
||||
label.color = config.GetNumberTextColor(cell.NeighborMines);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (!inputEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventData.button == PointerEventData.InputButton.Left)
|
||||
{
|
||||
OpenRequested?.Invoke(x, y);
|
||||
}
|
||||
else if (eventData.button == PointerEventData.InputButton.Right)
|
||||
{
|
||||
FlagRequested?.Invoke(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (inputEnabled && eventData.button == PointerEventData.InputButton.Left)
|
||||
{
|
||||
PressStarted?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button == PointerEventData.InputButton.Left)
|
||||
{
|
||||
PressEnded?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2904d462d22809c499afe1842f6e6239
|
||||
@@ -0,0 +1,360 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c906a10872edd04480e534703fc4fea
|
||||
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Factories;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public interface IGameView : IView
|
||||
{
|
||||
event Action RestartRequested;
|
||||
event Action GoToMenuRequested;
|
||||
event Action PauseRequested;
|
||||
event Action ResumeRequested;
|
||||
event Action CellPressStarted;
|
||||
event Action CellPressEnded;
|
||||
event Action<int, int> CellOpenRequested;
|
||||
event Action<int, int> CellFlagRequested;
|
||||
|
||||
void ShowGame();
|
||||
void HideGame();
|
||||
void ShowPause();
|
||||
void HidePause();
|
||||
void ShowResult(GameState state);
|
||||
void HideResult();
|
||||
void SetMineCount(int minesCount);
|
||||
void SetTimer(float seconds);
|
||||
void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory);
|
||||
void RefreshBoard(IReadOnlyList<BoardCellData> cells);
|
||||
void SetBoardInputEnabled(bool enabled);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8c5423ea37354a4e82b05aadfbf239f
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public interface IMainMenuView : IView
|
||||
{
|
||||
event Action StartClicked;
|
||||
|
||||
void Show();
|
||||
void Hide();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66cad179080f23a479c3418932137653
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using Minesweeper.Config;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public interface ITopPanelView : IView
|
||||
{
|
||||
event Action SmileClicked;
|
||||
|
||||
void SetActive(bool active);
|
||||
void SetRemainingMines(int remainingMines);
|
||||
void SetTimer(float seconds);
|
||||
void SetSmile(SmileFaceState state);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0c8cae3d1ad4d7a9ca3e2f8f435cd7b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public interface IView
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66a8b79ae5812744680f9e386b007144
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class MainMenuView : MonoBehaviour, IMainMenuView
|
||||
{
|
||||
[SerializeField] private GameObject root;
|
||||
[SerializeField] private Button startButton;
|
||||
|
||||
public event Action StartClicked;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
root = gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (startButton != null)
|
||||
{
|
||||
startButton.onClick.AddListener(OnStartClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (startButton != null)
|
||||
{
|
||||
startButton.onClick.RemoveListener(OnStartClicked);
|
||||
}
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
root.SetActive(true);
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
root.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnStartClicked()
|
||||
{
|
||||
StartClicked?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb899c7e47cd4e341b0258dac3f7a238
|
||||
@@ -0,0 +1,102 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Minesweeper.Core;
|
||||
using Minesweeper.Presentation.Factories;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class NullGameView : IGameView
|
||||
{
|
||||
public event Action RestartRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action GoToMenuRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action PauseRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action ResumeRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action CellPressStarted
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action CellPressEnded
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action<int, int> CellOpenRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action<int, int> CellFlagRequested
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void ShowGame()
|
||||
{
|
||||
}
|
||||
|
||||
public void HideGame()
|
||||
{
|
||||
}
|
||||
|
||||
public void ShowPause()
|
||||
{
|
||||
}
|
||||
|
||||
public void HidePause()
|
||||
{
|
||||
}
|
||||
|
||||
public void ShowResult(GameState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void HideResult()
|
||||
{
|
||||
}
|
||||
|
||||
public void SetMineCount(int minesCount)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetTimer(float seconds)
|
||||
{
|
||||
}
|
||||
|
||||
public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory)
|
||||
{
|
||||
}
|
||||
|
||||
public void RefreshBoard(IReadOnlyList<BoardCellData> cells)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetBoardInputEnabled(bool enabled)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c800a42df535f9347bea10f164fd2e15
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class NullMainMenuView : IMainMenuView
|
||||
{
|
||||
public event Action StartClicked
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void Show()
|
||||
{
|
||||
}
|
||||
|
||||
public void Hide()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b41dd95488a1db42adccef0225d2f89
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using Minesweeper.Config;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class NullTopPanelView : ITopPanelView
|
||||
{
|
||||
public event Action SmileClicked
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetRemainingMines(int remainingMines)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetTimer(float seconds)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetSmile(SmileFaceState state)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8372ef8aa2747d7af12fce4f1324baf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using Minesweeper.Config;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Minesweeper.Presentation.Views
|
||||
{
|
||||
public sealed class TopPanelView : MonoBehaviour, ITopPanelView
|
||||
{
|
||||
[SerializeField] private GameObject root;
|
||||
[SerializeField] private TMP_Text mineText;
|
||||
[SerializeField] private TMP_Text timerText;
|
||||
[SerializeField] private Button smileButton;
|
||||
[SerializeField] private Image smileImage;
|
||||
[SerializeField] private MinesweeperUiConfig uiConfig;
|
||||
|
||||
public event Action SmileClicked;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (root == null)
|
||||
{
|
||||
root = gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (smileButton != null)
|
||||
{
|
||||
smileButton.onClick.AddListener(OnSmileClicked);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (smileButton != null)
|
||||
{
|
||||
smileButton.onClick.RemoveListener(OnSmileClicked);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetActive(bool active)
|
||||
{
|
||||
root.SetActive(active);
|
||||
}
|
||||
|
||||
public void SetRemainingMines(int remainingMines)
|
||||
{
|
||||
if (mineText != null)
|
||||
{
|
||||
mineText.text = Mathf.Max(0, remainingMines).ToString("00000");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTimer(float seconds)
|
||||
{
|
||||
if (timerText != null)
|
||||
{
|
||||
timerText.text = Mathf.FloorToInt(seconds).ToString("00000");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSmile(SmileFaceState state)
|
||||
{
|
||||
if (smileImage == null || uiConfig == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sprite = uiConfig.GetSmileSprite(state);
|
||||
if (sprite != null)
|
||||
{
|
||||
smileImage.sprite = sprite;
|
||||
}
|
||||
|
||||
smileImage.color = Color.white;
|
||||
}
|
||||
|
||||
public void BindConfig(MinesweeperUiConfig config)
|
||||
{
|
||||
uiConfig = config;
|
||||
}
|
||||
|
||||
private void OnSmileClicked()
|
||||
{
|
||||
SmileClicked?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb4efc57f8404c98bff81ed5db093d81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user