diff --git a/src/components/MainScene/SyncedComponents/LevelSelection.tsx b/src/components/MainScene/SyncedComponents/LevelSelection.tsx index d213aa1..face0d5 100644 --- a/src/components/MainScene/SyncedComponents/LevelSelection.tsx +++ b/src/components/MainScene/SyncedComponents/LevelSelection.tsx @@ -1,14 +1,17 @@ -import React, { useCallback } from "react"; +import React, { useEffect, useRef } from "react"; import level_selection_font from "../../../static/sprite/select_level_font.png"; import verticalHud from "../../../static/sprite/select_level_hud_vertical.png"; import horizontalHud from "../../../static/sprite/select_level_hud_horizontal.png"; import levelSelectionText from "../../../static/sprite/select_level_text.png"; import upArrow from "../../../static/sprite/select_level_up_arrow.png"; import downArrow from "../../../static/sprite/select_level_down_arrow.png"; +import upArrowActive from "../../../static/sprite/select_level_up_arrow_active.png"; +import downArrowActive from "../../../static/sprite/select_level_down_arrow_active.png"; import { useStore } from "../../../store"; import { useLoader } from "react-three-fiber"; import * as THREE from "three"; import { a, useSpring } from "@react-spring/three"; +import usePrevious from "../../../hooks/usePrevious"; const LevelSelection = () => { const levelSelectionFontTex = useLoader( @@ -23,47 +26,108 @@ const LevelSelection = () => { ); const upArrowTex = useLoader(THREE.TextureLoader, upArrow); const downArrowTex = useLoader(THREE.TextureLoader, downArrow); - - const toggled = useStore( - useCallback((state) => Number(state.mainSubscene === "level_selection"), []) - ); + const upArrowActiveTex = useLoader(THREE.TextureLoader, upArrowActive); + const downArrowActiveTex = useLoader(THREE.TextureLoader, downArrowActive); const selectedLevel = useStore((state) => state.selectedLevel) .toString() .padStart(2, "0"); + const activeLevel = useStore((state) => state.activeLevel); + const subscene = useStore((state) => state.mainSubscene); + const prevData = usePrevious({ subscene, selectedLevel }); - const { levelSelectionToggle } = useSpring({ - levelSelectionToggle: toggled, + const [pos, set] = useSpring(() => ({ + vertPosY: -2.5, + horizPosX: -4, config: { duration: 500 }, - }); + })); - const verticalHudPosY = levelSelectionToggle.to([0, 1], [-2.5, 0]); - const horizontalHudPosX = levelSelectionToggle.to([0, 1], [-4, -0.6]); + const fstNumberRef = useRef(); + const sndNumberRef = useRef(); + const upArrowRef = useRef(); + const downArrowRef = useRef(); - const generateGeom = useCallback((number: number) => { - const geometry = new THREE.PlaneBufferGeometry(); + useEffect(() => { + const generateGeom = (number: number) => { + const geometry = new THREE.PlaneBufferGeometry(); - const uvAttribute = geometry.attributes.uv; + const uvAttribute = geometry.attributes.uv; - for (let i = 0; i < uvAttribute.count; i++) { - let u = uvAttribute.getX(i); - let v = uvAttribute.getY(i); + for (let i = 0; i < uvAttribute.count; i++) { + let u = uvAttribute.getX(i); + let v = uvAttribute.getY(i); - u = (u * 22) / 240 + number / 10; + u = (u * 22) / 240 + number / 10; - uvAttribute.setXY(i, u, v); + uvAttribute.setXY(i, u, v); + } + return geometry; + }; + + if (subscene === "level_selection") { + set({ vertPosY: 0, horizPosX: -0.6 }); + if (fstNumberRef.current && sndNumberRef.current) { + fstNumberRef.current.geometry = generateGeom(parseInt(activeLevel[0])); + sndNumberRef.current.geometry = generateGeom(parseInt(activeLevel[1])); + } + } else if ( + subscene === "site" && + prevData?.subscene === "level_selection" + ) { + set({ vertPosY: -2.5, horizPosX: -4 }); } - return geometry; - }, []); + if (selectedLevel !== prevData?.selectedLevel) { + if (fstNumberRef.current && sndNumberRef.current) { + fstNumberRef.current.geometry = generateGeom( + parseInt(selectedLevel[0]) + ); + sndNumberRef.current.geometry = generateGeom( + parseInt(selectedLevel[1]) + ); + + if ( + prevData?.selectedLevel && + upArrowRef.current && + downArrowRef.current + ) { + if (selectedLevel > prevData?.selectedLevel) { + upArrowRef.current.material.map = upArrowActiveTex; + upArrowRef.current.material.needsUpdate = true; + setTimeout(() => { + upArrowRef.current!.material.map = upArrowTex; + upArrowRef.current!.material.needsUpdate = true; + }, 100); + } else if (selectedLevel < prevData?.selectedLevel) { + downArrowRef.current.material.map = downArrowActiveTex; + downArrowRef.current.material.needsUpdate = true; + setTimeout(() => { + downArrowRef.current!.material.map = downArrowTex; + downArrowRef.current!.material.needsUpdate = true; + }, 100); + } + } + } + } + }, [ + activeLevel, + downArrowActiveTex, + prevData?.selectedLevel, + prevData?.subscene, + selectedLevel, + set, + subscene, + upArrowActiveTex, + upArrowTex, + ]); return ( - + { scale={[0.3, 0.4, 0]} position={[1.23, 0, 0]} renderOrder={5} - geometry={generateGeom(parseInt(selectedLevel[1]))} + ref={sndNumberRef} > { scale={[0.3, 0.15, 0]} position={[1.1, -0.35, 0]} renderOrder={4} + ref={downArrowRef} > - + @@ -128,7 +200,7 @@ const LevelSelection = () => { scale={[3, 0.3, 0]} position={[-0.6, 0, 0]} renderOrder={4} - position-x={horizontalHudPosX} + position-x={pos.horizPosX} > { rotation-y={siteState.siteRotY} position-y={siteState.sitePosY} > - - {/**/} + + { const activeNodeId = useStore((state) => state.activeNode.id); - const gameProgress = useStore((state) => state.gameProgress); - const currentSite = useStore((state) => state.activeSite); const activeLevel = useStore((state) => state.activeLevel); const prevData = usePrevious({ activeLevel }); @@ -24,7 +22,6 @@ const ActiveLevelNodes = memo((props: ActiveLevelNodesProps) => { ); useEffect(() => { - const siteData = currentSite === "a" ? site_a : site_b; if ( prevData?.activeLevel !== activeLevel && prevData?.activeLevel !== undefined @@ -32,36 +29,38 @@ const ActiveLevelNodes = memo((props: ActiveLevelNodesProps) => { const prevLevel = parseInt(prevData?.activeLevel); const newLevel = parseInt(activeLevel); if (prevLevel - 1 === newLevel || prevLevel + 1 === newLevel) { - setVisibleNodes(siteData[activeLevel as keyof typeof siteData]); + setVisibleNodes( + props.visibleNodes[activeLevel as keyof typeof props.visibleNodes] + ); } else { setTimeout(() => { - setVisibleNodes(siteData[activeLevel as keyof typeof siteData]); + setVisibleNodes( + props.visibleNodes[activeLevel as keyof typeof props.visibleNodes] + ); }, 1500); } } - }, [activeLevel, currentSite, gameProgress, prevData?.activeLevel]); + }, [activeLevel, prevData?.activeLevel, props, props.visibleNodes]); return ( <> {Object.values(visibleNodes).map((node: NodeDataType) => { - if (isNodeVisible(node, gameProgress)) { - return ( - - ); - } + return ( + + ); })} ); diff --git a/src/components/MainScene/SyncedComponents/Site/InactiveLevelNode.tsx b/src/components/MainScene/SyncedComponents/Site/InactiveLevelNode.tsx new file mode 100644 index 0000000..324e177 --- /dev/null +++ b/src/components/MainScene/SyncedComponents/Site/InactiveLevelNode.tsx @@ -0,0 +1,84 @@ +import React, { memo, useMemo } from "react"; +import { useLoader } from "react-three-fiber"; +import { a } from "@react-spring/three"; +import * as THREE from "three"; +import Cou from "../../../../static/sprite/Cou.png"; +import Dc from "../../../../static/sprite/Dc.png"; +import SSkn from "../../../../static/sprite/SSkn.png"; +import Tda from "../../../../static/sprite/Tda.png"; +import Dia from "../../../../static/sprite/Dia.png"; +import Lda from "../../../../static/sprite/Lda.png"; +import MULTI from "../../../../static/sprite/MULTI.png"; +import level_y_values from "../../../../resources/level_y_values.json"; + +type NodeContructorProps = { + nodeName: string; + position: number[]; + rotation: number[]; + level: string; +}; + +const InactiveLevelNode = memo((props: NodeContructorProps) => { + const tex = useMemo(() => { + if (props.nodeName.includes("S")) { + return SSkn; + } else if ( + props.nodeName.startsWith("P") || + props.nodeName.startsWith("G") || + props.nodeName.includes("?") + ) { + return MULTI; + } else if (props.nodeName.includes("Dc")) { + return Dc; + } else { + switch (props.nodeName.substr(0, 3)) { + case "Tda": + return Tda; + case "Cou": + return Cou; + case "Dia": + return Dia; + case "Lda": + return Lda; + case "Ere": + case "Ekm": + case "Eda": + case "TaK": + case "Env": + return MULTI; + } + } + }, [props.nodeName]); + + const nonActiveTexture = useLoader(THREE.TextureLoader, tex!); + + return ( + + + + + + + ); +}); + +export default InactiveLevelNode; diff --git a/src/components/MainScene/SyncedComponents/Site/InactiveLevelNodes.tsx b/src/components/MainScene/SyncedComponents/Site/InactiveLevelNodes.tsx index ce96e31..1cebe62 100644 --- a/src/components/MainScene/SyncedComponents/Site/InactiveLevelNodes.tsx +++ b/src/components/MainScene/SyncedComponents/Site/InactiveLevelNodes.tsx @@ -1,59 +1,60 @@ -import React, { useMemo, memo } from "react"; -import Node from "./Node"; +import React, { memo, useEffect, useState } from "react"; import node_positions from "../../../../resources/node_positions.json"; import { useStore } from "../../../../store"; -import { isNodeVisible } from "../../../../core/nodeSelector"; -import site_a from "../../../../resources/site_a.json"; -import site_b from "../../../../resources/site_b.json"; +import { SiteType } from "../Site"; +import InactiveLevelNode from "./InactiveLevelNode"; +import usePrevious from "../../../../hooks/usePrevious"; +import { generateInactiveNodes } from "../../../../core/utils/nodeUtils"; -const InactiveLevelNodes = memo(() => { - const gameProgress = useStore((state) => state.gameProgress); - - const currentSite = useStore((state) => state.activeSite); - - const siteData = useMemo(() => (currentSite === "a" ? site_a : site_b), [ - currentSite, - ]); +type ActiveLevelNodesProps = { + visibleNodes: SiteType; +}; +const InactiveLevelNodes = memo((props: ActiveLevelNodesProps) => { const activeLevel = useStore((state) => state.activeLevel); + const prevData = usePrevious({ activeLevel }); - const visibleNodes = useMemo(() => { - const obj = {}; - const activeLevelNr = parseInt(activeLevel); - const visibleLevels = [ - (activeLevelNr - 2).toString().padStart(2, "0"), - (activeLevelNr - 1).toString().padStart(2, "0"), - (activeLevelNr + 1).toString().padStart(2, "0"), - (activeLevelNr + 2).toString().padStart(2, "0"), - ]; + const [visibleNodes, setVisibleNodes] = useState<{}>( + generateInactiveNodes(props.visibleNodes, activeLevel) + ); - visibleLevels.forEach((level) => { - Object.assign(obj, siteData[level as keyof typeof siteData]); - }); - - return obj; - }, [activeLevel, siteData]); + useEffect(() => { + if ( + prevData?.activeLevel !== activeLevel && + prevData?.activeLevel !== undefined + ) { + const prevLevel = parseInt(prevData?.activeLevel); + const newLevel = parseInt(activeLevel); + if (prevLevel - 1 === newLevel || prevLevel + 1 === newLevel) { + setVisibleNodes(generateInactiveNodes(props.visibleNodes, activeLevel)); + } else { + setTimeout(() => { + setVisibleNodes( + generateInactiveNodes(props.visibleNodes, activeLevel) + ); + }, 1500); + } + } + }, [activeLevel, prevData?.activeLevel, props.visibleNodes]); return ( <> {Object.entries(visibleNodes).map((node: [string, any]) => { - if (isNodeVisible(node[1], gameProgress)) { - return ( - - ); - } + return ( + + ); })} ); diff --git a/src/components/MainScene/SyncedComponents/Site/Node.tsx b/src/components/MainScene/SyncedComponents/Site/Node.tsx index 352119b..fb6888f 100644 --- a/src/components/MainScene/SyncedComponents/Site/Node.tsx +++ b/src/components/MainScene/SyncedComponents/Site/Node.tsx @@ -23,7 +23,7 @@ type NodeContructorProps = { nodeName: string; position: number[]; rotation: number[]; - active?: boolean; + active: boolean; level: string; }; diff --git a/src/core/StateManagers/MainSceneManagers/LevelSelectionManager.tsx b/src/core/StateManagers/MainSceneManagers/LevelSelectionManager.tsx index 86f3208..c47ba57 100644 --- a/src/core/StateManagers/MainSceneManagers/LevelSelectionManager.tsx +++ b/src/core/StateManagers/MainSceneManagers/LevelSelectionManager.tsx @@ -6,8 +6,17 @@ const LevelSelectionManager = (props: StateManagerProps) => { const setSelectedLevel = useStore((state) => state.setSelectedLevel); const dispatchObject = useCallback( - (eventState: { event: string; selectedLevelIdx: number }) => { + (eventState: { + event: string; + selectedLevelIdx: number; + level: number; + }) => { switch (eventState.event) { + case "toggle_level_selection": + return { + action: setSelectedLevel, + value: eventState.level, + }; case "level_selection_up": case "level_selection_down": return { diff --git a/src/core/mainSceneEventHandler.ts b/src/core/mainSceneEventHandler.ts index 6a46271..a1aee51 100644 --- a/src/core/mainSceneEventHandler.ts +++ b/src/core/mainSceneEventHandler.ts @@ -90,7 +90,7 @@ const handleMainSceneEvent = (gameContext: any) => { } break; case "L2": - return { event: "toggle_level_selection" }; + return { event: "toggle_level_selection", level: level }; case "TRIANGLE": return { event: "pause_game" }; case "SPACE": diff --git a/src/core/utils/filterInvisibleNodes.ts b/src/core/utils/filterInvisibleNodes.ts index 4aacb7a..78eb2b3 100644 --- a/src/core/utils/filterInvisibleNodes.ts +++ b/src/core/utils/filterInvisibleNodes.ts @@ -1,7 +1,4 @@ -import { - NodeDataType, - SiteType, -} from "../../components/MainScene/SyncedComponents/Site"; +import { SiteType } from "../../components/MainScene/SyncedComponents/Site"; import game_progress from "../../resources/initial_progress.json"; import { isNodeVisible } from "../nodeSelector"; @@ -9,10 +6,13 @@ const filterInvisibleNodes = ( siteData: SiteType, gameProgress: typeof game_progress ) => { - const visibleNodes: NodeDataType[] = []; - Object.values(siteData).forEach((level) => { - Object.values(level).forEach((node) => { - if (isNodeVisible(node, gameProgress)) visibleNodes.push(node); + const visibleNodes: SiteType = {}; + Object.entries(siteData).forEach((level) => { + visibleNodes[level[0]] = {}; + Object.entries(level[1]).forEach((node) => { + if (isNodeVisible(node[1], gameProgress)) { + visibleNodes[level[0]][node[0]] = node[1]; + } }); }); diff --git a/src/core/utils/nodeUtils.ts b/src/core/utils/nodeUtils.ts new file mode 100644 index 0000000..c37f97e --- /dev/null +++ b/src/core/utils/nodeUtils.ts @@ -0,0 +1,21 @@ +import { SiteType } from "../../components/MainScene/SyncedComponents/Site"; + +export const generateInactiveNodes = ( + visibleNodes: SiteType, + activeLevel: string +) => { + const obj = {}; + const activeLevelNr = parseInt(activeLevel); + const visibleLevels = [ + (activeLevelNr - 2).toString().padStart(2, "0"), + (activeLevelNr - 1).toString().padStart(2, "0"), + (activeLevelNr + 1).toString().padStart(2, "0"), + (activeLevelNr + 2).toString().padStart(2, "0"), + ]; + + visibleLevels.forEach((level) => { + Object.assign(obj, visibleNodes[level as keyof typeof visibleNodes]); + }); + + return obj; +};