Adjust Bomb Behavior

This commit is contained in:
2025-12-17 00:11:17 +08:00
parent 4c3980d84d
commit de2de35b1b
5 changed files with 84 additions and 47 deletions

View File

@@ -53,7 +53,7 @@ MonoBehaviour:
scoreValue: 10 scoreValue: 10
width: 7 width: 7
height: 7 height: 7
bombDelay: 1 bombDelay: 2
bombSelfDelay: 1 bombSelfDelay: 1
bombRadius: 2 bombRadius: 2
dropHeight: 2 dropHeight: 2

View File

@@ -18,8 +18,6 @@ namespace ScriptableObjects {
[Header("Bomb")] [Header("Bomb")]
[Tooltip("How long before the gems around the bomb explode")] [Tooltip("How long before the gems around the bomb explode")]
public float bombDelay = 0.1f; public float bombDelay = 0.1f;
[Tooltip("How long before the bomb itself explodes")]
public float bombSelfDelay = 0.05f;
[Tooltip("How far the explosion reaches")] [Tooltip("How far the explosion reaches")]
public int bombRadius = 1; public int bombRadius = 1;

View File

@@ -42,67 +42,98 @@ namespace Services
Func<Vector2Int, Gem> getGemAt, Func<Vector2Int, Gem> getGemAt,
Func<Vector2Int, UniTask> destroyAtAsync, Func<Vector2Int, UniTask> destroyAtAsync,
int radius, int radius,
float bombDelaySeconds, float bombDelaySeconds)
float bombSelfDelaySeconds)
{ {
if (initialBombs == null || initialBombs.Count == 0) if (initialBombs == null || initialBombs.Count == 0)
return; return;
Queue<Vector2Int> queue = new Queue<Vector2Int>(initialBombs); int waveDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombDelaySeconds * 1000f));
HashSet<Vector2Int> processed = new HashSet<Vector2Int>();
while (queue.Count > 0) HashSet<Vector2Int> processedBombs = new HashSet<Vector2Int>();
{
Vector2Int bombPos = queue.Dequeue();
if (processed.Contains(bombPos))
continue;
if (!inBounds(bombPos)) Queue<Vector2Int> waveQueue = new Queue<Vector2Int>(
continue; initialBombs.Where(p =>
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) if (!inBounds(p)) return false;
await UniTask.Delay(ringDelayMs); Gem g = getGemAt(p);
return g is { Type: GemType.Bomb };
})
);
foreach (Vector2Int n in DiamondRing(bombPos, dist)) while (waveQueue.Count > 0)
{
// current wave (per bomb)
List<Vector2Int> waveBombs = new List<Vector2Int>();
while (waveQueue.Count > 0)
{
Vector2Int b = waveQueue.Dequeue();
if (processedBombs.Contains(b))
continue;
if (!inBounds(b))
continue;
Gem g = getGemAt(b);
if (g is not { Type: GemType.Bomb })
continue;
processedBombs.Add(b);
waveBombs.Add(b);
}
if (waveBombs.Count == 0)
continue;
// delay once per wave
if (waveDelayMs > 0)
await UniTask.Delay(waveDelayMs);
HashSet<Vector2Int> nextWaveBombs = new HashSet<Vector2Int>();
HashSet<Vector2Int> toDestroyNow = new HashSet<Vector2Int>();
for (int i = 0; i < waveBombs.Count; i++)
{
Vector2Int bombPos = waveBombs[i];
// destroy self when it detonates
toDestroyNow.Add(bombPos);
foreach (Vector2Int p in DiamondAreaInclusive(bombPos, radius))
{ {
if (!inBounds(n)) if (!inBounds(p))
continue; continue;
Gem g = getGemAt(n); if (p == bombPos)
if (g == null)
continue; continue;
if (g.Type == GemType.Bomb) Gem cellGem = getGemAt(p);
if (cellGem == null)
continue;
if (cellGem.Type == GemType.Bomb)
{ {
if (!processed.Contains(n)) // bombs in range are NOT destroyed now. triggered to explode in a later wave.
queue.Enqueue(n); if (!processedBombs.Contains(p))
nextWaveBombs.Add(p);
continue; continue;
} }
await destroyAtAsync(n); // Non-bomb gem gets destroyed by this bomb
toDestroyNow.Add(p);
} }
} }
int selfDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombSelfDelaySeconds * 1000f)); // Destroy everything for this wave (non-bombs in range + the detonating bombs themselves)
if (selfDelayMs > 0) foreach (Vector2Int p in toDestroyNow)
await UniTask.Delay(selfDelayMs); await destroyAtAsync(p);
Gem stillBomb = getGemAt(bombPos); // Schedule the next wave (triggered bombs)
if (stillBomb is { Type: GemType.Bomb }) foreach (Vector2Int b in nextWaveBombs)
await destroyAtAsync(bombPos); waveQueue.Enqueue(b);
} }
} }
private static IEnumerable<Vector2Int> DiamondRing(Vector2Int center, int distance) private static IEnumerable<Vector2Int> DiamondRing(Vector2Int center, int distance)
{ {
for (int distanceX = -distance; distanceX <= distance; distanceX++) for (int distanceX = -distance; distanceX <= distance; distanceX++)
@@ -119,5 +150,16 @@ namespace Services
} }
} }
} }
private static IEnumerable<Vector2Int> DiamondAreaInclusive(Vector2Int center, int radius)
{
yield return center;
for (int dist = 1; dist <= radius; dist++)
{
foreach (Vector2Int pivot in DiamondRing(center, dist))
yield return pivot;
}
}
} }
} }

View File

@@ -217,8 +217,7 @@ namespace Services {
GetGem, GetGem,
DestroyAtAsync, DestroyAtAsync,
this.gameVariables.bombRadius, this.gameVariables.bombRadius,
this.gameVariables.bombDelay, this.gameVariables.bombDelay);
this.gameVariables.bombSelfDelay);
await MoveGemsDown(); await MoveGemsDown();
return; return;
@@ -239,8 +238,7 @@ namespace Services {
GetGem, GetGem,
DestroyAtAsync, DestroyAtAsync,
this.gameVariables.bombRadius, this.gameVariables.bombRadius,
this.gameVariables.bombDelay, this.gameVariables.bombDelay);
this.gameVariables.bombSelfDelay);
await MoveGemsDown(); await MoveGemsDown();
} }

View File

@@ -15,7 +15,6 @@ namespace Services.Interfaces
Func<Vector2Int, Gem> getGemAt, Func<Vector2Int, Gem> getGemAt,
Func<Vector2Int, UniTask> destroyAtAsync, Func<Vector2Int, UniTask> destroyAtAsync,
int radius, int radius,
float bombDelaySeconds, float bombDelaySeconds);
float bombSelfDelaySeconds);
} }
} }