added strikes and made games easier

This commit is contained in:
lingdocs 2022-05-30 14:43:15 -05:00
parent c907cc0726
commit c64e48d274
2 changed files with 56 additions and 24 deletions

View File

@ -1,4 +1,4 @@
import { useState, useRef } from "react"; import { useState, useRef, useEffect } from "react";
import { CountdownCircleTimer } from "react-countdown-circle-timer"; import { CountdownCircleTimer } from "react-countdown-circle-timer";
import Reward, { RewardElement } from 'react-rewards'; import Reward, { RewardElement } from 'react-rewards';
import Link from "../components/Link"; import Link from "../components/Link";
@ -16,10 +16,12 @@ import {
getTimestamp, getTimestamp,
} from "@lingdocs/lingdocs-main"; } from "@lingdocs/lingdocs-main";
import { import {
randFromArray,
Types, Types,
} from "@lingdocs/pashto-inflector"; } from "@lingdocs/pashto-inflector";
import ReactGA from "react-ga"; import ReactGA from "react-ga";
import { isProd } from "../lib/isProd"; import { isProd } from "../lib/isProd";
import autoAnimate from "@formkit/auto-animate";
const errorVibration = 200; const errorVibration = 200;
const maxStrikes = 2; const maxStrikes = 2;
@ -34,6 +36,7 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
}) { }) {
// TODO: report pass with id to user info // TODO: report pass with id to user info
const rewardRef = useRef<RewardElement | null>(null); const rewardRef = useRef<RewardElement | null>(null);
const parent = useRef<HTMLDivElement | null>(null);
const { user, pullUser, setUser } = useUser(); const { user, pullUser, setUser } = useUser();
const [finish, setFinish] = useState<undefined | "pass" | { msg: "fail", answer: JSX.Element } | "time out">(undefined); const [finish, setFinish] = useState<undefined | "pass" | { msg: "fail", answer: JSX.Element } | "time out">(undefined);
const [strikes, setStrikes] = useState<number>(0); const [strikes, setStrikes] = useState<number>(0);
@ -41,6 +44,9 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
const [current, setCurrent] = useState<Current<T> | undefined>(undefined); const [current, setCurrent] = useState<Current<T> | undefined>(undefined);
const [questionBox, setQuestionBox] = useState<QuestionGenerator<T>>(questions()); const [questionBox, setQuestionBox] = useState<QuestionGenerator<T>>(questions());
const [timerKey, setTimerKey] = useState<number>(1); const [timerKey, setTimerKey] = useState<number>(1);
useEffect(() => {
parent.current && autoAnimate(parent.current)
}, [parent]);
function logGameEvent(action: string) { function logGameEvent(action: string) {
if (isProd && !(user?.admin)) { if (isProd && !(user?.admin)) {
@ -53,15 +59,17 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
} }
function handleCallback(correct: true | JSX.Element) { function handleCallback(correct: true | JSX.Element) {
if (correct === true) handleAdvance(); if (correct === true) {
else if (strikes < maxStrikes) { handleAdvance();
navigator.vibrate(errorVibration); return;
setStrikes(s => s + 1); }
setJustStruck(false); setStrikes(s => s + 1);
navigator.vibrate(errorVibration);
if (strikes < maxStrikes) {
setJustStruck(true);
} else { } else {
logGameEvent("fail on game"); logGameEvent("fail on game");
setFinish({ msg: "fail", answer: correct }); setFinish({ msg: "fail", answer: correct });
navigator.vibrate(errorVibration);
} }
} }
function handleAdvance() { function handleAdvance() {
@ -111,6 +119,8 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
// just for type safety -- the generator will have at least one question // just for type safety -- the generator will have at least one question
if (!value) return; if (!value) return;
setQuestionBox(newQuestionBox); setQuestionBox(newQuestionBox);
setJustStruck(false);
setStrikes(0);
setFinish(undefined); setFinish(undefined);
setCurrent(value); setCurrent(value);
setTimerKey(prev => prev + 1); setTimerKey(prev => prev + 1);
@ -139,22 +149,28 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
<div className="progress" style={{ height: "5px" }}> <div className="progress" style={{ height: "5px" }}>
<div className={`progress-bar bg-${progressColor}`} role="progressbar" style={{ width: getProgressWidth() }} /> <div className={`progress-bar bg-${progressColor}`} role="progressbar" style={{ width: getProgressWidth() }} />
</div> </div>
<div>Strikes: {strikes}</div> {current && <div className="d-flex flex-row justify-content-between mt-2">
{current && <div className="d-flex flex-row-reverse mt-2"> <StrikesDisplay strikes={strikes} />
<CountdownCircleTimer <div className="d-flex flex-row-reverse">
key={timerKey} <CountdownCircleTimer
isPlaying={!!current && !finish} key={timerKey}
size={30} isPlaying={!!current && !finish}
colors={["#555555", "#F7B801", "#A30000"]} size={30}
colorsTime={[timeLimit, timeLimit*0.33, 0]} colors={["#555555", "#F7B801", "#A30000"]}
strokeWidth={4} colorsTime={[timeLimit, timeLimit*0.33, 0]}
strokeLinecap="square" strokeWidth={4}
duration={timeLimit} strokeLinecap="square"
onComplete={handleTimeOut} duration={timeLimit}
/> onComplete={handleTimeOut}
<button onClick={handleQuit} className="btn btn-outline-secondary btn-sm mr-2">Quit</button> />
<button onClick={handleQuit} className="btn btn-outline-secondary btn-sm mr-2">Quit</button>
</div>
</div>} </div>}
<div>OOPS, NOT QUITE - TRY AGAIN</div> <div ref={parent}>
{justStruck && <div className="alert alert-warning my-2" role="alert" style={{ maxWidth: "300px", margin: "0 auto" }}>
{getStrikeMessage()}
</div>}
</div>
<Reward ref={rewardRef} config={{ lifetime: 130, spread: 90, elementCount: 150, zIndex: 999999999 }} type="confetti"> <Reward ref={rewardRef} config={{ lifetime: 130, spread: 90, elementCount: 150, zIndex: 999999999 }} type="confetti">
<div> <div>
{finish === undefined && {finish === undefined &&
@ -212,6 +228,22 @@ function GameCore<T>({ questions, Display, timeLimit, Instructions, studyLink, i
</>; </>;
} }
function StrikesDisplay({ strikes }: { strikes: number }) {
return <div>
{[...Array(strikes)].map(_ => <span key={Math.random()} className="mr-2"></span>)}
</div>;
}
function getStrikeMessage() {
return randFromArray([
"Not quite! Try again.",
"No sorry, try again",
"Umm, no, try again",
"Try again",
"Oooooooo, sorry no...",
]);
}
function failMessage(progress: Progress | undefined, finish: "time out" | { msg: "fail", answer: JSX.Element }): string { function failMessage(progress: Progress | undefined, finish: "time out" | { msg: "fail", answer: JSX.Element }): string {
const pDone = progress ? getPercentageDone(progress) : 0; const pDone = progress ? getPercentageDone(progress) : 0;
const { message, face } = pDone < 20 const { message, face } = pDone < 20

View File

@ -168,8 +168,8 @@ const situations: Situation[] = [
}, },
]; ];
const amount = 17; const amount = 15;
const timeLimit = 90; const timeLimit = 100;
type Question = { type Question = {
EPS: T.EPSelectionComplete, EPS: T.EPSelectionComplete,