Skip to content

Commit 93bdb8b

Browse files
committed
add: Chain reactions
1 parent 63001cd commit 93bdb8b

2 files changed

Lines changed: 88 additions & 62 deletions

File tree

Assets/Code/Logic/Zuma/Balls/Ball.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,9 @@ public void Activate(Vector3 position, Quaternion rotation)
5959
public void Deactivate()
6060
{
6161
_text.text = "";
62-
62+
6363
Dispose();
64+
SetInteractive(false);
6465

6566
gameObject.SetActive(false);
6667
}
Lines changed: 86 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Collections.Generic;
2+
using System.Linq;
23
using Code.Logic.Zuma.Balls;
34
using Code.Services.Levels;
45
using Code.Services.LocalProgress;
@@ -21,10 +22,10 @@ public class AttachingBallChainHandler
2122
public AttachingBallChainHandler(
2223
PathCreator pathCreator,
2324
BallChainDTO ballChainDto,
24-
ChainTracker chainTracker,
25-
WidgetBallChainProvider widgetBallChainProvider,
26-
WinBallChainHandler winBallChainHandler,
27-
ILevelService levelService,
25+
ChainTracker chainTracker,
26+
WidgetBallChainProvider widgetBallChainProvider,
27+
WinBallChainHandler winBallChainHandler,
28+
ILevelService levelService,
2829
ILevelLocalProgressService levelLocalProgressService)
2930
{
3031
_pathCreator = pathCreator;
@@ -44,10 +45,9 @@ public void TryAttachBall(Ball newBall)
4445

4546
InsertBallToChainByDistance(newBall, collision);
4647
}
47-
48+
4849
private CollisionData GetClosestCollision(Ball newBall)
4950
{
50-
var path = _pathCreator.path;
5151
float minDistance = float.MaxValue;
5252
CollisionData closest = null;
5353

@@ -58,12 +58,7 @@ private CollisionData GetClosestCollision(Ball newBall)
5858

5959
if (distance <= _ballChainDto.CollisionThreshold && distance < minDistance)
6060
{
61-
closest = new CollisionData
62-
{
63-
Ball = existingBall,
64-
Distance = distance,
65-
Index = i
66-
};
61+
closest = new CollisionData { Ball = existingBall, Distance = distance, Index = i };
6762
minDistance = distance;
6863
}
6964
}
@@ -79,106 +74,136 @@ private void InsertBallToChainByDistance(Ball newBall, CollisionData collision)
7974
float closestBallDist = path.GetClosestDistanceAlongPath(collision.Ball.transform.position);
8075

8176
if (collision.Index == _chainTracker.Balls.Count - 1 && newBallDist < closestBallDist)
82-
{
8377
AttachBallToChain(newBall, _chainTracker.Balls.Count);
84-
}
8578
else if (collision.Index == 0 && newBallDist > closestBallDist)
86-
{
8779
AttachBallToChain(newBall, 0);
88-
}
8980
else
9081
{
9182
int insertIndex = (newBallDist > closestBallDist) ? collision.Index : collision.Index + 1;
9283
AttachBallToChain(newBall, insertIndex);
9384
}
9485
}
95-
86+
9687
private void AttachBallToChain(Ball newBall, int index)
9788
{
9889
newBall.Dispose();
99-
90+
10091
_chainTracker.InsertBall(index, newBall);
10192
_chainTracker.AddDistanceTravelled(_ballChainDto.SpacingBalls);
102-
93+
10394
ReIndexBalls();
104-
95+
10596
WaitToCheckAndDestroyMatches(newBall).Forget();
10697
}
10798

10899
private async UniTask WaitToCheckAndDestroyMatches(Ball insertedBall)
109100
{
110101
await UniTask.Delay((int)(_ballChainDto.DurationMovingOffset * 1000));
111-
CheckAndDestroyMatches(insertedBall, _chainTracker.Balls);
102+
await CheckAndDestroyMatches(insertedBall, _chainTracker.Balls);
112103
}
113-
114-
private void CheckAndDestroyMatches(Ball insertedBall, List<Ball> balls)
104+
105+
private async UniTask CheckAndDestroyMatches(Ball pivotBall, List<Ball> balls)
115106
{
116-
List<Ball> matchingBalls = new List<Ball> { insertedBall };
117-
Color matchColor = insertedBall.Color;
107+
var matchingBalls = FindMatchesAroundBall(pivotBall, balls);
118108

119-
for (int i = insertedBall.Index - 1; i >= 0; i--)
109+
if (matchingBalls.Count >= _ballChainDto.MatchingCount)
120110
{
121-
if (balls[i].Color == matchColor)
122-
matchingBalls.Add(balls[i]);
123-
else
124-
break;
125-
}
111+
int score = matchingBalls.Count * _levelService.GetCurrentLevelStaticData().LevelConfig.ScoreConfig.ScorePerItem;
112+
_levelLocalProgressService.AddScore(score);
113+
_widgetBallChainProvider.SetUpWidget(matchingBalls, pivotBall, score);
114+
_winBallChainHandler.TryWin(balls.Count - matchingBalls.Count);
115+
116+
int junctionIndex = matchingBalls.Min(b => b.Index) - 1;
126117

127-
for (int i = insertedBall.Index + 1; i < balls.Count; i++)
118+
await DestroyBalls(matchingBalls, balls);
119+
await CheckChainReaction(junctionIndex, balls);
120+
}
121+
else
128122
{
129-
if (balls[i].Color == matchColor)
130-
matchingBalls.Add(balls[i]);
131-
else
132-
break;
123+
pivotBall.SetInteractive(false);
133124
}
125+
}
126+
127+
// Recursively checks for new matches after a group is destroyed.
128+
// junctionIndex points to the ball just before the destroyed group.
129+
private async UniTask CheckChainReaction(int junctionIndex, List<Ball> balls)
130+
{
131+
if (balls.Count == 0 || _winBallChainHandler.IsWin)
132+
return;
133+
134+
await UniTask.Delay((int)(_ballChainDto.DurationMovingOffset * 1000));
135+
136+
junctionIndex = Mathf.Clamp(junctionIndex, 0, balls.Count - 1);
137+
Ball anchor = balls[junctionIndex];
138+
var matchingBalls = FindMatchesAroundBall(anchor, balls);
134139

135140
if (matchingBalls.Count >= _ballChainDto.MatchingCount)
136141
{
137-
int count = matchingBalls.Count * _levelService.GetCurrentLevelStaticData().LevelConfig.ScoreConfig.ScorePerItem;
138-
_levelLocalProgressService.AddScore(count);
139-
140-
_widgetBallChainProvider.SetUpWidget(matchingBalls, insertedBall, count);
142+
int score = matchingBalls.Count * _levelService.GetCurrentLevelStaticData().LevelConfig.ScoreConfig.ScorePerItem;
143+
_levelLocalProgressService.AddScore(score);
144+
_widgetBallChainProvider.SetUpWidget(matchingBalls, anchor, score);
141145
_winBallChainHandler.TryWin(balls.Count - matchingBalls.Count);
142-
143-
PlayDestroyMatchingBalls(insertedBall, balls, matchingBalls);
144-
145-
ReIndexBalls();
146+
147+
int nextJunctionIndex = matchingBalls.Min(b => b.Index) - 1;
148+
149+
await DestroyBalls(matchingBalls, balls);
150+
await CheckChainReaction(nextJunctionIndex, balls);
146151
}
147-
else
152+
}
153+
154+
// Waits for ALL destroy animations to finish, then removes balls and contracts the chain at once.
155+
private async UniTask DestroyBalls(List<Ball> matchingBalls, List<Ball> allBalls)
156+
{
157+
await UniTask.WhenAll(matchingBalls.Select(WaitForDestroyAnimation));
158+
159+
foreach (var ball in matchingBalls)
148160
{
149-
insertedBall.SetInteractive(false);
161+
allBalls.Remove(ball);
162+
ball.Deactivate();
150163
}
164+
165+
_chainTracker.SubtractDistanceTravelled(matchingBalls.Count * _ballChainDto.SpacingBalls);
166+
ReIndexBalls();
151167
}
152168

153-
private void PlayDestroyMatchingBalls(Ball insertedBall, List<Ball> balls, List<Ball> matchingBalls)
169+
private UniTask WaitForDestroyAnimation(Ball ball)
154170
{
155-
foreach (var ball in matchingBalls)
171+
var tcs = new UniTaskCompletionSource();
172+
ball.PlayDestroyAnimation(() => tcs.TrySetResult());
173+
return tcs.Task;
174+
}
175+
176+
private List<Ball> FindMatchesAroundBall(Ball pivotBall, List<Ball> balls)
177+
{
178+
var matching = new List<Ball> { pivotBall };
179+
Color color = pivotBall.Color;
180+
181+
for (int i = pivotBall.Index - 1; i >= 0; i--)
156182
{
157-
ball.PlayDestroyAnimation(() =>
158-
{
159-
balls.Remove(ball);
160-
ball.Deactivate();
161-
162-
insertedBall.SetInteractive(false);
163-
164-
_chainTracker.SubtractDistanceTravelled(_ballChainDto.SpacingBalls);
165-
});
183+
if (balls[i].Color == color) matching.Add(balls[i]);
184+
else break;
166185
}
186+
187+
for (int i = pivotBall.Index + 1; i < balls.Count; i++)
188+
{
189+
if (balls[i].Color == color) matching.Add(balls[i]);
190+
else break;
191+
}
192+
193+
return matching;
167194
}
168195

169196
private void ReIndexBalls()
170197
{
171198
for (int i = 0; i < _chainTracker.Balls.Count; i++)
172-
{
173199
_chainTracker.Balls[i].SetIndex(i);
174-
}
175200
}
176-
201+
177202
private class CollisionData
178203
{
179204
public Ball Ball;
180205
public float Distance;
181206
public int Index;
182207
}
183208
}
184-
}
209+
}

0 commit comments

Comments
 (0)