react-native-games 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +58 -7
- package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -167
- package/lib/module/games/balloon-blaster/BalloonBlaster.js.map +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js +1 -182
- package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js.map +1 -1
- package/lib/module/games/balloon-blaster/BalloonBlasterService.js +1 -318
- package/lib/module/games/balloon-blaster/BalloonBlasterStore.js +1 -183
- package/lib/module/games/balloon-blaster/components/BalloonComponent.js +1 -237
- package/lib/module/games/balloon-blaster/components/GameArea.js +1 -156
- package/lib/module/games/balloon-blaster/components/GameBackground.js +1 -476
- package/lib/module/games/balloon-blaster/components/ScoreBoard.js +1 -112
- package/lib/module/games/balloon-blaster/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/balloon-blaster/components/index.js +1 -7
- package/lib/module/games/candy-crush/CandyCrush.js +1 -131
- package/lib/module/games/candy-crush/CandyCrush.js.map +1 -1
- package/lib/module/games/candy-crush/CandyCrushConstants.js +1 -125
- package/lib/module/games/candy-crush/CandyCrushConstants.js.map +1 -1
- package/lib/module/games/candy-crush/CandyCrushService.js +1 -370
- package/lib/module/games/candy-crush/CandyCrushStore.js +1 -303
- package/lib/module/games/candy-crush/components/CandyItem.js +1 -191
- package/lib/module/games/candy-crush/components/GameBackground.js +1 -85
- package/lib/module/games/candy-crush/components/GameGrid.js +1 -314
- package/lib/module/games/candy-crush/components/ScoreBoard.js +1 -79
- package/lib/module/games/candy-crush/components/index.js +1 -7
- package/lib/module/games/candy-crush/index.js +1 -6
- package/lib/module/games/colors-sort/ColorsSort.js +1 -143
- package/lib/module/games/colors-sort/ColorsSort.js.map +1 -1
- package/lib/module/games/colors-sort/ColorsSortConstants.js +1 -72
- package/lib/module/games/colors-sort/ColorsSortConstants.js.map +1 -1
- package/lib/module/games/colors-sort/ColorsSortService.js +1 -255
- package/lib/module/games/colors-sort/ColorsSortStore.js +1 -257
- package/lib/module/games/colors-sort/ColorsSortStore.js.map +1 -1
- package/lib/module/games/colors-sort/components/ColorContainer.js +1 -140
- package/lib/module/games/colors-sort/components/GameBackground.js +1 -135
- package/lib/module/games/colors-sort/components/ScoreBoard.js +1 -70
- package/lib/module/games/colors-sort/components/index.js +1 -6
- package/lib/module/games/dino-jump/DinoJump.js +1 -209
- package/lib/module/games/dino-jump/DinoJump.js.map +1 -1
- package/lib/module/games/dino-jump/DinoJumpConstants.js +1 -189
- package/lib/module/games/dino-jump/DinoJumpConstants.js.map +1 -1
- package/lib/module/games/dino-jump/DinoJumpService.js +1 -270
- package/lib/module/games/dino-jump/DinoJumpStore.js +1 -381
- package/lib/module/games/dino-jump/components/DinoSprite.js +1 -418
- package/lib/module/games/dino-jump/components/DinoSprite.js.map +1 -1
- package/lib/module/games/dino-jump/components/GameArea.js +1 -68
- package/lib/module/games/dino-jump/components/GameBackground.js +1 -444
- package/lib/module/games/dino-jump/components/ObstacleSprite.js +1 -306
- package/lib/module/games/dino-jump/components/ScoreBoard.js +1 -105
- package/lib/module/games/dino-jump/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/dino-jump/components/StarSprite.js +1 -45
- package/lib/module/games/dino-jump/components/index.js +1 -9
- package/lib/module/games/flappy-bird/FlappyBird.js +1 -126
- package/lib/module/games/flappy-bird/FlappyBird.js.map +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdConstants.js +1 -90
- package/lib/module/games/flappy-bird/FlappyBirdConstants.js.map +1 -1
- package/lib/module/games/flappy-bird/FlappyBirdStore.js +1 -300
- package/lib/module/games/flappy-bird/components/Bird.js +1 -87
- package/lib/module/games/flappy-bird/components/GameArea.js +1 -87
- package/lib/module/games/flappy-bird/components/GameBackground.js +1 -79
- package/lib/module/games/flappy-bird/components/Pipes.js +1 -172
- package/lib/module/games/flappy-bird/components/ScoreBoard.js +1 -73
- package/lib/module/games/flappy-bird/components/index.js +1 -8
- package/lib/module/games/fruit-merger/FruitMerger.js +1 -120
- package/lib/module/games/fruit-merger/FruitMerger.js.map +1 -1
- package/lib/module/games/fruit-merger/FruitMergerConstants.js +1 -119
- package/lib/module/games/fruit-merger/FruitMergerConstants.js.map +1 -1
- package/lib/module/games/fruit-merger/FruitMergerService.js +1 -13
- package/lib/module/games/fruit-merger/FruitMergerStore.js +1 -315
- package/lib/module/games/fruit-merger/components/FruitItem.js +1 -102
- package/lib/module/games/fruit-merger/components/GameArea.js +1 -103
- package/lib/module/games/fruit-merger/components/GameBackground.js +1 -498
- package/lib/module/games/fruit-merger/components/ScoreBoard.js +1 -58
- package/lib/module/games/fruit-merger/components/index.js +1 -7
- package/lib/module/games/fruit-ninja/FruitNinja.js +1 -134
- package/lib/module/games/fruit-ninja/FruitNinja.js.map +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaConstants.js +1 -148
- package/lib/module/games/fruit-ninja/FruitNinjaConstants.js.map +1 -1
- package/lib/module/games/fruit-ninja/FruitNinjaService.js +1 -311
- package/lib/module/games/fruit-ninja/FruitNinjaStore.js +1 -191
- package/lib/module/games/fruit-ninja/FruitNinjaStore.js.map +1 -1
- package/lib/module/games/fruit-ninja/components/FruitComponent.js +1 -99
- package/lib/module/games/fruit-ninja/components/GameArea.js +1 -215
- package/lib/module/games/fruit-ninja/components/GameBackground.js +1 -1267
- package/lib/module/games/fruit-ninja/components/ScoreBoard.js +1 -92
- package/lib/module/games/fruit-ninja/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/fruit-ninja/components/index.js +1 -7
- package/lib/module/games/game-2048/Game2048.js +1 -149
- package/lib/module/games/game-2048/Game2048.js.map +1 -1
- package/lib/module/games/game-2048/Game2048Constants.js +1 -263
- package/lib/module/games/game-2048/Game2048Constants.js.map +1 -1
- package/lib/module/games/game-2048/Game2048Service.js +1 -457
- package/lib/module/games/game-2048/Game2048Store.js +1 -236
- package/lib/module/games/game-2048/components/GameBackground.js +1 -247
- package/lib/module/games/game-2048/components/GameGrid.js +1 -139
- package/lib/module/games/game-2048/components/GameTile.js +1 -72
- package/lib/module/games/game-2048/components/ScoreBoard.js +1 -52
- package/lib/module/games/game-2048/components/index.js +1 -7
- package/lib/module/games/maze-runner/MazeRunner.js +1 -267
- package/lib/module/games/maze-runner/MazeRunner.js.map +1 -1
- package/lib/module/games/maze-runner/MazeRunnerConstants.js +1 -100
- package/lib/module/games/maze-runner/MazeRunnerConstants.js.map +1 -1
- package/lib/module/games/maze-runner/MazeRunnerService.js +1 -586
- package/lib/module/games/maze-runner/components/EnhancedBallComponent.js +1 -150
- package/lib/module/games/maze-runner/components/EnhancedGameArea.js +1 -370
- package/lib/module/games/maze-runner/components/GameBackground.js +1 -175
- package/lib/module/games/maze-runner/components/ScoreBoard.js +1 -61
- package/lib/module/games/maze-runner/components/SkiaPipeComponent.js +1 -209
- package/lib/module/games/maze-runner/components/StaticGameBackground.js +1 -169
- package/lib/module/games/maze-runner/components/WallComponent.js +1 -91
- package/lib/module/games/maze-runner/components/index.js +1 -8
- package/lib/module/games/popit-fidget/PopitFidget.js +1 -285
- package/lib/module/games/popit-fidget/PopitFidget.js.map +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetConstants.js +1 -113
- package/lib/module/games/popit-fidget/PopitFidgetConstants.js.map +1 -1
- package/lib/module/games/popit-fidget/PopitFidgetService.js +1 -132
- package/lib/module/games/popit-fidget/PopitFidgetStore.js +1 -125
- package/lib/module/games/popit-fidget/components/BubbleComponent.js +1 -198
- package/lib/module/games/popit-fidget/components/FidgetGrid.js +1 -165
- package/lib/module/games/popit-fidget/components/GameBackground.js +1 -177
- package/lib/module/games/popit-fidget/components/ScoreBoard.js +1 -61
- package/lib/module/games/popit-fidget/components/index.js +1 -7
- package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -159
- package/lib/module/games/sliding-numbers/SlidingNumbers.js.map +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js +1 -207
- package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js.map +1 -1
- package/lib/module/games/sliding-numbers/SlidingNumbersService.js +1 -248
- package/lib/module/games/sliding-numbers/SlidingNumbersStore.js +1 -274
- package/lib/module/games/sliding-numbers/components/GameBackground.js +1 -259
- package/lib/module/games/sliding-numbers/components/NumbersGrid.js +1 -174
- package/lib/module/games/sliding-numbers/components/NumbersTile.js +1 -116
- package/lib/module/games/sliding-numbers/components/ScoreBoard.js +1 -64
- package/lib/module/games/sliding-numbers/components/index.js +1 -7
- package/lib/module/games/snake/Snake.js +1 -189
- package/lib/module/games/snake/Snake.js.map +1 -1
- package/lib/module/games/snake/SnakeConstants.js +1 -138
- package/lib/module/games/snake/SnakeConstants.js.map +1 -1
- package/lib/module/games/snake/SnakeService.js +1 -148
- package/lib/module/games/snake/SnakeStore.js +1 -182
- package/lib/module/games/snake/components/GameBackground.js +1 -221
- package/lib/module/games/snake/components/GameGrid.js +1 -153
- package/lib/module/games/snake/components/ScoreBoard.js +1 -51
- package/lib/module/games/snake/components/index.js +1 -6
- package/lib/module/games/snake/index.js +1 -6
- package/lib/module/games/space-fighter/SpaceFighter.js +1 -165
- package/lib/module/games/space-fighter/SpaceFighter.js.map +1 -1
- package/lib/module/games/space-fighter/SpaceFighterConstants.js +1 -108
- package/lib/module/games/space-fighter/SpaceFighterConstants.js.map +1 -1
- package/lib/module/games/space-fighter/SpaceFighterService.js +1 -326
- package/lib/module/games/space-fighter/SpaceFighterStore.js +1 -209
- package/lib/module/games/space-fighter/components/AsteroidComponent.js +1 -113
- package/lib/module/games/space-fighter/components/GameArea.js +1 -289
- package/lib/module/games/space-fighter/components/GameBackground.js +1 -239
- package/lib/module/games/space-fighter/components/ScoreBoard.js +1 -136
- package/lib/module/games/space-fighter/components/Spacecraft3D.js +1 -202
- package/lib/module/games/space-fighter/components/SpacecraftPath.js +1 -52
- package/lib/module/games/space-fighter/components/index.js +1 -9
- package/lib/module/games/whack-a-mole/WhackAMole.js +1 -270
- package/lib/module/games/whack-a-mole/WhackAMole.js.map +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleConstants.js +1 -115
- package/lib/module/games/whack-a-mole/WhackAMoleConstants.js.map +1 -1
- package/lib/module/games/whack-a-mole/WhackAMoleService.js +1 -120
- package/lib/module/games/whack-a-mole/WhackAMoleStore.js +1 -172
- package/lib/module/games/whack-a-mole/components/GameBackground.js +1 -477
- package/lib/module/games/whack-a-mole/components/GameGrid.js +1 -97
- package/lib/module/games/whack-a-mole/components/GameHole.js +1 -196
- package/lib/module/games/whack-a-mole/components/MoleCharacter.js +1 -241
- package/lib/module/games/whack-a-mole/components/ScoreBoard.js +1 -67
- package/lib/module/games/whack-a-mole/components/ScoreBoard.js.map +1 -1
- package/lib/module/games/whack-a-mole/components/index.js +1 -8
- package/lib/module/helpers/AnimationFrame.js +1 -120
- package/lib/module/helpers/AnimationTracker.js +1 -89
- package/lib/module/helpers/ErrorHandler.js +1 -269
- package/lib/module/helpers/GameControlButton.js +1 -219
- package/lib/module/helpers/GameOverModal.js +1 -144
- package/lib/module/helpers/GameOverModal.js.map +1 -1
- package/lib/module/helpers/GameSettingsModal.js +1 -287
- package/lib/module/helpers/ParticleBlast.js +1 -134
- package/lib/module/helpers/ScoreBoardContainer.js +1 -34
- package/lib/module/helpers/index.js +1 -12
- package/lib/module/index.js +1 -22
- package/lib/module/services/GamesConstants.js +1 -178
- package/lib/module/services/GamesService.js +1 -112
- package/lib/module/services/GamesService.js.map +1 -1
- package/lib/module/services/HapticsService.js +1 -77
- package/lib/module/services/SoundsService.js +1 -302
- package/lib/module/services/UtilsService.js +1 -32
- package/lib/typescript/src/games/balloon-blaster/BalloonBlaster.d.ts.map +1 -1
- package/lib/typescript/src/games/balloon-blaster/BalloonBlasterConstants.d.ts +1 -1
- package/lib/typescript/src/games/balloon-blaster/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/games/candy-crush/CandyCrushConstants.d.ts +7 -7
- package/lib/typescript/src/games/colors-sort/ColorsSort.d.ts.map +1 -1
- package/lib/typescript/src/games/colors-sort/ColorsSortStore.d.ts.map +1 -1
- package/lib/typescript/src/games/dino-jump/DinoJump.d.ts.map +1 -1
- package/lib/typescript/src/games/dino-jump/components/DinoSprite.d.ts.map +1 -1
- package/lib/typescript/src/games/flappy-bird/FlappyBird.d.ts.map +1 -1
- package/lib/typescript/src/games/flappy-bird/FlappyBirdConstants.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-merger/FruitMerger.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-merger/FruitMergerConstants.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-ninja/FruitNinja.d.ts.map +1 -1
- package/lib/typescript/src/games/fruit-ninja/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/games/game-2048/Game2048.d.ts.map +1 -1
- package/lib/typescript/src/games/maze-runner/MazeRunner.d.ts.map +1 -1
- package/lib/typescript/src/games/popit-fidget/PopitFidget.d.ts.map +1 -1
- package/lib/typescript/src/games/sliding-numbers/SlidingNumbers.d.ts.map +1 -1
- package/lib/typescript/src/games/space-fighter/SpaceFighter.d.ts.map +1 -1
- package/lib/typescript/src/games/whack-a-mole/WhackAMole.d.ts.map +1 -1
- package/lib/typescript/src/games/whack-a-mole/WhackAMoleConstants.d.ts +1 -1
- package/lib/typescript/src/games/whack-a-mole/components/ScoreBoard.d.ts.map +1 -1
- package/lib/typescript/src/helpers/GameOverModal.d.ts +3 -0
- package/lib/typescript/src/helpers/GameOverModal.d.ts.map +1 -1
- package/lib/typescript/src/services/GamesConstants.d.ts +7 -7
- package/package.json +2 -2
|
@@ -1,370 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { Animated } from 'react-native';
|
|
4
|
-
import { CANDY_CRUSH_GAME_CONFIG, CANDY_COLORS, CANDY_TYPES } from "./CandyCrushConstants.js";
|
|
5
|
-
export class CandyCrushService {
|
|
6
|
-
nextCandyId = 1;
|
|
7
|
-
|
|
8
|
-
// Create empty grid
|
|
9
|
-
createGrid() {
|
|
10
|
-
return Array(CANDY_CRUSH_GAME_CONFIG.GRID_SIZE).fill(null).map(() => Array(CANDY_CRUSH_GAME_CONFIG.GRID_SIZE).fill(null));
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Initialize grid with random candies (ensuring no initial matches)
|
|
14
|
-
initializeGrid(candyTypes) {
|
|
15
|
-
const grid = this.createGrid();
|
|
16
|
-
const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;
|
|
17
|
-
for (let row = 0; row < size; row++) {
|
|
18
|
-
for (let col = 0; col < size; col++) {
|
|
19
|
-
let candyType;
|
|
20
|
-
let attempts = 0;
|
|
21
|
-
|
|
22
|
-
// Generate candy type that doesn't create initial matches
|
|
23
|
-
do {
|
|
24
|
-
candyType = Math.floor(Math.random() * candyTypes);
|
|
25
|
-
attempts++;
|
|
26
|
-
} while (attempts < 10 && this.wouldCreateMatch(grid, row, col, candyType));
|
|
27
|
-
const candy = {
|
|
28
|
-
id: this.nextCandyId++,
|
|
29
|
-
row,
|
|
30
|
-
col,
|
|
31
|
-
type: candyType,
|
|
32
|
-
specialType: CANDY_TYPES.NORMAL,
|
|
33
|
-
animatedValue: new Animated.Value(1),
|
|
34
|
-
scaleValue: new Animated.Value(1),
|
|
35
|
-
isMatched: false,
|
|
36
|
-
isFalling: false
|
|
37
|
-
};
|
|
38
|
-
const gridRow = grid[row];
|
|
39
|
-
if (gridRow) {
|
|
40
|
-
gridRow[col] = candy;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return grid;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Check if placing candy would create a match
|
|
48
|
-
wouldCreateMatch(grid, row, col, type) {
|
|
49
|
-
// Check horizontal
|
|
50
|
-
let horizontalCount = 1;
|
|
51
|
-
|
|
52
|
-
// Check left
|
|
53
|
-
for (let c = col - 1; c >= 0; c--) {
|
|
54
|
-
const gridRow = grid[row];
|
|
55
|
-
const candy = gridRow ? gridRow[c] : null;
|
|
56
|
-
if (candy && candy.type === type) {
|
|
57
|
-
horizontalCount++;
|
|
58
|
-
} else {
|
|
59
|
-
break;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Check right
|
|
64
|
-
for (let c = col + 1; c < CANDY_CRUSH_GAME_CONFIG.GRID_SIZE; c++) {
|
|
65
|
-
const gridRow = grid[row];
|
|
66
|
-
const candy = gridRow ? gridRow[c] : null;
|
|
67
|
-
if (candy && candy.type === type) {
|
|
68
|
-
horizontalCount++;
|
|
69
|
-
} else {
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (horizontalCount >= CANDY_CRUSH_GAME_CONFIG.MIN_MATCH) {
|
|
74
|
-
return true;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Check vertical
|
|
78
|
-
let verticalCount = 1;
|
|
79
|
-
|
|
80
|
-
// Check up
|
|
81
|
-
for (let r = row - 1; r >= 0; r--) {
|
|
82
|
-
const gridRow = grid[r];
|
|
83
|
-
const candy = gridRow ? gridRow[col] : null;
|
|
84
|
-
if (candy && candy.type === type) {
|
|
85
|
-
verticalCount++;
|
|
86
|
-
} else {
|
|
87
|
-
break;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Check down
|
|
92
|
-
for (let r = row + 1; r < CANDY_CRUSH_GAME_CONFIG.GRID_SIZE; r++) {
|
|
93
|
-
const gridRow = grid[r];
|
|
94
|
-
const candy = gridRow ? gridRow[col] : null;
|
|
95
|
-
if (candy && candy.type === type) {
|
|
96
|
-
verticalCount++;
|
|
97
|
-
} else {
|
|
98
|
-
break;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return verticalCount >= CANDY_CRUSH_GAME_CONFIG.MIN_MATCH;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// Check if two candies can be swapped
|
|
105
|
-
canSwap(candy1, candy2) {
|
|
106
|
-
const rowDiff = Math.abs(candy1.row - candy2.row);
|
|
107
|
-
const colDiff = Math.abs(candy1.col - candy2.col);
|
|
108
|
-
|
|
109
|
-
// Must be adjacent (horizontally or vertically)
|
|
110
|
-
return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Swap two candies
|
|
114
|
-
swapCandies(grid, candy1, candy2) {
|
|
115
|
-
const row1 = candy1.row;
|
|
116
|
-
const col1 = candy1.col;
|
|
117
|
-
const row2 = candy2.row;
|
|
118
|
-
const col2 = candy2.col;
|
|
119
|
-
|
|
120
|
-
// Get references from grid
|
|
121
|
-
const gridRow1 = grid[row1];
|
|
122
|
-
const gridRow2 = grid[row2];
|
|
123
|
-
if (gridRow1 && gridRow2) {
|
|
124
|
-
const temp1 = gridRow1[col1];
|
|
125
|
-
const temp2 = gridRow2[col2];
|
|
126
|
-
|
|
127
|
-
// Swap in grid
|
|
128
|
-
gridRow1[col1] = temp2 || null;
|
|
129
|
-
gridRow2[col2] = temp1 || null;
|
|
130
|
-
|
|
131
|
-
// Update positions (these are mutable in Immer draft)
|
|
132
|
-
if (temp1) {
|
|
133
|
-
temp1.row = row2;
|
|
134
|
-
temp1.col = col2;
|
|
135
|
-
}
|
|
136
|
-
if (temp2) {
|
|
137
|
-
temp2.row = row1;
|
|
138
|
-
temp2.col = col1;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Find all matches in the grid
|
|
144
|
-
findMatches(grid) {
|
|
145
|
-
const matches = [];
|
|
146
|
-
const matchedSet = new Set();
|
|
147
|
-
const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;
|
|
148
|
-
|
|
149
|
-
// Check horizontal matches
|
|
150
|
-
for (let row = 0; row < size; row++) {
|
|
151
|
-
for (let col = 0; col < size - 2; col++) {
|
|
152
|
-
const gridRow = grid[row];
|
|
153
|
-
if (!gridRow) continue;
|
|
154
|
-
const candy1 = gridRow[col];
|
|
155
|
-
const candy2 = gridRow[col + 1];
|
|
156
|
-
const candy3 = gridRow[col + 2];
|
|
157
|
-
if (candy1 && candy2 && candy3 && candy1.type === candy2.type && candy2.type === candy3.type) {
|
|
158
|
-
let matchLength = 3;
|
|
159
|
-
const matchCandies = [candy1, candy2, candy3];
|
|
160
|
-
|
|
161
|
-
// Check for longer matches
|
|
162
|
-
for (let c = col + 3; c < size; c++) {
|
|
163
|
-
const nextCandy = gridRow[c];
|
|
164
|
-
if (nextCandy && nextCandy.type === candy1.type) {
|
|
165
|
-
matchLength++;
|
|
166
|
-
matchCandies.push(nextCandy);
|
|
167
|
-
} else {
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Add to matches
|
|
173
|
-
matchCandies.forEach(candy => {
|
|
174
|
-
if (!matchedSet.has(candy.id)) {
|
|
175
|
-
matchedSet.add(candy.id);
|
|
176
|
-
matches.push(candy);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
col += matchLength - 1;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Check vertical matches
|
|
185
|
-
for (let col = 0; col < size; col++) {
|
|
186
|
-
for (let row = 0; row < size - 2; row++) {
|
|
187
|
-
const gridRow1 = grid[row];
|
|
188
|
-
const gridRow2 = grid[row + 1];
|
|
189
|
-
const gridRow3 = grid[row + 2];
|
|
190
|
-
if (!gridRow1 || !gridRow2 || !gridRow3) continue;
|
|
191
|
-
const candy1 = gridRow1[col];
|
|
192
|
-
const candy2 = gridRow2[col];
|
|
193
|
-
const candy3 = gridRow3[col];
|
|
194
|
-
if (candy1 && candy2 && candy3 && candy1.type === candy2.type && candy2.type === candy3.type) {
|
|
195
|
-
let matchLength = 3;
|
|
196
|
-
const matchCandies = [candy1, candy2, candy3];
|
|
197
|
-
|
|
198
|
-
// Check for longer matches
|
|
199
|
-
for (let r = row + 3; r < size; r++) {
|
|
200
|
-
const nextRow = grid[r];
|
|
201
|
-
const nextCandy = nextRow ? nextRow[col] : null;
|
|
202
|
-
if (nextCandy && nextCandy.type === candy1.type) {
|
|
203
|
-
matchLength++;
|
|
204
|
-
matchCandies.push(nextCandy);
|
|
205
|
-
} else {
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Add to matches
|
|
211
|
-
matchCandies.forEach(candy => {
|
|
212
|
-
if (!matchedSet.has(candy.id)) {
|
|
213
|
-
matchedSet.add(candy.id);
|
|
214
|
-
matches.push(candy);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
row += matchLength - 1;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
const score = matches.length * CANDY_CRUSH_GAME_CONFIG.POINTS_PER_CANDY;
|
|
222
|
-
return {
|
|
223
|
-
matches,
|
|
224
|
-
score,
|
|
225
|
-
hasMatches: matches.length > 0
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Remove matched candies from grid
|
|
230
|
-
removeMatches(grid, matches) {
|
|
231
|
-
matches.forEach(candy => {
|
|
232
|
-
const gridRow = grid[candy.row];
|
|
233
|
-
if (gridRow) {
|
|
234
|
-
gridRow[candy.col] = null;
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Apply gravity - make candies fall down
|
|
240
|
-
applyGravity(grid) {
|
|
241
|
-
let hasMoved = false;
|
|
242
|
-
const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;
|
|
243
|
-
|
|
244
|
-
// Process from bottom to top
|
|
245
|
-
for (let col = 0; col < size; col++) {
|
|
246
|
-
for (let row = size - 1; row >= 0; row--) {
|
|
247
|
-
const gridRow = grid[row];
|
|
248
|
-
if (!gridRow) continue;
|
|
249
|
-
const candy = gridRow[col];
|
|
250
|
-
if (candy) {
|
|
251
|
-
// Find the lowest empty position below this candy
|
|
252
|
-
let newRow = row;
|
|
253
|
-
for (let r = row + 1; r < size; r++) {
|
|
254
|
-
const checkRow = grid[r];
|
|
255
|
-
if (checkRow && !checkRow[col]) {
|
|
256
|
-
newRow = r;
|
|
257
|
-
} else {
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// Move candy down if there's an empty space
|
|
263
|
-
if (newRow !== row) {
|
|
264
|
-
const targetRow = grid[newRow];
|
|
265
|
-
if (targetRow) {
|
|
266
|
-
targetRow[col] = candy;
|
|
267
|
-
gridRow[col] = null;
|
|
268
|
-
candy.row = newRow;
|
|
269
|
-
candy.isFalling = true;
|
|
270
|
-
hasMoved = true;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
return hasMoved;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Fill empty spaces with new candies
|
|
280
|
-
fillEmptySpaces(grid, candyTypes) {
|
|
281
|
-
const newCandies = [];
|
|
282
|
-
const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;
|
|
283
|
-
for (let col = 0; col < size; col++) {
|
|
284
|
-
// Count empty spaces in this column
|
|
285
|
-
let emptyCount = 0;
|
|
286
|
-
for (let row = 0; row < size; row++) {
|
|
287
|
-
const gridRow = grid[row];
|
|
288
|
-
if (gridRow && !gridRow[col]) {
|
|
289
|
-
emptyCount++;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Fill from top
|
|
294
|
-
for (let i = 0; i < emptyCount; i++) {
|
|
295
|
-
const row = i;
|
|
296
|
-
const candy = {
|
|
297
|
-
id: this.nextCandyId++,
|
|
298
|
-
row,
|
|
299
|
-
col,
|
|
300
|
-
type: Math.floor(Math.random() * candyTypes),
|
|
301
|
-
specialType: CANDY_TYPES.NORMAL,
|
|
302
|
-
animatedValue: new Animated.Value(0),
|
|
303
|
-
scaleValue: new Animated.Value(1),
|
|
304
|
-
isMatched: false,
|
|
305
|
-
isFalling: true
|
|
306
|
-
};
|
|
307
|
-
const gridRow = grid[row];
|
|
308
|
-
if (gridRow) {
|
|
309
|
-
gridRow[col] = candy;
|
|
310
|
-
newCandies.push(candy);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
return newCandies;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// Check if there are any possible moves
|
|
318
|
-
hasPossibleMoves(grid) {
|
|
319
|
-
const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;
|
|
320
|
-
for (let row = 0; row < size; row++) {
|
|
321
|
-
for (let col = 0; col < size; col++) {
|
|
322
|
-
const gridRow = grid[row];
|
|
323
|
-
if (!gridRow) continue;
|
|
324
|
-
const candy = gridRow[col];
|
|
325
|
-
if (!candy) continue;
|
|
326
|
-
|
|
327
|
-
// Try swapping with right neighbor
|
|
328
|
-
if (col < size - 1) {
|
|
329
|
-
const rightCandy = gridRow[col + 1];
|
|
330
|
-
if (rightCandy) {
|
|
331
|
-
this.swapCandies(grid, candy, rightCandy);
|
|
332
|
-
const result = this.findMatches(grid);
|
|
333
|
-
this.swapCandies(grid, candy, rightCandy); // Swap back
|
|
334
|
-
|
|
335
|
-
if (result.hasMatches) {
|
|
336
|
-
return true;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Try swapping with bottom neighbor
|
|
342
|
-
if (row < size - 1) {
|
|
343
|
-
const bottomRow = grid[row + 1];
|
|
344
|
-
const bottomCandy = bottomRow ? bottomRow[col] : null;
|
|
345
|
-
if (bottomCandy) {
|
|
346
|
-
this.swapCandies(grid, candy, bottomCandy);
|
|
347
|
-
const result = this.findMatches(grid);
|
|
348
|
-
this.swapCandies(grid, candy, bottomCandy); // Swap back
|
|
349
|
-
|
|
350
|
-
if (result.hasMatches) {
|
|
351
|
-
return true;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Get candy color
|
|
361
|
-
getCandyColor(type) {
|
|
362
|
-
return CANDY_COLORS[type % CANDY_COLORS.length] || CANDY_COLORS[0];
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Reset candy ID counter
|
|
366
|
-
resetIdCounter() {
|
|
367
|
-
this.nextCandyId = 1;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
//# sourceMappingURL=CandyCrushService.js.map
|
|
1
|
+
"use strict";import{Animated}from 'react-native';import{CANDY_CRUSH_GAME_CONFIG,CANDY_COLORS,CANDY_TYPES}from "./CandyCrushConstants.js";export class CandyCrushService{nextCandyId = 1;createGrid(){return Array(CANDY_CRUSH_GAME_CONFIG.GRID_SIZE).fill(null).map(()=> Array(CANDY_CRUSH_GAME_CONFIG.GRID_SIZE).fill(null));}initializeGrid(candyTypes){const grid = this.createGrid();const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;for(let row = 0;row < size;row++){for(let col = 0;col < size;col++){let candyType;let attempts = 0;do{candyType = Math.floor(Math.random()* candyTypes);attempts++;}while(attempts < 10 && this.wouldCreateMatch(grid,row,col,candyType));const candy ={id:this.nextCandyId++,row,col,type:candyType,specialType:CANDY_TYPES.NORMAL,animatedValue:new Animated.Value(1),scaleValue:new Animated.Value(1),isMatched:false,isFalling:false};const gridRow = grid[row];if(gridRow){gridRow[col] = candy;}}}return grid;}wouldCreateMatch(grid,row,col,type){let horizontalCount = 1;for(let c = col - 1;c >= 0;c--){const gridRow = grid[row];const candy = gridRow ? gridRow[c]:null;if(candy && candy.type === type){horizontalCount++;}else{break;}}for(let c = col + 1;c < CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;c++){const gridRow = grid[row];const candy = gridRow ? gridRow[c]:null;if(candy && candy.type === type){horizontalCount++;}else{break;}}if(horizontalCount >= CANDY_CRUSH_GAME_CONFIG.MIN_MATCH){return true;}let verticalCount = 1;for(let r = row - 1;r >= 0;r--){const gridRow = grid[r];const candy = gridRow ? gridRow[col]:null;if(candy && candy.type === type){verticalCount++;}else{break;}}for(let r = row + 1;r < CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;r++){const gridRow = grid[r];const candy = gridRow ? gridRow[col]:null;if(candy && candy.type === type){verticalCount++;}else{break;}}return verticalCount >= CANDY_CRUSH_GAME_CONFIG.MIN_MATCH;}canSwap(candy1,candy2){const rowDiff = Math.abs(candy1.row - candy2.row);const colDiff = Math.abs(candy1.col - candy2.col);return rowDiff === 1 && colDiff === 0 || rowDiff === 0 && colDiff === 1;}swapCandies(grid,candy1,candy2){const row1 = candy1.row;const col1 = candy1.col;const row2 = candy2.row;const col2 = candy2.col;const gridRow1 = grid[row1];const gridRow2 = grid[row2];if(gridRow1 && gridRow2){const temp1 = gridRow1[col1];const temp2 = gridRow2[col2];gridRow1[col1] = temp2 || null;gridRow2[col2] = temp1 || null;if(temp1){temp1.row = row2;temp1.col = col2;}if(temp2){temp2.row = row1;temp2.col = col1;}}}findMatches(grid){const matches = [];const matchedSet = new Set();const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;for(let row = 0;row < size;row++){for(let col = 0;col < size - 2;col++){const gridRow = grid[row];if(!gridRow)continue;const candy1 = gridRow[col];const candy2 = gridRow[col + 1];const candy3 = gridRow[col + 2];if(candy1 && candy2 && candy3 && candy1.type === candy2.type && candy2.type === candy3.type){let matchLength = 3;const matchCandies = [candy1,candy2,candy3];for(let c = col + 3;c < size;c++){const nextCandy = gridRow[c];if(nextCandy && nextCandy.type === candy1.type){matchLength++;matchCandies.push(nextCandy);}else{break;}}matchCandies.forEach(candy =>{if(!matchedSet.has(candy.id)){matchedSet.add(candy.id);matches.push(candy);}});col += matchLength - 1;}}}for(let col = 0;col < size;col++){for(let row = 0;row < size - 2;row++){const gridRow1 = grid[row];const gridRow2 = grid[row + 1];const gridRow3 = grid[row + 2];if(!gridRow1 || !gridRow2 || !gridRow3)continue;const candy1 = gridRow1[col];const candy2 = gridRow2[col];const candy3 = gridRow3[col];if(candy1 && candy2 && candy3 && candy1.type === candy2.type && candy2.type === candy3.type){let matchLength = 3;const matchCandies = [candy1,candy2,candy3];for(let r = row + 3;r < size;r++){const nextRow = grid[r];const nextCandy = nextRow ? nextRow[col]:null;if(nextCandy && nextCandy.type === candy1.type){matchLength++;matchCandies.push(nextCandy);}else{break;}}matchCandies.forEach(candy =>{if(!matchedSet.has(candy.id)){matchedSet.add(candy.id);matches.push(candy);}});row += matchLength - 1;}}}const score = matches.length * CANDY_CRUSH_GAME_CONFIG.POINTS_PER_CANDY;return{matches,score,hasMatches:matches.length > 0};}removeMatches(grid,matches){matches.forEach(candy =>{const gridRow = grid[candy.row];if(gridRow){gridRow[candy.col] = null;}});}applyGravity(grid){let hasMoved = false;const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;for(let col = 0;col < size;col++){for(let row = size - 1;row >= 0;row--){const gridRow = grid[row];if(!gridRow)continue;const candy = gridRow[col];if(candy){let newRow = row;for(let r = row + 1;r < size;r++){const checkRow = grid[r];if(checkRow && !checkRow[col]){newRow = r;}else{break;}}if(newRow !== row){const targetRow = grid[newRow];if(targetRow){targetRow[col] = candy;gridRow[col] = null;candy.row = newRow;candy.isFalling = true;hasMoved = true;}}}}}return hasMoved;}fillEmptySpaces(grid,candyTypes){const newCandies = [];const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;for(let col = 0;col < size;col++){let emptyCount = 0;for(let row = 0;row < size;row++){const gridRow = grid[row];if(gridRow && !gridRow[col]){emptyCount++;}}for(let i = 0;i < emptyCount;i++){const row = i;const candy ={id:this.nextCandyId++,row,col,type:Math.floor(Math.random()* candyTypes),specialType:CANDY_TYPES.NORMAL,animatedValue:new Animated.Value(0),scaleValue:new Animated.Value(1),isMatched:false,isFalling:true};const gridRow = grid[row];if(gridRow){gridRow[col] = candy;newCandies.push(candy);}}}return newCandies;}hasPossibleMoves(grid){const size = CANDY_CRUSH_GAME_CONFIG.GRID_SIZE;for(let row = 0;row < size;row++){for(let col = 0;col < size;col++){const gridRow = grid[row];if(!gridRow)continue;const candy = gridRow[col];if(!candy)continue;if(col < size - 1){const rightCandy = gridRow[col + 1];if(rightCandy){this.swapCandies(grid,candy,rightCandy);const result = this.findMatches(grid);this.swapCandies(grid,candy,rightCandy);if(result.hasMatches){return true;}}}if(row < size - 1){const bottomRow = grid[row + 1];const bottomCandy = bottomRow ? bottomRow[col]:null;if(bottomCandy){this.swapCandies(grid,candy,bottomCandy);const result = this.findMatches(grid);this.swapCandies(grid,candy,bottomCandy);if(result.hasMatches){return true;}}}}}return false;}getCandyColor(type){return CANDY_COLORS[type % CANDY_COLORS.length] || CANDY_COLORS[0];}resetIdCounter(){this.nextCandyId = 1;}}
|
|
@@ -1,303 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
import { create } from 'zustand';
|
|
4
|
-
import { subscribeWithSelector } from 'zustand/middleware';
|
|
5
|
-
import { CandyCrushService } from "./CandyCrushService.js";
|
|
6
|
-
import { CANDY_CRUSH_GAME_CONFIG, DIFFICULTY_CONFIG, CANDY_CRUSH_SOUNDS } from "./CandyCrushConstants.js";
|
|
7
|
-
import { immerMiddleware } from "../../services/UtilsService.js";
|
|
8
|
-
import { playSound } from "../../services/SoundsService.js";
|
|
9
|
-
import { playHaptic, HapticType } from "../../services/HapticsService.js";
|
|
10
|
-
const gameService = new CandyCrushService();
|
|
11
|
-
export const useCandyCrushStore = create()(subscribeWithSelector(immerMiddleware((set, get) => ({
|
|
12
|
-
// Initial state
|
|
13
|
-
grid: gameService.createGrid(),
|
|
14
|
-
score: 0,
|
|
15
|
-
timeRemaining: 180,
|
|
16
|
-
gameDuration: 180,
|
|
17
|
-
isPlaying: false,
|
|
18
|
-
isGameOver: false,
|
|
19
|
-
selectedCandy: null,
|
|
20
|
-
isProcessing: false,
|
|
21
|
-
difficulty: 'easy',
|
|
22
|
-
candyTypes: 4,
|
|
23
|
-
enableSounds: true,
|
|
24
|
-
enableHaptics: true,
|
|
25
|
-
comboCount: 0,
|
|
26
|
-
gameTimer: null,
|
|
27
|
-
matchedCandyIds: new Set(),
|
|
28
|
-
fallingCandyIds: new Set(),
|
|
29
|
-
startGame: () => {
|
|
30
|
-
const state = get();
|
|
31
|
-
|
|
32
|
-
// Clear any existing timer
|
|
33
|
-
if (state.gameTimer) {
|
|
34
|
-
clearInterval(state.gameTimer);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Play start sound
|
|
38
|
-
playSound(CANDY_CRUSH_SOUNDS.SWAP, state.enableSounds);
|
|
39
|
-
playHaptic(HapticType.MEDIUM, state.enableHaptics);
|
|
40
|
-
|
|
41
|
-
// Start countdown timer
|
|
42
|
-
const timer = setInterval(() => {
|
|
43
|
-
const currentState = get();
|
|
44
|
-
if (currentState.timeRemaining > 0 && currentState.isPlaying) {
|
|
45
|
-
set(draft => {
|
|
46
|
-
draft.timeRemaining--;
|
|
47
|
-
|
|
48
|
-
// Warning sound at 30 seconds
|
|
49
|
-
if (draft.timeRemaining === 30) {
|
|
50
|
-
playSound(CANDY_CRUSH_SOUNDS.SPECIAL, draft.enableSounds);
|
|
51
|
-
playHaptic(HapticType.MEDIUM, draft.enableHaptics);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Warning sound at 10 seconds
|
|
55
|
-
if (draft.timeRemaining === 10) {
|
|
56
|
-
playSound(CANDY_CRUSH_SOUNDS.SPECIAL, draft.enableSounds);
|
|
57
|
-
playHaptic(HapticType.HEAVY, draft.enableHaptics);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Check if time is up
|
|
61
|
-
if (draft.timeRemaining <= 0) {
|
|
62
|
-
draft.isGameOver = true;
|
|
63
|
-
draft.isPlaying = false;
|
|
64
|
-
if (draft.gameTimer) {
|
|
65
|
-
clearInterval(draft.gameTimer);
|
|
66
|
-
draft.gameTimer = null;
|
|
67
|
-
}
|
|
68
|
-
playSound(CANDY_CRUSH_SOUNDS.LOSE, draft.enableSounds);
|
|
69
|
-
playHaptic(HapticType.HEAVY, draft.enableHaptics);
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}, 1000);
|
|
74
|
-
set(state => {
|
|
75
|
-
state.isPlaying = true;
|
|
76
|
-
state.gameTimer = timer;
|
|
77
|
-
});
|
|
78
|
-
},
|
|
79
|
-
stopGame: () => {
|
|
80
|
-
const state = get();
|
|
81
|
-
if (state.gameTimer) {
|
|
82
|
-
clearInterval(state.gameTimer);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Play stop sound
|
|
86
|
-
playSound(CANDY_CRUSH_SOUNDS.LOSE, state.enableSounds);
|
|
87
|
-
playHaptic(HapticType.MEDIUM, state.enableHaptics);
|
|
88
|
-
|
|
89
|
-
// Reset the game instead of showing game over modal
|
|
90
|
-
state.initializeGame(state.difficulty);
|
|
91
|
-
},
|
|
92
|
-
resetGame: () => {
|
|
93
|
-
const state = get();
|
|
94
|
-
state.initializeGame(state.difficulty);
|
|
95
|
-
state.startGame();
|
|
96
|
-
},
|
|
97
|
-
initializeGame: (difficulty = 'easy') => {
|
|
98
|
-
const state = get();
|
|
99
|
-
if (state.gameTimer) {
|
|
100
|
-
clearInterval(state.gameTimer);
|
|
101
|
-
}
|
|
102
|
-
set(state => {
|
|
103
|
-
const config = DIFFICULTY_CONFIG[difficulty];
|
|
104
|
-
gameService.resetIdCounter();
|
|
105
|
-
state.grid = gameService.initializeGrid(config.candyTypes);
|
|
106
|
-
state.score = 0;
|
|
107
|
-
state.timeRemaining = config.gameDuration;
|
|
108
|
-
state.gameDuration = config.gameDuration;
|
|
109
|
-
state.isPlaying = false;
|
|
110
|
-
state.isGameOver = false;
|
|
111
|
-
state.selectedCandy = null;
|
|
112
|
-
state.isProcessing = false;
|
|
113
|
-
state.difficulty = difficulty;
|
|
114
|
-
state.candyTypes = config.candyTypes;
|
|
115
|
-
state.comboCount = 0;
|
|
116
|
-
state.gameTimer = null;
|
|
117
|
-
state.matchedCandyIds = new Set();
|
|
118
|
-
state.fallingCandyIds = new Set();
|
|
119
|
-
});
|
|
120
|
-
},
|
|
121
|
-
selectCandy: candy => {
|
|
122
|
-
const state = get();
|
|
123
|
-
if (state.isProcessing || !state.isPlaying || state.isGameOver) {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
set(draft => {
|
|
127
|
-
if (!draft.selectedCandy) {
|
|
128
|
-
// First candy selected
|
|
129
|
-
draft.selectedCandy = candy;
|
|
130
|
-
playSound(CANDY_CRUSH_SOUNDS.SWAP, draft.enableSounds);
|
|
131
|
-
playHaptic(HapticType.LIGHT, draft.enableHaptics);
|
|
132
|
-
} else if (draft.selectedCandy.id === candy.id) {
|
|
133
|
-
// Deselect same candy
|
|
134
|
-
draft.selectedCandy = null;
|
|
135
|
-
} else if (gameService.canSwap(draft.selectedCandy, candy)) {
|
|
136
|
-
// Valid swap - process it
|
|
137
|
-
const candy1 = draft.selectedCandy;
|
|
138
|
-
const candy2 = candy;
|
|
139
|
-
|
|
140
|
-
// Swap candies
|
|
141
|
-
gameService.swapCandies(draft.grid, candy1, candy2);
|
|
142
|
-
|
|
143
|
-
// Only update affected rows to minimize re-renders
|
|
144
|
-
const affectedRows = new Set([candy1.row, candy2.row]);
|
|
145
|
-
affectedRows.forEach(rowIndex => {
|
|
146
|
-
if (draft.grid[rowIndex]) {
|
|
147
|
-
draft.grid[rowIndex] = [...draft.grid[rowIndex]];
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Check for matches
|
|
152
|
-
const matchResult = gameService.findMatches(draft.grid);
|
|
153
|
-
if (matchResult.hasMatches) {
|
|
154
|
-
// Valid move
|
|
155
|
-
draft.selectedCandy = null;
|
|
156
|
-
draft.isProcessing = true;
|
|
157
|
-
playSound(CANDY_CRUSH_SOUNDS.SWAP, draft.enableSounds);
|
|
158
|
-
playHaptic(HapticType.MEDIUM, draft.enableHaptics);
|
|
159
|
-
|
|
160
|
-
// Process matches after animation
|
|
161
|
-
setTimeout(() => {
|
|
162
|
-
get().processMatches();
|
|
163
|
-
}, CANDY_CRUSH_GAME_CONFIG.SWAP_DURATION);
|
|
164
|
-
} else {
|
|
165
|
-
// Invalid move - swap back
|
|
166
|
-
gameService.swapCandies(draft.grid, candy1, candy2);
|
|
167
|
-
draft.selectedCandy = null;
|
|
168
|
-
|
|
169
|
-
// Only update affected rows to minimize re-renders
|
|
170
|
-
const affectedRows = new Set([candy1.row, candy2.row]);
|
|
171
|
-
affectedRows.forEach(rowIndex => {
|
|
172
|
-
if (draft.grid[rowIndex]) {
|
|
173
|
-
draft.grid[rowIndex] = [...draft.grid[rowIndex]];
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
playSound(CANDY_CRUSH_SOUNDS.SWAP, draft.enableSounds);
|
|
177
|
-
playHaptic(HapticType.LIGHT, draft.enableHaptics);
|
|
178
|
-
}
|
|
179
|
-
} else {
|
|
180
|
-
// Not adjacent - select new candy
|
|
181
|
-
draft.selectedCandy = candy;
|
|
182
|
-
playSound(CANDY_CRUSH_SOUNDS.SWAP, draft.enableSounds);
|
|
183
|
-
playHaptic(HapticType.LIGHT, draft.enableHaptics);
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
},
|
|
187
|
-
processMatches: async () => {
|
|
188
|
-
let hasMatches = true;
|
|
189
|
-
let comboCount = 0;
|
|
190
|
-
while (hasMatches) {
|
|
191
|
-
// Get fresh state at the start of each iteration
|
|
192
|
-
const state = get();
|
|
193
|
-
|
|
194
|
-
// Find matches
|
|
195
|
-
const matchResult = gameService.findMatches(state.grid);
|
|
196
|
-
if (!matchResult.hasMatches) {
|
|
197
|
-
hasMatches = false;
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
comboCount++;
|
|
201
|
-
|
|
202
|
-
// Calculate score with combo multiplier
|
|
203
|
-
const baseScore = matchResult.score;
|
|
204
|
-
const comboMultiplier = comboCount > 1 ? Math.pow(CANDY_CRUSH_GAME_CONFIG.COMBO_MULTIPLIER, comboCount - 1) : 1;
|
|
205
|
-
const finalScore = Math.floor(baseScore * comboMultiplier);
|
|
206
|
-
|
|
207
|
-
// Update score
|
|
208
|
-
set(draft => {
|
|
209
|
-
draft.score += finalScore;
|
|
210
|
-
draft.comboCount = comboCount;
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
// Play sound
|
|
214
|
-
if (comboCount > 1) {
|
|
215
|
-
playSound(CANDY_CRUSH_SOUNDS.COMBO, state.enableSounds);
|
|
216
|
-
playHaptic(HapticType.HEAVY, state.enableHaptics);
|
|
217
|
-
} else {
|
|
218
|
-
playSound(CANDY_CRUSH_SOUNDS.MATCH, state.enableSounds);
|
|
219
|
-
playHaptic(HapticType.MEDIUM, state.enableHaptics);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Mark candies as matched for animation using Set (no grid mutation needed)
|
|
223
|
-
set(draft => {
|
|
224
|
-
draft.matchedCandyIds = new Set(matchResult.matches.map(c => c.id));
|
|
225
|
-
// No grid update needed - animation state is separate
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Wait for animation
|
|
229
|
-
await new Promise(resolve => setTimeout(resolve, CANDY_CRUSH_GAME_CONFIG.ANIMATION_DURATION));
|
|
230
|
-
|
|
231
|
-
// Remove matches, apply gravity, and fill empty spaces (all in one set call)
|
|
232
|
-
set(draft => {
|
|
233
|
-
// Clear matched IDs
|
|
234
|
-
draft.matchedCandyIds = new Set();
|
|
235
|
-
|
|
236
|
-
// Track which rows will be affected by matches
|
|
237
|
-
const affectedRows = new Set();
|
|
238
|
-
matchResult.matches.forEach(candy => affectedRows.add(candy.row));
|
|
239
|
-
|
|
240
|
-
// Store original candy IDs per row to detect changes
|
|
241
|
-
const originalRowIds = draft.grid.map(row => row.map(c => c?.id).join(','));
|
|
242
|
-
|
|
243
|
-
// Remove matches
|
|
244
|
-
gameService.removeMatches(draft.grid, matchResult.matches);
|
|
245
|
-
|
|
246
|
-
// Apply gravity (affects all rows below matches)
|
|
247
|
-
gameService.applyGravity(draft.grid);
|
|
248
|
-
|
|
249
|
-
// Fill empty spaces (affects top rows)
|
|
250
|
-
gameService.fillEmptySpaces(draft.grid, state.candyTypes);
|
|
251
|
-
|
|
252
|
-
// Mark falling candies
|
|
253
|
-
const fallingIds = new Set();
|
|
254
|
-
draft.grid.forEach(row => {
|
|
255
|
-
row.forEach(candy => {
|
|
256
|
-
if (candy && candy.isFalling) {
|
|
257
|
-
fallingIds.add(candy.id);
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
draft.fallingCandyIds = fallingIds;
|
|
262
|
-
|
|
263
|
-
// Only update rows that actually changed
|
|
264
|
-
draft.grid.forEach((row, rowIndex) => {
|
|
265
|
-
const newRowIds = row.map(c => c?.id).join(',');
|
|
266
|
-
if (originalRowIds[rowIndex] !== newRowIds) {
|
|
267
|
-
draft.grid[rowIndex] = [...row];
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// Wait for fall animation
|
|
273
|
-
await new Promise(resolve => setTimeout(resolve, CANDY_CRUSH_GAME_CONFIG.FALL_DURATION));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Reset processing state and clear animation states
|
|
277
|
-
set(draft => {
|
|
278
|
-
draft.isProcessing = false;
|
|
279
|
-
draft.comboCount = 0;
|
|
280
|
-
draft.matchedCandyIds = new Set();
|
|
281
|
-
draft.fallingCandyIds = new Set();
|
|
282
|
-
});
|
|
283
|
-
},
|
|
284
|
-
updateSettings: (enableSounds, enableHaptics) => {
|
|
285
|
-
set(state => {
|
|
286
|
-
state.enableSounds = enableSounds;
|
|
287
|
-
state.enableHaptics = enableHaptics;
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
}))));
|
|
291
|
-
|
|
292
|
-
// Selector hooks for optimal re-renders
|
|
293
|
-
export const useGrid = () => useCandyCrushStore(state => state.grid);
|
|
294
|
-
export const useScore = () => useCandyCrushStore(state => state.score);
|
|
295
|
-
export const useTimeRemaining = () => useCandyCrushStore(state => state.timeRemaining);
|
|
296
|
-
export const useGameDuration = () => useCandyCrushStore(state => state.gameDuration);
|
|
297
|
-
export const useIsPlaying = () => useCandyCrushStore(state => state.isPlaying);
|
|
298
|
-
export const useIsGameOver = () => useCandyCrushStore(state => state.isGameOver);
|
|
299
|
-
export const useSelectedCandy = () => useCandyCrushStore(state => state.selectedCandy);
|
|
300
|
-
export const useComboCount = () => useCandyCrushStore(state => state.comboCount);
|
|
301
|
-
export const useMatchedCandyIds = () => useCandyCrushStore(state => state.matchedCandyIds);
|
|
302
|
-
export const useFallingCandyIds = () => useCandyCrushStore(state => state.fallingCandyIds);
|
|
303
|
-
//# sourceMappingURL=CandyCrushStore.js.map
|
|
1
|
+
"use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{CandyCrushService}from "./CandyCrushService.js";import{CANDY_CRUSH_GAME_CONFIG,DIFFICULTY_CONFIG,CANDY_CRUSH_SOUNDS}from "./CandyCrushConstants.js";import{immerMiddleware}from "../../services/UtilsService.js";import{playSound}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";const gameService = new CandyCrushService();export const useCandyCrushStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({grid:gameService.createGrid(),score:0,timeRemaining:180,gameDuration:180,isPlaying:false,isGameOver:false,selectedCandy:null,isProcessing:false,difficulty:'easy',candyTypes:4,enableSounds:true,enableHaptics:true,comboCount:0,gameTimer:null,matchedCandyIds:new Set(),fallingCandyIds:new Set(),startGame:()=>{const state = get();if(state.gameTimer){clearInterval(state.gameTimer);}playSound(CANDY_CRUSH_SOUNDS.SWAP,state.enableSounds);playHaptic(HapticType.MEDIUM,state.enableHaptics);const timer = setInterval(()=>{const currentState = get();if(currentState.timeRemaining > 0 && currentState.isPlaying){set(draft =>{draft.timeRemaining--;if(draft.timeRemaining === 30){playSound(CANDY_CRUSH_SOUNDS.SPECIAL,draft.enableSounds);playHaptic(HapticType.MEDIUM,draft.enableHaptics);}if(draft.timeRemaining === 10){playSound(CANDY_CRUSH_SOUNDS.SPECIAL,draft.enableSounds);playHaptic(HapticType.HEAVY,draft.enableHaptics);}if(draft.timeRemaining <= 0){draft.isGameOver = true;draft.isPlaying = false;if(draft.gameTimer){clearInterval(draft.gameTimer);draft.gameTimer = null;}playSound(CANDY_CRUSH_SOUNDS.LOSE,draft.enableSounds);playHaptic(HapticType.HEAVY,draft.enableHaptics);}});}},1000);set(state =>{state.isPlaying = true;state.gameTimer = timer;});},stopGame:()=>{const state = get();if(state.gameTimer){clearInterval(state.gameTimer);}playSound(CANDY_CRUSH_SOUNDS.LOSE,state.enableSounds);playHaptic(HapticType.MEDIUM,state.enableHaptics);state.initializeGame(state.difficulty);},resetGame:()=>{const state = get();state.initializeGame(state.difficulty);state.startGame();},initializeGame:(difficulty = 'easy')=>{const state = get();if(state.gameTimer){clearInterval(state.gameTimer);}set(state =>{const config = DIFFICULTY_CONFIG[difficulty];gameService.resetIdCounter();state.grid = gameService.initializeGrid(config.candyTypes);state.score = 0;state.timeRemaining = config.gameDuration;state.gameDuration = config.gameDuration;state.isPlaying = false;state.isGameOver = false;state.selectedCandy = null;state.isProcessing = false;state.difficulty = difficulty;state.candyTypes = config.candyTypes;state.comboCount = 0;state.gameTimer = null;state.matchedCandyIds = new Set();state.fallingCandyIds = new Set();});},selectCandy:candy =>{const state = get();if(state.isProcessing || !state.isPlaying || state.isGameOver){return;}set(draft =>{if(!draft.selectedCandy){draft.selectedCandy = candy;playSound(CANDY_CRUSH_SOUNDS.SWAP,draft.enableSounds);playHaptic(HapticType.LIGHT,draft.enableHaptics);}else if(draft.selectedCandy.id === candy.id){draft.selectedCandy = null;}else if(gameService.canSwap(draft.selectedCandy,candy)){const candy1 = draft.selectedCandy;const candy2 = candy;gameService.swapCandies(draft.grid,candy1,candy2);const affectedRows = new Set([candy1.row,candy2.row]);affectedRows.forEach(rowIndex =>{if(draft.grid[rowIndex]){draft.grid[rowIndex] = [...draft.grid[rowIndex]];}});const matchResult = gameService.findMatches(draft.grid);if(matchResult.hasMatches){draft.selectedCandy = null;draft.isProcessing = true;playSound(CANDY_CRUSH_SOUNDS.SWAP,draft.enableSounds);playHaptic(HapticType.MEDIUM,draft.enableHaptics);setTimeout(()=>{get().processMatches();},CANDY_CRUSH_GAME_CONFIG.SWAP_DURATION);}else{gameService.swapCandies(draft.grid,candy1,candy2);draft.selectedCandy = null;const affectedRows = new Set([candy1.row,candy2.row]);affectedRows.forEach(rowIndex =>{if(draft.grid[rowIndex]){draft.grid[rowIndex] = [...draft.grid[rowIndex]];}});playSound(CANDY_CRUSH_SOUNDS.SWAP,draft.enableSounds);playHaptic(HapticType.LIGHT,draft.enableHaptics);}}else{draft.selectedCandy = candy;playSound(CANDY_CRUSH_SOUNDS.SWAP,draft.enableSounds);playHaptic(HapticType.LIGHT,draft.enableHaptics);}});},processMatches:async()=>{let hasMatches = true;let comboCount = 0;while(hasMatches){const state = get();const matchResult = gameService.findMatches(state.grid);if(!matchResult.hasMatches){hasMatches = false;break;}comboCount++;const baseScore = matchResult.score;const comboMultiplier = comboCount > 1 ? Math.pow(CANDY_CRUSH_GAME_CONFIG.COMBO_MULTIPLIER,comboCount - 1):1;const finalScore = Math.floor(baseScore * comboMultiplier);set(draft =>{draft.score += finalScore;draft.comboCount = comboCount;});if(comboCount > 1){playSound(CANDY_CRUSH_SOUNDS.COMBO,state.enableSounds);playHaptic(HapticType.HEAVY,state.enableHaptics);}else{playSound(CANDY_CRUSH_SOUNDS.MATCH,state.enableSounds);playHaptic(HapticType.MEDIUM,state.enableHaptics);}set(draft =>{draft.matchedCandyIds = new Set(matchResult.matches.map(c => c.id));});await new Promise(resolve => setTimeout(resolve,CANDY_CRUSH_GAME_CONFIG.ANIMATION_DURATION));set(draft =>{draft.matchedCandyIds = new Set();const affectedRows = new Set();matchResult.matches.forEach(candy => affectedRows.add(candy.row));const originalRowIds = draft.grid.map(row => row.map(c => c?.id).join(','));gameService.removeMatches(draft.grid,matchResult.matches);gameService.applyGravity(draft.grid);gameService.fillEmptySpaces(draft.grid,state.candyTypes);const fallingIds = new Set();draft.grid.forEach(row =>{row.forEach(candy =>{if(candy && candy.isFalling){fallingIds.add(candy.id);}});});draft.fallingCandyIds = fallingIds;draft.grid.forEach((row,rowIndex)=>{const newRowIds = row.map(c => c?.id).join(',');if(originalRowIds[rowIndex] !== newRowIds){draft.grid[rowIndex] = [...row];}});});await new Promise(resolve => setTimeout(resolve,CANDY_CRUSH_GAME_CONFIG.FALL_DURATION));}set(draft =>{draft.isProcessing = false;draft.comboCount = 0;draft.matchedCandyIds = new Set();draft.fallingCandyIds = new Set();});},updateSettings:(enableSounds,enableHaptics)=>{set(state =>{state.enableSounds = enableSounds;state.enableHaptics = enableHaptics;});}}))));export const useGrid =()=> useCandyCrushStore(state => state.grid);export const useScore =()=> useCandyCrushStore(state => state.score);export const useTimeRemaining =()=> useCandyCrushStore(state => state.timeRemaining);export const useGameDuration =()=> useCandyCrushStore(state => state.gameDuration);export const useIsPlaying =()=> useCandyCrushStore(state => state.isPlaying);export const useIsGameOver =()=> useCandyCrushStore(state => state.isGameOver);export const useSelectedCandy =()=> useCandyCrushStore(state => state.selectedCandy);export const useComboCount =()=> useCandyCrushStore(state => state.comboCount);export const useMatchedCandyIds =()=> useCandyCrushStore(state => state.matchedCandyIds);export const useFallingCandyIds =()=> useCandyCrushStore(state => state.fallingCandyIds);
|