diff --git a/Assets/Scripts/Services/BombService.cs b/Assets/Scripts/Services/BombService.cs index b48ff26..d3bd9df 100644 --- a/Assets/Scripts/Services/BombService.cs +++ b/Assets/Scripts/Services/BombService.cs @@ -21,10 +21,6 @@ namespace Services private BombSpawnRequest? pendingBombSpawn; public BombSpawnRequest? PendingBombSpawn => this.pendingBombSpawn; - - public void ClearPendingBombs() { - this.pendingBombSpawn = null; - } public BombService(GameVariables gameVariables, IGameBoard gameBoard) { this.gameVariables = gameVariables; @@ -34,6 +30,38 @@ namespace Services public void SetLastSwap(Vector2Int from, Vector2Int to) { this.lastSwapFrom = from; this.lastSwapTo = to; + + ClearPendingBombs(); + } + + public UniTask> GetInitialBombs(List protectedPositions, List bombCandidates) { + List initialBombs = new List(); + foreach (Vector2Int p in bombCandidates) { + if (!GemUtils.IsInBounds(p, this.gameBoard)) continue; + + if (protectedPositions != null && protectedPositions.Contains(p)) + continue; + + Gem gem = this.gameBoard.GetGemAt(p); + if (gem is { Type: GemType.Bomb }) + initialBombs.Add(p); + } + + return UniTask.FromResult(initialBombs.Distinct().ToList()); + } + + public List ApplyPendingBombSpawns(Action spawnGem) { + List positions = new List(); + BombSpawnRequest? bombSpawnRequest = PendingBombSpawn; + + if (bombSpawnRequest != null) { + BombSpawnRequest bombRequest = PendingBombSpawn.GetValueOrDefault(); + positions.Add(bombRequest.Position); + spawnGem(bombRequest.Position, bombRequest.Color, true); + } + + ClearPendingBombs(); + return positions; } public void DetectBombSpawnFromLastSwap(List currentMatches) { @@ -201,5 +229,9 @@ namespace Services return count; } + + public void ClearPendingBombs() { + this.pendingBombSpawn = null; + } } } \ No newline at end of file diff --git a/Assets/Scripts/Services/GameBoardService.cs b/Assets/Scripts/Services/GameBoardService.cs index 8ab9bfd..6f59eb8 100644 --- a/Assets/Scripts/Services/GameBoardService.cs +++ b/Assets/Scripts/Services/GameBoardService.cs @@ -100,7 +100,7 @@ namespace Services { //Uses the ObjectPool to spawn a gem at the given position private void SpawnGem(Vector2Int position, GemType gemType, bool isBomb = false) { if (isBomb) { - DestroyMatchedGems(position); + ReleaseMatchedGems(position); } GemView gemView = this.objectPool.Get(isBomb ? GemType.Bomb : gemType, position, isBomb ? 0 : this.gameVariables.dropHeight); @@ -121,8 +121,8 @@ namespace Services { if (!GemUtils.IsInBounds(from, this.gameBoard) || !GemUtils.IsInBounds(to, this.gameBoard)) return false; - - if (!AreAdjacentCardinal(from, to)) + + if (!from.IsAdjacent(to)) return false; this.currentState = GameState.Wait; @@ -131,7 +131,6 @@ namespace Services { await UniTask.Delay(600); this.bombService.SetLastSwap(from, to); - this.bombService.ClearPendingBombs(); this.matchService.FindAllMatches(); this.bombService.DetectBombSpawnFromLastSwap(this.matchService.CurrentMatches); @@ -142,7 +141,7 @@ namespace Services { return false; } - List protectedPositions = ApplyPendingBombSpawns(); + List protectedPositions = this.bombService.ApplyPendingBombSpawns(SpawnGem); await DestroyMatchesAsync(protectedPositions); this.currentState = GameState.Move; return true; @@ -160,47 +159,9 @@ namespace Services { this.gameBoard.SetGemAt(to, fromGem); } - private List ApplyPendingBombSpawns() { - List positions = new List(); - BombSpawnRequest? bombSpawnRequest = this.bombService.PendingBombSpawn; - - if (bombSpawnRequest != null) { - BombSpawnRequest bombRequest = this.bombService.PendingBombSpawn.GetValueOrDefault(); - positions.Add(bombRequest.Position); - SpawnGem(bombRequest.Position, bombRequest.Color, isBomb: true); - } - - this.bombService.ClearPendingBombs(); - return positions; - } - private async UniTask DestroyMatchesAsync(List protectedPositions) { - List matchPositions = new List(this.matchService.CurrentMatches.Count); - for (int i = 0; i < this.matchService.CurrentMatches.Count; i++) { - Gem match = this.matchService.CurrentMatches[i]; - if (match == null) continue; - - Vector2Int pos = match.Position; - if (protectedPositions != null && protectedPositions.Contains(pos)) - continue; - - matchPositions.Add(pos); - } - - IReadOnlyList bombCandidates = matchPositions.Distinct().ToList(); - - List initialBombs = new List(); - foreach (Vector2Int p in bombCandidates) { - if (!GemUtils.IsInBounds(p, this.gameBoard)) continue; - - if (protectedPositions != null && protectedPositions.Contains(p)) - continue; - - Gem gem = this.gameBoard.GetGemAt(p); - if (gem is { Type: GemType.Bomb }) - initialBombs.Add(p); - } - initialBombs = initialBombs.Distinct().ToList(); + List matchPositions = await this.matchService.GetMatchPositionsAsync(protectedPositions); + List initialBombs = await this.bombService.GetInitialBombs(protectedPositions, matchPositions.Distinct().ToList()); // If a bomb is part of the match, do NOT destroy matching pieces immediately. // Let the bomb's manhattan-distance explosion destroy them in sequence. @@ -210,29 +171,27 @@ namespace Services { DestroyAtAsync, this.gameBoard); + await UniTask.Delay(600); + await MoveGemsDown(); return; } + // For audio SFX bool willBreakAnyNonBombGem = matchPositions.Select(pos => this.gameBoard.GetGemAt(pos)).Where(gem => gem != null).Any(gem => gem.Type != GemType.Bomb); - if (willBreakAnyNonBombGem) this.audioPresenter.OnMatch(this.gameVariables.matchSfx); + // For score counting foreach (Vector2Int pos in matchPositions.Distinct().ToList()) { Gem gem = this.gameBoard.GetGemAt(pos); if (gem == null) continue; if (gem.Type == GemType.Bomb) continue; this.scoreService.ScoreCheck(gem.ScoreValue); - DestroyMatchedGems(pos); + ReleaseMatchedGems(pos); } - await this.bombService.DetonateChainAsync( - initialBombs, - DestroyAtAsync, - this.gameBoard); - await MoveGemsDown(); } @@ -245,9 +204,25 @@ namespace Services { this.audioPresenter.OnBombExplosion(this.gameVariables.bombExplodeSfx); this.scoreService.ScoreCheck(gem.ScoreValue); - DestroyMatchedGems(pos); + ReleaseMatchedGems(pos); return UniTask.CompletedTask; } + + private void ReleaseMatchedGems(Vector2Int position) { + List gemsViews = this.gemsHolder.GetComponentsInChildren().ToList(); + Gem currentGem = this.gameBoard.GetGemAt(position); + if (currentGem != null) + { + GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem); + if (gemView is null) { + return; + } + + this.objectPool.Release(gemView); + RemovePresenterFor(gemView); + this.gameBoard.SetGemAt(position, null); + } + } private async UniTask MoveGemsDown() { await UniTask.Delay(50); @@ -313,22 +288,6 @@ namespace Services { } } } - - private void DestroyMatchedGems(Vector2Int position) { - List gemsViews = this.gemsHolder.GetComponentsInChildren().ToList(); - Gem currentGem = this.gameBoard.GetGemAt(position); - if (currentGem != null) - { - GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem); - if (gemView is null) { - return; - } - - this.objectPool.Release(gemView); - RemovePresenterFor(gemView); - this.gameBoard.SetGemAt(position, null); - } - } #region Utils private void RemovePresenterFor(GemView gemView) { @@ -339,11 +298,6 @@ namespace Services { GemPresenter presenter = this.gemPresenters.FirstOrDefault(p => p.GemView == gemView); this.gemPresenters.Remove(presenter); } - - private static bool AreAdjacentCardinal(Vector2Int a, Vector2Int b) { - Vector2Int d = b - a; - return (Mathf.Abs(d.x) == 1 && d.y == 0) || (Mathf.Abs(d.y) == 1 && d.x == 0); - } #endregion public void Dispose() { diff --git a/Assets/Scripts/Services/Interfaces/IBombService.cs b/Assets/Scripts/Services/Interfaces/IBombService.cs index ec520a9..65068f9 100644 --- a/Assets/Scripts/Services/Interfaces/IBombService.cs +++ b/Assets/Scripts/Services/Interfaces/IBombService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; +using Enums; using Models.Interfaces; using Structs; using UnityEngine; @@ -12,9 +13,10 @@ namespace Services.Interfaces public BombSpawnRequest? PendingBombSpawn { get; } void SetLastSwap(Vector2Int from, Vector2Int to); - void ClearPendingBombs(); void DetectBombSpawnFromLastSwap(List currentMatches); + List ApplyPendingBombSpawns(Action spawnGem); + UniTask> GetInitialBombs(List protectedPositions, List bombCandidates); UniTask DetonateChainAsync( IReadOnlyList initialBombs, diff --git a/Assets/Scripts/Services/Interfaces/IMatchService.cs b/Assets/Scripts/Services/Interfaces/IMatchService.cs index d6841d4..b2350b6 100644 --- a/Assets/Scripts/Services/Interfaces/IMatchService.cs +++ b/Assets/Scripts/Services/Interfaces/IMatchService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Cysharp.Threading.Tasks; using UnityEngine; using Enums; using Structs; @@ -6,6 +7,7 @@ using Structs; namespace Services.Interfaces { public interface IMatchService { List CurrentMatches { get; } + UniTask> GetMatchPositionsAsync(List protectedPositions); bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck); void FindAllMatches(); } diff --git a/Assets/Scripts/Services/MatchService.cs b/Assets/Scripts/Services/MatchService.cs index 8cd14c3..66240de 100644 --- a/Assets/Scripts/Services/MatchService.cs +++ b/Assets/Scripts/Services/MatchService.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Cysharp.Threading.Tasks; using Enums; using Models.Interfaces; using Services.Interfaces; @@ -50,6 +51,22 @@ namespace Services { return false; } + public UniTask> GetMatchPositionsAsync(List protectedPositions) { + List matchPositions = new List(CurrentMatches.Count); + for (int i = 0; i < CurrentMatches.Count; i++) { + Gem match = CurrentMatches[i]; + if (match == null) continue; + + Vector2Int pos = match.Position; + if (protectedPositions != null && protectedPositions.Contains(pos)) + continue; + + matchPositions.Add(pos); + } + + return UniTask.FromResult(matchPositions); + } + public void FindAllMatches() { this.currentMatches.Clear(); diff --git a/Assets/Scripts/Utils/Vector2IntUtils.cs b/Assets/Scripts/Utils/Vector2IntUtils.cs index d843470..698d9a6 100644 --- a/Assets/Scripts/Utils/Vector2IntUtils.cs +++ b/Assets/Scripts/Utils/Vector2IntUtils.cs @@ -14,5 +14,10 @@ namespace Utils { public static Vector2Int ToVector2Int(this Vector2 v) { return new Vector2Int((int)v.x, (int)v.y); } + + public static bool IsAdjacent(this Vector2Int a, Vector2Int b) { + Vector2Int d = b - a; + return (Mathf.Abs(d.x) == 1 && d.y == 0) || (Mathf.Abs(d.y) == 1 && d.x == 0); + } } } \ No newline at end of file