Change Bomb Implementation
- L and T shapes are no longer considered a match - Bomb explodes WITH gems - Bomb will have a small sprite of the creating gem
This commit is contained in:
@@ -21,21 +21,6 @@ namespace Services
|
||||
return matchPositions.Distinct().ToList();
|
||||
}
|
||||
|
||||
// This is the old implementation wherein any adjacent matches will detonate the bomb
|
||||
private List<Vector2Int> DetonateThroughAdjacentMatches(IReadOnlyList<Vector2Int> matchPositions) {
|
||||
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,
|
||||
@@ -134,31 +119,16 @@ namespace Services
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Vector2Int> DiamondAreaInclusive(Vector2Int center, int radius)
|
||||
{
|
||||
yield return center;
|
||||
|
||||
for (int dist = 1; dist <= radius; dist++)
|
||||
// Manhattan-distance filled diamond:
|
||||
for (int distanceX = -radius; distanceX <= radius; distanceX++)
|
||||
{
|
||||
foreach (Vector2Int pivot in DiamondRing(center, dist))
|
||||
yield return pivot;
|
||||
int remaining = radius - Mathf.Abs(distanceX);
|
||||
for (int distanceY = -remaining; distanceY <= remaining; distanceY++)
|
||||
{
|
||||
yield return new Vector2Int(center.x + distanceX, center.y + distanceY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,6 +306,14 @@ namespace Services {
|
||||
Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x,y));
|
||||
if (currentGem == null) {
|
||||
int gemToUse = RandomUtils.RandomGemTypeAsInt();
|
||||
|
||||
int iterations = 0;
|
||||
while (this.matchService.MatchesAt(new Vector2Int(x, y), (GemType)gemToUse) && iterations < 100)
|
||||
{
|
||||
gemToUse = RandomUtils.RandomGemTypeAsInt();
|
||||
iterations++;
|
||||
}
|
||||
|
||||
SpawnGem(new Vector2Int(x, y), (GemType)gemToUse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,18 +35,29 @@ namespace Services {
|
||||
public bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck) {
|
||||
Gem[,] gems = this.gameBoard.GemsGrid;
|
||||
|
||||
// We don't prevent spawning bombs via this rule (and bombs shouldn't be treated as a normal color here).
|
||||
if (gemTypeToCheck == GemType.Bomb)
|
||||
return false;
|
||||
|
||||
if (positionToCheck.x > 1)
|
||||
{
|
||||
if (gems[positionToCheck.x - 1, positionToCheck.y].Type == gemTypeToCheck && gems[positionToCheck.x - 2, positionToCheck.y].Type == gemTypeToCheck)
|
||||
// Check horizontal: would placing gemTypeToCheck at positionToCheck create XXX with 2-left?
|
||||
if (positionToCheck.x > 1) {
|
||||
Gem left1 = gems[positionToCheck.x - 1, positionToCheck.y];
|
||||
Gem left2 = gems[positionToCheck.x - 2, positionToCheck.y];
|
||||
|
||||
if (left1 != null && left2 != null &&
|
||||
left1.MatchColor == gemTypeToCheck &&
|
||||
left2.MatchColor == gemTypeToCheck)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (positionToCheck.y > 1)
|
||||
{
|
||||
if (gems[positionToCheck.x, positionToCheck.y - 1].Type == gemTypeToCheck && gems[positionToCheck.x, positionToCheck.y - 2].Type == gemTypeToCheck)
|
||||
// Check vertical: would placing gemTypeToCheck at positionToCheck create XXX with 2-down?
|
||||
if (positionToCheck.y > 1) {
|
||||
Gem down1 = gems[positionToCheck.x, positionToCheck.y - 1];
|
||||
Gem down2 = gems[positionToCheck.x, positionToCheck.y - 2];
|
||||
|
||||
if (down1 != null && down2 != null &&
|
||||
down1.MatchColor == gemTypeToCheck &&
|
||||
down2.MatchColor == gemTypeToCheck)
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -88,80 +99,12 @@ namespace Services {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentMatches.Count > 0)
|
||||
this.currentMatches = this.currentMatches.Distinct().ToList();
|
||||
|
||||
ExpandMatchesForLTShapes();
|
||||
|
||||
if (this.currentMatches.Count > 0)
|
||||
this.currentMatches = this.currentMatches.Distinct().ToList();
|
||||
|
||||
DetectBombSpawnFromLastSwap();
|
||||
}
|
||||
|
||||
private void ExpandMatchesForLTShapes() {
|
||||
if (this.currentMatches.Count == 0)
|
||||
return;
|
||||
|
||||
List<Gem> seeds = this.currentMatches.ToList();
|
||||
|
||||
foreach (Gem pivotGem in seeds) {
|
||||
if (pivotGem == null)
|
||||
continue;
|
||||
|
||||
Vector2Int pivot = pivotGem.Position;
|
||||
|
||||
int left = CountLine(pivot, Vector2Int.left);
|
||||
int right = CountLine(pivot, Vector2Int.right);
|
||||
int up = CountLine(pivot, Vector2Int.up);
|
||||
int down = CountLine(pivot, Vector2Int.down);
|
||||
|
||||
int horizontalLen = left + 1 + right;
|
||||
int verticalLen = up + 1 + down;
|
||||
|
||||
bool hasHorizontalMatch = horizontalLen >= 3;
|
||||
bool hasVerticalMatch = verticalLen >= 3;
|
||||
|
||||
// one more in the perpendicular direction = length = 2
|
||||
bool hasHorizontalArm = horizontalLen >= 2;
|
||||
bool hasVerticalArm = verticalLen >= 2;
|
||||
|
||||
// Main horizontal + vertical arm = include vertical run
|
||||
if (hasHorizontalMatch && hasVerticalArm) {
|
||||
AddRun(pivot, Vector2Int.up);
|
||||
AddRun(pivot, Vector2Int.down);
|
||||
this.currentMatches.Add(pivotGem);
|
||||
}
|
||||
|
||||
// Main vertical + horizontal arm = include horizontal run
|
||||
if (hasVerticalMatch && hasHorizontalArm) {
|
||||
AddRun(pivot, Vector2Int.left);
|
||||
AddRun(pivot, Vector2Int.right);
|
||||
this.currentMatches.Add(pivotGem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddRun(Vector2Int start, Vector2Int direction) {
|
||||
Gem startGem = this.gameBoard.GetGemAt(start);
|
||||
if (startGem == null)
|
||||
return;
|
||||
|
||||
GemType color = startGem.MatchColor;
|
||||
|
||||
Vector2Int position = start + direction;
|
||||
while (position.x >= 0 && position.x < this.gameBoard.Width &&
|
||||
position.y >= 0 && position.y < this.gameBoard.Height) {
|
||||
|
||||
Gem g = this.gameBoard.GetGemAt(position);
|
||||
if (g == null || g.MatchColor != color)
|
||||
break;
|
||||
|
||||
this.currentMatches.Add(g);
|
||||
position += direction;
|
||||
}
|
||||
}
|
||||
|
||||
private void DetectBombSpawnFromLastSwap() {
|
||||
Vector2Int from = this.lastSwapFrom;
|
||||
Vector2Int to = this.lastSwapTo;
|
||||
@@ -182,9 +125,8 @@ namespace Services {
|
||||
if (this.currentMatches.All(g => g.Position != pivot))
|
||||
return;
|
||||
|
||||
// NEW RULE:
|
||||
// If the match group that includes this pivot (including L/T expansions) has 4+ gems, spawn a bomb.
|
||||
int groupSize = GetMatchedGroupSizeIncludingLT(pivot);
|
||||
// If the matched group that includes this pivot has 4+ connected gems, spawn a bomb.
|
||||
int groupSize = GetMatchedGroupSize(pivot);
|
||||
if (groupSize < 4)
|
||||
return;
|
||||
|
||||
@@ -195,14 +137,13 @@ namespace Services {
|
||||
this.pendingBombSpawns.Add(new BombSpawnRequest(pivot, pivotGem.MatchColor));
|
||||
}
|
||||
|
||||
private int GetMatchedGroupSizeIncludingLT(Vector2Int pivot) {
|
||||
private int GetMatchedGroupSize(Vector2Int pivot) {
|
||||
Gem pivotGem = this.gameBoard.GetGemAt(pivot);
|
||||
if (pivotGem == null)
|
||||
return 0;
|
||||
|
||||
GemType color = pivotGem.MatchColor;
|
||||
|
||||
// Only count gems that are actually in currentMatches (which already includes L/T expansions).
|
||||
HashSet<Vector2Int> matchedPositions = new HashSet<Vector2Int>(
|
||||
this.currentMatches
|
||||
.Where(g => g != null && g.MatchColor == color)
|
||||
@@ -243,90 +184,5 @@ namespace Services {
|
||||
|
||||
return visited.Count;
|
||||
}
|
||||
|
||||
private int CountLine(Vector2Int start, Vector2Int direction) {
|
||||
Gem startGem = this.gameBoard.GetGemAt(start);
|
||||
if (startGem == null)
|
||||
return 0;
|
||||
|
||||
GemType color = startGem.MatchColor;
|
||||
|
||||
int count = 0;
|
||||
Vector2Int position = start + direction;
|
||||
|
||||
while (position.x >= 0 && position.x < this.gameBoard.Width && position.y >= 0 && position.y < this.gameBoard.Height) {
|
||||
Gem g = this.gameBoard.GetGemAt(position);
|
||||
if (g == null || g.MatchColor != color)
|
||||
break;
|
||||
|
||||
count++;
|
||||
position += direction;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private void CheckForBombs() {
|
||||
Gem[,] gems = this.gameBoard.GemsGrid;
|
||||
int width = this.gameBoard.Width;
|
||||
int height = this.gameBoard.Height;
|
||||
|
||||
for (int i = 0; i < this.currentMatches.Count; i++)
|
||||
{
|
||||
Gem gem = this.currentMatches[i];
|
||||
int x = gem.Position.x;
|
||||
int y = gem.Position.y;
|
||||
|
||||
Vector2Int[] directions =
|
||||
{
|
||||
Vector2Int.left,
|
||||
Vector2Int.right,
|
||||
Vector2Int.down,
|
||||
Vector2Int.up
|
||||
};
|
||||
|
||||
foreach (Vector2Int direction in directions)
|
||||
{
|
||||
int newX = x + direction.x;
|
||||
int newY = y + direction.y;
|
||||
|
||||
if (newX < 0 || newX >= width || newY < 0 || newY >= height)
|
||||
continue;
|
||||
|
||||
Gem neighbor = gems[newX, newY];
|
||||
if (neighbor?.Type == GemType.Bomb)
|
||||
MarkBombCross(new Vector2Int(newX, newY), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MarkBombCross(Vector2Int bombPosition, int radius) {
|
||||
Gem[,] gems = this.gameBoard.GemsGrid;
|
||||
int width = this.gameBoard.Width;
|
||||
int height = this.gameBoard.Height;
|
||||
|
||||
void Mark(Vector2Int p) {
|
||||
if (p.x < 0 || p.x >= width || p.y < 0 || p.y >= height)
|
||||
return;
|
||||
Gem g = gems[p.x, p.y];
|
||||
if (g == null)
|
||||
return;
|
||||
|
||||
this.currentMatches.Add(g);
|
||||
}
|
||||
|
||||
Mark(bombPosition);
|
||||
|
||||
for (int i = 1; i <= radius; i++) {
|
||||
Mark(bombPosition + Vector2Int.left * i);
|
||||
Mark(bombPosition + Vector2Int.right * i);
|
||||
Mark(bombPosition + Vector2Int.up * i);
|
||||
Mark(bombPosition + Vector2Int.down * i);
|
||||
}
|
||||
|
||||
this.currentMatches = this.currentMatches.Distinct().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user