[Fix] UI Logic

This commit is contained in:
2026-06-06 22:33:15 +07:00
parent f4ecf8b6f9
commit fdb22e9213
134 changed files with 5367 additions and 269 deletions
@@ -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: