From 1d895e64b009b9164448925376ff7f0abad8fb36 Mon Sep 17 00:00:00 2001 From: ad044 Date: Sat, 20 Feb 2021 18:35:23 +0400 Subject: [PATCH] event rewrite done, fixed a lot of stuff, renamed/moved some stuff --- .../EndScene/EndSelectionScreen.tsx | 14 +- src/components/Images.tsx | 8 +- src/components/KeyPressHandler.tsx | 79 ++-- src/components/Loading.tsx | 3 + src/components/MainScene/HUD.tsx | 3 +- src/components/MainScene/Pause/Pause.tsx | 2 +- src/components/MainScene/Popups/NotFound.tsx | 5 +- .../MainScene/Site/InactiveLevelNodes.tsx | 2 +- src/components/MainScene/Site/Site.tsx | 2 +- src/components/PolytanScene/PolytanBear.tsx | 32 +- src/components/TextRenderer/BigLetter.tsx | 1 - .../TextRenderer/MainYellowTextAnimator.tsx | 2 +- src/core/eventTemplates.ts | 447 +++++++++++++----- .../{scene-event-handlers => }/handleEvent.ts | 33 +- .../handleBootSceneEvent.ts | 84 ---- .../handleEndSceneEvent.ts | 20 - .../handleMediaSceneEvent.ts | 84 ---- .../handleSsknSceneEvent.ts | 41 -- .../handleBootSceneKeyPress.ts | 120 ++--- .../handleEndSceneKeyPress.ts | 20 +- .../handleMainSceneKeyPress.ts | 52 +- .../handleMediaSceneKeyPress.ts | 55 ++- .../handleSsknSceneKeyPress.ts | 46 +- .../idle-utils.ts => helpers/idle-helpers.ts} | 0 .../media-helpers.ts} | 22 +- .../name-selection-helpers.ts} | 0 .../node-animation-helpers.ts} | 10 +- .../node-utils.ts => helpers/node-helpers.ts} | 8 +- src/scenes/EndScene.tsx | 7 +- src/scenes/GateScene.tsx | 4 +- src/scenes/MainScene.tsx | 20 +- src/scenes/MediaScene.tsx | 2 +- src/scenes/PolytanScene.tsx | 7 +- src/store.ts | 315 ++++++------ src/tests/media-helpers.test.ts | 2 + src/utils/{keyPressUtils.ts => getKey.ts} | 0 36 files changed, 791 insertions(+), 761 deletions(-) rename src/core/{scene-event-handlers => }/handleEvent.ts (56%) delete mode 100644 src/core/scene-event-handlers/handleBootSceneEvent.ts delete mode 100644 src/core/scene-event-handlers/handleEndSceneEvent.ts delete mode 100644 src/core/scene-event-handlers/handleMediaSceneEvent.ts delete mode 100644 src/core/scene-event-handlers/handleSsknSceneEvent.ts rename src/{utils/idle-utils.ts => helpers/idle-helpers.ts} (100%) rename src/{utils/media-utils.ts => helpers/media-helpers.ts} (76%) rename src/{utils/handleNameSelection.ts => helpers/name-selection-helpers.ts} (100%) rename src/{utils/node-animations.ts => helpers/node-animation-helpers.ts} (99%) rename src/{utils/node-utils.ts => helpers/node-helpers.ts} (99%) create mode 100644 src/tests/media-helpers.test.ts rename src/utils/{keyPressUtils.ts => getKey.ts} (100%) diff --git a/src/components/EndScene/EndSelectionScreen.tsx b/src/components/EndScene/EndSelectionScreen.tsx index 41f8f1d..fb087a1 100644 --- a/src/components/EndScene/EndSelectionScreen.tsx +++ b/src/components/EndScene/EndSelectionScreen.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState } from "react"; +import React, { memo, useRef, useState } from "react"; import middleSpritesheet from "../../static/sprite/end_middle_spritesheet.png"; import middleLain from "../../static/sprite/end_middle_lain.png"; import circleSpritesheet from "../../static/sprite/end_circle_spritesheet.png"; @@ -10,7 +10,11 @@ import { PlainAnimator } from "three-plain-animator/lib/plain-animator"; import { a, useSpring } from "@react-spring/three"; import { useStore } from "../../store"; -const EndSelectionScreen = () => { +type EndSelectionScreenProps = { + visible: boolean; +}; + +const EndSelectionScreen = memo((props: EndSelectionScreenProps) => { const middleSpritesheetTex: any = useLoader( THREE.TextureLoader, middleSpritesheet @@ -63,7 +67,7 @@ const EndSelectionScreen = () => { const lainOpacity = lainOpacityToggle.to([0, 1], [0, 0.5]); return ( - <> + @@ -93,8 +97,8 @@ const EndSelectionScreen = () => { opacity={lainOpacity} /> - + ); -}; +}); export default EndSelectionScreen; diff --git a/src/components/Images.tsx b/src/components/Images.tsx index 4e6af8b..4d23875 100644 --- a/src/components/Images.tsx +++ b/src/components/Images.tsx @@ -31,13 +31,7 @@ const Images = () => { const textureLoader = useMemo(() => new THREE.TextureLoader(), []); useEffect(() => { - let images; - if (currentScene === "media" || currentScene === "tak") { - images = nodeImages; - } else if (currentScene === "idle_media") { - images = idleNodeImages; - } - + const images = currentScene === "idle_media" ? idleNodeImages : nodeImages; if (images) { // checking the length of the img arr doesn't work in some cases // since the amount of images varies from 1 to 3. diff --git a/src/components/KeyPressHandler.tsx b/src/components/KeyPressHandler.tsx index 1b15ad2..491febe 100644 --- a/src/components/KeyPressHandler.tsx +++ b/src/components/KeyPressHandler.tsx @@ -1,32 +1,23 @@ import { useCallback, useEffect, useRef } from "react"; import { - BootSceneContext, - EndSceneContext, getBootSceneContext, getEndSceneContext, getMainSceneContext, getMediaSceneContext, getSsknSceneContext, - MainSceneContext, - MediaSceneContext, playAudio, - SsknSceneContext, useStore, } from "../store"; -import { getKeyCodeAssociation } from "../utils/keyPressUtils"; +import { getKeyCodeAssociation } from "../utils/getKey"; import handleMediaSceneKeyPress from "../core/scene-keypress-handlers/handleMediaSceneKeyPress"; import handleSsknSceneKeyPress from "../core/scene-keypress-handlers/handleSsknSceneKeyPress"; import handleMainSceneKeyPress from "../core/scene-keypress-handlers/handleMainSceneKeyPress"; import handleBootSceneKeyPress from "../core/scene-keypress-handlers/handleBootSceneKeyPress"; import { useFrame } from "react-three-fiber"; -import { getRandomIdleLainAnim } from "../utils/idle-utils"; -import * as audio from "../static/sfx"; +import { getRandomIdleLainAnim } from "../helpers/idle-helpers"; +import * as audio from "../static/audio/sfx"; import handleEndSceneKeyPress from "../core/scene-keypress-handlers/handleEndSceneKeyPress"; -import handleMediaSceneEvent from "../core/scene-event-handlers/handleMediaSceneEvent"; -import handleSsknSceneEvent from "../core/scene-event-handlers/handleSsknSceneEvent"; -import handleBootSceneEvent from "../core/scene-event-handlers/handleBootSceneEvent"; -import handleEndSceneEvent from "../core/scene-event-handlers/handleEndSceneEvent"; -import handleEvent from "../core/scene-event-handlers/handleEvent"; +import handleEvent, { GameEvent } from "../core/handleEvent"; const KeyPressHandler = () => { const scene = useStore((state) => state.currentScene); @@ -83,9 +74,9 @@ const KeyPressHandler = () => { const now = Date.now(); if ( - keyPress - // !inputCooldown && - // now > timeSinceLastKeyPress.current + 1500 + keyPress && + now > timeSinceLastKeyPress.current + inputCooldown && + inputCooldown !== -1 ) { if (scene === "main") { lainIdleCounter.current = now; @@ -105,34 +96,31 @@ const KeyPressHandler = () => { contextProvider: getMediaSceneContext, keyPressHandler: handleMediaSceneKeyPress, }; - // case "sskn": - // return { - // contextProvider: getSsknSceneContext, - // keyPressHandler: handleSsknSceneKeyPress, - // eventHandler: handleSsknSceneEvent, - // }; - // case "boot": - // return { - // contextProvider: getBootSceneContext, - // keyPressHandler: handleBootSceneKeyPress, - // eventHandler: handleBootSceneEvent, - // }; - // case "end": - // return { - // contextProvider: getEndSceneContext, - // keyPressHandler: handleEndSceneKeyPress, - // eventHandler: handleEndSceneEvent, - // }; - // case "gate": - // case "polytan": - // useStore.setState({ currentScene: "main" }); - // break; - // case "idle_media": - // useStore.setState({ - // currentScene: "main", - // idleStarting: false, - // }); - // break; + case "sskn": + return { + contextProvider: getSsknSceneContext, + keyPressHandler: handleSsknSceneKeyPress, + }; + case "boot": + return { + contextProvider: getBootSceneContext, + keyPressHandler: handleBootSceneKeyPress, + }; + case "end": + return { + contextProvider: getEndSceneContext, + keyPressHandler: handleEndSceneKeyPress, + }; + case "gate": + case "polytan": + useStore.setState({ currentScene: "main" }); + break; + case "idle_media": + useStore.setState({ + currentScene: "main", + idleStarting: false, + }); + break; } })(); @@ -140,9 +128,8 @@ const KeyPressHandler = () => { const { contextProvider, keyPressHandler } = sceneFns; const ctx = contextProvider(keyPress); - const event = keyPressHandler(ctx as any) as any; + const event: GameEvent | undefined = keyPressHandler(ctx as any); if (event) handleEvent(event); - // if (event) eventHandler(event.event, event.mutations); } } }, diff --git a/src/components/Loading.tsx b/src/components/Loading.tsx index 1feeb64..6aad090 100644 --- a/src/components/Loading.tsx +++ b/src/components/Loading.tsx @@ -21,6 +21,9 @@ const Loading = () => { return ( <> + + + diff --git a/src/components/MainScene/HUD.tsx b/src/components/MainScene/HUD.tsx index 59b21e7..5ee9919 100644 --- a/src/components/MainScene/HUD.tsx +++ b/src/components/MainScene/HUD.tsx @@ -8,7 +8,7 @@ import { useStore } from "../../store"; import lerp from "../../utils/lerp"; import GreenTextRenderer from "../TextRenderer/GreenTextRenderer"; import usePrevious from "../../hooks/usePrevious"; -import { getNodeHud } from "../../utils/node-utils"; +import { getNodeHud } from "../../helpers/node-helpers"; export type HUDType = { mirrored: number; @@ -130,7 +130,6 @@ const HUD = memo(() => { if ( !(scene === "main" && prevData?.scene === "main") || (subscene === "site" && prevData?.subscene === "pause") || - (subscene === "site" && prevData?.subscene === "not_found") || subscene === "pause" ) { // set to final pos instantly diff --git a/src/components/MainScene/Pause/Pause.tsx b/src/components/MainScene/Pause/Pause.tsx index 0e6c3eb..d8b6f8b 100644 --- a/src/components/MainScene/Pause/Pause.tsx +++ b/src/components/MainScene/Pause/Pause.tsx @@ -64,7 +64,7 @@ const Pause = () => { setTimeout(() => { setShowActiveComponent(true); setIntro(false); - setInputCooldown(false); + setInputCooldown(1000); }, 3500); }, 3400); } diff --git a/src/components/MainScene/Popups/NotFound.tsx b/src/components/MainScene/Popups/NotFound.tsx index f345872..b9a3bff 100644 --- a/src/components/MainScene/Popups/NotFound.tsx +++ b/src/components/MainScene/Popups/NotFound.tsx @@ -3,13 +3,16 @@ import notFound from "../../../static/sprite/not_found.png"; import notFoundLof from "../../../static/sprite/not_found_lof.png"; import { useLoader } from "react-three-fiber"; import * as THREE from "three"; +import { useStore } from "../../../store"; const NotFound = memo(() => { const notFoundTex = useLoader(THREE.TextureLoader, notFound); const notFoundLofTex = useLoader(THREE.TextureLoader, notFoundLof); + const wordNotFound = useStore((state) => state.wordNotFound); + return ( - + { +const PolytanBear = memo(() => { const skeletonTex = useLoader(THREE.TextureLoader, skeleton); const headTex = useLoader(THREE.TextureLoader, head); const bodyTex = useLoader(THREE.TextureLoader, body); @@ -29,6 +19,8 @@ const PolytanBear = (props: PolytanBearProps) => { const rightArmTex = useLoader(THREE.TextureLoader, rightArm); const rightLegTex = useLoader(THREE.TextureLoader, rightLeg); + const unlockedParts = useStore((state) => state.polytanUnlockedParts); + return ( <> @@ -39,7 +31,7 @@ const PolytanBear = (props: PolytanBearProps) => { @@ -47,39 +39,39 @@ const PolytanBear = (props: PolytanBearProps) => { ); -}; +}); export default PolytanBear; diff --git a/src/components/TextRenderer/BigLetter.tsx b/src/components/TextRenderer/BigLetter.tsx index 5a5c85a..8fd7345 100644 --- a/src/components/TextRenderer/BigLetter.tsx +++ b/src/components/TextRenderer/BigLetter.tsx @@ -91,7 +91,6 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => { useEffect(() => { if ( subscene === "pause" || - (subscene === "site" && prevData?.subscene === "not_found") || (subscene === "site" && prevData?.subscene === "pause") ) return; diff --git a/src/components/TextRenderer/MainYellowTextAnimator.tsx b/src/components/TextRenderer/MainYellowTextAnimator.tsx index bd890a8..abade36 100644 --- a/src/components/TextRenderer/MainYellowTextAnimator.tsx +++ b/src/components/TextRenderer/MainYellowTextAnimator.tsx @@ -3,7 +3,7 @@ import { useStore } from "../../store"; import { a, useTrail } from "@react-spring/three"; import BigLetter from "./BigLetter"; import usePrevious from "../../hooks/usePrevious"; -import { getNodeHud } from "../../utils/node-utils"; +import { getNodeHud } from "../../helpers/node-helpers"; const MainYellowTextAnimator = (props: { visible?: boolean }) => { const activeNode = useStore((state) => state.activeNode); diff --git a/src/core/eventTemplates.ts b/src/core/eventTemplates.ts index 1a220ac..7f74853 100644 --- a/src/core/eventTemplates.ts +++ b/src/core/eventTemplates.ts @@ -1,13 +1,26 @@ import { NodeData } from "../components/MainScene/Site/Site"; -import * as audio from "../static/sfx"; +import * as audio from "../static/audio/sfx"; import { nodeExplodeAnimation, nodeKnockAndFallAnimation, nodeKnockAnimation, nodeRipAnimation, nodeThrowAnimation, -} from "../utils/node-animations"; -import { playAudio } from "../store"; +} from "../helpers/node-animation-helpers"; +import { playMediaElement, resetMediaElement } from "../helpers/media-helpers"; +import { + ActiveSite, + EndComponent, + GameProgress, + GameScene, + LeftMediaComponent, + MediaComponent, + MediaSide, + PromptComponent, + RightMediaComponent, + SiteSaveState, + SsknComponent, +} from "../store"; export const siteMoveHorizontal = (calculatedState: { lainMoveAnimation: string; @@ -19,15 +32,13 @@ export const siteMoveHorizontal = (calculatedState: { mutation: { lainMoveState: calculatedState.lainMoveAnimation, siteRot: calculatedState.siteRot, - inputCooldown: true, + inputCooldown: 5500, }, - delay: 0, }, { mutation: { activeNode: calculatedState.activeNode, lainMoveState: "standing", - inputCooldown: false, }, delay: 3900, }, @@ -45,37 +56,36 @@ export const siteMoveVertical = (calculatedState: { mutation: { lainMoveState: calculatedState.lainMoveAnimation, activeLevel: calculatedState.activeLevel, - inputCooldown: true, + inputCooldown: 5500, }, - delay: 0, }, { mutation: { activeNode: calculatedState.activeNode, lainMoveState: "standing", - inputCooldown: false, }, delay: 3900, }, ], audio: [ - { sfx: [audio.sound13], delay: 0 }, + { sfx: [audio.sound13] }, { sfx: [audio.sound10, audio.sound9], delay: 1300 }, { sfx: [audio.sound8], delay: 2700 }, ], }); export const changeNode = (calculatedState: { activeNode: NodeData }) => ({ - state: [{ mutation: { activeNode: calculatedState.activeNode }, delay: 0 }], - audio: [{ sfx: [audio.sound1], delay: 0 }], -}); - -export const throwNode = (calculatedState: { currentScene: string }) => ({ state: [ { - mutation: { lainMoveState: "throw_node", inputCooldown: true }, - delay: 0, + mutation: { activeNode: calculatedState.activeNode, inputCooldown: 1500 }, }, + ], + audio: [{ sfx: [audio.sound1] }], +}); + +export const throwNode = (calculatedState: { currentScene: GameScene }) => ({ + state: [ + { mutation: { lainMoveState: "throw_node", inputCooldown: -1 } }, { mutation: { currentScene: calculatedState.currentScene, @@ -87,18 +97,15 @@ export const throwNode = (calculatedState: { currentScene: string }) => ({ ], effects: [nodeThrowAnimation], audio: [ - { sfx: [audio.sound0], delay: 0 }, + { sfx: [audio.sound0] }, { sfx: [audio.sound12], delay: 1600 }, { sfx: [audio.sound13, audio.sound14], delay: 2800 }, ], }); -export const ripNode = (calculatedState: { currentScene: string }) => ({ +export const ripNode = (calculatedState: { currentScene: GameScene }) => ({ state: [ - { - mutation: { lainMoveState: "rip_node", inputCooldown: true }, - delay: 0, - }, + { mutation: { lainMoveState: "rip_node", inputCooldown: -1 } }, { mutation: { currentScene: calculatedState.currentScene, @@ -110,7 +117,7 @@ export const ripNode = (calculatedState: { currentScene: string }) => ({ ], effects: [nodeRipAnimation], audio: [ - { sfx: [audio.sound0], delay: 0 }, + { sfx: [audio.sound0] }, { sfx: [audio.sound12], delay: 1600 }, { sfx: [audio.sound13, audio.sound15], delay: 4000 }, ], @@ -121,18 +128,17 @@ export const explodeNode = { { mutation: { lainMoveState: "touch_node_and_get_scared", - inputCooldown: true, + inputCooldown: 3800, }, - delay: 0, }, { - mutation: { lainMoveState: "standing", inputCooldown: false }, + mutation: { lainMoveState: "standing" }, delay: 3800, }, ], effects: [nodeExplodeAnimation], audio: [ - { sfx: [audio.sound0], delay: 0 }, + { sfx: [audio.sound0] }, { sfx: [audio.sound17], delay: 2400 }, { sfx: [audio.sound33], delay: 3150 }, ], @@ -140,33 +146,26 @@ export const explodeNode = { export const knockNode = { state: [ - { mutation: { lainMoveState: "knock", inputCooldown: true }, delay: 0 }, + { mutation: { lainMoveState: "knock", inputCooldown: 3500 } }, { - mutation: { lainMoveState: "standing", inputCooldown: false }, - delay: 2900, + mutation: { lainMoveState: "standing" }, + delay: 3500, }, ], effects: [nodeKnockAnimation], - audio: [ - { sfx: [audio.sound0], delay: 0 }, - { sfx: [audio.sound18], delay: 1200 }, - ], + audio: [{ sfx: [audio.sound0] }, { sfx: [audio.sound18], delay: 1200 }], }; export const knockNodeAndFall = { state: [ + { mutation: { lainMoveState: "knock_and_fall", inputCooldown: 6000 } }, { - mutation: { lainMoveState: "knock_and_fall", inputCooldown: true }, - delay: 0, - }, - { - mutation: { lainMoveState: "standing", inputCooldown: false }, - delay: 7500, + mutation: { lainMoveState: "standing" }, }, ], effects: [nodeKnockAndFallAnimation], audio: [ - { sfx: [audio.sound0], delay: 0 }, + { sfx: [audio.sound0] }, { sfx: [audio.sound18], delay: 1200 }, { sfx: [audio.sound19], delay: 2300 }, { sfx: [audio.sound33], delay: 3150 }, @@ -181,23 +180,28 @@ export const enterLevelSelection = (calculatedState: { mutation: { selectedLevel: calculatedState.selectedLevel, mainSubscene: "level_selection", + inputCooldown: 1500, }, - delay: 0, }, ], - audio: [{ sfx: [audio.sound1], delay: 0 }], + audio: [{ sfx: [audio.sound1] }], }); export const exitLevelSelection = { - state: [{ mutation: { mainSubscene: "site" }, delay: 0 }], - audio: [{ sfx: [audio.sound1], delay: 0 }], + state: [{ mutation: { mainSubscene: "site", inputCooldown: 1500 } }], + audio: [{ sfx: [audio.sound1] }], }; export const changeSelectedLevel = (calculatedState: { selectedLevel: number; }) => ({ state: [ - { mutation: { selectedLevel: calculatedState.selectedLevel }, delay: 0 }, + { + mutation: { + selectedLevel: calculatedState.selectedLevel, + inputCooldown: 300, + }, + }, ], }); @@ -212,15 +216,13 @@ export const selectLevel = (calculatedState: { lainMoveState: calculatedState.lainMoveState, activeLevel: calculatedState.activeLevel, mainSubscene: "site", - inputCooldown: true, + inputCooldown: 5500, }, - delay: 0, }, { mutation: { activeNode: calculatedState.activeNode, lainMoveState: "standing", - inputCooldown: false, }, delay: 3900, }, @@ -238,19 +240,15 @@ export const pauseGame = (calculatedState: { siteRot: number[] }) => ({ lainMoveState: "rip_middle_ring", pauseExitAnimation: false, mainSubscene: "pause", - inputCooldown: true, + inputCooldown: -1, }, - delay: 0, }, { mutation: { siteRot: calculatedState.siteRot }, delay: 3600, }, ], - audio: [ - { sfx: [audio.sound7], delay: 0 }, - { sfx: [audio.sound23], delay: 3600 }, - ], + audio: [{ sfx: [audio.sound7] }, { sfx: [audio.sound23], delay: 3600 }], }); export const changePauseComponent = (calculatedState: { @@ -258,29 +256,31 @@ export const changePauseComponent = (calculatedState: { }) => ({ state: [ { - mutation: { activePauseComponent: calculatedState.activePauseComponent }, - delay: 0, + mutation: { + activePauseComponent: calculatedState.activePauseComponent, + inputCooldown: 500, + }, }, ], - audio: [{ sfx: [audio.sound1], delay: 0 }], + audio: [{ sfx: [audio.sound1] }], }); export const showPermissionDenied = { state: [ - { mutation: { permissionDenied: true }, delay: 0 }, + { mutation: { permissionDenied: true, inputCooldown: 1200 } }, { mutation: { permissionDenied: false }, delay: 1200 }, ], - audio: [{ sfx: [audio.sound0], delay: 0 }], + audio: [{ sfx: [audio.sound0] }], }; export const displayPrompt = { - state: [{ mutation: { promptVisible: true }, delay: 0 }], - audio: [{ sfx: [audio.sound0], delay: 0 }], + state: [{ mutation: { promptVisible: true, inputCooldown: 500 } }], + audio: [{ sfx: [audio.sound0] }], }; export const showAbout = { - state: [{ mutation: { showingAbout: true }, delay: 0 }], - audio: [{ sfx: [audio.sound0], delay: 0 }], + state: [{ mutation: { showingAbout: true } }], + audio: [{ sfx: [audio.sound0] }], }; export const exitPause = (calculatedState: { siteRot: number[] }) => ({ @@ -290,9 +290,8 @@ export const exitPause = (calculatedState: { siteRot: number[] }) => ({ siteRot: calculatedState.siteRot, pauseExitAnimation: true, activePauseComponent: "change", - inputCooldown: true, + inputCooldown: 1400, }, - delay: 0, }, { mutation: { @@ -303,84 +302,70 @@ export const exitPause = (calculatedState: { siteRot: number[] }) => ({ delay: 1200, }, ], - audio: [{ sfx: [audio.sound0], delay: 0 }], + audio: [{ sfx: [audio.sound0] }], }); export const exitAbout = { - state: [{ mutation: { showingAbout: false }, delay: 0 }], + state: [{ mutation: { showingAbout: false, inputCooldown: 500 } }], }; export const changePromptComponent = (calculatedState: { - activePromptComponent: "yes" | "no"; + activePromptComponent: PromptComponent; }) => ({ state: [ { mutation: { activePromptComponent: calculatedState.activePromptComponent, + inputCooldown: 500, }, - delay: 0, }, ], - audio: [{ sfx: [audio.sound1], delay: 0 }], + audio: [{ sfx: [audio.sound1] }], }); export const exitPrompt = { state: [ { - mutation: { activePromptComponent: "no", promptVisible: false }, - delay: 0, + mutation: { + activePromptComponent: "no", + promptVisible: false, + inputCooldown: 500, + }, }, ], - audio: [{ sfx: [audio.sound28], delay: 0 }], + audio: [{ sfx: [audio.sound28] }], }; // todo actually save export const saveGame = () => ({ state: [ - { - mutation: { saveSuccessful: true }, - delay: 0, - }, + { mutation: { saveSuccessful: true, inputCooldown: 1200 } }, { mutation: { saveSuccessful: undefined }, delay: 1200, }, ], - audio: [{ sfx: [audio.sound28], delay: 0 }], + audio: [{ sfx: [audio.sound28] }], }); // todo actually load export const loadGame = () => ({ state: [ - { - mutation: { loadSuccessful: true }, - delay: 0, - }, + { mutation: { loadSuccessful: true, inputCooldown: 1200 } }, { mutation: { loadSuccessful: undefined }, delay: 1200, }, ], - audio: [{ sfx: [audio.sound28], delay: 0 }], + audio: [{ sfx: [audio.sound28] }], }); export const changeSite = (calculatedState: { - newActiveSite: "a" | "b"; + newActiveSite: ActiveSite; newActiveNode: NodeData; newActiveLevel: string; newSiteRot: number[]; - newSiteSaveState: { - a: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - b: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - }; + newSiteSaveState: SiteSaveState; }) => ({ state: [ { @@ -397,26 +382,274 @@ export const changeSite = (calculatedState: { activeLevel: calculatedState.newActiveLevel, // save state siteSaveState: calculatedState.newSiteSaveState, + inputCooldown: -1, }, - delay: 0, }, ], }); export const changeLeftMediaComponent = (calculatedState: { - activeComponent: "play" | "exit"; + activeComponent: LeftMediaComponent; }) => ({ state: [ - { mutation: { activeMediaComponent: calculatedState.activeComponent } }, + { + mutation: { + activeMediaComponent: calculatedState.activeComponent, + inputCooldown: 1200, + }, + }, ], - audio: [{ sfx: [audio.sound1], delay: 0 }], + audio: [{ sfx: [audio.sound1] }], }); export const changeMediaSide = (calculatedState: { - activeMediaComponent: "fstWord" | "sndWord" | "thirdWord" | "exit" | "play"; + activeMediaComponent: MediaComponent; lastActiveMediaComponents: { - left: "play" | "exit"; - right: "fstWord" | "sndWord" | "thirdWord"; + left: LeftMediaComponent; + right: RightMediaComponent; }; - currentMediaSide: "right" | "left"; -}) => ({}); + currentMediaSide: MediaSide; +}) => ({ + state: [ + { + mutation: { + activeMediaComponent: calculatedState.activeMediaComponent, + lastActiveMediaComponents: calculatedState.lastActiveMediaComponents, + currentMediaSide: calculatedState.currentMediaSide, + inputCooldown: 500, + }, + }, + ], +}); + +export const playMedia = { + state: [{ mutation: { mediaPercentageElapsed: 0, inputCooldown: 500 } }], + effects: [playMediaElement], +}; + +export const exitMedia = { + state: [{ mutation: { currentScene: "main", inputCooldown: -1 } }], + effects: [resetMediaElement], +}; + +export const changeRightMediaComponent = (calculatedState: { + wordPosStateIdx: number; + activeComponent: RightMediaComponent; +}) => ({ + state: [ + { + mutation: { + activeMediaComponent: calculatedState.activeComponent, + mediaWordPosStateIdx: calculatedState.wordPosStateIdx, + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound1] }], +}); + +export const wordNotFound = { + state: [ + { + mutation: { + currentScene: "main", + wordNotFound: true, + inputCooldown: 300, + }, + }, + ], + audio: [{ sfx: [audio.sound30] }], + effects: [resetMediaElement], +}; + +export const hideWordNotFound = { + state: [{ mutation: { wordNotFound: false, inputCooldown: 300 } }], +}; + +export const selectWord = (calculatedState: { + activeLevel: string; + activeNode: NodeData; + siteRot: number[]; +}) => ({ + state: [ + { + mutation: { + activeLevel: calculatedState.activeLevel, + siteRot: calculatedState.siteRot, + activeNode: calculatedState.activeNode, + wordSelected: true, + currentScene: "main", + inputCooldown: -1, + }, + }, + ], + audio: [{ sfx: [audio.sound29] }], + effects: [resetMediaElement], +}); + +export const changeSsknComponent = (calculatedState: { + activeSsknComponent: SsknComponent; +}) => ({ + state: [ + { + mutation: { + activeSsknComponent: calculatedState.activeSsknComponent, + inputCooldown: 500, + }, + }, + ], +}); + +export const upgradeSskn = (calculatedState: { + gameProgress: GameProgress; + ssknLvl: number; +}) => ({ + state: [ + { + mutation: { + gameProgress: calculatedState.gameProgress, + ssknLvl: calculatedState.ssknLvl, + ssknLoading: true, + inputCooldown: -1, + }, + }, + { mutation: { currentScene: "main" }, delay: 6000 }, + ], +}); + +export const exitSskn = { + state: [ + { + mutation: { + currentScene: "main", + ssknLoading: false, + activeSsknComponent: "ok", + inputCooldown: -1, + }, + }, + ], +}; + +export const changeEndComponent = (calculatedState: { + activeEndComponent: EndComponent; +}) => ({ + state: [ + { + mutation: { + activeEndComponent: calculatedState.activeEndComponent, + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound1] }], +}); + +export const endGame = { + state: [{ mutation: { currentScene: "boot", inputCooldown: -1 } }], + audio: [{ sfx: [audio.sound0] }], +}; + +// todo this is probably buggy +export const continueGameAfterEnd = { + state: [ + { + mutation: { currentScene: "change_disc", intro: true, inputCooldown: -1 }, + }, + ], + audio: [{ sfx: [audio.sound0] }], +}; + +export const changeMainMenuComponent = (calculatedState: { + activeMainMenuComponent: "authorize_user" | "load_data"; +}) => ({ + state: [ + { + mutation: { + activeMainMenuComponent: calculatedState.activeMainMenuComponent, + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound1] }], +}); + +export const exitLoadData = { + state: [ + { + mutation: { + bootSubscene: "main_menu", + promptVisible: false, + activePromptComponent: "no", + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound29] }], +}; + +export const enterLoadData = { + state: [ + { mutation: { bootSubscene: "load_data", inputCooldown: 500 } }, + { mutation: { promptVisible: true }, delay: 500 }, + ], + audio: [{ sfx: [audio.sound0] }], +}; + +export const enterUserAuthorization = { + state: [ + { + mutation: { + bootSubscene: "authorize_user", + authorizeUserLetterIdx: 0, + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound0] }], +}; + +export const exitUserAuthorization = { + state: [ + { + mutation: { + playerName: "", + bootSubscene: "main_menu", + inputCooldown: 500, + }, + }, + ], + audio: [{ sfx: [audio.sound29] }], +}; + +export const startNewGame = { + state: [ + { mutation: { currentScene: "main", intro: true, inputCooldown: -1 } }, + ], +}; + +export const updatePlayerName = (calculatedState: { playerName: string }) => ({ + state: [{ mutation: { playerName: calculatedState.playerName } }], + audio: [{ sfx: [audio.sound0] }], +}); + +export const removePlayerNameLastChar = (calculatedState: { + playerName: string; +}) => ({ + state: [{ mutation: { playerName: calculatedState.playerName } }], + audio: [{ sfx: [audio.sound29] }], +}); + +export const failUpdatePlayerName = { audio: [{ sfx: [audio.sound0] }] }; + +export const updateAuthorizeUserLetterIdx = (calculatedState: { + authorizeUserLetterIdx: number; +}) => ({ + state: [ + { + mutation: { + inputCooldown: 300, + authorizeUserLetterIdx: calculatedState.authorizeUserLetterIdx, + }, + }, + ], +}); diff --git a/src/core/scene-event-handlers/handleEvent.ts b/src/core/handleEvent.ts similarity index 56% rename from src/core/scene-event-handlers/handleEvent.ts rename to src/core/handleEvent.ts index 7fa16b2..7dc4c9a 100644 --- a/src/core/scene-event-handlers/handleEvent.ts +++ b/src/core/handleEvent.ts @@ -1,47 +1,44 @@ -import { playAudio, useStore } from "../../store"; -import sleep from "../../utils/sleep"; +import { playAudio, useStore } from "../store"; +import sleep from "../utils/sleep"; type Mutation = { mutation: Object; - delay: number; + delay?: number; }; type EventAudio = { sfx: HTMLAudioElement[]; - delay: number; + delay?: number; }; -type Event = { - state: Mutation[]; +export type GameEvent = { + state?: Mutation[]; audio?: EventAudio[]; effects?: (() => void)[]; }; // the async/await here might be misleading for some, it functions as a setTimeout that fires // multiple async calls without stopping the execution, which is what we want. -const handleEvent = (event: Event) => { - const now = performance.now(); +const handleEvent = (event: GameEvent) => { const setState = useStore.setState; - const { state, audio, effects } = event; + const { state, effects, audio } = event; - state.forEach(async (mutationData) => { - const { delay, mutation } = mutationData; - if (delay) await sleep(delay); - setState(mutation); - }); + if (state) + state.forEach(async (mutationData) => { + const { delay, mutation } = mutationData; + if (delay) await sleep(delay); + setState(mutation); + }); if (effects) effects.forEach((effect) => effect()); - if (audio) { + if (audio) audio.forEach(async (audio) => { const { delay, sfx } = audio; if (delay) await sleep(delay); sfx.forEach((soundEffect) => playAudio(soundEffect)); }); - } - - // console.log(performance.now() - now); }; export default handleEvent; diff --git a/src/core/scene-event-handlers/handleBootSceneEvent.ts b/src/core/scene-event-handlers/handleBootSceneEvent.ts deleted file mode 100644 index 03cfcd3..0000000 --- a/src/core/scene-event-handlers/handleBootSceneEvent.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { playAudio, useStore } from "../../store"; -import * as audio from "../../static/sfx"; - -const handleBootSceneEvent = (eventState: any) => { - const setState = useStore.setState; - - switch (eventState.event) { - case "main_menu_up": - setState({ activeMainMenuComponent: "authorize_user" }); - playAudio(audio.sound1); - break; - case "main_menu_down": - setState({ activeMainMenuComponent: "load_data" }); - playAudio(audio.sound1); - break; - case "main_menu_load_data_select": - setState({ bootSubscene: "load_data" }); - playAudio(audio.sound0); - - setTimeout(() => setState({ promptVisible: true }), 500); - break; - case "main_menu_authorize_user_select": - setState({ authorizeUserLetterIdx: 0, bootSubscene: "authorize_user" }); - playAudio(audio.sound0); - break; - case "authorize_user_up": - case "authorize_user_down": - case "authorize_user_left": - case "authorize_user_right": - setState({ - authorizeUserLetterIdx: eventState.authorizeUserLetterIdx, - }); - break; - case "authorize_user_back": - setState({ - playerName: "", - bootSubscene: "main_menu", - }); - playAudio(audio.sound29); - break; - case "update_player_name": - setState({ - playerName: eventState.playerName, - }); - playAudio(audio.sound0); - break; - case "update_player_name_denied": - playAudio(audio.sound0); - break; - case "remove_last_char": - setState({ playerName: eventState.playerName }); - playAudio(audio.sound29); - break; - case "load_data_no": - setState({ - bootSubscene: "main_menu", - promptVisible: false, - activePromptComponent: "no", - }); - playAudio(audio.sound29); - break; - case "load_data_yes": - // todo check if data exists - setState({ loadSuccessful: true }); - playAudio(audio.sound28); - - //todo actually load - setTimeout(() => setState({ loadSuccessful: undefined }), 1200); - break; - case "prompt_left": - setState({ activePromptComponent: "yes" }); - playAudio(audio.sound1); - break; - case "prompt_right": - setState({ activePromptComponent: "no" }); - playAudio(audio.sound1); - break; - case "start_new_game": - setState({ currentScene: "main", intro: true }); - break; - } -}; - -export default handleBootSceneEvent; diff --git a/src/core/scene-event-handlers/handleEndSceneEvent.ts b/src/core/scene-event-handlers/handleEndSceneEvent.ts deleted file mode 100644 index 1707772..0000000 --- a/src/core/scene-event-handlers/handleEndSceneEvent.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useStore } from "../../store"; - -const handleEndSceneEvent = (eventState: any) => { - const setState = useStore.setState; - switch (eventState.event) { - case "end_selection_up": - setState({ activeEndComponent: "end" }); - break; - case "end_selection_down": - setState({ activeEndComponent: "continue" }); - break; - case "end_continue_select": - setState({ currentScene: "change_disc", intro: true }); - break; - case "end_end_select": - setState({ currentScene: "boot" }); - } -}; - -export default handleEndSceneEvent; diff --git a/src/core/scene-event-handlers/handleMediaSceneEvent.ts b/src/core/scene-event-handlers/handleMediaSceneEvent.ts deleted file mode 100644 index 445be01..0000000 --- a/src/core/scene-event-handlers/handleMediaSceneEvent.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { playAudio, useStore } from "../../store"; -import * as audio from "../../static/sfx"; - -const handleMediaSceneEvent = (eventState: any) => { - const setState = useStore.setState; - - const setNodeViewed = useStore.getState().setNodeViewed; - const updateLeftSide = useStore.getState().updateLeftSide; - const updateRightSide = useStore.getState().updateRightSide; - - const setPercentageElapsed = useStore.getState().setPercentageElapsed; - - const playMedia = () => { - const mediaElement = document.getElementById("media") as HTMLMediaElement; - - if (mediaElement && mediaElement.paused) { - setPercentageElapsed(0); - - mediaElement.play(); - } - }; - - const exitMedia = () => { - const mediaElement = document.getElementById("media") as HTMLMediaElement; - if (mediaElement) { - mediaElement.pause(); - mediaElement.currentTime = 0; - } - }; - - switch (eventState.event) { - case "media_rightside_down": - case "media_rightside_up": - setState({ - activeMediaComponent: eventState.newActiveComponent, - mediaWordPosStateIdx: eventState.wordPosStateIdx, - }); - playAudio(audio.sound1); - break; - case "media_leftside_right": - updateLeftSide( - eventState.newActiveComponent, - eventState.lastActiveComponent - ); - break; - case "media_rightside_left": - updateRightSide( - eventState.newActiveComponent, - eventState.lastActiveComponent - ); - break; - case "media_play_select": - setNodeViewed(eventState.node.node_name, { - is_viewed: 1, - is_visible: 1, - }); - playMedia(); - break; - case "media_exit_select": - exitMedia(); - playAudio(audio.sound29); - break; - case "media_word_select": - exitMedia(); - playAudio(audio.sound29); - setState({ - wordSelected: true, - activeLevel: eventState.level, - siteRot: [0, eventState.siteRotY, 0], - activeNode: eventState.node, - currentScene: "main", - }); - break; - case "word_node_not_found": - exitMedia(); - playAudio(audio.sound30); - setState({ - mainSubscene: "not_found", - currentScene: "main", - }); - } -}; - -export default handleMediaSceneEvent; diff --git a/src/core/scene-event-handlers/handleSsknSceneEvent.ts b/src/core/scene-event-handlers/handleSsknSceneEvent.ts deleted file mode 100644 index 459e970..0000000 --- a/src/core/scene-event-handlers/handleSsknSceneEvent.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useStore } from "../../store"; - -const handleSsknSceneEvent = (eventState: any) => { - const setState = useStore.setState; - - const setNodeViewed = useStore.getState().setNodeViewed; - - switch (eventState.event) { - case "sskn_cancel_up": - setState({ - activeSsknComponent: "ok", - }); - break; - case "sskn_ok_down": - setState({ - activeSsknComponent: "cancel", - }); - break; - case "sskn_ok_select": - setState({ - ssknLoading: true, - }); - setNodeViewed(eventState.node.node_name, { - is_viewed: 1, - is_visible: 0, - }); - // incrementSsknLvl(); - - setTimeout(() => setState({ currentScene: "main" }), 6000); - - break; - case "sskn_cancel_select": - setState({ - ssknLoading: false, - currentScene: "main", - activeSsknComponent: "ok", - }); - } -}; - -export default handleSsknSceneEvent; diff --git a/src/core/scene-keypress-handlers/handleBootSceneKeyPress.ts b/src/core/scene-keypress-handlers/handleBootSceneKeyPress.ts index edd0439..f2d34f0 100644 --- a/src/core/scene-keypress-handlers/handleBootSceneKeyPress.ts +++ b/src/core/scene-keypress-handlers/handleBootSceneKeyPress.ts @@ -1,8 +1,25 @@ import authorize_user_letters from "../../resources/authorize_user_letters.json"; -import handleNameSelection from "../../utils/handleNameSelection"; +import handleNameSelection from "../../helpers/name-selection-helpers"; import { BootSceneContext } from "../../store"; +import { + changeMainMenuComponent, + changePromptComponent, + enterLoadData, + enterUserAuthorization, + exitLoadData, + exitUserAuthorization, + failUpdatePlayerName, + loadGame, + removePlayerNameLastChar, + startNewGame, + updateAuthorizeUserLetterIdx, + updatePlayerName, +} from "../eventTemplates"; +import { GameEvent } from "../handleEvent"; -const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { +const handleBootSceneKeyPress = ( + bootSceneContext: BootSceneContext +): GameEvent | undefined => { const { keyPress, subscene, @@ -16,17 +33,15 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { if (promptVisible) { switch (keyPress) { case "LEFT": - return { event: "prompt_left" }; + return changePromptComponent({ activePromptComponent: "yes" }); case "RIGHT": - return { event: "prompt_right" }; + return changePromptComponent({ activePromptComponent: "no" }); case "CIRCLE": switch (activePromptComponent) { case "no": - return { event: "load_data_no" }; + return exitLoadData; case "yes": - return { - event: "load_data_yes", - }; + return loadGame(); } } } else { @@ -35,28 +50,34 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { switch (keyPress) { case "UP": case "DOWN": - return { event: `main_menu_${keyPress.toLowerCase()}` }; + const newComponent = + keyPress === "UP" ? "authorize_user" : "load_data"; + return changeMainMenuComponent({ + activeMainMenuComponent: newComponent, + }); case "CIRCLE": - return { event: `main_menu_${activeMainMenuComponent}_select` }; + switch (activeMainMenuComponent) { + case "authorize_user": + return enterUserAuthorization; + case "load_data": + return enterLoadData; + } } break; case "authorize_user": switch (keyPress) { case "START": if (playerName.length > 0) { - return { - event: "start_new_game", - }; + return startNewGame; } - break; + return; case "X": if (playerName.length > 0) { - return { - event: "remove_last_char", + return removePlayerNameLastChar({ playerName: playerName.slice(0, -1), - }; + }); } else { - return { event: "authorize_user_back" }; + return exitUserAuthorization; } case "LEFT": // if utmost left, break @@ -64,7 +85,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { [0, 13, 26, 39, 52].includes(authorizeUserLetterIdx) || authorizeUserLetterIdx === 15 ) - break; + return; // skip else if ( authorizeUserLetterIdx === 41 || @@ -74,19 +95,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx === 19 || authorizeUserLetterIdx === 45 ) { - return { - event: "authorize_user_left", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx - 2, - }; + }); } else { - return { - event: "authorize_user_left", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx - 1, - }; + }); } case "RIGHT": // if utmost right, break - if ([12, 25, 38, 51, 64].includes(authorizeUserLetterIdx)) break; + if ([12, 25, 38, 51, 64].includes(authorizeUserLetterIdx)) return; // skip empty else if ( authorizeUserLetterIdx === 39 || @@ -96,15 +115,13 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx === 43 || authorizeUserLetterIdx === 17 ) { - return { - event: "authorize_user_right", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx + 2, - }; + }); } else { - return { - event: "authorize_user_right", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx + 1, - }; + }); } case "DOWN": // if utmost down, break @@ -113,7 +130,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx ) ) { - break; + return; // skip empty } else if ( authorizeUserLetterIdx === 0 || @@ -123,20 +140,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx === 31 || authorizeUserLetterIdx === 5 ) { - return { - event: "authorize_user_down", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx + 26, - }; + }); } else if (authorizeUserLetterIdx === 3) { - return { - event: "authorize_user_down", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx + 52, - }; + }); } else { - return { - event: "authorize_user_down", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx + 13, - }; + }); } case "UP": // if utmost up, break @@ -145,7 +159,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx ) ) { - break; + return; // skip empty } else if ( authorizeUserLetterIdx === 26 || @@ -154,20 +168,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { authorizeUserLetterIdx === 31 || authorizeUserLetterIdx === 57 ) { - return { - event: "authorize_user_up", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx - 26, - }; + }); } else if (authorizeUserLetterIdx === 55) { - return { - event: "authorize_user_up", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx - 52, - }; + }); } else { - return { - event: "authorize_user_up", + return updateAuthorizeUserLetterIdx({ authorizeUserLetterIdx: authorizeUserLetterIdx - 13, - }; + }); } case "CIRCLE": const chosenCharacter = @@ -178,10 +189,9 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { const newName = handleNameSelection(playerName, chosenCharacter); if (newName !== undefined) - return { event: "update_player_name", playerName: newName }; - else return { event: "update_player_name_denied" }; + return updatePlayerName({ playerName: newName }); + else return failUpdatePlayerName; } - break; } } }; diff --git a/src/core/scene-keypress-handlers/handleEndSceneKeyPress.ts b/src/core/scene-keypress-handlers/handleEndSceneKeyPress.ts index 019ce40..d7420bb 100644 --- a/src/core/scene-keypress-handlers/handleEndSceneKeyPress.ts +++ b/src/core/scene-keypress-handlers/handleEndSceneKeyPress.ts @@ -1,15 +1,29 @@ import { EndSceneContext } from "../../store"; +import { + changeEndComponent, + continueGameAfterEnd, + endGame, +} from "../eventTemplates"; +import { GameEvent } from "../handleEvent"; -const handleEndSceneKeyPress = (endSceneContext: EndSceneContext) => { +const handleEndSceneKeyPress = ( + endSceneContext: EndSceneContext +): GameEvent | undefined => { const { keyPress, selectionVisible, activeEndComponent } = endSceneContext; if (selectionVisible) { switch (keyPress) { case "UP": case "DOWN": - return { event: `end_selection_${keyPress.toLowerCase()}` }; + const newComponent = keyPress === "UP" ? "end" : "continue"; + return changeEndComponent({ activeEndComponent: newComponent }); case "CIRCLE": - return { event: `end_${activeEndComponent}_select` }; + switch (activeEndComponent) { + case "end": + return endGame; + case "continue": + return continueGameAfterEnd; + } } } }; diff --git a/src/core/scene-keypress-handlers/handleMainSceneKeyPress.ts b/src/core/scene-keypress-handlers/handleMainSceneKeyPress.ts index b00c415..c32814d 100644 --- a/src/core/scene-keypress-handlers/handleMainSceneKeyPress.ts +++ b/src/core/scene-keypress-handlers/handleMainSceneKeyPress.ts @@ -3,7 +3,7 @@ import { getNodeById, isNodeVisible, unknownNodeTemplate, -} from "../../utils/node-utils"; +} from "../../helpers/node-helpers"; import { MainSceneContext } from "../../store"; import { changeNode, @@ -18,19 +18,25 @@ import { exitPause, exitPrompt, explodeNode, + hideWordNotFound, knockNode, knockNodeAndFall, loadGame, pauseGame, + ripNode, saveGame, selectLevel, showAbout, showPermissionDenied, siteMoveHorizontal, siteMoveVertical, + throwNode, } from "../eventTemplates"; +import { GameEvent } from "../handleEvent"; -const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { +const handleMainSceneKeyPress = ( + mainSceneContext: MainSceneContext +): GameEvent | undefined => { const { subscene, selectedLevel, @@ -47,6 +53,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { activePromptComponent, gateLvl, siteSaveState, + wordNotFound, } = mainSceneContext; if (promptVisible) { @@ -73,8 +80,6 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { activeLevel: level.toString().padStart(2, "0"), }, }; - console.log(newSiteSaveState); - return changeSite({ newActiveSite: siteToLoad, newActiveNode: stateToLoad.activeNode, @@ -92,6 +97,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { } else { switch (subscene) { case "site": + if (wordNotFound) return hideWordNotFound; switch (keyPress) { case "LEFT": case "RIGHT": { @@ -169,8 +175,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { else return changeNode({ activeNode: newNode }); } case "CIRCLE": - const eventAnimation = - Math.random() < 0.4 ? "rip_node" : "throw_node"; + const eventAnimation = Math.random() < 0.4 ? throwNode : ripNode; const nodeType = activeNode.type; @@ -181,7 +186,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { return; if (activeNode.upgrade_requirement > ssknLvl) { - const rejectEvents = [explodeNode, knockNode, knockNodeAndFall]; + const rejectEvents = [knockNodeAndFall, knockNode, explodeNode]; return rejectEvents[Math.floor(Math.random() * 3)]; } @@ -191,37 +196,19 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { case 4: case 3: case 5: - return { - event: `${eventAnimation}_media`, - mutations: { scene: "media" }, - }; + return eventAnimation({ currentScene: "media" }); case 6: if (activeNode.node_name.substr(0, 3) === "TaK") { - return { - event: `${eventAnimation}_tak`, - mutations: { scene: "tak" }, - }; + return eventAnimation({ currentScene: "tak" }); } else { - return { - event: `${eventAnimation}_media`, - mutations: { scene: "media" }, - }; + return eventAnimation({ currentScene: "media" }); } case 8: - return { - event: `${eventAnimation}_gate`, - mutations: { scene: "gate" }, - }; + return eventAnimation({ currentScene: "gate" }); case 7: - return { - event: `${eventAnimation}_sskn`, - mutations: { scene: "sskn" }, - }; + return eventAnimation({ currentScene: "gate" }); case 9: - return { - event: `${eventAnimation}_polytan`, - mutations: { scene: "polytan" }, - }; + return eventAnimation({ currentScene: "polytan" }); } break; case "L2": @@ -284,13 +271,12 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { switch (keyPress) { case "UP": case "DOWN": - const direction = keyPress.toLowerCase(); const components = ["load", "about", "change", "save", "exit"]; const newComponent = components[ components.indexOf(activePauseComponent) + - (direction === "up" ? -1 : 1) + (keyPress === "UP" ? -1 : 1) ]; if (newComponent) diff --git a/src/core/scene-keypress-handlers/handleMediaSceneKeyPress.ts b/src/core/scene-keypress-handlers/handleMediaSceneKeyPress.ts index fbf82de..c924d82 100644 --- a/src/core/scene-keypress-handlers/handleMediaSceneKeyPress.ts +++ b/src/core/scene-keypress-handlers/handleMediaSceneKeyPress.ts @@ -1,8 +1,19 @@ -import { findNodeFromWord } from "../../utils/media-utils"; +import { findNodeFromWord } from "../../helpers/media-helpers"; import { MediaSceneContext } from "../../store"; -import { changeLeftMediaComponent, changeMediaSide } from "../eventTemplates"; +import { + changeLeftMediaComponent, + changeMediaSide, + changeRightMediaComponent, + exitMedia, + playMedia, + selectWord, + wordNotFound, +} from "../eventTemplates"; +import { GameEvent } from "../handleEvent"; -const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { +const handleMediaSceneKeyPress = ( + mediaSceneContext: MediaSceneContext +): GameEvent | undefined => { const { keyPress, activeMediaComponent, @@ -19,8 +30,8 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { switch (keyPress) { case "UP": case "DOWN": { - const direction = keyPress.toLowerCase(); - const newComponent = direction === "up" ? "play" : "exit"; + const newComponent = keyPress === "UP" ? "play" : "exit"; + return changeLeftMediaComponent({ activeComponent: newComponent }); } case "RIGHT": { @@ -36,14 +47,9 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { case "CIRCLE": switch (activeMediaComponent) { case "play": - return { - event: "media_play_select", - node: activeNode, - }; + return playMedia; case "exit": - return { - event: "media_play_select", - }; + return exitMedia; } } break; @@ -54,6 +60,7 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { wordPosStateIdx - 1 < 1 ? 6 : wordPosStateIdx - 1; const newComponent = (() => { switch (activeMediaComponent) { + default: case "fstWord": return "thirdWord"; case "sndWord": @@ -62,17 +69,17 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { return "sndWord"; } })(); - return { - event: "media_rightside_up", - newActiveComponent: newComponent, + return changeRightMediaComponent({ + activeComponent: newComponent, wordPosStateIdx: newWordPosStateIdx, - }; + }); } case "DOWN": { const newWordPosStateIdx = wordPosStateIdx + 1 > 6 ? 1 : wordPosStateIdx + 1; const newComponent = (() => { switch (activeMediaComponent) { + default: case "fstWord": return "sndWord"; case "sndWord": @@ -82,11 +89,10 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { } })(); - return { - event: "media_rightside_down", - newActiveComponent: newComponent, + return changeRightMediaComponent({ + activeComponent: newComponent, wordPosStateIdx: newWordPosStateIdx, - }; + }); } case "LEFT": @@ -111,9 +117,14 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { ); if (data) { - return { event: `media_word_select`, ...data }; + const { node, level, siteRotY } = { ...data }; + return selectWord({ + activeNode: node, + activeLevel: level, + siteRot: [0, siteRotY, 0], + }); } else { - return { event: `word_node_not_found` }; + return wordNotFound; } } } diff --git a/src/core/scene-keypress-handlers/handleSsknSceneKeyPress.ts b/src/core/scene-keypress-handlers/handleSsknSceneKeyPress.ts index 6c5a42e..d708090 100644 --- a/src/core/scene-keypress-handlers/handleSsknSceneKeyPress.ts +++ b/src/core/scene-keypress-handlers/handleSsknSceneKeyPress.ts @@ -1,24 +1,42 @@ import { SsknSceneContext } from "../../store"; +import { changeSsknComponent, exitSskn, upgradeSskn } from "../eventTemplates"; +import { GameEvent } from "../handleEvent"; -const handleSsknSceneKeyPress = (ssknSceneContext: SsknSceneContext) => { - const { keyPress, activeSsknComponent, activeNode } = ssknSceneContext; +const handleSsknSceneKeyPress = ( + ssknSceneContext: SsknSceneContext +): GameEvent | undefined => { + const { + keyPress, + activeSsknComponent, + activeNode, + gameProgress, + ssknLvl, + } = ssknSceneContext; switch (keyPress) { case "UP": case "DOWN": - return { - event: `sskn_${activeSsknComponent}_${keyPress.toLowerCase()}`, - }; + const direction = keyPress.toLowerCase(); + const newComponent = direction === "up" ? "ok" : "cancel"; + return changeSsknComponent({ activeSsknComponent: newComponent }); case "CIRCLE": - if (activeSsknComponent === "ok") { - return { - event: `sskn_ok_select`, - node: activeNode, - }; - } else { - return { - event: `sskn_cancel_select`, - }; + switch (activeSsknComponent) { + case "ok": + const newGameProgress = { + ...gameProgress, + [activeNode.node_name]: { + is_viewed: 1, + is_visible: 0, + }, + }; + const newSsknLvl = ssknLvl + 1; + + return upgradeSskn({ + gameProgress: newGameProgress, + ssknLvl: newSsknLvl, + }); + case "cancel": + return exitSskn; } } }; diff --git a/src/utils/idle-utils.ts b/src/helpers/idle-helpers.ts similarity index 100% rename from src/utils/idle-utils.ts rename to src/helpers/idle-helpers.ts diff --git a/src/utils/media-utils.ts b/src/helpers/media-helpers.ts similarity index 76% rename from src/utils/media-utils.ts rename to src/helpers/media-helpers.ts index 49e741d..aa38217 100644 --- a/src/utils/media-utils.ts +++ b/src/helpers/media-helpers.ts @@ -2,12 +2,13 @@ import site_a from "../resources/site_a.json"; import site_b from "../resources/site_b.json"; import node_matrices from "../resources/node_matrices.json"; import { NodeData, SiteData } from "../components/MainScene/Site/Site"; -import { isNodeVisible } from "./node-utils"; +import { isNodeVisible } from "./node-helpers"; +import { ActiveSite, RightMediaComponent } from "../store"; export const findNodeFromWord = ( - wordLabel: string, + wordLabel: RightMediaComponent, activeNode: NodeData, - site: "a" | "b", + site: ActiveSite, gameProgress: any ) => { const labelToIdx = (() => { @@ -73,3 +74,18 @@ export const findNodeFromWord = ( level: chosenNode.id.substr(0, 2), }; }; + +export const playMediaElement = () => { + const mediaElement = document.getElementById("media") as HTMLMediaElement; + + if (mediaElement && mediaElement.paused) mediaElement.play(); +}; + +export const resetMediaElement = () => { + const mediaElement = document.getElementById("media") as HTMLMediaElement; + if (mediaElement) { + mediaElement.pause(); + + mediaElement.currentTime = 0; + } +}; diff --git a/src/utils/handleNameSelection.ts b/src/helpers/name-selection-helpers.ts similarity index 100% rename from src/utils/handleNameSelection.ts rename to src/helpers/name-selection-helpers.ts diff --git a/src/utils/node-animations.ts b/src/helpers/node-animation-helpers.ts similarity index 99% rename from src/utils/node-animations.ts rename to src/helpers/node-animation-helpers.ts index fbf216b..77dc777 100644 --- a/src/utils/node-animations.ts +++ b/src/helpers/node-animation-helpers.ts @@ -1,9 +1,5 @@ import { useStore } from "../store"; -import sleep from "./sleep"; - -const setActiveNodePos = useStore.getState().setNodePos; -const setActiveNodeRot = useStore.getState().setNodeRot; -const setActiveNodeAttributes = useStore.getState().setNodeAttributes; +import sleep from "../utils/sleep"; const calculateCoordsBasedOnRotation = ( x: number, @@ -18,6 +14,10 @@ const calculateCoordsBasedOnRotation = ( // throw a warning about an unhandled promise, and we dont care about that. // async/awaits here are used simply to improve readability, nothing else. +const setActiveNodePos = useStore.getState().setNodePos; +const setActiveNodeRot = useStore.getState().setNodeRot; +const setActiveNodeAttributes = useStore.getState().setNodeAttributes; + export const nodeThrowAnimation = () => { (async () => { const siteRotY = useStore.getState().siteRot[1]; diff --git a/src/utils/node-utils.ts b/src/helpers/node-helpers.ts similarity index 99% rename from src/utils/node-utils.ts rename to src/helpers/node-helpers.ts index a232c29..219a946 100644 --- a/src/utils/node-utils.ts +++ b/src/helpers/node-helpers.ts @@ -1,10 +1,10 @@ import { NodeData, SiteData } from "../components/MainScene/Site/Site"; -import node_matrices from "../resources/node_matrices.json"; -import unlocked_nodes from "../resources/initial_progress.json"; -import node_huds from "../resources/node_huds.json"; import site_a from "../resources/site_a.json"; import site_b from "../resources/site_b.json"; -import {GameProgress} from "../store"; +import node_huds from "../resources/node_huds.json"; +import unlocked_nodes from "../resources/initial_progress.json"; +import node_matrices from "../resources/node_matrices.json"; +import { GameProgress } from "../store"; export const generateInactiveNodes = ( visibleNodes: SiteData, diff --git a/src/scenes/EndScene.tsx b/src/scenes/EndScene.tsx index fae1f95..e74a3b4 100644 --- a/src/scenes/EndScene.tsx +++ b/src/scenes/EndScene.tsx @@ -15,7 +15,6 @@ import sleep from "../utils/sleep"; const EndScene = () => { const mainCylinderRef = useRef(); - const setAudioAnalyser = useStore((state) => state.setAudioAnalyser); const setSelectionVisible = useStore( (state) => state.setEndSceneSelectionVisible ); @@ -110,7 +109,7 @@ const EndScene = () => { mediaElement.load(); mediaElement.play(); } - }, [setAudioAnalyser]); + }, []); return ( <> @@ -128,9 +127,7 @@ const EndScene = () => { - - - + ); }; diff --git a/src/scenes/GateScene.tsx b/src/scenes/GateScene.tsx index f0e28d6..2e88e39 100644 --- a/src/scenes/GateScene.tsx +++ b/src/scenes/GateScene.tsx @@ -7,18 +7,20 @@ import { useStore } from "../store"; const GateScene = () => { const gateLvl = useStore((state) => state.gateLvl); + const incrementGateLvl = useStore((state) => state.incrementGateLvl); const [introAnim, setIntroAnim] = useState(true); const activeNodeName = useStore((state) => state.activeNode.node_name); const setNodeViewed = useStore((state) => state.setNodeViewed); useEffect(() => { + incrementGateLvl(); setNodeViewed(activeNodeName, { is_viewed: 1, is_visible: 0, }); setTimeout(() => setIntroAnim(false), 2500); - }, [activeNodeName, setNodeViewed]); + }, [activeNodeName, incrementGateLvl, setNodeViewed]); return ( diff --git a/src/scenes/MainScene.tsx b/src/scenes/MainScene.tsx index 43af324..b4d1a1a 100644 --- a/src/scenes/MainScene.tsx +++ b/src/scenes/MainScene.tsx @@ -14,7 +14,7 @@ import Lain from "../components/MainScene/Lain"; import * as THREE from "three"; import { useFrame } from "react-three-fiber"; import Popups from "../components/MainScene/Popups/Popups"; -import * as audio from "../static/sfx"; +import * as audio from "../static/audio/sfx"; import Loading from "../components/Loading"; import usePrevious from "../hooks/usePrevious"; @@ -26,6 +26,8 @@ const MainScene = () => { const wordSelected = useStore((state) => state.wordSelected); const setWordSelected = useStore((state) => state.setWordSelected); + const setInputCooldown = useStore((state) => state.setInputCooldown); + const wordNotFound = useStore((state) => state.wordNotFound); useEffect(() => { if (subscene === "pause") { @@ -78,13 +80,27 @@ const MainScene = () => { introWrapperRef.current.rotation.x -= 0.008; } + // introWrapperRef.current.position.z = THREE.MathUtils.lerp( + // introWrapperRef.current.position.z, + // intro ? 0 : -10, + // 0.01 + // ); + // + // introWrapperRef.current.rotation.x = THREE.MathUtils.lerp( + // introWrapperRef.current.rotation.x, + // intro ? 0 : Math.PI / 2, + // 0.01 + // ); + if ( + !introFinished && !( introWrapperRef.current.rotation.x > 0 && introWrapperRef.current.position.z < 0 ) ) { setIntroFinished(true); + setInputCooldown(0); } } }); @@ -97,7 +113,7 @@ const MainScene = () => { - + diff --git a/src/scenes/MediaScene.tsx b/src/scenes/MediaScene.tsx index c4304ec..c7d448e 100644 --- a/src/scenes/MediaScene.tsx +++ b/src/scenes/MediaScene.tsx @@ -71,7 +71,7 @@ const MediaScene = () => { useEffect(() => { setLoaded(true); - setTimeout(() => setInputCooldown(false), 1000); + setTimeout(() => setInputCooldown(500), 1000); }, [setInputCooldown]); return ( diff --git a/src/scenes/PolytanScene.tsx b/src/scenes/PolytanScene.tsx index 96c4616..7a72725 100644 --- a/src/scenes/PolytanScene.tsx +++ b/src/scenes/PolytanScene.tsx @@ -4,7 +4,6 @@ import PolytanBackground from "../components/PolytanScene/PolytanBackground"; import { useStore } from "../store"; const PolytanScene = () => { - const unlockedParts = useStore((state) => state.polytanUnlockedParts); const setNodeViewed = useStore((state) => state.setNodeViewed); const setPolytanPartUnlocked = useStore.getState().setPolytanPartUnlocked; const activeNodeName = useStore((state) => state.activeNode.node_name); @@ -34,10 +33,10 @@ const PolytanScene = () => { }, [activeNodeName, setNodeViewed, setPolytanPartUnlocked]); return ( - - + <> + - + ); }; diff --git a/src/store.ts b/src/store.ts index a6c84f8..89f1546 100644 --- a/src/store.ts +++ b/src/store.ts @@ -3,35 +3,103 @@ import { combine } from "zustand/middleware"; import * as THREE from "three"; import game_progress from "./resources/initial_progress.json"; import { NodeData } from "./components/MainScene/Site/Site"; -import { getNodeById } from "./utils/node-utils"; +import { getNodeById } from "./helpers/node-helpers"; import site_a from "./resources/site_a.json"; +import { AudioAnalyser } from "three"; +import MainScene from "./scenes/MainScene"; +import MediaScene from "./scenes/MediaScene"; +import IdleMediaScene from "./scenes/IdleMediaScene"; +import GateScene from "./scenes/GateScene"; +import BootScene from "./scenes/BootScene"; +import SsknScene from "./scenes/SsknScene"; +import PolytanScene from "./scenes/PolytanScene"; +import TaKScene from "./scenes/TaKScene"; +import ChangeDiscScene from "./scenes/ChangeDiscScene"; +import EndScene from "./scenes/EndScene"; +import React from "react"; export type GameProgress = typeof game_progress; +type NodeAttributes = { + interactedWith: boolean; + exploding: boolean; + shrinking: boolean; + visible: boolean; +}; + +export type GameScene = + | "main" + | "media" + | "tak" + | "sskn" + | "polytan" + | "idle_media" + | "gate" + | "boot" + | "change_disc" + | "end"; + +type MainSubscene = "site" | "pause" | "level_selection"; + +export type ActiveSite = "a" | "b"; + +export type EndComponent = "end" | "continue"; + +export type PauseComponent = "load" | "about" | "change" | "save" | "exit"; + +export type MediaSide = "left" | "right"; +export type LeftMediaComponent = "play" | "exit"; +export type RightMediaComponent = "fstWord" | "sndWord" | "thirdWord"; +export type MediaComponent = LeftMediaComponent | RightMediaComponent; + +export type SiteSaveState = { + a: { + activeNode: NodeData; + siteRot: number[]; + activeLevel: string; + }; + b: { + activeNode: NodeData; + siteRot: number[]; + activeLevel: string; + }; +}; + +export type SsknComponent = "ok" | "cancel"; + +export type MainMenuComponent = "authorize_user" | "load_data"; +export type BootSubscene = "main_menu" | "load_data" | "authorize_user"; + +export type PromptComponent = "yes" | "no"; + +type PolytanBodyParts = { + body: boolean; + head: boolean; + leftArm: boolean; + rightArm: boolean; + leftLeg: boolean; + rightLeg: boolean; +}; + type State = { - currentScene: string; + currentScene: GameScene; gameProgress: GameProgress; - mainSubscene: string; + mainSubscene: MainSubscene; intro: boolean; activeNode: NodeData; activeNodePos: number[]; activeNodeRot: number[]; - activeNodeAttributes: { - interactedWith: boolean; - exploding: boolean; - shrinking: boolean; - visible: boolean; - }; + activeNodeAttributes: NodeAttributes; // lain lainMoveState: string; // site - activeSite: "a" | "b"; + activeSite: ActiveSite; siteRot: number[]; oldSiteRot: number[]; @@ -43,23 +111,23 @@ type State = { selectedLevel: number; // end scene - activeEndComponent: "end" | "continue"; + activeEndComponent: EndComponent; endSceneSelectionVisible: boolean; // pause - activePauseComponent: "load" | "about" | "change" | "save" | "exit"; + activePauseComponent: PauseComponent; pauseExitAnimation: boolean; showingAbout: boolean; permissionDenied: boolean; // media/media scene - audioAnalyser: any; + audioAnalyser: AudioAnalyser | undefined; mediaPercentageElapsed: number; - currentMediaSide: "left" | "right"; - activeMediaComponent: "play" | "exit" | "fstWord" | "sndWord" | "thirdWord"; + currentMediaSide: MediaSide; + activeMediaComponent: MediaComponent; lastActiveMediaComponents: { - left: "play" | "exit"; - right: "fstWord" | "sndWord" | "thirdWord"; + left: LeftMediaComponent; + right: RightMediaComponent; }; mediaWordPosStateIdx: number; @@ -72,19 +140,12 @@ type State = { idleNodeName: string | undefined; // sskn scene - activeSsknComponent: "ok" | "cancel"; + activeSsknComponent: SsknComponent; ssknLoading: boolean; ssknLvl: number; // polytan scene - polytanUnlockedParts: { - body: boolean; - head: boolean; - leftArm: boolean; - rightArm: boolean; - leftLeg: boolean; - rightLeg: boolean; - }; + polytanUnlockedParts: PolytanBodyParts; // gate scene gateLvl: number; @@ -93,40 +154,32 @@ type State = { playerName: string; // boot scene - activeMainMenuComponent: "authorize_user" | "load_data"; + activeMainMenuComponent: MainMenuComponent; authorizeUserLetterIdx: number; - bootSubscene: "main_menu" | "load_data" | "authorize_user"; + bootSubscene: BootSubscene; // prompt promptVisible: boolean; - activePromptComponent: "yes" | "no"; + activePromptComponent: PromptComponent; // status notifiers loadSuccessful: boolean | undefined; saveSuccessful: boolean | undefined; - // save state - siteSaveState: { - a: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - b: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - }; + // word not found notification thing + wordNotFound: boolean; - inputCooldown: boolean; + // save state + siteSaveState: SiteSaveState; + + inputCooldown: number; }; export const useStore = create( combine( { // scene data - currentScene: "media", + currentScene: "main", // game progress gameProgress: game_progress, @@ -232,6 +285,9 @@ export const useStore = create( loadSuccessful: undefined, saveSuccessful: undefined, + // word not found notification thing + wordNotFound: false, + // save states for loading the game/changing sites siteSaveState: { a: { @@ -252,13 +308,11 @@ export const useStore = create( }, }, - inputCooldown: false, + inputCooldown: -1, } as State, (set) => ({ - // scene data setters - setScene: (to: string) => set(() => ({ currentScene: to })), + setScene: (to: GameScene) => set(() => ({ currentScene: to })), - // node setters setNodePos: (to: number[]) => set(() => ({ activeNodePos: to })), setNodeRot: (to: number[]) => set(() => ({ activeNodeRot: to })), setNodeAttributes: ( @@ -269,102 +323,6 @@ export const useStore = create( activeNodeAttributes: { ...state.activeNodeAttributes, [at]: to }, })), - // lain setters - setLainMoveState: (to: string) => set(() => ({ lainMoveState: to })), - - // site setters - setSiteRotX: (to: number) => - set((prev) => { - const nextRot = [...prev.siteRot]; - nextRot[0] = to; - return { siteRot: nextRot }; - }), - - // end scene setters - setEndSceneSelectionVisible: (to: boolean) => - set(() => ({ endSceneSelectionVisible: to })), - - // pause setters - setShowingAbout: (to: boolean) => set(() => ({ showingAbout: to })), - - // media scene setters - setAudioAnalyser: (to: any) => set(() => ({ audioAnalyser: to })), - setPercentageElapsed: (to: number) => - set(() => ({ mediaPercentageElapsed: to })), - setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })), - updateLeftSide: ( - newActiveComponent: "fstWord" | "sndWord" | "thirdWord", - lastActiveComponent: "exit" | "play" - ) => - set((state) => ({ - activeMediaComponent: newActiveComponent, - lastActiveMediaComponents: { - ...state.lastActiveMediaComponents, - left: lastActiveComponent, - }, - currentMediaSide: "right", - })), - updateRightSide: ( - newActiveComponent: "play" | "exit", - lastActiveComponent: "fstWord" | "sndWord" | "thirdWord" - ) => - set((state) => ({ - activeMediaComponent: newActiveComponent, - lastActiveMediaComponents: { - ...state.lastActiveMediaComponents, - right: lastActiveComponent, - }, - currentMediaSide: "left", - })), - - // idle media setters - setIdleStarting: (to: boolean) => set(() => ({ idleStarting: to })), - setIdleScene: (to: { - images: { "1": string; "2": string; "3": string } | undefined; - media: string | undefined; - nodeName: string | undefined; - }) => - set(() => ({ - idleMedia: to.media, - idleImages: to.images, - idleNodeName: to.nodeName, - })), - - //polytan setters - setPolytanPartUnlocked: (bodyPart: string) => - set((state) => ({ - polytanUnlockedParts: { - ...state.polytanUnlockedParts, - [bodyPart]: true, - }, - })), - - changeSite: (to: "a" | "b") => - set((state) => { - const newState = state.siteSaveState[to]; - return { - currentScene: "change_disc", - promptVisible: false, - activePromptComponent: "no", - mainSubscene: "site", - // load new state - activeSite: to, - activeNode: newState.activeNode, - siteRot: newState.siteRot, - activeLevel: newState.activeLevel, - // save current state - siteSaveState: { - ...state.siteSaveState, - [to === "a" ? "b" : "a"]: { - activeNode: state.activeNode, - siteRot: [0, state.siteRot[1], 0], - activeLevel: state.activeLevel, - }, - }, - }; - }), - - // progress setters setNodeViewed: ( nodeName: string, to: { is_viewed: number; is_visible: number } @@ -376,13 +334,37 @@ export const useStore = create( }, })), - setInputCooldown: (to: boolean) => set(() => ({ inputCooldown: to })), + setLainMoveState: (to: string) => set(() => ({ lainMoveState: to })), + + setEndSceneSelectionVisible: (to: boolean) => + set(() => ({ endSceneSelectionVisible: to })), + + setShowingAbout: (to: boolean) => set(() => ({ showingAbout: to })), + + setAudioAnalyser: (to: AudioAnalyser) => + set(() => ({ audioAnalyser: to })), + setPercentageElapsed: (to: number) => + set(() => ({ mediaPercentageElapsed: to })), + setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })), + + setPolytanPartUnlocked: (bodyPart: string) => + set((state) => ({ + polytanUnlockedParts: { + ...state.polytanUnlockedParts, + [bodyPart]: true, + }, + })), + + setInputCooldown: (to: number) => set(() => ({ inputCooldown: to })), + + incrementGateLvl: () => set((state) => ({ gateLvl: state.gateLvl + 1 })), + incrementSsknLvl: () => set((state) => ({ ssknLvl: state.ssknLvl + 1 })), }) ) ); type PromptContext = { - activePromptComponent: "yes" | "no"; + activePromptComponent: PromptComponent; promptVisible: boolean; }; @@ -401,25 +383,15 @@ export interface MainSceneContext extends PromptContext { activeNode: NodeData; showingAbout: boolean; level: number; - activePauseComponent: "load" | "about" | "change" | "save" | "exit"; + activePauseComponent: PauseComponent; gameProgress: GameProgress; gateLvl: number; subscene: string; siteRotY: number; - activeSite: "a" | "b"; + activeSite: ActiveSite; selectedLevel: number; - siteSaveState: { - a: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - b: { - activeNode: NodeData; - siteRot: number[]; - activeLevel: string; - }; - }; + wordNotFound: boolean; + siteSaveState: SiteSaveState; } export const getMainSceneContext = (keyPress: string): MainSceneContext => { @@ -440,13 +412,16 @@ export const getMainSceneContext = (keyPress: string): MainSceneContext => { showingAbout: state.showingAbout, gateLvl: state.gateLvl, siteSaveState: state.siteSaveState, + wordNotFound: state.wordNotFound, }; }; export type SsknSceneContext = { keyPress: string; - activeSsknComponent: "ok" | "cancel"; + activeSsknComponent: SsknComponent; activeNode: NodeData; + gameProgress: GameProgress; + ssknLvl: number; }; export const getSsknSceneContext = (keyPress: string): SsknSceneContext => { @@ -455,21 +430,23 @@ export const getSsknSceneContext = (keyPress: string): SsknSceneContext => { keyPress: keyPress, activeSsknComponent: state.activeSsknComponent, activeNode: state.activeNode, + gameProgress: state.gameProgress, + ssknLvl: state.ssknLvl, }; }; export type MediaSceneContext = { keyPress: string; wordPosStateIdx: number; - currentMediaSide: "left" | "right"; - activeMediaComponent: "play" | "exit" | "fstWord" | "sndWord" | "thirdWord"; + currentMediaSide: MediaSide; + activeMediaComponent: MediaComponent; activeNode: NodeData; gameProgress: GameProgress; lastActiveMediaComponents: { - left: "play" | "exit"; - right: "fstWord" | "sndWord" | "thirdWord"; + left: LeftMediaComponent; + right: RightMediaComponent; }; - activeSite: "a" | "b"; + activeSite: ActiveSite; }; export const getMediaSceneContext = (keyPress: string): MediaSceneContext => { @@ -490,8 +467,8 @@ export const getMediaSceneContext = (keyPress: string): MediaSceneContext => { export interface BootSceneContext extends PromptContext { keyPress: string; playerName: string; - subscene: "main_menu" | "load_data" | "authorize_user"; - activeMainMenuComponent: "load_data" | "authorize_user"; + subscene: BootSubscene; + activeMainMenuComponent: MainMenuComponent; authorizeUserLetterIdx: number; } @@ -510,7 +487,7 @@ export const getBootSceneContext = (keyPress: string): BootSceneContext => { export type EndSceneContext = { keyPress: string; - activeEndComponent: "end" | "continue"; + activeEndComponent: EndComponent; selectionVisible: boolean; }; diff --git a/src/tests/media-helpers.test.ts b/src/tests/media-helpers.test.ts new file mode 100644 index 0000000..56f3efa --- /dev/null +++ b/src/tests/media-helpers.test.ts @@ -0,0 +1,2 @@ +import { findNodeFromWord } from "../helpers/media-helpers"; + diff --git a/src/utils/keyPressUtils.ts b/src/utils/getKey.ts similarity index 100% rename from src/utils/keyPressUtils.ts rename to src/utils/getKey.ts