diff --git a/web/src/App.tsx b/web/src/App.tsx
index 45b37f2..3819d9c 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 0000000..57c2074
--- /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 && (
+ <>
+
+
+ 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 0000000..80ad714
--- /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;