Separate bomb logic
This commit is contained in:
@@ -22,6 +22,7 @@ namespace Services {
|
||||
private readonly GameVariables gameVariables;
|
||||
private readonly IMatchService matchService;
|
||||
private readonly IScoreService scoreService;
|
||||
private readonly IBombService bombService;
|
||||
private readonly IObjectPool<GemView> objectPool;
|
||||
private readonly Transform gemsHolder;
|
||||
#endregion
|
||||
@@ -32,11 +33,12 @@ namespace Services {
|
||||
private GameState currentState = GameState.Move;
|
||||
#endregion
|
||||
|
||||
public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IObjectPool<GemView> objectPool, Transform gemsHolder, ScorePresenter scorePresenter) {
|
||||
public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService, IScoreService scoreSerivce, IBombService bombService, IObjectPool<GemView> objectPool, Transform gemsHolder, ScorePresenter scorePresenter) {
|
||||
this.gameBoard = gameBoard;
|
||||
this.gameVariables = gameVariables;
|
||||
this.matchService = matchService;
|
||||
this.scoreService = scoreSerivce;
|
||||
this.bombService = bombService;
|
||||
this.objectPool = objectPool;
|
||||
this.gemsHolder = gemsHolder;
|
||||
this.scorePresenter = scorePresenter;
|
||||
@@ -179,109 +181,63 @@ namespace Services {
|
||||
}
|
||||
|
||||
private async UniTask DestroyMatchesAsync(List<Vector2Int> protectedPositions) {
|
||||
// Build initial queues from current matches
|
||||
Queue<Vector2Int> bombsToProcess = new Queue<Vector2Int>();
|
||||
List<Vector2Int> processedBombs = new List<Vector2Int>();
|
||||
List<Vector2Int> regularToDestroy = new List<Vector2Int>();
|
||||
|
||||
// Collect match positions, excluding protected (bomb creation slots).
|
||||
List<Vector2Int> matchPositions = new List<Vector2Int>(this.matchService.CurrentMatches.Count);
|
||||
for (int i = 0; i < this.matchService.CurrentMatches.Count; i++) {
|
||||
Gem matchedGem = this.matchService.CurrentMatches[i];
|
||||
if (matchedGem == null)
|
||||
continue;
|
||||
var m = this.matchService.CurrentMatches[i];
|
||||
if (m == null) continue;
|
||||
|
||||
Vector2Int pos = matchedGem.Position;
|
||||
|
||||
// If a bomb was spawned at this cell due to 4+ creation, it must survive this destruction pass.
|
||||
Vector2Int pos = m.Position;
|
||||
if (protectedPositions != null && protectedPositions.Contains(pos))
|
||||
continue;
|
||||
|
||||
Gem current = GetGem(pos);
|
||||
if (current == null)
|
||||
continue;
|
||||
|
||||
if (current.Type == GemType.Bomb) {
|
||||
bombsToProcess.Enqueue(pos);
|
||||
} else {
|
||||
regularToDestroy.Add(pos);
|
||||
}
|
||||
matchPositions.Add(pos);
|
||||
}
|
||||
|
||||
// Process bombs: neighbors first (after delay), then the bomb itself (after delay).
|
||||
while (bombsToProcess.Count > 0) {
|
||||
Vector2Int bombPos = bombsToProcess.Dequeue();
|
||||
if (processedBombs.Contains(bombPos))
|
||||
continue;
|
||||
|
||||
Gem bomb = GetGem(bombPos);
|
||||
if (bomb is not { Type: GemType.Bomb })
|
||||
continue;
|
||||
|
||||
processedBombs.Add(bombPos);
|
||||
|
||||
// Delay before destroying neighbor group
|
||||
if (this.gameVariables.bombDelay > 0f) {
|
||||
int msDelay = Mathf.RoundToInt(this.gameVariables.bombDelay * 1000f);
|
||||
await UniTask.Delay(msDelay);
|
||||
}
|
||||
|
||||
// Collect cross neighbors
|
||||
foreach (Vector2Int neighborPosition in CrossNeighbors(bombPos, this.gameVariables.bombRadius)) {
|
||||
if (!InBounds(neighborPosition))
|
||||
continue;
|
||||
|
||||
Gem g = GetGem(neighborPosition);
|
||||
if (g == null)
|
||||
continue;
|
||||
|
||||
// If we encounter another bomb, queue it (so it explodes too).
|
||||
if (g.Type == GemType.Bomb) {
|
||||
if (!processedBombs.Contains(neighborPosition))
|
||||
bombsToProcess.Enqueue(neighborPosition);
|
||||
continue;
|
||||
}
|
||||
|
||||
regularToDestroy.Add(neighborPosition);
|
||||
}
|
||||
|
||||
// Destroy the neighbor group now
|
||||
foreach (Vector2Int position in regularToDestroy.ToList()) {
|
||||
Gem gem = GetGem(position);
|
||||
if (gem == null)
|
||||
continue;
|
||||
|
||||
this.scoreService.ScoreCheck(gem.ScoreValue);
|
||||
DestroyMatchedGems(position);
|
||||
}
|
||||
regularToDestroy.Clear();
|
||||
|
||||
// Delay before destroying the bomb itself
|
||||
if (this.gameVariables.bombSelfDelay > 0f) {
|
||||
int ms = Mathf.RoundToInt(this.gameVariables.bombSelfDelay * 1000f);
|
||||
await UniTask.Delay(ms);
|
||||
}
|
||||
|
||||
// Destroy the bomb
|
||||
Gem b = GetGem(bombPos);
|
||||
if (b != null && b.Type == GemType.Bomb) {
|
||||
this.scoreService.ScoreCheck(b.ScoreValue);
|
||||
DestroyMatchedGems(bombPos);
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy any remaining regular matches (non-bomb) after bomb processing
|
||||
foreach (Vector2Int pos in regularToDestroy) {
|
||||
Gem g = GetGem(pos);
|
||||
if (g == null)
|
||||
continue;
|
||||
// Bombs are handled by BombService so they can respect delays + chaining.
|
||||
foreach (Vector2Int pos in matchPositions.Distinct().ToList()) {
|
||||
var g = GetGem(pos);
|
||||
if (g == null) continue;
|
||||
if (g.Type == GemType.Bomb) continue;
|
||||
|
||||
this.scoreService.ScoreCheck(g.ScoreValue);
|
||||
DestroyMatchedGems(pos);
|
||||
}
|
||||
|
||||
// Now we can cascade
|
||||
IReadOnlyList<Vector2Int> bombCandidates = this.bombService.CollectTriggeredBombs(matchPositions);
|
||||
|
||||
List<Vector2Int> initialBombs = new List<Vector2Int>();
|
||||
foreach (Vector2Int p in bombCandidates) {
|
||||
if (!InBounds(p)) continue;
|
||||
var g = GetGem(p);
|
||||
if (g is { Type: GemType.Bomb })
|
||||
initialBombs.Add(p);
|
||||
}
|
||||
initialBombs = initialBombs.Distinct().ToList();
|
||||
|
||||
await this.bombService.DetonateChainAsync(
|
||||
initialBombs,
|
||||
InBounds,
|
||||
GetGem,
|
||||
DestroyAtAsync,
|
||||
this.gameVariables.bombRadius,
|
||||
this.gameVariables.bombDelay,
|
||||
this.gameVariables.bombSelfDelay
|
||||
);
|
||||
|
||||
await MoveGemsDown();
|
||||
}
|
||||
|
||||
private UniTask DestroyAtAsync(Vector2Int pos) {
|
||||
Gem gem = GetGem(pos);
|
||||
if (gem == null)
|
||||
return UniTask.CompletedTask;
|
||||
|
||||
this.scoreService.ScoreCheck(gem.ScoreValue);
|
||||
DestroyMatchedGems(pos);
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
private static IEnumerable<Vector2Int> CrossNeighbors(Vector2Int center, int radius) {
|
||||
// center excluded for "neighbors first"
|
||||
for (int i = 1; i <= radius; i++) {
|
||||
|
||||
Reference in New Issue
Block a user