forked from vergnet/application-amicale
Convert game into functional component
This commit is contained in:
parent
14365a92a4
commit
9ae585bdf8
10 changed files with 856 additions and 770 deletions
44
src/screens/Game/components/FullGamePodium.tsx
Normal file
44
src/screens/Game/components/FullGamePodium.tsx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { GamePodium } from './GamePodium';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
scores: Array<number>;
|
||||||
|
isHighScore: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
topScoreContainer: {
|
||||||
|
marginBottom: 20,
|
||||||
|
marginTop: 20,
|
||||||
|
},
|
||||||
|
topScoreSubcontainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function FullGamePodium(props: Props) {
|
||||||
|
const { scores, isHighScore } = props;
|
||||||
|
const gold = scores.length > 0 ? scores[0] : '-';
|
||||||
|
const silver = scores.length > 1 ? scores[1] : '-';
|
||||||
|
const bronze = scores.length > 2 ? scores[2] : '-';
|
||||||
|
return (
|
||||||
|
<View style={styles.topScoreContainer}>
|
||||||
|
<GamePodium place={1} score={gold.toString()} isHighScore={isHighScore} />
|
||||||
|
<View style={styles.topScoreSubcontainer}>
|
||||||
|
<GamePodium
|
||||||
|
place={3}
|
||||||
|
score={silver.toString()}
|
||||||
|
isHighScore={isHighScore}
|
||||||
|
/>
|
||||||
|
<GamePodium
|
||||||
|
place={2}
|
||||||
|
score={bronze.toString()}
|
||||||
|
isHighScore={isHighScore}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
65
src/screens/Game/components/GameBrackground.tsx
Normal file
65
src/screens/Game/components/GameBrackground.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
import { useTheme } from 'react-native-paper';
|
||||||
|
import GridManager from '../logic/GridManager';
|
||||||
|
import Piece from '../logic/Piece';
|
||||||
|
import GridComponent from './GridComponent';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
pieceContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
pieceBackground: {
|
||||||
|
position: 'absolute',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function GameBackground() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const gridManager = new GridManager(4, 4, theme);
|
||||||
|
const gridList = [];
|
||||||
|
for (let i = 0; i < 18; i += 1) {
|
||||||
|
gridList.push(gridManager.getEmptyGrid(4, 4));
|
||||||
|
const piece = new Piece(theme);
|
||||||
|
piece.toGrid(gridList[i], true);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<View style={styles.pieceContainer}>
|
||||||
|
{gridList.map((item, index) => {
|
||||||
|
const size = 10 + Math.floor(Math.random() * 30);
|
||||||
|
const top = Math.floor(Math.random() * 100);
|
||||||
|
const rot = Math.floor(Math.random() * 360);
|
||||||
|
const left = (index % 6) * 20;
|
||||||
|
const animDelay = size * 20;
|
||||||
|
const animDuration = 2 * (2000 - size * 30);
|
||||||
|
return (
|
||||||
|
<Animatable.View
|
||||||
|
useNativeDriver={true}
|
||||||
|
animation={'fadeInDownBig'}
|
||||||
|
delay={animDelay}
|
||||||
|
duration={animDuration}
|
||||||
|
key={`piece${index.toString()}`}
|
||||||
|
style={{
|
||||||
|
width: `${size}%`,
|
||||||
|
top: `${top}%`,
|
||||||
|
left: `${left}%`,
|
||||||
|
...styles.pieceBackground,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<GridComponent
|
||||||
|
width={4}
|
||||||
|
height={4}
|
||||||
|
grid={item}
|
||||||
|
style={{
|
||||||
|
transform: [{ rotateZ: `${rot}deg` }],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Animatable.View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
62
src/screens/Game/components/GameControls.tsx
Normal file
62
src/screens/Game/components/GameControls.tsx
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { IconButton, useTheme } from 'react-native-paper';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
import GameLogic, { MovementCallbackType } from '../logic/GameLogic';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
logic: GameLogic;
|
||||||
|
onDirectionPressed: MovementCallbackType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
controlsContainer: {
|
||||||
|
height: 80,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
directionsContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
flex: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function GameControls(props: Props) {
|
||||||
|
const { logic } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<View style={styles.controlsContainer}>
|
||||||
|
<IconButton
|
||||||
|
icon={'rotate-right-variant'}
|
||||||
|
size={40}
|
||||||
|
onPress={() => logic.rotatePressed(props.onDirectionPressed)}
|
||||||
|
style={GENERAL_STYLES.flex}
|
||||||
|
/>
|
||||||
|
<View style={styles.directionsContainer}>
|
||||||
|
<IconButton
|
||||||
|
icon={'chevron-left'}
|
||||||
|
size={40}
|
||||||
|
style={GENERAL_STYLES.flex}
|
||||||
|
onPress={() => logic.pressedOut()}
|
||||||
|
onPressIn={() => logic.leftPressedIn(props.onDirectionPressed)}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
icon={'chevron-right'}
|
||||||
|
size={40}
|
||||||
|
style={GENERAL_STYLES.flex}
|
||||||
|
onPress={() => logic.pressedOut()}
|
||||||
|
onPressIn={() => logic.rightPressed(props.onDirectionPressed)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<IconButton
|
||||||
|
icon={'arrow-down-bold'}
|
||||||
|
size={40}
|
||||||
|
onPressIn={() => logic.downPressedIn(props.onDirectionPressed)}
|
||||||
|
onPress={() => logic.pressedOut()}
|
||||||
|
style={GENERAL_STYLES.flex}
|
||||||
|
color={theme.colors.tetrisScore}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(GameControls, () => true);
|
95
src/screens/Game/components/GamePodium.tsx
Normal file
95
src/screens/Game/components/GamePodium.tsx
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { Text, useTheme } from 'react-native-paper';
|
||||||
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import * as Animatable from 'react-native-animatable';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
place: 1 | 2 | 3;
|
||||||
|
score: string;
|
||||||
|
isHighScore: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
podiumContainer: {
|
||||||
|
flexDirection: 'column',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
},
|
||||||
|
podiumIconContainer: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: -20,
|
||||||
|
},
|
||||||
|
centertext: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export function GamePodium(props: Props) {
|
||||||
|
const { place, score, isHighScore } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
let icon = 'podium-gold';
|
||||||
|
let color = theme.colors.gameGold;
|
||||||
|
let fontSize = 20;
|
||||||
|
let size = 70;
|
||||||
|
if (place === 2) {
|
||||||
|
icon = 'podium-silver';
|
||||||
|
color = theme.colors.gameSilver;
|
||||||
|
fontSize = 18;
|
||||||
|
size = 60;
|
||||||
|
} else if (place === 3) {
|
||||||
|
icon = 'podium-bronze';
|
||||||
|
color = theme.colors.gameBronze;
|
||||||
|
fontSize = 15;
|
||||||
|
size = 50;
|
||||||
|
}
|
||||||
|
const marginLeft = place === 2 ? 20 : 'auto';
|
||||||
|
const marginRight = place === 3 ? 20 : 'auto';
|
||||||
|
const fontWeight = place === 1 ? 'bold' : undefined;
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginLeft: marginLeft,
|
||||||
|
marginRight: marginRight,
|
||||||
|
...styles.podiumContainer,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isHighScore && place === 1 ? (
|
||||||
|
<Animatable.View
|
||||||
|
animation="swing"
|
||||||
|
iterationCount="infinite"
|
||||||
|
duration={2000}
|
||||||
|
delay={1000}
|
||||||
|
useNativeDriver
|
||||||
|
style={styles.podiumIconContainer}
|
||||||
|
>
|
||||||
|
<Animatable.View
|
||||||
|
animation="pulse"
|
||||||
|
iterationCount="infinite"
|
||||||
|
useNativeDriver
|
||||||
|
>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="decagram"
|
||||||
|
color={theme.colors.gameGold}
|
||||||
|
size={150}
|
||||||
|
/>
|
||||||
|
</Animatable.View>
|
||||||
|
</Animatable.View>
|
||||||
|
) : null}
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={icon}
|
||||||
|
color={isHighScore && place === 1 ? '#fff' : color}
|
||||||
|
size={size}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
fontWeight: fontWeight,
|
||||||
|
fontSize,
|
||||||
|
...styles.centertext,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{score}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
81
src/screens/Game/components/GameScore.tsx
Normal file
81
src/screens/Game/components/GameScore.tsx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
import { Text, useTheme } from 'react-native-paper';
|
||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
score: number;
|
||||||
|
highScore?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
scoreMainContainer: {
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
scoreCurrentContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
scoreText: {
|
||||||
|
marginLeft: 5,
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
scoreBestContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 5,
|
||||||
|
},
|
||||||
|
centerVerticalSmallMargin: {
|
||||||
|
...GENERAL_STYLES.centerVertical,
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function GameScore(props: Props) {
|
||||||
|
const { score, highScore } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
const displayHighScore =
|
||||||
|
highScore == null || score > highScore ? score : highScore;
|
||||||
|
return (
|
||||||
|
<View style={styles.scoreMainContainer}>
|
||||||
|
<View style={styles.scoreCurrentContainer}>
|
||||||
|
<Text style={styles.scoreText}>
|
||||||
|
{i18n.t('screens.game.score', { score: score })}
|
||||||
|
</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="star"
|
||||||
|
color={theme.colors.tetrisScore}
|
||||||
|
size={20}
|
||||||
|
style={styles.centerVerticalSmallMargin}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.scoreBestContainer}>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
...styles.scoreText,
|
||||||
|
color: theme.colors.textDisabled,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n.t('screens.game.highScore', { score: displayHighScore })}
|
||||||
|
</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name="star"
|
||||||
|
color={theme.colors.tetrisScore}
|
||||||
|
size={10}
|
||||||
|
style={styles.centerVerticalSmallMargin}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(
|
||||||
|
GameScore,
|
||||||
|
(pp, np) => pp.highScore === np.highScore && pp.score === np.score
|
||||||
|
);
|
96
src/screens/Game/components/GameStatus.tsx
Normal file
96
src/screens/Game/components/GameStatus.tsx
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { Caption, Text, useTheme } from 'react-native-paper';
|
||||||
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
time: number;
|
||||||
|
level: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
centerSmallMargin: {
|
||||||
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
|
marginBottom: 5,
|
||||||
|
},
|
||||||
|
centerBigMargin: {
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
statusContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
statusIcon: {
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
function getFormattedTime(seconds: number): string {
|
||||||
|
const date = new Date();
|
||||||
|
date.setHours(0);
|
||||||
|
date.setMinutes(0);
|
||||||
|
date.setSeconds(seconds);
|
||||||
|
let format;
|
||||||
|
if (date.getHours()) {
|
||||||
|
format = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
|
||||||
|
} else if (date.getMinutes()) {
|
||||||
|
format = `${date.getMinutes()}:${date.getSeconds()}`;
|
||||||
|
} else {
|
||||||
|
format = date.getSeconds().toString();
|
||||||
|
}
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
function GameStatus(props: Props) {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
...GENERAL_STYLES.flex,
|
||||||
|
...GENERAL_STYLES.centerVertical,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={GENERAL_STYLES.centerHorizontal}>
|
||||||
|
<Caption style={styles.centerSmallMargin}>
|
||||||
|
{i18n.t('screens.game.time')}
|
||||||
|
</Caption>
|
||||||
|
<View style={styles.statusContainer}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={'timer'}
|
||||||
|
color={theme.colors.subtitle}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
...styles.statusIcon,
|
||||||
|
color: theme.colors.subtitle,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getFormattedTime(props.time)}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View style={styles.centerBigMargin}>
|
||||||
|
<Caption style={styles.centerSmallMargin}>
|
||||||
|
{i18n.t('screens.game.level')}
|
||||||
|
</Caption>
|
||||||
|
<View style={styles.statusContainer}>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={'gamepad-square'}
|
||||||
|
color={theme.colors.text}
|
||||||
|
size={20}
|
||||||
|
/>
|
||||||
|
<Text style={styles.statusIcon}>{props.level}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(
|
||||||
|
GameStatus,
|
||||||
|
(pp, np) => pp.level === np.level && pp.time === np.time
|
||||||
|
);
|
130
src/screens/Game/components/PostGameContent.tsx
Normal file
130
src/screens/Game/components/PostGameContent.tsx
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import { Card, Divider, Headline, Text, useTheme } from 'react-native-paper';
|
||||||
|
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
||||||
|
import Mascot, { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
||||||
|
import SpeechArrow from '../../../components/Mascot/SpeechArrow';
|
||||||
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
type GameStatsType = {
|
||||||
|
score: number;
|
||||||
|
level: number;
|
||||||
|
time: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isHighScore: boolean;
|
||||||
|
stats: GameStatsType;
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
recapCard: {
|
||||||
|
borderWidth: 2,
|
||||||
|
marginLeft: 20,
|
||||||
|
marginRight: 20,
|
||||||
|
},
|
||||||
|
recapContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
recapScoreContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
marginTop: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
recapScore: {
|
||||||
|
fontSize: 20,
|
||||||
|
},
|
||||||
|
recapScoreIcon: {
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
|
recapIcon: {
|
||||||
|
marginRight: 5,
|
||||||
|
marginLeft: 5,
|
||||||
|
},
|
||||||
|
centertext: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function PostGameContent(props: Props) {
|
||||||
|
const { isHighScore, stats } = props;
|
||||||
|
const theme = useTheme();
|
||||||
|
const width = isHighScore ? '50%' : '30%';
|
||||||
|
const margin = isHighScore ? 'auto' : undefined;
|
||||||
|
const marginLeft = isHighScore ? '60%' : '20%';
|
||||||
|
const color = isHighScore ? theme.colors.gameGold : theme.colors.primary;
|
||||||
|
return (
|
||||||
|
<View style={GENERAL_STYLES.flex}>
|
||||||
|
<Mascot
|
||||||
|
emotion={isHighScore ? MASCOT_STYLE.LOVE : MASCOT_STYLE.NORMAL}
|
||||||
|
animated={isHighScore}
|
||||||
|
style={{
|
||||||
|
width: width,
|
||||||
|
marginLeft: margin,
|
||||||
|
marginRight: margin,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<SpeechArrow
|
||||||
|
style={{ marginLeft: marginLeft }}
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.mascotMessageArrow}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
borderColor: theme.colors.mascotMessageArrow,
|
||||||
|
...styles.recapCard,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card.Content>
|
||||||
|
<Headline
|
||||||
|
style={{
|
||||||
|
color: color,
|
||||||
|
...styles.centertext,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isHighScore
|
||||||
|
? i18n.t('screens.game.newHighScore')
|
||||||
|
: i18n.t('screens.game.gameOver')}
|
||||||
|
</Headline>
|
||||||
|
<Divider />
|
||||||
|
<View style={styles.recapScoreContainer}>
|
||||||
|
<Text style={styles.recapScore}>
|
||||||
|
{i18n.t('screens.game.score', { score: stats.score })}
|
||||||
|
</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
name={'star'}
|
||||||
|
color={theme.colors.tetrisScore}
|
||||||
|
size={30}
|
||||||
|
style={styles.recapScoreIcon}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={styles.recapContainer}>
|
||||||
|
<Text>{i18n.t('screens.game.level')}</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
style={styles.recapIcon}
|
||||||
|
name={'gamepad-square'}
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.textDisabled}
|
||||||
|
/>
|
||||||
|
<Text>{stats.level}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.recapContainer}>
|
||||||
|
<Text>{i18n.t('screens.game.time')}</Text>
|
||||||
|
<MaterialCommunityIcons
|
||||||
|
style={styles.recapIcon}
|
||||||
|
name={'timer'}
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.textDisabled}
|
||||||
|
/>
|
||||||
|
<Text>{stats.time}</Text>
|
||||||
|
</View>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
70
src/screens/Game/components/WelcomeGameContent.tsx
Normal file
70
src/screens/Game/components/WelcomeGameContent.tsx
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import React from 'react';
|
||||||
|
import { StyleSheet, View } from 'react-native';
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Divider,
|
||||||
|
Headline,
|
||||||
|
Paragraph,
|
||||||
|
useTheme,
|
||||||
|
} from 'react-native-paper';
|
||||||
|
import Mascot, { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
||||||
|
import SpeechArrow from '../../../components/Mascot/SpeechArrow';
|
||||||
|
import i18n from 'i18n-js';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
welcomeMascot: {
|
||||||
|
width: '40%',
|
||||||
|
marginLeft: 'auto',
|
||||||
|
marginRight: 'auto',
|
||||||
|
},
|
||||||
|
welcomeCard: {
|
||||||
|
borderWidth: 2,
|
||||||
|
marginLeft: 10,
|
||||||
|
marginRight: 10,
|
||||||
|
},
|
||||||
|
speechArrow: {
|
||||||
|
marginLeft: '60%',
|
||||||
|
},
|
||||||
|
welcomeText: {
|
||||||
|
textAlign: 'center',
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
centertext: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function WelcomeGameContent() {
|
||||||
|
const theme = useTheme();
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Mascot emotion={MASCOT_STYLE.COOL} style={styles.welcomeMascot} />
|
||||||
|
<SpeechArrow
|
||||||
|
style={styles.speechArrow}
|
||||||
|
size={20}
|
||||||
|
color={theme.colors.mascotMessageArrow}
|
||||||
|
/>
|
||||||
|
<Card
|
||||||
|
style={{
|
||||||
|
borderColor: theme.colors.mascotMessageArrow,
|
||||||
|
...styles.welcomeCard,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Card.Content>
|
||||||
|
<Headline
|
||||||
|
style={{
|
||||||
|
color: theme.colors.primary,
|
||||||
|
...styles.centertext,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n.t('screens.game.welcomeTitle')}
|
||||||
|
</Headline>
|
||||||
|
<Divider />
|
||||||
|
<Paragraph style={styles.welcomeText}>
|
||||||
|
{i18n.t('screens.game.welcomeMessage')}
|
||||||
|
</Paragraph>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,10 +17,9 @@
|
||||||
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
* along with Campus INSAT. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import React, { useLayoutEffect, useRef, useState } from 'react';
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import { Caption, IconButton, Text, withTheme } from 'react-native-paper';
|
import { useTheme } from 'react-native-paper';
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import GameLogic from '../logic/GameLogic';
|
import GameLogic from '../logic/GameLogic';
|
||||||
|
@ -34,25 +33,15 @@ import type { OptionsDialogButtonType } from '../../../components/Dialogs/Option
|
||||||
import OptionsDialog from '../../../components/Dialogs/OptionsDialog';
|
import OptionsDialog from '../../../components/Dialogs/OptionsDialog';
|
||||||
import GENERAL_STYLES from '../../../constants/Styles';
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
import { MainRoutes } from '../../../navigation/MainNavigator';
|
import { MainRoutes } from '../../../navigation/MainNavigator';
|
||||||
|
import GameStatus from '../components/GameStatus';
|
||||||
type PropsType = {
|
import GameControls from '../components/GameControls';
|
||||||
navigation: StackNavigationProp<any>;
|
import GameScore from '../components/GameScore';
|
||||||
route: { params: { highScore: number } };
|
import { usePreferences } from '../../../context/preferencesContext';
|
||||||
theme: ReactNativePaper.Theme;
|
import {
|
||||||
};
|
getPreferenceObject,
|
||||||
|
PreferenceKeys,
|
||||||
type StateType = {
|
} from '../../../utils/asyncStorage';
|
||||||
grid: GridType;
|
import { useNavigation } from '@react-navigation/core';
|
||||||
gameTime: number;
|
|
||||||
gameScore: number;
|
|
||||||
gameLevel: number;
|
|
||||||
|
|
||||||
dialogVisible: boolean;
|
|
||||||
dialogTitle: string;
|
|
||||||
dialogMessage: string;
|
|
||||||
dialogButtons: Array<OptionsDialogButtonType>;
|
|
||||||
onDialogDismiss: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
container: {
|
container: {
|
||||||
|
@ -62,44 +51,6 @@ const styles = StyleSheet.create({
|
||||||
gridContainer: {
|
gridContainer: {
|
||||||
flex: 4,
|
flex: 4,
|
||||||
},
|
},
|
||||||
centerSmallMargin: {
|
|
||||||
...GENERAL_STYLES.centerHorizontal,
|
|
||||||
marginBottom: 5,
|
|
||||||
},
|
|
||||||
centerVerticalSmallMargin: {
|
|
||||||
...GENERAL_STYLES.centerVertical,
|
|
||||||
marginLeft: 5,
|
|
||||||
},
|
|
||||||
centerBigMargin: {
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginBottom: 20,
|
|
||||||
},
|
|
||||||
statusContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
statusIcon: {
|
|
||||||
marginLeft: 5,
|
|
||||||
},
|
|
||||||
scoreMainContainer: {
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
scoreCurrentContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
},
|
|
||||||
scoreText: {
|
|
||||||
marginLeft: 5,
|
|
||||||
fontSize: 20,
|
|
||||||
},
|
|
||||||
scoreBestContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 5,
|
|
||||||
},
|
|
||||||
controlsContainer: {
|
controlsContainer: {
|
||||||
height: 80,
|
height: 80,
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
|
@ -115,273 +66,125 @@ const styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
class GameMainScreen extends React.Component<PropsType, StateType> {
|
export default function GameMainScreen() {
|
||||||
static getFormattedTime(seconds: number): string {
|
const theme = useTheme();
|
||||||
const date = new Date();
|
const navigation = useNavigation<StackNavigationProp<any>>();
|
||||||
date.setHours(0);
|
const logic = useRef(new GameLogic(20, 10, theme));
|
||||||
date.setMinutes(0);
|
|
||||||
date.setSeconds(seconds);
|
const [gameTime, setGameTime] = useState(0);
|
||||||
let format;
|
|
||||||
if (date.getHours()) {
|
const [gameState, setGameState] = useState({
|
||||||
format = `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
|
grid: logic.current.getCurrentGrid(),
|
||||||
} else if (date.getMinutes()) {
|
gameScore: 0,
|
||||||
format = `${date.getMinutes()}:${date.getSeconds()}`;
|
gameLevel: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const [dialogContent, setDialogContent] = useState<{
|
||||||
|
dialogTitle: string;
|
||||||
|
dialogMessage: string;
|
||||||
|
dialogButtons: Array<OptionsDialogButtonType>;
|
||||||
|
onDialogDismiss: () => void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { preferences, updatePreferences } = usePreferences();
|
||||||
|
|
||||||
|
function getScores() {
|
||||||
|
const pref = getPreferenceObject(PreferenceKeys.gameScores, preferences) as
|
||||||
|
| Array<number>
|
||||||
|
| undefined;
|
||||||
|
if (pref) {
|
||||||
|
return pref.sort((a, b) => b - a);
|
||||||
} else {
|
} else {
|
||||||
format = date.getSeconds().toString();
|
return [];
|
||||||
}
|
|
||||||
return format;
|
|
||||||
}
|
|
||||||
|
|
||||||
logic: GameLogic;
|
|
||||||
|
|
||||||
highScore: number | null;
|
|
||||||
|
|
||||||
constructor(props: PropsType) {
|
|
||||||
super(props);
|
|
||||||
this.highScore = null;
|
|
||||||
this.logic = new GameLogic(20, 10, props.theme);
|
|
||||||
this.state = {
|
|
||||||
grid: this.logic.getCurrentGrid(),
|
|
||||||
gameTime: 0,
|
|
||||||
gameScore: 0,
|
|
||||||
gameLevel: 0,
|
|
||||||
dialogVisible: false,
|
|
||||||
dialogTitle: '',
|
|
||||||
dialogMessage: '',
|
|
||||||
dialogButtons: [],
|
|
||||||
onDialogDismiss: () => {},
|
|
||||||
};
|
|
||||||
if (props.route.params != null) {
|
|
||||||
this.highScore = props.route.params.highScore;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
const savedScores = getScores();
|
||||||
const { navigation } = this.props;
|
const highScore = savedScores.length > 0 ? savedScores[0] : undefined;
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
navigation.setOptions({
|
navigation.setOptions({
|
||||||
headerRight: this.getRightButton,
|
headerRight: getRightButton,
|
||||||
});
|
});
|
||||||
this.startGame();
|
startGame();
|
||||||
}
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [navigation]);
|
||||||
|
|
||||||
componentWillUnmount() {
|
const getRightButton = () => (
|
||||||
this.logic.endGame(true);
|
<MaterialHeaderButtons>
|
||||||
}
|
<Item title={'pause'} iconName={'pause'} onPress={togglePause} />
|
||||||
|
</MaterialHeaderButtons>
|
||||||
|
);
|
||||||
|
|
||||||
getRightButton = () => {
|
const onTick = (score: number, level: number, newGrid: GridType) => {
|
||||||
return (
|
setGameState({
|
||||||
<MaterialHeaderButtons>
|
|
||||||
<Item title="pause" iconName="pause" onPress={this.togglePause} />
|
|
||||||
</MaterialHeaderButtons>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
onTick = (score: number, level: number, newGrid: GridType) => {
|
|
||||||
this.setState({
|
|
||||||
gameScore: score,
|
gameScore: score,
|
||||||
gameLevel: level,
|
gameLevel: level,
|
||||||
grid: newGrid,
|
grid: newGrid,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
onClock = (time: number) => {
|
const onDialogDismiss = () => setDialogContent(undefined);
|
||||||
this.setState({
|
|
||||||
gameTime: time,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onDialogDismiss = () => {
|
const onGameEnd = (time: number, score: number, isRestart: boolean) => {
|
||||||
this.setState({ dialogVisible: false });
|
setGameState((prevState) => ({
|
||||||
};
|
...prevState,
|
||||||
|
|
||||||
onGameEnd = (time: number, score: number, isRestart: boolean) => {
|
|
||||||
const { props, state } = this;
|
|
||||||
this.setState({
|
|
||||||
gameTime: time,
|
|
||||||
gameScore: score,
|
gameScore: score,
|
||||||
});
|
}));
|
||||||
|
setGameTime(time);
|
||||||
|
const newScores = [...savedScores];
|
||||||
|
const isHighScore = newScores.length === 0 || score > newScores[0];
|
||||||
|
for (let i = 0; i < 3; i += 1) {
|
||||||
|
if (newScores.length > i && score > newScores[i]) {
|
||||||
|
newScores.splice(i, 0, score);
|
||||||
|
break;
|
||||||
|
} else if (newScores.length <= i) {
|
||||||
|
newScores.push(score);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newScores.length > 3) {
|
||||||
|
newScores.splice(3, 1);
|
||||||
|
}
|
||||||
|
console.log(newScores);
|
||||||
|
updatePreferences(PreferenceKeys.gameScores, newScores);
|
||||||
if (!isRestart) {
|
if (!isRestart) {
|
||||||
props.navigation.replace(MainRoutes.GameStart, {
|
navigation.replace(MainRoutes.GameStart, {
|
||||||
score: state.gameScore,
|
score: score,
|
||||||
level: state.gameLevel,
|
level: gameState.gameLevel,
|
||||||
time: state.gameTime,
|
time: time,
|
||||||
|
isHighScore: isHighScore,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getStatusIcons() {
|
const onDirectionPressed = (newGrid: GridType, score?: number) => {
|
||||||
const { props, state } = this;
|
setGameState((prevState) => ({
|
||||||
return (
|
...prevState,
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
...GENERAL_STYLES.flex,
|
|
||||||
...GENERAL_STYLES.centerVertical,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={GENERAL_STYLES.centerHorizontal}>
|
|
||||||
<Caption style={styles.centerSmallMargin}>
|
|
||||||
{i18n.t('screens.game.time')}
|
|
||||||
</Caption>
|
|
||||||
<View style={styles.statusContainer}>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="timer"
|
|
||||||
color={props.theme.colors.subtitle}
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
...styles.statusIcon,
|
|
||||||
color: props.theme.colors.subtitle,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{GameMainScreen.getFormattedTime(state.gameTime)}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
<View style={styles.centerBigMargin}>
|
|
||||||
<Caption style={styles.centerSmallMargin}>
|
|
||||||
{i18n.t('screens.game.level')}
|
|
||||||
</Caption>
|
|
||||||
<View style={styles.statusContainer}>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="gamepad-square"
|
|
||||||
color={props.theme.colors.text}
|
|
||||||
size={20}
|
|
||||||
/>
|
|
||||||
<Text style={styles.statusIcon}>{state.gameLevel}</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getScoreIcon() {
|
|
||||||
const { props, state } = this;
|
|
||||||
const highScore =
|
|
||||||
this.highScore == null || state.gameScore > this.highScore
|
|
||||||
? state.gameScore
|
|
||||||
: this.highScore;
|
|
||||||
return (
|
|
||||||
<View style={styles.scoreMainContainer}>
|
|
||||||
<View style={styles.scoreCurrentContainer}>
|
|
||||||
<Text style={styles.scoreText}>
|
|
||||||
{i18n.t('screens.game.score', { score: state.gameScore })}
|
|
||||||
</Text>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="star"
|
|
||||||
color={props.theme.colors.tetrisScore}
|
|
||||||
size={20}
|
|
||||||
style={styles.centerVerticalSmallMargin}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.scoreBestContainer}>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
...styles.scoreText,
|
|
||||||
color: props.theme.colors.textDisabled,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{i18n.t('screens.game.highScore', { score: highScore })}
|
|
||||||
</Text>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="star"
|
|
||||||
color={props.theme.colors.tetrisScore}
|
|
||||||
size={10}
|
|
||||||
style={styles.centerVerticalSmallMargin}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getControlButtons() {
|
|
||||||
const { props } = this;
|
|
||||||
return (
|
|
||||||
<View style={styles.controlsContainer}>
|
|
||||||
<IconButton
|
|
||||||
icon="rotate-right-variant"
|
|
||||||
size={40}
|
|
||||||
onPress={() => {
|
|
||||||
this.logic.rotatePressed(this.updateGrid);
|
|
||||||
}}
|
|
||||||
style={GENERAL_STYLES.flex}
|
|
||||||
/>
|
|
||||||
<View style={styles.directionsContainer}>
|
|
||||||
<IconButton
|
|
||||||
icon="chevron-left"
|
|
||||||
size={40}
|
|
||||||
style={GENERAL_STYLES.flex}
|
|
||||||
onPress={() => {
|
|
||||||
this.logic.pressedOut();
|
|
||||||
}}
|
|
||||||
onPressIn={() => {
|
|
||||||
this.logic.leftPressedIn(this.updateGrid);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
icon="chevron-right"
|
|
||||||
size={40}
|
|
||||||
style={GENERAL_STYLES.flex}
|
|
||||||
onPress={() => {
|
|
||||||
this.logic.pressedOut();
|
|
||||||
}}
|
|
||||||
onPressIn={() => {
|
|
||||||
this.logic.rightPressed(this.updateGrid);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<IconButton
|
|
||||||
icon="arrow-down-bold"
|
|
||||||
size={40}
|
|
||||||
onPressIn={() => {
|
|
||||||
this.logic.downPressedIn(this.updateGridScore);
|
|
||||||
}}
|
|
||||||
onPress={() => {
|
|
||||||
this.logic.pressedOut();
|
|
||||||
}}
|
|
||||||
style={GENERAL_STYLES.flex}
|
|
||||||
color={props.theme.colors.tetrisScore}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateGrid = (newGrid: GridType) => {
|
|
||||||
this.setState({
|
|
||||||
grid: newGrid,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateGridScore = (newGrid: GridType, score?: number) => {
|
|
||||||
this.setState((prevState: StateType): {
|
|
||||||
grid: GridType;
|
|
||||||
gameScore: number;
|
|
||||||
} => ({
|
|
||||||
grid: newGrid,
|
grid: newGrid,
|
||||||
gameScore: score != null ? score : prevState.gameScore,
|
gameScore: score != null ? score : prevState.gameScore,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
togglePause = () => {
|
const togglePause = () => {
|
||||||
this.logic.togglePause();
|
logic.current.togglePause();
|
||||||
if (this.logic.isGamePaused()) {
|
if (logic.current.isGamePaused()) {
|
||||||
this.showPausePopup();
|
showPausePopup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
showPausePopup = () => {
|
const showPausePopup = () => {
|
||||||
const onDismiss = () => {
|
const onDismiss = () => {
|
||||||
this.togglePause();
|
togglePause();
|
||||||
this.onDialogDismiss();
|
onDialogDismiss();
|
||||||
};
|
};
|
||||||
this.setState({
|
setDialogContent({
|
||||||
dialogVisible: true,
|
|
||||||
dialogTitle: i18n.t('screens.game.pause'),
|
dialogTitle: i18n.t('screens.game.pause'),
|
||||||
dialogMessage: i18n.t('screens.game.pauseMessage'),
|
dialogMessage: i18n.t('screens.game.pauseMessage'),
|
||||||
dialogButtons: [
|
dialogButtons: [
|
||||||
{
|
{
|
||||||
title: i18n.t('screens.game.restart.text'),
|
title: i18n.t('screens.game.restart.text'),
|
||||||
onPress: this.showRestartConfirm,
|
onPress: showRestartConfirm,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18n.t('screens.game.resume'),
|
title: i18n.t('screens.game.resume'),
|
||||||
|
@ -392,71 +195,68 @@ class GameMainScreen extends React.Component<PropsType, StateType> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
showRestartConfirm = () => {
|
const showRestartConfirm = () => {
|
||||||
this.setState({
|
setDialogContent({
|
||||||
dialogVisible: true,
|
|
||||||
dialogTitle: i18n.t('screens.game.restart.confirm'),
|
dialogTitle: i18n.t('screens.game.restart.confirm'),
|
||||||
dialogMessage: i18n.t('screens.game.restart.confirmMessage'),
|
dialogMessage: i18n.t('screens.game.restart.confirmMessage'),
|
||||||
dialogButtons: [
|
dialogButtons: [
|
||||||
{
|
{
|
||||||
title: i18n.t('screens.game.restart.confirmYes'),
|
title: i18n.t('screens.game.restart.confirmYes'),
|
||||||
onPress: () => {
|
onPress: () => {
|
||||||
this.onDialogDismiss();
|
onDialogDismiss();
|
||||||
this.startGame();
|
startGame();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18n.t('screens.game.restart.confirmNo'),
|
title: i18n.t('screens.game.restart.confirmNo'),
|
||||||
onPress: this.showPausePopup,
|
onPress: showPausePopup,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
onDialogDismiss: this.showPausePopup,
|
onDialogDismiss: showPausePopup,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
startGame = () => {
|
const startGame = () => {
|
||||||
this.logic.startGame(this.onTick, this.onClock, this.onGameEnd);
|
logic.current.startGame(onTick, setGameTime, onGameEnd);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
return (
|
||||||
const { props, state } = this;
|
<View style={GENERAL_STYLES.flex}>
|
||||||
return (
|
<View style={styles.container}>
|
||||||
<View style={GENERAL_STYLES.flex}>
|
<GameStatus time={gameTime} level={gameState.gameLevel} />
|
||||||
<View style={styles.container}>
|
<View style={styles.gridContainer}>
|
||||||
{this.getStatusIcons()}
|
<GameScore score={gameState.gameScore} highScore={highScore} />
|
||||||
<View style={styles.gridContainer}>
|
<GridComponent
|
||||||
{this.getScoreIcon()}
|
width={logic.current.getWidth()}
|
||||||
<GridComponent
|
height={logic.current.getHeight()}
|
||||||
width={this.logic.getWidth()}
|
grid={gameState.grid}
|
||||||
height={this.logic.getHeight()}
|
style={{
|
||||||
grid={state.grid}
|
backgroundColor: theme.colors.tetrisBackground,
|
||||||
style={{
|
...GENERAL_STYLES.flex,
|
||||||
backgroundColor: props.theme.colors.tetrisBackground,
|
...GENERAL_STYLES.centerHorizontal,
|
||||||
...GENERAL_STYLES.flex,
|
}}
|
||||||
...GENERAL_STYLES.centerHorizontal,
|
/>
|
||||||
}}
|
</View>
|
||||||
/>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
</View>
|
<Preview
|
||||||
|
items={logic.current.getNextPiecesPreviews()}
|
||||||
<View style={GENERAL_STYLES.flex}>
|
style={styles.preview}
|
||||||
<Preview
|
/>
|
||||||
items={this.logic.getNextPiecesPreviews()}
|
|
||||||
style={styles.preview}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
{this.getControlButtons()}
|
|
||||||
|
|
||||||
<OptionsDialog
|
|
||||||
visible={state.dialogVisible}
|
|
||||||
title={state.dialogTitle}
|
|
||||||
message={state.dialogMessage}
|
|
||||||
buttons={state.dialogButtons}
|
|
||||||
onDismiss={state.onDialogDismiss}
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
<GameControls
|
||||||
}
|
logic={logic.current}
|
||||||
|
onDirectionPressed={onDirectionPressed}
|
||||||
|
/>
|
||||||
|
{dialogContent ? (
|
||||||
|
<OptionsDialog
|
||||||
|
visible={dialogContent !== undefined}
|
||||||
|
title={dialogContent.dialogTitle}
|
||||||
|
message={dialogContent.dialogMessage}
|
||||||
|
buttons={dialogContent.dialogButtons}
|
||||||
|
onDismiss={dialogContent.onDialogDismiss}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(GameMainScreen);
|
|
||||||
|
|
|
@ -18,478 +18,121 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { Button, useTheme } from 'react-native-paper';
|
||||||
import {
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Divider,
|
|
||||||
Headline,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
withTheme,
|
|
||||||
} from 'react-native-paper';
|
|
||||||
import { StyleSheet, View } from 'react-native';
|
import { StyleSheet, View } from 'react-native';
|
||||||
import i18n from 'i18n-js';
|
import i18n from 'i18n-js';
|
||||||
import * as Animatable from 'react-native-animatable';
|
|
||||||
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
|
|
||||||
import LinearGradient from 'react-native-linear-gradient';
|
import LinearGradient from 'react-native-linear-gradient';
|
||||||
import Mascot, { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
import { MASCOT_STYLE } from '../../../components/Mascot/Mascot';
|
||||||
import MascotPopup from '../../../components/Mascot/MascotPopup';
|
import MascotPopup from '../../../components/Mascot/MascotPopup';
|
||||||
import type { GridType } from '../components/GridComponent';
|
|
||||||
import GridComponent from '../components/GridComponent';
|
|
||||||
import GridManager from '../logic/GridManager';
|
|
||||||
import Piece from '../logic/Piece';
|
|
||||||
import SpeechArrow from '../../../components/Mascot/SpeechArrow';
|
|
||||||
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
import CollapsibleScrollView from '../../../components/Collapsible/CollapsibleScrollView';
|
||||||
import GENERAL_STYLES from '../../../constants/Styles';
|
import GENERAL_STYLES from '../../../constants/Styles';
|
||||||
|
import GameBackground from '../components/GameBrackground';
|
||||||
|
import PostGameContent from '../components/PostGameContent';
|
||||||
|
import WelcomeGameContent from '../components/WelcomeGameContent';
|
||||||
|
import FullGamePodium from '../components/FullGamePodium';
|
||||||
|
import { useNavigation } from '@react-navigation/core';
|
||||||
|
import { usePreferences } from '../../../context/preferencesContext';
|
||||||
|
import {
|
||||||
|
getPreferenceObject,
|
||||||
|
PreferenceKeys,
|
||||||
|
} from '../../../utils/asyncStorage';
|
||||||
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
|
||||||
type GameStatsType = {
|
type GameStatsType = {
|
||||||
score: number;
|
score: number;
|
||||||
level: number;
|
level: number;
|
||||||
time: number;
|
time: number;
|
||||||
|
isHighScore: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsType = {
|
type Props = {
|
||||||
navigation: StackNavigationProp<any>;
|
|
||||||
route: {
|
route: {
|
||||||
params: GameStatsType;
|
params?: GameStatsType;
|
||||||
};
|
};
|
||||||
theme: ReactNativePaper.Theme;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
pieceContainer: {
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
},
|
|
||||||
pieceBackground: {
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
playButton: {
|
playButton: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
marginTop: 10,
|
marginTop: 10,
|
||||||
},
|
},
|
||||||
recapCard: {
|
|
||||||
borderWidth: 2,
|
|
||||||
marginLeft: 20,
|
|
||||||
marginRight: 20,
|
|
||||||
},
|
|
||||||
recapContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
},
|
|
||||||
recapScoreContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
marginTop: 10,
|
|
||||||
marginBottom: 10,
|
|
||||||
},
|
|
||||||
recapScore: {
|
|
||||||
fontSize: 20,
|
|
||||||
},
|
|
||||||
recapScoreIcon: {
|
|
||||||
marginLeft: 5,
|
|
||||||
},
|
|
||||||
recapIcon: {
|
|
||||||
marginRight: 5,
|
|
||||||
marginLeft: 5,
|
|
||||||
},
|
|
||||||
welcomeMascot: {
|
|
||||||
width: '40%',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
},
|
|
||||||
welcomeCard: {
|
|
||||||
borderWidth: 2,
|
|
||||||
marginLeft: 10,
|
|
||||||
marginRight: 10,
|
|
||||||
},
|
|
||||||
centertext: {
|
|
||||||
textAlign: 'center',
|
|
||||||
},
|
|
||||||
welcomeText: {
|
|
||||||
textAlign: 'center',
|
|
||||||
marginTop: 10,
|
|
||||||
},
|
|
||||||
speechArrow: {
|
|
||||||
marginLeft: '60%',
|
|
||||||
},
|
|
||||||
podiumContainer: {
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
},
|
|
||||||
podiumIconContainer: {
|
|
||||||
position: 'absolute',
|
|
||||||
top: -20,
|
|
||||||
},
|
|
||||||
topScoreContainer: {
|
|
||||||
marginBottom: 20,
|
|
||||||
marginTop: 20,
|
|
||||||
},
|
|
||||||
topScoreSubcontainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
marginLeft: 'auto',
|
|
||||||
marginRight: 'auto',
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
class GameStartScreen extends React.Component<PropsType> {
|
export default function GameStartScreen(props: Props) {
|
||||||
gridManager: GridManager;
|
const theme = useTheme();
|
||||||
|
const navigation = useNavigation<StackNavigationProp<any>>();
|
||||||
|
|
||||||
scores: Array<number>;
|
const { preferences } = usePreferences();
|
||||||
|
|
||||||
gameStats?: GameStatsType;
|
function getScores() {
|
||||||
|
const pref = getPreferenceObject(PreferenceKeys.gameScores, preferences) as
|
||||||
isHighScore: boolean;
|
| Array<number>
|
||||||
|
| undefined;
|
||||||
constructor(props: PropsType) {
|
if (pref) {
|
||||||
super(props);
|
return pref.sort((a, b) => b - a);
|
||||||
this.isHighScore = false;
|
} else {
|
||||||
this.gridManager = new GridManager(4, 4, props.theme);
|
return [];
|
||||||
// TODO
|
|
||||||
// this.scores = AsyncStorageManager.getObject(
|
|
||||||
// AsyncStorageManager.PREFERENCES.gameScores.key
|
|
||||||
// );
|
|
||||||
this.scores = [];
|
|
||||||
this.scores.sort((a: number, b: number): number => b - a);
|
|
||||||
if (props.route.params != null) {
|
|
||||||
this.recoverGameScore();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getPiecesBackground() {
|
const scores = getScores();
|
||||||
const { theme } = this.props;
|
const lastGameStats = props.route.params;
|
||||||
const gridList = [];
|
|
||||||
for (let i = 0; i < 18; i += 1) {
|
|
||||||
gridList.push(this.gridManager.getEmptyGrid(4, 4));
|
|
||||||
const piece = new Piece(theme);
|
|
||||||
piece.toGrid(gridList[i], true);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<View style={styles.pieceContainer}>
|
|
||||||
{gridList.map((item: GridType, index: number) => {
|
|
||||||
const size = 10 + Math.floor(Math.random() * 30);
|
|
||||||
const top = Math.floor(Math.random() * 100);
|
|
||||||
const rot = Math.floor(Math.random() * 360);
|
|
||||||
const left = (index % 6) * 20;
|
|
||||||
const animDelay = size * 20;
|
|
||||||
const animDuration = 2 * (2000 - size * 30);
|
|
||||||
return (
|
|
||||||
<Animatable.View
|
|
||||||
useNativeDriver
|
|
||||||
animation="fadeInDownBig"
|
|
||||||
delay={animDelay}
|
|
||||||
duration={animDuration}
|
|
||||||
key={`piece${index.toString()}`}
|
|
||||||
style={{
|
|
||||||
width: `${size}%`,
|
|
||||||
top: `${top}%`,
|
|
||||||
left: `${left}%`,
|
|
||||||
...styles.pieceBackground,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<GridComponent
|
|
||||||
width={4}
|
|
||||||
height={4}
|
|
||||||
grid={item}
|
|
||||||
style={{
|
|
||||||
transform: [{ rotateZ: `${rot}deg` }],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Animatable.View>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPostGameContent(stats: GameStatsType) {
|
const getMainContent = () => {
|
||||||
const { props } = this;
|
|
||||||
const width = this.isHighScore ? '50%' : '30%';
|
|
||||||
const margin = this.isHighScore ? 'auto' : undefined;
|
|
||||||
const marginLeft = this.isHighScore ? '60%' : '20%';
|
|
||||||
const color = this.isHighScore
|
|
||||||
? props.theme.colors.gameGold
|
|
||||||
: props.theme.colors.primary;
|
|
||||||
return (
|
return (
|
||||||
<View style={GENERAL_STYLES.flex}>
|
<View style={GENERAL_STYLES.flex}>
|
||||||
<Mascot
|
{lastGameStats ? (
|
||||||
emotion={this.isHighScore ? MASCOT_STYLE.LOVE : MASCOT_STYLE.NORMAL}
|
<PostGameContent
|
||||||
animated={this.isHighScore}
|
stats={lastGameStats}
|
||||||
style={{
|
isHighScore={lastGameStats.isHighScore}
|
||||||
width: width,
|
/>
|
||||||
marginLeft: margin,
|
) : (
|
||||||
marginRight: margin,
|
<WelcomeGameContent />
|
||||||
}}
|
)}
|
||||||
/>
|
|
||||||
<SpeechArrow
|
|
||||||
style={{ marginLeft: marginLeft }}
|
|
||||||
size={20}
|
|
||||||
color={props.theme.colors.mascotMessageArrow}
|
|
||||||
/>
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
borderColor: props.theme.colors.mascotMessageArrow,
|
|
||||||
...styles.recapCard,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card.Content>
|
|
||||||
<Headline
|
|
||||||
style={{
|
|
||||||
color: color,
|
|
||||||
...styles.centertext,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{this.isHighScore
|
|
||||||
? i18n.t('screens.game.newHighScore')
|
|
||||||
: i18n.t('screens.game.gameOver')}
|
|
||||||
</Headline>
|
|
||||||
<Divider />
|
|
||||||
<View style={styles.recapScoreContainer}>
|
|
||||||
<Text style={styles.recapScore}>
|
|
||||||
{i18n.t('screens.game.score', { score: stats.score })}
|
|
||||||
</Text>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="star"
|
|
||||||
color={props.theme.colors.tetrisScore}
|
|
||||||
size={30}
|
|
||||||
style={styles.recapScoreIcon}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View style={styles.recapContainer}>
|
|
||||||
<Text>{i18n.t('screens.game.level')}</Text>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
style={styles.recapIcon}
|
|
||||||
name="gamepad-square"
|
|
||||||
size={20}
|
|
||||||
color={props.theme.colors.textDisabled}
|
|
||||||
/>
|
|
||||||
<Text>{stats.level}</Text>
|
|
||||||
</View>
|
|
||||||
<View style={styles.recapContainer}>
|
|
||||||
<Text>{i18n.t('screens.game.time')}</Text>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
style={styles.recapIcon}
|
|
||||||
name="timer"
|
|
||||||
size={20}
|
|
||||||
color={props.theme.colors.textDisabled}
|
|
||||||
/>
|
|
||||||
<Text>{stats.time}</Text>
|
|
||||||
</View>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getWelcomeText() {
|
|
||||||
const { props } = this;
|
|
||||||
return (
|
|
||||||
<View>
|
|
||||||
<Mascot emotion={MASCOT_STYLE.COOL} style={styles.welcomeMascot} />
|
|
||||||
<SpeechArrow
|
|
||||||
style={styles.speechArrow}
|
|
||||||
size={20}
|
|
||||||
color={props.theme.colors.mascotMessageArrow}
|
|
||||||
/>
|
|
||||||
<Card
|
|
||||||
style={{
|
|
||||||
borderColor: props.theme.colors.mascotMessageArrow,
|
|
||||||
...styles.welcomeCard,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Card.Content>
|
|
||||||
<Headline
|
|
||||||
style={{
|
|
||||||
color: props.theme.colors.primary,
|
|
||||||
...styles.centertext,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{i18n.t('screens.game.welcomeTitle')}
|
|
||||||
</Headline>
|
|
||||||
<Divider />
|
|
||||||
<Paragraph style={styles.welcomeText}>
|
|
||||||
{i18n.t('screens.game.welcomeMessage')}
|
|
||||||
</Paragraph>
|
|
||||||
</Card.Content>
|
|
||||||
</Card>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPodiumRender(place: 1 | 2 | 3, score: string) {
|
|
||||||
const { props } = this;
|
|
||||||
let icon = 'podium-gold';
|
|
||||||
let color = props.theme.colors.gameGold;
|
|
||||||
let fontSize = 20;
|
|
||||||
let size = 70;
|
|
||||||
if (place === 2) {
|
|
||||||
icon = 'podium-silver';
|
|
||||||
color = props.theme.colors.gameSilver;
|
|
||||||
fontSize = 18;
|
|
||||||
size = 60;
|
|
||||||
} else if (place === 3) {
|
|
||||||
icon = 'podium-bronze';
|
|
||||||
color = props.theme.colors.gameBronze;
|
|
||||||
fontSize = 15;
|
|
||||||
size = 50;
|
|
||||||
}
|
|
||||||
const marginLeft = place === 2 ? 20 : 'auto';
|
|
||||||
const marginRight = place === 3 ? 20 : 'auto';
|
|
||||||
const fontWeight = place === 1 ? 'bold' : undefined;
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
marginLeft: marginLeft,
|
|
||||||
marginRight: marginRight,
|
|
||||||
...styles.podiumContainer,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{this.isHighScore && place === 1 ? (
|
|
||||||
<Animatable.View
|
|
||||||
animation="swing"
|
|
||||||
iterationCount="infinite"
|
|
||||||
duration={2000}
|
|
||||||
delay={1000}
|
|
||||||
useNativeDriver
|
|
||||||
style={styles.podiumIconContainer}
|
|
||||||
>
|
|
||||||
<Animatable.View
|
|
||||||
animation="pulse"
|
|
||||||
iterationCount="infinite"
|
|
||||||
useNativeDriver
|
|
||||||
>
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name="decagram"
|
|
||||||
color={props.theme.colors.gameGold}
|
|
||||||
size={150}
|
|
||||||
/>
|
|
||||||
</Animatable.View>
|
|
||||||
</Animatable.View>
|
|
||||||
) : null}
|
|
||||||
<MaterialCommunityIcons
|
|
||||||
name={icon}
|
|
||||||
color={this.isHighScore && place === 1 ? '#fff' : color}
|
|
||||||
size={size}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
fontWeight: fontWeight,
|
|
||||||
fontSize,
|
|
||||||
...styles.centertext,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{score}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTopScoresRender() {
|
|
||||||
const gold = this.scores.length > 0 ? this.scores[0] : '-';
|
|
||||||
const silver = this.scores.length > 1 ? this.scores[1] : '-';
|
|
||||||
const bronze = this.scores.length > 2 ? this.scores[2] : '-';
|
|
||||||
return (
|
|
||||||
<View style={styles.topScoreContainer}>
|
|
||||||
{this.getPodiumRender(1, gold.toString())}
|
|
||||||
<View style={styles.topScoreSubcontainer}>
|
|
||||||
{this.getPodiumRender(3, bronze.toString())}
|
|
||||||
{this.getPodiumRender(2, silver.toString())}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMainContent() {
|
|
||||||
const { props } = this;
|
|
||||||
return (
|
|
||||||
<View style={GENERAL_STYLES.flex}>
|
|
||||||
{this.gameStats != null
|
|
||||||
? this.getPostGameContent(this.gameStats)
|
|
||||||
: this.getWelcomeText()}
|
|
||||||
<Button
|
<Button
|
||||||
icon="play"
|
icon={'play'}
|
||||||
mode="contained"
|
mode={'contained'}
|
||||||
onPress={() => {
|
onPress={() => {
|
||||||
props.navigation.replace('game-main', {
|
navigation.replace('game-main');
|
||||||
highScore: this.scores.length > 0 ? this.scores[0] : null,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
style={styles.playButton}
|
style={styles.playButton}
|
||||||
>
|
>
|
||||||
{i18n.t('screens.game.play')}
|
{i18n.t('screens.game.play')}
|
||||||
</Button>
|
</Button>
|
||||||
{this.getTopScoresRender()}
|
<FullGamePodium
|
||||||
|
scores={scores}
|
||||||
|
isHighScore={lastGameStats?.isHighScore === true}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
keyExtractor = (item: number): string => item.toString();
|
return (
|
||||||
|
<View style={GENERAL_STYLES.flex}>
|
||||||
recoverGameScore() {
|
<GameBackground />
|
||||||
const { route } = this.props;
|
<LinearGradient
|
||||||
this.gameStats = route.params;
|
style={GENERAL_STYLES.flex}
|
||||||
if (this.gameStats.score != null) {
|
colors={[`${theme.colors.background}00`, theme.colors.background]}
|
||||||
this.isHighScore =
|
start={{ x: 0, y: 0 }}
|
||||||
this.scores.length === 0 || this.gameStats.score > this.scores[0];
|
end={{ x: 0, y: 1 }}
|
||||||
for (let i = 0; i < 3; i += 1) {
|
>
|
||||||
if (this.scores.length > i && this.gameStats.score > this.scores[i]) {
|
<CollapsibleScrollView headerColors={'transparent'}>
|
||||||
this.scores.splice(i, 0, this.gameStats.score);
|
{getMainContent()}
|
||||||
break;
|
<MascotPopup
|
||||||
} else if (this.scores.length <= i) {
|
title={i18n.t('screens.game.mascotDialog.title')}
|
||||||
this.scores.push(this.gameStats.score);
|
message={i18n.t('screens.game.mascotDialog.message')}
|
||||||
break;
|
icon="gamepad-variant"
|
||||||
}
|
buttons={{
|
||||||
}
|
cancel: {
|
||||||
if (this.scores.length > 3) {
|
message: i18n.t('screens.game.mascotDialog.button'),
|
||||||
this.scores.splice(3, 1);
|
icon: 'check',
|
||||||
}
|
},
|
||||||
// TODO
|
}}
|
||||||
// AsyncStorageManager.set(
|
emotion={MASCOT_STYLE.COOL}
|
||||||
// AsyncStorageManager.PREFERENCES.gameScores.key,
|
/>
|
||||||
// this.scores
|
</CollapsibleScrollView>
|
||||||
// );
|
</LinearGradient>
|
||||||
}
|
</View>
|
||||||
}
|
);
|
||||||
|
|
||||||
render() {
|
|
||||||
const { props } = this;
|
|
||||||
return (
|
|
||||||
<View style={GENERAL_STYLES.flex}>
|
|
||||||
{this.getPiecesBackground()}
|
|
||||||
<LinearGradient
|
|
||||||
style={GENERAL_STYLES.flex}
|
|
||||||
colors={[
|
|
||||||
`${props.theme.colors.background}00`,
|
|
||||||
props.theme.colors.background,
|
|
||||||
]}
|
|
||||||
start={{ x: 0, y: 0 }}
|
|
||||||
end={{ x: 0, y: 1 }}
|
|
||||||
>
|
|
||||||
<CollapsibleScrollView headerColors={'transparent'}>
|
|
||||||
{this.getMainContent()}
|
|
||||||
<MascotPopup
|
|
||||||
title={i18n.t('screens.game.mascotDialog.title')}
|
|
||||||
message={i18n.t('screens.game.mascotDialog.message')}
|
|
||||||
icon="gamepad-variant"
|
|
||||||
buttons={{
|
|
||||||
cancel: {
|
|
||||||
message: i18n.t('screens.game.mascotDialog.button'),
|
|
||||||
icon: 'check',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
emotion={MASCOT_STYLE.COOL}
|
|
||||||
/>
|
|
||||||
</CollapsibleScrollView>
|
|
||||||
</LinearGradient>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withTheme(GameStartScreen);
|
|
||||||
|
|
Loading…
Reference in a new issue