117 lines
3.8 KiB
C#
117 lines
3.8 KiB
C#
// Assets/Scripts/Services/BombService.cs
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Cysharp.Threading.Tasks;
|
|
using Enums;
|
|
using Models;
|
|
using Services.Interfaces;
|
|
using UnityEngine;
|
|
|
|
namespace Services
|
|
{
|
|
public class BombService : IBombService
|
|
{
|
|
public IReadOnlyList<Vector2Int> CollectTriggeredBombs(IReadOnlyList<Vector2Int> matchPositions)
|
|
{
|
|
if (matchPositions == null || matchPositions.Count == 0)
|
|
return Array.Empty<Vector2Int>();
|
|
|
|
HashSet<Vector2Int> candidates = new HashSet<Vector2Int>();
|
|
|
|
foreach (Vector2Int p in matchPositions)
|
|
{
|
|
candidates.Add(p + Vector2Int.left);
|
|
candidates.Add(p + Vector2Int.right);
|
|
candidates.Add(p + Vector2Int.up);
|
|
candidates.Add(p + Vector2Int.down);
|
|
}
|
|
|
|
return candidates.ToList();
|
|
}
|
|
|
|
public async UniTask DetonateChainAsync(
|
|
IReadOnlyList<Vector2Int> initialBombs,
|
|
Func<Vector2Int, bool> inBounds,
|
|
Func<Vector2Int, Gem> getGemAt,
|
|
Func<Vector2Int, UniTask> destroyAtAsync,
|
|
int radius,
|
|
float bombDelaySeconds,
|
|
float bombSelfDelaySeconds)
|
|
{
|
|
if (initialBombs == null || initialBombs.Count == 0)
|
|
return;
|
|
|
|
Queue<Vector2Int> queue = new Queue<Vector2Int>(initialBombs);
|
|
HashSet<Vector2Int> processed = new HashSet<Vector2Int>();
|
|
|
|
while (queue.Count > 0)
|
|
{
|
|
Vector2Int bombPos = queue.Dequeue();
|
|
if (processed.Contains(bombPos))
|
|
continue;
|
|
|
|
if (!inBounds(bombPos))
|
|
continue;
|
|
|
|
Gem bomb = getGemAt(bombPos);
|
|
if (bomb is not { Type: GemType.Bomb })
|
|
continue;
|
|
|
|
processed.Add(bombPos);
|
|
|
|
int ringDelayMs = Mathf.RoundToInt(bombDelaySeconds * 1000f);
|
|
|
|
for (int dist = 1; dist <= radius; dist++)
|
|
{
|
|
if (ringDelayMs > 0)
|
|
await UniTask.Delay(ringDelayMs);
|
|
|
|
foreach (Vector2Int n in DiamondRing(bombPos, dist))
|
|
{
|
|
if (!inBounds(n))
|
|
continue;
|
|
|
|
Gem g = getGemAt(n);
|
|
if (g == null)
|
|
continue;
|
|
|
|
if (g.Type == GemType.Bomb)
|
|
{
|
|
if (!processed.Contains(n))
|
|
queue.Enqueue(n);
|
|
continue;
|
|
}
|
|
|
|
await destroyAtAsync(n);
|
|
}
|
|
}
|
|
|
|
int selfDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombSelfDelaySeconds * 1000f));
|
|
if (selfDelayMs > 0)
|
|
await UniTask.Delay(selfDelayMs);
|
|
|
|
Gem stillBomb = getGemAt(bombPos);
|
|
if (stillBomb is { Type: GemType.Bomb })
|
|
await destroyAtAsync(bombPos);
|
|
}
|
|
}
|
|
|
|
private static IEnumerable<Vector2Int> DiamondRing(Vector2Int center, int distance)
|
|
{
|
|
for (int distanceX = -distance; distanceX <= distance; distanceX++)
|
|
{
|
|
int distanceY = distance - Mathf.Abs(distanceX);
|
|
if (distanceY == 0)
|
|
{
|
|
yield return new Vector2Int(center.x + distanceX, center.y);
|
|
}
|
|
else
|
|
{
|
|
yield return new Vector2Int(center.x + distanceX, center.y + distanceY);
|
|
yield return new Vector2Int(center.x + distanceX, center.y - distanceY);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |