mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
progress saving implemented, bug fixes and improvements
This commit is contained in:
parent
d92e259bcc
commit
0956ae9fba
27 changed files with 455 additions and 199 deletions
|
@ -5,18 +5,12 @@
|
||||||
<link rel="icon" type="image/png" href="icon.png" />
|
<link rel="icon" type="image/png" href="icon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: #000;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript
|
<noscript
|
||||||
>do you actually believe i could rewrite this in plain html css or
|
>do you actually believe i could rewrite this in plain html css or
|
||||||
smoetihng</noscript
|
smoetihng</noscript
|
||||||
>
|
>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r119/three.min.js"></script>
|
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -14,7 +14,6 @@ import ChangeDiscScene from "./scenes/ChangeDiscScene";
|
||||||
import EndScene from "./scenes/EndScene";
|
import EndScene from "./scenes/EndScene";
|
||||||
import IdleMediaScene from "./scenes/IdleMediaScene";
|
import IdleMediaScene from "./scenes/IdleMediaScene";
|
||||||
import KeyPressHandler from "./components/KeyPressHandler";
|
import KeyPressHandler from "./components/KeyPressHandler";
|
||||||
import Preloader from "./components/Preloader";
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const currentScene = useStore((state) => state.currentScene);
|
const currentScene = useStore((state) => state.currentScene);
|
||||||
|
@ -35,6 +34,7 @@ const App = () => {
|
||||||
tak: <TaKScene />,
|
tak: <TaKScene />,
|
||||||
change_disc: <ChangeDiscScene />,
|
change_disc: <ChangeDiscScene />,
|
||||||
end: <EndScene />,
|
end: <EndScene />,
|
||||||
|
null: <></>,
|
||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
9
src/__tests__/save-state.test.ts
Normal file
9
src/__tests__/save-state.test.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { getCurrentUserState } from "../store";
|
||||||
|
|
||||||
|
it("Checks if setting state on localStorage works", () => {
|
||||||
|
const spy = jest.spyOn(Storage.prototype, "setItem");
|
||||||
|
const saveState = JSON.stringify(getCurrentUserState());
|
||||||
|
localStorage.setItem("lainSaveState", saveState);
|
||||||
|
expect(spy).toHaveBeenCalledTimes(1);
|
||||||
|
expect(localStorage.getItem("lainSaveState")).toEqual(saveState);
|
||||||
|
});
|
|
@ -8,7 +8,7 @@ import {
|
||||||
playAudio,
|
playAudio,
|
||||||
useStore,
|
useStore,
|
||||||
} from "../store";
|
} from "../store";
|
||||||
import { getKeyCodeAssociation } from "../utils/getKey";
|
import { getKeyCodeAssociation } from "../utils/parseUserInput";
|
||||||
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";
|
||||||
|
@ -75,9 +75,9 @@ const KeyPressHandler = () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
keyPress
|
keyPress &&
|
||||||
// now > timeSinceLastKeyPress.current + inputCooldown &&
|
now > timeSinceLastKeyPress.current + inputCooldown &&
|
||||||
// inputCooldown !== -1
|
inputCooldown !== -1
|
||||||
) {
|
) {
|
||||||
if (scene === "main") {
|
if (scene === "main") {
|
||||||
lainIdleCounter.current = now;
|
lainIdleCounter.current = now;
|
||||||
|
|
|
@ -21,14 +21,26 @@ const Loading = memo(() => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<sprite scale={[5, 5, 5]}>
|
<sprite scale={[5, 5, 5]} renderOrder={999}>
|
||||||
<spriteMaterial attach="material" color={0x000000} />
|
<spriteMaterial attach="material" color={0x000000} depthTest={false} />
|
||||||
</sprite>
|
</sprite>
|
||||||
<sprite scale={[0.35, 0.6, 0.35]} position={[0, 0.2, 0]}>
|
<sprite
|
||||||
<spriteMaterial attach="material" map={loadingTex} />
|
scale={[0.35, 0.6, 0.35]}
|
||||||
|
position={[0, 0.2, 0]}
|
||||||
|
renderOrder={1000}
|
||||||
|
>
|
||||||
|
<spriteMaterial attach="material" map={loadingTex} depthTest={false} />
|
||||||
</sprite>
|
</sprite>
|
||||||
<sprite scale={[0.4, 0.6, 0.4]} position={[0, -0.5, 0]}>
|
<sprite
|
||||||
<spriteMaterial attach="material" map={lifeInstinctTex} />
|
scale={[0.4, 0.6, 0.4]}
|
||||||
|
position={[0, -0.5, 0]}
|
||||||
|
renderOrder={1000}
|
||||||
|
>
|
||||||
|
<spriteMaterial
|
||||||
|
attach="material"
|
||||||
|
map={lifeInstinctTex}
|
||||||
|
depthTest={false}
|
||||||
|
/>
|
||||||
</sprite>
|
</sprite>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useEffect, useRef } from "react";
|
import React, { useEffect, useMemo, useRef } from "react";
|
||||||
import level_selection_font from "../../static/sprite/select_level_font.png";
|
import level_selection_font from "../../static/sprite/select_level_font.png";
|
||||||
import verticalHud from "../../static/sprite/select_level_hud_vertical.png";
|
import verticalHud from "../../static/sprite/select_level_hud_vertical.png";
|
||||||
import horizontalHud from "../../static/sprite/select_level_hud_horizontal.png";
|
import horizontalHud from "../../static/sprite/select_level_hud_horizontal.png";
|
||||||
|
|
|
@ -67,7 +67,7 @@ const Site = (props: SiteProps) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={null}>
|
||||||
<a.group rotation-x={rotXState.x}>
|
<a.group rotation-x={rotXState.x}>
|
||||||
<a.group rotation-y={rotYState.y} position-y={posState.y}>
|
<a.group rotation-y={rotYState.y} position-y={posState.y}>
|
||||||
<ActiveLevelNodes visibleNodes={visibleNodes} />
|
<ActiveLevelNodes visibleNodes={visibleNodes} />
|
||||||
|
|
79
src/components/MainScene/Starfield/IntroStar.tsx
Normal file
79
src/components/MainScene/Starfield/IntroStar.tsx
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import React, { useMemo, useRef } from "react";
|
||||||
|
import { a } from "@react-spring/three";
|
||||||
|
import * as THREE from "three";
|
||||||
|
import { useFrame } from "react-three-fiber";
|
||||||
|
|
||||||
|
type IntroStarProps = {
|
||||||
|
position: number[];
|
||||||
|
color: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const IntroStar = (props: IntroStarProps) => {
|
||||||
|
const uniforms = useMemo(
|
||||||
|
() => ({
|
||||||
|
color1: {
|
||||||
|
value: new THREE.Color("white"),
|
||||||
|
},
|
||||||
|
color2: {
|
||||||
|
value: new THREE.Color(props.color),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
[props.color]
|
||||||
|
);
|
||||||
|
|
||||||
|
const vertexShader = `
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vUv = uv;
|
||||||
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const fragmentShader = `
|
||||||
|
uniform vec3 color1;
|
||||||
|
uniform vec3 color2;
|
||||||
|
uniform float alpha;
|
||||||
|
|
||||||
|
varying vec2 vUv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float alpha = smoothstep(0.0, 1.0, vUv.y);
|
||||||
|
float colorMix = smoothstep(1.0, 2.0, 1.8);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(mix(color1, color2, colorMix), alpha) * 0.8;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const starRef = useRef<THREE.Object3D>();
|
||||||
|
|
||||||
|
const amp = useRef(Math.random() / 10);
|
||||||
|
|
||||||
|
useFrame(() => {
|
||||||
|
if (starRef.current && starRef.current.visible) {
|
||||||
|
starRef.current.position.y += 0.25 + amp.current;
|
||||||
|
if (starRef.current.position.y > 40) starRef.current.visible = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<mesh
|
||||||
|
position={props.position as [number, number, number]}
|
||||||
|
scale={[0.01, 2, 0.01]}
|
||||||
|
renderOrder={-1}
|
||||||
|
ref={starRef}
|
||||||
|
>
|
||||||
|
<boxBufferGeometry attach="geometry" args={[1, 1, 1]} />
|
||||||
|
<a.shaderMaterial
|
||||||
|
attach="material"
|
||||||
|
fragmentShader={fragmentShader}
|
||||||
|
vertexShader={vertexShader}
|
||||||
|
transparent={true}
|
||||||
|
depthWrite={false}
|
||||||
|
uniforms={uniforms}
|
||||||
|
/>
|
||||||
|
</mesh>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default IntroStar;
|
|
@ -1,26 +1,27 @@
|
||||||
import React, { useRef } from "react";
|
import React, { useMemo, useRef } from "react";
|
||||||
import { a } from "@react-spring/three";
|
import { a } from "@react-spring/three";
|
||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import { useFrame } from "react-three-fiber";
|
import { useFrame } from "react-three-fiber";
|
||||||
|
import lerp from "../../../utils/lerp";
|
||||||
|
|
||||||
type StarProps = {
|
type StarProps = {
|
||||||
position: number[];
|
position: number[];
|
||||||
color: string;
|
color: string;
|
||||||
introStar?: boolean;
|
shouldIntro: boolean;
|
||||||
shouldIntro?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Star = (props: StarProps) => {
|
const Star = (props: StarProps) => {
|
||||||
const uniformConstructor = (col: string) => {
|
const uniforms = useMemo(
|
||||||
return {
|
() => ({
|
||||||
color1: {
|
color1: {
|
||||||
value: new THREE.Color("white"),
|
value: new THREE.Color("white"),
|
||||||
},
|
},
|
||||||
color2: {
|
color2: {
|
||||||
value: new THREE.Color(col),
|
value: new THREE.Color(props.color),
|
||||||
},
|
},
|
||||||
};
|
}),
|
||||||
};
|
[props.color]
|
||||||
|
);
|
||||||
|
|
||||||
const vertexShader = `
|
const vertexShader = `
|
||||||
varying vec2 vUv;
|
varying vec2 vUv;
|
||||||
|
@ -54,17 +55,11 @@ const Star = (props: StarProps) => {
|
||||||
|
|
||||||
useFrame(() => {
|
useFrame(() => {
|
||||||
if (starRef.current) {
|
if (starRef.current) {
|
||||||
if (props.introStar) {
|
|
||||||
starRef.current.position.y += 0.25 + amp.current;
|
|
||||||
} else {
|
|
||||||
if (starRef.current.position.y > 4) {
|
if (starRef.current.position.y > 4) {
|
||||||
starRef.current.position.y = props.position[1];
|
starRef.current.position.y = props.position[1];
|
||||||
}
|
}
|
||||||
starRef.current.position.y += 0.01 + amp.current + introAmpRef.current;
|
starRef.current.position.y += 0.01 + amp.current + introAmpRef.current;
|
||||||
if (introAmpRef.current > 0) {
|
introAmpRef.current = lerp(introAmpRef.current, 0, 0.01);
|
||||||
introAmpRef.current -= 0.004;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,7 +77,7 @@ const Star = (props: StarProps) => {
|
||||||
vertexShader={vertexShader}
|
vertexShader={vertexShader}
|
||||||
transparent={true}
|
transparent={true}
|
||||||
depthWrite={false}
|
depthWrite={false}
|
||||||
uniforms={uniformConstructor(props.color)}
|
uniforms={uniforms}
|
||||||
/>
|
/>
|
||||||
</mesh>
|
</mesh>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import React, { memo, useEffect, useMemo, useState } from "react";
|
import React, { memo, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import Star from "./Star";
|
import Star from "./Star";
|
||||||
|
import IntroStar from "./IntroStar";
|
||||||
|
|
||||||
type StarfieldProps = {
|
type StarfieldProps = {
|
||||||
shouldIntro: boolean;
|
shouldIntro: boolean;
|
||||||
|
@ -34,17 +35,11 @@ const Starfield = memo((props: StarfieldProps) => {
|
||||||
].map((x) =>
|
].map((x) =>
|
||||||
Array.from({ length: x }, () => [
|
Array.from({ length: x }, () => [
|
||||||
lcgInstance() / 1000000050,
|
lcgInstance() / 1000000050,
|
||||||
lcgInstance() / 100000099 - 15,
|
lcgInstance() / 100000059 + 5,
|
||||||
lcgInstance() / 1000000050,
|
lcgInstance() / 1000000050,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
const [introVisible, setIntroVisible] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeout(() => setIntroVisible(false), 3200);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<group position={[0, -1, 2]} visible={props.mainVisible}>
|
<group position={[0, -1, 2]} visible={props.mainVisible}>
|
||||||
|
@ -101,20 +96,18 @@ const Starfield = memo((props: StarfieldProps) => {
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
</group>
|
</group>
|
||||||
{introVisible && props.shouldIntro ? (
|
{props.shouldIntro && (
|
||||||
<group position={[-2, -15, -30]} rotation={[Math.PI / 3, 0, 0]}>
|
<group position={[-2, -15, -30]} rotation={[Math.PI / 3, 0, 0]}>
|
||||||
{posesBlueFromBottom.map((poses, idx) => (
|
{posesBlueFromBottom.map((poses, idx) => (
|
||||||
<Star position={poses} color={"blue"} key={idx} introStar={true} />
|
<IntroStar position={poses} color={"blue"} key={idx} />
|
||||||
))}
|
))}
|
||||||
{posesWhiteFromBottom.map((poses, idx) => (
|
{posesWhiteFromBottom.map((poses, idx) => (
|
||||||
<Star position={poses} color={"white"} key={idx} introStar={true} />
|
<IntroStar position={poses} color={"white"} key={idx} />
|
||||||
))}
|
))}
|
||||||
{posesCyanFromBottom.map((poses, idx) => (
|
{posesCyanFromBottom.map((poses, idx) => (
|
||||||
<Star position={poses} color={"cyan"} key={idx} introStar={true} />
|
<IntroStar position={poses} color={"cyan"} key={idx} />
|
||||||
))}
|
))}
|
||||||
</group>
|
</group>
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -80,7 +80,7 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => {
|
||||||
|
|
||||||
const subscene = useStore((state) => state.mainSubscene);
|
const subscene = useStore((state) => state.mainSubscene);
|
||||||
const scene = useStore((state) => state.currentScene);
|
const scene = useStore((state) => state.currentScene);
|
||||||
const prevData = usePrevious({ scene, subscene });
|
const prevData = usePrevious({ scene, subscene, activeNode });
|
||||||
const [lastMediaLeftComponent, setLastMediaLeftComponent] = useState("play");
|
const [lastMediaLeftComponent, setLastMediaLeftComponent] = useState("play");
|
||||||
|
|
||||||
const [shrinkState, set] = useSpring(() => ({
|
const [shrinkState, set] = useSpring(() => ({
|
||||||
|
@ -91,7 +91,13 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
subscene === "pause" ||
|
subscene === "pause" ||
|
||||||
(subscene === "site" && prevData?.subscene === "pause")
|
(subscene === "site" && prevData?.subscene === "pause") ||
|
||||||
|
(activeNode === prevData?.activeNode &&
|
||||||
|
!(
|
||||||
|
subscene === "level_selection" ||
|
||||||
|
color === "orange" ||
|
||||||
|
scene === "media"
|
||||||
|
))
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
if (scene === "main" && prevData?.scene === "main") {
|
if (scene === "main" && prevData?.scene === "main") {
|
||||||
|
@ -123,6 +129,7 @@ const BigLetter = memo((props: { letter: string; letterIdx: number }) => {
|
||||||
lastMediaLeftComponent,
|
lastMediaLeftComponent,
|
||||||
prevData?.scene,
|
prevData?.scene,
|
||||||
prevData?.subscene,
|
prevData?.subscene,
|
||||||
|
prevData?.activeNode,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -9,14 +9,23 @@ import {
|
||||||
import { playMediaElement, resetMediaElement } from "../helpers/media-helpers";
|
import { playMediaElement, resetMediaElement } from "../helpers/media-helpers";
|
||||||
import {
|
import {
|
||||||
ActiveSite,
|
ActiveSite,
|
||||||
EndComponent, GameProgress, GameScene,
|
EndComponent,
|
||||||
|
GameScene,
|
||||||
LeftMediaComponent,
|
LeftMediaComponent,
|
||||||
MediaComponent, MediaSide, NodeData,
|
MediaComponent,
|
||||||
|
MediaSide,
|
||||||
|
NodeData,
|
||||||
PromptComponent,
|
PromptComponent,
|
||||||
RightMediaComponent,
|
RightMediaComponent,
|
||||||
SiteSaveState,
|
SiteSaveState,
|
||||||
SsknComponent
|
SsknComponent,
|
||||||
|
UserSaveState,
|
||||||
} from "../types/types";
|
} from "../types/types";
|
||||||
|
import { saveUserProgress, useStore } from "../store";
|
||||||
|
|
||||||
|
const setNodeViewed = useStore.getState().setNodeViewed;
|
||||||
|
const resetMediaScene = useStore.getState().resetMediaScene;
|
||||||
|
const loadUserSaveState = useStore.getState().loadUserSaveState;
|
||||||
|
|
||||||
export const siteMoveHorizontal = (calculatedState: {
|
export const siteMoveHorizontal = (calculatedState: {
|
||||||
lainMoveAnimation: string;
|
lainMoveAnimation: string;
|
||||||
|
@ -332,28 +341,64 @@ export const exitPrompt = {
|
||||||
audio: [{ sfx: [audio.sound28] }],
|
audio: [{ sfx: [audio.sound28] }],
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo actually save
|
export const saveGame = (calculatedState: {
|
||||||
export const saveGame = () => ({
|
userSaveState: UserSaveState;
|
||||||
|
}) => ({
|
||||||
state: [
|
state: [
|
||||||
{ mutation: { saveSuccessful: true, inputCooldown: 1200 } },
|
{ mutation: { saveSuccessful: true, inputCooldown: 1200 } },
|
||||||
{
|
{
|
||||||
mutation: { saveSuccessful: undefined },
|
mutation: {
|
||||||
|
saveSuccessful: undefined,
|
||||||
|
promptVisible: false,
|
||||||
|
activePromptComponent: "no",
|
||||||
|
},
|
||||||
delay: 1200,
|
delay: 1200,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
audio: [{ sfx: [audio.sound28] }],
|
audio: [{ sfx: [audio.sound28] }],
|
||||||
|
effects: [() => saveUserProgress(calculatedState.userSaveState)],
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo actually load
|
export const loadGameFail = {
|
||||||
export const loadGame = () => ({
|
state: [
|
||||||
|
{
|
||||||
|
mutation: {
|
||||||
|
loadSuccessful: false,
|
||||||
|
inputCooldown: 1200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ mutation: { loadSuccessful: undefined }, delay: 1200 },
|
||||||
|
],
|
||||||
|
audio: [{ sfx: [audio.sound28] }],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loadGame = (calculatedState: {
|
||||||
|
userSaveState: UserSaveState;
|
||||||
|
}) => ({
|
||||||
state: [
|
state: [
|
||||||
{ mutation: { loadSuccessful: true, inputCooldown: 1200 } },
|
{ mutation: { loadSuccessful: true, inputCooldown: 1200 } },
|
||||||
{
|
{
|
||||||
mutation: { loadSuccessful: undefined },
|
mutation: {
|
||||||
|
loadSuccessful: undefined,
|
||||||
|
currentScene: "null",
|
||||||
|
mainSubscene: "site",
|
||||||
|
lainMoveState: "standing",
|
||||||
|
promptVisible: false,
|
||||||
|
activePromptComponent: "no",
|
||||||
|
activePauseComponent: "change",
|
||||||
|
},
|
||||||
delay: 1200,
|
delay: 1200,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
mutation: { currentScene: "main", intro: true },
|
||||||
|
delay: 1300,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
audio: [{ sfx: [audio.sound28] }],
|
audio: [{ sfx: [audio.sound28] }],
|
||||||
|
effects: [
|
||||||
|
() =>
|
||||||
|
setTimeout(() => loadUserSaveState(calculatedState.userSaveState), 1200),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const changeSite = (calculatedState: {
|
export const changeSite = (calculatedState: {
|
||||||
|
@ -366,6 +411,7 @@ export const changeSite = (calculatedState: {
|
||||||
state: [
|
state: [
|
||||||
{
|
{
|
||||||
mutation: {
|
mutation: {
|
||||||
|
intro: true,
|
||||||
currentScene: "change_disc",
|
currentScene: "change_disc",
|
||||||
lainMoveState: "standing",
|
lainMoveState: "standing",
|
||||||
promptVisible: false,
|
promptVisible: false,
|
||||||
|
@ -418,14 +464,28 @@ export const changeMediaSide = (calculatedState: {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const playMedia = {
|
export const playMedia = (calculatedState: { activeNode: NodeData }) => ({
|
||||||
state: [{ mutation: { mediaPercentageElapsed: 0, inputCooldown: 500 } }],
|
state: [{ mutation: { mediaPercentageElapsed: 0, inputCooldown: 500 } }],
|
||||||
effects: [playMediaElement],
|
effects: [
|
||||||
};
|
playMediaElement,
|
||||||
|
() =>
|
||||||
|
setNodeViewed(calculatedState.activeNode.node_name, {
|
||||||
|
is_viewed: 1,
|
||||||
|
is_visible: 1,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
export const exitMedia = {
|
export const exitMedia = {
|
||||||
state: [{ mutation: { currentScene: "main", inputCooldown: -1 } }],
|
state: [
|
||||||
effects: [resetMediaElement],
|
{
|
||||||
|
mutation: {
|
||||||
|
currentScene: "main",
|
||||||
|
inputCooldown: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
effects: [resetMediaElement, resetMediaScene],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const changeRightMediaComponent = (calculatedState: {
|
export const changeRightMediaComponent = (calculatedState: {
|
||||||
|
@ -455,7 +515,7 @@ export const wordNotFound = {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
audio: [{ sfx: [audio.sound30] }],
|
audio: [{ sfx: [audio.sound30] }],
|
||||||
effects: [resetMediaElement],
|
effects: [resetMediaElement, resetMediaScene],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const hideWordNotFound = {
|
export const hideWordNotFound = {
|
||||||
|
@ -480,7 +540,7 @@ export const selectWord = (calculatedState: {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
audio: [{ sfx: [audio.sound29] }],
|
audio: [{ sfx: [audio.sound29] }],
|
||||||
effects: [resetMediaElement],
|
effects: [resetMediaElement, resetMediaScene],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const changeSsknComponent = (calculatedState: {
|
export const changeSsknComponent = (calculatedState: {
|
||||||
|
@ -496,19 +556,23 @@ export const changeSsknComponent = (calculatedState: {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const upgradeSskn = (calculatedState: {
|
export const upgradeSskn = (calculatedState: { activeNode: NodeData }) => ({
|
||||||
gameProgress: GameProgress;
|
|
||||||
}) => ({
|
|
||||||
state: [
|
state: [
|
||||||
{
|
{
|
||||||
mutation: {
|
mutation: {
|
||||||
gameProgress: calculatedState.gameProgress,
|
|
||||||
ssknLoading: true,
|
ssknLoading: true,
|
||||||
inputCooldown: -1,
|
inputCooldown: -1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ mutation: { currentScene: "main" }, delay: 6000 },
|
{ mutation: { currentScene: "main" }, delay: 6000 },
|
||||||
],
|
],
|
||||||
|
effects: [
|
||||||
|
() =>
|
||||||
|
setNodeViewed(calculatedState.activeNode.node_name, {
|
||||||
|
is_viewed: 1,
|
||||||
|
is_visible: 0,
|
||||||
|
}),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const exitSskn = {
|
export const exitSskn = {
|
||||||
|
@ -538,20 +602,11 @@ export const changeEndComponent = (calculatedState: {
|
||||||
audio: [{ sfx: [audio.sound1] }],
|
audio: [{ sfx: [audio.sound1] }],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const endGame = {
|
export const endGame = (calculatedState: { userSaveState: UserSaveState }) => ({
|
||||||
state: [{ mutation: { currentScene: "boot", inputCooldown: -1 } }],
|
state: [{ mutation: { currentScene: "boot", inputCooldown: -1 } }],
|
||||||
audio: [{ sfx: [audio.sound0] }],
|
audio: [{ sfx: [audio.sound0] }],
|
||||||
};
|
effects: [() => saveUserProgress(calculatedState.userSaveState)],
|
||||||
|
});
|
||||||
// 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: {
|
export const changeMainMenuComponent = (calculatedState: {
|
||||||
activeMainMenuComponent: "authorize_user" | "load_data";
|
activeMainMenuComponent: "authorize_user" | "load_data";
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
exitUserAuthorization,
|
exitUserAuthorization,
|
||||||
failUpdatePlayerName,
|
failUpdatePlayerName,
|
||||||
loadGame,
|
loadGame,
|
||||||
|
loadGameFail,
|
||||||
removePlayerNameLastChar,
|
removePlayerNameLastChar,
|
||||||
startNewGame,
|
startNewGame,
|
||||||
updateAuthorizeUserLetterIdx,
|
updateAuthorizeUserLetterIdx,
|
||||||
|
@ -40,7 +41,13 @@ const handleBootSceneKeyPress = (
|
||||||
case "no":
|
case "no":
|
||||||
return exitLoadData;
|
return exitLoadData;
|
||||||
case "yes":
|
case "yes":
|
||||||
return loadGame();
|
const stateToLoad = localStorage.getItem("lainSaveState");
|
||||||
|
|
||||||
|
if (stateToLoad)
|
||||||
|
return loadGame({
|
||||||
|
userSaveState: JSON.parse(stateToLoad),
|
||||||
|
});
|
||||||
|
else return loadGameFail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
import {
|
import { changeEndComponent, changeSite, endGame } from "../eventTemplates";
|
||||||
changeEndComponent,
|
|
||||||
continueGameAfterEnd,
|
|
||||||
endGame,
|
|
||||||
} from "../eventTemplates";
|
|
||||||
import { EndSceneContext, GameEvent } from "../../types/types";
|
import { EndSceneContext, GameEvent } from "../../types/types";
|
||||||
|
import { getCurrentUserState } from "../../store";
|
||||||
|
|
||||||
const handleEndSceneKeyPress = (
|
const handleEndSceneKeyPress = (
|
||||||
endSceneContext: EndSceneContext
|
endSceneContext: EndSceneContext
|
||||||
): GameEvent | undefined => {
|
): GameEvent | undefined => {
|
||||||
const { keyPress, selectionVisible, activeEndComponent } = endSceneContext;
|
const {
|
||||||
|
keyPress,
|
||||||
|
selectionVisible,
|
||||||
|
activeEndComponent,
|
||||||
|
siteSaveState,
|
||||||
|
activeNode,
|
||||||
|
activeLevel,
|
||||||
|
siteRot,
|
||||||
|
} = endSceneContext;
|
||||||
|
|
||||||
if (selectionVisible) {
|
if (selectionVisible) {
|
||||||
switch (keyPress) {
|
switch (keyPress) {
|
||||||
|
@ -19,9 +24,26 @@ const handleEndSceneKeyPress = (
|
||||||
case "CIRCLE":
|
case "CIRCLE":
|
||||||
switch (activeEndComponent) {
|
switch (activeEndComponent) {
|
||||||
case "end":
|
case "end":
|
||||||
return endGame;
|
return endGame({ userSaveState: getCurrentUserState() });
|
||||||
case "continue":
|
case "continue":
|
||||||
return continueGameAfterEnd;
|
const siteToLoad = "a";
|
||||||
|
const stateToLoad = siteSaveState[siteToLoad];
|
||||||
|
|
||||||
|
const newSiteSaveState = {
|
||||||
|
...siteSaveState,
|
||||||
|
b: {
|
||||||
|
activeNode: activeNode,
|
||||||
|
siteRot: [0, siteRot[1], 0],
|
||||||
|
activeLevel: activeLevel.toString().padStart(2, "0"),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return changeSite({
|
||||||
|
newActiveSite: siteToLoad,
|
||||||
|
newActiveNode: stateToLoad.activeNode,
|
||||||
|
newSiteRot: stateToLoad.siteRot,
|
||||||
|
newActiveLevel: stateToLoad.activeLevel,
|
||||||
|
newSiteSaveState: newSiteSaveState,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
knockNode,
|
knockNode,
|
||||||
knockNodeAndFall,
|
knockNodeAndFall,
|
||||||
loadGame,
|
loadGame,
|
||||||
|
loadGameFail,
|
||||||
pauseGame,
|
pauseGame,
|
||||||
ripNode,
|
ripNode,
|
||||||
saveGame,
|
saveGame,
|
||||||
|
@ -32,6 +33,7 @@ import {
|
||||||
throwNode,
|
throwNode,
|
||||||
} from "../eventTemplates";
|
} from "../eventTemplates";
|
||||||
import { GameEvent, MainSceneContext } from "../../types/types";
|
import { GameEvent, MainSceneContext } from "../../types/types";
|
||||||
|
import { getCurrentUserState } from "../../store";
|
||||||
|
|
||||||
const handleMainSceneKeyPress = (
|
const handleMainSceneKeyPress = (
|
||||||
mainSceneContext: MainSceneContext
|
mainSceneContext: MainSceneContext
|
||||||
|
@ -65,7 +67,7 @@ const handleMainSceneKeyPress = (
|
||||||
return exitPrompt;
|
return exitPrompt;
|
||||||
case "yes":
|
case "yes":
|
||||||
switch (activePauseComponent) {
|
switch (activePauseComponent) {
|
||||||
case "change":
|
case "change": {
|
||||||
const siteToLoad = activeSite === "a" ? "b" : "a";
|
const siteToLoad = activeSite === "a" ? "b" : "a";
|
||||||
const stateToLoad = siteSaveState[siteToLoad];
|
const stateToLoad = siteSaveState[siteToLoad];
|
||||||
|
|
||||||
|
@ -84,10 +86,18 @@ const handleMainSceneKeyPress = (
|
||||||
newActiveLevel: stateToLoad.activeLevel,
|
newActiveLevel: stateToLoad.activeLevel,
|
||||||
newSiteSaveState: newSiteSaveState,
|
newSiteSaveState: newSiteSaveState,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
case "save":
|
case "save":
|
||||||
return saveGame();
|
return saveGame({ userSaveState: getCurrentUserState() });
|
||||||
case "load":
|
case "load": {
|
||||||
return loadGame();
|
const stateToLoad = localStorage.getItem("lainSaveState");
|
||||||
|
|
||||||
|
if (stateToLoad)
|
||||||
|
return loadGame({
|
||||||
|
userSaveState: JSON.parse(stateToLoad),
|
||||||
|
});
|
||||||
|
else return loadGameFail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,6 +149,13 @@ const handleMainSceneKeyPress = (
|
||||||
case "DOWN": {
|
case "DOWN": {
|
||||||
const direction = keyPress.toLowerCase();
|
const direction = keyPress.toLowerCase();
|
||||||
|
|
||||||
|
const upperLimit = activeSite === "a" ? 22 : 13;
|
||||||
|
if (
|
||||||
|
(direction === "up" && level === upperLimit) ||
|
||||||
|
(direction === "down" && level === 1)
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
const nodeData = findNode(
|
const nodeData = findNode(
|
||||||
activeNode,
|
activeNode,
|
||||||
direction,
|
direction,
|
||||||
|
@ -198,9 +215,9 @@ const handleMainSceneKeyPress = (
|
||||||
} else {
|
} else {
|
||||||
return eventAnimation({ currentScene: "media" });
|
return eventAnimation({ currentScene: "media" });
|
||||||
}
|
}
|
||||||
case 8:
|
|
||||||
return eventAnimation({ currentScene: "gate" });
|
|
||||||
case 7:
|
case 7:
|
||||||
|
return eventAnimation({ currentScene: "sskn" });
|
||||||
|
case 8:
|
||||||
return eventAnimation({ currentScene: "gate" });
|
return eventAnimation({ currentScene: "gate" });
|
||||||
case 9:
|
case 9:
|
||||||
return eventAnimation({ currentScene: "polytan" });
|
return eventAnimation({ currentScene: "polytan" });
|
||||||
|
@ -231,11 +248,17 @@ const handleMainSceneKeyPress = (
|
||||||
|
|
||||||
const direction = selectedLevel > level ? "up" : "down";
|
const direction = selectedLevel > level ? "up" : "down";
|
||||||
|
|
||||||
// todo implement this row idx without mutating activenode
|
const newStartingPoint = {
|
||||||
const rowIdx = direction === "up" ? 2 : 0;
|
...activeNode,
|
||||||
|
matrixIndices: {
|
||||||
|
matrixIdx: activeNode.matrixIndices!.matrixIdx,
|
||||||
|
rowIdx: direction === "up" ? 2 : 0,
|
||||||
|
colIdx: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
const nodeData = findNode(
|
const nodeData = findNode(
|
||||||
activeNode,
|
newStartingPoint,
|
||||||
direction,
|
direction,
|
||||||
selectedLevel,
|
selectedLevel,
|
||||||
activeSite,
|
activeSite,
|
||||||
|
|
|
@ -9,7 +9,11 @@ import {
|
||||||
wordNotFound,
|
wordNotFound,
|
||||||
} from "../eventTemplates";
|
} from "../eventTemplates";
|
||||||
import { isNodeVisible } from "../../helpers/node-helpers";
|
import { isNodeVisible } from "../../helpers/node-helpers";
|
||||||
import {GameEvent, MediaSceneContext, RightMediaComponent} from "../../types/types";
|
import {
|
||||||
|
GameEvent,
|
||||||
|
MediaSceneContext,
|
||||||
|
RightMediaComponent,
|
||||||
|
} from "../../types/types";
|
||||||
|
|
||||||
const handleMediaSceneKeyPress = (
|
const handleMediaSceneKeyPress = (
|
||||||
mediaSceneContext: MediaSceneContext
|
mediaSceneContext: MediaSceneContext
|
||||||
|
@ -47,7 +51,7 @@ const handleMediaSceneKeyPress = (
|
||||||
case "CIRCLE":
|
case "CIRCLE":
|
||||||
switch (activeMediaComponent) {
|
switch (activeMediaComponent) {
|
||||||
case "play":
|
case "play":
|
||||||
return playMedia;
|
return playMedia({ activeNode: activeNode });
|
||||||
case "exit":
|
case "exit":
|
||||||
return exitMedia;
|
return exitMedia;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,16 +20,7 @@ const handleSsknSceneKeyPress = (
|
||||||
case "CIRCLE":
|
case "CIRCLE":
|
||||||
switch (activeSsknComponent) {
|
switch (activeSsknComponent) {
|
||||||
case "ok":
|
case "ok":
|
||||||
const newGameProgress = {
|
return upgradeSskn({ activeNode: activeNode });
|
||||||
...gameProgress,
|
|
||||||
[activeNode.node_name]: {
|
|
||||||
is_viewed: 1,
|
|
||||||
is_visible: 0,
|
|
||||||
},
|
|
||||||
sskn_level: gameProgress.sskn_level + 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
return upgradeSskn({ gameProgress: newGameProgress });
|
|
||||||
case "cancel":
|
case "cancel":
|
||||||
return exitSskn;
|
return exitSskn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@ const move = (direction: string, [matrix, level]: [number, number]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const findNode = (
|
export const findNode = (
|
||||||
activeNode: NodeData,
|
startingPoint: NodeData,
|
||||||
direction: string,
|
direction: string,
|
||||||
level: number,
|
level: number,
|
||||||
activeSite: ActiveSite,
|
activeSite: ActiveSite,
|
||||||
|
@ -192,11 +192,10 @@ export const findNode = (
|
||||||
down: [nextPos_down, ([, c]: [number, number]) => nextPos_down([-1, c])],
|
down: [nextPos_down, ([, c]: [number, number]) => nextPos_down([-1, c])],
|
||||||
};
|
};
|
||||||
|
|
||||||
if (activeNode.matrixIndices) {
|
if (startingPoint.matrixIndices) {
|
||||||
const nextPos = funcs[direction];
|
const nextPos = funcs[direction];
|
||||||
|
|
||||||
const nodeId = activeNode.id;
|
let { matrixIdx, colIdx, rowIdx } = { ...startingPoint.matrixIndices };
|
||||||
let { matrixIdx, colIdx, rowIdx } = { ...activeNode.matrixIndices };
|
|
||||||
|
|
||||||
const initialMatrixIdx = matrixIdx;
|
const initialMatrixIdx = matrixIdx;
|
||||||
|
|
||||||
|
@ -228,6 +227,7 @@ export const findNode = (
|
||||||
[matrixIdx, level] = move(direction, [matrixIdx, level]);
|
[matrixIdx, level] = move(direction, [matrixIdx, level]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const nodeId = startingPoint.id;
|
||||||
if (nodeId === "") [matrixIdx] = move(direction, [initialMatrixIdx, level]);
|
if (nodeId === "") [matrixIdx] = move(direction, [initialMatrixIdx, level]);
|
||||||
|
|
||||||
if (direction === "up" || direction === "down" || nodeId === "") {
|
if (direction === "up" || direction === "down" || nodeId === "") {
|
||||||
|
|
|
@ -12,10 +12,13 @@ const BootScene = () => {
|
||||||
const [accelaVisible, setAccelaVisible] = useState(true);
|
const [accelaVisible, setAccelaVisible] = useState(true);
|
||||||
const [mainMenuVisible, setMainMenuVisible] = useState(false);
|
const [mainMenuVisible, setMainMenuVisible] = useState(false);
|
||||||
|
|
||||||
|
const setInputCooldown = useStore((state) => state.setInputCooldown);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setTimeout(() => setAccelaVisible(false), 2000);
|
setTimeout(() => setAccelaVisible(false), 2000);
|
||||||
setTimeout(() => setMainMenuVisible(true), 6200);
|
setTimeout(() => setMainMenuVisible(true), 6200);
|
||||||
}, []);
|
setTimeout(() => setInputCooldown(0), 500);
|
||||||
|
}, [setInputCooldown]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<perspectiveCamera position-z={3}>
|
<perspectiveCamera position-z={3}>
|
||||||
|
|
|
@ -12,6 +12,7 @@ const GateScene = () => {
|
||||||
|
|
||||||
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);
|
||||||
|
const setInputCooldown = useStore((state) => state.setInputCooldown);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
incrementGateLvl();
|
incrementGateLvl();
|
||||||
|
@ -20,7 +21,8 @@ const GateScene = () => {
|
||||||
is_visible: 0,
|
is_visible: 0,
|
||||||
});
|
});
|
||||||
setTimeout(() => setIntroAnim(false), 2500);
|
setTimeout(() => setIntroAnim(false), 2500);
|
||||||
}, [activeNodeName, incrementGateLvl, setNodeViewed]);
|
setTimeout(() => setInputCooldown(0), 3500);
|
||||||
|
}, [activeNodeName, incrementGateLvl, setInputCooldown, setNodeViewed]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<perspectiveCamera position-z={3}>
|
<perspectiveCamera position-z={3}>
|
||||||
|
|
|
@ -50,8 +50,11 @@ const MainScene = () => {
|
||||||
setStarfieldIntro(false);
|
setStarfieldIntro(false);
|
||||||
setLainIntroAnim(false);
|
setLainIntroAnim(false);
|
||||||
setIntroFinished(false);
|
setIntroFinished(false);
|
||||||
|
setInputCooldown(-1);
|
||||||
|
} else {
|
||||||
|
setInputCooldown(0);
|
||||||
}
|
}
|
||||||
}, [intro]);
|
}, [intro, setInputCooldown]);
|
||||||
|
|
||||||
const [starfieldIntro, setStarfieldIntro] = useState(false);
|
const [starfieldIntro, setStarfieldIntro] = useState(false);
|
||||||
const [lainIntroAnim, setLainIntroAnim] = useState(false);
|
const [lainIntroAnim, setLainIntroAnim] = useState(false);
|
||||||
|
@ -80,18 +83,6 @@ 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 &&
|
!introFinished &&
|
||||||
!(
|
!(
|
||||||
|
|
|
@ -19,6 +19,9 @@ const MediaScene = () => {
|
||||||
const activeNode = useStore((state) => state.activeNode);
|
const activeNode = useStore((state) => state.activeNode);
|
||||||
|
|
||||||
const setScene = useStore((state) => state.setScene);
|
const setScene = useStore((state) => state.setScene);
|
||||||
|
const incrementFinalVideoViewCount = useStore(
|
||||||
|
(state) => state.incrementFinalVideoViewCount
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.getElementsByTagName("canvas")[0].className =
|
document.getElementsByTagName("canvas")[0].className =
|
||||||
|
@ -30,9 +33,16 @@ const MediaScene = () => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (percentageElapsed === 100 && activeNode.triggers_final_video)
|
if (percentageElapsed === 100 && activeNode.triggers_final_video) {
|
||||||
setScene("end");
|
setScene("end");
|
||||||
}, [activeNode.triggers_final_video, percentageElapsed, setScene]);
|
incrementFinalVideoViewCount();
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
activeNode.triggers_final_video,
|
||||||
|
incrementFinalVideoViewCount,
|
||||||
|
percentageElapsed,
|
||||||
|
setScene,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mediaElement = document.getElementById("media") as HTMLMediaElement;
|
const mediaElement = document.getElementById("media") as HTMLMediaElement;
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
import SsknIcon from "../components/SsknScene/SsknIcon";
|
import SsknIcon from "../components/SsknScene/SsknIcon";
|
||||||
import SsknBackground from "../components/SsknScene/SsknBackground";
|
import SsknBackground from "../components/SsknScene/SsknBackground";
|
||||||
import SsknHUD from "../components/SsknScene/SsknHUD";
|
import SsknHUD from "../components/SsknScene/SsknHUD";
|
||||||
|
import { useStore } from "../store";
|
||||||
|
|
||||||
const SsknScene = () => (
|
const SsknScene = () => {
|
||||||
|
const setInputCooldown = useStore((state) => state.setInputCooldown);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => setInputCooldown(0), 500);
|
||||||
|
}, [setInputCooldown]);
|
||||||
|
|
||||||
|
return (
|
||||||
<>
|
<>
|
||||||
<SsknBackground />
|
<SsknBackground />
|
||||||
<SsknIcon />
|
<SsknIcon />
|
||||||
<SsknHUD />
|
<SsknHUD />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default SsknScene;
|
export default SsknScene;
|
||||||
|
|
69
src/store.ts
69
src/store.ts
|
@ -20,7 +20,8 @@ import {
|
||||||
MediaComponent,
|
MediaComponent,
|
||||||
MediaSceneContext,
|
MediaSceneContext,
|
||||||
MediaSide,
|
MediaSide,
|
||||||
NodeAttributes, NodeData,
|
NodeAttributes,
|
||||||
|
NodeData,
|
||||||
PauseComponent,
|
PauseComponent,
|
||||||
PolytanBodyParts,
|
PolytanBodyParts,
|
||||||
PromptComponent,
|
PromptComponent,
|
||||||
|
@ -28,6 +29,7 @@ import {
|
||||||
SiteSaveState,
|
SiteSaveState,
|
||||||
SsknComponent,
|
SsknComponent,
|
||||||
SsknSceneContext,
|
SsknSceneContext,
|
||||||
|
UserSaveState,
|
||||||
} from "./types/types";
|
} from "./types/types";
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -137,8 +139,8 @@ export const useStore = create(
|
||||||
|
|
||||||
// nodes
|
// nodes
|
||||||
activeNode: {
|
activeNode: {
|
||||||
...site_a["04"]["0422"],
|
...site_a["04"]["0414"],
|
||||||
matrixIndices: { matrixIdx: 7, rowIdx: 0, colIdx: 0 },
|
matrixIndices: { matrixIdx: 7, rowIdx: 1, colIdx: 0 },
|
||||||
},
|
},
|
||||||
activeNodePos: [0, 0, 0],
|
activeNodePos: [0, 0, 0],
|
||||||
activeNodeRot: [0, 0, 0],
|
activeNodeRot: [0, 0, 0],
|
||||||
|
@ -233,19 +235,19 @@ export const useStore = create(
|
||||||
siteSaveState: {
|
siteSaveState: {
|
||||||
a: {
|
a: {
|
||||||
activeNode: {
|
activeNode: {
|
||||||
...getNodeById("0422", "a"),
|
...getNodeById("0408", "a"),
|
||||||
matrixIndices: { matrixIdx: 7, rowIdx: 0, colIdx: 0 },
|
matrixIndices: { matrixIdx: 7, rowIdx: 1, colIdx: 0 },
|
||||||
},
|
},
|
||||||
siteRot: [0, 0, 0],
|
siteRot: [0, 0, 0],
|
||||||
activeLevel: "04",
|
activeLevel: "04",
|
||||||
},
|
},
|
||||||
b: {
|
b: {
|
||||||
activeNode: {
|
activeNode: {
|
||||||
...getNodeById("0414", "b"),
|
...getNodeById("0105", "b"),
|
||||||
matrixIndices: { matrixIdx: 7, rowIdx: 1, colIdx: 0 },
|
matrixIndices: { matrixIdx: 6, rowIdx: 2, colIdx: 0 },
|
||||||
},
|
},
|
||||||
siteRot: [0, 0, 0],
|
siteRot: [0, 0 - Math.PI / 4, 0],
|
||||||
activeLevel: "04",
|
activeLevel: "01",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -268,10 +270,28 @@ export const useStore = create(
|
||||||
nodeName: string,
|
nodeName: string,
|
||||||
to: { is_viewed: number; is_visible: number }
|
to: { is_viewed: number; is_visible: number }
|
||||||
) =>
|
) =>
|
||||||
|
set((state) => {
|
||||||
|
const nodes = { ...state.gameProgress.nodes, [nodeName]: to };
|
||||||
|
return {
|
||||||
|
gameProgress: {
|
||||||
|
...state.gameProgress,
|
||||||
|
nodes: nodes,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
resetMediaScene: () =>
|
||||||
|
set(() => ({
|
||||||
|
activeMediaComponent: "play",
|
||||||
|
currentMediaSide: "left",
|
||||||
|
mediaWordPosStateIdx: 1,
|
||||||
|
})),
|
||||||
|
|
||||||
|
incrementFinalVideoViewCount: () =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
gameProgress: {
|
gameProgress: {
|
||||||
...state.gameProgress,
|
...state.gameProgress,
|
||||||
[nodeName]: to,
|
final_video_viewcount: state.gameProgress.final_video_viewcount + 1,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|
||||||
|
@ -305,6 +325,15 @@ export const useStore = create(
|
||||||
gate_level: state.gameProgress.gate_level + 1,
|
gate_level: state.gameProgress.gate_level + 1,
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
loadUserSaveState: (userState: UserSaveState) =>
|
||||||
|
set(() => ({
|
||||||
|
siteSaveState: userState.siteSaveState,
|
||||||
|
activeNode: userState.activeNode,
|
||||||
|
siteRot: userState.siteRot,
|
||||||
|
activeLevel: userState.activeLevel,
|
||||||
|
activeSite: userState.activeSite,
|
||||||
|
gameProgress: userState.gameProgress,
|
||||||
|
})),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -383,9 +412,29 @@ export const getEndSceneContext = (keyPress: string): EndSceneContext => {
|
||||||
keyPress: keyPress,
|
keyPress: keyPress,
|
||||||
activeEndComponent: state.activeEndComponent,
|
activeEndComponent: state.activeEndComponent,
|
||||||
selectionVisible: state.endSceneSelectionVisible,
|
selectionVisible: state.endSceneSelectionVisible,
|
||||||
|
siteSaveState: state.siteSaveState,
|
||||||
|
activeNode: state.activeNode,
|
||||||
|
siteRot: state.siteRot,
|
||||||
|
activeLevel: state.activeLevel,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCurrentUserState = (): UserSaveState => {
|
||||||
|
const state = useStore.getState();
|
||||||
|
|
||||||
|
return {
|
||||||
|
siteSaveState: state.siteSaveState,
|
||||||
|
activeNode: state.activeNode,
|
||||||
|
siteRot: [0, state.siteRot[1], 0],
|
||||||
|
activeLevel: state.activeLevel,
|
||||||
|
activeSite: state.activeSite,
|
||||||
|
gameProgress: state.gameProgress,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const saveUserProgress = (state: UserSaveState) =>
|
||||||
|
localStorage.setItem("lainSaveState", JSON.stringify(state));
|
||||||
|
|
||||||
export const playAudio = (audio: HTMLAudioElement) => {
|
export const playAudio = (audio: HTMLAudioElement) => {
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
audio.currentTime = 0;
|
audio.currentTime = 0;
|
||||||
|
|
|
@ -19,7 +19,8 @@ export type GameScene =
|
||||||
| "gate"
|
| "gate"
|
||||||
| "boot"
|
| "boot"
|
||||||
| "change_disc"
|
| "change_disc"
|
||||||
| "end";
|
| "end"
|
||||||
|
| "null";
|
||||||
|
|
||||||
export type MainSubscene = "site" | "pause" | "level_selection";
|
export type MainSubscene = "site" | "pause" | "level_selection";
|
||||||
|
|
||||||
|
@ -143,6 +144,10 @@ export type EndSceneContext = {
|
||||||
keyPress: string;
|
keyPress: string;
|
||||||
activeEndComponent: EndComponent;
|
activeEndComponent: EndComponent;
|
||||||
selectionVisible: boolean;
|
selectionVisible: boolean;
|
||||||
|
siteSaveState: SiteSaveState;
|
||||||
|
activeNode: NodeData;
|
||||||
|
siteRot: number[];
|
||||||
|
activeLevel: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Level = {
|
export type Level = {
|
||||||
|
@ -188,3 +193,12 @@ export type HUDData = {
|
||||||
initial_position: number[];
|
initial_position: number[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UserSaveState = {
|
||||||
|
siteSaveState: SiteSaveState;
|
||||||
|
activeNode: NodeData;
|
||||||
|
siteRot: number[];
|
||||||
|
activeLevel: string;
|
||||||
|
activeSite: ActiveSite;
|
||||||
|
gameProgress: GameProgress;
|
||||||
|
};
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
import React, { FC, ReactElement, Suspense } from "react";
|
|
||||||
import { render } from "@testing-library/react";
|
|
||||||
|
|
||||||
const SuspenseProvider: FC = ({ children }) => {
|
|
||||||
return <Suspense fallback={null}>{children}</Suspense>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const customRender = (ui: ReactElement) =>
|
|
||||||
render(ui, { wrapper: SuspenseProvider });
|
|
||||||
|
|
||||||
export * from "@testing-library/react";
|
|
||||||
|
|
||||||
export { customRender as render };
|
|
Loading…
Reference in a new issue