111 lines
3.5 KiB
C#
111 lines
3.5 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 neighborDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombDelaySeconds * 1000f));
|
|
if (neighborDelayMs > 0)
|
|
await UniTask.Delay(neighborDelayMs);
|
|
|
|
foreach (Vector2Int n in DiamondNeighbors(bombPos, radius))
|
|
{
|
|
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> DiamondNeighbors(Vector2Int center, int radius)
|
|
{
|
|
for (int x = -radius; x <= radius; x++)
|
|
{
|
|
int maxY = radius - Mathf.Abs(x);
|
|
for (int y = -maxY; y <= maxY; y++)
|
|
{
|
|
if (x == 0 && y == 0)
|
|
continue;
|
|
|
|
yield return new Vector2Int(center.x + x, center.y + y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |