mirror of
https://github.com/CommunityOx/ox_lib.git
synced 2026-08-17 06:56:03 +01:00
feat(web/skillcheck): random skill check key from inputs table
This commit is contained in:
@@ -5,14 +5,17 @@ local skillcheck
|
|||||||
|
|
||||||
---@param difficulty SkillCheckDifficulity | SkillCheckDifficulity[]
|
---@param difficulty SkillCheckDifficulity | SkillCheckDifficulity[]
|
||||||
---@return boolean?
|
---@return boolean?
|
||||||
function lib.skillCheck(difficulty)
|
function lib.skillCheck(difficulty, inputs)
|
||||||
if skillcheck then return end
|
if skillcheck then return end
|
||||||
skillcheck = promise:new()
|
skillcheck = promise:new()
|
||||||
|
|
||||||
SetNuiFocus(true, false)
|
SetNuiFocus(true, false)
|
||||||
SendNUIMessage({
|
SendNUIMessage({
|
||||||
action = 'startSkillCheck',
|
action = 'startSkillCheck',
|
||||||
data = difficulty
|
data = {
|
||||||
|
difficulty = difficulty,
|
||||||
|
inputs = inputs
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return Citizen.Await(skillcheck)
|
return Citizen.Await(skillcheck)
|
||||||
|
|||||||
@@ -2,10 +2,13 @@ import { debugData } from '../../../utils/debugData';
|
|||||||
import { GameDifficulty } from '../../skillcheck';
|
import { GameDifficulty } from '../../skillcheck';
|
||||||
|
|
||||||
export const debugSkillCheck = () => {
|
export const debugSkillCheck = () => {
|
||||||
debugData<GameDifficulty | GameDifficulty[]>([
|
debugData<{ difficulty: GameDifficulty | GameDifficulty[]; inputs?: string[] }>([
|
||||||
{
|
{
|
||||||
action: 'startSkillCheck',
|
action: 'startSkillCheck',
|
||||||
data: ['easy', 'easy', 'hard'],
|
data: {
|
||||||
|
difficulty: ['easy', 'easy', 'hard'],
|
||||||
|
inputs: ['W', 'A', 'S', 'D'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export interface SkillCheckProps {
|
|||||||
angle: number;
|
angle: number;
|
||||||
difficultyOffset: number;
|
difficultyOffset: number;
|
||||||
difficulty: GameDifficulty;
|
difficulty: GameDifficulty;
|
||||||
|
key: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const circleCircumference = 2 * 50 * Math.PI;
|
export const circleCircumference = 2 * 50 * Math.PI;
|
||||||
@@ -65,49 +66,56 @@ const useStyles = createStyles((theme) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const SkillCheck: React.FC = () => {
|
const SkillCheck: React.FC = () => {
|
||||||
const theme = useMantineTheme();
|
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const dataRef = useRef<GameDifficulty | GameDifficulty[] | null>(null);
|
const dataRef = useRef<{ difficulty: GameDifficulty | GameDifficulty[]; inputs?: string[] } | null>(null);
|
||||||
const dataIndexRef = useRef<number>(0);
|
const dataIndexRef = useRef<number>(0);
|
||||||
const [skillCheck, setSkillCheck] = useState<SkillCheckProps>({
|
const [skillCheck, setSkillCheck] = useState<SkillCheckProps>({
|
||||||
angle: 0,
|
angle: 0,
|
||||||
difficultyOffset: 50,
|
difficultyOffset: 50,
|
||||||
difficulty: 'easy',
|
difficulty: 'easy',
|
||||||
|
key: 'e',
|
||||||
});
|
});
|
||||||
|
|
||||||
useNuiEvent('startSkillCheck', (data: GameDifficulty | GameDifficulty[]) => {
|
useNuiEvent('startSkillCheck', (data: { difficulty: GameDifficulty | GameDifficulty[]; inputs?: string[] }) => {
|
||||||
dataRef.current = data;
|
dataRef.current = data;
|
||||||
dataIndexRef.current = 0;
|
dataIndexRef.current = 0;
|
||||||
const gameData = Array.isArray(data) ? data[0] : data;
|
const gameData = Array.isArray(data.difficulty) ? data.difficulty[0] : data.difficulty;
|
||||||
const offset = typeof gameData === 'object' ? gameData.areaSize : difficultyOffsets[gameData];
|
const offset = typeof gameData === 'object' ? gameData.areaSize : difficultyOffsets[gameData];
|
||||||
|
const randomKey = data.inputs ? data.inputs[Math.floor(Math.random() * data.inputs.length)] : 'e';
|
||||||
setSkillCheck({
|
setSkillCheck({
|
||||||
angle: -90 + getRandomAngle(120, 360 - offset),
|
angle: -90 + getRandomAngle(120, 360 - offset),
|
||||||
difficultyOffset: offset,
|
difficultyOffset: offset,
|
||||||
difficulty: gameData,
|
difficulty: gameData,
|
||||||
|
key: randomKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleComplete = (success: boolean) => {
|
const handleComplete = (success: boolean) => {
|
||||||
if (!success || !Array.isArray(dataRef.current)) {
|
if (!dataRef.current) return;
|
||||||
|
if (!success || !Array.isArray(dataRef.current.difficulty)) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return fetchNui('skillCheckOver', success);
|
return fetchNui('skillCheckOver', success);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dataIndexRef.current >= dataRef.current.length - 1) {
|
if (dataIndexRef.current >= dataRef.current.difficulty.length - 1) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return fetchNui('skillCheckOver', success);
|
return fetchNui('skillCheckOver', success);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataIndexRef.current++;
|
dataIndexRef.current++;
|
||||||
const data = dataRef.current[dataIndexRef.current];
|
const data = dataRef.current.difficulty[dataIndexRef.current];
|
||||||
|
const key = dataRef.current.inputs
|
||||||
|
? dataRef.current.inputs[Math.floor(Math.random() * dataRef.current.inputs.length)]
|
||||||
|
: 'e';
|
||||||
const offset = typeof data === 'object' ? data.areaSize : difficultyOffsets[data];
|
const offset = typeof data === 'object' ? data.areaSize : difficultyOffsets[data];
|
||||||
setSkillCheck({
|
setSkillCheck({
|
||||||
angle: -90 + getRandomAngle(120, 360 - offset),
|
angle: -90 + getRandomAngle(120, 360 - offset),
|
||||||
difficultyOffset: offset,
|
difficultyOffset: offset,
|
||||||
difficulty: data,
|
difficulty: data,
|
||||||
|
key,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -145,7 +153,7 @@ const SkillCheck: React.FC = () => {
|
|||||||
skillCheck={skillCheck}
|
skillCheck={skillCheck}
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<Box className={classes.button}>E</Box>
|
<Box className={classes.button}>{skillCheck.key.toUpperCase()}</Box>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { useKeyPress } from '../../hooks/useKeyPress';
|
import { useKeyPress } from '../../hooks/useKeyPress';
|
||||||
import { SkillCheckProps } from './index';
|
import { SkillCheckProps } from './index';
|
||||||
// import { useInterval } from '@chakra-ui/react';
|
|
||||||
import { useInterval } from '@mantine/hooks';
|
import { useInterval } from '@mantine/hooks';
|
||||||
import { circleCircumference } from './index';
|
import { circleCircumference } from './index';
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ interface Props {
|
|||||||
|
|
||||||
const Indicator: React.FC<Props> = ({ angle, offset, multiplier, handleComplete, skillCheck, className }) => {
|
const Indicator: React.FC<Props> = ({ angle, offset, multiplier, handleComplete, skillCheck, className }) => {
|
||||||
const [indicatorAngle, setIndicatorAngle] = useState(-90);
|
const [indicatorAngle, setIndicatorAngle] = useState(-90);
|
||||||
const isKeyPressed = useKeyPress('e');
|
const [keyPressed, setKeyPressed] = useState(false);
|
||||||
const interval = useInterval(
|
const interval = useInterval(
|
||||||
() =>
|
() =>
|
||||||
setIndicatorAngle((prevState) => {
|
setIndicatorAngle((prevState) => {
|
||||||
@@ -25,8 +24,17 @@ const Indicator: React.FC<Props> = ({ angle, offset, multiplier, handleComplete,
|
|||||||
1
|
1
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const keyHandler = useCallback(
|
||||||
|
(e: KeyboardEvent) => {
|
||||||
|
if (e.key.toLowerCase() !== skillCheck.key.toLowerCase()) return;
|
||||||
|
setKeyPressed(true);
|
||||||
|
},
|
||||||
|
[skillCheck]
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIndicatorAngle(-90);
|
setIndicatorAngle(-90);
|
||||||
|
window.addEventListener('keydown', keyHandler);
|
||||||
interval.start();
|
interval.start();
|
||||||
}, [skillCheck]);
|
}, [skillCheck]);
|
||||||
|
|
||||||
@@ -38,13 +46,16 @@ const Indicator: React.FC<Props> = ({ angle, offset, multiplier, handleComplete,
|
|||||||
}, [indicatorAngle]);
|
}, [indicatorAngle]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isKeyPressed) return;
|
if (!keyPressed) return;
|
||||||
|
|
||||||
interval.stop();
|
interval.stop();
|
||||||
|
|
||||||
|
setKeyPressed(false);
|
||||||
|
window.removeEventListener('keydown', keyHandler);
|
||||||
|
|
||||||
if (indicatorAngle < angle || indicatorAngle > angle + offset) handleComplete(false);
|
if (indicatorAngle < angle || indicatorAngle > angle + offset) handleComplete(false);
|
||||||
else handleComplete(true);
|
else handleComplete(true);
|
||||||
}, [isKeyPressed]);
|
}, [keyPressed]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<circle
|
<circle
|
||||||
|
|||||||
Reference in New Issue
Block a user