Move Bomb Responsibilities to BombService
This commit is contained in:
@@ -17,32 +17,48 @@ using Object = UnityEngine.Object;
|
||||
namespace Services {
|
||||
public class GameBoardService : IGameBoardService, ITickable, IDisposable {
|
||||
#region Inject
|
||||
private readonly IGameBoard gameBoard;
|
||||
private readonly GameVariables gameVariables;
|
||||
private readonly IMatchService matchService;
|
||||
private readonly IScoreService scoreService;
|
||||
private readonly AudioPresenter audioPresenter;
|
||||
private readonly IBombService bombService;
|
||||
|
||||
private readonly IGameBoard gameBoard;
|
||||
private readonly IObjectPool<GemView> objectPool;
|
||||
|
||||
private readonly IMatchService matchService;
|
||||
private readonly IBombService bombService;
|
||||
private readonly IScoreService scoreService;
|
||||
|
||||
private readonly ScorePresenter scorePresenter;
|
||||
private readonly AudioPresenter audioPresenter;
|
||||
|
||||
private readonly Transform gemsHolder;
|
||||
private readonly Transform backgroundHolder;
|
||||
#endregion
|
||||
|
||||
#region Variables
|
||||
private readonly List<GemPresenter> gemPresenters = new List<GemPresenter>();
|
||||
private readonly ScorePresenter scorePresenter;
|
||||
private GameState currentState = GameState.Move;
|
||||
#endregion
|
||||
|
||||
public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IBombService bombService, IObjectPool<GemView> objectPool, Transform gemsHolder, ScorePresenter scorePresenter, AudioPresenter audioPresenter) {
|
||||
this.gameBoard = gameBoard;
|
||||
public GameBoardService(
|
||||
GameVariables gameVariables,
|
||||
IGameBoard gameBoard,
|
||||
IObjectPool<GemView> objectPool,
|
||||
IMatchService matchService,
|
||||
IBombService bombService,
|
||||
IScoreService scoreService,
|
||||
ScorePresenter scorePresenter,
|
||||
AudioPresenter audioPresenter,
|
||||
Transform gemsHolder,
|
||||
Transform backgroundHolder) {
|
||||
this.gameVariables = gameVariables;
|
||||
this.matchService = matchService;
|
||||
this.scoreService = scoreSerivce;
|
||||
this.bombService = bombService;
|
||||
this.gameBoard = gameBoard;
|
||||
this.objectPool = objectPool;
|
||||
this.gemsHolder = gemsHolder;
|
||||
this.matchService = matchService;
|
||||
this.bombService = bombService;
|
||||
this.scoreService = scoreService;
|
||||
this.scorePresenter = scorePresenter;
|
||||
this.audioPresenter = audioPresenter;
|
||||
this.gemsHolder = gemsHolder;
|
||||
this.backgroundHolder = backgroundHolder;
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
@@ -60,65 +76,42 @@ namespace Services {
|
||||
for (int y = 0; y < this.gameBoard.Height; y++)
|
||||
{
|
||||
Vector2 position = new Vector2(x, y);
|
||||
GameObject backgroundTile = Object.Instantiate(this.gameVariables.bgTilePrefabs, position, Quaternion.identity);
|
||||
backgroundTile.transform.SetParent(this.gemsHolder);
|
||||
backgroundTile.name = "BG Tile - " + x + ", " + y;
|
||||
|
||||
int gemToUse = RandomUtils.RandomGemTypeAsInt();
|
||||
SpawnBackgroundTile(position);
|
||||
|
||||
int iterations = 0;
|
||||
while (this.matchService.MatchesAt(new Vector2Int(x, y), (GemType)gemToUse) && iterations < 100)
|
||||
{
|
||||
int gemToUse = -1;
|
||||
do {
|
||||
gemToUse = RandomUtils.RandomGemTypeAsInt();
|
||||
iterations++;
|
||||
}
|
||||
} while (this.matchService.MatchesAt(position.ToVector2Int(), (GemType)gemToUse) && iterations < 100);
|
||||
|
||||
SpawnGem(new Vector2Int(x, y), (GemType)gemToUse);
|
||||
SpawnGem(position.ToVector2Int(), (GemType)gemToUse);
|
||||
}
|
||||
|
||||
this.currentState = GameState.Move;
|
||||
}
|
||||
|
||||
private void SpawnBackgroundTile(Vector2 position) {
|
||||
GameObject backgroundTile = Object.Instantiate(this.gameVariables.bgTilePrefabs, position, Quaternion.identity);
|
||||
backgroundTile.transform.SetParent(this.backgroundHolder);
|
||||
backgroundTile.name = "BG Tile - " + position.x + ", " + position.y;
|
||||
}
|
||||
|
||||
//Uses the ObjectPool to spawn a gem at the given position
|
||||
private void SpawnGem(Vector2Int position, GemType gemType) {
|
||||
GemView gemView = this.objectPool.Get(gemType, position, this.gameVariables.dropHeight);
|
||||
gemView.name = "Gem - " + position.x + ", " + position.y + ' ' + gemType;
|
||||
|
||||
GemTypeValues gemValue = GemUtils.GetGemValues(gemType, this.gameVariables.gemsPrefabs);
|
||||
private void SpawnGem(Vector2Int position, GemType gemType, bool isBomb = false) {
|
||||
if (isBomb) {
|
||||
DestroyMatchedGems(position);
|
||||
}
|
||||
|
||||
// If we randomly spawned a bomb, give it a random color group (so it can match by color).
|
||||
Gem gem = new Gem(gemType, position, gemValue);
|
||||
|
||||
gemView.Bind(gem, gemValue);
|
||||
GemView gemView = this.objectPool.Get(isBomb ? GemType.Bomb : gemType, position, isBomb ? 0 : this.gameVariables.dropHeight);
|
||||
gemView.name = "Gem - " + position.x + ", " + position.y + ' ' + gemType;
|
||||
|
||||
GemTypeValues gemValue = GemUtils.GetGemValues(gemType, this.gameVariables.gemsPrefabs);
|
||||
Gem gem = new Gem(isBomb ? GemType.Bomb : gemType, position, gemValue, gemType);
|
||||
gemView.Bind(gem, gemValue, isBomb: isBomb);
|
||||
|
||||
this.gemPresenters.Add(new GemPresenter(gem, gemView));
|
||||
SetGem(new Vector2Int(position.x, position.y), gem);
|
||||
}
|
||||
|
||||
private void SpawnBomb(Vector2Int position, GemType color) {
|
||||
// remove existing gem/view at that position
|
||||
DestroyMatchedGems(position);
|
||||
|
||||
GemView gemView = this.objectPool.Get(GemType.Bomb, position, 0);
|
||||
gemView.name = "Bomb - " + position.x + ", " + position.y + ' ' + GemType.Bomb;
|
||||
|
||||
GemTypeValues gemValue = GemUtils.GetGemValues(color, this.gameVariables.gemsPrefabs);
|
||||
|
||||
Gem bombGem = new Gem(GemType.Bomb, position, gemValue, color);
|
||||
gemView.Bind(bombGem, gemValue, isBomb: true);
|
||||
|
||||
this.gemPresenters.Add(new GemPresenter(bombGem, gemView));
|
||||
SetGem(position, bombGem);
|
||||
}
|
||||
|
||||
//Sets the gem on the GameBoard
|
||||
private void SetGem(Vector2Int position, Gem gem) {
|
||||
this.gameBoard.SetGemAt(new Vector2Int(position.x, position.y), gem);
|
||||
}
|
||||
|
||||
//Gets the gem from the GameBoard
|
||||
private Gem GetGem(Vector2Int position) {
|
||||
return this.gameBoard.GetGemAt(position);
|
||||
this.gameBoard.SetGemAt(position, gem);
|
||||
}
|
||||
|
||||
//Listens to InputService OnSwapRequest
|
||||
@@ -126,60 +119,58 @@ namespace Services {
|
||||
if (this.currentState != GameState.Move)
|
||||
return false;
|
||||
|
||||
if (!InBounds(from) || !InBounds(to))
|
||||
if (!GemUtils.IsInBounds(from, this.gameBoard) || !GemUtils.IsInBounds(to, this.gameBoard))
|
||||
return false;
|
||||
|
||||
if (!AreAdjacentCardinal(from, to))
|
||||
return false;
|
||||
|
||||
Gem fromGem = GetGem(from);
|
||||
Gem toGem = GetGem(to);
|
||||
|
||||
if(fromGem == null || toGem == null)
|
||||
return false;
|
||||
|
||||
this.currentState = GameState.Wait;
|
||||
|
||||
ApplySwap(from, to, fromGem, toGem);
|
||||
ApplySwap(from, to);
|
||||
|
||||
await UniTask.Delay(600);
|
||||
this.matchService.SetLastSwap(from, to);
|
||||
this.bombService.SetLastSwap(from, to);
|
||||
this.bombService.ClearPendingBombs();
|
||||
this.matchService.FindAllMatches();
|
||||
bool hasMatch = this.matchService.CurrentMatches.Count > 0;
|
||||
|
||||
if (!hasMatch) {
|
||||
ApplySwap(to, from, fromGem, toGem);
|
||||
this.bombService.DetectBombSpawnFromLastSwap(this.matchService.CurrentMatches);
|
||||
|
||||
if (this.matchService.CurrentMatches.Count == 0) {
|
||||
ApplySwap(to, from);
|
||||
await UniTask.Delay(600);
|
||||
this.currentState = GameState.Move;
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Vector2Int> protectedPositions = ApplyPendingBombSpawns();
|
||||
|
||||
await DestroyMatchesAsync(protectedPositions);
|
||||
this.currentState = GameState.Move;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ApplySwap(Vector2Int posA, Vector2Int posB, Gem gemA, Gem gemB) {
|
||||
private void ApplySwap(Vector2Int from, Vector2Int to) {
|
||||
Gem fromGem = this.gameBoard.GetGemAt(from);
|
||||
Gem toGem = this.gameBoard.GetGemAt(to);
|
||||
// swap their stored positions
|
||||
gemA.SetPosition(posB);
|
||||
gemB.SetPosition(posA);
|
||||
fromGem.SetPosition(to);
|
||||
toGem.SetPosition(from);
|
||||
|
||||
// update grid
|
||||
SetGem(posA, gemB);
|
||||
SetGem(posB, gemA);
|
||||
this.gameBoard.SetGemAt(from, toGem);
|
||||
this.gameBoard.SetGemAt(to, fromGem);
|
||||
}
|
||||
|
||||
private List<Vector2Int> ApplyPendingBombSpawns() {
|
||||
List<Vector2Int> positions = new List<Vector2Int>();
|
||||
|
||||
foreach (BombSpawnRequest bomSpawnRequest in this.matchService.PendingBombSpawns) {
|
||||
positions.Add(bomSpawnRequest.Position);
|
||||
SpawnBomb(bomSpawnRequest.Position, bomSpawnRequest.Color);
|
||||
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.matchService.ClearPendingBombs();
|
||||
this.bombService.ClearPendingBombs();
|
||||
return positions;
|
||||
}
|
||||
|
||||
@@ -196,16 +187,16 @@ namespace Services {
|
||||
matchPositions.Add(pos);
|
||||
}
|
||||
|
||||
IReadOnlyList<Vector2Int> bombCandidates = this.bombService.CollectTriggeredBombs(matchPositions);
|
||||
IReadOnlyList<Vector2Int> bombCandidates = matchPositions.Distinct().ToList();
|
||||
|
||||
List<Vector2Int> initialBombs = new List<Vector2Int>();
|
||||
foreach (Vector2Int p in bombCandidates) {
|
||||
if (!InBounds(p)) continue;
|
||||
if (!GemUtils.IsInBounds(p, this.gameBoard)) continue;
|
||||
|
||||
if (protectedPositions != null && protectedPositions.Contains(p))
|
||||
continue;
|
||||
|
||||
Gem gem = GetGem(p);
|
||||
Gem gem = this.gameBoard.GetGemAt(p);
|
||||
if (gem is { Type: GemType.Bomb })
|
||||
initialBombs.Add(p);
|
||||
}
|
||||
@@ -216,31 +207,20 @@ namespace Services {
|
||||
if (initialBombs.Count > 0) {
|
||||
await this.bombService.DetonateChainAsync(
|
||||
initialBombs,
|
||||
InBounds,
|
||||
GetGem,
|
||||
DestroyAtAsync,
|
||||
this.gameVariables.bombRadius,
|
||||
this.gameVariables.bombDelay);
|
||||
this.gameBoard);
|
||||
|
||||
await MoveGemsDown();
|
||||
return;
|
||||
}
|
||||
|
||||
bool willBreakAnyNonBombGem = false;
|
||||
foreach (Vector2Int pos in matchPositions) {
|
||||
Gem gem = GetGem(pos);
|
||||
if (gem == null) continue;
|
||||
if (gem.Type == GemType.Bomb) continue;
|
||||
|
||||
willBreakAnyNonBombGem = true;
|
||||
break;
|
||||
}
|
||||
bool willBreakAnyNonBombGem = matchPositions.Select(pos => this.gameBoard.GetGemAt(pos)).Where(gem => gem != null).Any(gem => gem.Type != GemType.Bomb);
|
||||
|
||||
if (willBreakAnyNonBombGem)
|
||||
this.audioPresenter.OnMatch(this.gameVariables.matchSfx);
|
||||
|
||||
foreach (Vector2Int pos in matchPositions.Distinct().ToList()) {
|
||||
Gem gem = GetGem(pos);
|
||||
Gem gem = this.gameBoard.GetGemAt(pos);
|
||||
if (gem == null) continue;
|
||||
if (gem.Type == GemType.Bomb) continue;
|
||||
|
||||
@@ -250,17 +230,14 @@ namespace Services {
|
||||
|
||||
await this.bombService.DetonateChainAsync(
|
||||
initialBombs,
|
||||
InBounds,
|
||||
GetGem,
|
||||
DestroyAtAsync,
|
||||
this.gameVariables.bombRadius,
|
||||
this.gameVariables.bombDelay);
|
||||
this.gameBoard);
|
||||
|
||||
await MoveGemsDown();
|
||||
}
|
||||
|
||||
private UniTask DestroyAtAsync(Vector2Int pos) {
|
||||
Gem gem = GetGem(pos);
|
||||
Gem gem = this.gameBoard.GetGemAt(pos);
|
||||
if (gem == null)
|
||||
return UniTask.CompletedTask;
|
||||
|
||||
@@ -288,8 +265,8 @@ namespace Services {
|
||||
else if (nullCounter > 0)
|
||||
{
|
||||
currentGem.SetPosition(new Vector2Int(currentGem.Position.x, currentGem.Position.y - nullCounter));
|
||||
SetGem(currentGem.Position, currentGem);
|
||||
SetGem(new Vector2Int(x,y), null);
|
||||
this.gameBoard.SetGemAt(currentGem.Position, currentGem);
|
||||
this.gameBoard.SetGemAt(new Vector2Int(x,y), null);
|
||||
}
|
||||
}
|
||||
nullCounter = 0;
|
||||
@@ -338,7 +315,7 @@ namespace Services {
|
||||
}
|
||||
|
||||
private void DestroyMatchedGems(Vector2Int position) {
|
||||
List<GemView> gemsViews = GemsViews();
|
||||
List<GemView> gemsViews = this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
|
||||
Gem currentGem = this.gameBoard.GetGemAt(position);
|
||||
if (currentGem != null)
|
||||
{
|
||||
@@ -349,7 +326,7 @@ namespace Services {
|
||||
|
||||
this.objectPool.Release(gemView);
|
||||
RemovePresenterFor(gemView);
|
||||
SetGem(position, null);
|
||||
this.gameBoard.SetGemAt(position, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,14 +340,6 @@ namespace Services {
|
||||
this.gemPresenters.Remove(presenter);
|
||||
}
|
||||
|
||||
private List<GemView> GemsViews() {
|
||||
return this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
|
||||
}
|
||||
|
||||
private bool InBounds(Vector2Int p) {
|
||||
return p.x >= 0 && p.x < this.gameBoard.Width && p.y >= 0 && p.y < this.gameBoard.Height;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user