Cleanup GameBOardService
This commit is contained in:
@@ -21,10 +21,6 @@ namespace Services
|
|||||||
|
|
||||||
private BombSpawnRequest? pendingBombSpawn;
|
private BombSpawnRequest? pendingBombSpawn;
|
||||||
public BombSpawnRequest? PendingBombSpawn => this.pendingBombSpawn;
|
public BombSpawnRequest? PendingBombSpawn => this.pendingBombSpawn;
|
||||||
|
|
||||||
public void ClearPendingBombs() {
|
|
||||||
this.pendingBombSpawn = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BombService(GameVariables gameVariables, IGameBoard gameBoard) {
|
public BombService(GameVariables gameVariables, IGameBoard gameBoard) {
|
||||||
this.gameVariables = gameVariables;
|
this.gameVariables = gameVariables;
|
||||||
@@ -34,6 +30,38 @@ namespace Services
|
|||||||
public void SetLastSwap(Vector2Int from, Vector2Int to) {
|
public void SetLastSwap(Vector2Int from, Vector2Int to) {
|
||||||
this.lastSwapFrom = from;
|
this.lastSwapFrom = from;
|
||||||
this.lastSwapTo = to;
|
this.lastSwapTo = to;
|
||||||
|
|
||||||
|
ClearPendingBombs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<List<Vector2Int>> GetInitialBombs(List<Vector2Int> protectedPositions, List<Vector2Int> bombCandidates) {
|
||||||
|
List<Vector2Int> initialBombs = new List<Vector2Int>();
|
||||||
|
foreach (Vector2Int p in bombCandidates) {
|
||||||
|
if (!GemUtils.IsInBounds(p, this.gameBoard)) continue;
|
||||||
|
|
||||||
|
if (protectedPositions != null && protectedPositions.Contains(p))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Gem gem = this.gameBoard.GetGemAt(p);
|
||||||
|
if (gem is { Type: GemType.Bomb })
|
||||||
|
initialBombs.Add(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UniTask.FromResult(initialBombs.Distinct().ToList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Vector2Int> ApplyPendingBombSpawns(Action<Vector2Int, GemType, bool> spawnGem) {
|
||||||
|
List<Vector2Int> positions = new List<Vector2Int>();
|
||||||
|
BombSpawnRequest? bombSpawnRequest = PendingBombSpawn;
|
||||||
|
|
||||||
|
if (bombSpawnRequest != null) {
|
||||||
|
BombSpawnRequest bombRequest = PendingBombSpawn.GetValueOrDefault();
|
||||||
|
positions.Add(bombRequest.Position);
|
||||||
|
spawnGem(bombRequest.Position, bombRequest.Color, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClearPendingBombs();
|
||||||
|
return positions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DetectBombSpawnFromLastSwap(List<Gem> currentMatches) {
|
public void DetectBombSpawnFromLastSwap(List<Gem> currentMatches) {
|
||||||
@@ -201,5 +229,9 @@ namespace Services
|
|||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ClearPendingBombs() {
|
||||||
|
this.pendingBombSpawn = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,7 +100,7 @@ namespace Services {
|
|||||||
//Uses the ObjectPool to spawn a gem at the given position
|
//Uses the ObjectPool to spawn a gem at the given position
|
||||||
private void SpawnGem(Vector2Int position, GemType gemType, bool isBomb = false) {
|
private void SpawnGem(Vector2Int position, GemType gemType, bool isBomb = false) {
|
||||||
if (isBomb) {
|
if (isBomb) {
|
||||||
DestroyMatchedGems(position);
|
ReleaseMatchedGems(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
GemView gemView = this.objectPool.Get(isBomb ? GemType.Bomb : gemType, position, isBomb ? 0 : this.gameVariables.dropHeight);
|
GemView gemView = this.objectPool.Get(isBomb ? GemType.Bomb : gemType, position, isBomb ? 0 : this.gameVariables.dropHeight);
|
||||||
@@ -121,8 +121,8 @@ namespace Services {
|
|||||||
|
|
||||||
if (!GemUtils.IsInBounds(from, this.gameBoard) || !GemUtils.IsInBounds(to, this.gameBoard))
|
if (!GemUtils.IsInBounds(from, this.gameBoard) || !GemUtils.IsInBounds(to, this.gameBoard))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!AreAdjacentCardinal(from, to))
|
if (!from.IsAdjacent(to))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this.currentState = GameState.Wait;
|
this.currentState = GameState.Wait;
|
||||||
@@ -131,7 +131,6 @@ namespace Services {
|
|||||||
|
|
||||||
await UniTask.Delay(600);
|
await UniTask.Delay(600);
|
||||||
this.bombService.SetLastSwap(from, to);
|
this.bombService.SetLastSwap(from, to);
|
||||||
this.bombService.ClearPendingBombs();
|
|
||||||
this.matchService.FindAllMatches();
|
this.matchService.FindAllMatches();
|
||||||
this.bombService.DetectBombSpawnFromLastSwap(this.matchService.CurrentMatches);
|
this.bombService.DetectBombSpawnFromLastSwap(this.matchService.CurrentMatches);
|
||||||
|
|
||||||
@@ -142,7 +141,7 @@ namespace Services {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Vector2Int> protectedPositions = ApplyPendingBombSpawns();
|
List<Vector2Int> protectedPositions = this.bombService.ApplyPendingBombSpawns(SpawnGem);
|
||||||
await DestroyMatchesAsync(protectedPositions);
|
await DestroyMatchesAsync(protectedPositions);
|
||||||
this.currentState = GameState.Move;
|
this.currentState = GameState.Move;
|
||||||
return true;
|
return true;
|
||||||
@@ -160,47 +159,9 @@ namespace Services {
|
|||||||
this.gameBoard.SetGemAt(to, fromGem);
|
this.gameBoard.SetGemAt(to, fromGem);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Vector2Int> ApplyPendingBombSpawns() {
|
|
||||||
List<Vector2Int> positions = new List<Vector2Int>();
|
|
||||||
BombSpawnRequest? bombSpawnRequest = this.bombService.PendingBombSpawn;
|
|
||||||
|
|
||||||
if (bombSpawnRequest != null) {
|
|
||||||
BombSpawnRequest bombRequest = this.bombService.PendingBombSpawn.GetValueOrDefault();
|
|
||||||
positions.Add(bombRequest.Position);
|
|
||||||
SpawnGem(bombRequest.Position, bombRequest.Color, isBomb: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bombService.ClearPendingBombs();
|
|
||||||
return positions;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async UniTask DestroyMatchesAsync(List<Vector2Int> protectedPositions) {
|
private async UniTask DestroyMatchesAsync(List<Vector2Int> protectedPositions) {
|
||||||
List<Vector2Int> matchPositions = new List<Vector2Int>(this.matchService.CurrentMatches.Count);
|
List<Vector2Int> matchPositions = await this.matchService.GetMatchPositionsAsync(protectedPositions);
|
||||||
for (int i = 0; i < this.matchService.CurrentMatches.Count; i++) {
|
List<Vector2Int> initialBombs = await this.bombService.GetInitialBombs(protectedPositions, matchPositions.Distinct().ToList());
|
||||||
Gem match = this.matchService.CurrentMatches[i];
|
|
||||||
if (match == null) continue;
|
|
||||||
|
|
||||||
Vector2Int pos = match.Position;
|
|
||||||
if (protectedPositions != null && protectedPositions.Contains(pos))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
matchPositions.Add(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
IReadOnlyList<Vector2Int> bombCandidates = matchPositions.Distinct().ToList();
|
|
||||||
|
|
||||||
List<Vector2Int> initialBombs = new List<Vector2Int>();
|
|
||||||
foreach (Vector2Int p in bombCandidates) {
|
|
||||||
if (!GemUtils.IsInBounds(p, this.gameBoard)) continue;
|
|
||||||
|
|
||||||
if (protectedPositions != null && protectedPositions.Contains(p))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Gem gem = this.gameBoard.GetGemAt(p);
|
|
||||||
if (gem is { Type: GemType.Bomb })
|
|
||||||
initialBombs.Add(p);
|
|
||||||
}
|
|
||||||
initialBombs = initialBombs.Distinct().ToList();
|
|
||||||
|
|
||||||
// If a bomb is part of the match, do NOT destroy matching pieces immediately.
|
// If a bomb is part of the match, do NOT destroy matching pieces immediately.
|
||||||
// Let the bomb's manhattan-distance explosion destroy them in sequence.
|
// Let the bomb's manhattan-distance explosion destroy them in sequence.
|
||||||
@@ -210,29 +171,27 @@ namespace Services {
|
|||||||
DestroyAtAsync,
|
DestroyAtAsync,
|
||||||
this.gameBoard);
|
this.gameBoard);
|
||||||
|
|
||||||
|
await UniTask.Delay(600);
|
||||||
|
|
||||||
await MoveGemsDown();
|
await MoveGemsDown();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For audio SFX
|
||||||
bool willBreakAnyNonBombGem = matchPositions.Select(pos => this.gameBoard.GetGemAt(pos)).Where(gem => gem != null).Any(gem => gem.Type != GemType.Bomb);
|
bool willBreakAnyNonBombGem = matchPositions.Select(pos => this.gameBoard.GetGemAt(pos)).Where(gem => gem != null).Any(gem => gem.Type != GemType.Bomb);
|
||||||
|
|
||||||
if (willBreakAnyNonBombGem)
|
if (willBreakAnyNonBombGem)
|
||||||
this.audioPresenter.OnMatch(this.gameVariables.matchSfx);
|
this.audioPresenter.OnMatch(this.gameVariables.matchSfx);
|
||||||
|
|
||||||
|
// For score counting
|
||||||
foreach (Vector2Int pos in matchPositions.Distinct().ToList()) {
|
foreach (Vector2Int pos in matchPositions.Distinct().ToList()) {
|
||||||
Gem gem = this.gameBoard.GetGemAt(pos);
|
Gem gem = this.gameBoard.GetGemAt(pos);
|
||||||
if (gem == null) continue;
|
if (gem == null) continue;
|
||||||
if (gem.Type == GemType.Bomb) continue;
|
if (gem.Type == GemType.Bomb) continue;
|
||||||
|
|
||||||
this.scoreService.ScoreCheck(gem.ScoreValue);
|
this.scoreService.ScoreCheck(gem.ScoreValue);
|
||||||
DestroyMatchedGems(pos);
|
ReleaseMatchedGems(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.bombService.DetonateChainAsync(
|
|
||||||
initialBombs,
|
|
||||||
DestroyAtAsync,
|
|
||||||
this.gameBoard);
|
|
||||||
|
|
||||||
await MoveGemsDown();
|
await MoveGemsDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,9 +204,25 @@ namespace Services {
|
|||||||
this.audioPresenter.OnBombExplosion(this.gameVariables.bombExplodeSfx);
|
this.audioPresenter.OnBombExplosion(this.gameVariables.bombExplodeSfx);
|
||||||
|
|
||||||
this.scoreService.ScoreCheck(gem.ScoreValue);
|
this.scoreService.ScoreCheck(gem.ScoreValue);
|
||||||
DestroyMatchedGems(pos);
|
ReleaseMatchedGems(pos);
|
||||||
return UniTask.CompletedTask;
|
return UniTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ReleaseMatchedGems(Vector2Int position) {
|
||||||
|
List<GemView> gemsViews = this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
|
||||||
|
Gem currentGem = this.gameBoard.GetGemAt(position);
|
||||||
|
if (currentGem != null)
|
||||||
|
{
|
||||||
|
GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem);
|
||||||
|
if (gemView is null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.objectPool.Release(gemView);
|
||||||
|
RemovePresenterFor(gemView);
|
||||||
|
this.gameBoard.SetGemAt(position, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async UniTask MoveGemsDown() {
|
private async UniTask MoveGemsDown() {
|
||||||
await UniTask.Delay(50);
|
await UniTask.Delay(50);
|
||||||
@@ -313,22 +288,6 @@ namespace Services {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DestroyMatchedGems(Vector2Int position) {
|
|
||||||
List<GemView> gemsViews = this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
|
|
||||||
Gem currentGem = this.gameBoard.GetGemAt(position);
|
|
||||||
if (currentGem != null)
|
|
||||||
{
|
|
||||||
GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem);
|
|
||||||
if (gemView is null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.objectPool.Release(gemView);
|
|
||||||
RemovePresenterFor(gemView);
|
|
||||||
this.gameBoard.SetGemAt(position, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Utils
|
#region Utils
|
||||||
private void RemovePresenterFor(GemView gemView) {
|
private void RemovePresenterFor(GemView gemView) {
|
||||||
@@ -339,11 +298,6 @@ namespace Services {
|
|||||||
GemPresenter presenter = this.gemPresenters.FirstOrDefault(p => p.GemView == gemView);
|
GemPresenter presenter = this.gemPresenters.FirstOrDefault(p => p.GemView == gemView);
|
||||||
this.gemPresenters.Remove(presenter);
|
this.gemPresenters.Remove(presenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool AreAdjacentCardinal(Vector2Int a, Vector2Int b) {
|
|
||||||
Vector2Int d = b - a;
|
|
||||||
return (Mathf.Abs(d.x) == 1 && d.y == 0) || (Mathf.Abs(d.y) == 1 && d.x == 0);
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Cysharp.Threading.Tasks;
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Enums;
|
||||||
using Models.Interfaces;
|
using Models.Interfaces;
|
||||||
using Structs;
|
using Structs;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
@@ -12,9 +13,10 @@ namespace Services.Interfaces
|
|||||||
public BombSpawnRequest? PendingBombSpawn { get; }
|
public BombSpawnRequest? PendingBombSpawn { get; }
|
||||||
|
|
||||||
void SetLastSwap(Vector2Int from, Vector2Int to);
|
void SetLastSwap(Vector2Int from, Vector2Int to);
|
||||||
void ClearPendingBombs();
|
|
||||||
|
|
||||||
void DetectBombSpawnFromLastSwap(List<Gem> currentMatches);
|
void DetectBombSpawnFromLastSwap(List<Gem> currentMatches);
|
||||||
|
List<Vector2Int> ApplyPendingBombSpawns(Action<Vector2Int, GemType, bool> spawnGem);
|
||||||
|
UniTask<List<Vector2Int>> GetInitialBombs(List<Vector2Int> protectedPositions, List<Vector2Int> bombCandidates);
|
||||||
|
|
||||||
UniTask DetonateChainAsync(
|
UniTask DetonateChainAsync(
|
||||||
IReadOnlyList<Vector2Int> initialBombs,
|
IReadOnlyList<Vector2Int> initialBombs,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using Enums;
|
using Enums;
|
||||||
using Structs;
|
using Structs;
|
||||||
@@ -6,6 +7,7 @@ using Structs;
|
|||||||
namespace Services.Interfaces {
|
namespace Services.Interfaces {
|
||||||
public interface IMatchService {
|
public interface IMatchService {
|
||||||
List<Gem> CurrentMatches { get; }
|
List<Gem> CurrentMatches { get; }
|
||||||
|
UniTask<List<Vector2Int>> GetMatchPositionsAsync(List<Vector2Int> protectedPositions);
|
||||||
bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck);
|
bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck);
|
||||||
void FindAllMatches();
|
void FindAllMatches();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
using Enums;
|
using Enums;
|
||||||
using Models.Interfaces;
|
using Models.Interfaces;
|
||||||
using Services.Interfaces;
|
using Services.Interfaces;
|
||||||
@@ -50,6 +51,22 @@ namespace Services {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UniTask<List<Vector2Int>> GetMatchPositionsAsync(List<Vector2Int> protectedPositions) {
|
||||||
|
List<Vector2Int> matchPositions = new List<Vector2Int>(CurrentMatches.Count);
|
||||||
|
for (int i = 0; i < CurrentMatches.Count; i++) {
|
||||||
|
Gem match = CurrentMatches[i];
|
||||||
|
if (match == null) continue;
|
||||||
|
|
||||||
|
Vector2Int pos = match.Position;
|
||||||
|
if (protectedPositions != null && protectedPositions.Contains(pos))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
matchPositions.Add(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return UniTask.FromResult(matchPositions);
|
||||||
|
}
|
||||||
|
|
||||||
public void FindAllMatches() {
|
public void FindAllMatches() {
|
||||||
this.currentMatches.Clear();
|
this.currentMatches.Clear();
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,10 @@ namespace Utils {
|
|||||||
public static Vector2Int ToVector2Int(this Vector2 v) {
|
public static Vector2Int ToVector2Int(this Vector2 v) {
|
||||||
return new Vector2Int((int)v.x, (int)v.y);
|
return new Vector2Int((int)v.x, (int)v.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsAdjacent(this Vector2Int a, Vector2Int b) {
|
||||||
|
Vector2Int d = b - a;
|
||||||
|
return (Mathf.Abs(d.x) == 1 && d.y == 0) || (Mathf.Abs(d.y) == 1 && d.x == 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user