diff --git a/src/components/MainScene/SyncedComponentLoader.tsx b/src/components/MainScene/SyncedComponentLoader.tsx index 30ab4f4..2e89271 100644 --- a/src/components/MainScene/SyncedComponentLoader.tsx +++ b/src/components/MainScene/SyncedComponentLoader.tsx @@ -9,6 +9,7 @@ import Starfield from "./SyncedComponents/Starfield"; import Site from "./SyncedComponents/Site"; import MiddleRing from "./SyncedComponents/MiddleRing"; import { a } from "@react-spring/three"; +import NodeExplosion from "./SyncedComponents/Site/NodeExplosion"; type SyncedComponentLoaderProps = { paused: boolean; diff --git a/src/components/MainScene/SyncedComponents/Site/Node.tsx b/src/components/MainScene/SyncedComponents/Site/Node.tsx index 9dfe187..4be2318 100644 --- a/src/components/MainScene/SyncedComponents/Site/Node.tsx +++ b/src/components/MainScene/SyncedComponents/Site/Node.tsx @@ -4,18 +4,25 @@ import { a, useSpring } from "@react-spring/three"; import * as THREE from "three"; import Cou from "../../../../static/sprite/Cou.png"; import CouActive from "../../../../static/sprite/Cou_active.png"; +import CouGold from "../../../../static/sprite/Cou_gold.png"; import Dc from "../../../../static/sprite/Dc.png"; import DcActive from "../../../../static/sprite/Dc_active.png"; +import DcGold from "../../../../static/sprite/Dc_gold.png"; import SSkn from "../../../../static/sprite/SSkn.png"; import SSKnActive from "../../../../static/sprite/SSkn_active.png"; +import SSKnGold from "../../../../static/sprite/SSkn_gold.png"; import Tda from "../../../../static/sprite/Tda.png"; import TdaActive from "../../../../static/sprite/Tda_active.png"; +import TdaGold from "../../../../static/sprite/Tda_gold.png"; import Dia from "../../../../static/sprite/Dia.png"; import DiaActive from "../../../../static/sprite/Dia_active.png"; +import DiaGold from "../../../../static/sprite/Dia_gold.png"; import Lda from "../../../../static/sprite/Lda.png"; import LdaActive from "../../../../static/sprite/Lda_active.png"; +import LdaGold from "../../../../static/sprite/Lda_gold.png"; import MULTI from "../../../../static/sprite/MULTI.png"; import MULTIActive from "../../../../static/sprite/MULTI_active.png"; +import MULTIGold from "../../../../static/sprite/MULTI_gold.png"; import level_y_values from "../../../../resources/level_y_values.json"; import { useNodeStore } from "../../../../store"; @@ -35,26 +42,26 @@ const Node = (props: NodeContructorProps) => { const spriteToPath = (sprite: string) => { if (sprite.includes("S")) { - return [SSkn, SSKnActive]; + return [SSkn, SSKnActive, SSKnGold]; } else if ( sprite.startsWith("P") || sprite.startsWith("G") || sprite.includes("?") ) { - return [MULTI, MULTIActive]; + return [MULTI, MULTIActive, MULTIGold]; } else if (sprite.includes("Dc")) { - return [Dc, DcActive]; + return [Dc, DcActive, DcGold]; } else { const spriteAssocs = { - Tda: [Tda, TdaActive], - Cou: [Cou, CouActive], - Dia: [Dia, DiaActive], - Lda: [Lda, LdaActive], - Ere: [MULTI, MULTIActive], - Ekm: [MULTI, MULTIActive], - Eda: [MULTI, MULTIActive], - TaK: [MULTI, MULTIActive], - Env: [MULTI, MULTIActive], + Tda: [Tda, TdaActive, TdaGold], + Cou: [Cou, CouActive, CouGold], + Dia: [Dia, DiaActive, DiaGold], + Lda: [Lda, LdaActive, LdaGold], + Ere: [MULTI, MULTIActive, MULTIGold], + Ekm: [MULTI, MULTIActive, MULTIGold], + Eda: [MULTI, MULTIActive, MULTIGold], + TaK: [MULTI, MULTIActive, MULTIGold], + Env: [MULTI, MULTIActive, MULTIGold], }; return spriteAssocs[sprite.substr(0, 3) as keyof typeof spriteAssocs]; @@ -63,18 +70,21 @@ const Node = (props: NodeContructorProps) => { const materialRef = useRef(); - const spriteSheet = spriteToPath(props.sprite); + const sprite = spriteToPath(props.sprite); - const nonActiveTexture = useLoader(THREE.TextureLoader, spriteSheet[0]); - const activeTexture = useLoader(THREE.TextureLoader, spriteSheet[1]); + const nonActiveTexture = useLoader(THREE.TextureLoader, sprite[0]); + const activeTexture = useLoader(THREE.TextureLoader, sprite[1]); + const goldTexture = useLoader(THREE.TextureLoader, sprite[2]); const uniforms = useMemo( () => ({ tex1: { type: "t", value: nonActiveTexture }, tex2: { type: "t", value: activeTexture }, + tex3: { type: "t", value: goldTexture }, + goldTextureBias: { value: 0 }, timeMSeconds: { value: (Date.now() % (Math.PI * 2000)) / 1000.0 }, }), - [nonActiveTexture, activeTexture] + [nonActiveTexture, activeTexture, goldTexture] ); const vertexShader = ` @@ -91,7 +101,9 @@ const Node = (props: NodeContructorProps) => { uniform sampler2D tex1; uniform sampler2D tex2; + uniform sampler2D tex3; uniform float timeMSeconds; + uniform float goldTextureBias; varying vec2 vUv; @@ -100,22 +112,24 @@ const Node = (props: NodeContructorProps) => { void main() { vec4 t1 = texture2D(tex1,vUv); vec4 t2 = texture2D(tex2,vUv); + vec4 t3 = texture2D(tex3,vUv); float bias = abs(sin(timeMSeconds / (1.6 / M_PI))); - gl_FragColor = mix(t1, t2, bias); + gl_FragColor = mix(mix(t1, t2, bias), t3, goldTextureBias); } `; - useFrame(() => { - if (materialRef.current) { - materialRef.current.uniforms.timeMSeconds.value = - (Date.now() % (Math.PI * 2000)) / 1000.0; - } - }); - // these pieces of state get updated transiently rather than reactively // to avoid excess unnecessary renders (this is absolutely crucial for performance). + const [ - { activeNodePosX, activeNodePosY, activeNodePosZ, activeNodeRotZ, activeNodeRotY }, + { + activeNodePosX, + activeNodePosY, + activeNodePosZ, + activeNodeRotZ, + activeNodeRotY, + goldTextureBias, + }, set, ] = useSpring(() => ({ activeNodePosX: useNodeStore.getState().activeNodeState.interactedWith @@ -134,6 +148,7 @@ const Node = (props: NodeContructorProps) => { activeNodeRotY: useNodeStore.getState().activeNodeState.interactedWith ? useNodeStore.getState().activeNodeState.rotY : props.rotation[1], + goldTextureBias: useNodeStore.getState().activeNodeState.goldTextureBias, config: { duration: 800 }, })); @@ -154,6 +169,7 @@ const Node = (props: NodeContructorProps) => { activeNodeRotY: useNodeStore.getState().activeNodeState.interactedWith ? state.activeNodeState.rotY : props.rotation[1], + goldTextureBias: useNodeStore.getState().activeNodeState.goldTextureBias, })); }, [ props.level, @@ -165,6 +181,14 @@ const Node = (props: NodeContructorProps) => { props.rotation, ]); + useFrame(() => { + if (materialRef.current) { + materialRef.current.uniforms.timeMSeconds.value = + (Date.now() % (Math.PI * 2000)) / 1000.0; + materialRef.current.uniforms.goldTextureBias.value = goldTextureBias.get(); + } + }); + return ( { + return ; +}; + +export default NodeExplosion; diff --git a/src/components/MainScene/SyncedComponents/Site/NodeExplosion/ExplosionLine.tsx b/src/components/MainScene/SyncedComponents/Site/NodeExplosion/ExplosionLine.tsx new file mode 100644 index 0000000..c22362f --- /dev/null +++ b/src/components/MainScene/SyncedComponents/Site/NodeExplosion/ExplosionLine.tsx @@ -0,0 +1,67 @@ +import React, { useMemo } from "react"; +import * as THREE from "three"; +import { a } from "@react-spring/three"; + +type LineProps = { + rotation: number[]; + color: string; + length: number; +}; + +const ExplosionLine = (props: LineProps) => { + const uniforms = useMemo( + () => ({ + color1: { + value: new THREE.Color("white"), + }, + color2: { + value: new THREE.Color(props.color), + }, + }), + [props.color] + ); + + const vertexShader = ` + varying vec2 vUv; + + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `; + + const fragmentShader = ` + uniform vec3 color1; + uniform vec3 color2; + uniform float alpha; + + varying vec2 vUv; + + void main() { + float alpha = smoothstep(0.0, 1.0, vUv.y); + float colorMix = smoothstep(1.0, 2.0, 1.8); + + gl_FragColor = vec4(mix(color1, color2, colorMix), alpha) * 0.8; + } + `; + + return ( + + + + + ); +}; + +export default ExplosionLine; diff --git a/src/core/StateManagers/MainSceneManagers/NodeManager.tsx b/src/core/StateManagers/MainSceneManagers/NodeManager.tsx index 723672c..85dea84 100644 --- a/src/core/StateManagers/MainSceneManagers/NodeManager.tsx +++ b/src/core/StateManagers/MainSceneManagers/NodeManager.tsx @@ -103,12 +103,16 @@ const NodeManager = (props: StateManagerProps) => { setTimeout(() => { setActiveNodeState(Math.PI, "rotZ"); setActiveNodeState(Math.PI, "rotY"); + setActiveNodeState(Math.PI, "rotX"); + setActiveNodeState(1, "goldTextureBias"); }, 1200); setTimeout(() => { setActiveNodeState(false, "interactedWith"); setActiveNodeState(0, "rotZ"); setActiveNodeState(0, "rotY"); + setActiveNodeState(0, "rotX"); + setActiveNodeState(0, "goldTextureBias"); }, 2500); }, [setActiveNodeState] diff --git a/src/scenes/MainScene.tsx b/src/scenes/MainScene.tsx index 3bfac51..db5d7ec 100644 --- a/src/scenes/MainScene.tsx +++ b/src/scenes/MainScene.tsx @@ -9,6 +9,7 @@ import { useMainSceneStore } from "../store"; import Pause from "../components/MainScene/PauseSubscene/Pause"; import SyncedComponentLoader from "../components/MainScene/SyncedComponentLoader"; import LevelSelection from "../components/MainScene/SyncedComponents/LevelSelection"; +import NodeExplosion from "../components/MainScene/SyncedComponents/Site/NodeExplosion"; const MainScene = () => { const currentSubscene = useMainSceneStore((state) => state.subscene); @@ -36,8 +37,9 @@ const MainScene = () => { + - + {/**/} ); diff --git a/src/store.ts b/src/store.ts index 2d7dcff..6d34430 100644 --- a/src/store.ts +++ b/src/store.ts @@ -65,6 +65,7 @@ type NodeState = { rotY: number; rotZ: number; interactedWith: boolean; + goldTextureBias: number; }; nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number }; gameProgress: typeof game_progress; @@ -289,10 +290,6 @@ export const useHudStore = create((set) => ({ export const useNodeStore = create( combine( { - siteASave: { - activeNodeId: "0422", - nodeMatrixIndices: { matrixIdx: 7, rowIdx: 0, colIdx: 0 }, - }, activeNodeState: { id: "0422", posX: 0, @@ -301,6 +298,7 @@ export const useNodeStore = create( rotY: 0, posY: 0, interactedWith: false, + goldTextureBias: 0, }, nodeMatrixIndices: { matrixIdx: 7, rowIdx: 0, colIdx: 0 }, gameProgress: game_progress,