event rewrite done, fixed a lot of stuff, renamed/moved some stuff

This commit is contained in:
ad044 2021-02-20 18:35:23 +04:00
parent 7620610097
commit 1d895e64b0
36 changed files with 791 additions and 761 deletions

View file

@ -1,4 +1,4 @@
import React, { useRef, useState } from "react"; import React, { memo, useRef, useState } from "react";
import middleSpritesheet from "../../static/sprite/end_middle_spritesheet.png"; import middleSpritesheet from "../../static/sprite/end_middle_spritesheet.png";
import middleLain from "../../static/sprite/end_middle_lain.png"; import middleLain from "../../static/sprite/end_middle_lain.png";
import circleSpritesheet from "../../static/sprite/end_circle_spritesheet.png"; import circleSpritesheet from "../../static/sprite/end_circle_spritesheet.png";
@ -10,7 +10,11 @@ import { PlainAnimator } from "three-plain-animator/lib/plain-animator";
import { a, useSpring } from "@react-spring/three"; import { a, useSpring } from "@react-spring/three";
import { useStore } from "../../store"; import { useStore } from "../../store";
const EndSelectionScreen = () => { type EndSelectionScreenProps = {
visible: boolean;
};
const EndSelectionScreen = memo((props: EndSelectionScreenProps) => {
const middleSpritesheetTex: any = useLoader( const middleSpritesheetTex: any = useLoader(
THREE.TextureLoader, THREE.TextureLoader,
middleSpritesheet middleSpritesheet
@ -63,7 +67,7 @@ const EndSelectionScreen = () => {
const lainOpacity = lainOpacityToggle.to([0, 1], [0, 0.5]); const lainOpacity = lainOpacityToggle.to([0, 1], [0, 0.5]);
return ( return (
<> <group visible={props.visible}>
<sprite position={[-3.5, 0, -3]} scale={[10, 0.8, 0]}> <sprite position={[-3.5, 0, -3]} scale={[10, 0.8, 0]}>
<spriteMaterial attach="material" map={middleSpritesheetTex} /> <spriteMaterial attach="material" map={middleSpritesheetTex} />
</sprite> </sprite>
@ -93,8 +97,8 @@ const EndSelectionScreen = () => {
opacity={lainOpacity} opacity={lainOpacity}
/> />
</sprite> </sprite>
</> </group>
); );
}; });
export default EndSelectionScreen; export default EndSelectionScreen;

View file

@ -31,13 +31,7 @@ const Images = () => {
const textureLoader = useMemo(() => new THREE.TextureLoader(), []); const textureLoader = useMemo(() => new THREE.TextureLoader(), []);
useEffect(() => { useEffect(() => {
let images; const images = currentScene === "idle_media" ? idleNodeImages : nodeImages;
if (currentScene === "media" || currentScene === "tak") {
images = nodeImages;
} else if (currentScene === "idle_media") {
images = idleNodeImages;
}
if (images) { if (images) {
// checking the length of the img arr doesn't work in some cases // checking the length of the img arr doesn't work in some cases
// since the amount of images varies from 1 to 3. // since the amount of images varies from 1 to 3.

View file

@ -1,32 +1,23 @@
import { useCallback, useEffect, useRef } from "react"; import { useCallback, useEffect, useRef } from "react";
import { import {
BootSceneContext,
EndSceneContext,
getBootSceneContext, getBootSceneContext,
getEndSceneContext, getEndSceneContext,
getMainSceneContext, getMainSceneContext,
getMediaSceneContext, getMediaSceneContext,
getSsknSceneContext, getSsknSceneContext,
MainSceneContext,
MediaSceneContext,
playAudio, playAudio,
SsknSceneContext,
useStore, useStore,
} from "../store"; } from "../store";
import { getKeyCodeAssociation } from "../utils/keyPressUtils"; import { getKeyCodeAssociation } from "../utils/getKey";
import handleMediaSceneKeyPress from "../core/scene-keypress-handlers/handleMediaSceneKeyPress"; import handleMediaSceneKeyPress from "../core/scene-keypress-handlers/handleMediaSceneKeyPress";
import handleSsknSceneKeyPress from "../core/scene-keypress-handlers/handleSsknSceneKeyPress"; import handleSsknSceneKeyPress from "../core/scene-keypress-handlers/handleSsknSceneKeyPress";
import handleMainSceneKeyPress from "../core/scene-keypress-handlers/handleMainSceneKeyPress"; import handleMainSceneKeyPress from "../core/scene-keypress-handlers/handleMainSceneKeyPress";
import handleBootSceneKeyPress from "../core/scene-keypress-handlers/handleBootSceneKeyPress"; import handleBootSceneKeyPress from "../core/scene-keypress-handlers/handleBootSceneKeyPress";
import { useFrame } from "react-three-fiber"; import { useFrame } from "react-three-fiber";
import { getRandomIdleLainAnim } from "../utils/idle-utils"; import { getRandomIdleLainAnim } from "../helpers/idle-helpers";
import * as audio from "../static/sfx"; import * as audio from "../static/audio/sfx";
import handleEndSceneKeyPress from "../core/scene-keypress-handlers/handleEndSceneKeyPress"; import handleEndSceneKeyPress from "../core/scene-keypress-handlers/handleEndSceneKeyPress";
import handleMediaSceneEvent from "../core/scene-event-handlers/handleMediaSceneEvent"; import handleEvent, { GameEvent } from "../core/handleEvent";
import handleSsknSceneEvent from "../core/scene-event-handlers/handleSsknSceneEvent";
import handleBootSceneEvent from "../core/scene-event-handlers/handleBootSceneEvent";
import handleEndSceneEvent from "../core/scene-event-handlers/handleEndSceneEvent";
import handleEvent from "../core/scene-event-handlers/handleEvent";
const KeyPressHandler = () => { const KeyPressHandler = () => {
const scene = useStore((state) => state.currentScene); const scene = useStore((state) => state.currentScene);
@ -83,9 +74,9 @@ const KeyPressHandler = () => {
const now = Date.now(); const now = Date.now();
if ( if (
keyPress keyPress &&
// !inputCooldown && now > timeSinceLastKeyPress.current + inputCooldown &&
// now > timeSinceLastKeyPress.current + 1500 inputCooldown !== -1
) { ) {
if (scene === "main") { if (scene === "main") {
lainIdleCounter.current = now; lainIdleCounter.current = now;
@ -105,34 +96,31 @@ const KeyPressHandler = () => {
contextProvider: getMediaSceneContext, contextProvider: getMediaSceneContext,
keyPressHandler: handleMediaSceneKeyPress, keyPressHandler: handleMediaSceneKeyPress,
}; };
// case "sskn": case "sskn":
// return { return {
// contextProvider: getSsknSceneContext, contextProvider: getSsknSceneContext,
// keyPressHandler: handleSsknSceneKeyPress, keyPressHandler: handleSsknSceneKeyPress,
// eventHandler: handleSsknSceneEvent, };
// }; case "boot":
// case "boot": return {
// return { contextProvider: getBootSceneContext,
// contextProvider: getBootSceneContext, keyPressHandler: handleBootSceneKeyPress,
// keyPressHandler: handleBootSceneKeyPress, };
// eventHandler: handleBootSceneEvent, case "end":
// }; return {
// case "end": contextProvider: getEndSceneContext,
// return { keyPressHandler: handleEndSceneKeyPress,
// contextProvider: getEndSceneContext, };
// keyPressHandler: handleEndSceneKeyPress, case "gate":
// eventHandler: handleEndSceneEvent, case "polytan":
// }; useStore.setState({ currentScene: "main" });
// case "gate": break;
// case "polytan": case "idle_media":
// useStore.setState({ currentScene: "main" }); useStore.setState({
// break; currentScene: "main",
// case "idle_media": idleStarting: false,
// useStore.setState({ });
// currentScene: "main", break;
// idleStarting: false,
// });
// break;
} }
})(); })();
@ -140,9 +128,8 @@ const KeyPressHandler = () => {
const { contextProvider, keyPressHandler } = sceneFns; const { contextProvider, keyPressHandler } = sceneFns;
const ctx = contextProvider(keyPress); const ctx = contextProvider(keyPress);
const event = keyPressHandler(ctx as any) as any; const event: GameEvent | undefined = keyPressHandler(ctx as any);
if (event) handleEvent(event); if (event) handleEvent(event);
// if (event) eventHandler(event.event, event.mutations);
} }
} }
}, },

View file

@ -21,6 +21,9 @@ const Loading = () => {
return ( return (
<> <>
<sprite scale={[5, 5, 5]}>
<spriteMaterial attach="material" color={0x000000} />
</sprite>
<sprite scale={[0.35, 0.6, 0.35]} position={[0, 0.2, 0]}> <sprite scale={[0.35, 0.6, 0.35]} position={[0, 0.2, 0]}>
<spriteMaterial attach="material" map={loadingTex} /> <spriteMaterial attach="material" map={loadingTex} />
</sprite> </sprite>

View file

@ -8,7 +8,7 @@ import { useStore } from "../../store";
import lerp from "../../utils/lerp"; import lerp from "../../utils/lerp";
import GreenTextRenderer from "../TextRenderer/GreenTextRenderer"; import GreenTextRenderer from "../TextRenderer/GreenTextRenderer";
import usePrevious from "../../hooks/usePrevious"; import usePrevious from "../../hooks/usePrevious";
import { getNodeHud } from "../../utils/node-utils"; import { getNodeHud } from "../../helpers/node-helpers";
export type HUDType = { export type HUDType = {
mirrored: number; mirrored: number;
@ -130,7 +130,6 @@ 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

@ -64,7 +64,7 @@ const Pause = () => {
setTimeout(() => { setTimeout(() => {
setShowActiveComponent(true); setShowActiveComponent(true);
setIntro(false); setIntro(false);
setInputCooldown(false); setInputCooldown(1000);
}, 3500); }, 3500);
}, 3400); }, 3400);
} }

View file

@ -3,13 +3,16 @@ import notFound from "../../../static/sprite/not_found.png";
import notFoundLof from "../../../static/sprite/not_found_lof.png"; import notFoundLof from "../../../static/sprite/not_found_lof.png";
import { useLoader } from "react-three-fiber"; import { useLoader } from "react-three-fiber";
import * as THREE from "three"; import * as THREE from "three";
import { useStore } from "../../../store";
const NotFound = memo(() => { const NotFound = memo(() => {
const notFoundTex = useLoader(THREE.TextureLoader, notFound); const notFoundTex = useLoader(THREE.TextureLoader, notFound);
const notFoundLofTex = useLoader(THREE.TextureLoader, notFoundLof); const notFoundLofTex = useLoader(THREE.TextureLoader, notFoundLof);
const wordNotFound = useStore((state) => state.wordNotFound);
return ( return (
<group visible={false}> <group visible={wordNotFound}>
<sprite scale={[1, 0.25, 0]} renderOrder={106} position={[-1, -0.05, 0]}> <sprite scale={[1, 0.25, 0]} renderOrder={106} position={[-1, -0.05, 0]}>
<spriteMaterial <spriteMaterial
attach="material" attach="material"

View file

@ -4,7 +4,7 @@ import { useStore } from "../../../store";
import { SiteData } from "./Site"; import { SiteData } from "./Site";
import InactiveLevelNode from "./InactiveLevelNode"; import InactiveLevelNode from "./InactiveLevelNode";
import usePrevious from "../../../hooks/usePrevious"; import usePrevious from "../../../hooks/usePrevious";
import { generateInactiveNodes } from "../../../utils/node-utils"; import { generateInactiveNodes } from "../../../helpers/node-helpers";
type ActiveLevelNodesProps = { type ActiveLevelNodesProps = {
visibleNodes: SiteData; visibleNodes: SiteData;

View file

@ -8,7 +8,7 @@ import InactiveLevelNodes from "./InactiveLevelNodes";
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 level_y_values from "../../../resources/level_y_values.json"; import level_y_values from "../../../resources/level_y_values.json";
import { filterInvisibleNodes } from "../../../utils/node-utils"; import { filterInvisibleNodes } from "../../../helpers/node-helpers";
import Loading from "../../Loading"; import Loading from "../../Loading";
export type NodeData = { export type NodeData = {

View file

@ -1,4 +1,4 @@
import React from "react"; import React, { memo } from "react";
import body from "../../static/sprite/body.png"; import body from "../../static/sprite/body.png";
import head from "../../static/sprite/head.png"; import head from "../../static/sprite/head.png";
import leftLeg from "../../static/sprite/left_leg.png"; import leftLeg from "../../static/sprite/left_leg.png";
@ -8,19 +8,9 @@ import rightLeg from "../../static/sprite/right_leg.png";
import skeleton from "../../static/sprite/polytan_skeleton.png"; import skeleton from "../../static/sprite/polytan_skeleton.png";
import { useLoader } from "react-three-fiber"; import { useLoader } from "react-three-fiber";
import * as THREE from "three"; import * as THREE from "three";
import { useStore } from "../../store";
type PolytanBearProps = { const PolytanBear = memo(() => {
unlockedParts: {
body: boolean;
head: boolean;
leftArm: boolean;
rightArm: boolean;
leftLeg: boolean;
rightLeg: boolean;
};
};
const PolytanBear = (props: PolytanBearProps) => {
const skeletonTex = useLoader(THREE.TextureLoader, skeleton); const skeletonTex = useLoader(THREE.TextureLoader, skeleton);
const headTex = useLoader(THREE.TextureLoader, head); const headTex = useLoader(THREE.TextureLoader, head);
const bodyTex = useLoader(THREE.TextureLoader, body); const bodyTex = useLoader(THREE.TextureLoader, body);
@ -29,6 +19,8 @@ const PolytanBear = (props: PolytanBearProps) => {
const rightArmTex = useLoader(THREE.TextureLoader, rightArm); const rightArmTex = useLoader(THREE.TextureLoader, rightArm);
const rightLegTex = useLoader(THREE.TextureLoader, rightLeg); const rightLegTex = useLoader(THREE.TextureLoader, rightLeg);
const unlockedParts = useStore((state) => state.polytanUnlockedParts);
return ( return (
<> <>
<sprite scale={[4, 5, 0]} position={[0, -0.4, 0]}> <sprite scale={[4, 5, 0]} position={[0, -0.4, 0]}>
@ -39,7 +31,7 @@ const PolytanBear = (props: PolytanBearProps) => {
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={bodyTex} map={bodyTex}
visible={props.unlockedParts.body} visible={unlockedParts.body}
/> />
</sprite> </sprite>
@ -47,39 +39,39 @@ const PolytanBear = (props: PolytanBearProps) => {
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={headTex} map={headTex}
visible={props.unlockedParts.head} visible={unlockedParts.head}
/> />
</sprite> </sprite>
<sprite scale={[1.9, 1, 0]} position={[1, -2.2, 0]}> <sprite scale={[1.9, 1, 0]} position={[1, -2.2, 0]}>
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={leftLegTex} map={leftLegTex}
visible={props.unlockedParts.leftLeg} visible={unlockedParts.leftLeg}
/> />
</sprite> </sprite>
<sprite scale={[1.5, 1.9, 0]} position={[1.2, -0.4, 0]}> <sprite scale={[1.5, 1.9, 0]} position={[1.2, -0.4, 0]}>
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={leftArmTex} map={leftArmTex}
visible={props.unlockedParts.leftArm} visible={unlockedParts.leftArm}
/> />
</sprite> </sprite>
<sprite scale={[1.6, 2, 0]} position={[-1.2, -1.2, 0]}> <sprite scale={[1.6, 2, 0]} position={[-1.2, -1.2, 0]}>
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={rightArmTex} map={rightArmTex}
visible={props.unlockedParts.rightArm} visible={unlockedParts.rightArm}
/> />
</sprite> </sprite>
<sprite scale={[1.9, 1, 0]} position={[-1, -2.2, 0]}> <sprite scale={[1.9, 1, 0]} position={[-1, -2.2, 0]}>
<spriteMaterial <spriteMaterial
attach="material" attach="material"
map={rightLegTex} map={rightLegTex}
visible={props.unlockedParts.rightLeg} visible={unlockedParts.rightLeg}
/> />
</sprite> </sprite>
</> </>
); );
}; });
export default PolytanBear; export default PolytanBear;

View file

@ -91,7 +91,6 @@ 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 "../../helpers/node-helpers";
const MainYellowTextAnimator = (props: { visible?: boolean }) => { const MainYellowTextAnimator = (props: { visible?: boolean }) => {
const activeNode = useStore((state) => state.activeNode); const activeNode = useStore((state) => state.activeNode);

View file

@ -1,13 +1,26 @@
import { NodeData } from "../components/MainScene/Site/Site"; import { NodeData } from "../components/MainScene/Site/Site";
import * as audio from "../static/sfx"; import * as audio from "../static/audio/sfx";
import { import {
nodeExplodeAnimation, nodeExplodeAnimation,
nodeKnockAndFallAnimation, nodeKnockAndFallAnimation,
nodeKnockAnimation, nodeKnockAnimation,
nodeRipAnimation, nodeRipAnimation,
nodeThrowAnimation, nodeThrowAnimation,
} from "../utils/node-animations"; } from "../helpers/node-animation-helpers";
import { playAudio } from "../store"; import { playMediaElement, resetMediaElement } from "../helpers/media-helpers";
import {
ActiveSite,
EndComponent,
GameProgress,
GameScene,
LeftMediaComponent,
MediaComponent,
MediaSide,
PromptComponent,
RightMediaComponent,
SiteSaveState,
SsknComponent,
} from "../store";
export const siteMoveHorizontal = (calculatedState: { export const siteMoveHorizontal = (calculatedState: {
lainMoveAnimation: string; lainMoveAnimation: string;
@ -19,15 +32,13 @@ export const siteMoveHorizontal = (calculatedState: {
mutation: { mutation: {
lainMoveState: calculatedState.lainMoveAnimation, lainMoveState: calculatedState.lainMoveAnimation,
siteRot: calculatedState.siteRot, siteRot: calculatedState.siteRot,
inputCooldown: true, inputCooldown: 5500,
}, },
delay: 0,
}, },
{ {
mutation: { mutation: {
activeNode: calculatedState.activeNode, activeNode: calculatedState.activeNode,
lainMoveState: "standing", lainMoveState: "standing",
inputCooldown: false,
}, },
delay: 3900, delay: 3900,
}, },
@ -45,37 +56,36 @@ export const siteMoveVertical = (calculatedState: {
mutation: { mutation: {
lainMoveState: calculatedState.lainMoveAnimation, lainMoveState: calculatedState.lainMoveAnimation,
activeLevel: calculatedState.activeLevel, activeLevel: calculatedState.activeLevel,
inputCooldown: true, inputCooldown: 5500,
}, },
delay: 0,
}, },
{ {
mutation: { mutation: {
activeNode: calculatedState.activeNode, activeNode: calculatedState.activeNode,
lainMoveState: "standing", lainMoveState: "standing",
inputCooldown: false,
}, },
delay: 3900, delay: 3900,
}, },
], ],
audio: [ audio: [
{ sfx: [audio.sound13], delay: 0 }, { sfx: [audio.sound13] },
{ sfx: [audio.sound10, audio.sound9], delay: 1300 }, { sfx: [audio.sound10, audio.sound9], delay: 1300 },
{ sfx: [audio.sound8], delay: 2700 }, { sfx: [audio.sound8], delay: 2700 },
], ],
}); });
export const changeNode = (calculatedState: { activeNode: NodeData }) => ({ export const changeNode = (calculatedState: { activeNode: NodeData }) => ({
state: [{ mutation: { activeNode: calculatedState.activeNode }, delay: 0 }],
audio: [{ sfx: [audio.sound1], delay: 0 }],
});
export const throwNode = (calculatedState: { currentScene: string }) => ({
state: [ state: [
{ {
mutation: { lainMoveState: "throw_node", inputCooldown: true }, mutation: { activeNode: calculatedState.activeNode, inputCooldown: 1500 },
delay: 0,
}, },
],
audio: [{ sfx: [audio.sound1] }],
});
export const throwNode = (calculatedState: { currentScene: GameScene }) => ({
state: [
{ mutation: { lainMoveState: "throw_node", inputCooldown: -1 } },
{ {
mutation: { mutation: {
currentScene: calculatedState.currentScene, currentScene: calculatedState.currentScene,
@ -87,18 +97,15 @@ export const throwNode = (calculatedState: { currentScene: string }) => ({
], ],
effects: [nodeThrowAnimation], effects: [nodeThrowAnimation],
audio: [ audio: [
{ sfx: [audio.sound0], delay: 0 }, { sfx: [audio.sound0] },
{ sfx: [audio.sound12], delay: 1600 }, { sfx: [audio.sound12], delay: 1600 },
{ sfx: [audio.sound13, audio.sound14], delay: 2800 }, { sfx: [audio.sound13, audio.sound14], delay: 2800 },
], ],
}); });
export const ripNode = (calculatedState: { currentScene: string }) => ({ export const ripNode = (calculatedState: { currentScene: GameScene }) => ({
state: [ state: [
{ { mutation: { lainMoveState: "rip_node", inputCooldown: -1 } },
mutation: { lainMoveState: "rip_node", inputCooldown: true },
delay: 0,
},
{ {
mutation: { mutation: {
currentScene: calculatedState.currentScene, currentScene: calculatedState.currentScene,
@ -110,7 +117,7 @@ export const ripNode = (calculatedState: { currentScene: string }) => ({
], ],
effects: [nodeRipAnimation], effects: [nodeRipAnimation],
audio: [ audio: [
{ sfx: [audio.sound0], delay: 0 }, { sfx: [audio.sound0] },
{ sfx: [audio.sound12], delay: 1600 }, { sfx: [audio.sound12], delay: 1600 },
{ sfx: [audio.sound13, audio.sound15], delay: 4000 }, { sfx: [audio.sound13, audio.sound15], delay: 4000 },
], ],
@ -121,18 +128,17 @@ export const explodeNode = {
{ {
mutation: { mutation: {
lainMoveState: "touch_node_and_get_scared", lainMoveState: "touch_node_and_get_scared",
inputCooldown: true, inputCooldown: 3800,
}, },
delay: 0,
}, },
{ {
mutation: { lainMoveState: "standing", inputCooldown: false }, mutation: { lainMoveState: "standing" },
delay: 3800, delay: 3800,
}, },
], ],
effects: [nodeExplodeAnimation], effects: [nodeExplodeAnimation],
audio: [ audio: [
{ sfx: [audio.sound0], delay: 0 }, { sfx: [audio.sound0] },
{ sfx: [audio.sound17], delay: 2400 }, { sfx: [audio.sound17], delay: 2400 },
{ sfx: [audio.sound33], delay: 3150 }, { sfx: [audio.sound33], delay: 3150 },
], ],
@ -140,33 +146,26 @@ export const explodeNode = {
export const knockNode = { export const knockNode = {
state: [ state: [
{ mutation: { lainMoveState: "knock", inputCooldown: true }, delay: 0 }, { mutation: { lainMoveState: "knock", inputCooldown: 3500 } },
{ {
mutation: { lainMoveState: "standing", inputCooldown: false }, mutation: { lainMoveState: "standing" },
delay: 2900, delay: 3500,
}, },
], ],
effects: [nodeKnockAnimation], effects: [nodeKnockAnimation],
audio: [ audio: [{ sfx: [audio.sound0] }, { sfx: [audio.sound18], delay: 1200 }],
{ sfx: [audio.sound0], delay: 0 },
{ sfx: [audio.sound18], delay: 1200 },
],
}; };
export const knockNodeAndFall = { export const knockNodeAndFall = {
state: [ state: [
{ mutation: { lainMoveState: "knock_and_fall", inputCooldown: 6000 } },
{ {
mutation: { lainMoveState: "knock_and_fall", inputCooldown: true }, mutation: { lainMoveState: "standing" },
delay: 0,
},
{
mutation: { lainMoveState: "standing", inputCooldown: false },
delay: 7500,
}, },
], ],
effects: [nodeKnockAndFallAnimation], effects: [nodeKnockAndFallAnimation],
audio: [ audio: [
{ sfx: [audio.sound0], delay: 0 }, { sfx: [audio.sound0] },
{ sfx: [audio.sound18], delay: 1200 }, { sfx: [audio.sound18], delay: 1200 },
{ sfx: [audio.sound19], delay: 2300 }, { sfx: [audio.sound19], delay: 2300 },
{ sfx: [audio.sound33], delay: 3150 }, { sfx: [audio.sound33], delay: 3150 },
@ -181,23 +180,28 @@ export const enterLevelSelection = (calculatedState: {
mutation: { mutation: {
selectedLevel: calculatedState.selectedLevel, selectedLevel: calculatedState.selectedLevel,
mainSubscene: "level_selection", mainSubscene: "level_selection",
inputCooldown: 1500,
}, },
delay: 0,
}, },
], ],
audio: [{ sfx: [audio.sound1], delay: 0 }], audio: [{ sfx: [audio.sound1] }],
}); });
export const exitLevelSelection = { export const exitLevelSelection = {
state: [{ mutation: { mainSubscene: "site" }, delay: 0 }], state: [{ mutation: { mainSubscene: "site", inputCooldown: 1500 } }],
audio: [{ sfx: [audio.sound1], delay: 0 }], audio: [{ sfx: [audio.sound1] }],
}; };
export const changeSelectedLevel = (calculatedState: { export const changeSelectedLevel = (calculatedState: {
selectedLevel: number; selectedLevel: number;
}) => ({ }) => ({
state: [ state: [
{ mutation: { selectedLevel: calculatedState.selectedLevel }, delay: 0 }, {
mutation: {
selectedLevel: calculatedState.selectedLevel,
inputCooldown: 300,
},
},
], ],
}); });
@ -212,15 +216,13 @@ export const selectLevel = (calculatedState: {
lainMoveState: calculatedState.lainMoveState, lainMoveState: calculatedState.lainMoveState,
activeLevel: calculatedState.activeLevel, activeLevel: calculatedState.activeLevel,
mainSubscene: "site", mainSubscene: "site",
inputCooldown: true, inputCooldown: 5500,
}, },
delay: 0,
}, },
{ {
mutation: { mutation: {
activeNode: calculatedState.activeNode, activeNode: calculatedState.activeNode,
lainMoveState: "standing", lainMoveState: "standing",
inputCooldown: false,
}, },
delay: 3900, delay: 3900,
}, },
@ -238,19 +240,15 @@ export const pauseGame = (calculatedState: { siteRot: number[] }) => ({
lainMoveState: "rip_middle_ring", lainMoveState: "rip_middle_ring",
pauseExitAnimation: false, pauseExitAnimation: false,
mainSubscene: "pause", mainSubscene: "pause",
inputCooldown: true, inputCooldown: -1,
}, },
delay: 0,
}, },
{ {
mutation: { siteRot: calculatedState.siteRot }, mutation: { siteRot: calculatedState.siteRot },
delay: 3600, delay: 3600,
}, },
], ],
audio: [ audio: [{ sfx: [audio.sound7] }, { sfx: [audio.sound23], delay: 3600 }],
{ sfx: [audio.sound7], delay: 0 },
{ sfx: [audio.sound23], delay: 3600 },
],
}); });
export const changePauseComponent = (calculatedState: { export const changePauseComponent = (calculatedState: {
@ -258,29 +256,31 @@ export const changePauseComponent = (calculatedState: {
}) => ({ }) => ({
state: [ state: [
{ {
mutation: { activePauseComponent: calculatedState.activePauseComponent }, mutation: {
delay: 0, activePauseComponent: calculatedState.activePauseComponent,
inputCooldown: 500,
},
}, },
], ],
audio: [{ sfx: [audio.sound1], delay: 0 }], audio: [{ sfx: [audio.sound1] }],
}); });
export const showPermissionDenied = { export const showPermissionDenied = {
state: [ state: [
{ mutation: { permissionDenied: true }, delay: 0 }, { mutation: { permissionDenied: true, inputCooldown: 1200 } },
{ mutation: { permissionDenied: false }, delay: 1200 }, { mutation: { permissionDenied: false }, delay: 1200 },
], ],
audio: [{ sfx: [audio.sound0], delay: 0 }], audio: [{ sfx: [audio.sound0] }],
}; };
export const displayPrompt = { export const displayPrompt = {
state: [{ mutation: { promptVisible: true }, delay: 0 }], state: [{ mutation: { promptVisible: true, inputCooldown: 500 } }],
audio: [{ sfx: [audio.sound0], delay: 0 }], audio: [{ sfx: [audio.sound0] }],
}; };
export const showAbout = { export const showAbout = {
state: [{ mutation: { showingAbout: true }, delay: 0 }], state: [{ mutation: { showingAbout: true } }],
audio: [{ sfx: [audio.sound0], delay: 0 }], audio: [{ sfx: [audio.sound0] }],
}; };
export const exitPause = (calculatedState: { siteRot: number[] }) => ({ export const exitPause = (calculatedState: { siteRot: number[] }) => ({
@ -290,9 +290,8 @@ export const exitPause = (calculatedState: { siteRot: number[] }) => ({
siteRot: calculatedState.siteRot, siteRot: calculatedState.siteRot,
pauseExitAnimation: true, pauseExitAnimation: true,
activePauseComponent: "change", activePauseComponent: "change",
inputCooldown: true, inputCooldown: 1400,
}, },
delay: 0,
}, },
{ {
mutation: { mutation: {
@ -303,84 +302,70 @@ export const exitPause = (calculatedState: { siteRot: number[] }) => ({
delay: 1200, delay: 1200,
}, },
], ],
audio: [{ sfx: [audio.sound0], delay: 0 }], audio: [{ sfx: [audio.sound0] }],
}); });
export const exitAbout = { export const exitAbout = {
state: [{ mutation: { showingAbout: false }, delay: 0 }], state: [{ mutation: { showingAbout: false, inputCooldown: 500 } }],
}; };
export const changePromptComponent = (calculatedState: { export const changePromptComponent = (calculatedState: {
activePromptComponent: "yes" | "no"; activePromptComponent: PromptComponent;
}) => ({ }) => ({
state: [ state: [
{ {
mutation: { mutation: {
activePromptComponent: calculatedState.activePromptComponent, activePromptComponent: calculatedState.activePromptComponent,
inputCooldown: 500,
}, },
delay: 0,
}, },
], ],
audio: [{ sfx: [audio.sound1], delay: 0 }], audio: [{ sfx: [audio.sound1] }],
}); });
export const exitPrompt = { export const exitPrompt = {
state: [ state: [
{ {
mutation: { activePromptComponent: "no", promptVisible: false }, mutation: {
delay: 0, activePromptComponent: "no",
promptVisible: false,
inputCooldown: 500,
},
}, },
], ],
audio: [{ sfx: [audio.sound28], delay: 0 }], audio: [{ sfx: [audio.sound28] }],
}; };
// todo actually save // todo actually save
export const saveGame = () => ({ export const saveGame = () => ({
state: [ state: [
{ { mutation: { saveSuccessful: true, inputCooldown: 1200 } },
mutation: { saveSuccessful: true },
delay: 0,
},
{ {
mutation: { saveSuccessful: undefined }, mutation: { saveSuccessful: undefined },
delay: 1200, delay: 1200,
}, },
], ],
audio: [{ sfx: [audio.sound28], delay: 0 }], audio: [{ sfx: [audio.sound28] }],
}); });
// todo actually load // todo actually load
export const loadGame = () => ({ export const loadGame = () => ({
state: [ state: [
{ { mutation: { loadSuccessful: true, inputCooldown: 1200 } },
mutation: { loadSuccessful: true },
delay: 0,
},
{ {
mutation: { loadSuccessful: undefined }, mutation: { loadSuccessful: undefined },
delay: 1200, delay: 1200,
}, },
], ],
audio: [{ sfx: [audio.sound28], delay: 0 }], audio: [{ sfx: [audio.sound28] }],
}); });
export const changeSite = (calculatedState: { export const changeSite = (calculatedState: {
newActiveSite: "a" | "b"; newActiveSite: ActiveSite;
newActiveNode: NodeData; newActiveNode: NodeData;
newActiveLevel: string; newActiveLevel: string;
newSiteRot: number[]; newSiteRot: number[];
newSiteSaveState: { newSiteSaveState: SiteSaveState;
a: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
b: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
};
}) => ({ }) => ({
state: [ state: [
{ {
@ -397,26 +382,274 @@ export const changeSite = (calculatedState: {
activeLevel: calculatedState.newActiveLevel, activeLevel: calculatedState.newActiveLevel,
// save state // save state
siteSaveState: calculatedState.newSiteSaveState, siteSaveState: calculatedState.newSiteSaveState,
inputCooldown: -1,
}, },
delay: 0,
}, },
], ],
}); });
export const changeLeftMediaComponent = (calculatedState: { export const changeLeftMediaComponent = (calculatedState: {
activeComponent: "play" | "exit"; activeComponent: LeftMediaComponent;
}) => ({ }) => ({
state: [ state: [
{ mutation: { activeMediaComponent: calculatedState.activeComponent } }, {
mutation: {
activeMediaComponent: calculatedState.activeComponent,
inputCooldown: 1200,
},
},
], ],
audio: [{ sfx: [audio.sound1], delay: 0 }], audio: [{ sfx: [audio.sound1] }],
}); });
export const changeMediaSide = (calculatedState: { export const changeMediaSide = (calculatedState: {
activeMediaComponent: "fstWord" | "sndWord" | "thirdWord" | "exit" | "play"; activeMediaComponent: MediaComponent;
lastActiveMediaComponents: { lastActiveMediaComponents: {
left: "play" | "exit"; left: LeftMediaComponent;
right: "fstWord" | "sndWord" | "thirdWord"; right: RightMediaComponent;
}; };
currentMediaSide: "right" | "left"; currentMediaSide: MediaSide;
}) => ({}); }) => ({
state: [
{
mutation: {
activeMediaComponent: calculatedState.activeMediaComponent,
lastActiveMediaComponents: calculatedState.lastActiveMediaComponents,
currentMediaSide: calculatedState.currentMediaSide,
inputCooldown: 500,
},
},
],
});
export const playMedia = {
state: [{ mutation: { mediaPercentageElapsed: 0, inputCooldown: 500 } }],
effects: [playMediaElement],
};
export const exitMedia = {
state: [{ mutation: { currentScene: "main", inputCooldown: -1 } }],
effects: [resetMediaElement],
};
export const changeRightMediaComponent = (calculatedState: {
wordPosStateIdx: number;
activeComponent: RightMediaComponent;
}) => ({
state: [
{
mutation: {
activeMediaComponent: calculatedState.activeComponent,
mediaWordPosStateIdx: calculatedState.wordPosStateIdx,
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound1] }],
});
export const wordNotFound = {
state: [
{
mutation: {
currentScene: "main",
wordNotFound: true,
inputCooldown: 300,
},
},
],
audio: [{ sfx: [audio.sound30] }],
effects: [resetMediaElement],
};
export const hideWordNotFound = {
state: [{ mutation: { wordNotFound: false, inputCooldown: 300 } }],
};
export const selectWord = (calculatedState: {
activeLevel: string;
activeNode: NodeData;
siteRot: number[];
}) => ({
state: [
{
mutation: {
activeLevel: calculatedState.activeLevel,
siteRot: calculatedState.siteRot,
activeNode: calculatedState.activeNode,
wordSelected: true,
currentScene: "main",
inputCooldown: -1,
},
},
],
audio: [{ sfx: [audio.sound29] }],
effects: [resetMediaElement],
});
export const changeSsknComponent = (calculatedState: {
activeSsknComponent: SsknComponent;
}) => ({
state: [
{
mutation: {
activeSsknComponent: calculatedState.activeSsknComponent,
inputCooldown: 500,
},
},
],
});
export const upgradeSskn = (calculatedState: {
gameProgress: GameProgress;
ssknLvl: number;
}) => ({
state: [
{
mutation: {
gameProgress: calculatedState.gameProgress,
ssknLvl: calculatedState.ssknLvl,
ssknLoading: true,
inputCooldown: -1,
},
},
{ mutation: { currentScene: "main" }, delay: 6000 },
],
});
export const exitSskn = {
state: [
{
mutation: {
currentScene: "main",
ssknLoading: false,
activeSsknComponent: "ok",
inputCooldown: -1,
},
},
],
};
export const changeEndComponent = (calculatedState: {
activeEndComponent: EndComponent;
}) => ({
state: [
{
mutation: {
activeEndComponent: calculatedState.activeEndComponent,
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound1] }],
});
export const endGame = {
state: [{ mutation: { currentScene: "boot", inputCooldown: -1 } }],
audio: [{ sfx: [audio.sound0] }],
};
// todo this is probably buggy
export const continueGameAfterEnd = {
state: [
{
mutation: { currentScene: "change_disc", intro: true, inputCooldown: -1 },
},
],
audio: [{ sfx: [audio.sound0] }],
};
export const changeMainMenuComponent = (calculatedState: {
activeMainMenuComponent: "authorize_user" | "load_data";
}) => ({
state: [
{
mutation: {
activeMainMenuComponent: calculatedState.activeMainMenuComponent,
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound1] }],
});
export const exitLoadData = {
state: [
{
mutation: {
bootSubscene: "main_menu",
promptVisible: false,
activePromptComponent: "no",
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound29] }],
};
export const enterLoadData = {
state: [
{ mutation: { bootSubscene: "load_data", inputCooldown: 500 } },
{ mutation: { promptVisible: true }, delay: 500 },
],
audio: [{ sfx: [audio.sound0] }],
};
export const enterUserAuthorization = {
state: [
{
mutation: {
bootSubscene: "authorize_user",
authorizeUserLetterIdx: 0,
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound0] }],
};
export const exitUserAuthorization = {
state: [
{
mutation: {
playerName: "",
bootSubscene: "main_menu",
inputCooldown: 500,
},
},
],
audio: [{ sfx: [audio.sound29] }],
};
export const startNewGame = {
state: [
{ mutation: { currentScene: "main", intro: true, inputCooldown: -1 } },
],
};
export const updatePlayerName = (calculatedState: { playerName: string }) => ({
state: [{ mutation: { playerName: calculatedState.playerName } }],
audio: [{ sfx: [audio.sound0] }],
});
export const removePlayerNameLastChar = (calculatedState: {
playerName: string;
}) => ({
state: [{ mutation: { playerName: calculatedState.playerName } }],
audio: [{ sfx: [audio.sound29] }],
});
export const failUpdatePlayerName = { audio: [{ sfx: [audio.sound0] }] };
export const updateAuthorizeUserLetterIdx = (calculatedState: {
authorizeUserLetterIdx: number;
}) => ({
state: [
{
mutation: {
inputCooldown: 300,
authorizeUserLetterIdx: calculatedState.authorizeUserLetterIdx,
},
},
],
});

View file

@ -1,47 +1,44 @@
import { playAudio, useStore } from "../../store"; import { playAudio, useStore } from "../store";
import sleep from "../../utils/sleep"; import sleep from "../utils/sleep";
type Mutation = { type Mutation = {
mutation: Object; mutation: Object;
delay: number; delay?: number;
}; };
type EventAudio = { type EventAudio = {
sfx: HTMLAudioElement[]; sfx: HTMLAudioElement[];
delay: number; delay?: number;
}; };
type Event = { export type GameEvent = {
state: Mutation[]; state?: Mutation[];
audio?: EventAudio[]; audio?: EventAudio[];
effects?: (() => void)[]; effects?: (() => void)[];
}; };
// the async/await here might be misleading for some, it functions as a setTimeout that fires // the async/await here might be misleading for some, it functions as a setTimeout that fires
// multiple async calls without stopping the execution, which is what we want. // multiple async calls without stopping the execution, which is what we want.
const handleEvent = (event: Event) => { const handleEvent = (event: GameEvent) => {
const now = performance.now();
const setState = useStore.setState; const setState = useStore.setState;
const { state, audio, effects } = event; const { state, effects, audio } = event;
state.forEach(async (mutationData) => { if (state)
const { delay, mutation } = mutationData; state.forEach(async (mutationData) => {
if (delay) await sleep(delay); const { delay, mutation } = mutationData;
setState(mutation); if (delay) await sleep(delay);
}); setState(mutation);
});
if (effects) effects.forEach((effect) => effect()); if (effects) effects.forEach((effect) => effect());
if (audio) { if (audio)
audio.forEach(async (audio) => { audio.forEach(async (audio) => {
const { delay, sfx } = audio; const { delay, sfx } = audio;
if (delay) await sleep(delay); if (delay) await sleep(delay);
sfx.forEach((soundEffect) => playAudio(soundEffect)); sfx.forEach((soundEffect) => playAudio(soundEffect));
}); });
}
// console.log(performance.now() - now);
}; };
export default handleEvent; export default handleEvent;

View file

@ -1,84 +0,0 @@
import { playAudio, useStore } from "../../store";
import * as audio from "../../static/sfx";
const handleBootSceneEvent = (eventState: any) => {
const setState = useStore.setState;
switch (eventState.event) {
case "main_menu_up":
setState({ activeMainMenuComponent: "authorize_user" });
playAudio(audio.sound1);
break;
case "main_menu_down":
setState({ activeMainMenuComponent: "load_data" });
playAudio(audio.sound1);
break;
case "main_menu_load_data_select":
setState({ bootSubscene: "load_data" });
playAudio(audio.sound0);
setTimeout(() => setState({ promptVisible: true }), 500);
break;
case "main_menu_authorize_user_select":
setState({ authorizeUserLetterIdx: 0, bootSubscene: "authorize_user" });
playAudio(audio.sound0);
break;
case "authorize_user_up":
case "authorize_user_down":
case "authorize_user_left":
case "authorize_user_right":
setState({
authorizeUserLetterIdx: eventState.authorizeUserLetterIdx,
});
break;
case "authorize_user_back":
setState({
playerName: "",
bootSubscene: "main_menu",
});
playAudio(audio.sound29);
break;
case "update_player_name":
setState({
playerName: eventState.playerName,
});
playAudio(audio.sound0);
break;
case "update_player_name_denied":
playAudio(audio.sound0);
break;
case "remove_last_char":
setState({ playerName: eventState.playerName });
playAudio(audio.sound29);
break;
case "load_data_no":
setState({
bootSubscene: "main_menu",
promptVisible: false,
activePromptComponent: "no",
});
playAudio(audio.sound29);
break;
case "load_data_yes":
// todo check if data exists
setState({ loadSuccessful: true });
playAudio(audio.sound28);
//todo actually load
setTimeout(() => setState({ loadSuccessful: undefined }), 1200);
break;
case "prompt_left":
setState({ activePromptComponent: "yes" });
playAudio(audio.sound1);
break;
case "prompt_right":
setState({ activePromptComponent: "no" });
playAudio(audio.sound1);
break;
case "start_new_game":
setState({ currentScene: "main", intro: true });
break;
}
};
export default handleBootSceneEvent;

View file

@ -1,20 +0,0 @@
import { useStore } from "../../store";
const handleEndSceneEvent = (eventState: any) => {
const setState = useStore.setState;
switch (eventState.event) {
case "end_selection_up":
setState({ activeEndComponent: "end" });
break;
case "end_selection_down":
setState({ activeEndComponent: "continue" });
break;
case "end_continue_select":
setState({ currentScene: "change_disc", intro: true });
break;
case "end_end_select":
setState({ currentScene: "boot" });
}
};
export default handleEndSceneEvent;

View file

@ -1,84 +0,0 @@
import { playAudio, useStore } from "../../store";
import * as audio from "../../static/sfx";
const handleMediaSceneEvent = (eventState: any) => {
const setState = useStore.setState;
const setNodeViewed = useStore.getState().setNodeViewed;
const updateLeftSide = useStore.getState().updateLeftSide;
const updateRightSide = useStore.getState().updateRightSide;
const setPercentageElapsed = useStore.getState().setPercentageElapsed;
const playMedia = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement && mediaElement.paused) {
setPercentageElapsed(0);
mediaElement.play();
}
};
const exitMedia = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement) {
mediaElement.pause();
mediaElement.currentTime = 0;
}
};
switch (eventState.event) {
case "media_rightside_down":
case "media_rightside_up":
setState({
activeMediaComponent: eventState.newActiveComponent,
mediaWordPosStateIdx: eventState.wordPosStateIdx,
});
playAudio(audio.sound1);
break;
case "media_leftside_right":
updateLeftSide(
eventState.newActiveComponent,
eventState.lastActiveComponent
);
break;
case "media_rightside_left":
updateRightSide(
eventState.newActiveComponent,
eventState.lastActiveComponent
);
break;
case "media_play_select":
setNodeViewed(eventState.node.node_name, {
is_viewed: 1,
is_visible: 1,
});
playMedia();
break;
case "media_exit_select":
exitMedia();
playAudio(audio.sound29);
break;
case "media_word_select":
exitMedia();
playAudio(audio.sound29);
setState({
wordSelected: true,
activeLevel: eventState.level,
siteRot: [0, eventState.siteRotY, 0],
activeNode: eventState.node,
currentScene: "main",
});
break;
case "word_node_not_found":
exitMedia();
playAudio(audio.sound30);
setState({
mainSubscene: "not_found",
currentScene: "main",
});
}
};
export default handleMediaSceneEvent;

View file

@ -1,41 +0,0 @@
import { useStore } from "../../store";
const handleSsknSceneEvent = (eventState: any) => {
const setState = useStore.setState;
const setNodeViewed = useStore.getState().setNodeViewed;
switch (eventState.event) {
case "sskn_cancel_up":
setState({
activeSsknComponent: "ok",
});
break;
case "sskn_ok_down":
setState({
activeSsknComponent: "cancel",
});
break;
case "sskn_ok_select":
setState({
ssknLoading: true,
});
setNodeViewed(eventState.node.node_name, {
is_viewed: 1,
is_visible: 0,
});
// incrementSsknLvl();
setTimeout(() => setState({ currentScene: "main" }), 6000);
break;
case "sskn_cancel_select":
setState({
ssknLoading: false,
currentScene: "main",
activeSsknComponent: "ok",
});
}
};
export default handleSsknSceneEvent;

View file

@ -1,8 +1,25 @@
import authorize_user_letters from "../../resources/authorize_user_letters.json"; import authorize_user_letters from "../../resources/authorize_user_letters.json";
import handleNameSelection from "../../utils/handleNameSelection"; import handleNameSelection from "../../helpers/name-selection-helpers";
import { BootSceneContext } from "../../store"; import { BootSceneContext } from "../../store";
import {
changeMainMenuComponent,
changePromptComponent,
enterLoadData,
enterUserAuthorization,
exitLoadData,
exitUserAuthorization,
failUpdatePlayerName,
loadGame,
removePlayerNameLastChar,
startNewGame,
updateAuthorizeUserLetterIdx,
updatePlayerName,
} from "../eventTemplates";
import { GameEvent } from "../handleEvent";
const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => { const handleBootSceneKeyPress = (
bootSceneContext: BootSceneContext
): GameEvent | undefined => {
const { const {
keyPress, keyPress,
subscene, subscene,
@ -16,17 +33,15 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
if (promptVisible) { if (promptVisible) {
switch (keyPress) { switch (keyPress) {
case "LEFT": case "LEFT":
return { event: "prompt_left" }; return changePromptComponent({ activePromptComponent: "yes" });
case "RIGHT": case "RIGHT":
return { event: "prompt_right" }; return changePromptComponent({ activePromptComponent: "no" });
case "CIRCLE": case "CIRCLE":
switch (activePromptComponent) { switch (activePromptComponent) {
case "no": case "no":
return { event: "load_data_no" }; return exitLoadData;
case "yes": case "yes":
return { return loadGame();
event: "load_data_yes",
};
} }
} }
} else { } else {
@ -35,28 +50,34 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
switch (keyPress) { switch (keyPress) {
case "UP": case "UP":
case "DOWN": case "DOWN":
return { event: `main_menu_${keyPress.toLowerCase()}` }; const newComponent =
keyPress === "UP" ? "authorize_user" : "load_data";
return changeMainMenuComponent({
activeMainMenuComponent: newComponent,
});
case "CIRCLE": case "CIRCLE":
return { event: `main_menu_${activeMainMenuComponent}_select` }; switch (activeMainMenuComponent) {
case "authorize_user":
return enterUserAuthorization;
case "load_data":
return enterLoadData;
}
} }
break; break;
case "authorize_user": case "authorize_user":
switch (keyPress) { switch (keyPress) {
case "START": case "START":
if (playerName.length > 0) { if (playerName.length > 0) {
return { return startNewGame;
event: "start_new_game",
};
} }
break; return;
case "X": case "X":
if (playerName.length > 0) { if (playerName.length > 0) {
return { return removePlayerNameLastChar({
event: "remove_last_char",
playerName: playerName.slice(0, -1), playerName: playerName.slice(0, -1),
}; });
} else { } else {
return { event: "authorize_user_back" }; return exitUserAuthorization;
} }
case "LEFT": case "LEFT":
// if utmost left, break // if utmost left, break
@ -64,7 +85,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
[0, 13, 26, 39, 52].includes(authorizeUserLetterIdx) || [0, 13, 26, 39, 52].includes(authorizeUserLetterIdx) ||
authorizeUserLetterIdx === 15 authorizeUserLetterIdx === 15
) )
break; return;
// skip // skip
else if ( else if (
authorizeUserLetterIdx === 41 || authorizeUserLetterIdx === 41 ||
@ -74,19 +95,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx === 19 || authorizeUserLetterIdx === 19 ||
authorizeUserLetterIdx === 45 authorizeUserLetterIdx === 45
) { ) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_left",
authorizeUserLetterIdx: authorizeUserLetterIdx - 2, authorizeUserLetterIdx: authorizeUserLetterIdx - 2,
}; });
} else { } else {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_left",
authorizeUserLetterIdx: authorizeUserLetterIdx - 1, authorizeUserLetterIdx: authorizeUserLetterIdx - 1,
}; });
} }
case "RIGHT": case "RIGHT":
// if utmost right, break // if utmost right, break
if ([12, 25, 38, 51, 64].includes(authorizeUserLetterIdx)) break; if ([12, 25, 38, 51, 64].includes(authorizeUserLetterIdx)) return;
// skip empty // skip empty
else if ( else if (
authorizeUserLetterIdx === 39 || authorizeUserLetterIdx === 39 ||
@ -96,15 +115,13 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx === 43 || authorizeUserLetterIdx === 43 ||
authorizeUserLetterIdx === 17 authorizeUserLetterIdx === 17
) { ) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_right",
authorizeUserLetterIdx: authorizeUserLetterIdx + 2, authorizeUserLetterIdx: authorizeUserLetterIdx + 2,
}; });
} else { } else {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_right",
authorizeUserLetterIdx: authorizeUserLetterIdx + 1, authorizeUserLetterIdx: authorizeUserLetterIdx + 1,
}; });
} }
case "DOWN": case "DOWN":
// if utmost down, break // if utmost down, break
@ -113,7 +130,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx authorizeUserLetterIdx
) )
) { ) {
break; return;
// skip empty // skip empty
} else if ( } else if (
authorizeUserLetterIdx === 0 || authorizeUserLetterIdx === 0 ||
@ -123,20 +140,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx === 31 || authorizeUserLetterIdx === 31 ||
authorizeUserLetterIdx === 5 authorizeUserLetterIdx === 5
) { ) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_down",
authorizeUserLetterIdx: authorizeUserLetterIdx + 26, authorizeUserLetterIdx: authorizeUserLetterIdx + 26,
}; });
} else if (authorizeUserLetterIdx === 3) { } else if (authorizeUserLetterIdx === 3) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_down",
authorizeUserLetterIdx: authorizeUserLetterIdx + 52, authorizeUserLetterIdx: authorizeUserLetterIdx + 52,
}; });
} else { } else {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_down",
authorizeUserLetterIdx: authorizeUserLetterIdx + 13, authorizeUserLetterIdx: authorizeUserLetterIdx + 13,
}; });
} }
case "UP": case "UP":
// if utmost up, break // if utmost up, break
@ -145,7 +159,7 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx authorizeUserLetterIdx
) )
) { ) {
break; return;
// skip empty // skip empty
} else if ( } else if (
authorizeUserLetterIdx === 26 || authorizeUserLetterIdx === 26 ||
@ -154,20 +168,17 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
authorizeUserLetterIdx === 31 || authorizeUserLetterIdx === 31 ||
authorizeUserLetterIdx === 57 authorizeUserLetterIdx === 57
) { ) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_up",
authorizeUserLetterIdx: authorizeUserLetterIdx - 26, authorizeUserLetterIdx: authorizeUserLetterIdx - 26,
}; });
} else if (authorizeUserLetterIdx === 55) { } else if (authorizeUserLetterIdx === 55) {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_up",
authorizeUserLetterIdx: authorizeUserLetterIdx - 52, authorizeUserLetterIdx: authorizeUserLetterIdx - 52,
}; });
} else { } else {
return { return updateAuthorizeUserLetterIdx({
event: "authorize_user_up",
authorizeUserLetterIdx: authorizeUserLetterIdx - 13, authorizeUserLetterIdx: authorizeUserLetterIdx - 13,
}; });
} }
case "CIRCLE": case "CIRCLE":
const chosenCharacter = const chosenCharacter =
@ -178,10 +189,9 @@ const handleBootSceneKeyPress = (bootSceneContext: BootSceneContext) => {
const newName = handleNameSelection(playerName, chosenCharacter); const newName = handleNameSelection(playerName, chosenCharacter);
if (newName !== undefined) if (newName !== undefined)
return { event: "update_player_name", playerName: newName }; return updatePlayerName({ playerName: newName });
else return { event: "update_player_name_denied" }; else return failUpdatePlayerName;
} }
break;
} }
} }
}; };

View file

@ -1,15 +1,29 @@
import { EndSceneContext } from "../../store"; import { EndSceneContext } from "../../store";
import {
changeEndComponent,
continueGameAfterEnd,
endGame,
} from "../eventTemplates";
import { GameEvent } from "../handleEvent";
const handleEndSceneKeyPress = (endSceneContext: EndSceneContext) => { const handleEndSceneKeyPress = (
endSceneContext: EndSceneContext
): GameEvent | undefined => {
const { keyPress, selectionVisible, activeEndComponent } = endSceneContext; const { keyPress, selectionVisible, activeEndComponent } = endSceneContext;
if (selectionVisible) { if (selectionVisible) {
switch (keyPress) { switch (keyPress) {
case "UP": case "UP":
case "DOWN": case "DOWN":
return { event: `end_selection_${keyPress.toLowerCase()}` }; const newComponent = keyPress === "UP" ? "end" : "continue";
return changeEndComponent({ activeEndComponent: newComponent });
case "CIRCLE": case "CIRCLE":
return { event: `end_${activeEndComponent}_select` }; switch (activeEndComponent) {
case "end":
return endGame;
case "continue":
return continueGameAfterEnd;
}
} }
} }
}; };

View file

@ -3,7 +3,7 @@ import {
getNodeById, getNodeById,
isNodeVisible, isNodeVisible,
unknownNodeTemplate, unknownNodeTemplate,
} from "../../utils/node-utils"; } from "../../helpers/node-helpers";
import { MainSceneContext } from "../../store"; import { MainSceneContext } from "../../store";
import { import {
changeNode, changeNode,
@ -18,19 +18,25 @@ import {
exitPause, exitPause,
exitPrompt, exitPrompt,
explodeNode, explodeNode,
hideWordNotFound,
knockNode, knockNode,
knockNodeAndFall, knockNodeAndFall,
loadGame, loadGame,
pauseGame, pauseGame,
ripNode,
saveGame, saveGame,
selectLevel, selectLevel,
showAbout, showAbout,
showPermissionDenied, showPermissionDenied,
siteMoveHorizontal, siteMoveHorizontal,
siteMoveVertical, siteMoveVertical,
throwNode,
} from "../eventTemplates"; } from "../eventTemplates";
import { GameEvent } from "../handleEvent";
const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => { const handleMainSceneKeyPress = (
mainSceneContext: MainSceneContext
): GameEvent | undefined => {
const { const {
subscene, subscene,
selectedLevel, selectedLevel,
@ -47,6 +53,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
activePromptComponent, activePromptComponent,
gateLvl, gateLvl,
siteSaveState, siteSaveState,
wordNotFound,
} = mainSceneContext; } = mainSceneContext;
if (promptVisible) { if (promptVisible) {
@ -73,8 +80,6 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
activeLevel: level.toString().padStart(2, "0"), activeLevel: level.toString().padStart(2, "0"),
}, },
}; };
console.log(newSiteSaveState);
return changeSite({ return changeSite({
newActiveSite: siteToLoad, newActiveSite: siteToLoad,
newActiveNode: stateToLoad.activeNode, newActiveNode: stateToLoad.activeNode,
@ -92,6 +97,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
} else { } else {
switch (subscene) { switch (subscene) {
case "site": case "site":
if (wordNotFound) return hideWordNotFound;
switch (keyPress) { switch (keyPress) {
case "LEFT": case "LEFT":
case "RIGHT": { case "RIGHT": {
@ -169,8 +175,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
else return changeNode({ activeNode: newNode }); else return changeNode({ activeNode: newNode });
} }
case "CIRCLE": case "CIRCLE":
const eventAnimation = const eventAnimation = Math.random() < 0.4 ? throwNode : ripNode;
Math.random() < 0.4 ? "rip_node" : "throw_node";
const nodeType = activeNode.type; const nodeType = activeNode.type;
@ -181,7 +186,7 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
return; return;
if (activeNode.upgrade_requirement > ssknLvl) { if (activeNode.upgrade_requirement > ssknLvl) {
const rejectEvents = [explodeNode, knockNode, knockNodeAndFall]; const rejectEvents = [knockNodeAndFall, knockNode, explodeNode];
return rejectEvents[Math.floor(Math.random() * 3)]; return rejectEvents[Math.floor(Math.random() * 3)];
} }
@ -191,37 +196,19 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
case 4: case 4:
case 3: case 3:
case 5: case 5:
return { return eventAnimation({ currentScene: "media" });
event: `${eventAnimation}_media`,
mutations: { scene: "media" },
};
case 6: case 6:
if (activeNode.node_name.substr(0, 3) === "TaK") { if (activeNode.node_name.substr(0, 3) === "TaK") {
return { return eventAnimation({ currentScene: "tak" });
event: `${eventAnimation}_tak`,
mutations: { scene: "tak" },
};
} else { } else {
return { return eventAnimation({ currentScene: "media" });
event: `${eventAnimation}_media`,
mutations: { scene: "media" },
};
} }
case 8: case 8:
return { return eventAnimation({ currentScene: "gate" });
event: `${eventAnimation}_gate`,
mutations: { scene: "gate" },
};
case 7: case 7:
return { return eventAnimation({ currentScene: "gate" });
event: `${eventAnimation}_sskn`,
mutations: { scene: "sskn" },
};
case 9: case 9:
return { return eventAnimation({ currentScene: "polytan" });
event: `${eventAnimation}_polytan`,
mutations: { scene: "polytan" },
};
} }
break; break;
case "L2": case "L2":
@ -284,13 +271,12 @@ const handleMainSceneKeyPress = (mainSceneContext: MainSceneContext) => {
switch (keyPress) { switch (keyPress) {
case "UP": case "UP":
case "DOWN": case "DOWN":
const direction = keyPress.toLowerCase();
const components = ["load", "about", "change", "save", "exit"]; const components = ["load", "about", "change", "save", "exit"];
const newComponent = const newComponent =
components[ components[
components.indexOf(activePauseComponent) + components.indexOf(activePauseComponent) +
(direction === "up" ? -1 : 1) (keyPress === "UP" ? -1 : 1)
]; ];
if (newComponent) if (newComponent)

View file

@ -1,8 +1,19 @@
import { findNodeFromWord } from "../../utils/media-utils"; import { findNodeFromWord } from "../../helpers/media-helpers";
import { MediaSceneContext } from "../../store"; import { MediaSceneContext } from "../../store";
import { changeLeftMediaComponent, changeMediaSide } from "../eventTemplates"; import {
changeLeftMediaComponent,
changeMediaSide,
changeRightMediaComponent,
exitMedia,
playMedia,
selectWord,
wordNotFound,
} from "../eventTemplates";
import { GameEvent } from "../handleEvent";
const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => { const handleMediaSceneKeyPress = (
mediaSceneContext: MediaSceneContext
): GameEvent | undefined => {
const { const {
keyPress, keyPress,
activeMediaComponent, activeMediaComponent,
@ -19,8 +30,8 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
switch (keyPress) { switch (keyPress) {
case "UP": case "UP":
case "DOWN": { case "DOWN": {
const direction = keyPress.toLowerCase(); const newComponent = keyPress === "UP" ? "play" : "exit";
const newComponent = direction === "up" ? "play" : "exit";
return changeLeftMediaComponent({ activeComponent: newComponent }); return changeLeftMediaComponent({ activeComponent: newComponent });
} }
case "RIGHT": { case "RIGHT": {
@ -36,14 +47,9 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
case "CIRCLE": case "CIRCLE":
switch (activeMediaComponent) { switch (activeMediaComponent) {
case "play": case "play":
return { return playMedia;
event: "media_play_select",
node: activeNode,
};
case "exit": case "exit":
return { return exitMedia;
event: "media_play_select",
};
} }
} }
break; break;
@ -54,6 +60,7 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
wordPosStateIdx - 1 < 1 ? 6 : wordPosStateIdx - 1; wordPosStateIdx - 1 < 1 ? 6 : wordPosStateIdx - 1;
const newComponent = (() => { const newComponent = (() => {
switch (activeMediaComponent) { switch (activeMediaComponent) {
default:
case "fstWord": case "fstWord":
return "thirdWord"; return "thirdWord";
case "sndWord": case "sndWord":
@ -62,17 +69,17 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
return "sndWord"; return "sndWord";
} }
})(); })();
return { return changeRightMediaComponent({
event: "media_rightside_up", activeComponent: newComponent,
newActiveComponent: newComponent,
wordPosStateIdx: newWordPosStateIdx, wordPosStateIdx: newWordPosStateIdx,
}; });
} }
case "DOWN": { case "DOWN": {
const newWordPosStateIdx = const newWordPosStateIdx =
wordPosStateIdx + 1 > 6 ? 1 : wordPosStateIdx + 1; wordPosStateIdx + 1 > 6 ? 1 : wordPosStateIdx + 1;
const newComponent = (() => { const newComponent = (() => {
switch (activeMediaComponent) { switch (activeMediaComponent) {
default:
case "fstWord": case "fstWord":
return "sndWord"; return "sndWord";
case "sndWord": case "sndWord":
@ -82,11 +89,10 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
} }
})(); })();
return { return changeRightMediaComponent({
event: "media_rightside_down", activeComponent: newComponent,
newActiveComponent: newComponent,
wordPosStateIdx: newWordPosStateIdx, wordPosStateIdx: newWordPosStateIdx,
}; });
} }
case "LEFT": case "LEFT":
@ -111,9 +117,14 @@ const handleMediaSceneKeyPress = (mediaSceneContext: MediaSceneContext) => {
); );
if (data) { if (data) {
return { event: `media_word_select`, ...data }; const { node, level, siteRotY } = { ...data };
return selectWord({
activeNode: node,
activeLevel: level,
siteRot: [0, siteRotY, 0],
});
} else { } else {
return { event: `word_node_not_found` }; return wordNotFound;
} }
} }
} }

View file

@ -1,24 +1,42 @@
import { SsknSceneContext } from "../../store"; import { SsknSceneContext } from "../../store";
import { changeSsknComponent, exitSskn, upgradeSskn } from "../eventTemplates";
import { GameEvent } from "../handleEvent";
const handleSsknSceneKeyPress = (ssknSceneContext: SsknSceneContext) => { const handleSsknSceneKeyPress = (
const { keyPress, activeSsknComponent, activeNode } = ssknSceneContext; ssknSceneContext: SsknSceneContext
): GameEvent | undefined => {
const {
keyPress,
activeSsknComponent,
activeNode,
gameProgress,
ssknLvl,
} = ssknSceneContext;
switch (keyPress) { switch (keyPress) {
case "UP": case "UP":
case "DOWN": case "DOWN":
return { const direction = keyPress.toLowerCase();
event: `sskn_${activeSsknComponent}_${keyPress.toLowerCase()}`, const newComponent = direction === "up" ? "ok" : "cancel";
}; return changeSsknComponent({ activeSsknComponent: newComponent });
case "CIRCLE": case "CIRCLE":
if (activeSsknComponent === "ok") { switch (activeSsknComponent) {
return { case "ok":
event: `sskn_ok_select`, const newGameProgress = {
node: activeNode, ...gameProgress,
}; [activeNode.node_name]: {
} else { is_viewed: 1,
return { is_visible: 0,
event: `sskn_cancel_select`, },
}; };
const newSsknLvl = ssknLvl + 1;
return upgradeSskn({
gameProgress: newGameProgress,
ssknLvl: newSsknLvl,
});
case "cancel":
return exitSskn;
} }
} }
}; };

View file

@ -2,12 +2,13 @@ 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 { NodeData, SiteData } from "../components/MainScene/Site/Site"; import { NodeData, SiteData } from "../components/MainScene/Site/Site";
import { isNodeVisible } from "./node-utils"; import { isNodeVisible } from "./node-helpers";
import { ActiveSite, RightMediaComponent } from "../store";
export const findNodeFromWord = ( export const findNodeFromWord = (
wordLabel: string, wordLabel: RightMediaComponent,
activeNode: NodeData, activeNode: NodeData,
site: "a" | "b", site: ActiveSite,
gameProgress: any gameProgress: any
) => { ) => {
const labelToIdx = (() => { const labelToIdx = (() => {
@ -73,3 +74,18 @@ export const findNodeFromWord = (
level: chosenNode.id.substr(0, 2), level: chosenNode.id.substr(0, 2),
}; };
}; };
export const playMediaElement = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement && mediaElement.paused) mediaElement.play();
};
export const resetMediaElement = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement) {
mediaElement.pause();
mediaElement.currentTime = 0;
}
};

View file

@ -1,9 +1,5 @@
import { useStore } from "../store"; import { useStore } from "../store";
import sleep from "./sleep"; import sleep from "../utils/sleep";
const setActiveNodePos = useStore.getState().setNodePos;
const setActiveNodeRot = useStore.getState().setNodeRot;
const setActiveNodeAttributes = useStore.getState().setNodeAttributes;
const calculateCoordsBasedOnRotation = ( const calculateCoordsBasedOnRotation = (
x: number, x: number,
@ -18,6 +14,10 @@ const calculateCoordsBasedOnRotation = (
// throw a warning about an unhandled promise, and we dont care about that. // throw a warning about an unhandled promise, and we dont care about that.
// async/awaits here are used simply to improve readability, nothing else. // async/awaits here are used simply to improve readability, nothing else.
const setActiveNodePos = useStore.getState().setNodePos;
const setActiveNodeRot = useStore.getState().setNodeRot;
const setActiveNodeAttributes = useStore.getState().setNodeAttributes;
export const nodeThrowAnimation = () => { export const nodeThrowAnimation = () => {
(async () => { (async () => {
const siteRotY = useStore.getState().siteRot[1]; const siteRotY = useStore.getState().siteRot[1];

View file

@ -1,10 +1,10 @@
import { NodeData, SiteData } from "../components/MainScene/Site/Site"; import { NodeData, SiteData } from "../components/MainScene/Site/Site";
import node_matrices from "../resources/node_matrices.json";
import unlocked_nodes from "../resources/initial_progress.json";
import node_huds from "../resources/node_huds.json";
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 {GameProgress} from "../store"; import node_huds from "../resources/node_huds.json";
import unlocked_nodes from "../resources/initial_progress.json";
import node_matrices from "../resources/node_matrices.json";
import { GameProgress } from "../store";
export const generateInactiveNodes = ( export const generateInactiveNodes = (
visibleNodes: SiteData, visibleNodes: SiteData,

View file

@ -15,7 +15,6 @@ import sleep from "../utils/sleep";
const EndScene = () => { const EndScene = () => {
const mainCylinderRef = useRef<THREE.Object3D>(); const mainCylinderRef = useRef<THREE.Object3D>();
const setAudioAnalyser = useStore((state) => state.setAudioAnalyser);
const setSelectionVisible = useStore( const setSelectionVisible = useStore(
(state) => state.setEndSceneSelectionVisible (state) => state.setEndSceneSelectionVisible
); );
@ -110,7 +109,7 @@ const EndScene = () => {
mediaElement.load(); mediaElement.load();
mediaElement.play(); mediaElement.play();
} }
}, [setAudioAnalyser]); }, []);
return ( return (
<> <>
@ -128,9 +127,7 @@ const EndScene = () => {
<EndSphere position={[2, 1.7, -0.5]} outroAnim={sceneOutro} /> <EndSphere position={[2, 1.7, -0.5]} outroAnim={sceneOutro} />
<LainSpeak intro={isIntro} outro={isOutro} /> <LainSpeak intro={isIntro} outro={isOutro} />
</group> </group>
<group visible={showSelectionScreen}> <EndSelectionScreen visible={showSelectionScreen} />
<EndSelectionScreen />
</group>
</> </>
); );
}; };

View file

@ -7,18 +7,20 @@ import { useStore } from "../store";
const GateScene = () => { const GateScene = () => {
const gateLvl = useStore((state) => state.gateLvl); const gateLvl = useStore((state) => state.gateLvl);
const incrementGateLvl = useStore((state) => state.incrementGateLvl);
const [introAnim, setIntroAnim] = useState(true); const [introAnim, setIntroAnim] = useState(true);
const activeNodeName = useStore((state) => state.activeNode.node_name); const activeNodeName = useStore((state) => state.activeNode.node_name);
const setNodeViewed = useStore((state) => state.setNodeViewed); const setNodeViewed = useStore((state) => state.setNodeViewed);
useEffect(() => { useEffect(() => {
incrementGateLvl();
setNodeViewed(activeNodeName, { setNodeViewed(activeNodeName, {
is_viewed: 1, is_viewed: 1,
is_visible: 0, is_visible: 0,
}); });
setTimeout(() => setIntroAnim(false), 2500); setTimeout(() => setIntroAnim(false), 2500);
}, [activeNodeName, setNodeViewed]); }, [activeNodeName, incrementGateLvl, setNodeViewed]);
return ( return (
<perspectiveCamera position-z={3}> <perspectiveCamera position-z={3}>

View file

@ -14,7 +14,7 @@ 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 Popups from "../components/MainScene/Popups/Popups"; import Popups from "../components/MainScene/Popups/Popups";
import * as audio from "../static/sfx"; import * as audio from "../static/audio/sfx";
import Loading from "../components/Loading"; import Loading from "../components/Loading";
import usePrevious from "../hooks/usePrevious"; import usePrevious from "../hooks/usePrevious";
@ -26,6 +26,8 @@ const MainScene = () => {
const wordSelected = useStore((state) => state.wordSelected); const wordSelected = useStore((state) => state.wordSelected);
const setWordSelected = useStore((state) => state.setWordSelected); const setWordSelected = useStore((state) => state.setWordSelected);
const setInputCooldown = useStore((state) => state.setInputCooldown);
const wordNotFound = useStore((state) => state.wordNotFound);
useEffect(() => { useEffect(() => {
if (subscene === "pause") { if (subscene === "pause") {
@ -78,13 +80,27 @@ const MainScene = () => {
introWrapperRef.current.rotation.x -= 0.008; introWrapperRef.current.rotation.x -= 0.008;
} }
// introWrapperRef.current.position.z = THREE.MathUtils.lerp(
// introWrapperRef.current.position.z,
// intro ? 0 : -10,
// 0.01
// );
//
// introWrapperRef.current.rotation.x = THREE.MathUtils.lerp(
// introWrapperRef.current.rotation.x,
// intro ? 0 : Math.PI / 2,
// 0.01
// );
if ( if (
!introFinished &&
!( !(
introWrapperRef.current.rotation.x > 0 && introWrapperRef.current.rotation.x > 0 &&
introWrapperRef.current.position.z < 0 introWrapperRef.current.position.z < 0
) )
) { ) {
setIntroFinished(true); setIntroFinished(true);
setInputCooldown(0);
} }
} }
}); });
@ -97,7 +113,7 @@ const MainScene = () => {
<Pause /> <Pause />
<group visible={!paused}> <group visible={!paused}>
<group visible={!wordSelected && (intro ? introFinished : true)}> <group visible={!wordSelected && (intro ? introFinished : true)}>
<group visible={subscene !== "not_found"}> <group visible={!wordNotFound}>
<HUD /> <HUD />
<MainYellowTextAnimator /> <MainYellowTextAnimator />
</group> </group>

View file

@ -71,7 +71,7 @@ const MediaScene = () => {
useEffect(() => { useEffect(() => {
setLoaded(true); setLoaded(true);
setTimeout(() => setInputCooldown(false), 1000); setTimeout(() => setInputCooldown(500), 1000);
}, [setInputCooldown]); }, [setInputCooldown]);
return ( return (

View file

@ -4,7 +4,6 @@ import PolytanBackground from "../components/PolytanScene/PolytanBackground";
import { useStore } from "../store"; import { useStore } from "../store";
const PolytanScene = () => { const PolytanScene = () => {
const unlockedParts = useStore((state) => state.polytanUnlockedParts);
const setNodeViewed = useStore((state) => state.setNodeViewed); const setNodeViewed = useStore((state) => state.setNodeViewed);
const setPolytanPartUnlocked = useStore.getState().setPolytanPartUnlocked; const setPolytanPartUnlocked = useStore.getState().setPolytanPartUnlocked;
const activeNodeName = useStore((state) => state.activeNode.node_name); const activeNodeName = useStore((state) => state.activeNode.node_name);
@ -34,10 +33,10 @@ const PolytanScene = () => {
}, [activeNodeName, setNodeViewed, setPolytanPartUnlocked]); }, [activeNodeName, setNodeViewed, setPolytanPartUnlocked]);
return ( return (
<perspectiveCamera> <>
<PolytanBear unlockedParts={unlockedParts} /> <PolytanBear />
<PolytanBackground /> <PolytanBackground />
</perspectiveCamera> </>
); );
}; };

View file

@ -3,35 +3,103 @@ import { combine } from "zustand/middleware";
import * as THREE from "three"; import * as THREE from "three";
import game_progress from "./resources/initial_progress.json"; import game_progress from "./resources/initial_progress.json";
import { NodeData } from "./components/MainScene/Site/Site"; import { NodeData } from "./components/MainScene/Site/Site";
import { getNodeById } from "./utils/node-utils"; import { getNodeById } from "./helpers/node-helpers";
import site_a from "./resources/site_a.json"; import site_a from "./resources/site_a.json";
import { AudioAnalyser } from "three";
import MainScene from "./scenes/MainScene";
import MediaScene from "./scenes/MediaScene";
import IdleMediaScene from "./scenes/IdleMediaScene";
import GateScene from "./scenes/GateScene";
import BootScene from "./scenes/BootScene";
import SsknScene from "./scenes/SsknScene";
import PolytanScene from "./scenes/PolytanScene";
import TaKScene from "./scenes/TaKScene";
import ChangeDiscScene from "./scenes/ChangeDiscScene";
import EndScene from "./scenes/EndScene";
import React from "react";
export type GameProgress = typeof game_progress; export type GameProgress = typeof game_progress;
type NodeAttributes = {
interactedWith: boolean;
exploding: boolean;
shrinking: boolean;
visible: boolean;
};
export type GameScene =
| "main"
| "media"
| "tak"
| "sskn"
| "polytan"
| "idle_media"
| "gate"
| "boot"
| "change_disc"
| "end";
type MainSubscene = "site" | "pause" | "level_selection";
export type ActiveSite = "a" | "b";
export type EndComponent = "end" | "continue";
export type PauseComponent = "load" | "about" | "change" | "save" | "exit";
export type MediaSide = "left" | "right";
export type LeftMediaComponent = "play" | "exit";
export type RightMediaComponent = "fstWord" | "sndWord" | "thirdWord";
export type MediaComponent = LeftMediaComponent | RightMediaComponent;
export type SiteSaveState = {
a: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
b: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
};
export type SsknComponent = "ok" | "cancel";
export type MainMenuComponent = "authorize_user" | "load_data";
export type BootSubscene = "main_menu" | "load_data" | "authorize_user";
export type PromptComponent = "yes" | "no";
type PolytanBodyParts = {
body: boolean;
head: boolean;
leftArm: boolean;
rightArm: boolean;
leftLeg: boolean;
rightLeg: boolean;
};
type State = { type State = {
currentScene: string; currentScene: GameScene;
gameProgress: GameProgress; gameProgress: GameProgress;
mainSubscene: string; mainSubscene: MainSubscene;
intro: boolean; intro: boolean;
activeNode: NodeData; activeNode: NodeData;
activeNodePos: number[]; activeNodePos: number[];
activeNodeRot: number[]; activeNodeRot: number[];
activeNodeAttributes: { activeNodeAttributes: NodeAttributes;
interactedWith: boolean;
exploding: boolean;
shrinking: boolean;
visible: boolean;
};
// lain // lain
lainMoveState: string; lainMoveState: string;
// site // site
activeSite: "a" | "b"; activeSite: ActiveSite;
siteRot: number[]; siteRot: number[];
oldSiteRot: number[]; oldSiteRot: number[];
@ -43,23 +111,23 @@ type State = {
selectedLevel: number; selectedLevel: number;
// end scene // end scene
activeEndComponent: "end" | "continue"; activeEndComponent: EndComponent;
endSceneSelectionVisible: boolean; endSceneSelectionVisible: boolean;
// pause // pause
activePauseComponent: "load" | "about" | "change" | "save" | "exit"; activePauseComponent: PauseComponent;
pauseExitAnimation: boolean; pauseExitAnimation: boolean;
showingAbout: boolean; showingAbout: boolean;
permissionDenied: boolean; permissionDenied: boolean;
// media/media scene // media/media scene
audioAnalyser: any; audioAnalyser: AudioAnalyser | undefined;
mediaPercentageElapsed: number; mediaPercentageElapsed: number;
currentMediaSide: "left" | "right"; currentMediaSide: MediaSide;
activeMediaComponent: "play" | "exit" | "fstWord" | "sndWord" | "thirdWord"; activeMediaComponent: MediaComponent;
lastActiveMediaComponents: { lastActiveMediaComponents: {
left: "play" | "exit"; left: LeftMediaComponent;
right: "fstWord" | "sndWord" | "thirdWord"; right: RightMediaComponent;
}; };
mediaWordPosStateIdx: number; mediaWordPosStateIdx: number;
@ -72,19 +140,12 @@ type State = {
idleNodeName: string | undefined; idleNodeName: string | undefined;
// sskn scene // sskn scene
activeSsknComponent: "ok" | "cancel"; activeSsknComponent: SsknComponent;
ssknLoading: boolean; ssknLoading: boolean;
ssknLvl: number; ssknLvl: number;
// polytan scene // polytan scene
polytanUnlockedParts: { polytanUnlockedParts: PolytanBodyParts;
body: boolean;
head: boolean;
leftArm: boolean;
rightArm: boolean;
leftLeg: boolean;
rightLeg: boolean;
};
// gate scene // gate scene
gateLvl: number; gateLvl: number;
@ -93,40 +154,32 @@ type State = {
playerName: string; playerName: string;
// boot scene // boot scene
activeMainMenuComponent: "authorize_user" | "load_data"; activeMainMenuComponent: MainMenuComponent;
authorizeUserLetterIdx: number; authorizeUserLetterIdx: number;
bootSubscene: "main_menu" | "load_data" | "authorize_user"; bootSubscene: BootSubscene;
// prompt // prompt
promptVisible: boolean; promptVisible: boolean;
activePromptComponent: "yes" | "no"; activePromptComponent: PromptComponent;
// status notifiers // status notifiers
loadSuccessful: boolean | undefined; loadSuccessful: boolean | undefined;
saveSuccessful: boolean | undefined; saveSuccessful: boolean | undefined;
// save state // word not found notification thing
siteSaveState: { wordNotFound: boolean;
a: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
b: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
};
inputCooldown: boolean; // save state
siteSaveState: SiteSaveState;
inputCooldown: number;
}; };
export const useStore = create( export const useStore = create(
combine( combine(
{ {
// scene data // scene data
currentScene: "media", currentScene: "main",
// game progress // game progress
gameProgress: game_progress, gameProgress: game_progress,
@ -232,6 +285,9 @@ export const useStore = create(
loadSuccessful: undefined, loadSuccessful: undefined,
saveSuccessful: undefined, saveSuccessful: undefined,
// word not found notification thing
wordNotFound: false,
// save states for loading the game/changing sites // save states for loading the game/changing sites
siteSaveState: { siteSaveState: {
a: { a: {
@ -252,13 +308,11 @@ export const useStore = create(
}, },
}, },
inputCooldown: false, inputCooldown: -1,
} as State, } as State,
(set) => ({ (set) => ({
// scene data setters setScene: (to: GameScene) => set(() => ({ currentScene: to })),
setScene: (to: string) => set(() => ({ currentScene: to })),
// node setters
setNodePos: (to: number[]) => set(() => ({ activeNodePos: to })), setNodePos: (to: number[]) => set(() => ({ activeNodePos: to })),
setNodeRot: (to: number[]) => set(() => ({ activeNodeRot: to })), setNodeRot: (to: number[]) => set(() => ({ activeNodeRot: to })),
setNodeAttributes: ( setNodeAttributes: (
@ -269,102 +323,6 @@ export const useStore = create(
activeNodeAttributes: { ...state.activeNodeAttributes, [at]: to }, activeNodeAttributes: { ...state.activeNodeAttributes, [at]: to },
})), })),
// lain setters
setLainMoveState: (to: string) => set(() => ({ lainMoveState: to })),
// site setters
setSiteRotX: (to: number) =>
set((prev) => {
const nextRot = [...prev.siteRot];
nextRot[0] = to;
return { siteRot: nextRot };
}),
// end scene setters
setEndSceneSelectionVisible: (to: boolean) =>
set(() => ({ endSceneSelectionVisible: to })),
// pause setters
setShowingAbout: (to: boolean) => set(() => ({ showingAbout: to })),
// media scene setters
setAudioAnalyser: (to: any) => set(() => ({ audioAnalyser: to })),
setPercentageElapsed: (to: number) =>
set(() => ({ mediaPercentageElapsed: to })),
setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })),
updateLeftSide: (
newActiveComponent: "fstWord" | "sndWord" | "thirdWord",
lastActiveComponent: "exit" | "play"
) =>
set((state) => ({
activeMediaComponent: newActiveComponent,
lastActiveMediaComponents: {
...state.lastActiveMediaComponents,
left: lastActiveComponent,
},
currentMediaSide: "right",
})),
updateRightSide: (
newActiveComponent: "play" | "exit",
lastActiveComponent: "fstWord" | "sndWord" | "thirdWord"
) =>
set((state) => ({
activeMediaComponent: newActiveComponent,
lastActiveMediaComponents: {
...state.lastActiveMediaComponents,
right: lastActiveComponent,
},
currentMediaSide: "left",
})),
// idle media setters
setIdleStarting: (to: boolean) => set(() => ({ idleStarting: to })),
setIdleScene: (to: {
images: { "1": string; "2": string; "3": string } | undefined;
media: string | undefined;
nodeName: string | undefined;
}) =>
set(() => ({
idleMedia: to.media,
idleImages: to.images,
idleNodeName: to.nodeName,
})),
//polytan setters
setPolytanPartUnlocked: (bodyPart: string) =>
set((state) => ({
polytanUnlockedParts: {
...state.polytanUnlockedParts,
[bodyPart]: true,
},
})),
changeSite: (to: "a" | "b") =>
set((state) => {
const newState = state.siteSaveState[to];
return {
currentScene: "change_disc",
promptVisible: false,
activePromptComponent: "no",
mainSubscene: "site",
// load new state
activeSite: to,
activeNode: newState.activeNode,
siteRot: newState.siteRot,
activeLevel: newState.activeLevel,
// save current state
siteSaveState: {
...state.siteSaveState,
[to === "a" ? "b" : "a"]: {
activeNode: state.activeNode,
siteRot: [0, state.siteRot[1], 0],
activeLevel: state.activeLevel,
},
},
};
}),
// progress setters
setNodeViewed: ( setNodeViewed: (
nodeName: string, nodeName: string,
to: { is_viewed: number; is_visible: number } to: { is_viewed: number; is_visible: number }
@ -376,13 +334,37 @@ export const useStore = create(
}, },
})), })),
setInputCooldown: (to: boolean) => set(() => ({ inputCooldown: to })), setLainMoveState: (to: string) => set(() => ({ lainMoveState: to })),
setEndSceneSelectionVisible: (to: boolean) =>
set(() => ({ endSceneSelectionVisible: to })),
setShowingAbout: (to: boolean) => set(() => ({ showingAbout: to })),
setAudioAnalyser: (to: AudioAnalyser) =>
set(() => ({ audioAnalyser: to })),
setPercentageElapsed: (to: number) =>
set(() => ({ mediaPercentageElapsed: to })),
setWordSelected: (to: boolean) => set(() => ({ wordSelected: to })),
setPolytanPartUnlocked: (bodyPart: string) =>
set((state) => ({
polytanUnlockedParts: {
...state.polytanUnlockedParts,
[bodyPart]: true,
},
})),
setInputCooldown: (to: number) => set(() => ({ inputCooldown: to })),
incrementGateLvl: () => set((state) => ({ gateLvl: state.gateLvl + 1 })),
incrementSsknLvl: () => set((state) => ({ ssknLvl: state.ssknLvl + 1 })),
}) })
) )
); );
type PromptContext = { type PromptContext = {
activePromptComponent: "yes" | "no"; activePromptComponent: PromptComponent;
promptVisible: boolean; promptVisible: boolean;
}; };
@ -401,25 +383,15 @@ export interface MainSceneContext extends PromptContext {
activeNode: NodeData; activeNode: NodeData;
showingAbout: boolean; showingAbout: boolean;
level: number; level: number;
activePauseComponent: "load" | "about" | "change" | "save" | "exit"; activePauseComponent: PauseComponent;
gameProgress: GameProgress; gameProgress: GameProgress;
gateLvl: number; gateLvl: number;
subscene: string; subscene: string;
siteRotY: number; siteRotY: number;
activeSite: "a" | "b"; activeSite: ActiveSite;
selectedLevel: number; selectedLevel: number;
siteSaveState: { wordNotFound: boolean;
a: { siteSaveState: SiteSaveState;
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
b: {
activeNode: NodeData;
siteRot: number[];
activeLevel: string;
};
};
} }
export const getMainSceneContext = (keyPress: string): MainSceneContext => { export const getMainSceneContext = (keyPress: string): MainSceneContext => {
@ -440,13 +412,16 @@ export const getMainSceneContext = (keyPress: string): MainSceneContext => {
showingAbout: state.showingAbout, showingAbout: state.showingAbout,
gateLvl: state.gateLvl, gateLvl: state.gateLvl,
siteSaveState: state.siteSaveState, siteSaveState: state.siteSaveState,
wordNotFound: state.wordNotFound,
}; };
}; };
export type SsknSceneContext = { export type SsknSceneContext = {
keyPress: string; keyPress: string;
activeSsknComponent: "ok" | "cancel"; activeSsknComponent: SsknComponent;
activeNode: NodeData; activeNode: NodeData;
gameProgress: GameProgress;
ssknLvl: number;
}; };
export const getSsknSceneContext = (keyPress: string): SsknSceneContext => { export const getSsknSceneContext = (keyPress: string): SsknSceneContext => {
@ -455,21 +430,23 @@ export const getSsknSceneContext = (keyPress: string): SsknSceneContext => {
keyPress: keyPress, keyPress: keyPress,
activeSsknComponent: state.activeSsknComponent, activeSsknComponent: state.activeSsknComponent,
activeNode: state.activeNode, activeNode: state.activeNode,
gameProgress: state.gameProgress,
ssknLvl: state.ssknLvl,
}; };
}; };
export type MediaSceneContext = { export type MediaSceneContext = {
keyPress: string; keyPress: string;
wordPosStateIdx: number; wordPosStateIdx: number;
currentMediaSide: "left" | "right"; currentMediaSide: MediaSide;
activeMediaComponent: "play" | "exit" | "fstWord" | "sndWord" | "thirdWord"; activeMediaComponent: MediaComponent;
activeNode: NodeData; activeNode: NodeData;
gameProgress: GameProgress; gameProgress: GameProgress;
lastActiveMediaComponents: { lastActiveMediaComponents: {
left: "play" | "exit"; left: LeftMediaComponent;
right: "fstWord" | "sndWord" | "thirdWord"; right: RightMediaComponent;
}; };
activeSite: "a" | "b"; activeSite: ActiveSite;
}; };
export const getMediaSceneContext = (keyPress: string): MediaSceneContext => { export const getMediaSceneContext = (keyPress: string): MediaSceneContext => {
@ -490,8 +467,8 @@ export const getMediaSceneContext = (keyPress: string): MediaSceneContext => {
export interface BootSceneContext extends PromptContext { export interface BootSceneContext extends PromptContext {
keyPress: string; keyPress: string;
playerName: string; playerName: string;
subscene: "main_menu" | "load_data" | "authorize_user"; subscene: BootSubscene;
activeMainMenuComponent: "load_data" | "authorize_user"; activeMainMenuComponent: MainMenuComponent;
authorizeUserLetterIdx: number; authorizeUserLetterIdx: number;
} }
@ -510,7 +487,7 @@ export const getBootSceneContext = (keyPress: string): BootSceneContext => {
export type EndSceneContext = { export type EndSceneContext = {
keyPress: string; keyPress: string;
activeEndComponent: "end" | "continue"; activeEndComponent: EndComponent;
selectionVisible: boolean; selectionVisible: boolean;
}; };

View file

@ -0,0 +1,2 @@
import { findNodeFromWord } from "../helpers/media-helpers";