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 Animated,{useAnimatedStyle,interpolate,Extrapolate}from 'react-native-reanimated';import{Canvas,Circle,Group}from '@shopify/react-native-skia';import{FLAPPY_BIRD_GAME_CONFIG,FLAPPY_BIRD_COLORS}from "../FlappyBirdConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{BIRD_SIZE}= FLAPPY_BIRD_GAME_CONFIG;const birdContainerStyle ={position:'absolute',width:BIRD_SIZE,height:BIRD_SIZE};const canvasStyle ={width:BIRD_SIZE,height:BIRD_SIZE};export const Bird = React.memo(({birdY,velocity})=>{const{BIRD_START_X}= FLAPPY_BIRD_GAME_CONFIG;const animatedStyle = useAnimatedStyle(()=>{'worklet';const targetRotation = interpolate(velocity,[-15,15],[-30,90],Extrapolate.CLAMP);return{transform:[{translateX:BIRD_START_X},{translateY:birdY},{rotate:`${targetRotation}deg`}]};},[birdY,velocity]);return _jsx(Animated.View,{style:[birdContainerStyle,animatedStyle],children:_jsx(Canvas,{style:canvasStyle,children:_jsxs(Group,{children:[_jsx(Circle,{cx:BIRD_SIZE / 2,cy:BIRD_SIZE / 2,r:BIRD_SIZE / 2,color:FLAPPY_BIRD_COLORS.BIRD_YELLOW}),_jsx(Circle,{cx:BIRD_SIZE / 2,cy:BIRD_SIZE / 2 + 5,r:BIRD_SIZE / 3,color:FLAPPY_BIRD_COLORS.BIRD_ORANGE}),_jsx(Circle,{cx:BIRD_SIZE / 2 + 8,cy:BIRD_SIZE / 2 - 5,r:4,color:"#FFFFFF"}),_jsx(Circle,{cx:BIRD_SIZE / 2 + 8,cy:BIRD_SIZE / 2 - 5,r:2,color:"#000000"}),_jsx(Circle,{cx:BIRD_SIZE / 2 + 15,cy:BIRD_SIZE / 2,r:5,color:"#FF8C00"}),_jsx(Circle,{cx:BIRD_SIZE / 2 - 5,cy:BIRD_SIZE / 2 + 5,r:10,color:FLAPPY_BIRD_COLORS.BIRD_ORANGE,opacity:0.8})]})})});});Bird.displayName = 'Bird';
1
+ "use strict";import r from"react";import t,{useAnimatedStyle as o,interpolate as c,Extrapolate as i}from"react-native-reanimated";import{Canvas as e,Circle as s,Group as a}from"@shopify/react-native-skia";import{FLAPPY_BIRD_GAME_CONFIG as n,FLAPPY_BIRD_COLORS as l}from"../FlappyBirdConstants.js";import{jsx as m,jsxs as d}from"react/jsx-runtime";const{BIRD_SIZE:y}=n,p={position:"absolute",width:y,height:y},h={width:y,height:y};export const Bird=r.memo(({birdY:r,velocity:x})=>{const{BIRD_START_X:F}=n,f=o(()=>{const t=c(x,[-15,15],[-30,90],i.CLAMP);return{transform:[{translateX:F},{translateY:r},{rotate:t+"deg"}]}},[r,x]);return m(t.View,{style:[p,f],children:m(e,{style:h,children:d(a,{children:[m(s,{cx:y/2,cy:y/2,r:y/2,color:l.BIRD_YELLOW}),m(s,{cx:y/2,cy:y/2+5,r:y/3,color:l.BIRD_ORANGE}),m(s,{cx:y/2+8,cy:y/2-5,r:4,color:"#FFFFFF"}),m(s,{cx:y/2+8,cy:y/2-5,r:2,color:"#000000"}),m(s,{cx:y/2+15,cy:y/2,r:5,color:"#FF8C00"}),m(s,{cx:y/2-5,cy:y/2+5,r:10,color:l.BIRD_ORANGE,opacity:.8})]})})})});Bird.displayName="Bird";
@@ -1 +1 @@
1
- "use strict";import React,{useCallback,useEffect,useRef}from 'react';import{View,StyleSheet,TouchableOpacity}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{usePipes,useFlappyBirdStore,useCollisionBlasts}from "../FlappyBirdStore.js";import{Bird}from "./Bird.js";import{Pipes}from "./Pipes.js";import{ParticleBlast}from "../../../helpers/index.js";import{playSound,GAME_SOUNDS}from "../../../services/SoundsService.js";import{playHaptic,HapticType}from "../../../services/HapticsService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const PARTICLE_COLORS = ['#FFD700','#FF8C00','#FF6B6B','#FF4757','#FFA500','#FF6347'];export const GameArea = React.memo(({settings})=>{const birdY = useFlappyBirdStore(state => state.birdY);const birdVelocity = useFlappyBirdStore(state => state.birdVelocity);const isPlaying = useFlappyBirdStore(state => state.isPlaying);const score = useFlappyBirdStore(state => state.score);const pipes = usePipes();const collisionBlasts = useCollisionBlasts();const jump = useFlappyBirdStore(state => state.jump);const removeCollisionBlast = useFlappyBirdStore(state => state.removeCollisionBlast);const prevScoreRef = useRef(0);useEffect(()=>{if(score > prevScoreRef.current && isPlaying){playSound(GAME_SOUNDS.FLAPPY_BIRD.SCORE,settings.enableSounds);playHaptic(HapticType.LIGHT,settings.enableHaptics);}prevScoreRef.current = score;},[score,isPlaying,settings.enableSounds,settings.enableHaptics]);const handleTap = useCallback(()=>{if(!isPlaying)return;jump();playSound(GAME_SOUNDS.FLAPPY_BIRD.FLAP,settings.enableSounds);playHaptic(HapticType.LIGHT,settings.enableHaptics);},[isPlaying,jump,settings.enableSounds,settings.enableHaptics]);const handleBlastComplete = useCallback(id =>{removeCollisionBlast(id);},[removeCollisionBlast]);return _jsx(View,{style:styles.container,children:_jsx(GestureHandlerRootView,{children:_jsx(TouchableOpacity,{style:styles.container,activeOpacity:1,onPress:handleTap,children:_jsxs(View,{style:styles.gameContainer,children:[_jsx(Pipes,{pipes:pipes,offset:settings.offset ?? 0}),_jsx(Bird,{birdY:birdY,velocity:birdVelocity}),collisionBlasts.map(blast => _jsx(ParticleBlast,{x:blast.x,y:blast.y,particleCount:12,colors:PARTICLE_COLORS,duration:800,onComplete:()=> handleBlastComplete(blast.id)},blast.id))]})})})});});const styles = StyleSheet.create({container:{flex:1},gameContainer:{flex:1,position:'relative'}});GameArea.displayName = 'GameArea';
1
+ "use strict";import e,{useCallback as r,useEffect as t,useRef as o}from"react";import{View as i,StyleSheet as s,TouchableOpacity as m}from"react-native";import{GestureHandlerRootView as n}from"react-native-gesture-handler";import{usePipes as a,useFlappyBirdStore as c,useCollisionBlasts as p}from"../FlappyBirdStore.js";import{Bird as l}from"./Bird.js";import{Pipes as f}from"./Pipes.js";import{ParticleBlast as F}from"../../../helpers/index.js";import{playSound as d,GAME_SOUNDS as j}from"../../../services/SoundsService.js";import{playHaptic as v,HapticType as x}from"../../../services/HapticsService.js";import{jsx as u,jsxs as y}from"react/jsx-runtime";const h=["#FFD700","#FF8C00","#FF6B6B","#FF4757","#FFA500","#FF6347"];export const GameArea=e.memo(({settings:e})=>{const s=c(e=>e.birdY),B=c(e=>e.birdVelocity),C=c(e=>e.isPlaying),S=c(e=>e.score),g=a(),G=p(),P=c(e=>e.jump),b=c(e=>e.removeCollisionBlast),D=o(0);t(()=>{S>D.current&&C&&(d(j.FLAPPY_BIRD.SCORE,e.enableSounds),v(x.LIGHT,e.enableHaptics)),D.current=S},[S,C,e.enableSounds,e.enableHaptics]);const H=r(()=>{C&&(P(),d(j.FLAPPY_BIRD.FLAP,e.enableSounds),v(x.LIGHT,e.enableHaptics))},[C,P,e.enableSounds,e.enableHaptics]),O=r(e=>{b(e)},[b]);return u(i,{style:A.container,children:u(n,{children:u(m,{style:A.container,activeOpacity:1,onPress:H,children:y(i,{style:A.gameContainer,children:[u(f,{pipes:g,offset:e.offset??0}),u(l,{birdY:s,velocity:B}),G.map(e=>u(F,{x:e.x,y:e.y,particleCount:12,colors:h,duration:800,onComplete:()=>O(e.id)},e.id))]})})})})});const A=s.create({container:{flex:1},gameContainer:{flex:1,position:"relative"}});GameArea.displayName="GameArea";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Dimensions}from 'react-native';import{Canvas,LinearGradient,Rect,Circle,vec,Group}from '@shopify/react-native-skia';import{FLAPPY_BIRD_COLORS}from "../FlappyBirdConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const cloudPositions = [{x:width * 0.15,y:height * 0.15,scale:1},{x:width * 0.45,y:height * 0.25,scale:0.8},{x:width * 0.75,y:height * 0.18,scale:1.2},{x:width * 0.3,y:height * 0.35,scale:0.9}];export const GameBackground = React.memo(({children,offset = 0})=>{const bottomWallHeight = offset > 0 ? 150:250;const skyEndY = height - bottomWallHeight;return _jsxs(View,{style:{flex:1},children:[_jsxs(Canvas,{style:{position:'absolute',width,height},children:[_jsx(Rect,{x:0,y:0,width:width,height:skyEndY,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(0,skyEndY),colors:[FLAPPY_BIRD_COLORS.SKY_START,FLAPPY_BIRD_COLORS.SKY_END]})}),cloudPositions.map((cloud,index)=> _jsxs(Group,{children:[_jsx(Circle,{cx:cloud.x - 20 * cloud.scale,cy:cloud.y,r:15 * cloud.scale,color:"rgba(255,255,255,0.7)"}),_jsx(Circle,{cx:cloud.x,cy:cloud.y - 5 * cloud.scale,r:20 * cloud.scale,color:"rgba(255,255,255,0.8)"}),_jsx(Circle,{cx:cloud.x + 20 * cloud.scale,cy:cloud.y,r:15 * cloud.scale,color:"rgba(255,255,255,0.7)"})]},`cloud-${index}`))]}),children]});});GameBackground.displayName = 'GameBackground';
1
+ "use strict";import r from"react";import{View as t,Dimensions as o}from"react-native";import{Canvas as c,LinearGradient as e,Rect as s,Circle as a,vec as i,Group as n}from"@shopify/react-native-skia";import{FLAPPY_BIRD_COLORS as l}from"../FlappyBirdConstants.js";import{jsx as d,jsxs as h}from"react/jsx-runtime";const{width:m,height:x}=o.get("window"),y=[{x:.15*m,y:.15*x,scale:1},{x:.45*m,y:.25*x,scale:.8},{x:.75*m,y:.18*x,scale:1.2},{x:.3*m,y:.35*x,scale:.9}];export const GameBackground=r.memo(({children:r,offset:o=0})=>{const p=x-(o>0?150:250);return h(t,{style:{flex:1},children:[h(c,{style:{position:"absolute",width:m,height:x},children:[d(s,{x:0,y:0,width:m,height:p,children:d(e,{start:i(0,0),end:i(0,p),colors:[l.SKY_START,l.SKY_END]})}),y.map((r,t)=>h(n,{children:[d(a,{cx:r.x-20*r.scale,cy:r.y,r:15*r.scale,color:"rgba(255, 255, 255, 0.7)"}),d(a,{cx:r.x,cy:r.y-5*r.scale,r:20*r.scale,color:"rgba(255, 255, 255, 0.8)"}),d(a,{cx:r.x+20*r.scale,cy:r.y,r:15*r.scale,color:"rgba(255, 255, 255, 0.7)"})]},"cloud-"+t))]}),r]})});GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,StyleSheet,Dimensions}from 'react-native';import{Canvas,Rect,RoundedRect,LinearGradient,vec,Shadow}from '@shopify/react-native-skia';import{FLAPPY_BIRD_GAME_CONFIG,FLAPPY_BIRD_COLORS}from "../FlappyBirdConstants.js";import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from "react/jsx-runtime";const{width}= Dimensions.get('window');const brickWallHeight = 150;const brickWidth = 40;const brickHeight = 20;const actualScreenHeight = FLAPPY_BIRD_GAME_CONFIG.GAME_HEIGHT;export const Pipes = React.memo(({pipes,offset = 0})=>{const{PIPE_WIDTH}= FLAPPY_BIRD_GAME_CONFIG;const adjustedTopWallHeight = brickWallHeight + offset;const bottomWallHeight = offset > 0 ? 150:250;return _jsx(View,{style:StyleSheet.absoluteFill,pointerEvents:"none",children:_jsxs(Canvas,{style:{flex:1},children:[_jsx(Rect,{x:0,y:0,width:width,height:adjustedTopWallHeight,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(0,adjustedTopWallHeight),colors:['#8B4513','#A0522D','#8B4513']})}),Array.from({length:Math.ceil(width / brickWidth)+ 1}).map((_,col)=> Array.from({length:Math.ceil(adjustedTopWallHeight / brickHeight)}).map((_,row)=>{const xOffset = row % 2 === 0 ? 0:brickWidth / 2;return _jsxs(React.Fragment,{children:[_jsx(Rect,{x:col * brickWidth - xOffset,y:row * brickHeight,width:brickWidth - 2,height:2,color:"rgba(0,0,0,0.3)"}),_jsx(Rect,{x:col * brickWidth - xOffset,y:row * brickHeight,width:2,height:brickHeight - 2,color:"rgba(0,0,0,0.3)"})]},`top-brick-${col}-${row}`);})),_jsx(Rect,{x:0,y:actualScreenHeight - bottomWallHeight,width:width,height:bottomWallHeight,children:_jsx(LinearGradient,{start:vec(0,actualScreenHeight - bottomWallHeight),end:vec(0,actualScreenHeight),colors:['#8B4513','#A0522D','#8B4513']})}),Array.from({length:Math.ceil(width / brickWidth)+ 1}).map((_,col)=> Array.from({length:Math.ceil(bottomWallHeight / brickHeight)}).map((_,row)=>{const xOffset = row % 2 === 0 ? 0:brickWidth / 2;return _jsxs(React.Fragment,{children:[_jsx(Rect,{x:col * brickWidth - xOffset,y:actualScreenHeight - bottomWallHeight + row * brickHeight,width:brickWidth - 2,height:2,color:"rgba(0,0,0,0.3)"}),_jsx(Rect,{x:col * brickWidth - xOffset,y:actualScreenHeight - bottomWallHeight + row * brickHeight,width:2,height:brickHeight - 2,color:"rgba(0,0,0,0.3)"})]},`bottom-brick-${col}-${row}`);})),pipes.map(pipe =>{const topPipeStart = adjustedTopWallHeight;const bottomWallTop = actualScreenHeight - bottomWallHeight;const topPipeEnd = Math.max(topPipeStart,Math.min(pipe.topHeight,bottomWallTop));const bottomPipeStart = Math.max(topPipeStart,Math.min(pipe.bottomY,bottomWallTop));const bottomPipeEnd = bottomWallTop;return _jsxs(React.Fragment,{children:[topPipeEnd > topPipeStart && _jsxs(_Fragment,{children:[_jsx(RoundedRect,{x:pipe.x,y:topPipeStart,width:PIPE_WIDTH,height:topPipeEnd - topPipeStart,r:4,children:_jsx(LinearGradient,{start:vec(pipe.x,0),end:vec(pipe.x + PIPE_WIDTH,0),colors:[FLAPPY_BIRD_COLORS.PIPE_GREEN,FLAPPY_BIRD_COLORS.PIPE_DARK]})}),_jsxs(RoundedRect,{x:pipe.x - 5,y:topPipeEnd - 20,width:PIPE_WIDTH + 10,height:25,r:4,children:[_jsx(LinearGradient,{start:vec(pipe.x - 5,0),end:vec(pipe.x + PIPE_WIDTH + 5,0),colors:[FLAPPY_BIRD_COLORS.PIPE_DARK,FLAPPY_BIRD_COLORS.PIPE_GREEN]}),_jsx(Shadow,{dx:0,dy:4,blur:8,color:"rgba(0,0,0,0.4)"})]})]}),bottomPipeStart < bottomPipeEnd && _jsxs(_Fragment,{children:[_jsx(RoundedRect,{x:pipe.x,y:bottomPipeStart,width:PIPE_WIDTH,height:bottomPipeEnd - bottomPipeStart,r:4,children:_jsx(LinearGradient,{start:vec(pipe.x,0),end:vec(pipe.x + PIPE_WIDTH,0),colors:[FLAPPY_BIRD_COLORS.PIPE_GREEN,FLAPPY_BIRD_COLORS.PIPE_DARK]})}),_jsxs(RoundedRect,{x:pipe.x - 5,y:bottomPipeStart,width:PIPE_WIDTH + 10,height:25,r:4,children:[_jsx(LinearGradient,{start:vec(pipe.x - 5,0),end:vec(pipe.x + PIPE_WIDTH + 5,0),colors:[FLAPPY_BIRD_COLORS.PIPE_DARK,FLAPPY_BIRD_COLORS.PIPE_GREEN]}),_jsx(Shadow,{dx:0,dy:-4,blur:8,color:"rgba(0,0,0,0.4)"})]})]})]},pipe.id);})]})});});Pipes.displayName = 'Pipes';
1
+ "use strict";import r from"react";import{View as t,StyleSheet as e,Dimensions as h}from"react-native";import{Canvas as i,Rect as o,RoundedRect as n,LinearGradient as s,vec as c,Shadow as a}from"@shopify/react-native-skia";import{FLAPPY_BIRD_GAME_CONFIG as d,FLAPPY_BIRD_COLORS as l}from"../FlappyBirdConstants.js";import{jsx as g,jsxs as y,Fragment as x}from"react/jsx-runtime";const{width:p}=h.get("window"),m=150,w=40,b=20,f=d.GAME_HEIGHT;export const Pipes=r.memo(({pipes:h,offset:u=0})=>{const{PIPE_WIDTH:M}=d,A=m+u,B=u>0?150:250;return g(t,{style:e.absoluteFill,pointerEvents:"none",children:y(i,{style:{flex:1},children:[g(o,{x:0,y:0,width:p,height:A,children:g(s,{start:c(0,0),end:c(0,A),colors:["#8B4513","#A0522D","#8B4513"]})}),Array.from({length:Math.ceil(p/w)+1}).map((t,e)=>Array.from({length:Math.ceil(A/b)}).map((t,h)=>{const i=h%2==0?0:20;return y(r.Fragment,{children:[g(o,{x:e*w-i,y:h*b,width:38,height:2,color:"rgba(0,0,0,0.3)"}),g(o,{x:e*w-i,y:h*b,width:2,height:18,color:"rgba(0,0,0,0.3)"})]},`top-brick-${e}-${h}`)})),g(o,{x:0,y:f-B,width:p,height:B,children:g(s,{start:c(0,f-B),end:c(0,f),colors:["#8B4513","#A0522D","#8B4513"]})}),Array.from({length:Math.ceil(p/w)+1}).map((t,e)=>Array.from({length:Math.ceil(B/b)}).map((t,h)=>{const i=h%2==0?0:20;return y(r.Fragment,{children:[g(o,{x:e*w-i,y:f-B+h*b,width:38,height:2,color:"rgba(0,0,0,0.3)"}),g(o,{x:e*w-i,y:f-B+h*b,width:2,height:18,color:"rgba(0,0,0,0.3)"})]},`bottom-brick-${e}-${h}`)})),h.map(t=>{const e=A,h=f-B,i=Math.max(e,Math.min(t.topHeight,h)),o=Math.max(e,Math.min(t.bottomY,h)),d=h;return y(r.Fragment,{children:[i>e&&y(x,{children:[g(n,{x:t.x,y:e,width:M,height:i-e,r:4,children:g(s,{start:c(t.x,0),end:c(t.x+M,0),colors:[l.PIPE_GREEN,l.PIPE_DARK]})}),y(n,{x:t.x-5,y:i-20,width:M+10,height:25,r:4,children:[g(s,{start:c(t.x-5,0),end:c(t.x+M+5,0),colors:[l.PIPE_DARK,l.PIPE_GREEN]}),g(a,{dx:0,dy:4,blur:8,color:"rgba(0,0,0,0.4)"})]})]}),d>o&&y(x,{children:[g(n,{x:t.x,y:o,width:M,height:d-o,r:4,children:g(s,{start:c(t.x,0),end:c(t.x+M,0),colors:[l.PIPE_GREEN,l.PIPE_DARK]})}),y(n,{x:t.x-5,y:o,width:M+10,height:25,r:4,children:[g(s,{start:c(t.x-5,0),end:c(t.x+M+5,0),colors:[l.PIPE_DARK,l.PIPE_GREEN]}),g(a,{dx:0,dy:-4,blur:8,color:"rgba(0,0,0,0.4)"})]})]})]},t.id)})]})})});Pipes.displayName="Pipes";
@@ -1 +1 @@
1
- "use strict";import React,{useMemo}from 'react';import{View,Text,StyleSheet}from 'react-native';import{useSurvivalTime}from "../FlappyBirdStore.js";import{ScoreBoardContainer}from "../../../helpers/index.js";import{FLAPPY_BIRD_COLORS}from "../FlappyBirdConstants.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const formatTime = seconds =>{const mins = Math.floor(seconds / 60);const secs = seconds % 60;return `${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;};export const ScoreBoard = React.memo(({offset = 0})=>{const survivalTime = useSurvivalTime();const formattedTime = useMemo(()=> formatTime(survivalTime),[survivalTime]);return _jsx(ScoreBoardContainer,{offset:offset,backgroundColor:FLAPPY_BIRD_COLORS.SCORE_BACKGROUND,borderColor:FLAPPY_BIRD_COLORS.SCORE_BORDER,children:_jsx(View,{style:styles.scoreBoard,children:_jsxs(View,{style:styles.scoreSection,children:[_jsx(Text,{style:styles.scoreLabel,children:"Time"}),_jsx(Text,{style:styles.scoreValue,children:formattedTime})]})})});});const styles = StyleSheet.create({scoreBoard:{flexDirection:'row',justifyContent:'center',alignItems:'center'},scoreSection:{alignItems:'center',flex:1},scoreLabel:{fontSize:16,fontWeight:'bold',color:FLAPPY_BIRD_COLORS.SCORE_TEXT,marginBottom:4,textShadowColor:'rgba(0,0,0,0.5)',textShadowOffset:{width:1,height:1},textShadowRadius:2},scoreValue:{fontSize:25,fontWeight:'bold',color:FLAPPY_BIRD_COLORS.SCORE_TEXT,textShadowColor:'rgba(0,0,0,0.8)',textShadowOffset:{width:2,height:2},textShadowRadius:4}});ScoreBoard.displayName = 'ScoreBoard';
1
+ "use strict";import t,{useMemo as o}from"react";import{View as e,Text as r,StyleSheet as s}from"react-native";import{useSurvivalTime as i}from"../FlappyBirdStore.js";import{ScoreBoardContainer as n}from"../../../helpers/index.js";import{FLAPPY_BIRD_COLORS as a}from"../FlappyBirdConstants.js";import{jsx as c,jsxs as d}from"react/jsx-runtime";const l=t=>{const o=t%60;return`${Math.floor(t/60).toString().padStart(2,"0")}:${o.toString().padStart(2,"0")}`};export const ScoreBoard=t.memo(({offset:t=0})=>{const s=i(),h=o(()=>l(s),[s]);return c(n,{offset:t,backgroundColor:a.SCORE_BACKGROUND,borderColor:a.SCORE_BORDER,children:c(e,{style:f.scoreBoard,children:d(e,{style:f.scoreSection,children:[c(r,{style:f.scoreLabel,children:"Time"}),c(r,{style:f.scoreValue,children:h})]})})})});const f=s.create({scoreBoard:{flexDirection:"row",justifyContent:"center",alignItems:"center"},scoreSection:{alignItems:"center",flex:1},scoreLabel:{fontSize:16,fontWeight:"bold",color:a.SCORE_TEXT,marginBottom:4,textShadowColor:"rgba(0, 0, 0, 0.5)",textShadowOffset:{width:1,height:1},textShadowRadius:2},scoreValue:{fontSize:25,fontWeight:"bold",color:a.SCORE_TEXT,textShadowColor:"rgba(0, 0, 0, 0.8)",textShadowOffset:{width:2,height:2},textShadowRadius:4}});ScoreBoard.displayName="ScoreBoard";
@@ -1 +1 @@
1
- "use strict";export{ScoreBoard}from "./ScoreBoard.js";export{Bird}from "./Bird.js";export{Pipes}from "./Pipes.js";export{GameArea}from "./GameArea.js";export{GameBackground}from "./GameBackground.js";
1
+ "use strict";export{ScoreBoard}from"./ScoreBoard.js";export{Bird}from"./Bird.js";export{Pipes}from"./Pipes.js";export{GameArea}from"./GameArea.js";export{GameBackground}from"./GameBackground.js";
@@ -1 +1 @@
1
- "use strict";import React,{useEffect,useCallback,useMemo,useRef}from 'react';import{View,StyleSheet}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{useFruitMergerStore,useIsPlaying,useIsGameOver,useTime}from "./FruitMergerStore.js";import{ScoreBoard,GameArea,GameBackground}from "./components/index.js";import{GameControlButton,GameOverModal}from "../../helpers/index.js";import{GameSettingsModal}from "../../helpers/index.js";import{FRUIT_MERGER_COLORS}from "./FruitMergerConstants.js";import{GAME_IDS,DEFAULT_GAME_SETTINGS}from "../../services/UtilsService.js";import{playSound,GAME_SOUNDS}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";import{formatTime}from "./FruitMergerService.js";import{calculateTimeBasedScore}from "../../services/ScoringService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const FruitMerger = React.memo(({settings = DEFAULT_GAME_SETTINGS,onSettingsChange,onEndGame})=>{const isPlaying = useIsPlaying();const isGameOver = useIsGameOver();const time = useTime();const gameReportedRef = useRef(false);const reportGameEnd = useCallback(status =>{if(onEndGame && !gameReportedRef.current){const expectedTimes ={easy:60,medium:120,hard:180};const expectedTime = expectedTimes[settings.difficulty];const calculatedScore = status === 'lose' ? calculateTimeBasedScore(time,expectedTime):0;onEndGame({status,score:calculatedScore.toString()});gameReportedRef.current = true;}},[onEndGame,time,settings.difficulty]);const initializeGame = useFruitMergerStore(state => state.initializeGame);const startGame = useFruitMergerStore(state => state.startGame);const stopGame = useFruitMergerStore(state => state.stopGame);const resetGame = useFruitMergerStore(state => state.resetGame);useEffect(()=>{initializeGame();},[initializeGame]);const prevGameOverRef = useRef(false);useEffect(()=>{if(isGameOver && !prevGameOverRef.current){playSound(GAME_SOUNDS.FRUIT_MERGER.GAME_OVER,settings.enableSounds);playHaptic(HapticType.HEAVY,settings.enableHaptics);reportGameEnd('lose');}prevGameOverRef.current = isGameOver;if(!isGameOver){gameReportedRef.current = false;}},[isGameOver,settings.enableSounds,settings.enableHaptics,reportGameEnd]);const handleStartGame = useCallback(()=>{gameReportedRef.current = false;initializeGame();startGame();playSound(GAME_SOUNDS.FRUIT_MERGER.START,settings.enableSounds);playHaptic(HapticType.MEDIUM,settings.enableHaptics);},[initializeGame,startGame,settings.enableSounds,settings.enableHaptics]);const handleStopGame = useCallback(()=>{reportGameEnd('cancel');stopGame();},[stopGame,reportGameEnd]);const handleResetGame = useCallback(()=>{resetGame();initializeGame();},[resetGame,initializeGame]);const gameOverModalProps = useMemo(()=>({isVisible:isGameOver,score:time,onPlayAgain:handleResetGame,buttonText:"Merge Again!",primaryColor:"rgba(76,175,80,0.5)",borderColor:"rgba(76,175,80,0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#4CAF50",scoreLabel:"Time Survived",scoreFormatter:formatTime}),[isGameOver,time,handleResetGame]);const gameSettingsModalProps = useMemo(()=>({gameId:GAME_IDS.FRUIT_MERGER,settings,onSettingsChange}),[settings,onSettingsChange]);const offset = settings?.offset ?? 0;const gameControlButtonProps = useMemo(()=>({isPlaying,gameOver:isGameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,startButtonText:"START MERGING",stopButtonText:"STOP GAME",startButtonSubtext:"Merge fruits!",startButtonColor:FRUIT_MERGER_COLORS.BUTTON_PRIMARY,startButtonBorderColor:FRUIT_MERGER_COLORS.BUTTON_SECONDARY}),[isPlaying,isGameOver,handleStartGame,handleStopGame]);return _jsx(View,{style:styles.root,children:_jsx(GestureHandlerRootView,{children:_jsx(GameBackground,{offset:offset,children:_jsxs(View,{style:[styles.container,{paddingTop:offset}],children:[_jsx(ScoreBoard,{offset:offset}),_jsx(GameArea,{settings:settings}),_jsx(GameControlButton,{...gameControlButtonProps}),_jsx(GameOverModal,{...gameOverModalProps}),_jsx(GameSettingsModal,{...gameSettingsModalProps})]})})})});});const styles = StyleSheet.create({root:{flex:1},container:{flex:1}});FruitMerger.displayName = 'FruitMerger';
1
+ "use strict";import r,{useEffect as e,useCallback as t,useMemo as o,useRef as s}from"react";import{View as i,StyleSheet as n}from"react-native";import{GestureHandlerRootView as m}from"react-native-gesture-handler";import{useFruitMergerStore as a,useIsPlaying as c,useIsGameOver as f,useTime as l}from"./FruitMergerStore.js";import{ScoreBoard as g,GameArea as u,GameBackground as p}from"./components/index.js";import{GameControlButton as d,GameOverModal as S}from"../../helpers/index.js";import{GameSettingsModal as v}from"../../helpers/index.js";import{FRUIT_MERGER_COLORS as x}from"./FruitMergerConstants.js";import{GAME_IDS as j,DEFAULT_GAME_SETTINGS as C}from"../../services/UtilsService.js";import{playSound as b,GAME_SOUNDS as h}from"../../services/SoundsService.js";import{playHaptic as M,HapticType as T}from"../../services/HapticsService.js";import{formatTime as F}from"./FruitMergerService.js";import{calculateTimeBasedScore as B}from"../../services/ScoringService.js";import{jsx as y,jsxs as G}from"react/jsx-runtime";export const FruitMerger=r.memo(({settings:r=C,onSettingsChange:n,onEndGame:E})=>{const P=c(),I=f(),O=l(),R=s(!1),H=t(e=>{if(E&&!R.current){const t={easy:60,medium:120,hard:180}[r.difficulty],o="lose"===e?B(O,t):0;E({status:e,score:o.toString()}),R.current=!0}},[E,O,r.difficulty]),L=a(r=>r.initializeGame),N=a(r=>r.startGame),U=a(r=>r.stopGame),V=a(r=>r.resetGame);e(()=>{L()},[L]);const k=s(!1);e(()=>{I&&!k.current&&(b(h.FRUIT_MERGER.GAME_OVER,r.enableSounds),M(T.HEAVY,r.enableHaptics),H("lose")),k.current=I,I||(R.current=!1)},[I,r.enableSounds,r.enableHaptics,H]);const q=t(()=>{R.current=!1,L(),N(),b(h.FRUIT_MERGER.START,r.enableSounds),M(T.MEDIUM,r.enableHaptics)},[L,N,r.enableSounds,r.enableHaptics]),w=t(()=>{H("cancel"),U()},[U,H]),z=t(()=>{V(),L()},[V,L]),D=o(()=>({isVisible:I,score:O,onPlayAgain:z,buttonText:"Merge Again!",primaryColor:"rgba(76, 175, 80, 0.5)",borderColor:"rgba(76, 175, 80, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#4CAF50",scoreLabel:"Time Survived",scoreFormatter:F}),[I,O,z]),J=o(()=>({gameId:j.FRUIT_MERGER,settings:r,onSettingsChange:n}),[r,n]),K=r?.offset??0,Q=o(()=>({isPlaying:P,gameOver:I,onStartGame:q,onStopGame:w,startButtonText:"START MERGING",stopButtonText:"STOP GAME",startButtonSubtext:"Merge fruits!",startButtonColor:x.BUTTON_PRIMARY,startButtonBorderColor:x.BUTTON_SECONDARY}),[P,I,q,w]);return y(i,{style:A.root,children:y(m,{children:y(p,{offset:K,children:G(i,{style:[A.container,{paddingTop:K}],children:[y(g,{offset:K}),y(u,{settings:r}),y(d,{...Q}),y(S,{...D}),y(v,{...J})]})})})})});const A=n.create({root:{flex:1},container:{flex:1}});FruitMerger.displayName="FruitMerger";
@@ -1 +1 @@
1
- "use strict";export const FRUIT_MERGER_COLORS ={BACKGROUND:'#FFF8E7',SCORE_BACKGROUND:'rgba(76,175,80,0.4)',SCORE_BORDER:'#4CAF50',SCORE_TEXT:'#FFFFFF',BUTTON_PRIMARY:'#4CAF50',BUTTON_SECONDARY:'#66BB6A',GAME_OVER_BG:'rgba(76,175,80,0.95)'};export const FRUIT_TYPES = [{id:0,name:'Cherry',emoji:'🍒',size:30,color:'#FFB3BA',score:1},{id:1,name:'Strawberry',emoji:'🍓',size:40,color:'#FFCDD2',score:2},{id:2,name:'Grape',emoji:'🍇',size:50,color:'#E1BEE7',score:4},{id:3,name:'Mango',emoji:'🥭',size:60,color:'#FFF9C4',score:8},{id:4,name:'Orange',emoji:'🍊',size:70,color:'#FFE0B2',score:16},{id:5,name:'Apple',emoji:'🍎',size:80,color:'#FFCDD2',score:32},{id:6,name:'Peach',emoji:'🍑',size:90,color:'#FFE4E1',score:64},{id:7,name:'Pineapple',emoji:'🍍',size:100,color:'#FFF59D',score:128},{id:8,name:'Watermelon',emoji:'🍈',size:110,color:'#C8E6C9',score:256},{id:9,name:'Pumpkin',emoji:'🎃',size:120,color:'#FFCC80',score:512}];export const FRUIT_MERGER_GAME_CONFIG ={CONTAINER_WIDTH:350,CONTAINER_HEIGHT:480,PLAY_AREA_WIDTH:337,PLAY_AREA_HEIGHT:467,GRAVITY:0.5,BOUNCE:0.3,FRICTION:0.99,SPAWN_Y:-50,MERGE_DISTANCE:10,FALL_SPEED:2,MAX_FRUIT_TYPE:4,SPAWN_DELAY:1500};export const FRUIT_MERGER_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#4CAF50',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(76,175,80,0.15)',sectionTitleColor:'#4CAF50',buttonSelectedColor:'#4CAF50',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(76,175,80,0.3)',switchTrackColorTrue:'#4CAF50',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const FRUIT_MERGER_DIFFICULTY_DESCRIPTIONS ={easy:'Relaxed merging with slower fruit drops',medium:'Balanced challenge with moderate speed',hard:'Fast-paced merging with quick drops'};
1
+ "use strict";export const FRUIT_MERGER_COLORS={BACKGROUND:"#FFF8E7",SCORE_BACKGROUND:"rgba(76, 175, 80, 0.4)",SCORE_BORDER:"#4CAF50",SCORE_TEXT:"#FFFFFF",BUTTON_PRIMARY:"#4CAF50",BUTTON_SECONDARY:"#66BB6A",GAME_OVER_BG:"rgba(76, 175, 80, 0.95)"};export const FRUIT_TYPES=[{id:0,name:"Cherry",emoji:"\ud83c\udf52",size:30,color:"#FFB3BA",score:1},{id:1,name:"Strawberry",emoji:"\ud83c\udf53",size:40,color:"#FFCDD2",score:2},{id:2,name:"Grape",emoji:"\ud83c\udf47",size:50,color:"#E1BEE7",score:4},{id:3,name:"Mango",emoji:"\ud83e\udd6d",size:60,color:"#FFF9C4",score:8},{id:4,name:"Orange",emoji:"\ud83c\udf4a",size:70,color:"#FFE0B2",score:16},{id:5,name:"Apple",emoji:"\ud83c\udf4e",size:80,color:"#FFCDD2",score:32},{id:6,name:"Peach",emoji:"\ud83c\udf51",size:90,color:"#FFE4E1",score:64},{id:7,name:"Pineapple",emoji:"\ud83c\udf4d",size:100,color:"#FFF59D",score:128},{id:8,name:"Watermelon",emoji:"\ud83c\udf48",size:110,color:"#C8E6C9",score:256},{id:9,name:"Pumpkin",emoji:"\ud83c\udf83",size:120,color:"#FFCC80",score:512}];export const FRUIT_MERGER_GAME_CONFIG={CONTAINER_WIDTH:350,CONTAINER_HEIGHT:480,PLAY_AREA_WIDTH:337,PLAY_AREA_HEIGHT:467,GRAVITY:.5,BOUNCE:.3,FRICTION:.99,SPAWN_Y:-50,MERGE_DISTANCE:10,FALL_SPEED:2,MAX_FRUIT_TYPE:4,SPAWN_DELAY:1500};export const FRUIT_MERGER_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#4CAF50",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(76, 175, 80, 0.15)",sectionTitleColor:"#4CAF50",buttonSelectedColor:"#4CAF50",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(76, 175, 80, 0.3)",switchTrackColorTrue:"#4CAF50",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const FRUIT_MERGER_DIFFICULTY_DESCRIPTIONS={easy:"Relaxed merging with slower fruit drops",medium:"Balanced challenge with moderate speed",hard:"Fast-paced merging with quick drops"};
@@ -1 +1 @@
1
- "use strict";export const formatTime = seconds =>{const mins = Math.floor(seconds / 60);const secs = seconds % 60;return `${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`;};export class FruitMergerService{cleanup(){}}
1
+ "use strict";export const formatTime=e=>{const r=e%60;return`${Math.floor(e/60).toString().padStart(2,"0")}:${r.toString().padStart(2,"0")}`};export class FruitMergerService{cleanup(){}}
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{immerMiddleware}from "../../services/UtilsService.js";import{FRUIT_TYPES,FRUIT_MERGER_GAME_CONFIG}from "./FruitMergerConstants.js";import{playSound,GAME_SOUNDS}from "../../services/SoundsService.js";import{playHaptic,HapticType}from "../../services/HapticsService.js";const generateRandomFruitType =()=>{return Math.floor(Math.random()* FRUIT_MERGER_GAME_CONFIG.MAX_FRUIT_TYPE);};export const useFruitMergerStore = create()(immerMiddleware(subscribeWithSelector((set,get)=>({isPlaying:false,isGameOver:false,fruits:[],currentFruit:null,nextFruitType:0,score:0,time:0,timerInterval:null,gameLoopInterval:null,spawnInterval:null,initializeGame:()=>{set(state =>{state.fruits = [];state.currentFruit = null;state.nextFruitType = generateRandomFruitType();state.score = 0;state.time = 0;state.isPlaying = false;state.isGameOver = false;});},startGame:()=>{const state = get();if(state.timerInterval)clearInterval(state.timerInterval);if(state.gameLoopInterval)clearInterval(state.gameLoopInterval);if(state.spawnInterval)clearInterval(state.spawnInterval);const timerInterval = setInterval(()=>{get().incrementTime();},1000);const gameLoopInterval = setInterval(()=>{get().updatePhysics();},1000 / 30);set(state =>{state.isPlaying = true;state.isGameOver = false;state.time = 0;state.timerInterval = timerInterval;state.gameLoopInterval = gameLoopInterval;});get().spawnCurrentFruit();},stopGame:()=>{const state = get();if(state.timerInterval)clearInterval(state.timerInterval);if(state.gameLoopInterval)clearInterval(state.gameLoopInterval);if(state.spawnInterval)clearInterval(state.spawnInterval);state.initializeGame();},resetGame:()=>{const state = get();if(state.timerInterval)clearInterval(state.timerInterval);if(state.gameLoopInterval)clearInterval(state.gameLoopInterval);if(state.spawnInterval)clearInterval(state.spawnInterval);set(state =>{state.isPlaying = false;state.isGameOver = false;state.fruits = [];state.currentFruit = null;state.score = 0;state.time = 0;state.timerInterval = null;state.gameLoopInterval = null;state.spawnInterval = null;});},spawnCurrentFruit:()=>{const state = get();const fruitType = state.nextFruitType;const fruit ={id:`fruit-${Date.now()}-${Math.random()}`,typeId:fruitType,x:FRUIT_MERGER_GAME_CONFIG.CONTAINER_WIDTH / 2,y:30,vx:0,vy:0,isActive:false,isMerging:false};set(state =>{state.currentFruit = fruit;});},dropCurrentFruit:()=>{const state = get();if(!state.currentFruit)return;set(draftState =>{if(draftState.currentFruit){draftState.currentFruit.isActive = true;draftState.fruits.push(draftState.currentFruit);draftState.currentFruit = null;draftState.nextFruitType = generateRandomFruitType();}});setTimeout(()=>{get().spawnCurrentFruit();},300);},updateCurrentFruitPosition:x =>{set(state =>{if(state.currentFruit){const nextFruitData = FRUIT_TYPES[state.nextFruitType];if(!nextFruitData)return;const fruitData = FRUIT_TYPES[state.currentFruit.typeId];if(fruitData){const radius = fruitData.size / 2;state.currentFruit.x = Math.max(radius,Math.min(FRUIT_MERGER_GAME_CONFIG.PLAY_AREA_WIDTH - radius,x));}}});},updatePhysics:()=>{const state = get();if(!state.isPlaying || state.fruits.length === 0)return;const{PLAY_AREA_WIDTH,PLAY_AREA_HEIGHT,GRAVITY,BOUNCE,FRICTION}= FRUIT_MERGER_GAME_CONFIG;set(draftState =>{for(let i = 0;i < draftState.fruits.length;i++){const fruit = draftState.fruits[i];if(!fruit || !fruit.isActive || fruit.isMerging)continue;const fruitData = FRUIT_TYPES[fruit.typeId];if(!fruitData)continue;const radius = fruitData.size / 2;fruit.vy += GRAVITY;fruit.x += fruit.vx;fruit.y += fruit.vy;fruit.vx *= FRICTION;fruit.vy *= FRICTION;if(fruit.x - radius < 0){fruit.x = radius;fruit.vx *= -BOUNCE;}if(fruit.x + radius > PLAY_AREA_WIDTH){fruit.x = PLAY_AREA_WIDTH - radius;fruit.vx *= -BOUNCE;}if(fruit.y + radius >= PLAY_AREA_HEIGHT){fruit.y = PLAY_AREA_HEIGHT - radius;fruit.vy *= -BOUNCE;fruit.vx *= FRICTION;if(Math.abs(fruit.vy)< 0.5){fruit.vy = 0;}}if(fruit.y - radius < 0){fruit.y = radius;fruit.vy = Math.abs(fruit.vy)* 0.5;}}for(let i = 0;i < draftState.fruits.length;i++){const fruit = draftState.fruits[i];if(!fruit || !fruit.isActive || fruit.isMerging)continue;const fruitData = FRUIT_TYPES[fruit.typeId];if(!fruitData)continue;const radius = fruitData.size / 2;for(let j = i + 1;j < draftState.fruits.length;j++){const otherFruit = draftState.fruits[j];if(!otherFruit || !otherFruit.isActive || otherFruit.isMerging)continue;const otherFruitData = FRUIT_TYPES[otherFruit.typeId];if(!otherFruitData)continue;const otherRadius = otherFruitData.size / 2;const dx = otherFruit.x - fruit.x;const dy = otherFruit.y - fruit.y;const distance = Math.sqrt(dx * dx + dy * dy);const minDistance = radius + otherRadius;if(distance < minDistance && distance > 0){const overlap = minDistance - distance;const angle = Math.atan2(dy,dx);const separationX = Math.cos(angle)* overlap * 0.5;const separationY = Math.sin(angle)* overlap * 0.5;fruit.x -= separationX;fruit.y -= separationY;otherFruit.x += separationX;otherFruit.y += separationY;const relativeVx = otherFruit.vx - fruit.vx;const relativeVy = otherFruit.vy - fruit.vy;const dotProduct = relativeVx * Math.cos(angle)+ relativeVy * Math.sin(angle);if(dotProduct < 0){const impulse = dotProduct * 0.8;fruit.vx += Math.cos(angle)* impulse;fruit.vy += Math.sin(angle)* impulse;otherFruit.vx -= Math.cos(angle)* impulse;otherFruit.vy -= Math.sin(angle)* impulse;}fruit.x = Math.max(radius,Math.min(PLAY_AREA_WIDTH - radius,fruit.x));fruit.y = Math.max(radius,Math.min(PLAY_AREA_HEIGHT - radius,fruit.y));otherFruit.x = Math.max(otherRadius,Math.min(PLAY_AREA_WIDTH - otherRadius,otherFruit.x));otherFruit.y = Math.max(otherRadius,Math.min(PLAY_AREA_HEIGHT - otherRadius,otherFruit.y));}}}});get().checkMerges({enableSounds:true,enableHaptics:true});},checkMerges:settings =>{const state = get();const{MERGE_DISTANCE}= FRUIT_MERGER_GAME_CONFIG;for(let i = 0;i < state.fruits.length;i++){const fruit1 = state.fruits[i];if(!fruit1 || !fruit1.isActive || fruit1.isMerging || fruit1.y < 0)continue;for(let j = i + 1;j < state.fruits.length;j++){const fruit2 = state.fruits[j];if(!fruit2 || !fruit2.isActive || fruit2.isMerging || fruit2.y < 0)continue;if(fruit1.typeId === fruit2.typeId){const dx = fruit2.x - fruit1.x;const dy = fruit2.y - fruit1.y;const distance = Math.sqrt(dx * dx + dy * dy);const fruit1Data = FRUIT_TYPES[fruit1.typeId];const fruit2Data = FRUIT_TYPES[fruit2.typeId];if(!fruit1Data || !fruit2Data)continue;const radius1 = fruit1Data.size / 2;const radius2 = fruit2Data.size / 2;const touchDistance = radius1 + radius2;if(distance <= touchDistance + MERGE_DISTANCE){const fruit1Speed = Math.sqrt(fruit1.vx * fruit1.vx + fruit1.vy * fruit1.vy);const fruit2Speed = Math.sqrt(fruit2.vx * fruit2.vx + fruit2.vy * fruit2.vy);if(fruit1Speed < 5 || fruit2Speed < 5){get().mergeFruits(fruit1,fruit2,settings);return;}}}}}const maxFruitReached = state.fruits.some(f => f.typeId === FRUIT_TYPES.length - 1 && f.isActive);if(maxFruitReached){playSound(GAME_SOUNDS.FRUIT_MERGER.COMPLETE,settings.enableSounds);playHaptic(HapticType.SUCCESS,settings.enableHaptics);get().stopGame();}},mergeFruits:(fruit1,fruit2,settings)=>{const nextTypeId = fruit1.typeId + 1;if(nextTypeId >= FRUIT_TYPES.length)return;const mergeX =(fruit1.x + fruit2.x)/ 2;const mergeY =(fruit1.y + fruit2.y)/ 2;set(state =>{state.fruits = state.fruits.filter(f => f.id !== fruit1.id && f.id !== fruit2.id);const newFruit ={id:`fruit-${Date.now()}-${Math.random()}`,typeId:nextTypeId,x:mergeX,y:mergeY,vx:0,vy:-2,isActive:true,isMerging:false};state.fruits.push(newFruit);const nextFruitType = FRUIT_TYPES[nextTypeId];if(nextFruitType){state.score += nextFruitType.score;}});playSound(GAME_SOUNDS.FRUIT_MERGER.MERGE,settings.enableSounds);playHaptic(HapticType.MEDIUM,settings.enableHaptics);},incrementTime:()=>{set(state =>{state.time += 1;});}}))));export const useIsPlaying =()=> useFruitMergerStore(state => state.isPlaying);export const useIsGameOver =()=> useFruitMergerStore(state => state.isGameOver);export const useFruits =()=> useFruitMergerStore(state => state.fruits);export const useCurrentFruit =()=> useFruitMergerStore(state => state.currentFruit);export const useNextFruitType =()=> useFruitMergerStore(state => state.nextFruitType);export const useScore =()=> useFruitMergerStore(state => state.score);export const useTime =()=> useFruitMergerStore(state => state.time);
1
+ "use strict";import{create as t}from"zustand";import{subscribeWithSelector as e}from"zustand/middleware";import{immerMiddleware as r}from"../../services/UtilsService.js";import{FRUIT_TYPES as n,FRUIT_MERGER_GAME_CONFIG as s}from"./FruitMergerConstants.js";import{playSound as o,GAME_SOUNDS as i}from"../../services/SoundsService.js";import{playHaptic as a,HapticType as c}from"../../services/HapticsService.js";const u=()=>Math.floor(Math.random()*s.MAX_FRUIT_TYPE);export const useFruitMergerStore=t()(r(e((t,e)=>({isPlaying:!1,isGameOver:!1,fruits:[],currentFruit:null,nextFruitType:0,score:0,time:0,timerInterval:null,gameLoopInterval:null,spawnInterval:null,initializeGame:()=>{t(t=>{t.fruits=[],t.currentFruit=null,t.nextFruitType=u(),t.score=0,t.time=0,t.isPlaying=!1,t.isGameOver=!1})},startGame:()=>{const r=e();r.timerInterval&&clearInterval(r.timerInterval),r.gameLoopInterval&&clearInterval(r.gameLoopInterval),r.spawnInterval&&clearInterval(r.spawnInterval);const n=setInterval(()=>{e().incrementTime()},1e3),s=setInterval(()=>{e().updatePhysics()},1e3/30);t(t=>{t.isPlaying=!0,t.isGameOver=!1,t.time=0,t.timerInterval=n,t.gameLoopInterval=s}),e().spawnCurrentFruit()},stopGame:()=>{const t=e();t.timerInterval&&clearInterval(t.timerInterval),t.gameLoopInterval&&clearInterval(t.gameLoopInterval),t.spawnInterval&&clearInterval(t.spawnInterval),t.initializeGame()},resetGame:()=>{const r=e();r.timerInterval&&clearInterval(r.timerInterval),r.gameLoopInterval&&clearInterval(r.gameLoopInterval),r.spawnInterval&&clearInterval(r.spawnInterval),t(t=>{t.isPlaying=!1,t.isGameOver=!1,t.fruits=[],t.currentFruit=null,t.score=0,t.time=0,t.timerInterval=null,t.gameLoopInterval=null,t.spawnInterval=null})},spawnCurrentFruit:()=>{const r=e().nextFruitType,n={id:`fruit-${Date.now()}-${Math.random()}`,typeId:r,x:s.CONTAINER_WIDTH/2,y:30,vx:0,vy:0,isActive:!1,isMerging:!1};t(t=>{t.currentFruit=n})},dropCurrentFruit:()=>{e().currentFruit&&(t(t=>{t.currentFruit&&(t.currentFruit.isActive=!0,t.fruits.push(t.currentFruit),t.currentFruit=null,t.nextFruitType=u())}),setTimeout(()=>{e().spawnCurrentFruit()},300))},updateCurrentFruitPosition:e=>{t(t=>{if(t.currentFruit){if(!n[t.nextFruitType])return;const r=n[t.currentFruit.typeId];if(r){const n=r.size/2;t.currentFruit.x=Math.max(n,Math.min(s.PLAY_AREA_WIDTH-n,e))}}})},updatePhysics:()=>{const r=e();if(!r.isPlaying||0===r.fruits.length)return;const{PLAY_AREA_WIDTH:o,PLAY_AREA_HEIGHT:i,GRAVITY:a,BOUNCE:c,FRICTION:u}=s;t(t=>{for(let e=0;e<t.fruits.length;e++){const r=t.fruits[e];if(!r||!r.isActive||r.isMerging)continue;const s=n[r.typeId];if(!s)continue;const l=s.size/2;r.vy+=a,r.x+=r.vx,r.y+=r.vy,r.vx*=u,r.vy*=u,0>r.x-l&&(r.x=l,r.vx*=-c),r.x+l>o&&(r.x=o-l,r.vx*=-c),r.y+l<i||(r.y=i-l,r.vy*=-c,r.vx*=u,.5>Math.abs(r.vy)&&(r.vy=0)),0>r.y-l&&(r.y=l,r.vy=.5*Math.abs(r.vy))}for(let e=0;e<t.fruits.length;e++){const r=t.fruits[e];if(!r||!r.isActive||r.isMerging)continue;const s=n[r.typeId];if(!s)continue;const a=s.size/2;for(let s=e+1;s<t.fruits.length;s++){const e=t.fruits[s];if(!e||!e.isActive||e.isMerging)continue;const c=n[e.typeId];if(!c)continue;const u=c.size/2,l=e.x-r.x,M=e.y-r.y,f=Math.sqrt(l*l+M*M),h=a+u;if(h>f&&f>0){const t=h-f,n=Math.atan2(M,l),s=Math.cos(n)*t*.5,c=Math.sin(n)*t*.5;r.x-=s,r.y-=c,e.x+=s,e.y+=c;const v=e.vx-r.vx,m=e.vy-r.vy,p=v*Math.cos(n)+m*Math.sin(n);if(0>p){const t=.8*p;r.vx+=Math.cos(n)*t,r.vy+=Math.sin(n)*t,e.vx-=Math.cos(n)*t,e.vy-=Math.sin(n)*t}r.x=Math.max(a,Math.min(o-a,r.x)),r.y=Math.max(a,Math.min(i-a,r.y)),e.x=Math.max(u,Math.min(o-u,e.x)),e.y=Math.max(u,Math.min(i-u,e.y))}}}}),e().checkMerges({enableSounds:!0,enableHaptics:!0})},checkMerges:t=>{const r=e(),{MERGE_DISTANCE:u}=s;for(let s=0;s<r.fruits.length;s++){const o=r.fruits[s];if(o&&o.isActive&&!o.isMerging&&o.y>=0)for(let i=s+1;i<r.fruits.length;i++){const s=r.fruits[i];if(s&&s.isActive&&!s.isMerging&&s.y>=0&&o.typeId===s.typeId){const r=s.x-o.x,i=s.y-o.y,a=Math.sqrt(r*r+i*i),c=n[o.typeId],l=n[s.typeId];if(!c||!l)continue;if(c.size/2+l.size/2+u>=a){const r=Math.sqrt(o.vx*o.vx+o.vy*o.vy),n=Math.sqrt(s.vx*s.vx+s.vy*s.vy);if(5>r||5>n)return void e().mergeFruits(o,s,t)}}}}r.fruits.some(t=>t.typeId===n.length-1&&t.isActive)&&(o(i.FRUIT_MERGER.COMPLETE,t.enableSounds),a(c.SUCCESS,t.enableHaptics),e().stopGame())},mergeFruits:(e,r,s)=>{const u=e.typeId+1;if(u>=n.length)return;const l=(e.x+r.x)/2,M=(e.y+r.y)/2;t(t=>{t.fruits=t.fruits.filter(t=>t.id!==e.id&&t.id!==r.id);const s={id:`fruit-${Date.now()}-${Math.random()}`,typeId:u,x:l,y:M,vx:0,vy:-2,isActive:!0,isMerging:!1};t.fruits.push(s);const o=n[u];o&&(t.score+=o.score)}),o(i.FRUIT_MERGER.MERGE,s.enableSounds),a(c.MEDIUM,s.enableHaptics)},incrementTime:()=>{t(t=>{t.time+=1})}}))));export const useIsPlaying=()=>useFruitMergerStore(t=>t.isPlaying);export const useIsGameOver=()=>useFruitMergerStore(t=>t.isGameOver);export const useFruits=()=>useFruitMergerStore(t=>t.fruits);export const useCurrentFruit=()=>useFruitMergerStore(t=>t.currentFruit);export const useNextFruitType=()=>useFruitMergerStore(t=>t.nextFruitType);export const useScore=()=>useFruitMergerStore(t=>t.score);export const useTime=()=>useFruitMergerStore(t=>t.time);
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{Text,StyleSheet}from 'react-native';import Animated,{useAnimatedStyle,useSharedValue,runOnJS}from 'react-native-reanimated';import{Gesture,GestureDetector}from 'react-native-gesture-handler';import{FRUIT_TYPES,FRUIT_MERGER_GAME_CONFIG}from "../FruitMergerConstants.js";import{jsx as _jsx}from "react/jsx-runtime";export const FruitItem = React.memo(({fruit,onDrag,isDraggable})=>{const fruitData = FRUIT_TYPES[fruit.typeId];if(!fruitData){return null;}const translateX = useSharedValue(fruit.x - fruitData.size / 2);const translateY = useSharedValue(fruit.y - fruitData.size / 2);const startX = useSharedValue(0);const isDragging = useSharedValue(false);const prevXRef = React.useRef(fruit.x);const prevYRef = React.useRef(fruit.y);React.useEffect(()=>{if(!isDragging.value &&(prevXRef.current !== fruit.x || prevYRef.current !== fruit.y)){translateX.value = fruit.x - fruitData.size / 2;translateY.value = fruit.y - fruitData.size / 2;prevXRef.current = fruit.x;prevYRef.current = fruit.y;}},[fruit.x,fruit.y,fruitData.size,isDragging,translateX,translateY]);const animatedStyle = useAnimatedStyle(()=>({transform:[{translateX:translateX.value},{translateY:translateY.value}]}));const panGesture = Gesture.Pan().enabled(isDraggable).onStart(()=>{'worklet';isDragging.value = true;startX.value = translateX.value;}).onUpdate(event =>{'worklet';const newX = startX.value + event.translationX;const radius = fruitData.size / 2;const minX = 0;const maxX = FRUIT_MERGER_GAME_CONFIG.PLAY_AREA_WIDTH - fruitData.size;const clampedX = Math.max(minX,Math.min(maxX,newX));translateX.value = clampedX;const fruitCenterX = clampedX + radius;if(onDrag){runOnJS(onDrag)(fruitCenterX);}}).onEnd(()=>{'worklet';isDragging.value = false;});return _jsx(GestureDetector,{gesture:panGesture,children:_jsx(Animated.View,{style:[styles.fruit,animatedStyle,{width:fruitData.size,height:fruitData.size,backgroundColor:fruitData.color}],children:_jsx(Text,{style:[styles.emoji,{fontSize:fruitData.size * 0.7}],children:fruitData.emoji})})});},(prevProps,nextProps)=>{return prevProps.fruit.id === nextProps.fruit.id && prevProps.fruit.x === nextProps.fruit.x && prevProps.fruit.y === nextProps.fruit.y && prevProps.fruit.typeId === nextProps.fruit.typeId && prevProps.isDraggable === nextProps.isDraggable;});const styles = StyleSheet.create({fruit:{position:'absolute',borderRadius:1000,justifyContent:'center',alignItems:'center'},emoji:{textAlign:'center'}});FruitItem.displayName = 'FruitItem';
1
+ "use strict";import t from"react";import{Text as r,StyleSheet as e}from"react-native";import n,{useAnimatedStyle as i,useSharedValue as o,runOnJS as s}from"react-native-reanimated";import{Gesture as a,GestureDetector as c}from"react-native-gesture-handler";import{FRUIT_TYPES as m,FRUIT_MERGER_GAME_CONFIG as u}from"../FruitMergerConstants.js";import{jsx as l}from"react/jsx-runtime";export const FruitItem=t.memo(({fruit:e,onDrag:g,isDraggable:d})=>{const h=m[e.typeId];if(!h)return null;const p=o(e.x-h.size/2),j=o(e.y-h.size/2),b=o(0),x=o(!1),F=t.useRef(e.x),I=t.useRef(e.y);t.useEffect(()=>{x.value||F.current===e.x&&I.current===e.y||(p.value=e.x-h.size/2,j.value=e.y-h.size/2,F.current=e.x,I.current=e.y)},[e.x,e.y,h.size,x,p,j]);const v=i(()=>({transform:[{translateX:p.value},{translateY:j.value}]})),y=a.Pan().enabled(d).onStart(()=>{x.value=!0,b.value=p.value}).onUpdate(t=>{const r=b.value+t.translationX,e=h.size/2,n=u.PLAY_AREA_WIDTH-h.size,i=Math.max(0,Math.min(n,r));p.value=i;const o=i+e;g&&s(g)(o)}).onEnd(()=>{x.value=!1});return l(c,{gesture:y,children:l(n.View,{style:[f.fruit,v,{width:h.size,height:h.size,backgroundColor:h.color}],children:l(r,{style:[f.emoji,{fontSize:.7*h.size}],children:h.emoji})})})},(t,r)=>t.fruit.id===r.fruit.id&&t.fruit.x===r.fruit.x&&t.fruit.y===r.fruit.y&&t.fruit.typeId===r.fruit.typeId&&t.isDraggable===r.isDraggable);const f=e.create({fruit:{position:"absolute",borderRadius:1e3,justifyContent:"center",alignItems:"center"},emoji:{textAlign:"center"}});FruitItem.displayName="FruitItem";
@@ -1 +1 @@
1
- "use strict";import React,{useCallback,useMemo}from 'react';import{View,StyleSheet,TouchableOpacity}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{useFruits,useCurrentFruit,useNextFruitType,useFruitMergerStore,useIsPlaying}from "../FruitMergerStore.js";import{FruitItem}from "./FruitItem.js";import{FRUIT_MERGER_GAME_CONFIG,FRUIT_TYPES}from "../FruitMergerConstants.js";import{playSound,GAME_SOUNDS}from "../../../services/SoundsService.js";import{playHaptic,HapticType}from "../../../services/HapticsService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const GameArea = React.memo(({settings})=>{const fruits = useFruits();const currentFruit = useCurrentFruit();const nextFruitType = useNextFruitType();const isPlaying = useIsPlaying();const dropCurrentFruit = useFruitMergerStore(state => state.dropCurrentFruit);const updateCurrentFruitPosition = useFruitMergerStore(state => state.updateCurrentFruitPosition);const nextFruitData = useMemo(()=> FRUIT_TYPES[nextFruitType],[nextFruitType]);if(!nextFruitData){return null;}const handleTap = useCallback(()=>{if(!isPlaying || !currentFruit)return;dropCurrentFruit();playSound(GAME_SOUNDS.FRUIT_MERGER.DROP,settings.enableSounds);playHaptic(HapticType.LIGHT,settings.enableHaptics);},[isPlaying,currentFruit,dropCurrentFruit,settings.enableSounds,settings.enableHaptics]);const lastUpdateRef = React.useRef(0);const handleDrag = useCallback(x =>{const now = Date.now();if(now - lastUpdateRef.current > 16){updateCurrentFruitPosition(x);lastUpdateRef.current = now;}},[updateCurrentFruitPosition]);return _jsx(View,{style:styles.container,children:_jsx(GestureHandlerRootView,{children:_jsx(TouchableOpacity,{style:styles.container,activeOpacity:1,onPress:handleTap,children:_jsxs(View,{style:styles.gameContainer,children:[currentFruit && _jsx(FruitItem,{fruit:currentFruit,onDrag:handleDrag,isDraggable:true},currentFruit.id),fruits.map(fruit => _jsx(FruitItem,{fruit:fruit,isDraggable:false},fruit.id))]})})})});});const styles = StyleSheet.create({container:{flex:1,justifyContent:'center',alignItems:'center'},gameContainer:{width:FRUIT_MERGER_GAME_CONFIG.CONTAINER_WIDTH,height:FRUIT_MERGER_GAME_CONFIG.CONTAINER_HEIGHT,backgroundColor:'rgba(255,248,231,0.65)',borderRadius:15,borderWidth:5,borderColor:'#8B4513',overflow:'hidden',position:'relative'},nextFruitPreview:{position:'absolute',top:-80,alignItems:'center'},previewLabel:{fontSize:12,fontWeight:'700',color:'#4CAF50',marginBottom:6},previewFruit:{borderRadius:1000,justifyContent:'center',alignItems:'center',borderWidth:2,borderColor:'#4CAF50'},emoji:{textAlign:'center'}});GameArea.displayName = 'GameArea';
1
+ "use strict";import e,{useCallback as r,useMemo as t}from"react";import{View as i,StyleSheet as o,TouchableOpacity as n}from"react-native";import{GestureHandlerRootView as s}from"react-native-gesture-handler";import{useFruits as a,useCurrentFruit as c,useNextFruitType as m,useFruitMergerStore as l,useIsPlaying as d}from"../FruitMergerStore.js";import{FruitItem as u}from"./FruitItem.js";import{FRUIT_MERGER_GAME_CONFIG as g,FRUIT_TYPES as f}from"../FruitMergerConstants.js";import{playSound as p,GAME_SOUNDS as b}from"../../../services/SoundsService.js";import{playHaptic as h,HapticType as v}from"../../../services/HapticsService.js";import{jsx as j,jsxs as C}from"react/jsx-runtime";export const GameArea=e.memo(({settings:o})=>{const g=a(),F=c(),y=m(),A=d(),w=l(e=>e.dropCurrentFruit),S=l(e=>e.updateCurrentFruitPosition);if(!t(()=>f[y],[y]))return null;const D=r(()=>{A&&F&&(w(),p(b.FRUIT_MERGER.DROP,o.enableSounds),h(v.LIGHT,o.enableHaptics))},[A,F,w,o.enableSounds,o.enableHaptics]),I=e.useRef(0),G=r(e=>{const r=Date.now();r-I.current>16&&(S(e),I.current=r)},[S]);return j(i,{style:x.container,children:j(s,{children:j(n,{style:x.container,activeOpacity:1,onPress:D,children:C(i,{style:x.gameContainer,children:[F&&j(u,{fruit:F,onDrag:G,isDraggable:!0},F.id),g.map(e=>j(u,{fruit:e,isDraggable:!1},e.id))]})})})})});const x=o.create({container:{flex:1,justifyContent:"center",alignItems:"center"},gameContainer:{width:g.CONTAINER_WIDTH,height:g.CONTAINER_HEIGHT,backgroundColor:"rgba(255, 248, 231, 0.65)",borderRadius:15,borderWidth:5,borderColor:"#8B4513",overflow:"hidden",position:"relative"},nextFruitPreview:{position:"absolute",top:-80,alignItems:"center"},previewLabel:{fontSize:12,fontWeight:"700",color:"#4CAF50",marginBottom:6},previewFruit:{borderRadius:1e3,justifyContent:"center",alignItems:"center",borderWidth:2,borderColor:"#4CAF50"},emoji:{textAlign:"center"}});GameArea.displayName="GameArea";
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,Dimensions}from 'react-native';import{Canvas,LinearGradient,Rect,Circle,Path,Skia,vec,Group,Shadow}from '@shopify/react-native-skia';import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";const{width,height}= Dimensions.get('window');const buttonAreaHeight = 250;const grassStartY = height - buttonAreaHeight;const createStaticGrassPath =()=>{const path = Skia.Path.Make();const segmentWidth = width / 8;path.moveTo(0,grassStartY);for(let i = 0;i < 8;i++){const x = i * segmentWidth;const y = grassStartY + Math.sin(i * 0.5)* 8;const controlX = x + segmentWidth / 2;const controlY = y + Math.cos(i * 0.3)* 6;path.quadTo(controlX,controlY,x + segmentWidth,y);}path.lineTo(width,height);path.lineTo(0,height);path.close();return path;};const createStaticSunRays =()=>{return Array.from({length:16},(_,index)=>{const angle = 360 / 16 * index;const radian = angle * Math.PI / 180;const isLongRay = index % 2 === 0;const rayLength = isLongRay ? 25:15;const rayWidth = isLongRay ? 4:2;const startDistance = 45;const startX = width * 0.8 + Math.cos(radian)* startDistance;const startY = height * 0.15 + Math.sin(radian)* startDistance;const endX = width * 0.8 + Math.cos(radian)*(startDistance + rayLength);const endY = height * 0.15 + Math.sin(radian)*(startDistance + rayLength);const rayPath = Skia.Path.Make();rayPath.moveTo(startX,startY);rayPath.lineTo(endX,endY);return{path:rayPath,width:rayWidth};});};const createStaticFlowerPositions =()=>{return Array.from({length:12},(_,i)=>({x:width / 12 * i + i % 3 * 15 + 20,y:grassStartY - 10 - i % 4 * 8}));};const staticGrassPath = createStaticGrassPath();const staticSunRays = createStaticSunRays();const flowerPositions = createStaticFlowerPositions();export const GameBackground =({children,offset = 0})=>{const sunY = height * 0.15 + offset * 0.5;const cloud1Y = height * 0.24 + offset * 0.5;const cloud2Y = height * 0.27 + offset * 0.2;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:grassStartY,children:_jsx(LinearGradient,{start:vec(0,0),end:vec(0,grassStartY),colors:['#87CEEB','#98D8E8','#B0E0E6']})}),_jsx(Rect,{x:0,y:grassStartY,width:width,height:buttonAreaHeight,children:_jsx(LinearGradient,{start:vec(0,grassStartY),end:vec(0,height),colors:['#8B4513','#A0522D','#654321']})}),_jsxs(Group,{children:[_jsxs(Circle,{cx:width * 0.8,cy:sunY,r:40,children:[_jsx(LinearGradient,{start:vec(-40,-40),end:vec(40,40),colors:['#FFFF99','#FFD700','#FFA500','#FF8C00']}),_jsx(Shadow,{dx:0,dy:0,blur:32,color:"#FFD700"})]}),_jsx(Circle,{cx:width * 0.8 - 8,cy:sunY - 8,r:15,children:_jsx(LinearGradient,{start:vec(-15,-15),end:vec(15,15),colors:['#FFFFFF','#FFFF99']})}),staticSunRays.map((_,index)=>{const angle = 360 / 16 * index;const radian = angle * Math.PI / 180;const isLongRay = index % 2 === 0;const rayLength = isLongRay ? 25:15;const rayWidth = isLongRay ? 4:2;const startDistance = 45;const startX = width * 0.8 + Math.cos(radian)* startDistance;const startY = sunY + Math.sin(radian)* startDistance;const endX = width * 0.8 + Math.cos(radian)*(startDistance + rayLength);const endY = sunY + Math.sin(radian)*(startDistance + rayLength);const rayPath = Skia.Path.Make();rayPath.moveTo(startX,startY);rayPath.lineTo(endX,endY);return _jsx(Path,{path:rayPath,style:"stroke",strokeWidth:rayWidth,color:"#FFD700",opacity:0.75},index);}),_jsx(Circle,{cx:width * 0.8,cy:sunY,r:60,opacity:0.1,children:_jsx(LinearGradient,{start:vec(-60,-60),end:vec(60,60),colors:['#FFD700','transparent']})})]}),_jsxs(Group,{children:[_jsxs(Group,{children:[_jsx(Circle,{cx:width * 0.12,cy:cloud1Y,r:16,color:"#F0F8FF",opacity:0.35,children:_jsx(Shadow,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),_jsx(Circle,{cx:width * 0.18,cy:cloud1Y -(height * 0.24 - height * 0.21),r:22,color:"#FFFFFF",opacity:0.45,children:_jsx(Shadow,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),_jsx(Circle,{cx:width * 0.25,cy:cloud1Y -(height * 0.24 - height * 0.19),r:26,color:"#F8F8FF",opacity:0.55,children:_jsx(Shadow,{dx:2,dy:3,blur:12,color:"rgba(0,0,0,0.06)"})}),_jsx(Circle,{cx:width * 0.32,cy:cloud1Y -(height * 0.24 - height * 0.21),r:20,color:"#FFFFFF",opacity:0.50,children:_jsx(Shadow,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),_jsx(Circle,{cx:width * 0.37,cy:cloud1Y,r:14,color:"#F0F8FF",opacity:0.40,children:_jsx(Shadow,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.04)"})})]}),_jsxs(Group,{children:[_jsx(Circle,{cx:width * 0.62,cy:cloud2Y,r:15,color:"#F0F8FF",opacity:0.40,children:_jsx(Shadow,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.04)"})}),_jsx(Circle,{cx:width * 0.69,cy:cloud2Y -(height * 0.27 - height * 0.24),r:19,color:"#FFFFFF",opacity:0.50,children:_jsx(Shadow,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.05)"})}),_jsx(Circle,{cx:width * 0.76,cy:cloud2Y -(height * 0.27 - height * 0.26),r:13,color:"#F8F8FF",opacity:0.45,children:_jsx(Shadow,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.04)"})})]})]}),_jsxs(Path,{path:staticGrassPath,children:[_jsx(LinearGradient,{start:vec(0,grassStartY - 20),end:vec(0,grassStartY + 20),colors:['#32CD32','#228B22','#006400']}),_jsx(Shadow,{dx:0,dy:-2,blur:4,color:"rgba(0,0,0,0.2)"})]}),Array.from({length:25},(_,i)=>{const x = width / 25 * i + i % 3 * 5;const grassHeight = 15 + i % 4 * 5;const grassType = i % 3;const bladePath = Skia.Path.Make();bladePath.moveTo(x,grassStartY);if(grassType === 0){bladePath.quadTo(x + 1,grassStartY - grassHeight * 0.6,x + 1,grassStartY - grassHeight);return _jsx(Path,{path:bladePath,style:"stroke",strokeWidth:1.5,color:"#16a34a",opacity:0.8},`grass-${i}`);}else if(grassType === 1){bladePath.quadTo(x + 2,grassStartY - grassHeight * 0.4,x + 3,grassStartY - grassHeight);return _jsx(Path,{path:bladePath,style:"stroke",strokeWidth:2,color:"#15803d",opacity:0.7},`grass-${i}`);}else{const midHeight = grassHeight * 0.5;bladePath.quadTo(x + 1,grassStartY - midHeight,x + 2,grassStartY - grassHeight);return _jsx(Path,{path:bladePath,style:"stroke",strokeWidth:2.5,color:"#166534",opacity:0.6},`grass-${i}`);}}),_jsxs(Group,{children:[_jsxs(Group,{children:[_jsxs(Rect,{x:width * 0.05,y:grassStartY - 150,width:20,height:150,children:[_jsx(LinearGradient,{start:vec(0,0),end:vec(20,0),colors:['#654321','#8B4513','#A0522D','#8B4513']}),_jsx(Shadow,{dx:3,dy:0,blur:6,color:"rgba(0,0,0,0.4)"})]}),_jsx(Circle,{cx:width * 0.05 + 10,cy:grassStartY - 150,r:38,color:"#1B5E20",opacity:0.9,children:_jsx(Shadow,{dx:4,dy:4,blur:10,color:"rgba(0,0,0,0.3)"})}),_jsx(Circle,{cx:width * 0.05 + 18,cy:grassStartY - 180,r:32,color:"#2E7D32",opacity:0.8}),_jsx(Circle,{cx:width * 0.05 + 25,cy:grassStartY - 140,r:28,color:"#388E3C",opacity:0.7}),_jsx(Circle,{cx:width * 0.05 + 2,cy:grassStartY - 120,r:30,color:"#2E7D32",opacity:0.75})]}),_jsxs(Group,{children:[_jsxs(Rect,{x:width * 0.88,y:grassStartY - 200,width:18,height:200,children:[_jsx(LinearGradient,{start:vec(0,0),end:vec(18,0),colors:['#4A2C2A','#654321','#8B4513','#654321']}),_jsx(Shadow,{dx:-3,dy:0,blur:6,color:"rgba(0,0,0,0.4)"})]}),_jsx(Circle,{cx:width * 0.88 + 9,cy:grassStartY - 120,r:35,color:"#0D4F0C",opacity:0.95,children:_jsx(Shadow,{dx:-4,dy:4,blur:10,color:"rgba(0,0,0,0.3)"})}),_jsx(Circle,{cx:width * 0.88 + 9,cy:grassStartY - 180,r:32,color:"#1B5E20",opacity:0.9}),_jsx(Circle,{cx:width * 0.88 + 9,cy:grassStartY - 240,r:28,color:"#2E7D32",opacity:0.9})]})]}),_jsxs(Group,{children:[_jsxs(Group,{children:[_jsx(Circle,{cx:width * 0.15,cy:grassStartY - 60,r:26,color:"#1B5E20",opacity:0.95,children:_jsx(Shadow,{dx:3,dy:3,blur:8,color:"rgba(0,0,0,0.3)"})}),_jsx(Circle,{cx:width * 0.18,cy:grassStartY - 90,r:22,color:"#2E7D32",opacity:0.9}),_jsx(Circle,{cx:width * 0.21,cy:grassStartY - 70,r:18,color:"#388E3C",opacity:0.8})]}),_jsxs(Group,{children:[_jsx(Circle,{cx:width * 0.72,cy:grassStartY - 35,r:28,color:"#0D4F0C",opacity:0.95,children:_jsx(Shadow,{dx:-2,dy:3,blur:8,color:"rgba(0,0,0,0.3)"})}),_jsx(Circle,{cx:width * 0.75,cy:grassStartY - 70,r:24,color:"#1B5E20",opacity:0.9}),_jsx(Circle,{cx:width * 0.69,cy:grassStartY - 25,r:22,color:"#2E7D32",opacity:0.85})]})]}),_jsx(Group,{children:flowerPositions.map((pos,i)=>{const colors = ['#FF6B6B','#FF69B4','#9370DB','#4169E1','#FF8C00','#32CD32'];const color = colors[i % colors.length];const size = 3 + i % 3;return _jsxs(Group,{children:[_jsx(Circle,{cx:pos.x,cy:pos.y,r:size,color:color,opacity:0.8,children:_jsx(Shadow,{dx:0,dy:1,blur:1,color:"rgba(0,0,0,0.2)"})}),_jsx(Circle,{cx:pos.x,cy:pos.y,r:size * 0.4,color:"#FFD700",opacity:0.9})]},`flower-${i}`);})})]}),children]});};GameBackground.displayName = 'GameBackground';
1
+ "use strict";import r from"react";import{View as c,Dimensions as o}from"react-native";import{Canvas as t,LinearGradient as l,Rect as i,Circle as a,Path as d,Skia as e,vec as y,Group as h,Shadow as n}from"@shopify/react-native-skia";import{jsx as F,jsxs as s}from"react/jsx-runtime";const{width:x,height:p}=o.get("window"),b=250,g=p-b,u=()=>{const r=e.Path.Make(),c=x/8;r.moveTo(0,g);for(let o=0;8>o;o++){const t=o*c,l=g+8*Math.sin(.5*o),i=t+c/2,a=l+6*Math.cos(.3*o);r.quadTo(i,a,t+c,l)}return r.lineTo(x,p),r.lineTo(0,p),r.close(),r},B=()=>Array.from({length:16},(r,c)=>{const o=22.5*c*Math.PI/180,t=c%2==0,l=t?25:15,i=t?4:2,a=.8*x+45*Math.cos(o),d=.15*p+45*Math.sin(o),y=.8*x+Math.cos(o)*(45+l),h=.15*p+Math.sin(o)*(45+l),n=e.Path.Make();return n.moveTo(a,d),n.lineTo(y,h),{path:n,width:i}}),D=()=>Array.from({length:12},(r,c)=>({x:x/12*c+c%3*15+20,y:g-10-c%4*8})),E=u(),f=B(),m=D();export const GameBackground=({children:r,offset:o=0})=>{const u=.15*p+.5*o,B=.24*p+.5*o,D=.27*p+.2*o;return s(c,{style:{flex:1},children:[s(t,{style:{position:"absolute",top:0,left:0,right:0,bottom:0,width:x,height:p},children:[F(i,{x:0,y:0,width:x,height:g,children:F(l,{start:y(0,0),end:y(0,g),colors:["#87CEEB","#98D8E8","#B0E0E6"]})}),F(i,{x:0,y:g,width:x,height:b,children:F(l,{start:y(0,g),end:y(0,p),colors:["#8B4513","#A0522D","#654321"]})}),s(h,{children:[s(a,{cx:.8*x,cy:u,r:40,children:[F(l,{start:y(-40,-40),end:y(40,40),colors:["#FFFF99","#FFD700","#FFA500","#FF8C00"]}),F(n,{dx:0,dy:0,blur:32,color:"#FFD700"})]}),F(a,{cx:.8*x-8,cy:u-8,r:15,children:F(l,{start:y(-15,-15),end:y(15,15),colors:["#FFFFFF","#FFFF99"]})}),f.map((r,c)=>{const o=22.5*c*Math.PI/180,t=c%2==0,l=t?25:15,i=t?4:2,a=.8*x+45*Math.cos(o),y=u+45*Math.sin(o),h=.8*x+Math.cos(o)*(45+l),n=u+Math.sin(o)*(45+l),s=e.Path.Make();return s.moveTo(a,y),s.lineTo(h,n),F(d,{path:s,style:"stroke",strokeWidth:i,color:"#FFD700",opacity:.75},c)}),F(a,{cx:.8*x,cy:u,r:60,opacity:.1,children:F(l,{start:y(-60,-60),end:y(60,60),colors:["#FFD700","transparent"]})})]}),s(h,{children:[s(h,{children:[F(a,{cx:.12*x,cy:B,r:16,color:"#F0F8FF",opacity:.35,children:F(n,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),F(a,{cx:.18*x,cy:B-(.24*p-.21*p),r:22,color:"#FFFFFF",opacity:.45,children:F(n,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),F(a,{cx:.25*x,cy:B-(.24*p-.19*p),r:26,color:"#F8F8FF",opacity:.55,children:F(n,{dx:2,dy:3,blur:12,color:"rgba(0,0,0,0.06)"})}),F(a,{cx:.32*x,cy:B-(.24*p-.21*p),r:20,color:"#FFFFFF",opacity:.5,children:F(n,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.05)"})}),F(a,{cx:.37*x,cy:B,r:14,color:"#F0F8FF",opacity:.4,children:F(n,{dx:1,dy:2,blur:12,color:"rgba(0,0,0,0.04)"})})]}),s(h,{children:[F(a,{cx:.62*x,cy:D,r:15,color:"#F0F8FF",opacity:.4,children:F(n,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.04)"})}),F(a,{cx:.69*x,cy:D-(.27*p-.24*p),r:19,color:"#FFFFFF",opacity:.5,children:F(n,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.05)"})}),F(a,{cx:.76*x,cy:D-(.27*p-.26*p),r:13,color:"#F8F8FF",opacity:.45,children:F(n,{dx:1,dy:2,blur:10,color:"rgba(0,0,0,0.04)"})})]})]}),s(d,{path:E,children:[F(l,{start:y(0,g-20),end:y(0,g+20),colors:["#32CD32","#228B22","#006400"]}),F(n,{dx:0,dy:-2,blur:4,color:"rgba(0,0,0,0.2)"})]}),Array.from({length:25},(r,c)=>{const o=x/25*c+c%3*5,t=15+c%4*5,l=c%3,i=e.Path.Make();if(i.moveTo(o,g),0===l)return i.quadTo(o+1,g-.6*t,o+1,g-t),F(d,{path:i,style:"stroke",strokeWidth:1.5,color:"#16a34a",opacity:.8},"grass-"+c);if(1===l)return i.quadTo(o+2,g-.4*t,o+3,g-t),F(d,{path:i,style:"stroke",strokeWidth:2,color:"#15803d",opacity:.7},"grass-"+c);{const r=.5*t;return i.quadTo(o+1,g-r,o+2,g-t),F(d,{path:i,style:"stroke",strokeWidth:2.5,color:"#166534",opacity:.6},"grass-"+c)}}),s(h,{children:[s(h,{children:[s(i,{x:.05*x,y:g-150,width:20,height:150,children:[F(l,{start:y(0,0),end:y(20,0),colors:["#654321","#8B4513","#A0522D","#8B4513"]}),F(n,{dx:3,dy:0,blur:6,color:"rgba(0,0,0,0.4)"})]}),F(a,{cx:.05*x+10,cy:g-150,r:38,color:"#1B5E20",opacity:.9,children:F(n,{dx:4,dy:4,blur:10,color:"rgba(0,0,0,0.3)"})}),F(a,{cx:.05*x+18,cy:g-180,r:32,color:"#2E7D32",opacity:.8}),F(a,{cx:.05*x+25,cy:g-140,r:28,color:"#388E3C",opacity:.7}),F(a,{cx:.05*x+2,cy:g-120,r:30,color:"#2E7D32",opacity:.75})]}),s(h,{children:[s(i,{x:.88*x,y:g-200,width:18,height:200,children:[F(l,{start:y(0,0),end:y(18,0),colors:["#4A2C2A","#654321","#8B4513","#654321"]}),F(n,{dx:-3,dy:0,blur:6,color:"rgba(0,0,0,0.4)"})]}),F(a,{cx:.88*x+9,cy:g-120,r:35,color:"#0D4F0C",opacity:.95,children:F(n,{dx:-4,dy:4,blur:10,color:"rgba(0,0,0,0.3)"})}),F(a,{cx:.88*x+9,cy:g-180,r:32,color:"#1B5E20",opacity:.9}),F(a,{cx:.88*x+9,cy:g-240,r:28,color:"#2E7D32",opacity:.9})]})]}),s(h,{children:[s(h,{children:[F(a,{cx:.15*x,cy:g-60,r:26,color:"#1B5E20",opacity:.95,children:F(n,{dx:3,dy:3,blur:8,color:"rgba(0,0,0,0.3)"})}),F(a,{cx:.18*x,cy:g-90,r:22,color:"#2E7D32",opacity:.9}),F(a,{cx:.21*x,cy:g-70,r:18,color:"#388E3C",opacity:.8})]}),s(h,{children:[F(a,{cx:.72*x,cy:g-35,r:28,color:"#0D4F0C",opacity:.95,children:F(n,{dx:-2,dy:3,blur:8,color:"rgba(0,0,0,0.3)"})}),F(a,{cx:.75*x,cy:g-70,r:24,color:"#1B5E20",opacity:.9}),F(a,{cx:.69*x,cy:g-25,r:22,color:"#2E7D32",opacity:.85})]})]}),F(h,{children:m.map((r,c)=>{const o=["#FF6B6B","#FF69B4","#9370DB","#4169E1","#FF8C00","#32CD32"],t=o[c%o.length],l=3+c%3;return s(h,{children:[F(a,{cx:r.x,cy:r.y,r:l,color:t,opacity:.8,children:F(n,{dx:0,dy:1,blur:1,color:"rgba(0,0,0,0.2)"})}),F(a,{cx:r.x,cy:r.y,r:.4*l,color:"#FFD700",opacity:.9})]},"flower-"+c)})})]}),r]})};GameBackground.displayName="GameBackground";
@@ -1 +1 @@
1
- "use strict";import React,{useMemo}from 'react';import{View,Text,StyleSheet}from 'react-native';import{useTime}from "../FruitMergerStore.js";import{FRUIT_MERGER_COLORS}from "../FruitMergerConstants.js";import{formatTime}from "../FruitMergerService.js";import{ScoreBoardContainer}from "../../../helpers/index.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const ScoreBoard = React.memo(({offset = 0})=>{const time = useTime();const formattedTime = useMemo(()=> formatTime(time),[time]);return _jsx(ScoreBoardContainer,{offset:offset,backgroundColor:FRUIT_MERGER_COLORS.SCORE_BACKGROUND,borderColor:FRUIT_MERGER_COLORS.SCORE_BORDER,children:_jsx(View,{style:styles.scoreBoard,children:_jsxs(View,{style:styles.scoreItem,children:[_jsx(Text,{style:styles.scoreLabel,children:"TIME"}),_jsx(Text,{style:styles.scoreValue,children:formattedTime})]})})});});const styles = StyleSheet.create({scoreBoard:{flexDirection:'row',justifyContent:'space-around',alignItems:'center'},scoreItem:{alignItems:'center',flex:1},scoreLabel:{fontSize:16,fontWeight:'700',color:FRUIT_MERGER_COLORS.SCORE_TEXT,marginBottom:4,letterSpacing:1},scoreValue:{fontSize:25,fontWeight:'900',color:FRUIT_MERGER_COLORS.SCORE_TEXT}});ScoreBoard.displayName = 'ScoreBoard';
1
+ "use strict";import r,{useMemo as e}from"react";import{View as o,Text as t,StyleSheet as s}from"react-native";import{useTime as i}from"../FruitMergerStore.js";import{FRUIT_MERGER_COLORS as n}from"../FruitMergerConstants.js";import{formatTime as c}from"../FruitMergerService.js";import{ScoreBoardContainer as l}from"../../../helpers/index.js";import{jsx as a,jsxs as m}from"react/jsx-runtime";export const ScoreBoard=r.memo(({offset:r=0})=>{const s=i(),d=e(()=>c(s),[s]);return a(l,{offset:r,backgroundColor:n.SCORE_BACKGROUND,borderColor:n.SCORE_BORDER,children:a(o,{style:f.scoreBoard,children:m(o,{style:f.scoreItem,children:[a(t,{style:f.scoreLabel,children:"TIME"}),a(t,{style:f.scoreValue,children:d})]})})})});const f=s.create({scoreBoard:{flexDirection:"row",justifyContent:"space-around",alignItems:"center"},scoreItem:{alignItems:"center",flex:1},scoreLabel:{fontSize:16,fontWeight:"700",color:n.SCORE_TEXT,marginBottom:4,letterSpacing:1},scoreValue:{fontSize:25,fontWeight:"900",color:n.SCORE_TEXT}});ScoreBoard.displayName="ScoreBoard";
@@ -1 +1 @@
1
- "use strict";export{ScoreBoard}from "./ScoreBoard.js";export{FruitItem}from "./FruitItem.js";export{GameArea}from "./GameArea.js";export{GameBackground}from "./GameBackground.js";
1
+ "use strict";export{ScoreBoard}from"./ScoreBoard.js";export{FruitItem}from"./FruitItem.js";export{GameArea}from"./GameArea.js";export{GameBackground}from"./GameBackground.js";
@@ -1 +1 @@
1
- "use strict";import React,{useEffect,useRef}from 'react';import{View,StyleSheet}from 'react-native';import{GestureHandlerRootView}from 'react-native-gesture-handler';import{useFruitNinjaStore,registerGameService}from "./FruitNinjaStore.js";import{createFruitNinjaService,getDifficultySpawnInterval}from "./FruitNinjaService.js";import{ScoreBoard,GameArea,GameBackground}from "./components/index.js";import{GameControlButton,GameOverModal,useAnimationTrackers,useGameErrorHandler,GameErrorType}from "../../helpers/index.js";import{GameSettingsModal}from "../../helpers/index.js";import{GAME_IDS,DEFAULT_GAME_SETTINGS}from "../../services/UtilsService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const FruitNinja =({settings,onSettingsChange,onEndGame})=>{const isPlaying = useFruitNinjaStore(state => state.isPlaying);const gameOver = useFruitNinjaStore(state => state.gameOver);const score = useFruitNinjaStore(state => state.score);const startGame = useFruitNinjaStore(state => state.startGame);const stopGame = useFruitNinjaStore(state => state.stopGame);const resetGame = useFruitNinjaStore(state => state.resetGame);const decrementTime = useFruitNinjaStore(state => state.decrementTime);const addFruit = useFruitNinjaStore(state => state.addFruit);const removeFruit = useFruitNinjaStore(state => state.removeFruit);const updateFruit = useFruitNinjaStore(state => state.updateFruit);const gameReportedRef = useRef(false);const reportGameEnd = status =>{if(onEndGame && !gameReportedRef.current){const currentState = useFruitNinjaStore.getState();onEndGame({status,score:currentState.score.toString()});gameReportedRef.current = true;}};const{fruitAnimations,particleAnimations}= useAnimationTrackers(['fruitAnimations','particleAnimations']);const{wrapFunction,safeExecute}= useGameErrorHandler(GAME_IDS.FRUIT_NINJA);const gameService = useRef(safeExecute(()=> createFruitNinjaService(fruitAnimations,particleAnimations),createFruitNinjaService(fruitAnimations,particleAnimations),GameErrorType.INITIALIZATION_ERROR)).current;useEffect(()=>{registerGameService(gameService);return()=>{handleResetGame();};},[gameService]);useEffect(()=>{if(gameOver && !gameReportedRef.current){const currentState = useFruitNinjaStore.getState();const status = currentState.lives <= 0 ? 'lose':'win';reportGameEnd(status);}if(!gameOver){gameReportedRef.current = false;}},[gameOver]);const handleStartGame = wrapFunction(()=>{gameReportedRef.current = false;startGame();if(gameService){gameService.resetAnimations();gameService.startGameTimer(decrementTime);gameService.startFruitSpawning(spawnFruit,getDifficultySpawnInterval(settings?.difficulty || 'medium'));gameService.startPhysicsLoop(updateFruit,removeFruit,()=> useFruitNinjaStore.getState().fruits);}},GameErrorType.INITIALIZATION_ERROR);const handleStopGame = wrapFunction(()=>{reportGameEnd('cancel');stopGame();if(gameService){gameService.cleanup();gameService.resetAnimations();}},GameErrorType.UNKNOWN_ERROR);const handleResetGame = wrapFunction(()=>{if(gameService){gameService.cleanup();}resetGame();if(gameService){gameService.resetAnimations();}},GameErrorType.UNKNOWN_ERROR);const spawnFruit = fruit =>{addFruit(fruit);};const offset = settings?.offset ?? 0;return _jsx(View,{style:styles.container,children:_jsx(GestureHandlerRootView,{children:_jsxs(GameBackground,{offset:offset,children:[_jsx(GameArea,{}),_jsx(ScoreBoard,{offset:offset}),_jsx(GameControlButton,{isPlaying:isPlaying,gameOver:gameOver,onStartGame:handleStartGame,onStopGame:handleStopGame,startButtonText:"START SLICING",stopButtonText:"STOP GAME",startButtonSubtext:"Slice fruits to score!",stopButtonSubtext:"End current game",startButtonColor:"#10b981",stopButtonColor:"#dc2626",startButtonBorderColor:"#34d399",stopButtonBorderColor:"#f87171"}),_jsx(GameOverModal,{isVisible:gameOver,score:score,onPlayAgain:handleResetGame,buttonText:"Slice Again!",primaryColor:"rgba(16,185,129,0.5)",borderColor:"rgba(16,185,129,0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#10b981"}),_jsx(GameSettingsModal,{gameId:GAME_IDS.FRUIT_NINJA,settings:settings || DEFAULT_GAME_SETTINGS,onSettingsChange:onSettingsChange})]})})});};const styles = StyleSheet.create({container:{flex:1}});
1
+ "use strict";import t,{useEffect as o,useRef as r}from"react";import{View as e,StyleSheet as n}from"react-native";import{GestureHandlerRootView as i}from"react-native-gesture-handler";import{useFruitNinjaStore as s,registerGameService as a}from"./FruitNinjaStore.js";import{createFruitNinjaService as m,getDifficultySpawnInterval as f}from"./FruitNinjaService.js";import{ScoreBoard as c,GameArea as u,GameBackground as l}from"./components/index.js";import{GameControlButton as p,GameOverModal as d,useAnimationTrackers as g,useGameErrorHandler as x,GameErrorType as S}from"../../helpers/index.js";import{GameSettingsModal as b}from"../../helpers/index.js";import{GAME_IDS as j,DEFAULT_GAME_SETTINGS as C}from"../../services/UtilsService.js";import{jsx as B,jsxs as h}from"react/jsx-runtime";export const FruitNinja=({settings:t,onSettingsChange:n,onEndGame:T})=>{const v=s(t=>t.isPlaying),G=s(t=>t.gameOver),y=s(t=>t.score),E=s(t=>t.startGame),F=s(t=>t.stopGame),N=s(t=>t.resetGame),I=s(t=>t.decrementTime),P=s(t=>t.addFruit),w=s(t=>t.removeFruit),O=s(t=>t.updateFruit),L=r(!1),M=t=>{if(T&&!L.current){const o=s.getState();T({status:t,score:o.score.toString()}),L.current=!0}},{fruitAnimations:R,particleAnimations:U}=g(["fruitAnimations","particleAnimations"]),{wrapFunction:V,safeExecute:k}=x(j.FRUIT_NINJA),q=r(k(()=>m(R,U),m(R,U),S.INITIALIZATION_ERROR)).current;o(()=>(a(q),()=>{H()}),[q]),o(()=>{if(G&&!L.current){const t=s.getState().lives>0?"win":"lose";M(t)}G||(L.current=!1)},[G]);const z=V(()=>{L.current=!1,E(),q&&(q.resetAnimations(),q.startGameTimer(I),q.startFruitSpawning(J,f(t?.difficulty||"medium")),q.startPhysicsLoop(O,w,()=>s.getState().fruits))},S.INITIALIZATION_ERROR),D=V(()=>{M("cancel"),F(),q&&(q.cleanup(),q.resetAnimations())},S.UNKNOWN_ERROR),H=V(()=>{q&&q.cleanup(),N(),q&&q.resetAnimations()},S.UNKNOWN_ERROR),J=t=>{P(t)},K=t?.offset??0;return B(e,{style:A.container,children:B(i,{children:h(l,{offset:K,children:[B(u,{}),B(c,{offset:K}),B(p,{isPlaying:v,gameOver:G,onStartGame:z,onStopGame:D,startButtonText:"START SLICING",stopButtonText:"STOP GAME",startButtonSubtext:"Slice fruits to score!",stopButtonSubtext:"End current game",startButtonColor:"#10b981",stopButtonColor:"#dc2626",startButtonBorderColor:"#34d399",stopButtonBorderColor:"#f87171"}),B(d,{isVisible:G,score:y,onPlayAgain:H,buttonText:"Slice Again!",primaryColor:"rgba(16, 185, 129, 0.5)",borderColor:"rgba(16, 185, 129, 0.5)",buttonColor:"#ffffff",buttonBorderColor:"#ffffff",buttonTextColor:"#10b981"}),B(b,{gameId:j.FRUIT_NINJA,settings:t||C,onSettingsChange:n})]})})})};const A=n.create({container:{flex:1}});
@@ -1 +1 @@
1
- "use strict";export const FRUIT_NINJA_GAME_CONFIG ={GAME_DURATION:60,MAX_LIVES:3,FRUIT_SPAWN_INTERVAL:1500,FRUIT_FALL_SPEED:2,GRAVITY:0.12,INITIAL_VELOCITY_Y:-10,INITIAL_VELOCITY_X_RANGE:1.5,FRUIT_SIZE:60,SLICE_TRAIL_LENGTH:10,SLICE_TRAIL_WIDTH:8,COMBO_TIMEOUT:2000,PARTICLE_COUNT:6,PARTICLE_LIFETIME:1000};export const FRUIT_NINJA_ANIMATION_CONFIG ={FRUIT_SPRING:{damping:15,stiffness:200},SLICE_SPRING:{damping:20,stiffness:300},PARTICLE_SPRING:{damping:25,stiffness:400}};export const FRUIT_NINJA_COLORS ={BACKGROUND:'#87CEEB',SCORE_BOARD:'#4169E1',SLICE_TRAIL:'#FFD700',PARTICLE_COLORS:['#FF6B6B','#4ECDC4','#45B7D1','#96CEB4','#FFEAA7','#DDA0DD'],PARTICLE_GLOW:'rgba(255,255,255,0.9)',COMBO_TEXT:'#FF4757',LIVES_COLOR:'#FF6B6B',SCORE:'#FFFFFF',UI:'#FF6347',BUTTON_ACTIVE:'#FF7F50',BUTTON_INACTIVE:'#FFA07A',TEXT_PRIMARY:'#FFFFFF',TEXT_SECONDARY:'#F0F8FF',WHITE:'#FFFFFF',START_BUTTON:'#4CAF50',GAME_OVER_OVERLAY:'rgba(0,0,0,0.85)'};export const FRUIT_TYPES = [{emoji:'🍎',points:10,name:'apple'},{emoji:'🍊',points:10,name:'orange'},{emoji:'🍌',points:15,name:'banana'},{emoji:'🍇',points:20,name:'grapes'},{emoji:'🥝',points:25,name:'kiwi'},{emoji:'🍓',points:15,name:'strawberry'},{emoji:'🥭',points:30,name:'mango'},{emoji:'🍑',points:25,name:'cherry'},{emoji:'🍒',points:35,name:'cherries'},{emoji:'🥥',points:40,name:'coconut'}];export const FRUIT_SPECIAL_ITEMS = [{emoji:'💣',points:-50,name:'bomb',isBomb:true},{emoji:'⭐',points:50,name:'star',isBonus:true},{emoji:'❄️',points:0,name:'freeze',isFreeze:true}];export const FRUIT_NINJA_THEME ={backgroundColor:'rgba(0,0,0,0.7)',headerBackgroundColor:'#10b981',headerTextColor:'#ffffff',sectionBackgroundColor:'rgba(16,185,129,0.15)',sectionTitleColor:'#10b981',buttonSelectedColor:'#10b981',buttonUnselectedColor:'rgba(255,255,255,0.2)',buttonSelectedTextColor:'#ffffff',buttonUnselectedTextColor:'rgba(255,255,255,0.7)',switchTrackColorFalse:'rgba(16,185,129,0.3)',switchTrackColorTrue:'#10b981',switchThumbColor:'#ffffff',infoTextColor:'rgba(255,255,255,0.9)'};export const FRUIT_NINJA_DIFFICULTY_DESCRIPTIONS ={easy:'Fruits spawn slower,more forgiving',medium:'Balanced gameplay and fruit speed',hard:'Fast fruits,quick reflexes needed!'};
1
+ "use strict";export const FRUIT_NINJA_GAME_CONFIG={GAME_DURATION:60,MAX_LIVES:3,FRUIT_SPAWN_INTERVAL:1500,FRUIT_FALL_SPEED:2,GRAVITY:.12,INITIAL_VELOCITY_Y:-10,INITIAL_VELOCITY_X_RANGE:1.5,FRUIT_SIZE:60,SLICE_TRAIL_LENGTH:10,SLICE_TRAIL_WIDTH:8,COMBO_TIMEOUT:2e3,PARTICLE_COUNT:6,PARTICLE_LIFETIME:1e3};export const FRUIT_NINJA_ANIMATION_CONFIG={FRUIT_SPRING:{damping:15,stiffness:200},SLICE_SPRING:{damping:20,stiffness:300},PARTICLE_SPRING:{damping:25,stiffness:400}};export const FRUIT_NINJA_COLORS={BACKGROUND:"#87CEEB",SCORE_BOARD:"#4169E1",SLICE_TRAIL:"#FFD700",PARTICLE_COLORS:["#FF6B6B","#4ECDC4","#45B7D1","#96CEB4","#FFEAA7","#DDA0DD"],PARTICLE_GLOW:"rgba(255,255,255,0.9)",COMBO_TEXT:"#FF4757",LIVES_COLOR:"#FF6B6B",SCORE:"#FFFFFF",UI:"#FF6347",BUTTON_ACTIVE:"#FF7F50",BUTTON_INACTIVE:"#FFA07A",TEXT_PRIMARY:"#FFFFFF",TEXT_SECONDARY:"#F0F8FF",WHITE:"#FFFFFF",START_BUTTON:"#4CAF50",GAME_OVER_OVERLAY:"rgba(0, 0, 0, 0.85)"};export const FRUIT_TYPES=[{emoji:"\ud83c\udf4e",points:10,name:"apple"},{emoji:"\ud83c\udf4a",points:10,name:"orange"},{emoji:"\ud83c\udf4c",points:15,name:"banana"},{emoji:"\ud83c\udf47",points:20,name:"grapes"},{emoji:"\ud83e\udd5d",points:25,name:"kiwi"},{emoji:"\ud83c\udf53",points:15,name:"strawberry"},{emoji:"\ud83e\udd6d",points:30,name:"mango"},{emoji:"\ud83c\udf51",points:25,name:"cherry"},{emoji:"\ud83c\udf52",points:35,name:"cherries"},{emoji:"\ud83e\udd65",points:40,name:"coconut"}];export const FRUIT_SPECIAL_ITEMS=[{emoji:"\ud83d\udca3",points:-50,name:"bomb",isBomb:!0},{emoji:"\u2b50",points:50,name:"star",isBonus:!0},{emoji:"\u2744\ufe0f",points:0,name:"freeze",isFreeze:!0}];export const FRUIT_NINJA_THEME={backgroundColor:"rgba(0, 0, 0, 0.7)",headerBackgroundColor:"#10b981",headerTextColor:"#ffffff",sectionBackgroundColor:"rgba(16, 185, 129, 0.15)",sectionTitleColor:"#10b981",buttonSelectedColor:"#10b981",buttonUnselectedColor:"rgba(255, 255, 255, 0.2)",buttonSelectedTextColor:"#ffffff",buttonUnselectedTextColor:"rgba(255, 255, 255, 0.7)",switchTrackColorFalse:"rgba(16, 185, 129, 0.3)",switchTrackColorTrue:"#10b981",switchThumbColor:"#ffffff",infoTextColor:"rgba(255, 255, 255, 0.9)"};export const FRUIT_NINJA_DIFFICULTY_DESCRIPTIONS={easy:"Fruits spawn slower, more forgiving",medium:"Balanced gameplay and fruit speed",hard:"Fast fruits, quick reflexes needed!"};
@@ -1 +1 @@
1
- "use strict";import{Dimensions}from 'react-native';import{FRUIT_NINJA_GAME_CONFIG as GAME_CONFIG,FRUIT_NINJA_ANIMATION_CONFIG as ANIMATION_CONFIG,FRUIT_NINJA_COLORS as COLORS,FRUIT_TYPES,FRUIT_SPECIAL_ITEMS as SPECIAL_ITEMS}from "./FruitNinjaConstants.js";const{width,height}= Dimensions.get('window');export{GAME_CONFIG,ANIMATION_CONFIG,COLORS,FRUIT_TYPES,SPECIAL_ITEMS};export const DEFAULT_FRUIT_NINJA_SETTINGS ={gameDuration:60,difficulty:'medium',soundEnabled:true,hapticEnabled:true};export const getDifficultySpawnInterval = difficulty =>{const intervals ={easy:2000,medium:1500,hard:1000};return intervals[difficulty];};export class FruitNinjaService{gameTimerRef = null;fruitSpawnRef = null;animationRef = null;comboTimeoutRef = null;particles = [];constructor(fruitAnimations,particleAnimations){this.fruitAnimations = fruitAnimations;this.particleAnimations = particleAnimations;}startGameTimer(onTick){this.clearGameTimer();this.gameTimerRef = setInterval(onTick,1000);}startFruitSpawning(spawnFruit,customSpawnInterval){this.clearFruitSpawn();const spawnInterval = customSpawnInterval || GAME_CONFIG.FRUIT_SPAWN_INTERVAL;const spawnLoop =()=>{const fruit = this.createRandomFruit();spawnFruit(fruit);const nextSpawnTime = spawnInterval +(Math.random()- 0.5)* 400;this.fruitSpawnRef = setTimeout(spawnLoop,Math.max(300,nextSpawnTime));};this.fruitSpawnRef = setTimeout(spawnLoop,1000);}startPhysicsLoop(updateFruit,removeFruit,getFruits){const updateLoop =()=>{const fruits = getFruits();fruits.forEach(fruit =>{if(fruit.isSliced)return;const newVelocityY = fruit.velocity.y + GAME_CONFIG.GRAVITY;let newX = fruit.position.x + fruit.velocity.x;const newY = fruit.position.y + newVelocityY;const newRotation = fruit.rotation + fruit.rotationSpeed;if(newY > height + 100){removeFruit(fruit.id);return;}let newVelocityX = fruit.velocity.x;if(newY < -50){const adjustedVelocityY = Math.max(newVelocityY,0.5);updateFruit(fruit.id,{position:{x:newX,y:-50},velocity:{x:newVelocityX,y:adjustedVelocityY},rotation:newRotation});return;}if(newX <= 0){newX = 0;newVelocityX = Math.abs(newVelocityX)* 0.8;}if(newX >= width - GAME_CONFIG.FRUIT_SIZE){newX = width - GAME_CONFIG.FRUIT_SIZE;newVelocityX = -Math.abs(newVelocityX)* 0.8;}updateFruit(fruit.id,{position:{x:newX,y:newY},velocity:{x:newVelocityX,y:newVelocityY},rotation:newRotation});});this.updateParticles();this.animationRef = requestAnimationFrame(updateLoop);};this.animationRef = requestAnimationFrame(updateLoop);}createRandomFruit(){const isSpecial = Math.random()< 0.5;const type = isSpecial ? SPECIAL_ITEMS[Math.floor(Math.random()* SPECIAL_ITEMS.length)]:FRUIT_TYPES[Math.floor(Math.random()* FRUIT_TYPES.length)];if(!type){const defaultType = FRUIT_TYPES[0];const spawnX = Math.random()*(width - GAME_CONFIG.FRUIT_SIZE);const spawnY = height - 50;const velocityX =(Math.random()- 0.5)* GAME_CONFIG.INITIAL_VELOCITY_X_RANGE;const velocityY = GAME_CONFIG.INITIAL_VELOCITY_Y - 3;return{id:`fruit_${Date.now()}_${Math.random()}`,type:defaultType,position:{x:spawnX,y:spawnY},velocity:{x:velocityX,y:velocityY},rotation:0,rotationSpeed:(Math.random()- 0.5)* 0.2,isSliced:false,spawnTime:Date.now(),isBomb:Boolean(defaultType.isBomb),isBonus:Boolean(defaultType.isBonus),isFreeze:Boolean(defaultType.isFreeze)};}const spawnX = Math.random()*(width - GAME_CONFIG.FRUIT_SIZE);const spawnY = height - 50;const velocityX =(Math.random()- 0.5)* GAME_CONFIG.INITIAL_VELOCITY_X_RANGE;const velocityY = GAME_CONFIG.INITIAL_VELOCITY_Y - 3 +(Math.random()- 0.5)* 2;return{id:`fruit_${Date.now()}_${Math.random()}`,type,position:{x:spawnX,y:spawnY},velocity:{x:velocityX,y:velocityY},rotation:0,rotationSpeed:(Math.random()- 0.5)* 0.2,isSliced:false,spawnTime:Date.now(),isBomb:Boolean(type.isBomb),isBonus:Boolean(type.isBonus),isFreeze:Boolean(type.isFreeze)};}createSliceParticles(fruit){const particles = [];for(let i = 0;i < GAME_CONFIG.PARTICLE_COUNT;i++){const angle = i / GAME_CONFIG.PARTICLE_COUNT * Math.PI * 2;const speed = 3 + Math.random()* 4;particles.push({id:`particle_${fruit.id}_${Date.now()}_${Math.random()}_${i}`,position:{...fruit.position},velocity:{x:Math.cos(angle)* speed,y:Math.sin(angle)* speed - 2},color:COLORS.PARTICLE_COLORS[Math.floor(Math.random()* COLORS.PARTICLE_COLORS.length)] || '#ff6b6b',size:4 + Math.random()* 6,lifetime:0,maxLifetime:GAME_CONFIG.PARTICLE_LIFETIME});}this.particles.push(...particles);return particles;}updateParticles(){this.particles = this.particles.filter(particle =>{particle.lifetime += 16;if(particle.lifetime >= particle.maxLifetime){return false;}particle.position.x += particle.velocity.x;particle.position.y += particle.velocity.y;particle.velocity.y += 0.1;return true;});}getParticles(){return this.particles;}checkFruitSlice(fruit,slicePath){if(fruit.isSliced || slicePath.length < 2)return false;const fruitCenter ={x:fruit.position.x + GAME_CONFIG.FRUIT_SIZE / 2,y:fruit.position.y + GAME_CONFIG.FRUIT_SIZE / 2};const fruitRadius = GAME_CONFIG.FRUIT_SIZE / 2;for(let i = 0;i < slicePath.length - 1;i++){const p1 = slicePath[i];const p2 = slicePath[i + 1];if(!p1 || !p2)continue;const distance = this.distanceFromPointToLine(fruitCenter,p1,p2);if(distance <= fruitRadius){return true;}}return false;}distanceFromPointToLine(point,lineStart,lineEnd){const A = point.x - lineStart.x;const B = point.y - lineStart.y;const C = lineEnd.x - lineStart.x;const D = lineEnd.y - lineStart.y;const dot = A * C + B * D;const lenSq = C * C + D * D;if(lenSq === 0)return Math.sqrt(A * A + B * B);let param = dot / lenSq;param = Math.max(0,Math.min(1,param));const xx = lineStart.x + param * C;const yy = lineStart.y + param * D;const dx = point.x - xx;const dy = point.y - yy;return Math.sqrt(dx * dx + dy * dy);}resetAnimations(){this.fruitAnimations.clear();this.particleAnimations.clear();this.particles = [];}cleanup(){this.clearGameTimer();this.clearFruitSpawn();this.clearAnimationLoop();this.clearComboTimeout();this.particles = [];}clearGameTimer(){if(this.gameTimerRef){clearInterval(this.gameTimerRef);this.gameTimerRef = null;}}clearFruitSpawn(){if(this.fruitSpawnRef){clearTimeout(this.fruitSpawnRef);this.fruitSpawnRef = null;}}clearAnimationLoop(){if(this.animationRef){cancelAnimationFrame(this.animationRef);this.animationRef = null;}}clearComboTimeout(){if(this.comboTimeoutRef){clearTimeout(this.comboTimeoutRef);this.comboTimeoutRef = null;}}}export const createFruitNinjaService =(fruitAnimations,particleAnimations)=>{return new FruitNinjaService(fruitAnimations,particleAnimations);};
1
+ "use strict";import{Dimensions as t}from"react-native";import{FRUIT_NINJA_GAME_CONFIG as i,FRUIT_NINJA_ANIMATION_CONFIG as e,FRUIT_NINJA_COLORS as a,FRUIT_TYPES as s,FRUIT_SPECIAL_ITEMS as o}from"./FruitNinjaConstants.js";const{width:n,height:r}=t.get("window");export{i as GAME_CONFIG,e as ANIMATION_CONFIG,a as COLORS,s as FRUIT_TYPES,o as SPECIAL_ITEMS};export const DEFAULT_FRUIT_NINJA_SETTINGS={gameDuration:60,difficulty:"medium",soundEnabled:!0,hapticEnabled:!0};export const getDifficultySpawnInterval=t=>({easy:2e3,medium:1500,hard:1e3}[t]);export class FruitNinjaService{gameTimerRef=null;fruitSpawnRef=null;animationRef=null;comboTimeoutRef=null;particles=[];constructor(t,i){this.fruitAnimations=t,this.particleAnimations=i}startGameTimer(t){this.clearGameTimer(),this.gameTimerRef=setInterval(t,1e3)}startFruitSpawning(t,e){this.clearFruitSpawn();const a=e||i.FRUIT_SPAWN_INTERVAL,s=()=>{const i=this.createRandomFruit();t(i);const e=a+400*(Math.random()-.5);this.fruitSpawnRef=setTimeout(s,Math.max(300,e))};this.fruitSpawnRef=setTimeout(s,1e3)}startPhysicsLoop(t,e,a){const s=()=>{a().forEach(a=>{if(a.isSliced)return;const s=a.velocity.y+i.GRAVITY;let o=a.position.x+a.velocity.x;const h=a.position.y+s,c=a.rotation+a.rotationSpeed;if(h>r+100)return void e(a.id);let l=a.velocity.x;if(-50>h){const i=Math.max(s,.5);return void t(a.id,{position:{x:o,y:-50},velocity:{x:l,y:i},rotation:c})}o>0||(o=0,l=.8*Math.abs(l)),o<n-i.FRUIT_SIZE||(o=n-i.FRUIT_SIZE,l=.8*-Math.abs(l)),t(a.id,{position:{x:o,y:h},velocity:{x:l,y:s},rotation:c})}),this.updateParticles(),this.animationRef=requestAnimationFrame(s)};this.animationRef=requestAnimationFrame(s)}createRandomFruit(){const t=.5>Math.random()?o[Math.floor(Math.random()*o.length)]:s[Math.floor(Math.random()*s.length)];if(!t){const t=s[0],e=Math.random()*(n-i.FRUIT_SIZE),a=r-50,o=(Math.random()-.5)*i.INITIAL_VELOCITY_X_RANGE,h=i.INITIAL_VELOCITY_Y-3;return{id:`fruit_${Date.now()}_${Math.random()}`,type:t,position:{x:e,y:a},velocity:{x:o,y:h},rotation:0,rotationSpeed:.2*(Math.random()-.5),isSliced:!1,spawnTime:Date.now(),isBomb:!!t.isBomb,isBonus:!!t.isBonus,isFreeze:!!t.isFreeze}}const e=Math.random()*(n-i.FRUIT_SIZE),a=r-50,h=(Math.random()-.5)*i.INITIAL_VELOCITY_X_RANGE,c=i.INITIAL_VELOCITY_Y-3+2*(Math.random()-.5);return{id:`fruit_${Date.now()}_${Math.random()}`,type:t,position:{x:e,y:a},velocity:{x:h,y:c},rotation:0,rotationSpeed:.2*(Math.random()-.5),isSliced:!1,spawnTime:Date.now(),isBomb:!!t.isBomb,isBonus:!!t.isBonus,isFreeze:!!t.isFreeze}}createSliceParticles(t){const e=[];for(let s=0;s<i.PARTICLE_COUNT;s++){const o=s/i.PARTICLE_COUNT*Math.PI*2,n=3+4*Math.random();e.push({id:`particle_${t.id}_${Date.now()}_${Math.random()}_${s}`,position:{...t.position},velocity:{x:Math.cos(o)*n,y:Math.sin(o)*n-2},color:a.PARTICLE_COLORS[Math.floor(Math.random()*a.PARTICLE_COLORS.length)]||"#ff6b6b",size:4+6*Math.random(),lifetime:0,maxLifetime:i.PARTICLE_LIFETIME})}return this.particles.push(...e),e}updateParticles(){this.particles=this.particles.filter(t=>(t.lifetime+=16,t.lifetime<t.maxLifetime&&(t.position.x+=t.velocity.x,t.position.y+=t.velocity.y,t.velocity.y+=.1,!0)))}getParticles(){return this.particles}checkFruitSlice(t,e){if(t.isSliced||2>e.length)return!1;const a={x:t.position.x+i.FRUIT_SIZE/2,y:t.position.y+i.FRUIT_SIZE/2},s=i.FRUIT_SIZE/2;for(let t=0;t<e.length-1;t++){const i=e[t],o=e[t+1];if(i&&o&&s>=this.distanceFromPointToLine(a,i,o))return!0}return!1}distanceFromPointToLine(t,i,e){const a=t.x-i.x,s=t.y-i.y,o=e.x-i.x,n=e.y-i.y,r=a*o+s*n,h=o*o+n*n;if(0===h)return Math.sqrt(a*a+s*s);let c=r/h;c=Math.max(0,Math.min(1,c));const l=i.x+c*o,u=i.y+c*n,m=t.x-l,M=t.y-u;return Math.sqrt(m*m+M*M)}resetAnimations(){this.fruitAnimations.clear(),this.particleAnimations.clear(),this.particles=[]}cleanup(){this.clearGameTimer(),this.clearFruitSpawn(),this.clearAnimationLoop(),this.clearComboTimeout(),this.particles=[]}clearGameTimer(){this.gameTimerRef&&(clearInterval(this.gameTimerRef),this.gameTimerRef=null)}clearFruitSpawn(){this.fruitSpawnRef&&(clearTimeout(this.fruitSpawnRef),this.fruitSpawnRef=null)}clearAnimationLoop(){this.animationRef&&(cancelAnimationFrame(this.animationRef),this.animationRef=null)}clearComboTimeout(){this.comboTimeoutRef&&(clearTimeout(this.comboTimeoutRef),this.comboTimeoutRef=null)}}export const createFruitNinjaService=(t,i)=>new FruitNinjaService(t,i);
@@ -1 +1 @@
1
- "use strict";import{create}from 'zustand';import{subscribeWithSelector}from 'zustand/middleware';import{GAME_CONFIG}from "./FruitNinjaService.js";import{immerMiddleware}from "../../services/UtilsService.js";export const useFruitNinjaStore = create()(subscribeWithSelector(immerMiddleware((set,get)=>({score:0,timeLeft:120,isPlaying:false,gameOver:false,combo:0,lives:GAME_CONFIG.MAX_LIVES,fruits:[],slicePath:[],isSlicing:false,startGame:_gameDuration =>{const duration = 120;set(draft =>{draft.score = 0;draft.timeLeft = duration;draft.isPlaying = true;draft.gameOver = false;draft.combo = 0;draft.lives = GAME_CONFIG.MAX_LIVES;draft.fruits = [];draft.slicePath = [];draft.isSlicing = false;});},stopGame:()=>{set(draft =>{draft.score = 0;draft.timeLeft = 120;draft.isPlaying = false;draft.gameOver = false;draft.combo = 0;draft.lives = GAME_CONFIG.MAX_LIVES;draft.fruits = [];draft.slicePath = [];draft.isSlicing = false;});},resetGame:()=>{set(draft =>{draft.score = 0;draft.timeLeft = 120;draft.isPlaying = false;draft.gameOver = false;draft.combo = 0;draft.lives = GAME_CONFIG.MAX_LIVES;draft.fruits = [];draft.slicePath = [];draft.isSlicing = false;});},sliceFruit:fruitId =>{const{fruits,isPlaying}= get();if(!isPlaying)return;let fruitIndex = -1;for(let i = 0;i < fruits.length;i++){if(fruits[i].id === fruitId){fruitIndex = i;break;}}if(fruitIndex === -1 || fruits[fruitIndex].isSliced)return;const points = 10;set(draft =>{draft.score = draft.score + points;if(draft.fruits[fruitIndex]){draft.fruits[fruitIndex].isSliced = true;draft.fruits[fruitIndex].sliceTime = Date.now();}});},updateScore:points =>{set(draft =>{draft.score = draft.score + points;});},decrementTime:()=>{set(draft =>{const newTimeLeft = draft.timeLeft - 1;if(newTimeLeft <= 0){draft.timeLeft = 0;draft.isPlaying = false;draft.gameOver = true;}else{draft.timeLeft = newTimeLeft;}});},addFruit:fruit =>{set(draft =>{draft.fruits.push(fruit);});},removeFruit:fruitId =>{set(draft =>{draft.fruits = draft.fruits.filter(f => f.id !== fruitId);});},updateFruit:(fruitId,updates)=>{set(draft =>{for(let i = 0;i < draft.fruits.length;i++){if(draft.fruits[i].id === fruitId){Object.assign(draft.fruits[i],updates);break;}}});},setSlicePath:path =>{set(draft =>{draft.slicePath = path;});},setIsSlicing:isSlicing =>{set(draft =>{draft.isSlicing = isSlicing;});},incrementCombo:()=>{set(draft =>{draft.combo = draft.combo + 1;});},resetCombo:()=>{set(draft =>{draft.combo = 0;});},loseLife:()=>{set(draft =>{const newLives = Math.max(0,draft.lives - 1);draft.lives = newLives;if(newLives <= 0){draft.isPlaying = false;draft.gameOver = true;}});},addLife:()=>{set(draft =>{draft.lives = Math.min(draft.lives + 1,GAME_CONFIG.MAX_LIVES);});}}))));let gameServiceInstance = null;export const registerGameService = gameService =>{gameServiceInstance = gameService;};useFruitNinjaStore.subscribe(state => state.gameOver,(gameOver,previousGameOver)=>{if(gameOver && !previousGameOver){setTimeout(()=>{if(gameServiceInstance){gameServiceInstance.cleanup();}const state = useFruitNinjaStore.getState();state.fruits.forEach(fruit =>{useFruitNinjaStore.getState().removeFruit(fruit.id);});},50);}});
1
+ "use strict";import{create as e}from"zustand";import{subscribeWithSelector as t}from"zustand/middleware";import{GAME_CONFIG as i}from"./FruitNinjaService.js";import{immerMiddleware as r}from"../../services/UtilsService.js";export const useFruitNinjaStore=e()(t(r((e,t)=>({score:0,timeLeft:120,isPlaying:!1,gameOver:!1,combo:0,lives:i.MAX_LIVES,fruits:[],slicePath:[],isSlicing:!1,startGame:t=>{e(e=>{e.score=0,e.timeLeft=120,e.isPlaying=!0,e.gameOver=!1,e.combo=0,e.lives=i.MAX_LIVES,e.fruits=[],e.slicePath=[],e.isSlicing=!1})},stopGame:()=>{e(e=>{e.score=0,e.timeLeft=120,e.isPlaying=!1,e.gameOver=!1,e.combo=0,e.lives=i.MAX_LIVES,e.fruits=[],e.slicePath=[],e.isSlicing=!1})},resetGame:()=>{e(e=>{e.score=0,e.timeLeft=120,e.isPlaying=!1,e.gameOver=!1,e.combo=0,e.lives=i.MAX_LIVES,e.fruits=[],e.slicePath=[],e.isSlicing=!1})},sliceFruit:i=>{const{fruits:r,isPlaying:s}=t();if(!s)return;let o=-1;for(let e=0;e<r.length;e++)if(r[e].id===i){o=e;break}-1===o||r[o].isSliced||e(e=>{e.score=e.score+10,e.fruits[o]&&(e.fruits[o].isSliced=!0,e.fruits[o].sliceTime=Date.now())})},updateScore:t=>{e(e=>{e.score=e.score+t})},decrementTime:()=>{e(e=>{const t=e.timeLeft-1;t>0?e.timeLeft=t:(e.timeLeft=0,e.isPlaying=!1,e.gameOver=!0)})},addFruit:t=>{e(e=>{e.fruits.push(t)})},removeFruit:t=>{e(e=>{e.fruits=e.fruits.filter(e=>e.id!==t)})},updateFruit:(t,i)=>{e(e=>{for(let r=0;r<e.fruits.length;r++)if(e.fruits[r].id===t){Object.assign(e.fruits[r],i);break}})},setSlicePath:t=>{e(e=>{e.slicePath=t})},setIsSlicing:t=>{e(e=>{e.isSlicing=t})},incrementCombo:()=>{e(e=>{e.combo=e.combo+1})},resetCombo:()=>{e(e=>{e.combo=0})},loseLife:()=>{e(e=>{const t=Math.max(0,e.lives-1);e.lives=t,t>0||(e.isPlaying=!1,e.gameOver=!0)})},addLife:()=>{e(e=>{e.lives=Math.min(e.lives+1,i.MAX_LIVES)})}}))));let s=null;export const registerGameService=e=>{s=e};useFruitNinjaStore.subscribe(e=>e.gameOver,(e,t)=>{e&&!t&&setTimeout(()=>{s&&s.cleanup(),useFruitNinjaStore.getState().fruits.forEach(e=>{useFruitNinjaStore.getState().removeFruit(e.id)})},50)});
@@ -1 +1 @@
1
- "use strict";import React from 'react';import{View,TouchableOpacity,Text,StyleSheet}from 'react-native';import{GAME_CONFIG}from "../FruitNinjaService.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const FruitComponent =({fruit,onSlice})=>{const scale = fruit.isSliced ? 0:1;const opacity = fruit.isSliced ? 0:1;const style ={transform:[{translateX:fruit.position.x},{translateY:fruit.position.y},{rotate:`${fruit.rotation}rad`},{scale}],opacity,position:'absolute',width:GAME_CONFIG.FRUIT_SIZE,height:GAME_CONFIG.FRUIT_SIZE,zIndex:fruit.isBomb ? 10:5};const getBorderColor =()=>{if(fruit.isBomb)return '#ef4444';if(fruit.isBonus)return '#fbbf24';if(fruit.isFreeze)return '#60a5fa';return '#4ade80';};const getBackgroundColor =()=>{if(fruit.isBomb)return '#fee2e2';if(fruit.isBonus)return '#fef3c7';if(fruit.isFreeze)return '#dbeafe';return '#dcfce7';};return _jsx(View,{style:style,children:_jsxs(TouchableOpacity,{style:[styles.fruitButton,{width:GAME_CONFIG.FRUIT_SIZE,height:GAME_CONFIG.FRUIT_SIZE,borderColor:getBorderColor(),backgroundColor:getBackgroundColor()}],onPress:()=> onSlice?.(fruit.id),activeOpacity:0.8,children:[_jsx(Text,{style:styles.fruitEmoji,children:fruit.type.emoji}),(fruit.isBomb || fruit.isBonus || fruit.isFreeze)&& _jsx(View,{style:[styles.glowEffect,{backgroundColor:fruit.isBomb ? '#ff4757':fruit.isBonus ? '#ffa502':'#3742fa',shadowColor:fruit.isBomb ? '#ff4757':fruit.isBonus ? '#ffa502':'#3742fa',shadowOffset:{width:0,height:0},shadowOpacity:0.8,shadowRadius:10,elevation:10}]})]})});};const styles = StyleSheet.create({fruitButton:{justifyContent:'center',alignItems:'center',borderRadius:50,borderWidth:2,shadowColor:'#000',shadowOffset:{width:0,height:4},shadowOpacity:0.3,shadowRadius:8,elevation:8},fruitEmoji:{fontSize:32},glowEffect:{position:'absolute',top:0,left:0,right:0,bottom:0,borderRadius:50,opacity:0.3}});
1
+ "use strict";import t from"react";import{View as e,TouchableOpacity as o,Text as r,StyleSheet as a}from"react-native";import{GAME_CONFIG as i}from"../FruitNinjaService.js";import{jsx as s,jsxs as f}from"react/jsx-runtime";export const FruitComponent=({fruit:t,onSlice:a})=>{const n=t.isSliced?0:1,c=t.isSliced?0:1,h={transform:[{translateX:t.position.x},{translateY:t.position.y},{rotate:t.rotation+"rad"},{scale:n}],opacity:c,position:"absolute",width:i.FRUIT_SIZE,height:i.FRUIT_SIZE,zIndex:t.isBomb?10:5};return s(e,{style:h,children:f(o,{style:[d.fruitButton,{width:i.FRUIT_SIZE,height:i.FRUIT_SIZE,borderColor:t.isBomb?"#ef4444":t.isBonus?"#fbbf24":t.isFreeze?"#60a5fa":"#4ade80",backgroundColor:t.isBomb?"#fee2e2":t.isBonus?"#fef3c7":t.isFreeze?"#dbeafe":"#dcfce7"}],onPress:()=>a?.(t.id),activeOpacity:.8,children:[s(r,{style:d.fruitEmoji,children:t.type.emoji}),(t.isBomb||t.isBonus||t.isFreeze)&&s(e,{style:[d.glowEffect,{backgroundColor:t.isBomb?"#ff4757":t.isBonus?"#ffa502":"#3742fa",shadowColor:t.isBomb?"#ff4757":t.isBonus?"#ffa502":"#3742fa",shadowOffset:{width:0,height:0},shadowOpacity:.8,shadowRadius:10,elevation:10}]})]})})};const d=a.create({fruitButton:{justifyContent:"center",alignItems:"center",borderRadius:50,borderWidth:2,shadowColor:"#000",shadowOffset:{width:0,height:4},shadowOpacity:.3,shadowRadius:8,elevation:8},fruitEmoji:{fontSize:32},glowEffect:{position:"absolute",top:0,left:0,right:0,bottom:0,borderRadius:50,opacity:.3}});
@@ -1 +1 @@
1
- "use strict";import React,{useRef,useEffect,useState}from 'react';import{View,StyleSheet}from 'react-native';import{runOnJS}from 'react-native-reanimated';import{Gesture,GestureDetector}from 'react-native-gesture-handler';import{useFruitNinjaStore}from "../FruitNinjaStore.js";import{GAME_CONFIG}from "../FruitNinjaService.js";import{playSound,GAME_SOUNDS}from "../../../services/SoundsService.js";import{playHaptic,HapticType}from "../../../services/HapticsService.js";import{ParticleBlast,useAnimationTracker}from "../../../helpers/index.js";import{FruitComponent}from "./FruitComponent.js";import{jsx as _jsx,jsxs as _jsxs}from "react/jsx-runtime";export const GameArea = React.memo(()=>{const fruits = useFruitNinjaStore(state => state.fruits);const{sliceFruit,removeFruit,updateFruit,resetCombo,loseLife,addLife}= useFruitNinjaStore();const lastGesturePoint = useRef(null);const fruitAnimations = useAnimationTracker();const [activeBlasts,setActiveBlasts] = useState([]);const playSliceSound =()=>{playSound(GAME_SOUNDS.FRUIT_NINJA.SLICE,true);playHaptic(HapticType.LIGHT,true);};const playBombSound =()=>{playSound(GAME_SOUNDS.FRUIT_NINJA.BOMB,true);playHaptic(HapticType.HEAVY,true);};const createBlastParticles = fruit =>{const blastX = fruit.position.x + GAME_CONFIG.FRUIT_SIZE / 2;const blastY = fruit.position.y + GAME_CONFIG.FRUIT_SIZE / 2;const blastId = `blast_${Date.now()}_${Math.random()}`;setActiveBlasts(prev => [...prev,{id:blastId,x:blastX,y:blastY}]);};const handleBlastComplete = blastId =>{setActiveBlasts(prev => prev.filter(blast => blast.id !== blastId));};const handleGestureStart = event =>{const currentState = useFruitNinjaStore.getState();if(!currentState.isPlaying)return;lastGesturePoint.current ={x:event.x,y:event.y};playHaptic(HapticType.LIGHT,true);};const handleGestureUpdate = event =>{const currentState = useFruitNinjaStore.getState();if(!currentState.isPlaying)return;const currentPoint ={x:event.x,y:event.y};checkFruitCollisions(currentPoint);lastGesturePoint.current = currentPoint;};const handleGestureEnd =()=>{const currentState = useFruitNinjaStore.getState();if(!currentState.isPlaying)return;lastGesturePoint.current = null;};const panGesture = Gesture.Pan().onStart(event =>{runOnJS(handleGestureStart)(event);}).onUpdate(event =>{runOnJS(handleGestureUpdate)(event);}).onEnd(()=>{runOnJS(handleGestureEnd)();});const handleFruitSlice = fruitId =>{const currentState = useFruitNinjaStore.getState();if(!currentState.isPlaying || currentState.gameOver)return;const fruit = currentState.fruits.find(f => f.id === fruitId);if(!fruit || fruit.isSliced)return;if(fruit.isBomb){playBombSound();loseLife();resetCombo();createBlastParticles(fruit);updateFruit(fruitId,{isSliced:true});}else{playSliceSound();if(fruit.isBonus){addLife();playSliceSound();updateFruit(fruitId,{isSliced:true});}else if(fruit.isFreeze){sliceFruit(fruitId);}else{sliceFruit(fruitId);}createBlastParticles(fruit);}setTimeout(()=>{removeFruit(fruitId);fruitAnimations.delete(fruitId);},500);};const checkFruitCollisions = gesturePoint =>{const currentFruits = useFruitNinjaStore.getState().fruits;currentFruits.forEach(fruit =>{if(!fruit.isSliced){const fruitCenterX = fruit.position.x + GAME_CONFIG.FRUIT_SIZE / 2;const fruitCenterY = fruit.position.y + GAME_CONFIG.FRUIT_SIZE / 2;const fruitRadius = GAME_CONFIG.FRUIT_SIZE / 2;const distance = Math.sqrt(Math.pow(gesturePoint.x - fruitCenterX,2)+ Math.pow(gesturePoint.y - fruitCenterY,2));if(distance <= fruitRadius){handleFruitSlice(fruit.id);}}});};useEffect(()=>{fruits.forEach(fruit =>{if(!fruitAnimations.has(fruit.id)){fruitAnimations.initialize(fruit.id);}});},[fruits,fruitAnimations]);return _jsx(GestureDetector,{gesture:panGesture,children:_jsxs(View,{style:styles.gameArea,children:[fruits.map(fruit =>{const animation = fruitAnimations.get(fruit.id);return _jsx(FruitComponent,{fruit:fruit,animation:animation,onSlice:handleFruitSlice},fruit.id);}),activeBlasts.map(blast => _jsx(ParticleBlast,{x:blast.x,y:blast.y,particleCount:8,duration:1000,onComplete:()=> handleBlastComplete(blast.id)},blast.id))]})});});const styles = StyleSheet.create({gameArea:{flex:1,position:'relative',overflow:'hidden'},backgroundGradient:{position:'absolute',top:0,left:0,right:0,bottom:0}});
1
+ "use strict";import t,{useRef as e,useEffect as r,useState as i}from"react";import{View as o,StyleSheet as s}from"react-native";import{runOnJS as n}from"react-native-reanimated";import{Gesture as a,GestureDetector as m}from"react-native-gesture-handler";import{useFruitNinjaStore as c}from"../FruitNinjaStore.js";import{GAME_CONFIG as u}from"../FruitNinjaService.js";import{playSound as l,GAME_SOUNDS as p}from"../../../services/SoundsService.js";import{playHaptic as f,HapticType as d}from"../../../services/HapticsService.js";import{ParticleBlast as v,useAnimationTracker as j}from"../../../helpers/index.js";import{FruitComponent as h}from"./FruitComponent.js";import{jsx as x,jsxs as S}from"react/jsx-runtime";export const GameArea=t.memo(()=>{const t=c(t=>t.fruits),{sliceFruit:s,removeFruit:b,updateFruit:g,resetCombo:y,loseLife:C,addLife:M}=c(),A=e(null),G=j(),[L,N]=i([]),$=()=>{l(p.FRUIT_NINJA.SLICE,!0),f(d.LIGHT,!0)},_=t=>{const e=t.position.x+u.FRUIT_SIZE/2,r=t.position.y+u.FRUIT_SIZE/2,i=`blast_${Date.now()}_${Math.random()}`;N(t=>[...t,{id:i,x:e,y:r}])},k=t=>{c.getState().isPlaying&&(A.current={x:t.x,y:t.y},f(d.LIGHT,!0))},w=t=>{if(!c.getState().isPlaying)return;const e={x:t.x,y:t.y};q(e),A.current=e},D=()=>{c.getState().isPlaying&&(A.current=null)},H=a.Pan().onStart(t=>{n(k)(t)}).onUpdate(t=>{n(w)(t)}).onEnd(()=>{n(D)()}),T=t=>{const e=c.getState();if(!e.isPlaying||e.gameOver)return;const r=e.fruits.find(e=>e.id===t);r&&!r.isSliced&&(r.isBomb?(l(p.FRUIT_NINJA.BOMB,!0),f(d.HEAVY,!0),C(),y(),_(r),g(t,{isSliced:!0})):($(),r.isBonus?(M(),$(),g(t,{isSliced:!0})):(r.isFreeze,s(t)),_(r)),setTimeout(()=>{b(t),G.delete(t)},500))},q=t=>{c.getState().fruits.forEach(e=>{if(!e.isSliced){const r=e.position.x+u.FRUIT_SIZE/2,i=e.position.y+u.FRUIT_SIZE/2,o=u.FRUIT_SIZE/2;Math.sqrt(Math.pow(t.x-r,2)+Math.pow(t.y-i,2))>o||T(e.id)}})};return r(()=>{t.forEach(t=>{G.has(t.id)||G.initialize(t.id)})},[t,G]),x(m,{gesture:H,children:S(o,{style:F.gameArea,children:[t.map(t=>{const e=G.get(t.id);return x(h,{fruit:t,animation:e,onSlice:T},t.id)}),L.map(t=>x(v,{x:t.x,y:t.y,particleCount:8,duration:1e3,onComplete:()=>{return e=t.id,void N(t=>t.filter(t=>t.id!==e));var e}},t.id))]})})});const F=s.create({gameArea:{flex:1,position:"relative",overflow:"hidden"},backgroundGradient:{position:"absolute",top:0,left:0,right:0,bottom:0}});