floating-games-sdk 1.0.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 (77) hide show
  1. package/package.json +44 -0
  2. package/src/FloatingGames.tsx +25 -0
  3. package/src/assets/fonts/Creepster-Regular.ttf +0 -0
  4. package/src/assets/images/android-icon-background.png +0 -0
  5. package/src/assets/images/android-icon-foreground.png +0 -0
  6. package/src/assets/images/android-icon-monochrome.png +0 -0
  7. package/src/assets/images/blocks-icon.png +0 -0
  8. package/src/assets/images/bright-rubber-puzzles-blue.png +0 -0
  9. package/src/assets/images/close.png +0 -0
  10. package/src/assets/images/eriri.png +0 -0
  11. package/src/assets/images/favicon.png +0 -0
  12. package/src/assets/images/first-play.png +0 -0
  13. package/src/assets/images/game-over.gif +0 -0
  14. package/src/assets/images/game-pad.png +0 -0
  15. package/src/assets/images/game-pad2.png +0 -0
  16. package/src/assets/images/game-page-bg1.png +0 -0
  17. package/src/assets/images/hands-down-bg.png +0 -0
  18. package/src/assets/images/icon.png +0 -0
  19. package/src/assets/images/lets-play.gif +0 -0
  20. package/src/assets/images/lucky-day.gif +0 -0
  21. package/src/assets/images/maths.png +0 -0
  22. package/src/assets/images/not-playing.png +0 -0
  23. package/src/assets/images/number-puzzle-select.png +0 -0
  24. package/src/assets/images/number-puzzle-selector.png +0 -0
  25. package/src/assets/images/pause-clock.png +0 -0
  26. package/src/assets/images/pause-icon.png +0 -0
  27. package/src/assets/images/play-icon.png +0 -0
  28. package/src/assets/images/play-now.gif +0 -0
  29. package/src/assets/images/play-play.png +0 -0
  30. package/src/assets/images/play.png +0 -0
  31. package/src/assets/images/quit-game.png +0 -0
  32. package/src/assets/images/quit-icon.png +0 -0
  33. package/src/assets/images/quit.png +0 -0
  34. package/src/assets/images/review-win-selector.png +0 -0
  35. package/src/assets/images/select-bg.png +0 -0
  36. package/src/assets/images/select-logo.png +0 -0
  37. package/src/assets/images/spin-wheel-select.png +0 -0
  38. package/src/assets/images/spin-wheel-selector.png +0 -0
  39. package/src/assets/images/splash-icon.png +0 -0
  40. package/src/assets/images/start-button.png +0 -0
  41. package/src/assets/images/start-game.png +0 -0
  42. package/src/assets/images/stop-quit.png +0 -0
  43. package/src/assets/images/waiting-for-you.gif +0 -0
  44. package/src/assets/sounds/selector-music.mp3 +0 -0
  45. package/src/assets/sounds/timeup-sound.wav +0 -0
  46. package/src/components/atoms/Amount.tsx +48 -0
  47. package/src/components/atoms/FloatingMenu.tsx +119 -0
  48. package/src/components/atoms/ShimmerHR.tsx +64 -0
  49. package/src/components/atoms/UseAnimatedCounter.tsx +31 -0
  50. package/src/components/games/Chain.tsx +26 -0
  51. package/src/components/games/GameItem.tsx +38 -0
  52. package/src/components/games/GameSelectorScreen.tsx +221 -0
  53. package/src/components/games/GameSplash.tsx +44 -0
  54. package/src/components/games/HangingSign.tsx +75 -0
  55. package/src/components/games/SplashScreen.tsx +69 -0
  56. package/src/components/games/number-puzzle/DragTile.tsx +65 -0
  57. package/src/components/games/number-puzzle/GameScreen.tsx +597 -0
  58. package/src/components/games/number-puzzle/PuzzleSplash.tsx +50 -0
  59. package/src/components/games/number-puzzle/TileComponent.tsx +47 -0
  60. package/src/components/games/review-and-win/EmojiTracker.tsx +96 -0
  61. package/src/components/games/review-and-win/GameScreen.tsx +135 -0
  62. package/src/components/games/review-and-win/Tile.tsx +60 -0
  63. package/src/components/games/spin-wheel/SpinAndWin.tsx +507 -0
  64. package/src/components/payment/CheckoutScreen.tsx +50 -0
  65. package/src/components/payment/FundAccount.tsx +281 -0
  66. package/src/components/payment/Payment.tsx +45 -0
  67. package/src/components/reusables/AppSelect.tsx +70 -0
  68. package/src/components/reusables/CountdownTimer.tsx +116 -0
  69. package/src/components/reusables/FloatingButton.tsx +71 -0
  70. package/src/components/reusables/SoundPlayer.ts +28 -0
  71. package/src/components/utils/shuffle.ts +48 -0
  72. package/src/context/GameContext.tsx +15 -0
  73. package/src/index.tsx +5 -0
  74. package/src/modals/BottomSheetModal.tsx +38 -0
  75. package/src/modals/GameOverModal.tsx +76 -0
  76. package/src/modals/PauseModal.tsx +95 -0
  77. package/src/navigation/GameNavigator.tsx +48 -0
@@ -0,0 +1,221 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Image, ImageBackground, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
3
+ import { NativeStackScreenProps } from '@react-navigation/native-stack';
4
+ import { RootStackParamList } from '@/src/navigation/GameNavigator';
5
+ import FloatingButton from '../../components/reusables/FloatingButton';
6
+ import { LinearGradient } from 'expo-linear-gradient';
7
+ import { useSoundPlayer } from '../reusables/SoundPlayer';
8
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
9
+ import FloatingMenu from '../../components/atoms/FloatingMenu';
10
+ import AsyncStorage from '@react-native-async-storage/async-storage';
11
+ import ShimmerHR from '../../components/atoms/ShimmerHR';
12
+ import { SafeAreaView } from 'react-native-safe-area-context';
13
+
14
+ type Props = NativeStackScreenProps<RootStackParamList, 'GameSelector'>;
15
+
16
+ type Game = {
17
+ icon: string;
18
+ title: string;
19
+ backgroundColor: string;
20
+ navigateTo: string | any;
21
+ howTo: string;
22
+ };
23
+
24
+ const GAMES: Game[] = [
25
+ {
26
+ icon: '🧩',
27
+ title: 'Number Puzzle',
28
+ backgroundColor: '#022759',
29
+ navigateTo: 'Game1',
30
+ howTo: 'Slide the numbered tiles until arranged 1–15 using the empty space. Complete within the time limit to win a cash prize.',
31
+ },
32
+ {
33
+ icon: '🎡',
34
+ title: 'Spin Wheel',
35
+ backgroundColor: '#02594a',
36
+ navigateTo: 'Game2',
37
+ howTo: 'Spin the wheel and watch it land on a random segment to reveal your prize instantly.',
38
+ },
39
+ {
40
+ icon: '⭐',
41
+ title: 'Review And Win',
42
+ backgroundColor: '#5a0217',
43
+ navigateTo: 'Game3',
44
+ howTo: 'Tap to reveal hidden tiles and match three identical items to win the prize shown.',
45
+ },
46
+ ];
47
+
48
+ const GAME_IMAGES = [
49
+ require('../../assets/images/bright-rubber-puzzles-blue.png'),
50
+ require('../../assets/images/spin-wheel-selector.png'),
51
+ require('../../assets/images/review-win-selector.png'),
52
+ ];
53
+
54
+ const PLAY_NOW_GIF = require('../../assets/images/play-now.gif');
55
+ const BG_IMAGE = require('../../assets/images/select-bg.png');
56
+ const SELECTOR_MUSIC = require('../../assets/sounds/selector-music.mp3');
57
+
58
+ export default function GameSelectorScreen({ navigation }: Props) {
59
+ const insets = useSafeAreaInsets();
60
+ const [isPlaying, setIsPlaying] = useState(false);
61
+ const { playSound, stopSound } = useSoundPlayer(SELECTOR_MUSIC, 0.9);
62
+
63
+ const handleMusic = async () => {
64
+ const value = await AsyncStorage.getItem('hasMusic');
65
+ const enabled = value === 'true';
66
+ setIsPlaying(enabled);
67
+ enabled ? playSound() : stopSound();
68
+ };
69
+
70
+ const musicCtrl = async () => {
71
+ try {
72
+ const value = await AsyncStorage.getItem('hasMusic');
73
+ const next = value !== 'true';
74
+ await AsyncStorage.setItem('hasMusic', String(next));
75
+ setIsPlaying(next);
76
+ next ? playSound() : stopSound();
77
+ } catch (err) {
78
+ console.log('musicCtrl error:', err);
79
+ }
80
+ };
81
+
82
+ useEffect(() => {
83
+ handleMusic();
84
+ return () => stopSound();
85
+ }, []);
86
+
87
+ const onSelection = (navigateTo: string) => {
88
+ const game = GAMES.find(g => g.navigateTo === navigateTo);
89
+ if (game) {
90
+ navigation.navigate('GameSplash', {
91
+ icon: game.icon,
92
+ title: game.title,
93
+ backgroundColor: game.backgroundColor,
94
+ navigateTo: game.navigateTo,
95
+ });
96
+ }
97
+ };
98
+
99
+ return (
100
+ <ImageBackground source={BG_IMAGE} style={styles.bg} resizeMode="contain">
101
+ <LinearGradient colors={['rgba(198,198,198,0.5)', '#4c68af', '#597e79']} style={styles.container}>
102
+
103
+ <SafeAreaView style={{ paddingTop: insets.top }}>
104
+ <Text style={styles.brand}>Floating Games</Text>
105
+ <Text style={styles.ageNotice}>
106
+ This game contains content intended for players aged 18 and above.{'\n'}
107
+ By tapping 'Play Button', you confirm that you meet the age requirement.
108
+ </Text>
109
+ <Text style={styles.title}>Select Game</Text>
110
+
111
+ <ShimmerHR />
112
+
113
+ <ScrollView
114
+ style={styles.scroll}
115
+ contentContainerStyle={styles.scrollContent}
116
+ showsVerticalScrollIndicator={false}
117
+ >
118
+ {GAMES.map((game, index) => (
119
+ <View key={game.navigateTo} style={styles.cardWrapper}>
120
+ <FloatingButton preset="normal">
121
+ <Image
122
+ source={GAME_IMAGES[index]}
123
+ style={[styles.gameImage, index === 1 && styles.gameImageLarge]}
124
+ />
125
+ <TouchableOpacity onPress={() => onSelection(game.navigateTo)}>
126
+ <Text style={styles.gameTitle}>{game.title} {game.icon}</Text>
127
+ <Image source={PLAY_NOW_GIF} style={styles.playNow} />
128
+ </TouchableOpacity>
129
+ <Text style={styles.howTo}>{game.howTo}</Text>
130
+ </FloatingButton>
131
+ </View>
132
+ ))}
133
+ </ScrollView>
134
+
135
+ <FloatingMenu
136
+ isPlaying={isPlaying}
137
+ soundCtrl={musicCtrl}
138
+ paymentCtrl={() => {
139
+ navigation.navigate('Payment');
140
+ stopSound();
141
+ }}
142
+ />
143
+
144
+ </SafeAreaView>
145
+
146
+ </LinearGradient>
147
+ </ImageBackground>
148
+ );
149
+ }
150
+
151
+ const styles = StyleSheet.create({
152
+ bg: {
153
+ flex: 1,
154
+ backgroundColor: 'rgba(247,247,248,0.76)',
155
+ },
156
+ container: {
157
+ flex: 1,
158
+ alignItems: 'center',
159
+ },
160
+ brand: {
161
+ alignSelf: 'center',
162
+ fontWeight: '600',
163
+ },
164
+ ageNotice: {
165
+ textAlign: 'center',
166
+ marginBottom: 10,
167
+ marginHorizontal: 40,
168
+ color: '#8e5656',
169
+ fontSize: 10,
170
+ },
171
+ title: {
172
+ fontSize: 13,
173
+ fontWeight: '500',
174
+ color: '#4788dc',
175
+ textAlign: 'center',
176
+ marginBottom: 10,
177
+ alignSelf: 'center',
178
+ },
179
+ scroll: {
180
+ width: '100%',
181
+ },
182
+ scrollContent: {
183
+ paddingVertical: 20,
184
+ alignItems: 'center',
185
+ marginBottom: 30,
186
+ },
187
+ cardWrapper: {
188
+ marginVertical: 20,
189
+ },
190
+ gameImage: {
191
+ height: 80,
192
+ width: 150,
193
+ alignSelf: 'center',
194
+ },
195
+ gameImageLarge: {
196
+ height: 100,
197
+ width: 100,
198
+ },
199
+ gameTitle: {
200
+ fontFamily: 'Creepster_400Regular',
201
+ letterSpacing: 2,
202
+ fontSize: 15,
203
+ fontWeight: '800',
204
+ color: '#5a0217',
205
+ alignSelf: 'center',
206
+ marginTop: 5,
207
+ },
208
+ playNow: {
209
+ height: 15,
210
+ width: 150,
211
+ alignSelf: 'center',
212
+ borderRadius: 10,
213
+ marginVertical: 10,
214
+ },
215
+ howTo: {
216
+ fontSize: 10,
217
+ textAlign: 'center',
218
+ marginHorizontal: 35,
219
+ color: '#2b2727',
220
+ },
221
+ });
@@ -0,0 +1,44 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Animated, Image, StyleSheet, Text, View } from 'react-native';
3
+ import { NativeStackScreenProps } from '@react-navigation/native-stack';
4
+ import { RootStackParamList } from '@/src/navigation/GameNavigator';
5
+
6
+ type GameSplashProps = NativeStackScreenProps<RootStackParamList, 'GameSplash'>;
7
+
8
+ export default function GameSplash({ navigation, route }: GameSplashProps) {
9
+ const { icon = '🎮', title = 'Game', backgroundColor = '#000', backgroundImage, navigateTo } = route.params;
10
+ const opacity = useRef(new Animated.Value(0)).current;
11
+
12
+ useEffect(() => {
13
+ if (!navigateTo) {
14
+ console.warn('GameSplash: navigateTo is undefined!');
15
+ return;
16
+ }
17
+
18
+ Animated.sequence([
19
+ Animated.timing(opacity, { toValue: 1, duration: 1000, useNativeDriver: true }),
20
+ Animated.delay(1000),
21
+ Animated.timing(opacity, { toValue: 0, duration: 1000, useNativeDriver: true }),
22
+ ]).start(() => {
23
+ navigation.replace(navigateTo); // Navigate to the target screen
24
+ });
25
+ }, [navigateTo]);
26
+
27
+ return (
28
+ <View style={ [styles.container, { backgroundColor }] }>
29
+ { backgroundImage && (
30
+ <Image source={ backgroundImage } style={ StyleSheet.absoluteFillObject } resizeMode="cover"/>
31
+ ) }
32
+ <Animated.View style={ { opacity, alignItems: 'center' } }>
33
+ <Text style={ styles.icon }>{ icon }</Text>
34
+ <Text style={ styles.title }>{ title }</Text>
35
+ </Animated.View>
36
+ </View>
37
+ );
38
+ }
39
+
40
+ const styles = StyleSheet.create({
41
+ container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
42
+ icon: { fontSize: 50, marginBottom: 20 },
43
+ title: { fontSize: 25, fontWeight: 'bold', color: '#fff' },
44
+ });
@@ -0,0 +1,75 @@
1
+ import React, {useEffect} from 'react';
2
+ import {Pressable, StyleSheet, Text, View} from 'react-native';
3
+ import Animated, {Easing, useAnimatedStyle, useSharedValue, withRepeat, withTiming} from 'react-native-reanimated';
4
+ import Chain from './Chain';
5
+
6
+ type Props = {
7
+ title: string;
8
+ onPress: () => void;
9
+ }
10
+
11
+ export default function HangingSign({title, onPress}: Props) {
12
+
13
+ const rotate = useSharedValue(0);
14
+
15
+ useEffect(() => {
16
+ rotate.value = withRepeat(
17
+ withTiming(2, {
18
+ duration: 2000,
19
+ easing: Easing.inOut(Easing.ease)
20
+ }),
21
+ -1,
22
+ true
23
+ );
24
+ }, []);
25
+
26
+ const animatedBoard = useAnimatedStyle(() => ({
27
+ transform: [
28
+ {rotate: `${rotate.value}deg`}
29
+ ]
30
+ }))
31
+
32
+ return (
33
+ <Pressable style={styles.container} onPress={onPress}>
34
+
35
+ <View style={styles.chainRow}>
36
+ <Chain/>
37
+ <Chain/>
38
+ </View>
39
+
40
+ <Animated.View style={[styles.board, animatedBoard]}>
41
+ <Text style={styles.text}>{title}</Text>
42
+ </Animated.View>
43
+
44
+ </Pressable>
45
+ );
46
+ }
47
+
48
+ const styles = StyleSheet.create({
49
+ container: {
50
+ alignItems: 'center',
51
+ // marginTop: 20,
52
+ },
53
+ chainRow: {
54
+ flexDirection: 'row',
55
+ justifyContent: 'space-between',
56
+ width: 160,
57
+ zIndex: 0
58
+ },
59
+ board: {
60
+ width: 210,
61
+ height: 30,
62
+ backgroundColor: '#8B5A2B',
63
+ borderRadius: 12,
64
+ justifyContent: 'center',
65
+ alignItems: 'center',
66
+ elevation: 6,
67
+ zIndex: 10
68
+ },
69
+ text: {
70
+ color: 'white',
71
+ fontSize: 17,
72
+ // fontWeight: "bold"
73
+ fontFamily: 'Creepster_400Regular'
74
+ }
75
+ });
@@ -0,0 +1,69 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Animated, StyleSheet, Text, View } from 'react-native';
3
+ import { NativeStackScreenProps } from '@react-navigation/native-stack';
4
+ import { RootStackParamList } from '@/src/navigation/GameNavigator';
5
+
6
+ type Props = NativeStackScreenProps<RootStackParamList, 'Splash'>;
7
+
8
+ export default function SplashScreen({ navigation }: Props) {
9
+ const opacity = useRef(new Animated.Value(0)).current;
10
+
11
+ useEffect(() => {
12
+ Animated.sequence([
13
+ Animated.timing(opacity, {
14
+ toValue: 1,
15
+ duration: 2000, // fade in 2 sec
16
+ useNativeDriver: true,
17
+ }),
18
+ Animated.delay(1000), // stay visible 10 sec
19
+ Animated.timing(opacity, {
20
+ toValue: 0,
21
+ duration: 2000, // fade out 2 sec
22
+ useNativeDriver: true,
23
+ }),
24
+ ]).start(() => {
25
+ // navigation.replace('GameSelector'); // go to selector after splash
26
+ navigation.replace('Payment'); // go to selector after splash
27
+ });
28
+ }, []);
29
+
30
+ return (
31
+ <View style={ styles.container }>
32
+ <Animated.View style={ { opacity } }>
33
+ <View style={ styles.divContainer }>
34
+ <Text style={ styles.logo }>🎮 Floating Games</Text>
35
+ <Text style={ styles.disclaimer }>
36
+ <Text style={ { fontWeight:'bold',fontSize:13 } }>Disclaimer:</Text> { '\n' }This game is intended strictly for users aged 18 years and older.
37
+ By accessing or playing, you confirm that you are of legal age in your jurisdiction.
38
+ </Text>
39
+ </View>
40
+ </Animated.View>
41
+ </View>
42
+ );
43
+ }
44
+
45
+ const styles = StyleSheet.create({
46
+ container: {
47
+ flex: 1,
48
+ backgroundColor: '#000',
49
+ justifyContent: 'center',
50
+ alignItems: 'center',
51
+ },
52
+ divContainer: {
53
+ flex: 1, // take full screen
54
+ justifyContent: 'center', // vertical center
55
+ alignItems: 'center', // horizontal center
56
+ },
57
+ logo: {
58
+ fontSize: 25,
59
+ fontWeight: 'bold',
60
+ color: '#fff',
61
+ marginBottom: 20
62
+ },
63
+ disclaimer: {
64
+ fontSize: 11,
65
+ color: '#aaa',
66
+ marginHorizontal: 25,
67
+ textAlign: 'center'
68
+ }
69
+ });
@@ -0,0 +1,65 @@
1
+ import React from 'react';
2
+ import {Text} from 'react-native';
3
+ import Animated, {useAnimatedStyle, SharedValue} from 'react-native-reanimated';
4
+ import {Gesture, GestureDetector, Directions} from 'react-native-gesture-handler';
5
+
6
+ interface Tile {
7
+ value: number;
8
+ row: number;
9
+ col: number;
10
+ x: SharedValue<number>;
11
+ y: SharedValue<number>;
12
+ }
13
+
14
+ interface Props {
15
+ tile: Tile;
16
+ size: number;
17
+ emptyTile: { row: number; col: number };
18
+ onMove: (row: number, col: number) => void;
19
+ }
20
+
21
+ export default function DragTile({
22
+ tile,
23
+ size,
24
+ emptyTile,
25
+ onMove,
26
+ }: Props) {
27
+
28
+ const swipe = Gesture.Fling()
29
+ .direction(
30
+ Directions.UP |
31
+ Directions.DOWN |
32
+ Directions.LEFT |
33
+ Directions.RIGHT
34
+ )
35
+ .onEnd(() => {
36
+ const dx = Math.abs(tile.row - emptyTile.row);
37
+ const dy = Math.abs(tile.col - emptyTile.col);
38
+ if (dx + dy === 1) {
39
+ onMove(tile.row, tile.col);
40
+ }
41
+ });
42
+
43
+ const style = useAnimatedStyle(() => ({
44
+ position: 'absolute',
45
+ width: size,
46
+ height: size,
47
+ top: tile.y.value,
48
+ left: tile.x.value,
49
+ justifyContent: 'center',
50
+ alignItems: 'center',
51
+ backgroundColor: '#3498db',
52
+ borderRadius: 12,
53
+ margin: 20
54
+ }));
55
+
56
+ return (
57
+ <GestureDetector gesture={swipe}>
58
+ <Animated.View style={style}>
59
+ <Text style={{color: '#fff', fontSize: 22, fontWeight: 'bold'}}>
60
+ {tile.value}
61
+ </Text>
62
+ </Animated.View>
63
+ </GestureDetector>
64
+ );
65
+ }