From 9f283912e953f6515f54dfe76c9344970c479a2b Mon Sep 17 00:00:00 2001 From: ad044 Date: Tue, 26 Jul 2022 17:50:16 +0400 Subject: [PATCH] separated active and inactive nodes --- .../canvas/objects/MainScene/LevelNodes.tsx | 59 +++++++++++++ .../canvas/objects/MainScene/Levels.tsx | 77 ----------------- .../canvas/objects/MainScene/Node.tsx | 28 +++---- .../canvas/objects/MainScene/Rings.tsx | 42 ++++++++++ .../canvas/objects/MainScene/Site.tsx | 13 +-- .../objects/MainScene/StaticLevelNodes.tsx | 52 ++++++++++++ .../canvas/objects/MainScene/StaticNode.tsx | 83 +++++++++++++++++++ src/utils/node.ts | 12 ++- 8 files changed, 265 insertions(+), 101 deletions(-) create mode 100644 src/components/canvas/objects/MainScene/LevelNodes.tsx delete mode 100644 src/components/canvas/objects/MainScene/Levels.tsx create mode 100644 src/components/canvas/objects/MainScene/Rings.tsx create mode 100644 src/components/canvas/objects/MainScene/StaticLevelNodes.tsx create mode 100644 src/components/canvas/objects/MainScene/StaticNode.tsx diff --git a/src/components/canvas/objects/MainScene/LevelNodes.tsx b/src/components/canvas/objects/MainScene/LevelNodes.tsx new file mode 100644 index 0000000..cab451f --- /dev/null +++ b/src/components/canvas/objects/MainScene/LevelNodes.tsx @@ -0,0 +1,59 @@ +import usePrevious from "@/hooks/usePrevious"; +import { useStore } from "@/store"; +import { + FlattenedSiteLayout, + GameSite, + MainSubscene, + NodeID, + Position, +} from "@/types"; +import { getLevelY } from "@/utils/site"; +import React, { memo, useEffect, useState } from "react"; +import Node from "./Node"; + +type LevelNodesProps = { + flattenedLayout: FlattenedSiteLayout; + site: GameSite; +}; + +const LevelNodes = (props: LevelNodesProps) => { + const currentLevel = useStore((state) => state.level); + const paused = useStore((state) => state.mainSubscene === MainSubscene.Pause); + const currentNode = useStore((state) => state.node); + const prevData = usePrevious({ level: currentLevel }); + + const [nodes, setNodes] = useState( + props.flattenedLayout[currentLevel] + ); + const [pos, setPos] = useState([0, getLevelY(currentLevel), 0]); + + useEffect(() => { + if (prevData?.level !== currentLevel && prevData?.level !== undefined) { + if (Math.abs(prevData.level - currentLevel) === 1) { + // if only changed one level + setNodes(props.flattenedLayout[currentLevel]); + setPos([0, getLevelY(currentLevel), 0]); + } else { + // if changed from level selection + setTimeout(() => { + setNodes(props.flattenedLayout[currentLevel]); + setPos([0, getLevelY(currentLevel), 0]); + }, 1500); + } + } + }, [currentLevel, prevData?.level, props.flattenedLayout, props.site]); + + return ( + + {nodes.map((nodeId) => ( + + ))} + + ); +}; + +export default memo(LevelNodes); diff --git a/src/components/canvas/objects/MainScene/Levels.tsx b/src/components/canvas/objects/MainScene/Levels.tsx deleted file mode 100644 index 4cb9377..0000000 --- a/src/components/canvas/objects/MainScene/Levels.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { createRef, memo, useEffect, useMemo } from "react"; -import PurpleRing from "./PurpleRing"; -import GrayRing from "./GrayRing"; -import CyanCrystal from "./CyanCrystal"; -import { useStore } from "@/store"; -import { getLevelLimit, getLevelY } from "@/utils/site"; -import { FlattenedSiteLayout, MainSubscene } from "@/types"; -import Node from "./Node"; - -type LevelsProps = { - flattenedLayout: FlattenedSiteLayout; - activateAllLevels: boolean; -}; - -const Levels = (props: LevelsProps) => { - const level = useStore((state) => state.level); - const site = useStore((state) => state.site); - const node = useStore((state) => state.node); - const isPaused = useStore( - (state) => state.mainSubscene === MainSubscene.Pause - ); - - const refs = useMemo( - () => - Array.from({ length: getLevelLimit(site) + 1 }, () => - createRef() - ), - [site] - ); - - useEffect(() => { - if (props.activateAllLevels) { - refs.forEach((ref) => { - if (ref.current) { - ref.current.visible = true; - } - }); - } - }, [props.activateAllLevels, refs]); - - useEffect(() => { - if (!props.activateAllLevels) { - const start = Math.max(0, level - 2); - const end = Math.min(getLevelLimit(site), level + 2); - - refs.forEach((ref, idx) => { - if (ref.current && (idx <= start || idx >= end)) { - ref.current.visible = false; - } - }); - } - }, [site, level, props.activateAllLevels, refs]); - - return ( - <> - {refs.map( - (ref, level) => - level > 0 && ( - - {props.flattenedLayout[level].map((nodeId) => ( - - ))} - - - - - ) - )} - - ); -}; - -export default memo(Levels); diff --git a/src/components/canvas/objects/MainScene/Node.tsx b/src/components/canvas/objects/MainScene/Node.tsx index 67f7433..f92918e 100644 --- a/src/components/canvas/objects/MainScene/Node.tsx +++ b/src/components/canvas/objects/MainScene/Node.tsx @@ -11,8 +11,13 @@ import { a, useSpring } from "@react-spring/three"; import { useStore } from "@/store"; import { LainAnimation, NodeID, Position, Rotation, Scale } from "@/types"; import useNodeTexture from "@/hooks/useNodeTexture"; -import { getNode, isNodeViewed, translatePositionByAngle } from "@/utils/node"; -import nodePositionsJson from "@/json/node_positions.json"; +import { + getNode, + getNodeWorldPosition, + getNodeWorldRotation, + isNodeViewed, + translatePositionByAngle, +} from "@/utils/node"; import { DoubleSide, UniformsLib, UniformsUtils } from "three"; import vertex from "@/shaders/node.vert"; import fragment from "@/shaders/node.frag"; @@ -20,6 +25,7 @@ import NodeExplosion from "./NodeExplosion"; import NodeRip from "./NodeRip"; import sleep from "@/utils/sleep"; import { getRotationForSegment } from "@/utils/site"; +import StaticNode from "./StaticNode"; type NodeProps = { id: NodeID; @@ -40,8 +46,8 @@ const Node = (props: NodeProps) => { [gameProgress, props.id] ); - const worldPosition = nodePositionsJson[position].position as Position; - const rotation = nodePositionsJson[position].rotation as Rotation; + const worldPosition = getNodeWorldPosition(position); + const rotation = getNodeWorldRotation(position); const { activeTexture, viewedTexture, normalTexture } = useNodeTexture(type); @@ -294,19 +300,7 @@ const Node = (props: NodeProps) => { ) : ( - - - - + ); }; diff --git a/src/components/canvas/objects/MainScene/Rings.tsx b/src/components/canvas/objects/MainScene/Rings.tsx new file mode 100644 index 0000000..65ea0e1 --- /dev/null +++ b/src/components/canvas/objects/MainScene/Rings.tsx @@ -0,0 +1,42 @@ +import React, { memo, useMemo } from "react"; +import PurpleRing from "./PurpleRing"; +import GrayRing from "./GrayRing"; +import CyanCrystal from "./CyanCrystal"; +import { useStore } from "@/store"; +import { getLevelLimit, getLevelY } from "@/utils/site"; +import range from "@/utils/range"; +import { GameSite } from "@/types"; + +type RingsProps = { + activateAllRings: boolean; + site: GameSite; +}; + +const Rings = (props: RingsProps) => { + const level = useStore((state) => state.level); + + const visibleLevels: number[] = useMemo(() => { + if (props.activateAllRings) { + return range(1, getLevelLimit(props.site) + 1); + } else { + const start = Math.max(0, level - 2); + const end = Math.min(getLevelLimit(props.site) + 1, level + 2); + + return range(start, end); + } + }, [level, props.activateAllRings, props.site]); + + return ( + <> + {visibleLevels.map((level: number) => ( + + + + + + ))} + + ); +}; + +export default memo(Rings); diff --git a/src/components/canvas/objects/MainScene/Site.tsx b/src/components/canvas/objects/MainScene/Site.tsx index 2aefa5b..6df729c 100644 --- a/src/components/canvas/objects/MainScene/Site.tsx +++ b/src/components/canvas/objects/MainScene/Site.tsx @@ -1,10 +1,12 @@ import React, { useEffect, useMemo } from "react"; import { a, useSpring } from "@react-spring/three"; import { useStore } from "@/store"; -import Levels from "./Levels"; import { FlattenedSiteLayout, MainSubscene, NodeID } from "@/types"; import { getLevelY } from "@/utils/site"; import { getRotationForSegment } from "@/utils/site"; +import Rings from "./Rings"; +import StaticLevelNodes from "./StaticLevelNodes"; +import LevelNodes from "./LevelNodes"; type SiteProps = { introFinished: boolean; @@ -97,7 +99,7 @@ const Site = (props: SiteProps) => { ); const siteLayout = useStore((state) => state.siteLayouts[state.site]); - + const site = useStore((state) => state.site); const layout: FlattenedSiteLayout = useMemo(() => { return siteLayout.map((level) => level.flat().filter((e): e is NodeID => e !== null) @@ -108,10 +110,9 @@ const Site = (props: SiteProps) => { - + + + diff --git a/src/components/canvas/objects/MainScene/StaticLevelNodes.tsx b/src/components/canvas/objects/MainScene/StaticLevelNodes.tsx new file mode 100644 index 0000000..fdd32b7 --- /dev/null +++ b/src/components/canvas/objects/MainScene/StaticLevelNodes.tsx @@ -0,0 +1,52 @@ +import usePrevious from "@/hooks/usePrevious"; +import { useStore } from "@/store"; +import { FlattenedSiteLayout, GameSite } from "@/types"; +import range from "@/utils/range"; +import { getLevelLimit, getLevelY } from "@/utils/site"; +import React, { memo, useEffect, useState } from "react"; +import StaticNode from "./StaticNode"; + +type StaticLevelNodesProps = { + flattenedLayout: FlattenedSiteLayout; + site: GameSite; +}; + +const StaticLevelNodes = (props: StaticLevelNodesProps) => { + const currentLevel = useStore((state) => state.level); + const prevData = usePrevious({ level: currentLevel }); + + const [visibleLevels, setVisibleLevels] = useState( + range( + Math.max(currentLevel - 3, 0), + Math.min(currentLevel + 3, getLevelLimit(props.site)) + ) + ); + + useEffect(() => { + if (prevData?.level !== currentLevel && prevData?.level !== undefined) { + const start = Math.max(currentLevel - 3, 1); + const end = Math.min(currentLevel + 3, getLevelLimit(props.site)); + if (Math.abs(prevData.level - currentLevel) === 1) { + // if only changed one level + setVisibleLevels(range(start, end)); + } else { + // if changed from level selection + setTimeout(() => setVisibleLevels(range(start, end)), 1500); + } + } + }, [currentLevel, prevData?.level, props.site]); + + return ( + <> + {visibleLevels.map((level) => ( + + {props.flattenedLayout[level].map((nodeId) => ( + + ))} + + ))} + + ); +}; + +export default memo(StaticLevelNodes); diff --git a/src/components/canvas/objects/MainScene/StaticNode.tsx b/src/components/canvas/objects/MainScene/StaticNode.tsx new file mode 100644 index 0000000..e141a6f --- /dev/null +++ b/src/components/canvas/objects/MainScene/StaticNode.tsx @@ -0,0 +1,83 @@ +import useNodeTexture from "@/hooks/useNodeTexture"; +import { useStore } from "@/store"; +import { LainAnimation, NodeID } from "@/types"; +import { + getNode, + getNodeWorldPosition, + getNodeWorldRotation, + isNodeViewed, +} from "@/utils/node"; +import { memo, useEffect, useMemo, useRef } from "react"; +import { DoubleSide } from "three"; + +type StaticNodeProps = { + id: NodeID; +}; + +const StaticNode = (props: StaticNodeProps) => { + const currentNode = useStore((state) => state.node); + const node = useMemo(() => getNode(props.id), [props.id]); + const ref = useRef(null); + + useEffect( + () => + useStore.subscribe( + (state) => state.lainAnimation, + (lainAnimation) => { + switch (lainAnimation) { + case LainAnimation.ThrowNode: + case LainAnimation.RipNode: + case LainAnimation.TouchNodeAndGetScared: + case LainAnimation.KnockAndFall: + case LainAnimation.Knock: + if ( + ref.current && + props.id === currentNode?.id && + ref.current.visible + ) { + ref.current.visible = false; + } + break; + default: + if (ref.current && !ref.current.visible) { + ref.current.visible = true; + } + break; + } + } + ), + [currentNode?.id, props.id] + ); + + const { position, type } = node; + + const worldPosition = getNodeWorldPosition(position); + const rotation = getNodeWorldRotation(position); + const gameProgress = useStore((state) => state.gameProgress); + + const { viewedTexture, normalTexture } = useNodeTexture(type); + + const isViewed = useMemo( + () => isNodeViewed(props.id, gameProgress), + [gameProgress, props.id] + ); + + return ( + + + + + ); +}; + +export default memo(StaticNode); diff --git a/src/utils/node.ts b/src/utils/node.ts index 784ea6c..9eff760 100644 --- a/src/utils/node.ts +++ b/src/utils/node.ts @@ -11,10 +11,12 @@ import { NodeRow, NodeID, Position, + Rotation, } from "@/types"; import { State } from "@/types"; -import nodeHudsJson from "@/json/node_huds.json"; import { getLayout, getLevelLimit } from "./site"; +import nodeHudsJson from "@/json/node_huds.json"; +import nodePositionsJson from "@/json/node_positions.json"; export const getNode = (id: NodeID): NodeData => { return nodesJson[id] as NodeData; @@ -411,3 +413,11 @@ export const translatePositionByAngle = ( export const isAudioOnly = (media: string) => { return media.includes("XA"); }; + +export const getNodeWorldPosition = (position: number) => { + return nodePositionsJson[position].position as Position; +}; + +export const getNodeWorldRotation = (position: number) => { + return nodePositionsJson[position].rotation as Rotation; +};