added prompt, and word selection node not found handling

This commit is contained in:
ad044 2021-02-07 19:12:19 +04:00
parent bc018e964d
commit 8f303e455a
19 changed files with 658 additions and 339 deletions

View file

@ -14,92 +14,93 @@ type BootLoadDataProps = {
}; };
const BootLoadData = (props: BootLoadDataProps) => { const BootLoadData = (props: BootLoadDataProps) => {
const loadDataUnderlineTex = useLoader( // const loadDataUnderlineTex = useLoader(
THREE.TextureLoader, // THREE.TextureLoader,
loadDataUnderline // loadDataUnderline
); // );
const loadDataQuestionContainerTex = useLoader( // const loadDataQuestionContainerTex = useLoader(
THREE.TextureLoader, // THREE.TextureLoader,
loadDataQuestionContainer // loadDataQuestionContainer
); // );
const loadDataAnswerContainerTex = useLoader( // const loadDataAnswerContainerTex = useLoader(
THREE.TextureLoader, // THREE.TextureLoader,
loadDataAnswerContainer // loadDataAnswerContainer
); // );
const areYouSureTex = useLoader(THREE.TextureLoader, areYouSure); // const areYouSureTex = useLoader(THREE.TextureLoader, areYouSure);
const yesTex = useLoader(THREE.TextureLoader, yes); // const yesTex = useLoader(THREE.TextureLoader, yes);
const noTex = useLoader(THREE.TextureLoader, no); // const noTex = useLoader(THREE.TextureLoader, no);
//
return ( // return (
<> // <>
{props.visible ? ( // {props.visible ? (
<> // <>
<sprite scale={[4.1, 0.3, 0]} renderOrder={2} position={[0, 0.2, 0]}> // <sprite scale={[4.1, 0.3, 0]} renderOrder={2} position={[0, 0.2, 0]}>
<spriteMaterial // <spriteMaterial
map={loadDataQuestionContainerTex} // map={loadDataQuestionContainerTex}
attach="material" // attach="material"
transparent={true} // transparent={true}
opacity={0.6} // opacity={0.6}
/> // />
</sprite> // </sprite>
//
<sprite scale={[2, 0.24, 0]} renderOrder={3} position={[0, 0.19, 0]}> // <sprite scale={[2, 0.24, 0]} renderOrder={3} position={[0, 0.19, 0]}>
<spriteMaterial // <spriteMaterial
map={areYouSureTex} // map={areYouSureTex}
attach="material" // attach="material"
transparent={true} // transparent={true}
/> // />
</sprite> // </sprite>
//
<sprite // <sprite
scale={[0.5, 0.19, 0]} // scale={[0.5, 0.19, 0]}
renderOrder={3} // renderOrder={3}
position={[-1.2, -0.2, 0]} // position={[-1.2, -0.2, 0]}
> // >
<spriteMaterial map={yesTex} attach="material" transparent={true} /> // <spriteMaterial map={yesTex} attach="material" transparent={true} />
</sprite> // </sprite>
//
<sprite // <sprite
scale={[0.7, 0.3, 0]} // scale={[0.7, 0.3, 0]}
renderOrder={2} // renderOrder={2}
position={ // position={
props.activeBootElement === "load_data_yes" // props.activeBootElement === "load_data_yes"
? [-1.2, -0.2, 0] // ? [-1.2, -0.2, 0]
: [1.2, -0.2, 0] // : [1.2, -0.2, 0]
} // }
> // >
<spriteMaterial // <spriteMaterial
map={loadDataAnswerContainerTex} // map={loadDataAnswerContainerTex}
attach="material" // attach="material"
transparent={true} // transparent={true}
/> // />
</sprite> // </sprite>
//
<sprite // <sprite
scale={[0.4, 0.19, 0]} // scale={[0.4, 0.19, 0]}
renderOrder={3} // renderOrder={3}
position={[1.2, -0.2, 0]} // position={[1.2, -0.2, 0]}
> // >
<spriteMaterial map={noTex} attach="material" transparent={true} /> // <spriteMaterial map={noTex} attach="material" transparent={true} />
</sprite> // </sprite>
//
<sprite // <sprite
scale={[3.5, 0.01, 0]} // scale={[3.5, 0.01, 0]}
position={[-0.5, -1.15, 0]} // position={[-0.5, -1.15, 0]}
renderOrder={2} // renderOrder={2}
> // >
<spriteMaterial // <spriteMaterial
map={loadDataUnderlineTex} // map={loadDataUnderlineTex}
attach="material" // attach="material"
transparent={true} // transparent={true}
/> // />
</sprite> // </sprite>
</> // </>
) : ( // ) : (
<></> // <></>
)} // )}
</> // </>
); // );
return <></>;
}; };
export default BootLoadData; export default BootLoadData;

View file

@ -22,6 +22,7 @@ import handleMainSceneEvent from "../core/scene-keypress-handlers/handleMainKeyP
import gameLoader from "../core/setters/gameLoader"; import gameLoader from "../core/setters/gameLoader";
import gameSaver from "../core/setters/gameSaver"; import gameSaver from "../core/setters/gameSaver";
import progressManager from "../core/setters/progressManager"; import progressManager from "../core/setters/progressManager";
import promptManager from "../core/setters/promptManager";
const KeyPressHandler = () => { const KeyPressHandler = () => {
const mediaSceneSetters = useMemo( const mediaSceneSetters = useMemo(
@ -32,6 +33,7 @@ const KeyPressHandler = () => {
levelManager, levelManager,
siteManager, siteManager,
progressManager, progressManager,
mainSubsceneManager,
], ],
[] []
); );
@ -52,6 +54,7 @@ const KeyPressHandler = () => {
gameLoader, gameLoader,
gameSaver, gameSaver,
progressManager, progressManager,
promptManager,
], ],
[] []
); );
@ -92,6 +95,7 @@ const KeyPressHandler = () => {
}; };
case "gate": case "gate":
case "polytan": case "polytan":
case "about":
return { return {
action: () => useStore.setState({ currentScene: "main" }), action: () => useStore.setState({ currentScene: "main" }),
}; };

View file

@ -130,6 +130,7 @@ const HUD = memo(() => {
if ( if (
!(scene === "main" && prevData?.scene === "main") || !(scene === "main" && prevData?.scene === "main") ||
(subscene === "site" && prevData?.subscene === "pause") || (subscene === "site" && prevData?.subscene === "pause") ||
(subscene === "site" && prevData?.subscene === "not_found") ||
subscene === "pause" subscene === "pause"
) { ) {
// set to final pos instantly // set to final pos instantly

View file

@ -0,0 +1,48 @@
import React from "react";
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";
type NotFoundProps = {
visible: boolean;
};
const NotFound = (props: NotFoundProps) => {
const notFoundTex = useLoader(THREE.TextureLoader, notFound);
const notFoundLofTex = useLoader(THREE.TextureLoader, notFoundLof);
return (
<>
{props.visible && (
<>
<sprite
scale={[1, 0.25, 0]}
renderOrder={106}
position={[-1, -0.05, 0]}
>
<spriteMaterial
attach="material"
map={notFoundLofTex}
depthTest={false}
/>
</sprite>
<sprite
scale={[4.1, 0.6, 0]}
renderOrder={105}
position={[0, -0.15, 0]}
>
<spriteMaterial
attach="material"
map={notFoundTex}
depthTest={false}
/>
</sprite>
</>
)}
</>
);
};
export default NotFound;

View file

@ -0,0 +1,41 @@
import React, { useRef } from "react";
import aboutBg from "../../../static/sprite/about_background.png";
import { useFrame, useLoader } from "react-three-fiber";
import * as THREE from "three";
import { useStore } from "../../../store";
const About = () => {
const setShowingAbout = useStore((state) => state.setShowingAbout);
const aboutBgTex = useLoader(THREE.TextureLoader, aboutBg);
const bgRef = useRef<THREE.Sprite>();
// todo im not sure where the other bg file is located,
// the one here is just the text, in the original game there's another one
useFrame(() => {
if (bgRef.current) {
bgRef.current.position.y += 0.03;
if (Math.round(bgRef.current.position.y) === 14) {
setShowingAbout(false);
}
}
});
return (
<>
<sprite renderOrder={199} scale={[100, 100, 0]}>
<spriteMaterial attach="material" color={0x000000} depthTest={false} />
</sprite>
<sprite
ref={bgRef}
scale={[10.5 / 2.5, 52.8 / 2.5, 0]}
position={[1.1, -13, 0]}
renderOrder={200}
>
<spriteMaterial attach="material" map={aboutBgTex} depthTest={false} />
</sprite>
</>
);
};
export default About;

View file

@ -4,9 +4,12 @@ import PauseSquare from "./PauseSquare";
import StaticBigLetter from "../../TextRenderer/StaticBigLetter"; import StaticBigLetter from "../../TextRenderer/StaticBigLetter";
import { useStore } from "../../../store"; import { useStore } from "../../../store";
import { useLoader } from "react-three-fiber"; import { useLoader } from "react-three-fiber";
import About from "./About";
import Prompt from "../../Prompt";
const Pause = () => { const Pause = () => {
const exit = useStore((state) => state.pauseExitAnimation); const exit = useStore((state) => state.pauseExitAnimation);
const showingAbout = useStore((state) => state.showingAbout);
const [showActiveComponent, setShowActiveComponent] = useState(false); const [showActiveComponent, setShowActiveComponent] = useState(false);
const [animation, setAnimation] = useState(false); const [animation, setAnimation] = useState(false);
const [intro, setIntro] = useState(true); const [intro, setIntro] = useState(true);
@ -274,7 +277,6 @@ const Pause = () => {
active={activeComponent === "load"} active={activeComponent === "load"}
/> />
))} ))}
{"About".split("").map((letter, idx) => ( {"About".split("").map((letter, idx) => (
<StaticBigLetter <StaticBigLetter
color={idx > 0 ? "yellow" : "orange"} color={idx > 0 ? "yellow" : "orange"}
@ -286,7 +288,6 @@ const Pause = () => {
key={idx} key={idx}
/> />
))} ))}
{"Change".split("").map((letter, idx) => ( {"Change".split("").map((letter, idx) => (
<StaticBigLetter <StaticBigLetter
color={idx > 0 ? "yellow" : "orange"} color={idx > 0 ? "yellow" : "orange"}
@ -298,7 +299,6 @@ const Pause = () => {
key={idx} key={idx}
/> />
))} ))}
{"Save".split("").map((letter, idx) => ( {"Save".split("").map((letter, idx) => (
<StaticBigLetter <StaticBigLetter
color={idx > 0 ? "yellow" : "orange"} color={idx > 0 ? "yellow" : "orange"}
@ -310,7 +310,6 @@ const Pause = () => {
key={idx} key={idx}
/> />
))} ))}
{"Exit".split("").map((letter, idx) => ( {"Exit".split("").map((letter, idx) => (
<StaticBigLetter <StaticBigLetter
color={idx > 0 ? "yellow" : "orange"} color={idx > 0 ? "yellow" : "orange"}
@ -322,7 +321,6 @@ const Pause = () => {
active={activeComponent === "exit"} active={activeComponent === "exit"}
/> />
))} ))}
<group visible={!exit}> <group visible={!exit}>
<sprite <sprite
position={[0.5, -0.8, 0]} position={[0.5, -0.8, 0]}
@ -353,6 +351,10 @@ const Pause = () => {
/> />
</mesh> </mesh>
</group> </group>
{showingAbout && <About />}
<group position={[1, 0.6, 0]} scale={[1.2, 1.2, 0]}>
<Prompt />
</group>
</group> </group>
)} )}
</> </>

110
src/components/Prompt.tsx Normal file
View file

@ -0,0 +1,110 @@
import React, { useCallback, useEffect } from "react";
import answerContainer from "../static/sprite/prompt_answer_container.png";
import questionContainer from "../static/sprite/prompt_question_container.png";
import yes from "../static/sprite/prompt_yes.png";
import no from "../static/sprite/prompt_no.png";
import question from "../static/sprite/prompt_question.png";
import { useLoader } from "react-three-fiber";
import * as THREE from "three";
import { useStore } from "../store";
const Prompt = () => {
const promptVisible = useStore((state) => state.promptVisible);
const questionContainerTex = useLoader(
THREE.TextureLoader,
questionContainer
);
const answerContainerTex = useLoader(THREE.TextureLoader, answerContainer);
const questionTex = useLoader(THREE.TextureLoader, question);
const yesTex = useLoader(THREE.TextureLoader, yes);
const noTex = useLoader(THREE.TextureLoader, no);
const activeComponent = useStore(
useCallback(
(state) => state.promptComponentMatrix[state.promptComponentMatrixIdx],
[]
)
);
useEffect(() => {
console.log(promptVisible);
}, [promptVisible]);
return (
<>
{promptVisible && (
<>
<sprite
scale={[4.1, 0.3, 0]}
renderOrder={200}
position={[0, 0.2, 0]}
>
<spriteMaterial
map={questionContainerTex}
attach="material"
transparent={true}
opacity={0.6}
depthTest={false}
/>
</sprite>
<sprite
scale={[2, 0.24, 0]}
renderOrder={200}
position={[0, 0.19, 0]}
>
<spriteMaterial
map={questionTex}
attach="material"
transparent={true}
depthTest={false}
/>
</sprite>
<sprite
scale={[0.5, 0.19, 0]}
renderOrder={200}
position={[-1.2, -0.2, 0]}
>
<spriteMaterial
map={yesTex}
attach="material"
transparent={true}
depthTest={false}
/>
</sprite>
<sprite
scale={[0.7, 0.3, 0]}
renderOrder={199}
position={
activeComponent === "yes" ? [-1.2, -0.2, 0] : [1.2, -0.2, 0]
}
>
<spriteMaterial
map={answerContainerTex}
attach="material"
transparent={true}
depthTest={false}
/>
</sprite>
<sprite
scale={[0.4, 0.19, 0]}
renderOrder={200}
position={[1.2, -0.2, 0]}
>
<spriteMaterial
map={noTex}
attach="material"
transparent={true}
depthTest={false}
/>
</sprite>
</>
)}
</>
);
};
export default Prompt;

View file

@ -100,6 +100,7 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => {
useEffect(() => { useEffect(() => {
if ( if (
subscene === "pause" || subscene === "pause" ||
(subscene === "site" && prevData?.subscene === "not_found") ||
(subscene === "site" && prevData?.subscene === "pause") (subscene === "site" && prevData?.subscene === "pause")
) )
return; return;

View file

@ -3,7 +3,7 @@ import { useStore } from "../../store";
import { a, useTrail } from "@react-spring/three"; import { a, useTrail } from "@react-spring/three";
import BigLetter from "./BigLetter"; import BigLetter from "./BigLetter";
import usePrevious from "../../hooks/usePrevious"; import usePrevious from "../../hooks/usePrevious";
import {getNodeHud} from "../../utils/node-utils"; import { getNodeHud } from "../../utils/node-utils";
const YellowTextRenderer = (props: { visible?: boolean }) => { const YellowTextRenderer = (props: { visible?: boolean }) => {
const activeNode = useStore((state) => state.activeNode); const activeNode = useStore((state) => state.activeNode);
@ -22,6 +22,7 @@ const YellowTextRenderer = (props: { visible?: boolean }) => {
useEffect(() => { useEffect(() => {
const hud = getNodeHud(activeNode.matrixIndices!); const hud = getNodeHud(activeNode.matrixIndices!);
if (subscene === "level_selection") { if (subscene === "level_selection") {
setTimeout(() => { setTimeout(() => {
set({ posX: -0.02, posY: 0.005 }); set({ posX: -0.02, posY: 0.005 });

View file

@ -18,273 +18,308 @@ const handleMainSceneEvent = (mainSceneContext: any) => {
level, level,
keyPress, keyPress,
ssknLvl, ssknLvl,
showingAbout,
promptVisible,
activePromptComponent,
} = mainSceneContext; } = mainSceneContext;
switch (subscene) { if (promptVisible) {
case "site": switch (keyPress) {
switch (keyPress) { case "LEFT":
case "LEFT": return { event: "prompt_left" };
case "RIGHT": { case "RIGHT":
const keyPressToLower = keyPress.toLowerCase(); return { event: "prompt_right" };
case "CIRCLE":
const nodeData = findNode( switch (activePromptComponent) {
activeNode.id, case "no":
keyPressToLower, return { event: "exit_prompt" };
activeNode.matrixIndices!, case "yes":
level,
currentSite,
gameProgress,
true
);
if (!nodeData) return;
if (nodeData.didMove) {
return { return {
event: keyPressToLower === "left" ? `site_left` : "site_right", event: `pause_${activePauseComponent}_select`,
siteRotY: site: currentSite,
keyPressToLower === "left"
? siteRotY + Math.PI / 4
: siteRotY - Math.PI / 4,
node: {
...(nodeData.node !== "unknown"
? getNodeById(nodeData.node, currentSite)
: unknownNodeTemplate),
matrixIndices: nodeData.matrixIndices,
},
}; };
} else {
return {
event: "change_node",
nodeMatrixIndices: nodeData.matrixIndices,
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
}
} }
case "UP": }
case "DOWN": { } else {
const keyPressToLower = keyPress.toLowerCase(); switch (subscene) {
const nodeData = findNode( case "site":
activeNode.id, switch (keyPress) {
keyPressToLower, case "LEFT":
activeNode.matrixIndices!, case "RIGHT": {
level, const keyPressToLower = keyPress.toLowerCase();
currentSite,
gameProgress,
true
);
if (!nodeData) return; const nodeData = findNode(
activeNode.id,
keyPressToLower,
activeNode.matrixIndices!,
level,
currentSite,
gameProgress,
true
);
if (nodeData.didMove) { if (!nodeData) return;
return {
event: keyPressToLower === "up" ? "site_up" : "site_down", if (nodeData.didMove) {
level: (keyPressToLower === "up" ? level + 1 : level - 1) return {
.toString() event: keyPressToLower === "left" ? `site_left` : "site_right",
.padStart(2, "0"), siteRotY:
node: { keyPressToLower === "left"
...(nodeData.node !== "unknown" ? siteRotY + Math.PI / 4
? getNodeById(nodeData.node, currentSite) : siteRotY - Math.PI / 4,
: unknownNodeTemplate), node: {
matrixIndices: nodeData.matrixIndices, ...(nodeData.node !== "unknown"
}, ? getNodeById(nodeData.node, currentSite)
}; : unknownNodeTemplate),
} else { matrixIndices: nodeData.matrixIndices,
return { },
event: "change_node", };
node: { } else {
...getNodeById(nodeData.node, currentSite), return {
matrixIndices: nodeData.matrixIndices, event: "change_node",
}, nodeMatrixIndices: nodeData.matrixIndices,
}; node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
}
} }
} case "UP":
case "CIRCLE": case "DOWN": {
const eventAnimation = const keyPressToLower = keyPress.toLowerCase();
Math.random() < 0.4 ? "rip_node" : "throw_node"; const nodeData = findNode(
activeNode.id,
keyPressToLower,
activeNode.matrixIndices!,
level,
currentSite,
gameProgress,
true
);
const nodeType = activeNode.type; if (!nodeData) return;
if (activeNode.id === "" || !isNodeVisible(activeNode, gameProgress)) if (nodeData.didMove) {
return; return {
event: keyPressToLower === "up" ? "site_up" : "site_down",
level: (keyPressToLower === "up" ? level + 1 : level - 1)
.toString()
.padStart(2, "0"),
node: {
...(nodeData.node !== "unknown"
? getNodeById(nodeData.node, currentSite)
: unknownNodeTemplate),
matrixIndices: nodeData.matrixIndices,
},
};
} else {
return {
event: "change_node",
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
}
}
case "CIRCLE":
const eventAnimation =
Math.random() < 0.4 ? "rip_node" : "throw_node";
if (activeNode.upgrade_requirement > ssknLvl) { const nodeType = activeNode.type;
const rejectAnimations = [
"touch_and_scare",
"knock_and_fall",
"knock",
];
const pickedAnim = if (
rejectAnimations[ activeNode.id === "" ||
Math.floor(Math.random() * rejectAnimations.length) !isNodeVisible(activeNode, gameProgress)
)
return;
if (activeNode.upgrade_requirement > ssknLvl) {
const rejectAnimations = [
"touch_and_scare",
"knock_and_fall",
"knock",
]; ];
return { const pickedAnim =
event: pickedAnim, rejectAnimations[
siteRotY: siteRotY, Math.floor(Math.random() * rejectAnimations.length)
}; ];
}
switch (nodeType) {
case 0:
case 2:
case 4:
case 3:
case 5:
return { return {
event: `${eventAnimation}_media`, event: pickedAnim,
scene: "media",
siteRotY: siteRotY, siteRotY: siteRotY,
level: level.toString().padStart(2, "0"),
}; };
case 6: }
if (activeNode.node_name.substr(0, 3) === "TaK") {
return { switch (nodeType) {
event: `${eventAnimation}_tak`, case 0:
scene: "tak", case 2:
siteRotY: siteRotY, case 4:
node: activeNode, case 3:
}; case 5:
} else {
return { return {
event: `${eventAnimation}_media`, event: `${eventAnimation}_media`,
scene: "media", scene: "media",
siteRotY: siteRotY, siteRotY: siteRotY,
level: level.toString().padStart(2, "0"), level: level.toString().padStart(2, "0"),
}; };
} case 6:
case 8: if (activeNode.node_name.substr(0, 3) === "TaK") {
return { return {
event: `${eventAnimation}_gate`, event: `${eventAnimation}_tak`,
scene: "gate", scene: "tak",
siteRotY: siteRotY, siteRotY: siteRotY,
node: activeNode, node: activeNode,
}; };
case 7: } else {
return { return {
event: `${eventAnimation}_sskn`, event: `${eventAnimation}_media`,
scene: "sskn", scene: "media",
siteRotY: siteRotY, siteRotY: siteRotY,
}; level: level.toString().padStart(2, "0"),
case 9: };
const bodyPart = (() => {
switch (parseInt(activeNode.node_name.slice(-1))) {
case 6:
return "head";
case 5:
return "rightArm";
case 4:
return "leftArm";
case 3:
return "rightLeg";
case 2:
return "leftLeg";
case 1:
return "body";
} }
})(); case 8:
return {
event: `${eventAnimation}_gate`,
scene: "gate",
siteRotY: siteRotY,
node: activeNode,
};
case 7:
return {
event: `${eventAnimation}_sskn`,
scene: "sskn",
siteRotY: siteRotY,
};
case 9:
const bodyPart = (() => {
switch (parseInt(activeNode.node_name.slice(-1))) {
case 6:
return "head";
case 5:
return "rightArm";
case 4:
return "leftArm";
case 3:
return "rightLeg";
case 2:
return "leftLeg";
case 1:
return "body";
}
})();
return {
event: `${eventAnimation}_polytan`,
scene: "polytan",
siteRotY: siteRotY,
node: activeNode,
bodyPart: bodyPart,
};
}
break;
case "L2":
return { event: "toggle_level_selection", level: level };
case "TRIANGLE":
return { event: "pause_game" };
case "SPACE":
return { event: "play_with_hair", siteRotY: siteRotY };
}
break;
case "level_selection":
switch (keyPress) {
case "UP":
if (currentSite === "a") {
if (selectedLevel + 1 <= 22)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
} else if (currentSite === "b") {
if (selectedLevel + 1 <= 13)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
}
break;
case "DOWN":
if (selectedLevel - 1 >= 1)
return { return {
event: `${eventAnimation}_polytan`, event: `level_selection_down`,
scene: "polytan", selectedLevelIdx: selectedLevel - 1,
siteRotY: siteRotY,
node: activeNode,
bodyPart: bodyPart,
}; };
} break;
break; case "X":
case "L2":
return { event: "toggle_level_selection", level: level };
case "TRIANGLE":
return { event: "pause_game" };
case "SPACE":
return { event: "play_with_hair", siteRotY: siteRotY };
}
break;
case "level_selection":
switch (keyPress) {
case "UP":
if (currentSite === "a") {
if (selectedLevel + 1 <= 22)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
} else if (currentSite === "b") {
if (selectedLevel + 1 <= 13)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
}
break;
case "DOWN":
if (selectedLevel - 1 >= 1)
return { return {
event: `level_selection_down`, event: "level_selection_back",
selectedLevelIdx: selectedLevel - 1,
}; };
break;
case "X": case "CIRCLE":
if (level === selectedLevel) return;
const direction = selectedLevel > level ? "up" : "down";
const rowIdx = direction === "up" ? 2 : 0;
const nodeData = findNode(
activeNode.id,
direction,
{ ...activeNode.matrixIndices!, rowIdx: rowIdx },
selectedLevel,
currentSite,
gameProgress,
false
);
if (nodeData) {
const event =
selectedLevel < level ? "select_level_down" : "select_level_up";
return {
event: event,
node: {
...(nodeData.node !== "unknown"
? getNodeById(nodeData.node, currentSite)
: unknownNodeTemplate),
matrixIndices: nodeData.matrixIndices,
},
level: selectedLevel.toString().padStart(2, "0"),
};
}
}
break;
case "pause":
if (showingAbout)
return { return {
event: "level_selection_back", event: "exit_about",
}; };
else {
case "CIRCLE": switch (keyPress) {
if (level === selectedLevel) return; case "UP":
if (pauseMatrixIdx - 1 < 0) break;
const direction = selectedLevel > level ? "up" : "down"; return {
event: "pause_up",
const rowIdx = direction === "up" ? 2 : 0; pauseMatrixIdx: pauseMatrixIdx - 1,
const nodeData = findNode( };
activeNode.id, case "DOWN":
direction, if (pauseMatrixIdx + 1 > 4) break;
{ ...activeNode.matrixIndices!, rowIdx: rowIdx }, return {
selectedLevel, event: "pause_down",
currentSite, pauseMatrixIdx: pauseMatrixIdx + 1,
gameProgress, };
false case "CIRCLE":
); if (activePauseComponent === "change") {
return {
if (nodeData) { event: "display_prompt",
const event = };
selectedLevel < level ? "select_level_down" : "select_level_up"; }
return {
event: event,
node: {
...(nodeData.node !== "unknown"
? getNodeById(nodeData.node, currentSite)
: unknownNodeTemplate),
matrixIndices: nodeData.matrixIndices,
},
level: selectedLevel.toString().padStart(2, "0"),
};
} }
} }
break; break;
case "pause": case "not_found":
switch (keyPress) { return { event: "exit_not_found" };
case "UP": }
if (pauseMatrixIdx - 1 < 0) break;
return {
event: "pause_up",
pauseMatrixIdx: pauseMatrixIdx - 1,
};
case "DOWN":
if (pauseMatrixIdx + 1 > 4) break;
return {
event: "pause_down",
pauseMatrixIdx: pauseMatrixIdx + 1,
};
case "CIRCLE":
return {
event: `pause_${activePauseComponent}_select`,
site: currentSite,
};
}
} }
}; };

View file

@ -8,6 +8,7 @@ const handleMediaKeyPress = (mediaSceneContext: any) => {
rightSideComponentIdx, rightSideComponentIdx,
activeNode, activeNode,
activeSite, activeSite,
gameProgress,
} = mediaSceneContext; } = mediaSceneContext;
const calculateNewRightSide = ( const calculateNewRightSide = (
@ -72,14 +73,14 @@ const handleMediaKeyPress = (mediaSceneContext: any) => {
const data = findNodeFromWord( const data = findNodeFromWord(
activeMediaComponent, activeMediaComponent,
activeNode, activeNode,
activeSite activeSite,
gameProgress
); );
if (data) { if (data) {
return { event: `media_${activeMediaComponent}_select`, ...data }; return { event: `media_${activeMediaComponent}_select`, ...data };
} else { } else {
// todo in case node isnt unlocked yet return { event: `word_node_not_found` };
return;
} }
default: default:
if (activeMediaComponent === "play") { if (activeMediaComponent === "play") {

View file

@ -5,9 +5,15 @@ const mainSubsceneManager = (eventState: any) => {
const dispatchAction = (eventState: { event: string }) => { const dispatchAction = (eventState: { event: string }) => {
switch (eventState.event) { switch (eventState.event) {
case "word_node_not_found":
return {
action: () => setMainSubscene("not_found"),
delay: 0,
};
case "level_selection_back": case "level_selection_back":
case "select_level_up": case "select_level_up":
case "select_level_down": case "select_level_down":
case "exit_not_found":
return { return {
action: () => setMainSubscene("site"), action: () => setMainSubscene("site"),
delay: 0, delay: 0,

View file

@ -5,6 +5,7 @@ type PauseManagerProps = { event: string; pauseMatrixIdx: number };
const pauseManager = (eventState: any) => { const pauseManager = (eventState: any) => {
const setComponentMatrixIdx = useStore.getState().setPauseComponentMatrixIdx; const setComponentMatrixIdx = useStore.getState().setPauseComponentMatrixIdx;
const setExitAnimation = useStore.getState().setPauseExitAnimation; const setExitAnimation = useStore.getState().setPauseExitAnimation;
const setShowingAbout = useStore.getState().setShowingAbout;
const dispatchAction = (eventState: PauseManagerProps) => { const dispatchAction = (eventState: PauseManagerProps) => {
switch (eventState.event) { switch (eventState.event) {
@ -17,6 +18,14 @@ const pauseManager = (eventState: any) => {
return { return {
action: () => setExitAnimation(true), action: () => setExitAnimation(true),
}; };
case "pause_about_select":
return {
action: () => setShowingAbout(true),
};
case "exit_about":
return {
action: () => setShowingAbout(false),
};
case "pause_game": case "pause_game":
return { action: () => setExitAnimation(false) }; return { action: () => setExitAnimation(false) };
} }

View file

@ -1,5 +1,4 @@
import { useStore } from "../../../store"; import { useStore } from "../../../store";
import { useCallback } from "react";
import * as THREE from "three"; import * as THREE from "three";
const mediaManager = (eventState: any) => { const mediaManager = (eventState: any) => {

View file

@ -0,0 +1,32 @@
import { useStore } from "../../store";
const promptManager = (eventState: any) => {
const setComponentMatrixIdx = useStore.getState().setPromptComponentMatrixIdx;
const setPromptVisible = useStore.getState().setPromptVisible;
const dispatchAction = (eventState: { event: string; scene: string }) => {
switch (eventState.event) {
case "display_prompt": {
return { action: () => setPromptVisible(true) };
}
case "prompt_right":
return {
action: () => setComponentMatrixIdx(1),
};
case "prompt_left":
return { action: () => setComponentMatrixIdx(0) };
case "pause_change_select":
return { action: () => setPromptVisible(false) };
case "exit_prompt":
return { action: () => setPromptVisible(false) };
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default promptManager;

View file

@ -52,6 +52,7 @@ const sceneManager = (eventState: any) => {
case "media_fstWord_select": case "media_fstWord_select":
case "media_sndWord_select": case "media_sndWord_select":
case "media_thirdWord_select": case "media_thirdWord_select":
case "word_node_not_found":
return { return {
action: () => action: () =>
useStore.setState({ currentScene: "main", intro: false }), useStore.setState({ currentScene: "main", intro: false }),

View file

@ -13,6 +13,7 @@ import Site from "../components/MainScene/Site";
import Lain from "../components/MainScene/Lain"; import Lain from "../components/MainScene/Lain";
import * as THREE from "three"; import * as THREE from "three";
import { useFrame } from "react-three-fiber"; import { useFrame } from "react-three-fiber";
import NotFound from "../components/MainScene/NotFound";
const MainScene = () => { const MainScene = () => {
const intro = useStore((state) => state.intro); const intro = useStore((state) => state.intro);
@ -94,10 +95,13 @@ const MainScene = () => {
<Suspense fallback={null}> <Suspense fallback={null}>
<LevelSelection /> <LevelSelection />
<Pause /> <Pause />
<NotFound visible={subscene === "not_found"} />
<group visible={!paused}> <group visible={!paused}>
<group visible={!wordSelected && (intro ? introFinished : true)}> <group visible={!wordSelected && (intro ? introFinished : true)}>
<HUD /> <group visible={subscene !== "not_found"}>
<YellowTextRenderer /> <HUD />
<YellowTextRenderer />
</group>
<MiddleRing /> <MiddleRing />
<GrayPlanes /> <GrayPlanes />
</group> </group>

View file

@ -44,6 +44,7 @@ type State = {
pauseComponentMatrix: ["load", "about", "change", "save", "exit"]; pauseComponentMatrix: ["load", "about", "change", "save", "exit"];
pauseComponentMatrixIdx: number; pauseComponentMatrixIdx: number;
pauseExitAnimation: boolean; pauseExitAnimation: boolean;
showingAbout: boolean;
// media/media scene // media/media scene
audioAnalyser: undefined | THREE.AudioAnalyser; audioAnalyser: undefined | THREE.AudioAnalyser;
@ -98,6 +99,11 @@ type State = {
// end scene // end scene
endMediaPlayedCount: number; endMediaPlayedCount: number;
// prompt
promptVisible: boolean;
promptComponentMatrix: ["yes", "no"];
promptComponentMatrixIdx: 1 | 0;
// save state // save state
siteSaveState: { siteSaveState: {
a: { a: {
@ -188,6 +194,7 @@ export const useStore = create(
pauseComponentMatrix: ["load", "about", "change", "save", "exit"], pauseComponentMatrix: ["load", "about", "change", "save", "exit"],
pauseComponentMatrixIdx: 2, pauseComponentMatrixIdx: 2,
pauseExitAnimation: false, pauseExitAnimation: false,
showingAbout: false,
// media / media scene // media / media scene
audioAnalyser: undefined, audioAnalyser: undefined,
@ -249,6 +256,11 @@ export const useStore = create(
// end scene // end scene
endMediaPlayedCount: 0, endMediaPlayedCount: 0,
// prompt
promptVisible: false,
promptComponentMatrix: ["yes", "no"],
promptComponentMatrixIdx: 1,
// save states for loading the game/changing sites // save states for loading the game/changing sites
siteSaveState: { siteSaveState: {
a: { a: {
@ -325,6 +337,7 @@ export const useStore = create(
set(() => ({ pauseComponentMatrixIdx: to })), set(() => ({ pauseComponentMatrixIdx: to })),
setPauseExitAnimation: (to: boolean) => setPauseExitAnimation: (to: boolean) =>
set(() => ({ pauseExitAnimation: to })), set(() => ({ pauseExitAnimation: to })),
setShowingAbout: (to: boolean) => set(() => ({ showingAbout: to })),
// media/media scene setters // media/media scene setters
toggleMediaSide: () => toggleMediaSide: () =>
@ -404,6 +417,11 @@ export const useStore = create(
})), })),
resetEndMediaPlayedCount: () => set(() => ({ endMediaPlayedCount: 0 })), resetEndMediaPlayedCount: () => set(() => ({ endMediaPlayedCount: 0 })),
// prompt setters
setPromptVisible: (to: boolean) => set(() => ({ promptVisible: to })),
setPromptComponentMatrixIdx: (to: 1 | 0) =>
set(() => ({ promptComponentMatrixIdx: to })),
// site state setters // site state setters
setSiteSaveState: ( setSiteSaveState: (
site: string, site: string,
@ -467,6 +485,10 @@ export const getMainSceneContext = () => {
activeNode: state.activeNode, activeNode: state.activeNode,
level: parseInt(state.activeLevel), level: parseInt(state.activeLevel),
ssknLvl: state.ssknLvl, ssknLvl: state.ssknLvl,
showingAbout: state.showingAbout,
promptVisible: state.promptVisible,
activePromptComponent:
state.promptComponentMatrix[state.promptComponentMatrixIdx],
}; };
}; };
@ -493,5 +515,6 @@ export const getMediaSceneContext = () => {
wordPosStateIdx: state.mediaWordPosStateIdx, wordPosStateIdx: state.mediaWordPosStateIdx,
activeNode: state.activeNode, activeNode: state.activeNode,
activeSite: state.activeSite, activeSite: state.activeSite,
gameProgress: state.gameProgress,
}; };
}; };

View file

@ -1,15 +1,14 @@
import site_a from "../resources/site_a.json"; import site_a from "../resources/site_a.json";
import site_b from "../resources/site_b.json"; import site_b from "../resources/site_b.json";
import node_matrices from "../resources/node_matrices.json"; import node_matrices from "../resources/node_matrices.json";
import { import { NodeDataType, SiteType } from "../components/MainScene/Site";
NodeDataType, import { isNodeVisible } from "./node-utils";
SiteType,
} from "../components/MainScene/Site";
export const findNodeFromWord = ( export const findNodeFromWord = (
wordLabel: string, wordLabel: string,
activeNode: NodeDataType, activeNode: NodeDataType,
site: "a" | "b" site: "a" | "b",
gameProgress: any
) => { ) => {
const labelToIdx = (() => { const labelToIdx = (() => {
switch (wordLabel) { switch (wordLabel) {
@ -41,7 +40,8 @@ export const findNodeFromWord = (
) + 1 ) + 1
] ?? nodesWithSameWords[0]; ] ?? nodesWithSameWords[0];
//todo check if visible if (!isNodeVisible(chosenNode, gameProgress)) return;
const pos = chosenNode.id.substr(2); const pos = chosenNode.id.substr(2);
const matrixIndices = Object.entries(node_matrices).flatMap((matrixData) => const matrixIndices = Object.entries(node_matrices).flatMap((matrixData) =>