Move Bomb Responsibilities to BombService
This commit is contained in:
@@ -1,45 +1,90 @@
|
||||
// Assets/Scripts/Services/BombService.cs
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using Enums;
|
||||
using Models.Interfaces;
|
||||
using ScriptableObjects;
|
||||
using Services.Interfaces;
|
||||
using Structs;
|
||||
using UnityEngine;
|
||||
using Utils;
|
||||
|
||||
namespace Services
|
||||
{
|
||||
public class BombService : IBombService
|
||||
{
|
||||
public IReadOnlyList<Vector2Int> CollectTriggeredBombs(IReadOnlyList<Vector2Int> matchPositions)
|
||||
{
|
||||
if (matchPositions == null || matchPositions.Count == 0)
|
||||
return Array.Empty<Vector2Int>();
|
||||
|
||||
|
||||
return matchPositions.Distinct().ToList();
|
||||
public class BombService : IBombService {
|
||||
private readonly GameVariables gameVariables;
|
||||
private readonly IGameBoard gameBoard;
|
||||
|
||||
private Vector2Int lastSwapFrom;
|
||||
private Vector2Int lastSwapTo;
|
||||
|
||||
private BombSpawnRequest? pendingBombSpawn;
|
||||
public BombSpawnRequest? PendingBombSpawn => this.pendingBombSpawn;
|
||||
|
||||
public void ClearPendingBombs() {
|
||||
this.pendingBombSpawn = null;
|
||||
}
|
||||
|
||||
public BombService(GameVariables gameVariables, IGameBoard gameBoard) {
|
||||
this.gameVariables = gameVariables;
|
||||
this.gameBoard = gameBoard;
|
||||
}
|
||||
|
||||
public void SetLastSwap(Vector2Int from, Vector2Int to) {
|
||||
this.lastSwapFrom = from;
|
||||
this.lastSwapTo = to;
|
||||
}
|
||||
|
||||
public void DetectBombSpawnFromLastSwap(List<Gem> currentMatches) {
|
||||
Vector2Int from = this.lastSwapFrom;
|
||||
Vector2Int to = this.lastSwapTo;
|
||||
|
||||
TryCreateBombSpawnAt(from, currentMatches);
|
||||
TryCreateBombSpawnAt(to, currentMatches);
|
||||
}
|
||||
|
||||
private void TryCreateBombSpawnAt(Vector2Int pivot, List<Gem> currentMatches) {
|
||||
Gem pivotGem = this.gameBoard.GetGemAt(pivot);
|
||||
if (pivotGem == null)
|
||||
return;
|
||||
|
||||
// If it's already a bomb, don't create another.
|
||||
if (pivotGem.Type == GemType.Bomb)
|
||||
return;
|
||||
|
||||
if (currentMatches.All(g => g.Position != pivot))
|
||||
return;
|
||||
|
||||
// Only create a bomb if pivot is part of a straight 4+ line of the SAME color.
|
||||
int longestLine = GetLongestMatchedLineThroughPivot(pivot, pivotGem.MatchColor);
|
||||
if (longestLine < 4)
|
||||
return;
|
||||
|
||||
// Prevent duplicates for the same cell.
|
||||
if (this.pendingBombSpawn.GetValueOrDefault().Position == pivot)
|
||||
return;
|
||||
|
||||
this.pendingBombSpawn = new BombSpawnRequest(pivot, pivotGem.MatchColor);
|
||||
}
|
||||
|
||||
public async UniTask DetonateChainAsync(
|
||||
IReadOnlyList<Vector2Int> initialBombs,
|
||||
Func<Vector2Int, bool> inBounds,
|
||||
Func<Vector2Int, Gem> getGemAt,
|
||||
Func<Vector2Int, UniTask> destroyAtAsync,
|
||||
int radius,
|
||||
float bombDelaySeconds)
|
||||
IGameBoard gameBoard)
|
||||
{
|
||||
if (initialBombs == null || initialBombs.Count == 0)
|
||||
return;
|
||||
|
||||
int waveDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombDelaySeconds * 1000f));
|
||||
int waveDelayMs = Mathf.RoundToInt(this.gameVariables.bombDelay * 1000f);
|
||||
|
||||
HashSet<Vector2Int> processedBombs = new HashSet<Vector2Int>();
|
||||
|
||||
Queue<Vector2Int> waveQueue = new Queue<Vector2Int>(
|
||||
initialBombs.Where(p =>
|
||||
{
|
||||
if (!inBounds(p)) return false;
|
||||
Gem g = getGemAt(p);
|
||||
if (!GemUtils.IsInBounds(p, gameBoard)) return false;
|
||||
Gem g = gameBoard.GetGemAt(p);
|
||||
return g is { Type: GemType.Bomb };
|
||||
})
|
||||
);
|
||||
@@ -54,10 +99,10 @@ namespace Services
|
||||
if (processedBombs.Contains(b))
|
||||
continue;
|
||||
|
||||
if (!inBounds(b))
|
||||
if (!GemUtils.IsInBounds(b, gameBoard))
|
||||
continue;
|
||||
|
||||
Gem g = getGemAt(b);
|
||||
Gem g = gameBoard.GetGemAt(b);
|
||||
if (g is not { Type: GemType.Bomb })
|
||||
continue;
|
||||
|
||||
@@ -82,15 +127,15 @@ namespace Services
|
||||
// destroy self when it detonates
|
||||
toDestroyNow.Add(bombPos);
|
||||
|
||||
foreach (Vector2Int p in DiamondAreaInclusive(bombPos, radius))
|
||||
foreach (Vector2Int p in DiamondAreaInclusive(bombPos, this.gameVariables.bombRadius))
|
||||
{
|
||||
if (!inBounds(p))
|
||||
if (!GemUtils.IsInBounds(p, gameBoard))
|
||||
continue;
|
||||
|
||||
if (p == bombPos)
|
||||
continue;
|
||||
|
||||
Gem cellGem = getGemAt(p);
|
||||
Gem cellGem = gameBoard.GetGemAt(p);
|
||||
if (cellGem == null)
|
||||
continue;
|
||||
|
||||
@@ -130,5 +175,31 @@ namespace Services
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int GetLongestMatchedLineThroughPivot(Vector2Int pivot, GemType color) {
|
||||
int horizontal = 1 + CountSameColorInDirection(pivot, Vector2Int.left, color)
|
||||
+ CountSameColorInDirection(pivot, Vector2Int.right, color);
|
||||
|
||||
int vertical = 1 + CountSameColorInDirection(pivot, Vector2Int.up, color)
|
||||
+ CountSameColorInDirection(pivot, Vector2Int.down, color);
|
||||
|
||||
return Mathf.Max(horizontal, vertical);
|
||||
}
|
||||
|
||||
private int CountSameColorInDirection(Vector2Int start, Vector2Int direction, GemType color) {
|
||||
int count = 0;
|
||||
Vector2Int oivot = start + direction;
|
||||
|
||||
while (oivot.x >= 0 && oivot.x < this.gameBoard.Width && oivot.y >= 0 && oivot.y < this.gameBoard.Height) {
|
||||
Gem g = this.gameBoard.GetGemAt(oivot);
|
||||
if (g == null || g.Type == GemType.Bomb || g.MatchColor != color)
|
||||
break;
|
||||
|
||||
count++;
|
||||
oivot += direction;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user