react-native-games 1.3.0 → 1.5.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.
Files changed (195) hide show
  1. package/lib/module/games/balloon-blaster/BalloonBlaster.js +1 -1
  2. package/lib/module/games/balloon-blaster/BalloonBlasterConstants.js +1 -1
  3. package/lib/module/games/balloon-blaster/BalloonBlasterService.js +1 -1
  4. package/lib/module/games/balloon-blaster/BalloonBlasterStore.js +1 -1
  5. package/lib/module/games/balloon-blaster/components/BalloonComponent.js +1 -1
  6. package/lib/module/games/balloon-blaster/components/GameArea.js +1 -1
  7. package/lib/module/games/balloon-blaster/components/GameBackground.js +1 -1
  8. package/lib/module/games/balloon-blaster/components/ScoreBoard.js +1 -1
  9. package/lib/module/games/balloon-blaster/components/index.js +1 -1
  10. package/lib/module/games/bike-racing/BikeRacing.js +1 -1
  11. package/lib/module/games/bike-racing/BikeRacingConstants.js +1 -1
  12. package/lib/module/games/bike-racing/BikeRacingService.js +1 -1
  13. package/lib/module/games/bike-racing/BikeRacingStore.js +1 -1
  14. package/lib/module/games/bike-racing/components/BikeComponent.js +1 -1
  15. package/lib/module/games/bike-racing/components/GameBackground.js +1 -1
  16. package/lib/module/games/bike-racing/components/ScoreBoard.js +1 -1
  17. package/lib/module/games/bike-racing/components/index.js +1 -1
  18. package/lib/module/games/bike-racing/index.js +1 -1
  19. package/lib/module/games/block-blast/BlockBlast.js +1 -1
  20. package/lib/module/games/block-blast/BlockBlastConstants.js +1 -1
  21. package/lib/module/games/block-blast/BlockBlastService.js +1 -1
  22. package/lib/module/games/block-blast/BlockBlastStore.js +1 -1
  23. package/lib/module/games/block-blast/components/BlockPieceComponent.js +1 -1
  24. package/lib/module/games/block-blast/components/GameArea.js +1 -1
  25. package/lib/module/games/block-blast/components/GameBackground.js +1 -1
  26. package/lib/module/games/block-blast/components/GridComponent.js +1 -1
  27. package/lib/module/games/block-blast/components/ScoreBoard.js +1 -1
  28. package/lib/module/games/block-blast/components/index.js +1 -1
  29. package/lib/module/games/block-blast/index.js +1 -1
  30. package/lib/module/games/bubble-shooter/BubbleShooter.js +1 -1
  31. package/lib/module/games/bubble-shooter/BubbleShooterConstants.js +1 -1
  32. package/lib/module/games/bubble-shooter/BubbleShooterService.js +1 -1
  33. package/lib/module/games/bubble-shooter/BubbleShooterStore.js +1 -1
  34. package/lib/module/games/bubble-shooter/components/GameArea.js +1 -1
  35. package/lib/module/games/bubble-shooter/components/GameBackground.js +1 -1
  36. package/lib/module/games/bubble-shooter/components/ScoreBoard.js +1 -1
  37. package/lib/module/games/bubble-shooter/components/index.js +1 -1
  38. package/lib/module/games/bubble-shooter/index.js +1 -1
  39. package/lib/module/games/candy-crush/CandyCrush.js +1 -1
  40. package/lib/module/games/candy-crush/CandyCrushConstants.js +1 -1
  41. package/lib/module/games/candy-crush/CandyCrushService.js +1 -1
  42. package/lib/module/games/candy-crush/CandyCrushStore.js +1 -1
  43. package/lib/module/games/candy-crush/components/CandyItem.js +1 -1
  44. package/lib/module/games/candy-crush/components/GameBackground.js +1 -1
  45. package/lib/module/games/candy-crush/components/GameGrid.js +1 -1
  46. package/lib/module/games/candy-crush/components/ScoreBoard.js +1 -1
  47. package/lib/module/games/candy-crush/components/index.js +1 -1
  48. package/lib/module/games/candy-crush/index.js +1 -1
  49. package/lib/module/games/car-racing/CarRacing.js +1 -1
  50. package/lib/module/games/car-racing/CarRacingConstants.js +1 -1
  51. package/lib/module/games/car-racing/CarRacingService.js +1 -1
  52. package/lib/module/games/car-racing/CarRacingStore.js +1 -1
  53. package/lib/module/games/car-racing/components/CarComponent.js +1 -1
  54. package/lib/module/games/car-racing/components/GameBackground.js +1 -1
  55. package/lib/module/games/car-racing/components/ScoreBoard.js +1 -1
  56. package/lib/module/games/car-racing/components/index.js +1 -1
  57. package/lib/module/games/colors-sort/ColorsSort.js +1 -1
  58. package/lib/module/games/colors-sort/ColorsSortConstants.js +1 -1
  59. package/lib/module/games/colors-sort/ColorsSortService.js +1 -1
  60. package/lib/module/games/colors-sort/ColorsSortStore.js +1 -1
  61. package/lib/module/games/colors-sort/components/ColorContainer.js +1 -1
  62. package/lib/module/games/colors-sort/components/GameBackground.js +1 -1
  63. package/lib/module/games/colors-sort/components/ScoreBoard.js +1 -1
  64. package/lib/module/games/colors-sort/components/index.js +1 -1
  65. package/lib/module/games/dino-jump/DinoJump.js +1 -1
  66. package/lib/module/games/dino-jump/DinoJumpConstants.js +1 -1
  67. package/lib/module/games/dino-jump/DinoJumpService.js +1 -1
  68. package/lib/module/games/dino-jump/DinoJumpStore.js +1 -1
  69. package/lib/module/games/dino-jump/components/DinoSprite.js +1 -1
  70. package/lib/module/games/dino-jump/components/GameArea.js +1 -1
  71. package/lib/module/games/dino-jump/components/GameBackground.js +1 -1
  72. package/lib/module/games/dino-jump/components/ObstacleSprite.js +1 -1
  73. package/lib/module/games/dino-jump/components/ScoreBoard.js +1 -1
  74. package/lib/module/games/dino-jump/components/StarSprite.js +1 -1
  75. package/lib/module/games/dino-jump/components/index.js +1 -1
  76. package/lib/module/games/flappy-bird/FlappyBird.js +1 -1
  77. package/lib/module/games/flappy-bird/FlappyBirdConstants.js +1 -1
  78. package/lib/module/games/flappy-bird/FlappyBirdStore.js +1 -1
  79. package/lib/module/games/flappy-bird/components/Bird.js +1 -1
  80. package/lib/module/games/flappy-bird/components/GameArea.js +1 -1
  81. package/lib/module/games/flappy-bird/components/GameBackground.js +1 -1
  82. package/lib/module/games/flappy-bird/components/Pipes.js +1 -1
  83. package/lib/module/games/flappy-bird/components/ScoreBoard.js +1 -1
  84. package/lib/module/games/flappy-bird/components/index.js +1 -1
  85. package/lib/module/games/fruit-merger/FruitMerger.js +1 -1
  86. package/lib/module/games/fruit-merger/FruitMergerConstants.js +1 -1
  87. package/lib/module/games/fruit-merger/FruitMergerService.js +1 -1
  88. package/lib/module/games/fruit-merger/FruitMergerStore.js +1 -1
  89. package/lib/module/games/fruit-merger/components/FruitItem.js +1 -1
  90. package/lib/module/games/fruit-merger/components/GameArea.js +1 -1
  91. package/lib/module/games/fruit-merger/components/GameBackground.js +1 -1
  92. package/lib/module/games/fruit-merger/components/ScoreBoard.js +1 -1
  93. package/lib/module/games/fruit-merger/components/index.js +1 -1
  94. package/lib/module/games/fruit-ninja/FruitNinja.js +1 -1
  95. package/lib/module/games/fruit-ninja/FruitNinjaConstants.js +1 -1
  96. package/lib/module/games/fruit-ninja/FruitNinjaService.js +1 -1
  97. package/lib/module/games/fruit-ninja/FruitNinjaStore.js +1 -1
  98. package/lib/module/games/fruit-ninja/components/FruitComponent.js +1 -1
  99. package/lib/module/games/fruit-ninja/components/GameArea.js +1 -1
  100. package/lib/module/games/fruit-ninja/components/GameBackground.js +1 -1
  101. package/lib/module/games/fruit-ninja/components/ScoreBoard.js +1 -1
  102. package/lib/module/games/fruit-ninja/components/index.js +1 -1
  103. package/lib/module/games/game-2048/Game2048.js +1 -1
  104. package/lib/module/games/game-2048/Game2048Constants.js +1 -1
  105. package/lib/module/games/game-2048/Game2048Service.js +1 -1
  106. package/lib/module/games/game-2048/Game2048Store.js +1 -1
  107. package/lib/module/games/game-2048/components/GameBackground.js +1 -1
  108. package/lib/module/games/game-2048/components/GameGrid.js +1 -1
  109. package/lib/module/games/game-2048/components/GameTile.js +1 -1
  110. package/lib/module/games/game-2048/components/ScoreBoard.js +1 -1
  111. package/lib/module/games/game-2048/components/index.js +1 -1
  112. package/lib/module/games/maze-runner/MazeRunner.js +1 -1
  113. package/lib/module/games/maze-runner/MazeRunnerConstants.js +1 -1
  114. package/lib/module/games/maze-runner/MazeRunnerService.js +1 -1
  115. package/lib/module/games/maze-runner/components/EnhancedBallComponent.js +1 -1
  116. package/lib/module/games/maze-runner/components/EnhancedGameArea.js +1 -1
  117. package/lib/module/games/maze-runner/components/GameBackground.js +1 -1
  118. package/lib/module/games/maze-runner/components/ScoreBoard.js +1 -1
  119. package/lib/module/games/maze-runner/components/SkiaPipeComponent.js +1 -1
  120. package/lib/module/games/maze-runner/components/StaticGameBackground.js +1 -1
  121. package/lib/module/games/maze-runner/components/WallComponent.js +1 -1
  122. package/lib/module/games/maze-runner/components/index.js +1 -1
  123. package/lib/module/games/perfect-circle/PerfectCircle.js +1 -1
  124. package/lib/module/games/perfect-circle/PerfectCircleConstants.js +1 -1
  125. package/lib/module/games/perfect-circle/PerfectCircleService.js +1 -1
  126. package/lib/module/games/perfect-circle/PerfectCircleStore.js +1 -1
  127. package/lib/module/games/perfect-circle/components/DrawingCanvas.js +1 -1
  128. package/lib/module/games/perfect-circle/components/GameBackground.js +1 -1
  129. package/lib/module/games/perfect-circle/components/ScoreBoard.js +1 -1
  130. package/lib/module/games/perfect-circle/index.js +1 -1
  131. package/lib/module/games/popit-fidget/PopitFidget.js +1 -1
  132. package/lib/module/games/popit-fidget/PopitFidgetConstants.js +1 -1
  133. package/lib/module/games/popit-fidget/PopitFidgetService.js +1 -1
  134. package/lib/module/games/popit-fidget/PopitFidgetStore.js +1 -1
  135. package/lib/module/games/popit-fidget/components/BubbleComponent.js +1 -1
  136. package/lib/module/games/popit-fidget/components/FidgetGrid.js +1 -1
  137. package/lib/module/games/popit-fidget/components/GameBackground.js +1 -1
  138. package/lib/module/games/popit-fidget/components/ScoreBoard.js +1 -1
  139. package/lib/module/games/popit-fidget/components/index.js +1 -1
  140. package/lib/module/games/sliding-numbers/SlidingNumbers.js +1 -1
  141. package/lib/module/games/sliding-numbers/SlidingNumbersConstants.js +1 -1
  142. package/lib/module/games/sliding-numbers/SlidingNumbersService.js +1 -1
  143. package/lib/module/games/sliding-numbers/SlidingNumbersStore.js +1 -1
  144. package/lib/module/games/sliding-numbers/components/GameBackground.js +1 -1
  145. package/lib/module/games/sliding-numbers/components/NumbersGrid.js +1 -1
  146. package/lib/module/games/sliding-numbers/components/NumbersTile.js +1 -1
  147. package/lib/module/games/sliding-numbers/components/ScoreBoard.js +1 -1
  148. package/lib/module/games/sliding-numbers/components/index.js +1 -1
  149. package/lib/module/games/snake/Snake.js +1 -1
  150. package/lib/module/games/snake/SnakeConstants.js +1 -1
  151. package/lib/module/games/snake/SnakeService.js +1 -1
  152. package/lib/module/games/snake/SnakeStore.js +1 -1
  153. package/lib/module/games/snake/components/GameBackground.js +1 -1
  154. package/lib/module/games/snake/components/GameGrid.js +1 -1
  155. package/lib/module/games/snake/components/ScoreBoard.js +1 -1
  156. package/lib/module/games/snake/components/index.js +1 -1
  157. package/lib/module/games/snake/index.js +1 -1
  158. package/lib/module/games/space-fighter/SpaceFighter.js +1 -1
  159. package/lib/module/games/space-fighter/SpaceFighterConstants.js +1 -1
  160. package/lib/module/games/space-fighter/SpaceFighterService.js +1 -1
  161. package/lib/module/games/space-fighter/SpaceFighterStore.js +1 -1
  162. package/lib/module/games/space-fighter/components/AsteroidComponent.js +1 -1
  163. package/lib/module/games/space-fighter/components/GameArea.js +1 -1
  164. package/lib/module/games/space-fighter/components/GameBackground.js +1 -1
  165. package/lib/module/games/space-fighter/components/ScoreBoard.js +1 -1
  166. package/lib/module/games/space-fighter/components/Spacecraft3D.js +1 -1
  167. package/lib/module/games/space-fighter/components/SpacecraftPath.js +1 -1
  168. package/lib/module/games/space-fighter/components/index.js +1 -1
  169. package/lib/module/games/whack-a-mole/WhackAMole.js +1 -1
  170. package/lib/module/games/whack-a-mole/WhackAMoleConstants.js +1 -1
  171. package/lib/module/games/whack-a-mole/WhackAMoleService.js +1 -1
  172. package/lib/module/games/whack-a-mole/WhackAMoleStore.js +1 -1
  173. package/lib/module/games/whack-a-mole/components/GameBackground.js +1 -1
  174. package/lib/module/games/whack-a-mole/components/GameGrid.js +1 -1
  175. package/lib/module/games/whack-a-mole/components/GameHole.js +1 -1
  176. package/lib/module/games/whack-a-mole/components/MoleCharacter.js +1 -1
  177. package/lib/module/games/whack-a-mole/components/ScoreBoard.js +1 -1
  178. package/lib/module/games/whack-a-mole/components/index.js +1 -1
  179. package/lib/module/helpers/AnimationFrame.js +1 -1
  180. package/lib/module/helpers/AnimationTracker.js +1 -1
  181. package/lib/module/helpers/ErrorHandler.js +1 -1
  182. package/lib/module/helpers/GameControlButton.js +1 -1
  183. package/lib/module/helpers/GameOverModal.js +1 -1
  184. package/lib/module/helpers/GameSettingsModal.js +1 -1
  185. package/lib/module/helpers/ParticleBlast.js +1 -1
  186. package/lib/module/helpers/ScoreBoardContainer.js +1 -1
  187. package/lib/module/helpers/index.js +1 -1
  188. package/lib/module/index.js +1 -1
  189. package/lib/module/services/GamesConstants.js +1 -1
  190. package/lib/module/services/GamesService.js +1 -1
  191. package/lib/module/services/HapticsService.js +1 -1
  192. package/lib/module/services/ScoringService.js +1 -1
  193. package/lib/module/services/SoundsService.js +1 -1
  194. package/lib/module/services/UtilsService.js +1 -1
  195. package/package.json +5 -2
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{GameControlButton,GameOverModal,GameSettingsModal}from "../../helpers/index.js";import{DEFAULT_GAME_SETTINGS,GAME_IDS}from "../../services/UtilsService.js";import{playSound,GAME_SOUNDS}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";import{useBlockBlastStore,useScore,useIsPlaying,useGameOver,useGameWon}from "./BlockBlastStore.js";import{BlockBlastService}from "./BlockBlastService.js";import{BLOCK_BLAST_COLORS,BLOCK_BLAST_GAME_CONFIG,getDifficultySettings}from "./BlockBlastConstants.js";import{ScoreBoard,GameArea,GameBackground}from "./components/index.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');export const BlockBlast =({settings:externalSettings = DEFAULT_GAME_SETTINGS,onSettingsChange,onEndGame})=>{const settings = React.useMemo(()=>({difficulty:externalSettings?.difficulty || 'medium',soundEnabled:externalSettings?.enableSounds ?? true,hapticEnabled:externalSettings?.enableHaptics ?? true,offset:externalSettings?.offset ?? 0}),[externalSettings?.difficulty,externalSettings?.enableSounds,externalSettings?.enableHaptics,externalSettings?.offset]);const{difficulty,soundEnabled,hapticEnabled,offset}= settings;const score = useScore();const isPlaying = useIsPlaying();const gameOver = useGameOver();const gameWon = useGameWon();const storeGridSize = useBlockBlastStore(state => state.gridSize);const gameReportedRef = React.useRef(false);const reportGameEnd = React.useCallback(status =>{if(onEndGame && !gameReportedRef.current){onEndGame({status,score:score.toString()});gameReportedRef.current = true;}},[onEndGame,score]);const startGame = React.useCallback((gridSize,gameDuration)=> useBlockBlastStore.getState().startGame(gridSize,gameDuration),[]);const resetGame = React.useCallback(()=> useBlockBlastStore.getState().resetGame(),[]);const difficultySettings = React.useMemo(()=> getDifficultySettings(difficulty),[difficulty]);const gameService = React.useMemo(()=> new BlockBlastService(),[]);React.useEffect(()=>{const store = useBlockBlastStore.getState();if(!store.isPlaying && store.gridSize !== difficultySettings.gridSize){const newGrid = Array(difficultySettings.gridSize).fill(null).map(()=> Array(difficultySettings.gridSize).fill(null));useBlockBlastStore.setState({gridSize:difficultySettings.gridSize,grid:newGrid});}},[difficultySettings.gridSize]);const gridLayoutDimensions = React.useMemo(()=>{const cellGap = BLOCK_BLAST_GAME_CONFIG.CELL_GAP;const availableHeight = height - offset - 100;const availableWidth = width - 40;const maxCellSizeByWidth =(availableWidth -(storeGridSize - 1)* cellGap)/ storeGridSize;const maxCellSizeByHeight =(availableHeight -(storeGridSize - 1)* cellGap)/ storeGridSize;const cellSize = Math.floor(Math.min(maxCellSizeByWidth,maxCellSizeByHeight,BLOCK_BLAST_GAME_CONFIG.CELL_SIZE));return{cellSize,cellGap};},[storeGridSize,offset]);const{cellSize,cellGap}= gridLayoutDimensions;React.useEffect(()=>{const handleClearLines = linesCleared =>{if(linesCleared > 0){playSound(GAME_SOUNDS.BLOCK_BLAST.CLEAR,soundEnabled);playHaptic(HapticType.SUCCESS,hapticEnabled);}};const handlePlacePiece =()=>{playSound(GAME_SOUNDS.BLOCK_BLAST.PLACE,soundEnabled);playHaptic(HapticType.LIGHT,hapticEnabled);};const handleGameOver =()=>{playSound(GAME_SOUNDS.BLOCK_BLAST.GAME_OVER,soundEnabled);playHaptic(HapticType.ERROR,hapticEnabled);};gameService.setCallbacks(handleClearLines,handlePlacePiece,handleGameOver);},[gameService,soundEnabled,hapticEnabled]);React.useEffect(()=>{if((gameOver || gameWon)&& !gameReportedRef.current){const status = gameWon ? 'win':'lose';reportGameEnd(status);}if(!gameOver && !gameWon){gameReportedRef.current = false;}},[gameOver,gameWon,reportGameEnd]);React.useEffect(()=>{return()=>{gameService.cleanup();useBlockBlastStore.getState().resetGame();};},[gameService]);React.useEffect(()=>{if(!isPlaying)return;const timer = setInterval(()=>{useBlockBlastStore.getState().decrementTime();},1000);return()=> clearInterval(timer);},[isPlaying]);const handleStartGame = React.useCallback(()=>{gameReportedRef.current = false;startGame(difficultySettings.gridSize,difficultySettings.gameDuration);gameService.startGame(difficultySettings.gridSize);playSound(GAME_SOUNDS.BLOCK_BLAST.START,soundEnabled);playHaptic(HapticType.SUCCESS,hapticEnabled);},[startGame,gameService,difficultySettings.gridSize,difficultySettings.gameDuration,soundEnabled,hapticEnabled]);const handleStopGame = React.useCallback(()=>{reportGameEnd('cancel');resetGame();gameService.resetGame();},[resetGame,gameService,reportGameEnd]);const handleResetGame = React.useCallback(()=>{resetGame();gameService.resetGame();},[resetGame,gameService]);const handlePlayAgain = React.useCallback(()=>{handleResetGame();},[handleResetGame]);const gameControlButtonProps = React.useMemo(()=>({isPlaying,gameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,startButtonColor:BLOCK_BLAST_COLORS.UI,stopButtonColor:BLOCK_BLAST_COLORS.UI,startButtonBorderColor:BLOCK_BLAST_COLORS.UI,stopButtonBorderColor:BLOCK_BLAST_COLORS.UI,startButtonText:'START GAME',stopButtonText:'STOP GAME',startButtonSubtext:'Place blocks to clear lines!'}),[isPlaying,gameOver,handleStartGame,handleStopGame]);const gameOverModalProps = React.useMemo(()=>({isVisible:gameOver,score,onPlayAgain:handlePlayAgain,buttonText:'Play Again!',borderColor:'rgba(255,105,180,0.5)',buttonColor:'#ffffff',buttonBorderColor:'#ffffff',buttonTextColor:BLOCK_BLAST_COLORS.UI,title:gameWon ? '🎉 YOU WON!':'💥 GAME OVER',message:gameWon ? 'Time completed! Great job!':'No more moves available!'}),[gameOver,score,handlePlayAgain,gameWon]);return _jsx(GestureHandlerRootView,{style:styles.container,children:_jsxs(GameBackground,{children:[_jsx(View,{style:[styles.scoreboardContainer,{top:offset + 15}],children:_jsx(ScoreBoard,{offset:0})}),_jsx(GameArea,{gridSize:storeGridSize,cellSize:cellSize,cellGap:cellGap,gameService:gameService,offset:offset + 15}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{gameId:GAME_IDS.BLOCK_BLAST,settings:externalSettings || DEFAULT_GAME_SETTINGS,onSettingsChange:onSettingsChange})]})});};const styles = StyleSheet.create({container:{flex:1},scoreboardContainer:{position:'absolute',left:0,right:0,top:0,zIndex:10}});
1
+ "use strict";import t from"react";import{View as e,StyleSheet as o,Dimensions as r}from"react-native";import{GestureHandlerRootView as s}from"react-native-gesture-handler";import{GameControlButton as n,GameOverModal as i,GameSettingsModal as l}from"../../helpers/index.js";import{DEFAULT_GAME_SETTINGS as a,GAME_IDS as c}from"../../services/UtilsService.js";import{playSound as f,GAME_SOUNDS as m}from"../../services/SoundsService.js";import{playHaptic as d,HapticType as p}from"../../services/HapticsService.js";import{useBlockBlastStore as u,useScore as g,useIsPlaying as S,useGameOver as B,useGameWon as b}from"./BlockBlastStore.js";import{BlockBlastService as h}from"./BlockBlastService.js";import{BLOCK_BLAST_COLORS as v,BLOCK_BLAST_GAME_CONFIG as x,getDifficultySettings as j}from"./BlockBlastConstants.js";import{ScoreBoard as C,GameArea as G,GameBackground as y}from"./components/index.js";import{jsx as E,jsxs as A}from"react/jsx-runtime";const{width:T,height:z}=r.get("window");export const BlockBlast=({settings:o=a,onSettingsChange:r,onEndGame:w})=>{const M=t.useMemo(()=>({difficulty:o?.difficulty||"medium",soundEnabled:o?.enableSounds??!0,hapticEnabled:o?.enableHaptics??!0,offset:o?.offset??0}),[o?.difficulty,o?.enableSounds,o?.enableHaptics,o?.offset]),{difficulty:O,soundEnabled:P,hapticEnabled:I,offset:N}=M,R=g(),U=S(),V=B(),H=b(),W=u(t=>t.gridSize),Y=t.useRef(!1),q=t.useCallback(t=>{w&&!Y.current&&(w({status:t,score:R.toString()}),Y.current=!0)},[w,R]),D=t.useCallback((t,e)=>u.getState().startGame(t,e),[]),F=t.useCallback(()=>u.getState().resetGame(),[]),J=t.useMemo(()=>j(O),[O]),K=t.useMemo(()=>new h,[]);t.useEffect(()=>{const t=u.getState();if(!t.isPlaying&&t.gridSize!==J.gridSize){const t=Array(J.gridSize).fill(null).map(()=>Array(J.gridSize).fill(null));u.setState({gridSize:J.gridSize,grid:t})}},[J.gridSize]);const L=t.useMemo(()=>{const t=x.CELL_GAP;return{cellSize:Math.floor(Math.min((T-40-(W-1)*t)/W,(z-N-100-(W-1)*t)/W,x.CELL_SIZE)),cellGap:t}},[W,N]),{cellSize:Q,cellGap:X}=L;t.useEffect(()=>{K.setCallbacks(t=>{t>0&&(f(m.BLOCK_BLAST.CLEAR,P),d(p.SUCCESS,I))},()=>{f(m.BLOCK_BLAST.PLACE,P),d(p.LIGHT,I)},()=>{f(m.BLOCK_BLAST.GAME_OVER,P),d(p.ERROR,I)})},[K,P,I]),t.useEffect(()=>{!V&&!H||Y.current||q(H?"win":"lose"),V||H||(Y.current=!1)},[V,H,q]),t.useEffect(()=>()=>{K.cleanup(),u.getState().resetGame()},[K]),t.useEffect(()=>{if(!U)return;const t=setInterval(()=>{u.getState().decrementTime()},1e3);return()=>clearInterval(t)},[U]);const Z=t.useCallback(()=>{Y.current=!1,D(J.gridSize,J.gameDuration),K.startGame(J.gridSize),f(m.BLOCK_BLAST.START,P),d(p.SUCCESS,I)},[D,K,J.gridSize,J.gameDuration,P,I]),$=t.useCallback(()=>{q("cancel"),F(),K.resetGame()},[F,K,q]),_=t.useCallback(()=>{F(),K.resetGame()},[F,K]),tt=t.useCallback(()=>{_()},[_]),et=t.useMemo(()=>({isPlaying:U,gameOver:V,onStartGame:Z,onStopGame:$,startButtonColor:v.UI,stopButtonColor:v.UI,startButtonBorderColor:v.UI,stopButtonBorderColor:v.UI,startButtonText:"START GAME",stopButtonText:"STOP GAME",startButtonSubtext:"Place blocks to clear lines!"}),[U,V,Z,$]),ot=t.useMemo(()=>({isVisible:V,score:R,onPlayAgain:tt,buttonText:"Play Again!",borderColor:"rgba(255, 105, 180, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:v.UI,title:H?"\ud83c\udf89 YOU WON!":"\ud83d\udca5 GAME OVER",message:H?"Time completed! Great job!":"No more moves available!"}),[V,R,tt,H]);return E(s,{style:k.container,children:A(y,{children:[E(e,{style:[k.scoreboardContainer,{top:N+15}],children:E(C,{offset:0})}),E(G,{gridSize:W,cellSize:Q,cellGap:X,gameService:K,offset:N+15}),E(n,{...et}),E(i,{...ot}),E(l,{gameId:c.BLOCK_BLAST,settings:o||a,onSettingsChange:r})]})})};const k=o.create({container:{flex:1},scoreboardContainer:{position:"absolute",left:0,right:0,top:0,zIndex:10}});
@@ -1 +1 @@
1
- "use strict";export const BLOCK_BLAST_DIFFICULTY_CONFIG ={easy:{gridSize:8,nextPiecesCount:3,scoreMultiplier:1,gameDuration:180},medium:{gridSize:10,nextPiecesCount:3,scoreMultiplier:1.5,gameDuration:300},hard:{gridSize:10,nextPiecesCount:3,scoreMultiplier:2,gameDuration:600}};export const getDifficultySettings = difficulty =>{return BLOCK_BLAST_DIFFICULTY_CONFIG[difficulty];};export const DEFAULT_BLOCK_BLAST_SETTINGS ={difficulty:'medium',soundEnabled:true,hapticEnabled:true};export const BLOCK_BLAST_GAME_CONFIG ={GRID_SIZE:8,CELL_SIZE:40,CELL_GAP:2,NEXT_PIECES_COUNT:3,POINTS_PER_BLOCK:10,POINTS_PER_LINE:100,COMBO_MULTIPLIER:1.5,PREVIEW_PIECE_SCALE:0.7,DRAGGING_PIECE_SCALE:1.0};export const BLOCK_BLAST_COLORS ={BACKGROUND:'#2a2d5a',GRID_BACKGROUND:'#1a1d3a',EMPTY_CELL:'#3a3d6a',GRID_LINE:'#4a4d7a',BLOCK_CYAN:'#00d4ff',BLOCK_BLUE:'#4169ff',BLOCK_RED:'#ff4169',BLOCK_YELLOW:'#ffd700',BLOCK_GREEN:'#00ff88',BLOCK_PURPLE:'#b469ff',BLOCK_ORANGE:'#ff8800',BLOCK_PINK:'#ff69b4',SCORE_TEXT:'#ffffff',COMBO_TEXT:'#ffd700',UI:'#ff69b4',HEART:'#ff69b4'};export const BLOCK_SHAPES = [ [[1]],[[1,1]],[[1],[1]],[[1,1,1]],[[1],[1],[1]],[[1,1],[1,0]],[[1,1],[0,1]],[[1,0],[1,1]],[[0,1],[1,1]],[[1,1,1,1]],[[1],[1],[1],[1]],[[1,1],[1,1]],[[1,1,0],[0,1,1]],[[0,1,1],[1,1,0]],[[1,1,1],[1,0,0]],[[1,1,1],[0,0,1]],[[1,1,1],[0,1,0]] ];export const getRandomBlockShape =()=>{const randomIndex = Math.floor(Math.random()* BLOCK_SHAPES.length);return BLOCK_SHAPES[randomIndex];};export const getRandomBlockColor =()=>{const colors = [BLOCK_BLAST_COLORS.BLOCK_CYAN,BLOCK_BLAST_COLORS.BLOCK_BLUE,BLOCK_BLAST_COLORS.BLOCK_RED,BLOCK_BLAST_COLORS.BLOCK_YELLOW,BLOCK_BLAST_COLORS.BLOCK_GREEN,BLOCK_BLAST_COLORS.BLOCK_PURPLE,BLOCK_BLAST_COLORS.BLOCK_ORANGE,BLOCK_BLAST_COLORS.BLOCK_PINK];return colors[Math.floor(Math.random()* colors.length)];};export const BLOCK_BLAST_THEME ={backgroundColor:'rgba(42,45,90,0.95)',headerBackgroundColor:'#ff69b4',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(255,105,180,0.15)',sectionTitleColor:'#ff69b4',buttonSelectedColor:'#ff69b4',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(255,105,180,0.3)',switchTrackColorTrue:'#ff69b4',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const BLOCK_BLAST_DIFFICULTY_DESCRIPTIONS ={easy:'8x8 grid,relaxed puzzle solving',medium:'10x10 grid,balanced challenge,1.5x score',hard:'10x10 grid,expert strategy,2x score!'};
1
+ "use strict";export const BLOCK_BLAST_DIFFICULTY_CONFIG={easy:{gridSize:8,nextPiecesCount:3,scoreMultiplier:1,gameDuration:180},medium:{gridSize:10,nextPiecesCount:3,scoreMultiplier:1.5,gameDuration:300},hard:{gridSize:10,nextPiecesCount:3,scoreMultiplier:2,gameDuration:600}};export const getDifficultySettings=e=>BLOCK_BLAST_DIFFICULTY_CONFIG[e];export const DEFAULT_BLOCK_BLAST_SETTINGS={difficulty:"medium",soundEnabled:!0,hapticEnabled:!0};export const BLOCK_BLAST_GAME_CONFIG={GRID_SIZE:8,CELL_SIZE:40,CELL_GAP:2,NEXT_PIECES_COUNT:3,POINTS_PER_BLOCK:10,POINTS_PER_LINE:100,COMBO_MULTIPLIER:1.5,PREVIEW_PIECE_SCALE:.7,DRAGGING_PIECE_SCALE:1};export const BLOCK_BLAST_COLORS={BACKGROUND:"#2a2d5a",GRID_BACKGROUND:"#1a1d3a",EMPTY_CELL:"#3a3d6a",GRID_LINE:"#4a4d7a",BLOCK_CYAN:"#00d4ff",BLOCK_BLUE:"#4169ff",BLOCK_RED:"#ff4169",BLOCK_YELLOW:"#ffd700",BLOCK_GREEN:"#00ff88",BLOCK_PURPLE:"#b469ff",BLOCK_ORANGE:"#ff8800",BLOCK_PINK:"#ff69b4",SCORE_TEXT:"#ffffff",COMBO_TEXT:"#ffd700",UI:"#ff69b4",HEART:"#ff69b4"};export const BLOCK_SHAPES=[[[1]],[[1,1]],[[1],[1]],[[1,1,1]],[[1],[1],[1]],[[1,1],[1,0]],[[1,1],[0,1]],[[1,0],[1,1]],[[0,1],[1,1]],[[1,1,1,1]],[[1],[1],[1],[1]],[[1,1],[1,1]],[[1,1,0],[0,1,1]],[[0,1,1],[1,1,0]],[[1,1,1],[1,0,0]],[[1,1,1],[0,0,1]],[[1,1,1],[0,1,0]]];export const getRandomBlockShape=()=>{const e=Math.floor(Math.random()*BLOCK_SHAPES.length);return BLOCK_SHAPES[e]};export const getRandomBlockColor=()=>{const e=["#00d4ff","#4169ff","#ff4169","#ffd700","#00ff88","#b469ff","#ff8800","#ff69b4"];return e[Math.floor(Math.random()*e.length)]};export const BLOCK_BLAST_THEME={backgroundColor:"rgba(42, 45, 90, 0.95)",headerBackgroundColor:"#ff69b4",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(255, 105, 180, 0.15)",sectionTitleColor:"#ff69b4",buttonSelectedColor:"#ff69b4",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(255, 105, 180, 0.3)",switchTrackColorTrue:"#ff69b4",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const BLOCK_BLAST_DIFFICULTY_DESCRIPTIONS={easy:"8x8 grid, relaxed puzzle solving",medium:"10x10 grid, balanced challenge, 1.5x score",hard:"10x10 grid, expert strategy, 2x score!"};
@@ -1 +1 @@
1
- "use strict";import{useBlockBlastStore}from "./BlockBlastStore.js";export class BlockBlastService{setCallbacks(onClearLines,onPlacePiece,onGameOver){this.onClearLines = onClearLines;this.onPlacePiece = onPlacePiece;this.onGameOver = onGameOver;}startGame(gridSize){useBlockBlastStore.getState().startGame(gridSize);}stopGame(){useBlockBlastStore.getState().stopGame();}resetGame(){useBlockBlastStore.getState().resetGame();}placePiece(pieceId,row,col){const store = useBlockBlastStore.getState();const success = store.placePiece(pieceId,row,col);if(success){if(this.onPlacePiece){this.onPlacePiece();}const linesCleared = store.clearLines();if(linesCleared > 0 && this.onClearLines){this.onClearLines(linesCleared);}const freshStore = useBlockBlastStore.getState();freshStore.addNewPiece();const isGameOver = freshStore.checkGameOver();if(isGameOver && this.onGameOver){this.onGameOver();}}return success;}canPlacePiece(pieceId,row,col){const store = useBlockBlastStore.getState();const piece = store.nextPieces.find(p => p.id === pieceId);if(!piece)return false;const{shape}= piece;for(let r = 0;r < shape.length;r++){const shapeRow = shape[r];if(!shapeRow)continue;for(let c = 0;c < shapeRow.length;c++){if(shapeRow[c] === 1){const gridRow = row + r;const gridCol = col + c;if(gridRow < 0 || gridRow >= store.gridSize || gridCol < 0 || gridCol >= store.gridSize || store.grid[gridRow]?.[gridCol] !== null){return false;}}}}return true;}getGridPosition(x,y,gridStartX,gridStartY,cellSize,cellGap){const totalCellSize = cellSize + cellGap;const relativeX = x - gridStartX;const relativeY = y - gridStartY;if(relativeX < 0 || relativeY < 0)return null;const col = Math.floor(relativeX / totalCellSize);const row = Math.floor(relativeY / totalCellSize);return{row,col};}cleanup(){this.onClearLines = undefined;this.onPlacePiece = undefined;this.onGameOver = undefined;}}
1
+ "use strict";import{useBlockBlastStore as t}from"./BlockBlastStore.js";export class BlockBlastService{setCallbacks(t,s,i){this.onClearLines=t,this.onPlacePiece=s,this.onGameOver=i}startGame(s){t.getState().startGame(s)}stopGame(){t.getState().stopGame()}resetGame(){t.getState().resetGame()}placePiece(s,i,e){const o=t.getState(),r=o.placePiece(s,i,e);if(r){this.onPlacePiece&&this.onPlacePiece();const s=o.clearLines();s>0&&this.onClearLines&&this.onClearLines(s);const i=t.getState();i.addNewPiece(),i.checkGameOver()&&this.onGameOver&&this.onGameOver()}return r}canPlacePiece(s,i,e){const o=t.getState(),r=o.nextPieces.find(t=>t.id===s);if(!r)return!1;const{shape:c}=r;for(let t=0;t<c.length;t++){const s=c[t];if(s)for(let r=0;r<s.length;r++)if(1===s[r]){const s=i+t,c=e+r;if(0>s||s>=o.gridSize||0>c||c>=o.gridSize||null!==o.grid[s]?.[c])return!1}}return!0}getGridPosition(t,s,i,e,o,r){const c=o+r,n=t-i,l=s-e;if(0>n||0>l)return null;const a=Math.floor(n/c);return{row:Math.floor(l/c),col:a}}cleanup(){this.onClearLines=void 0,this.onPlacePiece=void 0,this.onGameOver=void 0}}
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{immerMiddleware}from "../../services/UtilsService.js";import{BLOCK_BLAST_GAME_CONFIG,getRandomBlockShape,getRandomBlockColor}from "./BlockBlastConstants.js";const createEmptyGrid = size =>{return Array(size).fill(null).map(()=> Array(size).fill(null));};const generateRandomPiece =()=>{const shape = getRandomBlockShape();const color = getRandomBlockColor();if(!shape || !color){throw new Error('Failed to generate random piece');}return{id:`piece-${Date.now()}-${Math.random()}`,shape:shape.map(row => [...row]),color:color};};export const useBlockBlastStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({score:0,isPlaying:false,gameOver:false,gameWon:false,timeRemaining:0,gameDuration:300,grid:createEmptyGrid(BLOCK_BLAST_GAME_CONFIG.GRID_SIZE),gridSize:BLOCK_BLAST_GAME_CONFIG.GRID_SIZE,nextPieces:[],selectedPieceId:null,dragPosition:null,previewPosition:null,canPlace:false,startGame:(gridSize,gameDuration)=>{const size = gridSize || BLOCK_BLAST_GAME_CONFIG.GRID_SIZE;const duration = gameDuration || 300;set(draft =>{draft.score = 0;draft.isPlaying = true;draft.gameOver = false;draft.gameWon = false;draft.timeRemaining = duration;draft.gameDuration = duration;draft.grid = createEmptyGrid(size);draft.gridSize = size;draft.nextPieces = Array(BLOCK_BLAST_GAME_CONFIG.NEXT_PIECES_COUNT).fill(null).map(()=> generateRandomPiece());draft.selectedPieceId = null;draft.dragPosition = null;draft.previewPosition = null;draft.canPlace = false;});},stopGame:()=>{set(draft =>{draft.isPlaying = false;draft.selectedPieceId = null;draft.dragPosition = null;draft.previewPosition = null;});},resetGame:()=>{set(draft =>{draft.score = 0;draft.isPlaying = false;draft.gameOver = false;draft.gameWon = false;draft.timeRemaining = 0;draft.grid = createEmptyGrid(draft.gridSize);draft.nextPieces = [];draft.selectedPieceId = null;draft.dragPosition = null;draft.previewPosition = null;draft.canPlace = false;});},updateScore:points =>{set(draft =>{draft.score = draft.score + points;});},selectPiece:pieceId =>{set(draft =>{draft.selectedPieceId = pieceId;});},deselectPiece:()=>{set(draft =>{draft.selectedPieceId = null;draft.dragPosition = null;draft.previewPosition = null;draft.canPlace = false;});},updateDragPosition:(x,y)=>{set(draft =>{draft.dragPosition ={x,y};});},updatePreviewPosition:(row,col,canPlace)=>{set(draft =>{draft.previewPosition ={row,col};draft.canPlace = canPlace;});},clearPreview:()=>{set(draft =>{draft.previewPosition = null;draft.canPlace = false;});},placePiece:(pieceId,row,col)=>{const state = get();const piece = state.nextPieces.find(p => p.id === pieceId);if(!piece)return false;const{shape,color}= piece;for(let r = 0;r < shape.length;r++){for(let c = 0;c < shape[r].length;c++){if(shape[r][c] === 1){const gridRow = row + r;const gridCol = col + c;if(gridRow < 0 || gridRow >= state.gridSize || gridCol < 0 || gridCol >= state.gridSize || state.grid[gridRow][gridCol] !== null){return false;}}}}set(draft =>{for(let r = 0;r < shape.length;r++){for(let c = 0;c < shape[r].length;c++){if(shape[r][c] === 1){draft.grid[row + r][col + c] = color;}}}const blockCount = shape.flat().filter(cell => cell === 1).length;draft.score += blockCount * BLOCK_BLAST_GAME_CONFIG.POINTS_PER_BLOCK;draft.nextPieces = draft.nextPieces.filter(p => p.id !== pieceId);draft.selectedPieceId = null;draft.dragPosition = null;draft.previewPosition = null;draft.canPlace = false;});return true;},generateNextPieces:()=>{set(draft =>{draft.nextPieces = Array(BLOCK_BLAST_GAME_CONFIG.NEXT_PIECES_COUNT).fill(null).map(()=> generateRandomPiece());});},addNewPiece:()=>{set(draft =>{draft.nextPieces.push(generateRandomPiece());});},clearLines:()=>{const state = get();let linesCleared = 0;const linesToClear = [];const colsToClear = [];const gridSize = state.gridSize;const grid = state.grid;for(let row = 0;row < gridSize;row++){let isRowFull = true;const rowData = grid[row];if(!rowData)continue;for(let col = 0;col < gridSize;col++){if(rowData[col] === null){isRowFull = false;break;}}if(isRowFull){linesToClear.push(row);linesCleared++;}}for(let col = 0;col < gridSize;col++){let isColFull = true;for(let row = 0;row < gridSize;row++){const rowData = grid[row];if(!rowData || rowData[col] === null){isColFull = false;break;}}if(isColFull){colsToClear.push(col);linesCleared++;}}if(linesCleared === 0){return 0;}set(draft =>{linesToClear.forEach(row =>{for(let col = 0;col < gridSize;col++){draft.grid[row][col] = null;}});colsToClear.forEach(col =>{for(let row = 0;row < gridSize;row++){draft.grid[row][col] = null;}});const totalPoints = linesCleared * BLOCK_BLAST_GAME_CONFIG.POINTS_PER_LINE;draft.score += totalPoints;});return linesCleared;},checkGameOver:()=>{const state = get();for(const piece of state.nextPieces){for(let row = 0;row < state.gridSize;row++){for(let col = 0;col < state.gridSize;col++){let canPlace = true;for(let r = 0;r < piece.shape.length;r++){for(let c = 0;c < piece.shape[r].length;c++){if(piece.shape[r][c] === 1){const gridRow = row + r;const gridCol = col + c;if(gridRow < 0 || gridRow >= state.gridSize || gridCol < 0 || gridCol >= state.gridSize || state.grid[gridRow][gridCol] !== null){canPlace = false;break;}}}if(!canPlace)break;}if(canPlace)return false;}}}set(draft =>{draft.gameOver = true;draft.isPlaying = false;});return true;},decrementTime:()=>{set(draft =>{if(draft.timeRemaining > 0){draft.timeRemaining -= 1;}if(draft.timeRemaining === 0 && draft.isPlaying){draft.gameWon = true;draft.gameOver = true;draft.isPlaying = false;}});},checkWinCondition:()=>{const state = get();if(state.timeRemaining === 0 && state.isPlaying){set(draft =>{draft.gameWon = true;draft.gameOver = true;draft.isPlaying = false;});}}}))));export const useScore =()=> useBlockBlastStore(state => state.score);export const useIsPlaying =()=> useBlockBlastStore(state => state.isPlaying);export const useGameOver =()=> useBlockBlastStore(state => state.gameOver);export const useGameWon =()=> useBlockBlastStore(state => state.gameWon);export const useTimeRemaining =()=> useBlockBlastStore(state => state.timeRemaining);export const useGrid =()=> useBlockBlastStore(state => state.grid);export const useNextPieces =()=> useBlockBlastStore(state => state.nextPieces);export const useSelectedPieceId =()=> useBlockBlastStore(state => state.selectedPieceId);
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{immerMiddleware as l}from"../../services/UtilsService.js";import{BLOCK_BLAST_GAME_CONFIG as o,getRandomBlockShape as r,getRandomBlockColor as n}from"./BlockBlastConstants.js";const s=e=>Array(e).fill(null).map(()=>Array(e).fill(null)),c=()=>{const e=r(),t=n();if(!e||!t)throw Error("Failed to generate random piece");return{id:`piece-${Date.now()}-${Math.random()}`,shape:e.map(e=>[...e]),color:t}};export const useBlockBlastStore=e()(t(l((e,t)=>({score:0,isPlaying:!1,gameOver:!1,gameWon:!1,timeRemaining:0,gameDuration:300,grid:s(o.GRID_SIZE),gridSize:o.GRID_SIZE,nextPieces:[],selectedPieceId:null,dragPosition:null,previewPosition:null,canPlace:!1,startGame:(t,l)=>{const r=t||o.GRID_SIZE,n=l||300;e(e=>{e.score=0,e.isPlaying=!0,e.gameOver=!1,e.gameWon=!1,e.timeRemaining=n,e.gameDuration=n,e.grid=s(r),e.gridSize=r,e.nextPieces=Array(o.NEXT_PIECES_COUNT).fill(null).map(()=>c()),e.selectedPieceId=null,e.dragPosition=null,e.previewPosition=null,e.canPlace=!1})},stopGame:()=>{e(e=>{e.isPlaying=!1,e.selectedPieceId=null,e.dragPosition=null,e.previewPosition=null})},resetGame:()=>{e(e=>{e.score=0,e.isPlaying=!1,e.gameOver=!1,e.gameWon=!1,e.timeRemaining=0,e.grid=s(e.gridSize),e.nextPieces=[],e.selectedPieceId=null,e.dragPosition=null,e.previewPosition=null,e.canPlace=!1})},updateScore:t=>{e(e=>{e.score=e.score+t})},selectPiece:t=>{e(e=>{e.selectedPieceId=t})},deselectPiece:()=>{e(e=>{e.selectedPieceId=null,e.dragPosition=null,e.previewPosition=null,e.canPlace=!1})},updateDragPosition:(t,l)=>{e(e=>{e.dragPosition={x:t,y:l}})},updatePreviewPosition:(t,l,o)=>{e(e=>{e.previewPosition={row:t,col:l},e.canPlace=o})},clearPreview:()=>{e(e=>{e.previewPosition=null,e.canPlace=!1})},placePiece:(l,r,n)=>{const s=t(),c=s.nextPieces.find(e=>e.id===l);if(!c)return!1;const{shape:i,color:u}=c;for(let e=0;e<i.length;e++)for(let t=0;t<i[e].length;t++)if(1===i[e][t]){const l=r+e,o=n+t;if(0>l||l>=s.gridSize||0>o||o>=s.gridSize||null!==s.grid[l][o])return!1}return e(e=>{for(let t=0;t<i.length;t++)for(let l=0;l<i[t].length;l++)1===i[t][l]&&(e.grid[r+t][n+l]=u);const t=i.flat().filter(e=>1===e).length;e.score+=t*o.POINTS_PER_BLOCK,e.nextPieces=e.nextPieces.filter(e=>e.id!==l),e.selectedPieceId=null,e.dragPosition=null,e.previewPosition=null,e.canPlace=!1}),!0},generateNextPieces:()=>{e(e=>{e.nextPieces=Array(o.NEXT_PIECES_COUNT).fill(null).map(()=>c())})},addNewPiece:()=>{e(e=>{e.nextPieces.push(c())})},clearLines:()=>{const l=t();let r=0;const n=[],s=[],c=l.gridSize,i=l.grid;for(let e=0;c>e;e++){let t=!0;const l=i[e];if(l){for(let e=0;c>e;e++)if(null===l[e]){t=!1;break}t&&(n.push(e),r++)}}for(let e=0;c>e;e++){let t=!0;for(let l=0;c>l;l++){const o=i[l];if(!o||null===o[e]){t=!1;break}}t&&(s.push(e),r++)}return 0===r?0:(e(e=>{n.forEach(t=>{for(let l=0;c>l;l++)e.grid[t][l]=null}),s.forEach(t=>{for(let l=0;c>l;l++)e.grid[l][t]=null});const t=r*o.POINTS_PER_LINE;e.score+=t}),r)},checkGameOver:()=>{const l=t();for(const e of l.nextPieces)for(let t=0;t<l.gridSize;t++)for(let o=0;o<l.gridSize;o++){let r=!0;for(let n=0;n<e.shape.length;n++){for(let s=0;s<e.shape[n].length;s++)if(1===e.shape[n][s]){const e=t+n,c=o+s;if(0>e||e>=l.gridSize||0>c||c>=l.gridSize||null!==l.grid[e][c]){r=!1;break}}if(!r)break}if(r)return!1}return e(e=>{e.gameOver=!0,e.isPlaying=!1}),!0},decrementTime:()=>{e(e=>{e.timeRemaining>0&&(e.timeRemaining-=1),0===e.timeRemaining&&e.isPlaying&&(e.gameWon=!0,e.gameOver=!0,e.isPlaying=!1)})},checkWinCondition:()=>{const l=t();0===l.timeRemaining&&l.isPlaying&&e(e=>{e.gameWon=!0,e.gameOver=!0,e.isPlaying=!1})}}))));export const useScore=()=>useBlockBlastStore(e=>e.score);export const useIsPlaying=()=>useBlockBlastStore(e=>e.isPlaying);export const useGameOver=()=>useBlockBlastStore(e=>e.gameOver);export const useGameWon=()=>useBlockBlastStore(e=>e.gameWon);export const useTimeRemaining=()=>useBlockBlastStore(e=>e.timeRemaining);export const useGrid=()=>useBlockBlastStore(e=>e.grid);export const useNextPieces=()=>useBlockBlastStore(e=>e.nextPieces);export const useSelectedPieceId=()=>useBlockBlastStore(e=>e.selectedPieceId);
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{Canvas,RoundedRect,Group}from '@shopify/react-native-skia';import Animated,{useAnimatedStyle,withSpring}from 'react-native-reanimated';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const BlockPieceComponentView =({shape,color,cellSize,cellGap,scale = 1,x = 0,y = 0,isDragging = false})=>{const scaledCellSize = cellSize * scale;const scaledGap = cellGap * scale;const scaledTotalSize = scaledCellSize + scaledGap;const pieceWidth = shape[0] ? shape[0].length * scaledTotalSize + scaledGap:0;const pieceHeight = shape.length * scaledTotalSize + scaledGap;const animatedStyle = useAnimatedStyle(()=>{if(isDragging){return{transform:[{translateX:x},{translateY:y},{scale:withSpring(1.1,{damping:15,stiffness:200})}]};}return{transform:[{translateX:0},{translateY:0},{scale:withSpring(1,{damping:15,stiffness:200})}]};},[isDragging,x,y]);return _jsx(Animated.View,{style:[{width:pieceWidth,height:pieceHeight},animatedStyle],children:_jsx(Canvas,{style:{width:pieceWidth,height:pieceHeight},children:shape.map((row,rowIndex)=> row.map((cell,colIndex)=>{if(cell === 1){const cellX = colIndex * scaledTotalSize + scaledGap;const cellY = rowIndex * scaledTotalSize + scaledGap;return _jsxs(Group,{children:[_jsx(RoundedRect,{x:cellX,y:cellY,width:scaledCellSize,height:scaledCellSize,r:4 * scale,color:color}),_jsx(RoundedRect,{x:cellX + 2 * scale,y:cellY + 2 * scale,width:scaledCellSize - 4 * scale,height:scaledCellSize / 3,r:3 * scale,color:"rgba(255,255,255,0.4)"}),_jsx(RoundedRect,{x:cellX,y:cellY + scaledCellSize * 0.7,width:scaledCellSize,height:scaledCellSize * 0.3,r:4 * scale,color:"rgba(0,0,0,0.2)"})]},`block-${rowIndex}-${colIndex}`);}return null;}))})});};const arePropsEqual =(prevProps,nextProps)=>{return prevProps.color === nextProps.color && prevProps.cellSize === nextProps.cellSize && prevProps.cellGap === nextProps.cellGap && prevProps.scale === nextProps.scale && prevProps.x === nextProps.x && prevProps.y === nextProps.y && prevProps.isDragging === nextProps.isDragging && prevProps.shape.length === nextProps.shape.length && prevProps.shape[0]?.length === nextProps.shape[0]?.length;};export const BlockPieceComponent = React.memo(BlockPieceComponentView,arePropsEqual);BlockPieceComponent.displayName = 'BlockPieceComponent';
1
+ "use strict";import t from"react";import{Canvas as e,RoundedRect as r,Group as i}from"@shopify/react-native-skia";import o,{useAnimatedStyle as s,withSpring as n}from"react-native-reanimated";import{jsx as a,jsxs as c}from"react/jsx-runtime";const l=({shape:t,color:l,cellSize:h,cellGap:m,scale:p=1,x:f=0,y:g=0,isDragging:d=!1})=>{const x=h*p,y=m*p,u=x+y,k=t[0]?t[0].length*u+y:0,w=t.length*u+y,b=s(()=>d?{transform:[{translateX:f},{translateY:g},{scale:n(1.1,{damping:15,stiffness:200})}]}:{transform:[{translateX:0},{translateY:0},{scale:n(1,{damping:15,stiffness:200})}]},[d,f,g]);return a(o.View,{style:[{width:k,height:w},b],children:a(e,{style:{width:k,height:w},children:t.map((t,e)=>t.map((t,o)=>{if(1===t){const t=o*u+y,s=e*u+y;return c(i,{children:[a(r,{x:t,y:s,width:x,height:x,r:4*p,color:l}),a(r,{x:t+2*p,y:s+2*p,width:x-4*p,height:x/3,r:3*p,color:"rgba(255, 255, 255, 0.4)"}),a(r,{x:t,y:s+.7*x,width:x,height:.3*x,r:4*p,color:"rgba(0, 0, 0, 0.2)"})]},`block-${e}-${o}`)}return null}))})})},h=(t,e)=>t.color===e.color&&t.cellSize===e.cellSize&&t.cellGap===e.cellGap&&t.scale===e.scale&&t.x===e.x&&t.y===e.y&&t.isDragging===e.isDragging&&t.shape.length===e.shape.length&&t.shape[0]?.length===e.shape[0]?.length;export const BlockPieceComponent=t.memo(l,h);BlockPieceComponent.displayName="BlockPieceComponent";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import{Gesture,GestureDetector}from 'react-native-gesture-handler';import{runOnJS}from 'react-native-reanimated';import{GridComponent}from "./GridComponent.js";import{BlockPieceComponent}from "./BlockPieceComponent.js";import{useBlockBlastStore}from "../BlockBlastStore.js";import{BLOCK_BLAST_GAME_CONFIG}from "../BlockBlastConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const GameAreaComponent =({gridSize,cellSize,cellGap,gameService,offset})=>{const grid = useBlockBlastStore(state => state.grid);const nextPieces = useBlockBlastStore(state => state.nextPieces);const selectedPieceId = useBlockBlastStore(state => state.selectedPieceId);const previewPosition = useBlockBlastStore(state => state.previewPosition);const canPlace = useBlockBlastStore(state => state.canPlace);const [dragState,setDragState] = React.useState({pieceId:null,x:0,y:0});const dragOffsetRef = React.useRef({x:0,y:0});const layoutDimensions = React.useMemo(()=>{const totalCellSize = cellSize + cellGap;const gridWidth = gridSize * totalCellSize - cellGap;const gridHeight = gridSize * totalCellSize - cellGap;const piecesAreaHeight = 120;const controlButtonHeight = 80;const gapBetweenGridAndPieces = 20;const topPadding = 30;const gridPadding = 10;const totalContentHeight = gridHeight + gapBetweenGridAndPieces + piecesAreaHeight;const availableHeight = height - offset - controlButtonHeight - topPadding;const contentStartY = offset + topPadding +(availableHeight - totalContentHeight)/ 2;const gridStartX =(width - gridWidth)/ 2;const gridStartY = Math.max(contentStartY,offset + topPadding);const piecesAreaY = gridStartY + gridHeight + gapBetweenGridAndPieces;return{totalCellSize,gridWidth,gridHeight,piecesAreaHeight,gridStartX,gridStartY,piecesAreaY,gridPadding};},[gridSize,cellSize,cellGap,offset,width,height]);const{piecesAreaHeight,gridStartX,gridStartY,piecesAreaY,gridPadding}= layoutDimensions;const handlePieceDeselect = React.useCallback(()=>{useBlockBlastStore.getState().deselectPiece();setDragState({pieceId:null,x:0,y:0});},[]);const handleTouchBegin = React.useCallback((touchX,touchY)=>{const store = useBlockBlastStore.getState();if(!store.isPlaying)return;const totalPadding = 60;const availableWidth = width - totalPadding * 2;const spaceBetweenPieces = 30;const totalSpacing = spaceBetweenPieces *(store.nextPieces.length - 1);const startX = totalPadding;for(let i = 0;i < store.nextPieces.length;i++){const piece = store.nextPieces[i];if(!piece || !piece.shape || !piece.shape[0])continue;const previewCellSize = cellSize * BLOCK_BLAST_GAME_CONFIG.PREVIEW_PIECE_SCALE;const previewGap = cellGap * BLOCK_BLAST_GAME_CONFIG.PREVIEW_PIECE_SCALE;const previewPieceWidth = piece.shape[0].length *(previewCellSize + previewGap);const previewPieceHeight = piece.shape.length *(previewCellSize + previewGap);const sectionWidth =(availableWidth - totalSpacing)/ store.nextPieces.length;const pieceCenterX = startX + i *(sectionWidth + spaceBetweenPieces)+ sectionWidth / 2;const pieceLeft = pieceCenterX - previewPieceWidth / 2;const pieceTop = piecesAreaY +(piecesAreaHeight - previewPieceHeight)/ 2;const touchPadding = 10;const pieceRight = pieceLeft + previewPieceWidth;const pieceBottom = pieceTop + previewPieceHeight;if(touchX >= pieceLeft - touchPadding && touchX <= pieceRight + touchPadding && touchY >= pieceTop - touchPadding && touchY <= pieceBottom + touchPadding){store.selectPiece(piece.id);const draggingCellSize = cellSize * BLOCK_BLAST_GAME_CONFIG.DRAGGING_PIECE_SCALE;const draggingGap = cellGap * BLOCK_BLAST_GAME_CONFIG.DRAGGING_PIECE_SCALE;const draggingPieceWidth = piece.shape[0].length *(draggingCellSize + draggingGap);const draggingPieceHeight = piece.shape.length *(draggingCellSize + draggingGap);const previewPieceCenterX = pieceLeft + previewPieceWidth / 2;const previewPieceCenterY = pieceTop + previewPieceHeight / 2;dragOffsetRef.current ={x:touchX - previewPieceCenterX,y:touchY - previewPieceCenterY};setDragState({pieceId:piece.id,x:touchX - dragOffsetRef.current.x - draggingPieceWidth / 2,y:touchY - dragOffsetRef.current.y - draggingPieceHeight / 2});return;}}},[cellSize,cellGap,piecesAreaY,piecesAreaHeight,width]);const handleTouchMove = React.useCallback((touchX,touchY)=>{if(!dragState.pieceId)return;const store = useBlockBlastStore.getState();const piece = store.nextPieces.find(p => p.id === dragState.pieceId);if(!piece || !piece.shape || !piece.shape[0])return;const draggingCellSize = cellSize * BLOCK_BLAST_GAME_CONFIG.DRAGGING_PIECE_SCALE;const draggingGap = cellGap * BLOCK_BLAST_GAME_CONFIG.DRAGGING_PIECE_SCALE;const pieceWidth = piece.shape[0].length *(draggingCellSize + draggingGap);const pieceHeight = piece.shape.length *(draggingCellSize + draggingGap);setDragState({pieceId:dragState.pieceId,x:touchX - dragOffsetRef.current.x - pieceWidth / 2,y:touchY - dragOffsetRef.current.y - pieceHeight / 2});const pieceCenterX = touchX - dragOffsetRef.current.x;const pieceCenterY = touchY - dragOffsetRef.current.y;const gridPos = gameService.getGridPosition(pieceCenterX,pieceCenterY,gridStartX,gridStartY,cellSize,cellGap);if(gridPos){const adjustedRow = gridPos.row - Math.floor(piece.shape.length / 2);const adjustedCol = gridPos.col - Math.floor(piece.shape[0].length / 2);const canPlacePiece = gameService.canPlacePiece(dragState.pieceId,adjustedRow,adjustedCol);store.updatePreviewPosition(adjustedRow,adjustedCol,canPlacePiece);}else{store.clearPreview();}},[dragState.pieceId,cellSize,cellGap,gridStartX,gridStartY,gameService]);const handleTouchEnd = React.useCallback((touchX,touchY)=>{if(!dragState.pieceId)return;const store = useBlockBlastStore.getState();const piece = store.nextPieces.find(p => p.id === dragState.pieceId);if(!piece || !piece.shape || !piece.shape[0])return;const pieceCenterX = touchX - dragOffsetRef.current.x;const pieceCenterY = touchY - dragOffsetRef.current.y;const gridPos = gameService.getGridPosition(pieceCenterX,pieceCenterY,gridStartX,gridStartY,cellSize,cellGap);if(gridPos){const adjustedRow = gridPos.row - Math.floor(piece.shape.length / 2);const adjustedCol = gridPos.col - Math.floor(piece.shape[0].length / 2);gameService.placePiece(dragState.pieceId,adjustedRow,adjustedCol);}handlePieceDeselect();},[dragState.pieceId,cellSize,cellGap,gridStartX,gridStartY,gameService,handlePieceDeselect]);const panGesture = React.useMemo(()=> Gesture.Pan().onBegin(event =>{'worklet';runOnJS(handleTouchBegin)(event.absoluteX,event.absoluteY);}).onUpdate(event =>{'worklet';runOnJS(handleTouchMove)(event.absoluteX,event.absoluteY);}).onEnd(event =>{'worklet';runOnJS(handleTouchEnd)(event.absoluteX,event.absoluteY);}),[handleTouchBegin,handleTouchMove,handleTouchEnd]);return _jsx(GestureDetector,{gesture:panGesture,children:_jsxs(View,{style:styles.container,children:[_jsx(View,{style:[styles.gridContainer,{left:gridStartX - gridPadding,top:gridStartY - gridPadding,padding:gridPadding,paddingTop:gridPadding + 20}],pointerEvents:"box-none",children:_jsx(GridComponent,{grid:grid,gridSize:gridSize,cellSize:cellSize,cellGap:cellGap,previewPiece:previewPosition && selectedPieceId ?(()=>{const piece = nextPieces.find(p => p.id === selectedPieceId);return piece ?{shape:piece.shape,color:piece.color,row:previewPosition.row,col:previewPosition.col,canPlace:canPlace}:null;})():null})}),_jsx(View,{style:[styles.piecesContainer,{top:piecesAreaY,left:0,right:0,height:piecesAreaHeight}],pointerEvents:"box-none",children:nextPieces.map((piece,index)=>{if(!piece || !piece.shape)return null;const isDragging = selectedPieceId === piece.id;const scaledCellSize = cellSize * BLOCK_BLAST_GAME_CONFIG.PREVIEW_PIECE_SCALE;const scaledGap = cellGap * BLOCK_BLAST_GAME_CONFIG.PREVIEW_PIECE_SCALE;const pieceWidth = piece.shape && piece.shape[0] ? piece.shape[0].length *(scaledCellSize + scaledGap):0;const totalPadding = 60;const availableWidth = width - totalPadding * 2;const spaceBetweenPieces = 30;const totalSpacing = spaceBetweenPieces *(nextPieces.length - 1);const startX = totalPadding;const sectionWidth =(availableWidth - totalSpacing)/ nextPieces.length;const pieceX = startX + index *(sectionWidth + spaceBetweenPieces)+ sectionWidth / 2;const pieceHeight = piece.shape && piece.shape.length ? piece.shape.length *(scaledCellSize + scaledGap):0;const pieceTopInContainer =(piecesAreaHeight - pieceHeight)/ 2;return _jsx(View,{style:[styles.pieceWrapper,{left:pieceX - pieceWidth / 2,top:pieceTopInContainer,width:pieceWidth,height:pieceHeight,opacity:isDragging ? 0.3:1}],pointerEvents:"box-none",children:_jsx(BlockPieceComponent,{shape:piece.shape,color:piece.color,cellSize:cellSize,cellGap:cellGap,scale:BLOCK_BLAST_GAME_CONFIG.PREVIEW_PIECE_SCALE})},piece.id);})}),dragState.pieceId &&(()=>{const piece = nextPieces.find(p => p.id === dragState.pieceId);if(!piece)return null;return _jsx(View,{style:[styles.draggedPiece,{left:dragState.x,top:dragState.y}],pointerEvents:"none",children:_jsx(BlockPieceComponent,{shape:piece.shape,color:piece.color,cellSize:cellSize,cellGap:cellGap,scale:BLOCK_BLAST_GAME_CONFIG.DRAGGING_PIECE_SCALE,isDragging:true})});})()]})});};const styles = StyleSheet.create({container:{flex:1},gridContainer:{position:'absolute',borderRadius:10},piecesContainer:{position:'absolute'},pieceWrapper:{position:'absolute'},draggedPiece:{position:'absolute',zIndex:1000}});const areGameAreaPropsEqual =(prevProps,nextProps)=>{return prevProps.gridSize === nextProps.gridSize && prevProps.cellSize === nextProps.cellSize && prevProps.cellGap === nextProps.cellGap && prevProps.offset === nextProps.offset && prevProps.gameService === nextProps.gameService;};export const GameArea = React.memo(GameAreaComponent,areGameAreaPropsEqual);GameArea.displayName = 'GameArea';
1
+ "use strict";import e from"react";import{View as t,StyleSheet as r,Dimensions as i}from"react-native";import{Gesture as o,GestureDetector as n}from"react-native-gesture-handler";import{runOnJS as s}from"react-native-reanimated";import{GridComponent as c}from"./GridComponent.js";import{BlockPieceComponent as l}from"./BlockPieceComponent.js";import{useBlockBlastStore as a}from"../BlockBlastStore.js";import{BLOCK_BLAST_GAME_CONFIG as p}from"../BlockBlastConstants.js";import{jsx as d,jsxs as g}from"react/jsx-runtime";const{width:h,height:u}=i.get("window"),f=({gridSize:r,cellSize:i,cellGap:f,gameService:x,offset:S})=>{const y=a(e=>e.grid),v=a(e=>e.nextPieces),b=a(e=>e.selectedPieceId),z=a(e=>e.previewPosition),G=a(e=>e.canPlace),[j,A]=e.useState({pieceId:null,x:0,y:0}),w=e.useRef({x:0,y:0}),C=e.useMemo(()=>{const e=i+f,t=r*e-f,o=r*e-f,n=(h-t)/2,s=Math.max(S+30+(u-S-80-30-(o+20+120))/2,S+30);return{totalCellSize:e,gridWidth:t,gridHeight:o,piecesAreaHeight:120,gridStartX:n,gridStartY:s,piecesAreaY:s+o+20,gridPadding:10}},[r,i,f,S,h,u]),{piecesAreaHeight:P,gridStartX:B,gridStartY:I,piecesAreaY:M,gridPadding:E}=C,Y=e.useCallback(()=>{a.getState().deselectPiece(),A({pieceId:null,x:0,y:0})},[]),k=e.useCallback((e,t)=>{const r=a.getState();if(!r.isPlaying)return;const o=h-120,n=30*(r.nextPieces.length-1);for(let s=0;s<r.nextPieces.length;s++){const c=r.nextPieces[s];if(!c||!c.shape||!c.shape[0])continue;const l=i*p.PREVIEW_PIECE_SCALE,a=f*p.PREVIEW_PIECE_SCALE,d=c.shape[0].length*(l+a),g=c.shape.length*(l+a),h=(o-n)/r.nextPieces.length,u=60+s*(h+30)+h/2-d/2,m=M+(P-g)/2,x=10;if(!(u-x>e||e>u+d+x||m-x>t||t>m+g+x)){r.selectPiece(c.id);const o=i*p.DRAGGING_PIECE_SCALE,n=f*p.DRAGGING_PIECE_SCALE,s=c.shape[0].length*(o+n),l=c.shape.length*(o+n),a=u+d/2,h=m+g/2;return w.current={x:e-a,y:t-h},void A({pieceId:c.id,x:e-w.current.x-s/2,y:t-w.current.y-l/2})}}},[i,f,M,P,h]),H=e.useCallback((e,t)=>{if(!j.pieceId)return;const r=a.getState(),o=r.nextPieces.find(e=>e.id===j.pieceId);if(!o||!o.shape||!o.shape[0])return;const n=i*p.DRAGGING_PIECE_SCALE,s=f*p.DRAGGING_PIECE_SCALE,c=o.shape[0].length*(n+s),l=o.shape.length*(n+s);A({pieceId:j.pieceId,x:e-w.current.x-c/2,y:t-w.current.y-l/2});const d=e-w.current.x,g=t-w.current.y,h=x.getGridPosition(d,g,B,I,i,f);if(h){const e=h.row-Math.floor(o.shape.length/2),t=h.col-Math.floor(o.shape[0].length/2),i=x.canPlacePiece(j.pieceId,e,t);r.updatePreviewPosition(e,t,i)}else r.clearPreview()},[j.pieceId,i,f,B,I,x]),W=e.useCallback((e,t)=>{if(!j.pieceId)return;const r=a.getState().nextPieces.find(e=>e.id===j.pieceId);if(!r||!r.shape||!r.shape[0])return;const o=e-w.current.x,n=t-w.current.y,s=x.getGridPosition(o,n,B,I,i,f);if(s){const e=s.row-Math.floor(r.shape.length/2),t=s.col-Math.floor(r.shape[0].length/2);x.placePiece(j.pieceId,e,t)}Y()},[j.pieceId,i,f,B,I,x,Y]),X=e.useMemo(()=>o.Pan().onBegin(e=>{s(k)(e.absoluteX,e.absoluteY)}).onUpdate(e=>{s(H)(e.absoluteX,e.absoluteY)}).onEnd(e=>{s(W)(e.absoluteX,e.absoluteY)}),[k,H,W]);return d(n,{gesture:X,children:g(t,{style:m.container,children:[d(t,{style:[m.gridContainer,{left:B-E,top:I-E,padding:E,paddingTop:E+20}],pointerEvents:"box-none",children:d(c,{grid:y,gridSize:r,cellSize:i,cellGap:f,previewPiece:z&&b?(()=>{const e=v.find(e=>e.id===b);return e?{shape:e.shape,color:e.color,row:z.row,col:z.col,canPlace:G}:null})():null})}),d(t,{style:[m.piecesContainer,{top:M,left:0,right:0,height:P}],pointerEvents:"box-none",children:v.map((e,r)=>{if(!e||!e.shape)return null;const o=b===e.id,n=i*p.PREVIEW_PIECE_SCALE,s=f*p.PREVIEW_PIECE_SCALE,c=e.shape&&e.shape[0]?e.shape[0].length*(n+s):0,a=(h-120-30*(v.length-1))/v.length,g=60+r*(a+30)+a/2,u=e.shape&&e.shape.length?e.shape.length*(n+s):0,x=(P-u)/2;return d(t,{style:[m.pieceWrapper,{left:g-c/2,top:x,width:c,height:u,opacity:o?.3:1}],pointerEvents:"box-none",children:d(l,{shape:e.shape,color:e.color,cellSize:i,cellGap:f,scale:p.PREVIEW_PIECE_SCALE})},e.id)})}),j.pieceId&&(()=>{const e=v.find(e=>e.id===j.pieceId);return e?d(t,{style:[m.draggedPiece,{left:j.x,top:j.y}],pointerEvents:"none",children:d(l,{shape:e.shape,color:e.color,cellSize:i,cellGap:f,scale:p.DRAGGING_PIECE_SCALE,isDragging:!0})}):null})()]})})},m=r.create({container:{flex:1},gridContainer:{position:"absolute",borderRadius:10},piecesContainer:{position:"absolute"},pieceWrapper:{position:"absolute"},draggedPiece:{position:"absolute",zIndex:1e3}}),x=(e,t)=>e.gridSize===t.gridSize&&e.cellSize===t.cellSize&&e.cellGap===t.cellGap&&e.offset===t.offset&&e.gameService===t.gameService;export const GameArea=e.memo(f,x);GameArea.displayName="GameArea";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Dimensions}from 'react-native';import{Canvas,LinearGradient,Rect,vec,Circle,RoundedRect}from '@shopify/react-native-skia';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const PUZZLE_COLORS = ['#E91E63','#F44336','#FF5722','#FF9800','#FFC107','#CDDC39','#4CAF50','#009688','#00BCD4','#2196F3','#3F51B5','#9C27B0' ];const GameBackgroundView =({children})=>{const generateFloatingBlocks = React.useMemo(()=>{const elements = [];const tileCount = 25;for(let i = 0;i < tileCount;i++){const baseX = width / tileCount * i;const baseY = height / tileCount * i;const x = baseX + Math.sin(i * 0.5)* 50 + Math.random()* 100;const y = baseY + Math.cos(i * 0.7)* 60 + Math.random()* 120;const tileSize = 20 + Math.random()* 20;const colorIndex = i % PUZZLE_COLORS.length;const opacity = 0.3 + Math.sin(i)* 0.15;const tileX = Math.max(tileSize / 2,Math.min(width - tileSize,x));const tileY = Math.max(tileSize / 2,Math.min(height - tileSize,y));if(i % 4 === 0){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize,height:tileSize,r:tileSize * 0.2,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`square-tile-${i}`));}else if(i % 4 === 1){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize * 0.6,height:tileSize,r:tileSize * 0.15,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`rect-tile-${i}`));}else if(i % 4 === 2){elements.push(_jsx(RoundedRect,{x:tileX,y:tileY,width:tileSize * 1.2,height:tileSize * 0.8,r:tileSize * 0.1,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`wide-tile-${i}`));}else{elements.push(_jsx(Circle,{cx:tileX + tileSize / 2,cy:tileY + tileSize / 2,r:tileSize / 2,color:`${PUZZLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`circle-tile-${i}`));}}return elements;},[width,height]);return _jsxs(View,{style:{flex:1},children:[_jsxs(Canvas,{style:{position:'absolute',top:0,left:0,right:0,bottom:0,width,height},children:[_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width,height),colors:[`${PUZZLE_COLORS[0]}30`,`${PUZZLE_COLORS[3]}25`,`${PUZZLE_COLORS[6]}30`,`${PUZZLE_COLORS[9]}25`,`${PUZZLE_COLORS[11]}30` ]})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width / 2,height / 2),colors:[`${PUZZLE_COLORS[1]}20`,'transparent',`${PUZZLE_COLORS[4]}20`,'transparent',`${PUZZLE_COLORS[7]}20`,'transparent']})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(width,0),end:vec(0,height),colors:[`${PUZZLE_COLORS[2]}15`,'transparent',`${PUZZLE_COLORS[5]}15`,'transparent',`${PUZZLE_COLORS[8]}15`,'transparent']})}),generateFloatingBlocks,_jsx(RoundedRect,{x:width * 0.08,y:height * 0.12,width:50,height:50,r:8,color:`${PUZZLE_COLORS[0]}50`}),_jsx(RoundedRect,{x:width * 0.85,y:height * 0.82,width:45,height:45,r:7,color:`${PUZZLE_COLORS[3]}50`}),_jsx(RoundedRect,{x:width * 0.82,y:height * 0.18,width:40,height:40,r:6,color:`${PUZZLE_COLORS[6]}50`}),_jsx(RoundedRect,{x:width * 0.12,y:height * 0.75,width:55,height:55,r:9,color:`${PUZZLE_COLORS[9]}50`}),_jsx(RoundedRect,{x:width * 0.47,y:height * 0.08,width:35,height:35,r:5,color:`${PUZZLE_COLORS[11]}50`}),_jsx(RoundedRect,{x:width * 0.15,y:height * 0.47,width:42,height:42,r:6,color:`${PUZZLE_COLORS[5]}50`}),_jsx(RoundedRect,{x:width * 0.75,y:height * 0.57,width:38,height:38,r:6,color:`${PUZZLE_COLORS[8]}50`}),_jsx(RoundedRect,{x:width * 0.57,y:height * 0.87,width:32,height:32,r:5,color:`${PUZZLE_COLORS[1]}50`}),_jsx(Circle,{cx:width * 0.25,cy:height * 0.25,r:18,color:`${PUZZLE_COLORS[4]}45`}),_jsx(Circle,{cx:width * 0.65,cy:height * 0.35,r:22,color:`${PUZZLE_COLORS[7]}45`}),_jsx(Circle,{cx:width * 0.35,cy:height * 0.65,r:16,color:`${PUZZLE_COLORS[10]}45`}),Array.from({length:Math.floor(width / 60)},(_,i)=> _jsx(Rect,{x:i * 60,y:0,width:1,height:height,color:"rgba(255,255,255,0.03)"},`grid-v-${i}`)),Array.from({length:Math.floor(height / 60)},(_,i)=> _jsx(Rect,{x:0,y:i * 60,width:width,height:1,color:"rgba(255,255,255,0.03)"},`grid-h-${i}`))]}),children]});};export const GameBackground = React.memo(GameBackgroundView);GameBackground.displayName = 'GameBackground';
1
+ "use strict";import t from"react";import{View as r,Dimensions as h}from"react-native";import{Canvas as o,LinearGradient as i,Rect as e,vec as a,Circle as c,RoundedRect as l}from"@shopify/react-native-skia";import{jsx as n,jsxs as d}from"react/jsx-runtime";const{width:s,height:g}=h.get("window"),x=["#E91E63","#F44336","#FF5722","#FF9800","#FFC107","#CDDC39","#4CAF50","#009688","#00BCD4","#2196F3","#3F51B5","#9C27B0"],y=({children:h})=>{const y=t.useMemo(()=>{const t=[];for(let r=0;25>r;r++){const h=g/25*r,o=s/25*r+50*Math.sin(.5*r)+100*Math.random(),i=h+60*Math.cos(.7*r)+120*Math.random(),e=20+20*Math.random(),a=r%12,d=.3+.15*Math.sin(r),y=Math.max(e/2,Math.min(s-e,o)),w=Math.max(e/2,Math.min(g-e,i));r%4==0?t.push(n(l,{x:y,y:w,width:e,height:e,r:.2*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"square-tile-"+r)):r%4==1?t.push(n(l,{x:y,y:w,width:.6*e,height:e,r:.15*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"rect-tile-"+r)):r%4==2?t.push(n(l,{x:y,y:w,width:1.2*e,height:.8*e,r:.1*e,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"wide-tile-"+r)):t.push(n(c,{cx:y+e/2,cy:w+e/2,r:e/2,color:`${x[a]}${Math.floor(255*d).toString(16).padStart(2,"0")}`},"circle-tile-"+r))}return t},[s,g]);return d(r,{style:{flex:1},children:[d(o,{style:{position:"absolute",top:0,left:0,right:0,bottom:0,width:s,height:g},children:[n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(0,0),end:a(s,g),colors:[x[0]+"30",x[3]+"25",x[6]+"30",x[9]+"25",x[11]+"30"]})}),n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(0,0),end:a(s/2,g/2),colors:[x[1]+"20","transparent",x[4]+"20","transparent",x[7]+"20","transparent"]})}),n(e,{x:0,y:0,width:s,height:g,children:n(i,{start:a(s,0),end:a(0,g),colors:[x[2]+"15","transparent",x[5]+"15","transparent",x[8]+"15","transparent"]})}),y,n(l,{x:.08*s,y:.12*g,width:50,height:50,r:8,color:x[0]+"50"}),n(l,{x:.85*s,y:.82*g,width:45,height:45,r:7,color:x[3]+"50"}),n(l,{x:.82*s,y:.18*g,width:40,height:40,r:6,color:x[6]+"50"}),n(l,{x:.12*s,y:.75*g,width:55,height:55,r:9,color:x[9]+"50"}),n(l,{x:.47*s,y:.08*g,width:35,height:35,r:5,color:x[11]+"50"}),n(l,{x:.15*s,y:.47*g,width:42,height:42,r:6,color:x[5]+"50"}),n(l,{x:.75*s,y:.57*g,width:38,height:38,r:6,color:x[8]+"50"}),n(l,{x:.57*s,y:.87*g,width:32,height:32,r:5,color:x[1]+"50"}),n(c,{cx:.25*s,cy:.25*g,r:18,color:x[4]+"45"}),n(c,{cx:.65*s,cy:.35*g,r:22,color:x[7]+"45"}),n(c,{cx:.35*s,cy:.65*g,r:16,color:x[10]+"45"}),Array.from({length:Math.floor(s/60)},(t,r)=>n(e,{x:60*r,y:0,width:1,height:g,color:"rgba(255, 255, 255, 0.03)"},"grid-v-"+r)),Array.from({length:Math.floor(g/60)},(t,r)=>n(e,{x:0,y:60*r,width:s,height:1,color:"rgba(255, 255, 255, 0.03)"},"grid-h-"+r))]}),h]})};export const GameBackground=t.memo(y);GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{Canvas,RoundedRect,Group}from '@shopify/react-native-skia';import{BLOCK_BLAST_COLORS}from "../BlockBlastConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const GridComponentView =({grid,gridSize,cellSize,cellGap,previewPiece})=>{const totalCellSize = cellSize + cellGap;const canvasSize = gridSize * totalCellSize + cellGap;return _jsxs(Canvas,{style:{width:canvasSize,height:canvasSize},children:[_jsx(RoundedRect,{x:0,y:0,width:canvasSize,height:canvasSize,r:12,color:"rgba(26,29,58,0.8)"}),grid.map((row,rowIndex)=> row.map((cell,colIndex)=>{const x = colIndex * totalCellSize + cellGap;const y = rowIndex * totalCellSize + cellGap;const color = cell || BLOCK_BLAST_COLORS.EMPTY_CELL;return _jsxs(Group,{children:[_jsx(RoundedRect,{x:x,y:y,width:cellSize,height:cellSize,r:4,color:color}),cell && _jsx(RoundedRect,{x:x + 2,y:y + 2,width:cellSize - 4,height:cellSize / 3,r:3,color:"rgba(255,255,255,0.3)"})]},`cell-${rowIndex}-${colIndex}`);})),previewPiece && previewPiece.shape.map((row,rowIndex)=> row.map((cell,colIndex)=>{if(cell === 1){const gridRow = previewPiece.row + rowIndex;const gridCol = previewPiece.col + colIndex;if(gridRow >= 0 && gridRow < gridSize && gridCol >= 0 && gridCol < gridSize){const x = gridCol * totalCellSize + cellGap;const y = gridRow * totalCellSize + cellGap;const previewColor = previewPiece.canPlace ? previewPiece.color:'#ff4444';const opacity = previewPiece.canPlace ? 0.5:0.3;return _jsx(RoundedRect,{x:x,y:y,width:cellSize,height:cellSize,r:4,color:`${previewColor}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`preview-${rowIndex}-${colIndex}`);}}return null;}))]});};const areGridPropsEqual =(prevProps,nextProps)=>{if(prevProps.gridSize !== nextProps.gridSize || prevProps.cellSize !== nextProps.cellSize || prevProps.cellGap !== nextProps.cellGap){return false;}if(prevProps.previewPiece !== nextProps.previewPiece){if(!prevProps.previewPiece || !nextProps.previewPiece)return false;if(prevProps.previewPiece.row !== nextProps.previewPiece.row || prevProps.previewPiece.col !== nextProps.previewPiece.col || prevProps.previewPiece.canPlace !== nextProps.previewPiece.canPlace || prevProps.previewPiece.color !== nextProps.previewPiece.color){return false;}}for(let i = 0;i < prevProps.grid.length;i++){const prevRow = prevProps.grid[i];const nextRow = nextProps.grid[i];if(!prevRow || !nextRow)return false;for(let j = 0;j < prevRow.length;j++){if(prevRow[j] !== nextRow[j]){return false;}}}return true;};export const GridComponent = React.memo(GridComponentView,areGridPropsEqual);GridComponent.displayName = 'GridComponent';
1
+ "use strict";import r from"react";import{Canvas as t,RoundedRect as e,Group as i}from"@shopify/react-native-skia";import{BLOCK_BLAST_COLORS as o}from"../BlockBlastConstants.js";import{jsx as n,jsxs as c}from"react/jsx-runtime";const s=({grid:r,gridSize:s,cellSize:h,cellGap:l,previewPiece:f})=>{const a=h+l,u=s*a+l;return c(t,{style:{width:u,height:u},children:[n(e,{x:0,y:0,width:u,height:u,r:12,color:"rgba(26, 29, 58, 0.8)"}),r.map((r,t)=>r.map((r,s)=>{const f=s*a+l,u=t*a+l,d=r||o.EMPTY_CELL;return c(i,{children:[n(e,{x:f,y:u,width:h,height:h,r:4,color:d}),r&&n(e,{x:f+2,y:u+2,width:h-4,height:h/3,r:3,color:"rgba(255, 255, 255, 0.3)"})]},`cell-${t}-${s}`)})),f&&f.shape.map((r,t)=>r.map((r,i)=>{if(1===r){const r=f.row+t,o=f.col+i;if(r>=0&&s>r&&o>=0&&s>o){const c=o*a+l,s=r*a+l,u=f.canPlace?f.color:"#ff4444",d=f.canPlace?.5:.3;return n(e,{x:c,y:s,width:h,height:h,r:4,color:`${u}${Math.floor(255*d).toString(16).padStart(2,"0")}`},`preview-${t}-${i}`)}}return null}))]})},h=(r,t)=>{if(r.gridSize!==t.gridSize||r.cellSize!==t.cellSize||r.cellGap!==t.cellGap)return!1;if(r.previewPiece!==t.previewPiece){if(!r.previewPiece||!t.previewPiece)return!1;if(r.previewPiece.row!==t.previewPiece.row||r.previewPiece.col!==t.previewPiece.col||r.previewPiece.canPlace!==t.previewPiece.canPlace||r.previewPiece.color!==t.previewPiece.color)return!1}for(let e=0;e<r.grid.length;e++){const i=r.grid[e],o=t.grid[e];if(!i||!o)return!1;for(let r=0;r<i.length;r++)if(i[r]!==o[r])return!1}return!0};export const GridComponent=r.memo(s,h);GridComponent.displayName="GridComponent";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Text,StyleSheet}from 'react-native';import{ScoreBoardContainer}from "../../../helpers/index.js";import{useScore,useTimeRemaining}from "../BlockBlastStore.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const ScoreBoard = React.memo(({offset = 0})=>{const score = useScore();const timeRemaining = useTimeRemaining();const formattedTime = React.useMemo(()=>{const mins = Math.floor(timeRemaining / 60);const secs = timeRemaining % 60;return `${mins}:${secs.toString().padStart(2,'0')}`;},[timeRemaining]);const isLowTime = React.useMemo(()=> timeRemaining < 30,[timeRemaining]);return _jsx(ScoreBoardContainer,{offset:offset,backgroundColor:"rgba(255,105,180,0.4)",borderColor:"rgba(255,105,180,0.4)",children:_jsxs(View,{style:styles.rowContainer,children:[_jsxs(View,{style:styles.scoreSection,children:[_jsx(Text,{style:styles.label,children:"SCORE"}),_jsx(Text,{style:styles.value,children:score})]}),_jsxs(View,{style:styles.timerSection,children:[_jsx(Text,{style:styles.label,children:"TIME"}),_jsx(Text,{style:[styles.value,isLowTime && styles.lowTime],children:formattedTime})]})]})});});ScoreBoard.displayName = 'ScoreBoard';const styles = StyleSheet.create({rowContainer:{flexDirection:'row',justifyContent:'space-between',alignItems:'center',width:'100%'},scoreSection:{flex:1,alignItems:'center'},timerSection:{flex:1,alignItems:'center'},lowTime:{color:'#ff4444'},label:{fontSize:16,fontWeight:'bold',color:'#ffffff',marginBottom:4,letterSpacing:0.5},value:{fontSize:25,fontWeight:'bold',color:'#ffffff'}});
1
+ "use strict";import e from"react";import{View as r,Text as t,StyleSheet as o}from"react-native";import{ScoreBoardContainer as l}from"../../../helpers/index.js";import{useScore as n,useTimeRemaining as i}from"../BlockBlastStore.js";import{jsx as c,jsxs as f}from"react/jsx-runtime";export const ScoreBoard=e.memo(({offset:o=0})=>{const a=n(),m=i(),d=e.useMemo(()=>`${Math.floor(m/60)}:${(""+m%60).padStart(2,"0")}`,[m]),h=e.useMemo(()=>30>m,[m]);return c(l,{offset:o,backgroundColor:"rgba(255, 105, 180, 0.4)",borderColor:"rgba(255, 105, 180, 0.4)",children:f(r,{style:s.rowContainer,children:[f(r,{style:s.scoreSection,children:[c(t,{style:s.label,children:"SCORE"}),c(t,{style:s.value,children:a})]}),f(r,{style:s.timerSection,children:[c(t,{style:s.label,children:"TIME"}),c(t,{style:[s.value,h&&s.lowTime],children:d})]})]})})});ScoreBoard.displayName="ScoreBoard";const s=o.create({rowContainer:{flexDirection:"row",justifyContent:"space-between",alignItems:"center",width:"100%"},scoreSection:{flex:1,alignItems:"center"},timerSection:{flex:1,alignItems:"center"},lowTime:{color:"#ff4444"},label:{fontSize:16,fontWeight:"bold",color:"#ffffff",marginBottom:4,letterSpacing:.5},value:{fontSize:25,fontWeight:"bold",color:"#ffffff"}});
@@ -1 +1 @@
1
- "use strict";export{ScoreBoard}from "./ScoreBoard.js";export{GridComponent}from "./GridComponent.js";export{BlockPieceComponent}from "./BlockPieceComponent.js";export{GameArea}from "./GameArea.js";export{GameBackground}from "./GameBackground.js";
1
+ "use strict";export{ScoreBoard}from"./ScoreBoard.js";export{GridComponent}from"./GridComponent.js";export{BlockPieceComponent}from"./BlockPieceComponent.js";export{GameArea}from"./GameArea.js";export{GameBackground}from"./GameBackground.js";
@@ -1 +1 @@
1
- "use strict";export{BlockBlast}from "./BlockBlast.js";export{BlockBlastService}from "./BlockBlastService.js";export{useBlockBlastStore}from "./BlockBlastStore.js";export * from "./BlockBlastConstants.js";
1
+ "use strict";export{BlockBlast}from"./BlockBlast.js";export{BlockBlastService}from"./BlockBlastService.js";export{useBlockBlastStore}from"./BlockBlastStore.js";export*from"./BlockBlastConstants.js";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{GameControlButton,GameOverModal,GameSettingsModal}from "../../helpers/index.js";import{DEFAULT_GAME_SETTINGS,GAME_IDS}from "../../services/UtilsService.js";import{playSound,GAME_SOUNDS}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";import{useBubbleShooterStore,useScore,useIsPlaying,useGameOver,useGameWon}from "./BubbleShooterStore.js";import{BubbleShooterService}from "./BubbleShooterService.js";import{BUBBLE_SHOOTER_COLORS,getDifficultySettings}from "./BubbleShooterConstants.js";import{ScoreBoard}from "./components/ScoreBoard.js";import{GameArea}from "./components/GameArea.js";import{GameBackground}from "./components/GameBackground.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const BubbleShooterComponent =({settings:externalSettings = DEFAULT_GAME_SETTINGS,onSettingsChange,onEndGame})=>{const settings = React.useMemo(()=>({difficulty:externalSettings?.difficulty || 'medium',soundEnabled:externalSettings?.enableSounds ?? true,hapticEnabled:externalSettings?.enableHaptics ?? true,offset:externalSettings?.offset ?? 0}),[externalSettings?.difficulty,externalSettings?.enableSounds,externalSettings?.enableHaptics,externalSettings?.offset]);const{difficulty,soundEnabled,hapticEnabled,offset}= settings;const score = useScore();const isPlaying = useIsPlaying();const gameOver = useGameOver();const gameWon = useGameWon();const gameReportedRef = React.useRef(false);const reportGameEnd = React.useCallback(status =>{if(onEndGame && !gameReportedRef.current){onEndGame({status,score:score.toString()});gameReportedRef.current = true;}},[onEndGame,score]);const resetGame = React.useCallback(()=> useBubbleShooterStore.getState().resetGame(),[]);const difficultySettings = React.useMemo(()=> getDifficultySettings(difficulty),[difficulty]);const gameService = React.useMemo(()=> new BubbleShooterService(),[]);React.useEffect(()=>{gameService.setDifficulty(difficultySettings.rowCount,difficultySettings.colorCount);},[gameService,difficultySettings.rowCount,difficultySettings.colorCount]);const handleShoot = React.useCallback(()=>{playSound(GAME_SOUNDS.BLOCK_BLAST.PLACE,soundEnabled);playHaptic(HapticType.LIGHT,hapticEnabled);},[soundEnabled,hapticEnabled]);const handleBubbleMatch = React.useCallback(()=>{playSound(GAME_SOUNDS.BLOCK_BLAST.CLEAR,soundEnabled);playHaptic(HapticType.SUCCESS,hapticEnabled);},[soundEnabled,hapticEnabled]);const handleGameWon = React.useCallback(()=>{playSound(GAME_SOUNDS.BLOCK_BLAST.START,soundEnabled);playHaptic(HapticType.SUCCESS,hapticEnabled);},[soundEnabled,hapticEnabled]);React.useEffect(()=>{gameService.setCallbacks(handleShoot,handleBubbleMatch,handleGameWon);},[gameService,handleShoot,handleBubbleMatch,handleGameWon]);React.useEffect(()=>{if((gameOver || gameWon)&& !gameReportedRef.current){const status = gameWon ? 'win':'lose';reportGameEnd(status);}if(!gameOver && !gameWon){gameReportedRef.current = false;}},[gameOver,gameWon,reportGameEnd]);React.useEffect(()=>{return()=>{gameService.cleanup();useBubbleShooterStore.getState().resetGame();};},[gameService]);React.useEffect(()=>{if(!isPlaying)return;const gameLoop = setInterval(()=>{gameService.updateShootingBubble(offset);},1000 / 60);return()=> clearInterval(gameLoop);},[isPlaying,gameService,offset]);const handleStartGame = React.useCallback(()=>{gameReportedRef.current = false;useBubbleShooterStore.getState().startGame(difficultySettings.rowCount,difficultySettings.colorCount);gameService.startGame();playSound(GAME_SOUNDS.BLOCK_BLAST.START,soundEnabled);playHaptic(HapticType.SUCCESS,hapticEnabled);},[gameService,difficultySettings.rowCount,difficultySettings.colorCount,soundEnabled,hapticEnabled]);const handleStopGame = React.useCallback(()=>{reportGameEnd('cancel');resetGame();gameService.resetGame();},[resetGame,gameService,reportGameEnd]);const handleResetGame = React.useCallback(()=>{resetGame();gameService.resetGame();gameService.startGame();},[resetGame,gameService]);const handlePlayAgain = React.useCallback(()=>{handleResetGame();},[handleResetGame]);const gameControlButtonProps = React.useMemo(()=>({isPlaying,gameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,startButtonColor:BUBBLE_SHOOTER_COLORS.UI,stopButtonColor:BUBBLE_SHOOTER_COLORS.UI,startButtonBorderColor:BUBBLE_SHOOTER_COLORS.UI,stopButtonBorderColor:BUBBLE_SHOOTER_COLORS.UI,startButtonText:'START GAME',stopButtonText:'STOP GAME',startButtonSubtext:'Match 3+ bubbles of same color!'}),[isPlaying,gameOver,handleStartGame,handleStopGame]);const gameOverModalProps = React.useMemo(()=>({isVisible:gameOver,score,onPlayAgain:handlePlayAgain,buttonText:'Shoot Again!',primaryColor:'rgba(255,107,157,0.85)',borderColor:'rgba(255,107,157,0.9)',buttonColor:BUBBLE_SHOOTER_COLORS.UI,buttonBorderColor:BUBBLE_SHOOTER_COLORS.UI,buttonTextColor:'#ffffff',title:gameWon ? 'YOU WON!':'GAME OVER',message:gameWon ? 'All bubbles cleared!':'Great shooting!'}),[gameOver,score,handlePlayAgain,gameWon]);return _jsx(View,{style:styles.container,children:_jsx(GestureHandlerRootView,{children:_jsxs(GameBackground,{offset:offset,children:[_jsx(GameArea,{gameService:gameService,offset:offset + 15}),_jsx(ScoreBoard,{offset:offset}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{gameId:GAME_IDS.BUBBLE_SHOOTER,settings:externalSettings || DEFAULT_GAME_SETTINGS,onSettingsChange:onSettingsChange})]})})});};const styles = StyleSheet.create({container:{flex:1}});export const BubbleShooter = React.memo(BubbleShooterComponent);BubbleShooter.displayName = 'BubbleShooter';
1
+ "use strict";import o from"react";import{View as t,StyleSheet as e}from"react-native";import{GestureHandlerRootView as r}from"react-native-gesture-handler";import{GameControlButton as s,GameOverModal as n,GameSettingsModal as i}from"../../helpers/index.js";import{DEFAULT_GAME_SETTINGS as a,GAME_IDS as m}from"../../services/UtilsService.js";import{playSound as l,GAME_SOUNDS as c}from"../../services/SoundsService.js";import{playHaptic as f,HapticType as u}from"../../services/HapticsService.js";import{useBubbleShooterStore as b,useScore as p,useIsPlaying as d,useGameOver as S,useGameWon as g}from"./BubbleShooterStore.js";import{BubbleShooterService as h}from"./BubbleShooterService.js";import{BUBBLE_SHOOTER_COLORS as B,getDifficultySettings as j}from"./BubbleShooterConstants.js";import{ScoreBoard as v}from"./components/ScoreBoard.js";import{GameArea as C}from"./components/GameArea.js";import{GameBackground as x}from"./components/GameBackground.js";import{jsx as E,jsxs as G}from"react/jsx-runtime";const A=({settings:e=a,onSettingsChange:A,onEndGame:y})=>{const O=o.useMemo(()=>({difficulty:e?.difficulty||"medium",soundEnabled:e?.enableSounds??!0,hapticEnabled:e?.enableHaptics??!0,offset:e?.offset??0}),[e?.difficulty,e?.enableSounds,e?.enableHaptics,e?.offset]),{difficulty:M,soundEnabled:I,hapticEnabled:P,offset:w}=O,R=p(),U=d(),V=S(),k=g(),H=o.useRef(!1),N=o.useCallback(o=>{y&&!H.current&&(y({status:o,score:R.toString()}),H.current=!0)},[y,R]),W=o.useCallback(()=>b.getState().resetGame(),[]),Y=o.useMemo(()=>j(M),[M]),q=o.useMemo(()=>new h,[]);o.useEffect(()=>{q.setDifficulty(Y.rowCount,Y.colorCount)},[q,Y.rowCount,Y.colorCount]);const z=o.useCallback(()=>{l(c.BLOCK_BLAST.PLACE,I),f(u.LIGHT,P)},[I,P]),D=o.useCallback(()=>{l(c.BLOCK_BLAST.CLEAR,I),f(u.SUCCESS,P)},[I,P]),F=o.useCallback(()=>{l(c.BLOCK_BLAST.START,I),f(u.SUCCESS,P)},[I,P]);o.useEffect(()=>{q.setCallbacks(z,D,F)},[q,z,D,F]),o.useEffect(()=>{!V&&!k||H.current||N(k?"win":"lose"),V||k||(H.current=!1)},[V,k,N]),o.useEffect(()=>()=>{q.cleanup(),b.getState().resetGame()},[q]),o.useEffect(()=>{if(!U)return;const o=setInterval(()=>{q.updateShootingBubble(w)},1e3/60);return()=>clearInterval(o)},[U,q,w]);const J=o.useCallback(()=>{H.current=!1,b.getState().startGame(Y.rowCount,Y.colorCount),q.startGame(),l(c.BLOCK_BLAST.START,I),f(u.SUCCESS,P)},[q,Y.rowCount,Y.colorCount,I,P]),K=o.useCallback(()=>{N("cancel"),W(),q.resetGame()},[W,q,N]),L=o.useCallback(()=>{W(),q.resetGame(),q.startGame()},[W,q]),Q=o.useCallback(()=>{L()},[L]),X=o.useMemo(()=>({isPlaying:U,gameOver:V,onStartGame:J,onStopGame:K,startButtonColor:B.UI,stopButtonColor:B.UI,startButtonBorderColor:B.UI,stopButtonBorderColor:B.UI,startButtonText:"START GAME",stopButtonText:"STOP GAME",startButtonSubtext:"Match 3+ bubbles of same color!"}),[U,V,J,K]),Z=o.useMemo(()=>({isVisible:V,score:R,onPlayAgain:Q,buttonText:"Shoot Again!",primaryColor:"rgba(255, 107, 157, 0.85)",borderColor:"rgba(255, 107, 157, 0.9)",buttonColor:B.UI,buttonBorderColor:B.UI,buttonTextColor:"#ffffff",title:k?"YOU WON!":"GAME OVER",message:k?"All bubbles cleared!":"Great shooting!"}),[V,R,Q,k]);return E(t,{style:T.container,children:E(r,{children:G(x,{offset:w,children:[E(C,{gameService:q,offset:w+15}),E(v,{offset:w}),E(s,{...X}),E(n,{...Z}),E(i,{gameId:m.BUBBLE_SHOOTER,settings:e||a,onSettingsChange:A})]})})})},T=e.create({container:{flex:1}});export const BubbleShooter=o.memo(A);BubbleShooter.displayName="BubbleShooter";
@@ -1 +1 @@
1
- "use strict";export const BUBBLE_SHOOTER_DIFFICULTY_CONFIG ={easy:{rowCount:6,colorCount:4},medium:{rowCount:8,colorCount:5},hard:{rowCount:10,colorCount:6}};export const getDifficultySettings = difficulty =>{return BUBBLE_SHOOTER_DIFFICULTY_CONFIG[difficulty];};export const DEFAULT_BUBBLE_SHOOTER_SETTINGS ={difficulty:'medium',soundEnabled:true,hapticEnabled:true,rowCount:BUBBLE_SHOOTER_DIFFICULTY_CONFIG.medium.rowCount,colorCount:BUBBLE_SHOOTER_DIFFICULTY_CONFIG.medium.colorCount};export const BUBBLE_SHOOTER_GAME_CONFIG ={BUBBLE_RADIUS:18,BUBBLE_SPACING:2,SHOOTER_SIZE:50,SHOOTER_Y_OFFSET:40,BUBBLE_SPEED:12,POINTS_PER_BUBBLE:10,POINTS_PER_DROP:20,MIN_MATCH_COUNT:3,COLS_PER_ROW:9,ROW_OFFSET:0.5};export const BUBBLE_COLORS = ['#FF3B30','#FF9500','#FFCC00','#34C759','#007AFF','#5856D6','#FF2D55','#00C7BE' ];export const BUBBLE_SHOOTER_COLORS ={BACKGROUND:'#87CEEB',SHOOTER:'#ffffff',SHOOTER_ARROW:'#ff6b9d',SCORE:'#ffffff',UI:'#ff6b9d',CEILING:'#e0e0e0'};export const BUBBLE_SHOOTER_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#ff6b9d',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(255,107,157,0.15)',sectionTitleColor:'#ff6b9d',buttonSelectedColor:'#ff6b9d',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(255,107,157,0.3)',switchTrackColorTrue:'#ff6b9d',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const BUBBLE_SHOOTER_DIFFICULTY_DESCRIPTIONS ={easy:'Fewer rows and colors,easier matches',medium:'Balanced challenge with more bubbles',hard:'Many rows and colors,strategic play!'};
1
+ "use strict";export const BUBBLE_SHOOTER_DIFFICULTY_CONFIG={easy:{rowCount:6,colorCount:4},medium:{rowCount:8,colorCount:5},hard:{rowCount:10,colorCount:6}};export const getDifficultySettings=o=>BUBBLE_SHOOTER_DIFFICULTY_CONFIG[o];export const DEFAULT_BUBBLE_SHOOTER_SETTINGS={difficulty:"medium",soundEnabled:!0,hapticEnabled:!0,rowCount:BUBBLE_SHOOTER_DIFFICULTY_CONFIG.medium.rowCount,colorCount:BUBBLE_SHOOTER_DIFFICULTY_CONFIG.medium.colorCount};export const BUBBLE_SHOOTER_GAME_CONFIG={BUBBLE_RADIUS:18,BUBBLE_SPACING:2,SHOOTER_SIZE:50,SHOOTER_Y_OFFSET:40,BUBBLE_SPEED:12,POINTS_PER_BUBBLE:10,POINTS_PER_DROP:20,MIN_MATCH_COUNT:3,COLS_PER_ROW:9,ROW_OFFSET:.5};export const BUBBLE_COLORS=["#FF3B30","#FF9500","#FFCC00","#34C759","#007AFF","#5856D6","#FF2D55","#00C7BE"];export const BUBBLE_SHOOTER_COLORS={BACKGROUND:"#87CEEB",SHOOTER:"#ffffff",SHOOTER_ARROW:"#ff6b9d",SCORE:"#ffffff",UI:"#ff6b9d",CEILING:"#e0e0e0"};export const BUBBLE_SHOOTER_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#ff6b9d",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(255, 107, 157, 0.15)",sectionTitleColor:"#ff6b9d",buttonSelectedColor:"#ff6b9d",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(255, 107, 157, 0.3)",switchTrackColorTrue:"#ff6b9d",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const BUBBLE_SHOOTER_DIFFICULTY_DESCRIPTIONS={easy:"Fewer rows and colors, easier matches",medium:"Balanced challenge with more bubbles",hard:"Many rows and colors, strategic play!"};
@@ -1 +1 @@
1
- "use strict";import{Dimensions}from 'react-native';import{useBubbleShooterStore}from "./BubbleShooterStore.js";import{BUBBLE_SHOOTER_GAME_CONFIG,BUBBLE_COLORS}from "./BubbleShooterConstants.js";const{width}= Dimensions.get('window');export class BubbleShooterService{rowCount = 8;colorCount = 5;shooterX = width / 2;shooterY = 0;offset = 0;constructor(){}setCallbacks(onShoot,onBubbleMatch,onGameWon){this.onShoot = onShoot;this.onBubbleMatch = onBubbleMatch;this.onGameWon = onGameWon;}setDifficulty(rowCount,colorCount){this.rowCount = rowCount;this.colorCount = colorCount;}setShooterPosition(x,y,offset = 0){this.shooterX = x;this.shooterY = y;this.offset = offset;}startGame(){this.initializeBubbles();}stopGame(){}resetGame(){}cleanup(){}initializeBubbles(){const store = useBubbleShooterStore.getState();const bubbles = [];const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;const spacing = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_SPACING;const diameter =(radius + spacing)* 2;const cols = BUBBLE_SHOOTER_GAME_CONFIG.COLS_PER_ROW;const gridWidth = cols * diameter;const startX =(width - gridWidth)/ 2 + radius;const availableColors = BUBBLE_COLORS.slice(0,this.colorCount);for(let row = 0;row < this.rowCount;row++){for(let col = 0;col < cols;col++){const xOffset = row % 2 === 1 ? diameter / 2:0;const x = startX + col * diameter + xOffset;const y = row * diameter * 0.866 + radius + 120 + this.offset;const color = availableColors[Math.floor(Math.random()* availableColors.length)];bubbles.push({id:`bubble-${row}-${col}`,row,col,color,x,y});}}store.updateBubbles(bubbles);const uniqueColors = [...new Set(bubbles.map(b => b.color))];const randomColor1 = uniqueColors[Math.floor(Math.random()* uniqueColors.length)];const randomColor2 = uniqueColors[Math.floor(Math.random()* uniqueColors.length)];useBubbleShooterStore.setState({currentBubbleColor:randomColor1,nextBubbleColor:randomColor2});}updateShootingBubble(_offset){const store = useBubbleShooterStore.getState();if(!store.shootingBubble)return;const bubble = store.shootingBubble;const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;let newX = bubble.x + bubble.vx;let newY = bubble.y + bubble.vy;let newVx = bubble.vx;const wallPadding = 5;if(newX - radius < wallPadding){newX = radius + wallPadding;newVx = -newVx;}else if(newX + radius > width - wallPadding){newX = width - radius - wallPadding;newVx = -newVx;}if(newY - radius <= 50 + this.offset){this.snapBubbleToGrid(newX,newY,bubble.color);return;}const hitBubble = this.checkBubbleCollision(newX,newY);if(hitBubble){this.snapBubbleToGrid(newX,newY,bubble.color);return;}store.updateShootingBubble({...bubble,x:newX,y:newY,vx:newVx});}checkBubbleCollision(x,y){const bubbles = useBubbleShooterStore.getState().bubbles;const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;const collisionDist = radius * 2;for(const bubble of bubbles){const dx = x - bubble.x;const dy = y - bubble.y;const dist = Math.sqrt(dx * dx + dy * dy);if(dist < collisionDist){return bubble;}}return null;}snapBubbleToGrid(x,y,color){const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;const spacing = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_SPACING;const diameter =(radius + spacing)* 2;const store = useBubbleShooterStore.getState();if(store.bubbles.length === 0){const newBubble ={id:`bubble-${Date.now()}`,row:0,col:0,color,x:x,y:Math.max(y,50 + this.offset + radius)};store.addBubble(newBubble);store.clearShootingBubble();store.generateNextBubble();return;}const currentBubbles = store.bubbles;let collidedBubble = currentBubbles[0];let minDist = Infinity;for(let i = 0;i < currentBubbles.length;i++){const bubble = currentBubbles[i];if(!bubble)continue;const dx = x - bubble.x;const dy = y - bubble.y;const dist = Math.sqrt(dx * dx + dy * dy);if(dist < minDist){minDist = dist;collidedBubble = bubble;}}if(!collidedBubble)return;const dx = x - collidedBubble.x;const dy = y - collidedBubble.y;const distance = Math.sqrt(dx * dx + dy * dy);if(distance < 0.1){const newBubble ={id:`bubble-${Date.now()}`,row:0,col:0,color,x:collidedBubble.x,y:collidedBubble.y + diameter};store.addBubble(newBubble);const updatedBubbles = useBubbleShooterStore.getState().bubbles;const matches = this.findConnectedBubbles(newBubble,updatedBubbles);if(matches.length >= BUBBLE_SHOOTER_GAME_CONFIG.MIN_MATCH_COUNT){const matchIds = matches.map(b => b.id);store.removeBubbles(matchIds);const points = matches.length * BUBBLE_SHOOTER_GAME_CONFIG.POINTS_PER_BUBBLE;store.updateScore(points);this.onBubbleMatch?.();const remainingBubbles = useBubbleShooterStore.getState().bubbles;if(remainingBubbles.length === 0){this.onGameWon?.();store.setGameWon();}}store.clearShootingBubble();store.generateNextBubble();return;}const normalizedDx = dx / distance;const normalizedDy = dy / distance;let snapX = collidedBubble.x + normalizedDx * diameter;let snapY = collidedBubble.y + normalizedDy * diameter;const minY = 50 + this.offset + radius;const maxY = collidedBubble.y + diameter * 2;snapY = Math.max(minY,Math.min(maxY,snapY));const minX = radius + 5;const maxX = width - radius - 5;snapX = Math.max(minX,Math.min(maxX,snapX));const newBubble ={id:`bubble-${Date.now()}`,row:0,col:0,color,x:snapX,y:snapY};store.addBubble(newBubble);const updatedBubbles = useBubbleShooterStore.getState().bubbles;const matches = this.findConnectedBubbles(newBubble,updatedBubbles);if(matches.length >= BUBBLE_SHOOTER_GAME_CONFIG.MIN_MATCH_COUNT){const matchIds = matches.map(b => b.id);store.removeBubbles(matchIds);const points = matches.length * BUBBLE_SHOOTER_GAME_CONFIG.POINTS_PER_BUBBLE;store.updateScore(points);this.onBubbleMatch?.();const remainingBubbles = useBubbleShooterStore.getState().bubbles;if(remainingBubbles.length === 0){this.onGameWon?.();store.setGameWon();}}store.clearShootingBubble();store.generateNextBubble();}findConnectedBubbles(startBubble,allBubbles){const matches = [];const visited = new Set();const queue = [startBubble];const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;const neighborDist = radius * 2.5;while(queue.length > 0){const current = queue.shift();if(visited.has(current.id))continue;visited.add(current.id);if(current.color === startBubble.color){matches.push(current);for(let i = 0;i < allBubbles.length;i++){const bubble = allBubbles[i];if(!bubble)continue;if(!visited.has(bubble.id)){const dx = current.x - bubble.x;const dy = current.y - bubble.y;const dist = Math.sqrt(dx * dx + dy * dy);if(dist < neighborDist && bubble.color === startBubble.color){queue.push(bubble);}}}}}return matches;}shoot(){const store = useBubbleShooterStore.getState();if(!store.isPlaying || store.shootingBubble){return;}const angle = store.shooterAngle;const speed = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_SPEED;const shootingBubble ={id:`shooting-${Date.now()}`,x:this.shooterX,y:this.shooterY,vx:Math.sin(angle)* speed,vy:-Math.cos(angle)* speed,color:store.currentBubbleColor};useBubbleShooterStore.getState().updateShootingBubble(shootingBubble);this.onShoot?.();}}
1
+ "use strict";import{Dimensions as t}from"react-native";import{useBubbleShooterStore as o}from"./BubbleShooterStore.js";import{BUBBLE_SHOOTER_GAME_CONFIG as s,BUBBLE_COLORS as e}from"./BubbleShooterConstants.js";const{width:i}=t.get("window");export class BubbleShooterService{rowCount=8;colorCount=5;shooterX=i/2;shooterY=0;offset=0;constructor(){}setCallbacks(t,o,s){this.onShoot=t,this.onBubbleMatch=o,this.onGameWon=s}setDifficulty(t,o){this.rowCount=t,this.colorCount=o}setShooterPosition(t,o,s=0){this.shooterX=t,this.shooterY=o,this.offset=s}startGame(){this.initializeBubbles()}stopGame(){}resetGame(){}cleanup(){}initializeBubbles(){const t=o.getState(),n=[],r=s.BUBBLE_RADIUS,c=2*(r+s.BUBBLE_SPACING),h=s.COLS_PER_ROW,l=(i-h*c)/2+r,a=e.slice(0,this.colorCount);for(let t=0;t<this.rowCount;t++)for(let o=0;h>o;o++){const s=l+o*c+(t%2==1?c/2:0),e=t*c*.866+r+120+this.offset,i=a[Math.floor(Math.random()*a.length)];n.push({id:`bubble-${t}-${o}`,row:t,col:o,color:i,x:s,y:e})}t.updateBubbles(n);const b=[...new Set(n.map(t=>t.color))],u=b[Math.floor(Math.random()*b.length)],f=b[Math.floor(Math.random()*b.length)];o.setState({currentBubbleColor:u,nextBubbleColor:f})}updateShootingBubble(t){const e=o.getState();if(!e.shootingBubble)return;const n=e.shootingBubble,r=s.BUBBLE_RADIUS;let c=n.x+n.vx,h=n.y+n.vy,l=n.vx;5>c-r?(c=r+5,l=-l):c+r>i-5&&(c=i-r-5,l=-l),h-r>50+this.offset?this.checkBubbleCollision(c,h)?this.snapBubbleToGrid(c,h,n.color):e.updateShootingBubble({...n,x:c,y:h,vx:l}):this.snapBubbleToGrid(c,h,n.color)}checkBubbleCollision(t,e){const i=o.getState().bubbles,n=2*s.BUBBLE_RADIUS;for(const o of i){const s=t-o.x,i=e-o.y;if(n>Math.sqrt(s*s+i*i))return o}return null}snapBubbleToGrid(t,e,n){const r=s.BUBBLE_RADIUS,c=2*(r+s.BUBBLE_SPACING),h=o.getState();if(0===h.bubbles.length){const o={id:"bubble-"+Date.now(),row:0,col:0,color:n,x:t,y:Math.max(e,50+this.offset+r)};return h.addBubble(o),h.clearShootingBubble(),void h.generateNextBubble()}const l=h.bubbles;let a=l[0],b=1/0;for(let o=0;o<l.length;o++){const s=l[o];if(!s)continue;const i=t-s.x,n=e-s.y,r=Math.sqrt(i*i+n*n);b>r&&(b=r,a=s)}if(!a)return;const u=t-a.x,f=e-a.y,M=Math.sqrt(u*u+f*f);if(.1>M){const t={id:"bubble-"+Date.now(),row:0,col:0,color:n,x:a.x,y:a.y+c};h.addBubble(t);const e=o.getState().bubbles,i=this.findConnectedBubbles(t,e);if(i.length>=s.MIN_MATCH_COUNT){const t=i.map(t=>t.id);h.removeBubbles(t);const e=i.length*s.POINTS_PER_BUBBLE;h.updateScore(e),this.onBubbleMatch?.(),0===o.getState().bubbles.length&&(this.onGameWon?.(),h.setGameWon())}return h.clearShootingBubble(),void h.generateNextBubble()}const d=u/M,w=f/M;let x=a.x+d*c,B=a.y+w*c;const m=50+this.offset+r,S=a.y+2*c;B=Math.max(m,Math.min(S,B)),x=Math.max(r+5,Math.min(i-r-5,x));const p={id:"bubble-"+Date.now(),row:0,col:0,color:n,x:x,y:B};h.addBubble(p);const y=o.getState().bubbles,C=this.findConnectedBubbles(p,y);if(C.length>=s.MIN_MATCH_COUNT){const t=C.map(t=>t.id);h.removeBubbles(t);const e=C.length*s.POINTS_PER_BUBBLE;h.updateScore(e),this.onBubbleMatch?.(),0===o.getState().bubbles.length&&(this.onGameWon?.(),h.setGameWon())}h.clearShootingBubble(),h.generateNextBubble()}findConnectedBubbles(t,o){const e=[],i=new Set,n=[t],r=2.5*s.BUBBLE_RADIUS;for(;n.length>0;){const s=n.shift();if(!i.has(s.id)&&(i.add(s.id),s.color===t.color)){e.push(s);for(let e=0;e<o.length;e++){const c=o[e];if(c&&!i.has(c.id)){const o=s.x-c.x,e=s.y-c.y;r>Math.sqrt(o*o+e*e)&&c.color===t.color&&n.push(c)}}}}return e}shoot(){const t=o.getState();if(!t.isPlaying||t.shootingBubble)return;const e=t.shooterAngle,i=s.BUBBLE_SPEED,n={id:"shooting-"+Date.now(),x:this.shooterX,y:this.shooterY,vx:Math.sin(e)*i,vy:-Math.cos(e)*i,color:t.currentBubbleColor};o.getState().updateShootingBubble(n),this.onShoot?.()}}
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{immerMiddleware}from "../../services/UtilsService.js";import{BUBBLE_SHOOTER_GAME_CONFIG}from "./BubbleShooterConstants.js";export const useBubbleShooterStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({score:0,isPlaying:false,gameOver:false,gameWon:false,shooterAngle:0,bubbles:[],shootingBubble:null,nextBubbleColor:'#FF3B30',currentBubbleColor:'#FF3B30',startGame:(_rowCount,_colorCount)=>{set(draft =>{draft.score = 0;draft.isPlaying = true;draft.gameOver = false;draft.gameWon = false;draft.shootingBubble = null;draft.bubbles = [];});},stopGame:()=>{set(draft =>{draft.isPlaying = false;});},resetGame:()=>{set(draft =>{draft.score = 0;draft.isPlaying = false;draft.gameOver = false;draft.gameWon = false;draft.shooterAngle = 0;draft.bubbles = [];draft.shootingBubble = null;});},updateScore:points =>{set(draft =>{draft.score = draft.score + points;});},setShooterAngle:angle =>{set(draft =>{draft.shooterAngle = angle;});},shootBubble:()=>{const state = get();if(state.shootingBubble)return;const angle = state.shooterAngle;const speed = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_SPEED;set(draft =>{draft.shootingBubble ={id:`shooting-${Date.now()}`,x:0,y:0,vx:Math.sin(angle)* speed,vy:-Math.cos(angle)* speed,color:draft.currentBubbleColor};});},updateShootingBubble:bubble =>{set(draft =>{const{width}= require('react-native').Dimensions.get('window');const radius = 18;const minX = radius + 5;const maxX = width - radius - 5;draft.shootingBubble ={...bubble,x:Math.max(minX,Math.min(maxX,bubble.x))};});},clearShootingBubble:()=>{set(draft =>{draft.currentBubbleColor = draft.nextBubbleColor;draft.shootingBubble = null;});},addBubble:bubble =>{set(draft =>{const{width}= require('react-native').Dimensions.get('window');const radius = 18;const minX = radius + 5;const maxX = width - radius - 5;const clampedBubble ={...bubble,x:Math.max(minX,Math.min(maxX,bubble.x))};draft.bubbles.push(clampedBubble);});},removeBubbles:bubbleIds =>{set(draft =>{draft.bubbles = draft.bubbles.filter(b => !bubbleIds.includes(b.id));});},updateBubbles:bubbles =>{set(draft =>{const{width}= require('react-native').Dimensions.get('window');const radius = 18;const minX = radius + 5;const maxX = width - radius - 5;draft.bubbles = bubbles.map(bubble =>({...bubble,x:Math.max(minX,Math.min(maxX,bubble.x))}));});},setGameOver:()=>{set(draft =>{draft.gameOver = true;draft.isPlaying = false;});},setGameWon:()=>{set(draft =>{draft.gameWon = true;draft.gameOver = true;draft.isPlaying = false;});},generateNextBubble:()=>{set(draft =>{const uniqueColors = [...new Set(draft.bubbles.map(b => b.color))];if(uniqueColors.length === 0){const defaultColors = ['#FF3B30','#FF9500','#FFCC00','#34C759','#007AFF'];draft.nextBubbleColor = defaultColors[Math.floor(Math.random()* defaultColors.length)];}else{draft.nextBubbleColor = uniqueColors[Math.floor(Math.random()* uniqueColors.length)];}});}}))));export const useScore =()=> useBubbleShooterStore(state => state.score);export const useIsPlaying =()=> useBubbleShooterStore(state => state.isPlaying);export const useGameOver =()=> useBubbleShooterStore(state => state.gameOver);export const useGameWon =()=> useBubbleShooterStore(state => state.gameWon);export const useShooterAngle =()=> useBubbleShooterStore(state => state.shooterAngle);export const useBubbles =()=> useBubbleShooterStore(state => state.bubbles);export const useShootingBubble =()=> useBubbleShooterStore(state => state.shootingBubble);export const useCurrentBubbleColor =()=> useBubbleShooterStore(state => state.currentBubbleColor);export const useNextBubbleColor =()=> useBubbleShooterStore(state => state.nextBubbleColor);
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{immerMiddleware as o}from"../../services/UtilsService.js";import{BUBBLE_SHOOTER_GAME_CONFIG as r}from"./BubbleShooterConstants.js";export const useBubbleShooterStore=e()(t(o((e,t)=>({score:0,isPlaying:!1,gameOver:!1,gameWon:!1,shooterAngle:0,bubbles:[],shootingBubble:null,nextBubbleColor:"#FF3B30",currentBubbleColor:"#FF3B30",startGame:(t,o)=>{e(e=>{e.score=0,e.isPlaying=!0,e.gameOver=!1,e.gameWon=!1,e.shootingBubble=null,e.bubbles=[]})},stopGame:()=>{e(e=>{e.isPlaying=!1})},resetGame:()=>{e(e=>{e.score=0,e.isPlaying=!1,e.gameOver=!1,e.gameWon=!1,e.shooterAngle=0,e.bubbles=[],e.shootingBubble=null})},updateScore:t=>{e(e=>{e.score=e.score+t})},setShooterAngle:t=>{e(e=>{e.shooterAngle=t})},shootBubble:()=>{const o=t();if(o.shootingBubble)return;const s=o.shooterAngle,u=r.BUBBLE_SPEED;e(e=>{e.shootingBubble={id:"shooting-"+Date.now(),x:0,y:0,vx:Math.sin(s)*u,vy:-Math.cos(s)*u,color:e.currentBubbleColor}})},updateShootingBubble:t=>{e(e=>{const{width:o}=require("react-native").Dimensions.get("window"),r=o-18-5;e.shootingBubble={...t,x:Math.max(23,Math.min(r,t.x))}})},clearShootingBubble:()=>{e(e=>{e.currentBubbleColor=e.nextBubbleColor,e.shootingBubble=null})},addBubble:t=>{e(e=>{const{width:o}=require("react-native").Dimensions.get("window"),r=o-18-5,s={...t,x:Math.max(23,Math.min(r,t.x))};e.bubbles.push(s)})},removeBubbles:t=>{e(e=>{e.bubbles=e.bubbles.filter(e=>!t.includes(e.id))})},updateBubbles:t=>{e(e=>{const{width:o}=require("react-native").Dimensions.get("window"),r=o-18-5;e.bubbles=t.map(e=>({...e,x:Math.max(23,Math.min(r,e.x))}))})},setGameOver:()=>{e(e=>{e.gameOver=!0,e.isPlaying=!1})},setGameWon:()=>{e(e=>{e.gameWon=!0,e.gameOver=!0,e.isPlaying=!1})},generateNextBubble:()=>{e(e=>{const t=[...new Set(e.bubbles.map(e=>e.color))];if(0===t.length){const t=["#FF3B30","#FF9500","#FFCC00","#34C759","#007AFF"];e.nextBubbleColor=t[Math.floor(Math.random()*t.length)]}else e.nextBubbleColor=t[Math.floor(Math.random()*t.length)]})}}))));export const useScore=()=>useBubbleShooterStore(e=>e.score);export const useIsPlaying=()=>useBubbleShooterStore(e=>e.isPlaying);export const useGameOver=()=>useBubbleShooterStore(e=>e.gameOver);export const useGameWon=()=>useBubbleShooterStore(e=>e.gameWon);export const useShooterAngle=()=>useBubbleShooterStore(e=>e.shooterAngle);export const useBubbles=()=>useBubbleShooterStore(e=>e.bubbles);export const useShootingBubble=()=>useBubbleShooterStore(e=>e.shootingBubble);export const useCurrentBubbleColor=()=>useBubbleShooterStore(e=>e.currentBubbleColor);export const useNextBubbleColor=()=>useBubbleShooterStore(e=>e.nextBubbleColor);
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import{Gesture,GestureDetector}from 'react-native-gesture-handler';import{runOnJS,useSharedValue,withRepeat,withSpring,useDerivedValue}from 'react-native-reanimated';import{Canvas,Circle,Group,Line,vec}from '@shopify/react-native-skia';import{useBubbleShooterStore}from "../BubbleShooterStore.js";import{BUBBLE_SHOOTER_GAME_CONFIG,BUBBLE_SHOOTER_COLORS}from "../BubbleShooterConstants.js";import{ParticleBlast}from "../../../helpers/index.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const GameAreaComponent =({gameService,offset})=>{const bubbles = useBubbleShooterStore(state => state.bubbles);const shootingBubble = useBubbleShooterStore(state => state.shootingBubble);const shooterAngle = useBubbleShooterStore(state => state.shooterAngle);const currentBubbleColor = useBubbleShooterStore(state => state.currentBubbleColor);const nextBubbleColor = useBubbleShooterStore(state => state.nextBubbleColor);const isPlaying = useBubbleShooterStore(state => state.isPlaying);const [activeBlasts,setActiveBlasts] = React.useState([]);const bubbleScale = useSharedValue(1);const nextBubbleScale = useSharedValue(1);const shooterX = width / 2;const shooterY = height - offset - BUBBLE_SHOOTER_GAME_CONFIG.SHOOTER_Y_OFFSET;const radius = BUBBLE_SHOOTER_GAME_CONFIG.BUBBLE_RADIUS;React.useEffect(()=>{gameService.setShooterPosition(shooterX,shooterY,offset);},[gameService,shooterX,shooterY,offset]);React.useEffect(()=>{if(isPlaying && !shootingBubble){bubbleScale.value = withRepeat(withSpring(1.1,{damping:3,stiffness:100,mass:0.5}),-1,true);nextBubbleScale.value = withRepeat(withSpring(1.15,{damping:2.5,stiffness:90,mass:0.6}),-1,true);}else{bubbleScale.value = withSpring(1,{damping:10,stiffness:100});nextBubbleScale.value = withSpring(1,{damping:10,stiffness:100});}},[isPlaying,shootingBubble]);const animatedRadius = useDerivedValue(()=> radius * bubbleScale.value);const animatedNextRadius = useDerivedValue(()=> radius * 0.6 * nextBubbleScale.value);const handleTouchMove = React.useCallback((x,y)=>{if(!isPlaying)return;const dx = x - shooterX;const dy = y - shooterY;const angle = Math.atan2(dx,-dy);const clampedAngle = Math.max(-Math.PI * 0.4,Math.min(Math.PI * 0.4,angle));useBubbleShooterStore.getState().setShooterAngle(clampedAngle);},[isPlaying,shooterX,shooterY]);const handleTap = React.useCallback(()=>{if(!isPlaying || shootingBubble){return;}gameService.shoot();},[gameService,isPlaying,shootingBubble]);const createBlastParticles = React.useCallback((x,y,color)=>{const newBlast ={id:`blast-${Date.now()}-${Math.random()}`,x,y,color};setActiveBlasts(prev => [...prev,newBlast]);},[]);const handleBlastComplete = React.useCallback(blastId =>{setActiveBlasts(prev => prev.filter(blast => blast.id !== blastId));},[]);React.useEffect(()=>{const unsubscribe = useBubbleShooterStore.subscribe(state => state.bubbles,(currentBubbles,previousBubbles)=>{if(!isPlaying || previousBubbles.length === 0)return;if(previousBubbles.length > currentBubbles.length){const removedBubbles = previousBubbles.filter(prevBubble => !currentBubbles.some(currBubble => currBubble.id === prevBubble.id));removedBubbles.forEach(bubble =>{createBlastParticles(bubble.x,bubble.y,bubble.color);});}});return()=> unsubscribe();},[createBlastParticles,isPlaying]);const panGesture = React.useMemo(()=> Gesture.Pan().onBegin(event =>{'worklet';runOnJS(handleTouchMove)(event.absoluteX,event.absoluteY);}).onUpdate(event =>{'worklet';runOnJS(handleTouchMove)(event.absoluteX,event.absoluteY);}).onEnd(()=>{'worklet';runOnJS(handleTap)();}),[handleTouchMove,handleTap]);const tapGesture = React.useMemo(()=> Gesture.Tap().onStart(event =>{'worklet';runOnJS(handleTouchMove)(event.absoluteX,event.absoluteY);}).onEnd(()=>{'worklet';runOnJS(handleTap)();}),[handleTouchMove,handleTap]);const composedGesture = Gesture.Race(panGesture,tapGesture);const aimLineLength = 150;const aimEndX = shooterX + Math.sin(shooterAngle)* aimLineLength;const aimEndY = shooterY - Math.cos(shooterAngle)* aimLineLength;return _jsx(GestureDetector,{gesture:composedGesture,children:_jsxs(View,{style:styles.container,children:[_jsxs(Canvas,{style:styles.canvas,children:[bubbles.map(bubble => _jsxs(Group,{children:[_jsx(Circle,{cx:bubble.x,cy:bubble.y,r:radius,color:bubble.color}),_jsx(Circle,{cx:bubble.x - radius * 0.3,cy:bubble.y - radius * 0.3,r:radius * 0.3,color:"rgba(255,255,255,0.4)"})]},bubble.id)),shootingBubble && _jsxs(Group,{children:[_jsx(Circle,{cx:shootingBubble.x,cy:shootingBubble.y,r:radius,color:shootingBubble.color}),_jsx(Circle,{cx:shootingBubble.x - radius * 0.3,cy:shootingBubble.y - radius * 0.3,r:radius * 0.3,color:"rgba(255,255,255,0.4)"})]}),isPlaying && !shootingBubble && _jsxs(Group,{children:[_jsx(Line,{p1:vec(shooterX,shooterY),p2:vec(aimEndX,aimEndY),color:BUBBLE_SHOOTER_COLORS.SHOOTER_ARROW,style:"stroke",strokeWidth:2}),_jsx(Circle,{cx:shooterX,cy:shooterY,r:animatedRadius,color:currentBubbleColor}),_jsx(Circle,{cx:shooterX - radius * 0.3,cy:shooterY - radius * 0.3,r:radius * 0.3,color:"rgba(255,255,255,0.4)"}),_jsx(Circle,{cx:shooterX + 50,cy:shooterY,r:animatedNextRadius,color:nextBubbleColor}),_jsx(Circle,{cx:shooterX + 50 - radius * 0.18,cy:shooterY - radius * 0.18,r:radius * 0.18,color:"rgba(255,255,255,0.4)"})]})]}),activeBlasts.map(blast => _jsx(ParticleBlast,{x:blast.x,y:blast.y,colors:[blast.color],particleCount:12,onComplete:()=> handleBlastComplete(blast.id)},blast.id))]})});};const styles = StyleSheet.create({container:{flex:1},canvas:{flex:1,width,height}});const areGameAreaPropsEqual =(prevProps,nextProps)=>{return prevProps.offset === nextProps.offset && prevProps.gameService === nextProps.gameService;};export const GameArea = React.memo(GameAreaComponent,areGameAreaPropsEqual);GameArea.displayName = 'GameArea';
1
+ "use strict";import r from"react";import{View as t,StyleSheet as e,Dimensions as o}from"react-native";import{Gesture as c,GestureDetector as s}from"react-native-gesture-handler";import{runOnJS as a,useSharedValue as i,withRepeat as n,withSpring as m,useDerivedValue as l}from"react-native-reanimated";import{Canvas as h,Circle as f,Group as p,Line as d,vec as x}from"@shopify/react-native-skia";import{useBubbleShooterStore as y}from"../BubbleShooterStore.js";import{BUBBLE_SHOOTER_GAME_CONFIG as g,BUBBLE_SHOOTER_COLORS as u}from"../BubbleShooterConstants.js";import{ParticleBlast as b}from"../../../helpers/index.js";import{jsx as M,jsxs as j}from"react/jsx-runtime";const{width:v,height:w}=o.get("window"),S=({gameService:e,offset:o})=>{const S=y(r=>r.bubbles),A=y(r=>r.shootingBubble),C=y(r=>r.shooterAngle),G=y(r=>r.currentBubbleColor),B=y(r=>r.nextBubbleColor),$=y(r=>r.isPlaying),[D,W]=r.useState([]),q=i(1),z=i(1),E=v/2,F=w-o-g.SHOOTER_Y_OFFSET,H=g.BUBBLE_RADIUS;r.useEffect(()=>{e.setShooterPosition(E,F,o)},[e,E,F,o]),r.useEffect(()=>{$&&!A?(q.value=n(m(1.1,{damping:3,stiffness:100,mass:.5}),-1,!0),z.value=n(m(1.15,{damping:2.5,stiffness:90,mass:.6}),-1,!0)):(q.value=m(1,{damping:10,stiffness:100}),z.value=m(1,{damping:10,stiffness:100}))},[$,A]);const I=l(()=>H*q.value),J=l(()=>.6*H*z.value),K=r.useCallback((r,t)=>{if(!$)return;const e=Math.max(.4*-Math.PI,Math.min(.4*Math.PI,Math.atan2(r-E,-(t-F))));y.getState().setShooterAngle(e)},[$,E,F]),L=r.useCallback(()=>{$&&!A&&e.shoot()},[e,$,A]),N=r.useCallback((r,t,e)=>{const o={id:`blast-${Date.now()}-${Math.random()}`,x:r,y:t,color:e};W(r=>[...r,o])},[]),O=r.useCallback(r=>{W(t=>t.filter(t=>t.id!==r))},[]);r.useEffect(()=>{const r=y.subscribe(r=>r.bubbles,(r,t)=>{$&&0!==t.length&&t.length>r.length&&t.filter(t=>!r.some(r=>r.id===t.id)).forEach(r=>{N(r.x,r.y,r.color)})});return()=>r()},[N,$]);const P=r.useMemo(()=>c.Pan().onBegin(r=>{a(K)(r.absoluteX,r.absoluteY)}).onUpdate(r=>{a(K)(r.absoluteX,r.absoluteY)}).onEnd(()=>{a(L)()}),[K,L]),Q=r.useMemo(()=>c.Tap().onStart(r=>{a(K)(r.absoluteX,r.absoluteY)}).onEnd(()=>{a(L)()}),[K,L]),R=c.Race(P,Q),T=E+150*Math.sin(C),U=F-150*Math.cos(C);return M(s,{gesture:R,children:j(t,{style:k.container,children:[j(h,{style:k.canvas,children:[S.map(r=>j(p,{children:[M(f,{cx:r.x,cy:r.y,r:H,color:r.color}),M(f,{cx:r.x-.3*H,cy:r.y-.3*H,r:.3*H,color:"rgba(255, 255, 255, 0.4)"})]},r.id)),A&&j(p,{children:[M(f,{cx:A.x,cy:A.y,r:H,color:A.color}),M(f,{cx:A.x-.3*H,cy:A.y-.3*H,r:.3*H,color:"rgba(255, 255, 255, 0.4)"})]}),$&&!A&&j(p,{children:[M(d,{p1:x(E,F),p2:x(T,U),color:u.SHOOTER_ARROW,style:"stroke",strokeWidth:2}),M(f,{cx:E,cy:F,r:I,color:G}),M(f,{cx:E-.3*H,cy:F-.3*H,r:.3*H,color:"rgba(255, 255, 255, 0.4)"}),M(f,{cx:E+50,cy:F,r:J,color:B}),M(f,{cx:E+50-.18*H,cy:F-.18*H,r:.18*H,color:"rgba(255, 255, 255, 0.4)"})]})]}),D.map(r=>M(b,{x:r.x,y:r.y,colors:[r.color],particleCount:12,onComplete:()=>O(r.id)},r.id))]})})},k=e.create({container:{flex:1},canvas:{flex:1,width:v,height:w}}),A=(r,t)=>r.offset===t.offset&&r.gameService===t.gameService;export const GameArea=r.memo(S,A);GameArea.displayName="GameArea";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Dimensions}from 'react-native';import{Canvas,LinearGradient,Rect,vec,Circle}from '@shopify/react-native-skia';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const BUBBLE_COLORS = ['#FF1493','#FF69B4','#FF6347','#FF8C00','#FFD700','#ADFF2F','#00FA9A','#00CED1','#1E90FF','#9370DB','#BA55D3','#FF1493' ];export const GameBackground = React.memo(({children})=>{const generateFloatingBubbles = React.useMemo(()=>{const elements = [];const bubbleCount = 30;for(let i = 0;i < bubbleCount;i++){const baseX = width / bubbleCount * i;const baseY = height / bubbleCount * i;const x = baseX + Math.sin(i * 0.8)* 70 + Math.random()* 120;const y = baseY + Math.cos(i * 0.6)* 80 + Math.random()* 100;const bubbleSize = 15 + Math.random()* 35;const colorIndex = i % BUBBLE_COLORS.length;const opacity = 0.2 + Math.sin(i * 1.2)* 0.15;const bubbleX = Math.max(bubbleSize,Math.min(width - bubbleSize,x));const bubbleY = Math.max(bubbleSize,Math.min(height - bubbleSize,y));elements.push(_jsx(Circle,{cx:bubbleX,cy:bubbleY,r:bubbleSize,color:`${BUBBLE_COLORS[colorIndex]}${Math.floor(opacity * 255).toString(16).padStart(2,'0')}`},`bubble-${i}`));}return elements;},[]);return _jsxs(View,{style:{flex:1},children:[_jsxs(Canvas,{style:{position:'absolute',top:0,left:0,right:0,bottom:0,width,height},children:[_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(width,height),colors:[`${BUBBLE_COLORS[0]}35`,`${BUBBLE_COLORS[4]}30`,`${BUBBLE_COLORS[6]}35`,`${BUBBLE_COLORS[8]}30`,`${BUBBLE_COLORS[10]}35` ]})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(width / 2,height / 2),end:vec(width,height),colors:[`${BUBBLE_COLORS[2]}25`,'transparent',`${BUBBLE_COLORS[5]}25`,'transparent',`${BUBBLE_COLORS[7]}25`,'transparent']})}),_jsx(Rect,{x:0,y:0,width:width,height:height,children:_jsx(LinearGradient,{start:vec(width * 0.3,height * 0.3),end:vec(width * 0.7,height * 0.7),colors:[`${BUBBLE_COLORS[3]}18`,'transparent',`${BUBBLE_COLORS[9]}18`,'transparent',`${BUBBLE_COLORS[1]}18`,'transparent']})}),generateFloatingBubbles,_jsx(Circle,{cx:width * 0.12,cy:height * 0.15,r:45,color:`${BUBBLE_COLORS[0]}40`}),_jsx(Circle,{cx:width * 0.88,cy:height * 0.85,r:38,color:`${BUBBLE_COLORS[4]}40`}),_jsx(Circle,{cx:width * 0.85,cy:height * 0.20,r:32,color:`${BUBBLE_COLORS[6]}40`}),_jsx(Circle,{cx:width * 0.15,cy:height * 0.78,r:50,color:`${BUBBLE_COLORS[8]}40`}),_jsx(Circle,{cx:width * 0.50,cy:height * 0.10,r:35,color:`${BUBBLE_COLORS[10]}40`}),_jsx(Circle,{cx:width * 0.18,cy:height * 0.48,r:40,color:`${BUBBLE_COLORS[2]}40`}),_jsx(Circle,{cx:width * 0.82,cy:height * 0.55,r:42,color:`${BUBBLE_COLORS[7]}40`}),_jsx(Circle,{cx:width * 0.60,cy:height * 0.90,r:30,color:`${BUBBLE_COLORS[1]}40`}),_jsx(Circle,{cx:width * 0.28,cy:height * 0.28,r:25,color:`${BUBBLE_COLORS[5]}38`}),_jsx(Circle,{cx:width * 0.68,cy:height * 0.38,r:28,color:`${BUBBLE_COLORS[9]}38`}),_jsx(Circle,{cx:width * 0.38,cy:height * 0.68,r:22,color:`${BUBBLE_COLORS[3]}38`}),_jsx(Circle,{cx:width * 0.72,cy:height * 0.72,r:26,color:`${BUBBLE_COLORS[11]}38`}),Array.from({length:Math.floor(width / 60)},(_,i)=> _jsx(Rect,{x:i * 60,y:0,width:1,height:height,color:"rgba(255,255,255,0.05)"},`grid-v-${i}`)),Array.from({length:Math.floor(height / 60)},(_,i)=> _jsx(Rect,{x:0,y:i * 60,width:width,height:1,color:"rgba(255,255,255,0.05)"},`grid-h-${i}`))]}),children]});});GameBackground.displayName = 'GameBackground';
1
+ "use strict";import r from"react";import{View as t,Dimensions as c}from"react-native";import{Canvas as o,LinearGradient as a,Rect as e,vec as h,Circle as n}from"@shopify/react-native-skia";import{jsx as i,jsxs as l}from"react/jsx-runtime";const{width:s,height:x}=c.get("window"),y=["#FF1493","#FF69B4","#FF6347","#FF8C00","#FFD700","#ADFF2F","#00FA9A","#00CED1","#1E90FF","#9370DB","#BA55D3","#FF1493"];export const GameBackground=r.memo(({children:c})=>{const d=r.useMemo(()=>{const r=[];for(let t=0;30>t;t++){const c=x/30*t,o=s/30*t+70*Math.sin(.8*t)+120*Math.random(),a=c+80*Math.cos(.6*t)+100*Math.random(),e=15+35*Math.random(),h=t%12,l=.2+.15*Math.sin(1.2*t),d=Math.max(e,Math.min(s-e,o)),F=Math.max(e,Math.min(x-e,a));r.push(i(n,{cx:d,cy:F,r:e,color:`${y[h]}${Math.floor(255*l).toString(16).padStart(2,"0")}`},"bubble-"+t))}return r},[]);return l(t,{style:{flex:1},children:[l(o,{style:{position:"absolute",top:0,left:0,right:0,bottom:0,width:s,height:x},children:[i(e,{x:0,y:0,width:s,height:x,children:i(a,{start:h(0,0),end:h(s,x),colors:[y[0]+"35",y[4]+"30",y[6]+"35",y[8]+"30",y[10]+"35"]})}),i(e,{x:0,y:0,width:s,height:x,children:i(a,{start:h(s/2,x/2),end:h(s,x),colors:[y[2]+"25","transparent",y[5]+"25","transparent",y[7]+"25","transparent"]})}),i(e,{x:0,y:0,width:s,height:x,children:i(a,{start:h(.3*s,.3*x),end:h(.7*s,.7*x),colors:[y[3]+"18","transparent",y[9]+"18","transparent",y[1]+"18","transparent"]})}),d,i(n,{cx:.12*s,cy:.15*x,r:45,color:y[0]+"40"}),i(n,{cx:.88*s,cy:.85*x,r:38,color:y[4]+"40"}),i(n,{cx:.85*s,cy:.2*x,r:32,color:y[6]+"40"}),i(n,{cx:.15*s,cy:.78*x,r:50,color:y[8]+"40"}),i(n,{cx:.5*s,cy:.1*x,r:35,color:y[10]+"40"}),i(n,{cx:.18*s,cy:.48*x,r:40,color:y[2]+"40"}),i(n,{cx:.82*s,cy:.55*x,r:42,color:y[7]+"40"}),i(n,{cx:.6*s,cy:.9*x,r:30,color:y[1]+"40"}),i(n,{cx:.28*s,cy:.28*x,r:25,color:y[5]+"38"}),i(n,{cx:.68*s,cy:.38*x,r:28,color:y[9]+"38"}),i(n,{cx:.38*s,cy:.68*x,r:22,color:y[3]+"38"}),i(n,{cx:.72*s,cy:.72*x,r:26,color:y[11]+"38"}),Array.from({length:Math.floor(s/60)},(r,t)=>i(e,{x:60*t,y:0,width:1,height:x,color:"rgba(255, 255, 255, 0.05)"},"grid-v-"+t)),Array.from({length:Math.floor(x/60)},(r,t)=>i(e,{x:0,y:60*t,width:s,height:1,color:"rgba(255, 255, 255, 0.05)"},"grid-h-"+t))]}),c]})});GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{Text,StyleSheet}from 'react-native';import{ScoreBoardContainer}from "../../../helpers/index.js";import{useScore}from "../BubbleShooterStore.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const ScoreBoard = React.memo(({offset = 0})=>{const score = useScore();return _jsxs(ScoreBoardContainer,{offset:offset,backgroundColor:"rgba(255,107,157,0.4)",borderColor:"rgba(255,107,157,0.4)",children:[_jsx(Text,{style:styles.label,children:"SCORE"}),_jsx(Text,{style:styles.value,children:score})]});});ScoreBoard.displayName = 'ScoreBoard';const styles = StyleSheet.create({label:{fontSize:16,fontWeight:'bold',color:'#ffffff',marginBottom:4,letterSpacing:0.5,textAlign:'center'},value:{fontSize:25,fontWeight:'bold',color:'#ffffff',textAlign:'center'}});
1
+ "use strict";import o from"react";import{Text as r,StyleSheet as e}from"react-native";import{ScoreBoardContainer as t}from"../../../helpers/index.js";import{useScore as f}from"../BubbleShooterStore.js";import{jsx as n,jsxs as i}from"react/jsx-runtime";export const ScoreBoard=o.memo(({offset:o=0})=>{const e=f();return i(t,{offset:o,backgroundColor:"rgba(255, 107, 157, 0.4)",borderColor:"rgba(255, 107, 157, 0.4)",children:[n(r,{style:c.label,children:"SCORE"}),n(r,{style:c.value,children:e})]})});ScoreBoard.displayName="ScoreBoard";const c=e.create({label:{fontSize:16,fontWeight:"bold",color:"#ffffff",marginBottom:4,letterSpacing:.5,textAlign:"center"},value:{fontSize:25,fontWeight:"bold",color:"#ffffff",textAlign:"center"}});
@@ -1 +1 @@
1
- "use strict";export{GameBackground}from "./GameBackground.js";export{ScoreBoard}from "./ScoreBoard.js";export{GameArea}from "./GameArea.js";
1
+ "use strict";export{GameBackground}from"./GameBackground.js";export{ScoreBoard}from"./ScoreBoard.js";export{GameArea}from"./GameArea.js";
@@ -1 +1 @@
1
- "use strict";export{BubbleShooter}from "./BubbleShooter.js";export{BUBBLE_SHOOTER_THEME}from "./BubbleShooterConstants.js";
1
+ "use strict";export{BubbleShooter}from"./BubbleShooter.js";export{BUBBLE_SHOOTER_THEME}from"./BubbleShooterConstants.js";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet}from 'react-native';import{GameControlButton,GameOverModal,GameSettingsModal}from "../../helpers/index.js";import{GAME_IDS,DEFAULT_GAME_SETTINGS}from "../../services/UtilsService.js";import{GameBackground,GameGrid,ScoreBoard}from "./components/index.js";import{useCandyCrushStore,useScore,useIsPlaying,useIsGameOver}from "./CandyCrushStore.js";import{CANDY_CRUSH_COLORS}from "./CandyCrushConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const CandyCrush = React.memo(({settings,onSettingsChange,onEndGame})=>{const score = useScore();const isPlaying = useIsPlaying();const isGameOver = useIsGameOver();const gameReportedRef = React.useRef(false);const reportGameEnd = React.useCallback(status =>{if(onEndGame && !gameReportedRef.current){onEndGame({status,score:score.toString()});gameReportedRef.current = true;}},[onEndGame,score]);const startGame = useCandyCrushStore(state => state.startGame);const stopGame = useCandyCrushStore(state => state.stopGame);const resetGame = useCandyCrushStore(state => state.resetGame);const initializeGame = useCandyCrushStore(state => state.initializeGame);const updateSettings = useCandyCrushStore(state => state.updateSettings);React.useEffect(()=>{const enableSounds = settings?.enableSounds ?? true;const enableHaptics = settings?.enableHaptics ?? true;updateSettings(enableSounds,enableHaptics);},[settings?.enableSounds,settings?.enableHaptics,updateSettings]);React.useEffect(()=>{const difficulty = settings?.difficulty || 'easy';initializeGame(difficulty);},[settings?.difficulty,initializeGame]);React.useEffect(()=>{if(isGameOver && !gameReportedRef.current){reportGameEnd('win');}if(!isGameOver){gameReportedRef.current = false;}},[isGameOver,reportGameEnd]);const handleStartGame = React.useCallback(()=>{gameReportedRef.current = false;startGame();},[startGame]);const handleResetGame = React.useCallback(()=>{resetGame();},[resetGame]);const handleStopGame = React.useCallback(()=>{reportGameEnd('cancel');stopGame();},[stopGame,reportGameEnd]);const gameControlButtonProps = React.useMemo(()=>({isPlaying,gameOver:isGameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,onResetGame:handleResetGame,startButtonText:'Start Crushing!',startButtonSubtext:'Match 3 or more',startButtonColor:CANDY_CRUSH_COLORS.BUTTON_PRIMARY,startButtonBorderColor:CANDY_CRUSH_COLORS.BUTTON_SECONDARY}),[isPlaying,isGameOver,handleStartGame,handleStopGame,handleResetGame]);const gameOverModalProps = React.useMemo(()=>({isVisible:isGameOver,score,onPlayAgain:handleResetGame,buttonText:'Crush Again!',primaryColor:'rgba(255,149,0,0.5)',borderColor:'rgba(255,149,0,0.5)',buttonColor:'#ffffff',buttonBorderColor:'#ffffff',buttonTextColor:'#FF9500',scoreFormatter:s => `${s}`}),[isGameOver,score,handleResetGame]);const gameSettingsModalProps = React.useMemo(()=>({gameId:GAME_IDS.CANDY_CRUSH,settings:settings || DEFAULT_GAME_SETTINGS,onSettingsChange}),[settings,onSettingsChange]);const offset = settings?.offset ?? 0;const dynamicStyles = React.useMemo(()=>({gridContainer:{...styles.gridContainer,paddingTop:offset}}),[offset]);return _jsx(GameBackground,{children:_jsxs(View,{style:styles.container,children:[_jsx(View,{style:dynamicStyles.gridContainer,children:_jsx(GameGrid,{})}),_jsx(ScoreBoard,{offset:offset}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{...gameSettingsModalProps})]})});});CandyCrush.displayName = 'CandyCrush';const styles = StyleSheet.create({container:{flex:1},gridContainer:{flex:1,justifyContent:'center',alignItems:'center'},scoreboardContainer:{position:'absolute',top:0,left:0,right:0,zIndex:10}});
1
+ "use strict";import t from"react";import{View as r,StyleSheet as o}from"react-native";import{GameControlButton as e,GameOverModal as n,GameSettingsModal as s}from"../../helpers/index.js";import{GAME_IDS as a,DEFAULT_GAME_SETTINGS as i}from"../../services/UtilsService.js";import{GameBackground as m,GameGrid as f,ScoreBoard as C}from"./components/index.js";import{useCandyCrushStore as c,useScore as l,useIsPlaying as u,useIsGameOver as d}from"./CandyCrushStore.js";import{CANDY_CRUSH_COLORS as g}from"./CandyCrushConstants.js";import{jsx as p,jsxs as h}from"react/jsx-runtime";export const CandyCrush=t.memo(({settings:o,onSettingsChange:y,onEndGame:b})=>{const j=l(),S=u(),B=d(),v=t.useRef(!1),G=t.useCallback(t=>{b&&!v.current&&(b({status:t,score:j.toString()}),v.current=!0)},[b,j]),T=c(t=>t.startGame),F=c(t=>t.stopGame),I=c(t=>t.resetGame),A=c(t=>t.initializeGame),P=c(t=>t.updateSettings);t.useEffect(()=>{P(o?.enableSounds??!0,o?.enableHaptics??!0)},[o?.enableSounds,o?.enableHaptics,P]),t.useEffect(()=>{A(o?.difficulty||"easy")},[o?.difficulty,A]),t.useEffect(()=>{B&&!v.current&&G("win"),B||(v.current=!1)},[B,G]);const w=t.useCallback(()=>{v.current=!1,T()},[T]),z=t.useCallback(()=>{I()},[I]),E=t.useCallback(()=>{G("cancel"),F()},[F,G]),M=t.useMemo(()=>({isPlaying:S,gameOver:B,onStartGame:w,onStopGame:E,onResetGame:z,startButtonText:"Start Crushing!",startButtonSubtext:"Match 3 or more",startButtonColor:g.BUTTON_PRIMARY,startButtonBorderColor:g.BUTTON_SECONDARY}),[S,B,w,E,z]),O=t.useMemo(()=>({isVisible:B,score:j,onPlayAgain:z,buttonText:"Crush Again!",primaryColor:"rgba(255, 149, 0, 0.5)",borderColor:"rgba(255, 149, 0, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#FF9500",scoreFormatter:t=>""+t}),[B,j,z]),R=t.useMemo(()=>({gameId:a.CANDY_CRUSH,settings:o||i,onSettingsChange:y}),[o,y]),U=o?.offset??0,V=t.useMemo(()=>({gridContainer:{...x.gridContainer,paddingTop:U}}),[U]);return p(m,{children:h(r,{style:x.container,children:[p(r,{style:V.gridContainer,children:p(f,{})}),p(C,{offset:U}),p(e,{...M}),p(n,{...O}),p(s,{...R})]})})});CandyCrush.displayName="CandyCrush";const x=o.create({container:{flex:1},gridContainer:{flex:1,justifyContent:"center",alignItems:"center"},scoreboardContainer:{position:"absolute",top:0,left:0,right:0,zIndex:10}});