From 66c16b5843bf01cbddec09c286422b335169c01f Mon Sep 17 00:00:00 2001 From: Luke Date: Thu, 13 Oct 2022 14:00:34 +0200 Subject: [PATCH] feat(web): skillcheck minigame --- web/src/App.tsx | 2 + web/src/features/skillcheck/index.tsx | 93 +++++++++++++++++++++++ web/src/features/skillcheck/indicator.tsx | 63 +++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 web/src/features/skillcheck/index.tsx create mode 100644 web/src/features/skillcheck/indicator.tsx diff --git a/web/src/App.tsx b/web/src/App.tsx index 45b37f27c..3819d9c27 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -12,6 +12,7 @@ import AlertDialog from './features/dialog/AlertDialog'; import ListMenu from './features/menu/list'; import Dev from './features/dev'; import { isEnvBrowser } from './utils/misc'; +import SkillCheck from './features/skillcheck'; const App: React.FC = () => { useNuiEvent('setClipboard', (data: string) => { @@ -31,6 +32,7 @@ const App: React.FC = () => { + {isEnvBrowser() && } ); diff --git a/web/src/features/skillcheck/index.tsx b/web/src/features/skillcheck/index.tsx new file mode 100644 index 000000000..57c207430 --- /dev/null +++ b/web/src/features/skillcheck/index.tsx @@ -0,0 +1,93 @@ +import { Box, Center } from '@chakra-ui/react'; +import { useEffect, useMemo, useState } from 'react'; +import { useNuiEvent } from '../../hooks/useNuiEvent'; +import { debugData } from '../../utils/debugData'; +import Indicator from './indicator'; + +export interface SkillCheckProps { + visible: boolean; + angle: number; + difficultyOffset: number; + difficulty: 'easy' | 'medium' | 'hard'; +} + +const getRandomAngle = (min: number, max: number) => Math.floor(Math.random() * (max - min)) + min; + +const difficultyOffsets = { + easy: 275, + medium: 290, + hard: 300, +}; + +debugData([ + { + action: 'startSkillCheck', + data: 'easy', + }, +]); + +const SkillCheck: React.FC = () => { + const [skillCheck, setSkillCheck] = useState({ + visible: false, + angle: 0, + difficultyOffset: 315, + difficulty: 'easy', + }); + + useNuiEvent('startSkillCheck', (data: 'easy' | 'medium' | 'hard') => { + setSkillCheck({ + visible: true, + angle: -90 + getRandomAngle(0, 360), + difficultyOffset: difficultyOffsets[data], + difficulty: data, + }); + }); + + return ( +
+ {skillCheck.visible && ( + <> + + {/*Circle track*/} + + {/*SkillCheck area*/} + + + + + E + + + )} +
+ ); +}; + +export default SkillCheck; diff --git a/web/src/features/skillcheck/indicator.tsx b/web/src/features/skillcheck/indicator.tsx new file mode 100644 index 000000000..80ad71467 --- /dev/null +++ b/web/src/features/skillcheck/indicator.tsx @@ -0,0 +1,63 @@ +import { useEffect, useRef, useState } from 'react'; +import { useNuiEvent } from '../../hooks/useNuiEvent'; +import { useKeyPress } from '../../hooks/useKeyPress'; +import { fetchNui } from '../../utils/fetchNui'; +import { SkillCheckProps } from './index'; + +interface Props { + angle: number; + offset: number; + multiplier: number; + setSkillCheck: React.Dispatch>; +} + +const Indicator: React.FC = ({ angle, offset, multiplier, setSkillCheck }) => { + const [indicatorAngle, setIndicatorAngle] = useState(-90); + const intervalRef = useRef(null); + const isKeyPressed = useKeyPress('e'); + + useEffect(() => { + console.log(1); + intervalRef.current = setInterval(() => { + setIndicatorAngle((prevState) => (prevState += multiplier)); + }, 1); + + return () => { + if (intervalRef.current) clearInterval(intervalRef.current); + }; + }, []); + + useEffect(() => { + if (intervalRef.current && indicatorAngle + 90 >= 360) { + clearInterval(intervalRef.current); + fetchNui('skillCheckOver', { success: false }); + setSkillCheck((prevState) => ({ ...prevState, visible: false })); + } + + if (isKeyPressed && intervalRef.current) { + clearInterval(intervalRef.current); + if (indicatorAngle < angle || indicatorAngle > angle + (315 - offset)) { + fetchNui('skillCheckOver', { success: false }); + } else { + fetchNui('skillCheckOver', { success: true }); + } + setSkillCheck((prevState) => ({ ...prevState, visible: false })); + } + }, [indicatorAngle, isKeyPressed]); + + return ( + + ); +}; + +export default Indicator;