diff --git a/src/App.tsx b/src/App.tsx index 17455a0..5121dcb 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,8 +21,8 @@ const App = () => { document.title = "< index >"; }, []); - const dispatchScene = useMemo(() => { - return { + const dispatchScene = useMemo( + () => ({ main: , media: , idle_media: , @@ -33,8 +33,9 @@ const App = () => { tak: , change_disc: , end: , - }; - }, []); + }), + [] + ); return (
@@ -45,7 +46,9 @@ const App = () => { - + {["media", "idle_media", "tak", "end"].includes(currentScene) && ( + + )}
); }; diff --git a/src/components/MainScene/SyncedComponents/HUD.tsx b/src/components/MainScene/SyncedComponents/HUD.tsx index 8be740e..a8dec8e 100644 --- a/src/components/MainScene/SyncedComponents/HUD.tsx +++ b/src/components/MainScene/SyncedComponents/HUD.tsx @@ -40,10 +40,10 @@ const HUD = memo(() => { (state) => state.activeNode.matrixIndices ); const siteRotY = useStore((state) => state.siteRot[1]); - const sitePosY = useStore((state) => state.sitePos[1]); + const activeLevel = useStore((state) => state.activeLevel); const subscene = useStore((state) => state.mainSubscene); const scene = useStore((state) => state.currentScene); - const prevData = usePrevious({ siteRotY, sitePosY, subscene, scene }); + const prevData = usePrevious({ siteRotY, activeLevel, subscene, scene }); // this part is imperative because it performs a lot better than having a toggleable spring. useFrame(() => { @@ -123,7 +123,7 @@ const HUD = memo(() => { } else { if ( prevData?.siteRotY !== siteRotY || - prevData?.sitePosY !== sitePosY || + prevData?.activeLevel !== activeLevel || subscene === "level_selection" ) { activeRef.current = false; @@ -165,13 +165,13 @@ const HUD = memo(() => { } } }, [ + activeLevel, activeNodeMatrixIndices, + prevData?.activeLevel, prevData?.scene, - prevData?.sitePosY, prevData?.siteRotY, prevData?.subscene, scene, - sitePosY, siteRotY, subscene, ]); diff --git a/src/components/MainScene/SyncedComponents/LevelSelection.tsx b/src/components/MainScene/SyncedComponents/LevelSelection.tsx index face0d5..96c7cac 100644 --- a/src/components/MainScene/SyncedComponents/LevelSelection.tsx +++ b/src/components/MainScene/SyncedComponents/LevelSelection.tsx @@ -111,6 +111,7 @@ const LevelSelection = () => { }, [ activeLevel, downArrowActiveTex, + downArrowTex, prevData?.selectedLevel, prevData?.subscene, selectedLevel, diff --git a/src/components/MainScene/SyncedComponents/Site.tsx b/src/components/MainScene/SyncedComponents/Site.tsx index 4296e73..ae454bc 100644 --- a/src/components/MainScene/SyncedComponents/Site.tsx +++ b/src/components/MainScene/SyncedComponents/Site.tsx @@ -7,10 +7,11 @@ import NodeAnimations from "./Site/NodeAnimations"; import InactiveLevelNodes from "./Site/InactiveLevelNodes"; import { useFrame } from "react-three-fiber"; import * as THREE from "three"; -import lerp from "../../../core/utils/lerp"; import filterInvisibleNodes from "../../../core/utils/filterInvisibleNodes"; import site_a from "../../../resources/site_a.json"; import site_b from "../../../resources/site_b.json"; +import level_y_values from "../../../resources/level_y_values.json"; +import usePrevious from "../../../hooks/usePrevious"; export type NodeDataType = { id: string; @@ -47,14 +48,33 @@ type SiteProps = { }; const Site = (props: SiteProps) => { - const siteRot = useStore((state) => state.siteRot); - const sitePos = useStore((state) => state.sitePos); - const siteState = useSpring({ - siteRotX: siteRot[0], - siteRotY: siteRot[1], - sitePosY: sitePos[1], + const activeLevel = useStore((state) => state.activeLevel); + + const [state, setState] = useSpring(() => ({ + posY: -level_y_values[ + useStore.getState().activeLevel as keyof typeof level_y_values + ], + rotX: useStore.getState().siteRot[0], + rotY: useStore.getState().siteRot[1], config: { duration: 1200 }, - }); + })); + + useEffect(() => { + setTimeout(() => { + setState({ + posY: -level_y_values[activeLevel as keyof typeof level_y_values], + }); + }, 1200); + }, [activeLevel, setState]); + + useEffect(() => { + useStore.subscribe(setState, (state) => { + return { + rotX: state.siteRot[0], + rotY: state.siteRot[1], + }; + }); + }, [setState]); const introWrapperRef = useRef(); @@ -90,18 +110,15 @@ const Site = (props: SiteProps) => { return ( - - + + - + diff --git a/src/components/MediaScene/MediaLoadingBar.tsx b/src/components/MediaScene/MediaLoadingBar.tsx index 6464aa7..810d69d 100644 --- a/src/components/MediaScene/MediaLoadingBar.tsx +++ b/src/components/MediaScene/MediaLoadingBar.tsx @@ -31,6 +31,11 @@ const MediaLoadingBar = () => { // doing it declaratively like this fixes that concern const loadingBarState = useMemo(() => { const mediaPercentageDispatch = { + 0: { + scaleX: 0, + texture: loadingBar10PercTex, + offsetX: 0, + }, 5: { scaleX: 0.25, texture: loadingBar10PercTex, diff --git a/src/components/MediaScene/MediaPlayer.tsx b/src/components/MediaScene/MediaPlayer.tsx index a3f150a..4cc3184 100644 --- a/src/components/MediaScene/MediaPlayer.tsx +++ b/src/components/MediaScene/MediaPlayer.tsx @@ -9,8 +9,8 @@ import React, { import { useStore } from "../../store"; import t from "../../static/webvtt/test.vtt"; import endroll from "../../static/movie/ENDROLL1.STR[0].webm"; -import xa0001 from "../../static/audio/a/Xa0001.mp4"; -import xa0006 from "../../static/audio/a/Xa0006.mp4"; +import xa0001 from "../../static/audio/Xa0001.mp4"; +import xa0006 from "../../static/audio/Xa0006.mp4"; const MediaPlayer = () => { const [mediaSource, setMediaSource] = useState(); @@ -18,9 +18,7 @@ const MediaPlayer = () => { const currentScene = useStore((state) => state.currentScene); const setScene = useStore((state) => state.setScene); - const setPercentageElapsed = useStore( - (state) => state.setPercentageElapsed - ); + const setPercentageElapsed = useStore((state) => state.setPercentageElapsed); const idleMedia = useStore((state) => state.idleMedia); const nodeMedia = useStore((state) => state.activeNode.media_file); @@ -32,12 +30,8 @@ const MediaPlayer = () => { const requestRef = useRef(); const videoRef = createRef(); - const currentSite = useStore((state) => state.activeSite); - // end scene specific stuff - const endMediaPlayedCount = useStore( - (state) => state.endMediaPlayedCount - ); + const endMediaPlayedCount = useStore((state) => state.endMediaPlayedCount); const incrementEndMediaPlayedCount = useStore( (state) => state.incrementEndMediaPlayedCount ); @@ -47,10 +41,7 @@ const MediaPlayer = () => { const updateTime = useCallback(() => { (requestRef.current as any) = requestAnimationFrame(updateTime); - if ( - videoRef.current && - ["media", "idle_media", "tak", "end"].includes(currentScene) - ) { + if (videoRef.current) { const timeElapsed = videoRef.current.currentTime; const duration = videoRef.current.duration; const percentageElapsed = Math.floor((timeElapsed / duration) * 100); @@ -58,6 +49,7 @@ const MediaPlayer = () => { if (percentageElapsed % 5 === 0 && percentageElapsed !== 0) { setPercentageElapsed(percentageElapsed); if (percentageElapsed === 100) { + if (currentScene === "media") setPercentageElapsed(0); videoRef.current.currentTime = 0; if (currentScene === "idle_media") { videoRef.current.pause(); @@ -124,45 +116,30 @@ const MediaPlayer = () => { } } } else { - if (currentScene === "media" || currentScene === "tak") { - if (nodeMedia.includes("XA")) { - import( - "../../static/audio/" + currentSite + "/" + nodeMedia + ".ogg" - ).then((media) => { - setMediaSource(media.default); - if (videoRef.current) { - videoRef.current.load(); - } - }); - } else { - import("../../static/movie/" + nodeMedia + "[0].webm").then( - (media) => { - setMediaSource(media.default); - if (videoRef.current) { - videoRef.current.load(); - } - } - ); - } - } else if (currentScene === "idle_media") { - if (idleMedia) { - if (idleMedia.includes("XA")) { - import( - "../../static/audio/" + currentSite + "/" + idleMedia + ".ogg" - ).then((media) => { - setMediaSource(media.default); - if (videoRef.current) { - videoRef.current.load(); - videoRef.current.play(); - } - }); - } else { - import("../../static/movie/" + idleMedia + "[0].webm").then( + if ( + currentScene === "media" || + currentScene === "tak" || + currentScene === "idle_media" + ) { + if (currentScene === "media") setPercentageElapsed(0); + const mediaToPlay = + currentScene === "idle_media" ? idleMedia : nodeMedia; + if (mediaToPlay) { + if (mediaToPlay.includes("XA")) { + import("../../static/audio/" + mediaToPlay + ".ogg").then( + (media) => { + setMediaSource(media.default); + if (videoRef.current) { + videoRef.current.load(); + } + } + ); + } else { + import("../../static/movie/" + mediaToPlay + "[0].webm").then( (media) => { setMediaSource(media.default); if (videoRef.current) { videoRef.current.load(); - videoRef.current.play(); } } ); @@ -172,10 +149,10 @@ const MediaPlayer = () => { } }, [ currentScene, - currentSite, endMediaPlayedCount, idleMedia, nodeMedia, + setPercentageElapsed, videoRef, ]); diff --git a/src/components/TextRenderer/BigLetter.tsx b/src/components/TextRenderer/BigLetter.tsx index e7e4984..7552843 100644 --- a/src/components/TextRenderer/BigLetter.tsx +++ b/src/components/TextRenderer/BigLetter.tsx @@ -137,6 +137,7 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => { activeMediaComponent, lastMediaLeftComponent, prevData?.scene, + prevData?.subscene, ]); return ( diff --git a/src/core/StateManagers/MainSceneEventManager.tsx b/src/core/StateManagers/MainSceneEventManager.tsx index 03ca78d..e9b70fa 100644 --- a/src/core/StateManagers/MainSceneEventManager.tsx +++ b/src/core/StateManagers/MainSceneEventManager.tsx @@ -25,7 +25,6 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => { const activeNodeId = useStore((state) => state.activeNode.id); const nodeMatrixIndices = useStore((state) => state.activeNode.matrixIndices); const siteRotY = useStore((state) => state.siteRot[1]); - const sitePosY = useStore((state) => state.sitePos[1]); const activeLevel = useStore((state) => state.activeLevel); const mainSubscene = useStore((state) => state.mainSubscene); const selectedLevel = useStore((state) => state.selectedLevel); @@ -54,7 +53,7 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => { // scene: "idle_media", // site: currentSite, // }); - // timePassedSinceLastKeyPress.current = -1; + timePassedSinceLastKeyPress.current = -1; } else if (now > timePassedSinceLastKeyPress.current + 10000) { const moves = [ "prayer", @@ -94,7 +93,6 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => { const event = handleMainSceneEvent({ mainSubscene: mainSubscene, keyPress: keyPress, - sitePosY: sitePosY, siteRotY: siteRotY, activeNodeId: activeNodeId, nodeMatrixIndices: nodeMatrixIndices, @@ -112,7 +110,6 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => { [ props.loaded, mainSubscene, - sitePosY, siteRotY, activeNodeId, nodeMatrixIndices, diff --git a/src/core/StateManagers/MainSceneManagers/SiteManager.tsx b/src/core/StateManagers/MainSceneManagers/SiteManager.tsx index ec157cb..78d9ce8 100644 --- a/src/core/StateManagers/MainSceneManagers/SiteManager.tsx +++ b/src/core/StateManagers/MainSceneManagers/SiteManager.tsx @@ -3,22 +3,12 @@ import { useStore } from "../../../store"; import { StateManagerProps } from "../EventManager"; const SiteManager = (props: StateManagerProps) => { - const setPos = useStore((state) => state.setSitePos); const setRot = useStore((state) => state.setSiteRot); const setRotX = useStore((state) => state.setSiteRotX); const dispatchObject = useCallback( - (eventState: { event: string; sitePosY: number; siteRotY: number }) => { + (eventState: { event: string; siteRotY: number }) => { switch (eventState.event) { - case "site_up": - case "site_down": - case "select_level_up": - case "select_level_down": - return { - action: setPos, - value: [[0, eventState.sitePosY, 0]], - actionDelay: 1300, - }; case "site_left": case "site_right": return { @@ -40,7 +30,7 @@ const SiteManager = (props: StateManagerProps) => { }; } }, - [setPos, setRot, setRotX] + [setRot, setRotX] ); useEffect(() => { diff --git a/src/core/nodeSelector.ts b/src/core/nodeSelector.ts index 1ea14b2..bc37912 100644 --- a/src/core/nodeSelector.ts +++ b/src/core/nodeSelector.ts @@ -8,6 +8,12 @@ import { import unlocked_nodes from "../resources/initial_progress.json"; import level_y_values from "../resources/level_y_values.json"; import node_huds from "../resources/node_huds.json"; +import filterInvisibleNodes from "./utils/filterInvisibleNodes"; +import { + findNodeLeft, + findNodeRight, + getVisibleNodesMatrix, +} from "./utils/nodeUtils"; type NodeSelectorContext = { action: string; @@ -35,6 +41,12 @@ const hudAssocs = { "23": "fg_hud_6", }; +export const getNodeById = (id: string, currentSite: string) => { + const siteData = currentSite === "a" ? site_a : site_b; + const level = id.substr(0, 2); + return (siteData as SiteType)[level][id]; +}; + export const getNode = ( level: number, nodeMatrixIndices: { @@ -377,6 +389,21 @@ const nodeSelector = (context: NodeSelectorContext) => { switch (context.action) { case "site_left": case "site_right": + const t = + context.action === "site_right" + ? findNodeRight( + context.nodeMatrixIndices, + context.level, + context.currentSite, + context.gameProgress + ) + : findNodeLeft( + context.nodeMatrixIndices, + context.level, + context.currentSite, + context.gameProgress + ); + move = context.action === "site_left" ? "left" : "right"; newNodeData = findNodeHorizontal( move, @@ -387,14 +414,15 @@ const nodeSelector = (context: NodeSelectorContext) => { context.currentSite ); + console.log(t!.matrixIndices); if (newNodeData) { const siteRotYModifier = move === "left" ? Math.PI / 4 : -Math.PI / 4; return { event: newNodeData.didMove ? context.action : "change_node", - node: newNodeData.node, + node: getNodeById(t!.node, context.currentSite), newActiveHud: newNodeData.newNodeHud, - newNodeMatrixIndices: newNodeData.newNodeMatrixIndices, + newNodeMatrixIndices: t!.matrixIndices, newSiteRotY: newNodeData.didMove ? context.siteRotY + siteRotYModifier : context.siteRotY, diff --git a/src/core/utils/nodeUtils.ts b/src/core/utils/nodeUtils.ts index c37f97e..17c8dbd 100644 --- a/src/core/utils/nodeUtils.ts +++ b/src/core/utils/nodeUtils.ts @@ -1,4 +1,6 @@ import { SiteType } from "../../components/MainScene/SyncedComponents/Site"; +import node_matrices from "../../resources/node_matrices.json"; +import { getNode, getNodeById, isNodeVisible } from "../nodeSelector"; export const generateInactiveNodes = ( visibleNodes: SiteType, @@ -19,3 +21,186 @@ export const generateInactiveNodes = ( return obj; }; + +export const getVisibleNodesMatrix = ( + matrixIdx: number, + activeLevel: number, + currentSite: string, + gameProgress: any +) => { + const formattedLevel = activeLevel.toString().padStart(2, "0"); + const currentMatrix = + node_matrices[matrixIdx.toString() as keyof typeof node_matrices]; + + return currentMatrix.map((row: string[]) => + row.map((nodePos: string) => { + const nodeId = formattedLevel + nodePos; + if (isNodeVisible(getNodeById(nodeId, currentSite), gameProgress)) + return nodeId; + else return undefined; + }) + ); +}; + +const generateRowPrecedence = (rowIdx: number) => { + switch (rowIdx) { + case 0: + return [0, 1, 2]; + case 1: + return [1, 0, 2]; + case 2: + return [2, 1, 0]; + default: + return [0, 1, 2]; + } +}; + +export const findNodeLeft = ( + nodeMatrixIndices: { + matrixIdx: number; + rowIdx: number; + colIdx: number; + }, + level: number, + currentSite: string, + gameProgress: any +) => { + const { matrixIdx, rowIdx, colIdx } = nodeMatrixIndices; + + const visibleNodes = getVisibleNodesMatrix( + matrixIdx, + level, + currentSite, + gameProgress + ); + + const precedence = generateRowPrecedence(rowIdx); + + let chosenNode; + let matrixIndices; + loop: for (let col = colIdx - 1; col > -1; col--) { + for (let i = 0; i < 3; i++) { + const current = visibleNodes[precedence[i]][col]; + if (current) { + chosenNode = current; + matrixIndices = { + matrixIdx: matrixIdx, + rowIdx: precedence[i], + colIdx: col, + }; + break loop; + } + } + } + + if (chosenNode) { + return { + node: chosenNode, + matrixIndices: matrixIndices, + didRotate: false, + }; + } else { + const newMatrixIdx = matrixIdx + 1 > 8 ? 1 : matrixIdx + 1; + const visibleNodes = getVisibleNodesMatrix( + newMatrixIdx, + level, + currentSite, + gameProgress + ); + + loop: for (let col = 0; col < 4; col++) { + for (let i = 0; i < 3; i++) { + const current = visibleNodes[precedence[i]][col]; + if (current) { + chosenNode = current; + matrixIndices = { + matrixIdx: newMatrixIdx, + rowIdx: precedence[i], + colIdx: col, + }; + break loop; + } + } + } + + if (chosenNode) + return { + node: chosenNode, + matrixIndices: matrixIndices, + didRotate: true, + }; + } +}; + +export const findNodeRight = ( + nodeMatrixIndices: { + matrixIdx: number; + rowIdx: number; + colIdx: number; + }, + level: number, + currentSite: string, + gameProgress: any +) => { + const { matrixIdx, rowIdx, colIdx } = nodeMatrixIndices; + + const visibleNodes = getVisibleNodesMatrix( + matrixIdx, + level, + currentSite, + gameProgress + ); + + const precedence = generateRowPrecedence(rowIdx); + + let chosenNode; + let matrixIndices; + loop: for (let col = colIdx + 1; col < 4; col++) { + for (let i = 0; i < 3; i++) { + const current = visibleNodes[precedence[i]][col]; + if (current) { + chosenNode = current; + matrixIndices = { + matrixIdx: matrixIdx, + rowIdx: precedence[i], + colIdx: col, + }; + break loop; + } + } + } + + if (chosenNode) { + return { node: chosenNode, didRotate: false, matrixIndices: matrixIndices }; + } else { + const newMatrixIdx = matrixIdx - 1 < 1 ? 8 : matrixIdx - 1; + const visibleNodes = getVisibleNodesMatrix( + newMatrixIdx, + level, + currentSite, + gameProgress + ); + + loop: for (let col = 3; col > -1; col--) { + for (let i = 0; i < 3; i++) { + const current = visibleNodes[precedence[i]][col]; + if (current) { + chosenNode = current; + matrixIndices = { + matrixIdx: newMatrixIdx, + rowIdx: precedence[i], + colIdx: col, + }; + break loop; + } + } + } + + if (chosenNode) + return { + node: chosenNode, + matrixIndices: matrixIndices, + didRotate: true, + }; + } +}; diff --git a/src/scenes/IdleMediaScene.tsx b/src/scenes/IdleMediaScene.tsx index 2476c74..c0a5764 100644 --- a/src/scenes/IdleMediaScene.tsx +++ b/src/scenes/IdleMediaScene.tsx @@ -6,12 +6,12 @@ const IdleMediaScene = () => { const idleMedia = useStore((state) => state.idleMedia); useEffect(() => { - document.getElementsByTagName("canvas")[0].className = - "media-scene-background"; + const mediaElement = document.getElementById("media") as HTMLMediaElement; - return () => { - document.getElementsByTagName("canvas")[0].className = ""; - }; + if (mediaElement) { + mediaElement.currentTime = 0; + mediaElement.play(); + } }, []); return ( diff --git a/src/store.ts b/src/store.ts index 962aa4e..5341d80 100644 --- a/src/store.ts +++ b/src/store.ts @@ -50,7 +50,6 @@ type State = { // site activeSite: "a" | "b"; siteRot: number[]; - sitePos: number[]; // level activeLevel: string; @@ -175,7 +174,6 @@ export const useStore = create( // site activeSite: "a", siteRot: [0, 0, 0], - sitePos: [0, 0, 0], // level activeLevel: "04", @@ -280,7 +278,6 @@ export const useStore = create( nextPos[0] = to; return { siteRot: nextPos }; }), - setSitePos: (to: number[]) => set(() => ({ sitePos: to })), // level setters setActiveLevel: (to: string) => set(() => ({ activeLevel: to })),