state management refactor almost done

This commit is contained in:
ad044 2021-02-01 22:13:32 +04:00
parent a1bb6260ae
commit cd584dd33b
48 changed files with 1383 additions and 2307 deletions

View file

@ -5,10 +5,10 @@ import bigHud from "../../../static/sprite/big_hud.png";
import longHud from "../../../static/sprite/long_hud.png";
import boringHud from "../../../static/sprite/long_hud_boring.png";
import { useStore } from "../../../store";
import { getNodeHud } from "../../../core/nodeSelector";
import lerp from "../../../core/utils/lerp";
import lerp from "../../../utils/lerp";
import GreenTextRenderer from "../../TextRenderer/GreenTextRenderer";
import usePrevious from "../../../hooks/usePrevious";
import {getNodeHud} from "../../../utils/nodeUtils";
export type HUDType = {
mirrored: number;

View file

@ -6,7 +6,7 @@ import { a, useSpring } from "@react-spring/three";
import { useStore } from "../../../store";
import MiddleRingPart from "./MiddleRing/MiddleRingPart";
import usePrevious from "../../../hooks/usePrevious";
import lerp from "../../../core/utils/lerp";
import lerp from "../../../utils/lerp";
const MiddleRing = () => {
const middleRingTex = useLoader(THREE.TextureLoader, middleRingTexture);

View file

@ -7,11 +7,11 @@ import NodeAnimations from "./Site/NodeAnimations";
import InactiveLevelNodes from "./Site/InactiveLevelNodes";
import { useFrame } from "react-three-fiber";
import * as THREE from "three";
import filterInvisibleNodes from "../../../core/utils/filterInvisibleNodes";
import site_a from "../../../resources/site_a.json";
import site_b from "../../../resources/site_b.json";
import level_y_values from "../../../resources/level_y_values.json";
import usePrevious from "../../../hooks/usePrevious";
import { filterInvisibleNodes } from "../../../utils/nodeUtils";
export type NodeDataType = {
id: string;

View file

@ -2,11 +2,11 @@ import React, { memo, useEffect, useState } from "react";
import Node from "./Node";
import node_positions from "../../../../resources/node_positions.json";
import { useStore } from "../../../../store";
import { isNodeVisible } from "../../../../core/nodeSelector";
import site_a from "../../../../resources/site_a.json";
import site_b from "../../../../resources/site_b.json";
import { NodeDataType, SiteType } from "../Site";
import usePrevious from "../../../../hooks/usePrevious";
import {isNodeVisible} from "../../../../utils/nodeUtils";
type ActiveLevelNodesProps = {
visibleNodes: SiteType;

View file

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

View file

@ -2,8 +2,8 @@ import React, { useEffect, useState } from "react";
import { useStore } from "../../store";
import { a, useTrail } from "@react-spring/three";
import BigLetter from "./BigLetter";
import { getNodeHud } from "../../core/nodeSelector";
import usePrevious from "../../hooks/usePrevious";
import {getNodeHud} from "../../utils/nodeUtils";
const YellowTextRenderer = (props: { visible?: boolean }) => {
const activeNode = useStore((state) => state.activeNode);

View file

@ -1,55 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const BootComponentManager = (props: StateManagerProps) => {
const toggleComponentMatrixIdx = useStore(
(state) => state.toggleBootComponentMatrixIdx
);
const dispatchObject = useCallback(
(
event: string,
activeSubscene: string,
newAuthorizeUserMatrixIdx: number
) => {
switch (event) {
case "main_menu_down":
case "main_menu_up":
case "load_data_left":
case "load_data_right":
return {
action: toggleComponentMatrixIdx,
value: activeSubscene,
};
case "authorize_user_right":
case "authorize_user_up":
case "authorize_user_left":
case "authorize_user_down":
}
},
[toggleComponentMatrixIdx]
);
useEffect(() => {
if (props.eventState) {
const eventAction = props.eventState.event;
const newAuthorizeUserMatrixIdx =
props.eventState.newAuthorizeUserMatrixIdx;
const activeSubscene = props.eventState.subscene;
const dispatchedObject = dispatchObject(
eventAction,
activeSubscene,
newAuthorizeUserMatrixIdx
);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value as never);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default BootComponentManager;

View file

@ -1,89 +0,0 @@
import { useCallback, useEffect } from "react";
import { useStore, useSiteSaveStore } from "../../../store";
import { StateManagerProps } from "../EventManager";
import node_huds from "../../../resources/node_huds.json";
import site_a from "../../../resources/site_a.json";
import site_b from "../../../resources/site_b.json";
import { SiteType } from "../../../components/MainScene/SyncedComponents/Site";
const GameLoader = (props: StateManagerProps) => {
const siteASaveState = useSiteSaveStore((state) => state.a);
const siteBSaveState = useSiteSaveStore((state) => state.b);
// setters for components, setting them all like this instead of their respective ones
// makes more sense since they all needed extra bit of context just for this certain event,
// which imo didn't make much sense
// level setter
// const setActiveLevel = useLevelStore((state) => state.setActiveLevel);
// site setter
const setSiteRot = useStore((state) => state.setSiteRot);
const setCurrentSite = useStore((state) => state.setActiveSite);
// node setter
const setActiveNode = useStore((state) => state.setNode);
// node hud setter
const changeSite = useCallback((site: string) => {
// load new site
// const siteToLoad = site === "a" ? siteASaveState : siteBSaveState;
// const siteData = site === "a" ? site_a : site_b;
//
// // load new site (the object itself)
// setCurrentSite(site);
// setSiteRot(siteToLoad.siteRotY);
// setSitePos(siteToLoad.sitePosY);
//
// // load new site level
// setActiveLevel(siteToLoad.level);
//
// // load new site yellow text
// setBigTexPos(
// node_huds[siteToLoad.nodeHudId as keyof typeof node_huds].big_text[0]
// );
//
// const targetYellowText = (siteData as SiteType)[siteToLoad.level][
// siteToLoad.activeNodeId
// ].node_name;
//
// setBigText(targetYellowText);
//
// // load new site node
// setActiveNode(siteToLoad.activeNodeId);
// setNodeMatrixIndices(siteToLoad.nodeMatrixIndices);
//
// // load new site node hud
// setHud(siteToLoad.nodeHudId);
}, []);
const dispatchObject = useCallback(
(eventState: { event: string; site: string }) => {
switch (eventState.event) {
case "pause_change_select":
return {
action: changeSite,
value: [eventState.site],
actionDelay: 0,
};
}
},
[changeSite]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
dispatchedObject.action.apply(null, dispatchedObject.value as any);
}, dispatchedObject.actionDelay);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default GameLoader;

View file

@ -1,59 +0,0 @@
import { useCallback, useEffect } from "react";
import { useSiteSaveStore } from "../../../store";
import { StateManagerProps } from "../EventManager";
const GameSaver = (props: StateManagerProps) => {
const setSiteSaveState = useSiteSaveStore((state) => state.setSiteSaveState);
const dispatchObject = useCallback(
(eventState: {
event: string;
currentSitePosY: number;
currentSiteRotY: number;
currentNodeId: string;
currentNodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
};
currentHudId: string;
currentLevel: string;
site: string;
}) => {
switch (eventState.event) {
case "pause_change_select":
return {
action: setSiteSaveState,
value: [
eventState.site === "a" ? "b" : "a",
{
activeNodeId: eventState.currentNodeId,
nodeMatrixIndices: eventState.currentNodeMatrixIndices,
nodeHudId: eventState.currentHudId,
siteRotY: eventState.currentSiteRotY,
sitePosY: eventState.currentSitePosY,
level: eventState.currentLevel,
},
],
actionDelay: 0,
};
}
},
[setSiteSaveState]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
dispatchedObject.action.apply(null, dispatchedObject.value as any);
}, dispatchedObject.actionDelay);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default GameSaver;

View file

@ -1,88 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const SceneManager = (props: StateManagerProps) => {
const setScene = useStore((state) => state.setScene);
const setMainSceneIntro = useStore((state) => state.setIntro);
const dispatchObject = useCallback(
(eventState: { event: string; scene: string }) => {
switch (eventState.event) {
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return {
action: setScene,
value: eventState.scene,
delay: 3450,
setMainSceneIntro: false,
};
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return {
action: setScene,
value: eventState.scene,
delay: 6000,
setMainSceneIntro: false,
};
case "media_exit_select":
case "exit_gate":
case "sskn_cancel_select":
return {
action: setScene,
value: "main",
delay: 0,
};
case "sskn_ok_select":
return {
action: setScene,
value: "main",
delay: 6000,
};
case "pause_change_select":
return {
action: setScene,
value: "change_disc",
delay: 0,
setMainSceneIntro: true,
};
case "play_idle_media":
return {
action: setScene,
value: "idle_media",
delay: 0,
setMainSceneIntro: false,
};
}
},
[setScene]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
dispatchedObject.action(dispatchedObject.value);
}, dispatchedObject.delay);
if (dispatchedObject.setMainSceneIntro !== undefined) {
if (dispatchedObject.setMainSceneIntro) {
setMainSceneIntro(true);
} else {
setMainSceneIntro(false);
}
}
}
}
}, [props.eventState, dispatchObject, setMainSceneIntro]);
return null;
};
export default SceneManager;

View file

@ -1,74 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const SubsceneManager = (props: StateManagerProps) => {
const setMainSubscene = useStore((state) => state.setMainSubscene);
const setBootSubscene = useStore((state) => state.setBootSubscene);
const dispatchObject = useCallback(
(eventState: { event: string }) => {
switch (eventState.event) {
case "level_selection_back":
case "select_level_up":
case "select_level_down":
return {
action: setMainSubscene,
value: "site",
delay: 0,
};
case "toggle_level_selection":
return {
action: setMainSubscene,
value: "level_selection",
delay: 0,
};
case "pause_game":
return {
action: setMainSubscene,
value: "pause",
delay: 0,
};
case "pause_exit_select":
case "pause_change_select":
return {
action: setMainSubscene,
value: "site",
delay: 1800,
};
case "authorize_user_back":
case "load_data_no_select":
return {
action: setBootSubscene,
value: "main_menu",
delay: 0,
};
case "authorize_user_select":
return {
action: setBootSubscene,
value: "authorize_user",
delay: 0,
};
case "load_data_select":
return { action: setBootSubscene, value: "load_data", delay: 0 };
}
},
[setBootSubscene, setMainSubscene]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
dispatchedObject.action(dispatchedObject.value as any);
}, dispatchedObject.delay);
}
}
}, [props.eventState, dispatchObject]);
return null;
};
export default SubsceneManager;

View file

@ -1,6 +1,5 @@
import React, { useCallback, useEffect, useState } from "react";
import { getKeyCodeAssociation } from "../utils/keyPressUtils";
import SceneManager from "./GameManagers/SceneManager";
import { getKeyCodeAssociation } from "../../utils/keyPressUtils";
const GateSceneManager = () => {
const [eventState, setEventState] = useState<any>();

View file

@ -1,44 +1,39 @@
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useStore } from "../../store";
import React, { useCallback, useEffect, useMemo, useRef } from "react";
import { getMainSceneContext, useStore } from "../../store";
import handleMainSceneEvent from "../mainSceneEventHandler";
import { getKeyCodeAssociation } from "../utils/keyPressUtils";
import NodeManager from "./MainSceneManagers/NodeManager";
import SiteManager from "./MainSceneManagers/SiteManager";
import LainManager from "./MainSceneManagers/LainManager";
import SceneManager from "./GameManagers/SceneManager";
import LevelManager from "./MainSceneManagers/LevelManager";
import LevelSelectionManager from "./MainSceneManagers/LevelSelectionManager";
import SubsceneManager from "./GameManagers/SubsceneManager";
import PauseComponentManager from "./MainSceneManagers/PauseComponentManager";
import GameSaver from "./GameManagers/GameSaver";
import GameLoader from "./GameManagers/GameLoader";
import IdleManager from "./MainSceneManagers/IdleManager";
import { getKeyCodeAssociation } from "../../utils/keyPressUtils";
import { useFrame } from "react-three-fiber";
import levelSelectionManager from "../setters/main/level_selection/levelSelectionManager";
import lainManager from "../setters/main/site/lainManager";
import levelManager from "../setters/main/site/levelManager";
import nodeManager from "../setters/main/site/nodeManager";
import pauseManager from "../setters/main/pause/pauseManager";
import siteManager from "../setters/main/site/siteManager";
import mainSubsceneManager from "../setters/main/mainSubsceneManager";
import sceneManager from "../setters/sceneManager";
type MainSceneEventManagerProps = {
loaded: boolean;
};
const MainSceneEventManager = (props: MainSceneEventManagerProps) => {
// all the possible context needed to calculate new state
const currentSite = useStore((state) => state.activeSite);
const activeNodeId = useStore((state) => state.activeNode.id);
const nodeMatrixIndices = useStore((state) => state.activeNode.matrixIndices);
const siteRotY = useStore((state) => state.siteRot[1]);
const activeLevel = useStore((state) => state.activeLevel);
const mainSubscene = useStore((state) => state.mainSubscene);
const selectedLevel = useStore((state) => state.selectedLevel);
const pauseMatrixIdx = useStore((state) => state.pauseComponentMatrixIdx);
const activePauseComponent = useStore(
useCallback((state) => state.pauseComponentMatrix[pauseMatrixIdx], [
pauseMatrixIdx,
])
);
const gameProgress = useStore((state) => state.gameProgress);
const timePassedSinceLastKeyPress = useRef(-1);
const [eventState, setEventState] = useState<any>();
const mainSceneSetters = useMemo(
() => [
levelSelectionManager,
nodeManager,
levelManager,
lainManager,
siteManager,
pauseManager,
mainSubsceneManager,
sceneManager,
],
[]
);
useFrame(() => {
const now = Date.now();
@ -76,7 +71,7 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => {
];
const event = moves[Math.floor(Math.random() * moves.length)];
setEventState({ event: event });
// setEventState({ event: event });
timePassedSinceLastKeyPress.current = now - 2500;
}
}
@ -90,36 +85,13 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => {
if (keyPress && props.loaded) {
timePassedSinceLastKeyPress.current = Date.now() + 2500;
const event = handleMainSceneEvent({
mainSubscene: mainSubscene,
keyPress: keyPress,
siteRotY: siteRotY,
activeNodeId: activeNodeId,
nodeMatrixIndices: nodeMatrixIndices,
activeLevel: activeLevel,
selectedLevel: selectedLevel,
pauseMatrixIdx: pauseMatrixIdx,
activePauseComponent: activePauseComponent,
gameProgress: gameProgress,
currentSite: currentSite,
});
const ctx = { ...getMainSceneContext(), keyPress: keyPress };
setEventState(event);
const event = handleMainSceneEvent(ctx);
mainSceneSetters.forEach((fn) => fn(event));
}
},
[
props.loaded,
mainSubscene,
siteRotY,
activeNodeId,
nodeMatrixIndices,
activeLevel,
selectedLevel,
pauseMatrixIdx,
activePauseComponent,
gameProgress,
currentSite,
]
[mainSceneSetters, props.loaded]
);
useEffect(() => {
@ -130,21 +102,7 @@ const MainSceneEventManager = (props: MainSceneEventManagerProps) => {
};
}, [handleKeyPress]);
return (
<>
<NodeManager eventState={eventState!} />
<SiteManager eventState={eventState!} />
<LainManager eventState={eventState!} />
<SceneManager eventState={eventState!} />
<LevelManager eventState={eventState!} />
<LevelSelectionManager eventState={eventState!} />
<SubsceneManager eventState={eventState!} />
<PauseComponentManager eventState={eventState!} />
<GameSaver eventState={eventState!} />
<GameLoader eventState={eventState!} />
<IdleManager eventState={eventState!} />
</>
);
return null;
};
export default MainSceneEventManager;

View file

@ -1,115 +0,0 @@
import React, { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { SiteType } from "../../../components/MainScene/SyncedComponents/Site";
import site_a from "../../../resources/site_a.json";
import site_b from "../../../resources/site_b.json";
import { useStore } from "../../../store";
const IdleManager = (props: StateManagerProps) => {
const setImages = useStore((state) => state.setIdleImages);
const setMedia = useStore((state) => state.setIdleMedia);
const playIdleMedia = useCallback(
(site: string) => {
const siteAIdleNodes = {
audio: [
"0000",
"0001",
"0002",
"0003",
"0004",
"0005",
"0006",
"0007",
"0008",
"0009",
],
video: [
"INS01.STR",
"INS02.STR",
"INS03.STR",
"INS04.STR",
"INS05.STR",
"INS06.STR",
"INS07.STR",
"INS08.STR",
"INS09.STR",
"INS10.STR",
"INS11.STR",
"INS12.STR",
"INS13.STR",
"INS14.STR",
"INS15.STR",
"INS16.STR",
"INS17.STR",
"INS18.STR",
"INS19.STR",
"INS20.STR",
"INS21.STR",
"INS22.STR",
],
};
const siteBIdleNodes = {
audio: ["1015", "1219", "0419", "0500", "0501", "0508", "0510", "0513"],
video: [
"INS16.STR",
"INS17.STR",
"INS18.STR",
"INS19.STR",
"INS20.STR",
"INS21.STR",
"INS22.STR",
],
};
const siteData = site === "a" ? site_a : site_b;
const idleNodes = site === "a" ? siteAIdleNodes : siteBIdleNodes;
if (Math.random() < 0.5) {
const nodeToPlay =
idleNodes.audio[Math.floor(Math.random() * idleNodes.audio.length)];
const level = nodeToPlay.substr(0, 2);
const images = (siteData as SiteType)[level][nodeToPlay]
.image_table_indices;
const media = (siteData as SiteType)[level][nodeToPlay].media_file;
setMedia(media);
setImages(images);
} else {
setMedia(
idleNodes.video[Math.floor(Math.random() * idleNodes.video.length)]
);
}
},
[setImages, setMedia]
);
const dispatchObject = useCallback(
(eventState: { event: string; site: string }) => {
switch (eventState.event) {
case "play_idle_media":
return {
action: playIdleMedia,
value: eventState.site,
};
}
},
[playIdleMedia]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default IdleManager;

View file

@ -1,87 +0,0 @@
import { useCallback, useEffect } from "react";
import { useStore } from "../../../store";
import { StateManagerProps } from "../EventManager";
const LainManager = (props: StateManagerProps) => {
const setLainMoveState = useStore((state) => state.setLainMoveState);
const dispatchObject = useCallback(
(eventState: { event: string }) => {
switch (eventState.event) {
case "site_up":
case "site_down":
case "site_left":
case "site_right":
case "select_level_up":
case "select_level_down":
case "pause_game":
case "knock_node":
case "prayer":
case "touch_sleeve":
case "thinking":
case "stretch_2":
case "stretch":
case "spin":
case "scratch_head":
case "blush":
case "hands_behind_head":
case "hands_on_hips":
case "hands_on_hips_2":
case "hands_together":
case "lean_forward":
case "lean_left":
case "lean_right":
case "look_around":
case "play_with_hair":
return {
action: setLainMoveState,
value: eventState.event,
duration: 3900,
};
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return {
action: setLainMoveState,
value: "throw_node",
duration: 3900,
};
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return {
action: setLainMoveState,
value: "rip_node",
duration: 6000,
};
case "knock_node_and_fall":
return {
action: setLainMoveState,
value: "knock_node_and_fall",
duration: 6000,
};
}
},
[setLainMoveState]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value);
setTimeout(() => {
setLainMoveState("standing");
}, dispatchedObject.duration);
}
}
}, [props.eventState, setLainMoveState, dispatchObject]);
return null;
};
export default LainManager;

View file

@ -1,40 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const LevelManager = (props: StateManagerProps) => {
const setActiveLevel = useStore((state) => state.setActiveLevel);
const dispatchObject = useCallback(
(eventState: { event: string; level: string }) => {
switch (eventState.event) {
case "site_up":
case "select_level_down":
case "select_level_up":
case "site_down":
return {
action: setActiveLevel,
value: eventState.level,
delay: 0,
};
}
},
[setActiveLevel]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
dispatchedObject.action(dispatchedObject.value as any);
}, dispatchedObject.delay);
}
}
}, [props.eventState, dispatchObject]);
return null;
};
export default LevelManager;

View file

@ -1,44 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const LevelSelectionManager = (props: StateManagerProps) => {
const setSelectedLevel = useStore((state) => state.setSelectedLevel);
const dispatchObject = useCallback(
(eventState: {
event: string;
selectedLevelIdx: number;
level: number;
}) => {
switch (eventState.event) {
case "toggle_level_selection":
return {
action: setSelectedLevel,
value: eventState.level,
};
case "level_selection_up":
case "level_selection_down":
return {
action: setSelectedLevel,
value: eventState.selectedLevelIdx,
};
}
},
[setSelectedLevel]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value as any);
}
}
}, [props.eventState, dispatchObject]);
return null;
};
export default LevelSelectionManager;

View file

@ -1,245 +0,0 @@
import { useCallback, useEffect } from "react";
import { useStore } from "../../../store";
import { StateManagerProps } from "../EventManager";
import { NodeDataType } from "../../../components/MainScene/SyncedComponents/Site";
const NodeManager = (props: StateManagerProps) => {
const setActiveNode = useStore((state) => state.setNode);
const setActiveNodePos = useStore((state) => state.setNodePos);
const setActiveNodeRot = useStore((state) => state.setNodeRot);
const setActiveNodeState = useStore((state) => state.setNodeState);
const calculateCoordsBasedOnRotation = (
x: number,
z: number,
rotation: number
) => ({
x: x * Math.cos(rotation) - z * Math.sin(rotation),
z: x * Math.sin(rotation) + z * Math.cos(rotation),
});
const animateActiveNodeThrow = useCallback(
(siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(0.9, 0.3, siteRotY);
const sndCoordSet = calculateCoordsBasedOnRotation(0.5, 0.2, siteRotY);
const thirdCoordSet = calculateCoordsBasedOnRotation(1.55, 0.2, siteRotY);
const fourthCoordSet = calculateCoordsBasedOnRotation(0, 2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodePos([sndCoordSet.x, 0, sndCoordSet.z]);
}, 800);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, 0, sndCoordSet.z]);
setActiveNodeRot([0, 0, -0.005]);
}, 2600);
setTimeout(() => {
setActiveNodePos([fourthCoordSet.x, 0, fourthCoordSet.z]);
setActiveNodeRot([0, 0, -0.5]);
}, 2700);
setTimeout(() => {
setActiveNodeRot([0, 0, 0]);
setActiveNodeState(false, "interactedWith");
}, 3800);
},
[setActiveNodePos, setActiveNodeRot, setActiveNodeState]
);
const animateNodeKnock = useCallback(
(siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(1.1, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, -0.6, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
}, 2500);
},
[setActiveNodePos, setActiveNodeState]
);
const animateNodeKnockAndFall = useCallback(
(siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(1.1, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, -0.6, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(false, "visible");
}, 2300);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
}, 2500);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 3200);
},
[setActiveNodePos, setActiveNodeState]
);
const animateNodeTouchAndScare = useCallback(
(siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(-0.6, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(true, "exploding");
setActiveNodeState(false, "visible");
}, 1200);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
setActiveNodeRot([0, 0, 0]);
}, 1400);
setTimeout(() => {
setActiveNodeState(false, "exploding");
}, 3150);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 3500);
},
[setActiveNodePos, setActiveNodeRot, setActiveNodeState]
);
const animateShrinkAndRip = useCallback(
(siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(0.9, 0.3, siteRotY);
const sndCoordSet = calculateCoordsBasedOnRotation(0.5, 0.2, siteRotY);
const thirdCoordSet = calculateCoordsBasedOnRotation(0, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodePos([sndCoordSet.x, 0, sndCoordSet.z]);
}, 800);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, -0.4, thirdCoordSet.z]);
}, 2800);
setTimeout(() => {
setActiveNodeState(true, "shrinking");
}, 3000);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, -1.5, thirdCoordSet.z]);
}, 3200);
setTimeout(() => {
setActiveNodeState(false, "visible");
}, 3500);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
setActiveNodeState(false, "shrinking");
setActiveNodeRot([0, 0, 0]);
}, 6400);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 7500);
},
[setActiveNodePos, setActiveNodeRot, setActiveNodeState]
);
const updateActiveNode = useCallback(
(
node: NodeDataType,
newNodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
},
isMoving?: boolean,
delay?: number
) => {
setTimeout(() => {
node.matrixIndices = newNodeMatrixIndices;
setActiveNode(node);
}, delay);
},
[setActiveNode]
);
const dispatchObject = useCallback(
(eventState: {
event: string;
node: NodeDataType;
nodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
};
siteRotY: number;
idleNodeId?: string;
}) => {
switch (eventState.event) {
case "site_up":
case "site_down":
case "site_left":
case "site_right":
case "select_level_up":
case "select_level_down":
return {
action: updateActiveNode,
value: [eventState.node, eventState.nodeMatrixIndices, true, 3900],
};
case "change_node":
return {
action: updateActiveNode,
value: [eventState.node, eventState.nodeMatrixIndices],
};
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return {
action: animateActiveNodeThrow,
value: [eventState.siteRotY],
};
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return {
action: animateShrinkAndRip,
value: [eventState.siteRotY],
};
}
},
[animateActiveNodeThrow, animateShrinkAndRip, updateActiveNode]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
(dispatchedObject.action as any).apply(
null,
dispatchedObject.value as never
);
}
}
}, [props.eventState, setActiveNodeState, dispatchObject]);
return null;
};
export default NodeManager;

View file

@ -1,49 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const PauseComponentManager = (props: StateManagerProps) => {
const setComponentMatrixIdx = useStore(
(state) => state.setPauseComponentMatrixIdx
);
const setExitAnimation = useStore(
(state) => state.setPauseExitAnimation
);
const dispatchObject = useCallback(
(eventState: { event: string; pauseMatrixIdx: number }) => {
switch (eventState.event) {
case "pause_up":
case "pause_down":
return {
action: setComponentMatrixIdx,
value: eventState.pauseMatrixIdx,
};
case "pause_exit_select":
return {
action: setExitAnimation,
value: true,
};
case "pause_game":
return {
action: setExitAnimation,
value: false,
};
}
},
[setComponentMatrixIdx, setExitAnimation]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value as never);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default PauseComponentManager;

View file

@ -1,53 +0,0 @@
import { useCallback, useEffect } from "react";
import { useStore } from "../../../store";
import { StateManagerProps } from "../EventManager";
const SiteManager = (props: StateManagerProps) => {
const setRot = useStore((state) => state.setSiteRot);
const setRotX = useStore((state) => state.setSiteRotX);
const dispatchObject = useCallback(
(eventState: { event: string; siteRotY: number }) => {
switch (eventState.event) {
case "site_left":
case "site_right":
return {
action: setRot,
value: [[0, eventState.siteRotY, 0]],
actionDelay: 1100,
};
case "pause_game":
return {
action: setRotX,
value: [Math.PI / 2],
actionDelay: 3600,
};
case "pause_exit_select":
return {
action: setRotX,
value: [0],
actionDelay: 0,
};
}
},
[setRot, setRotX]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
setTimeout(() => {
(dispatchedObject.action as any).apply(
null,
dispatchedObject.value as any
);
}, dispatchedObject.actionDelay);
}
}
}, [dispatchObject, props.eventState]);
return null;
};
export default SiteManager;

View file

@ -1,33 +1,12 @@
import React, { useCallback, useEffect, useState } from "react";
import { useStore } from "../../store";
import { getKeyCodeAssociation } from "../utils/keyPressUtils";
import SceneManager from "./GameManagers/SceneManager";
import React, { useCallback, useEffect, useMemo } from "react";
import { getMediaSceneContext } from "../../store";
import { getKeyCodeAssociation } from "../../utils/keyPressUtils";
import mediaManager from "../setters/media/mediaManager";
import handleMediaSceneEvent from "../mediaSceneEventHandler";
import MediaComponentManager from "./MediaSceneManagers/MediaComponentManager";
import sceneManager from "../setters/sceneManager";
const MediaSceneEventManager = () => {
// all the possible context needed to calculate new state
const activeMediaComponent = useStore(
useCallback(
(state) =>
state.mediaComponentMatrix[state.mediaComponentMatrixIndices.sideIdx][
state.mediaComponentMatrixIndices.sideIdx === 0
? state.mediaComponentMatrixIndices.leftSideIdx
: state.mediaComponentMatrixIndices.rightSideIdx
],
[]
)
);
const rightSideComponentIdx = useStore(
(state) => state.mediaComponentMatrixIndices.rightSideIdx
);
const wordPosStateIdx = useStore(
(state) => state.mediaWordPosStateIdx
);
const [eventState, setEventState] = useState<any>();
const mediaSceneSetters = useMemo(() => [mediaManager, sceneManager], []);
const handleKeyPress = useCallback(
(event) => {
@ -36,17 +15,14 @@ const MediaSceneEventManager = () => {
const keyPress = getKeyCodeAssociation(keyCode);
if (keyPress) {
const event = handleMediaSceneEvent({
keyPress: keyPress,
activeMediaComponent: activeMediaComponent,
wordPosStateIdx: wordPosStateIdx,
rightSideComponentIdx: rightSideComponentIdx,
});
const ctx = { ...getMediaSceneContext(), keyPress: keyPress };
setEventState(event);
const event = handleMediaSceneEvent(ctx);
mediaSceneSetters.forEach((fn) => fn(event));
}
},
[activeMediaComponent, rightSideComponentIdx, wordPosStateIdx]
[mediaSceneSetters]
);
useEffect(() => {
@ -57,12 +33,7 @@ const MediaSceneEventManager = () => {
};
}, [handleKeyPress]);
return (
<>
<MediaComponentManager eventState={eventState!} />
<SceneManager eventState={eventState!} />
</>
);
return null;
};
export default MediaSceneEventManager;

View file

@ -1,114 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
import * as THREE from "three";
const MediaComponentManager = (props: StateManagerProps) => {
const toggleSide = useStore((state) => state.toggleMediaSide);
const setLeftComponentMatrixIdx = useStore(
(state) => state.setMediaLeftComponentMatrixIdx
);
const setRightComponentMatrixIdx = useStore(
(state) => state.setMediaRightComponentMatrixIdx
);
const setWordPosStateIdx = useStore((state) => state.setMediaWordPosStateIdx);
const resetComponentMatrixIndices = useStore(
(state) => state.resetMediaComponentMatrixIndices
);
const resetWordPosStateIdx = useStore(
(state) => state.resetMediaWordPosStateIdx
);
const setAudioAnalyser = useStore((state) => state.setAudioAnalyser);
const playMedia = useCallback(() => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement && mediaElement.paused) {
const listener = new THREE.AudioListener();
const audio = new THREE.Audio(listener);
audio.setMediaElementSource(mediaElement);
setAudioAnalyser(new THREE.AudioAnalyser(audio, 2048));
mediaElement.play();
}
}, [setAudioAnalyser]);
const exitMedia = useCallback(() => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement) {
mediaElement.pause();
mediaElement.currentTime = 0;
}
resetComponentMatrixIndices();
resetWordPosStateIdx();
}, [resetComponentMatrixIndices, resetWordPosStateIdx]);
const updateRightSide = useCallback(
(newRightSideComponentIdx: 0 | 1 | 2, newWordPosStateIdx: number) => {
setRightComponentMatrixIdx(newRightSideComponentIdx);
setWordPosStateIdx(newWordPosStateIdx);
},
[setRightComponentMatrixIdx, setWordPosStateIdx]
);
const dispatchObject = useCallback(
(eventState: {
event: string;
leftSideComponentIdx: number;
rightSideComponentIdx: number;
wordPosStateIdx: number;
}) => {
switch (eventState.event) {
case "media_rightside_down":
case "media_rightside_up":
return {
action: updateRightSide,
value: [
eventState.rightSideComponentIdx,
eventState.wordPosStateIdx,
],
};
case "media_leftside_down":
case "media_leftside_up":
return {
action: setLeftComponentMatrixIdx,
value: [eventState.leftSideComponentIdx],
};
case "media_leftside_right":
case "media_rightside_left":
return {
action: toggleSide,
};
case "media_play_select":
return { action: playMedia };
case "media_exit_select":
return { action: exitMedia };
}
},
[
exitMedia,
playMedia,
setLeftComponentMatrixIdx,
toggleSide,
updateRightSide,
]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
(dispatchedObject.action as any).apply(null, dispatchedObject.value);
}
}
}, [props.eventState, dispatchObject]);
return null;
};
export default MediaComponentManager;

View file

@ -1,20 +1,12 @@
import React, { useCallback, useEffect, useState } from "react";
import { getKeyCodeAssociation } from "../utils/keyPressUtils";
import SceneManager from "./GameManagers/SceneManager";
import React, { useCallback, useEffect, useMemo } from "react";
import { getKeyCodeAssociation } from "../../utils/keyPressUtils";
import handleSSknSceneEvent from "../ssknSceneEventHandler";
import { useStore } from "../../store";
import SSknComponentManager from "./SSknSceneManagers/SSknComponentManager";
import { getSSknSceneContext } from "../../store";
import ssknManager from "../setters/sskn/ssknManager";
import sceneManager from "../setters/sceneManager";
const SSknSceneManager = () => {
// all the possible context needed to calculate new state
const activeSSknComponent = useStore(
useCallback(
(state) => state.ssknComponentMatrix[state.ssknComponentMatrixIdx],
[]
)
);
const [eventState, setEventState] = useState<any>();
const ssknSceneSetters = useMemo(() => [ssknManager, sceneManager], []);
const handleKeyPress = useCallback(
(event) => {
@ -23,15 +15,14 @@ const SSknSceneManager = () => {
const keyPress = getKeyCodeAssociation(keyCode);
if (keyPress) {
const event = handleSSknSceneEvent({
keyPress: keyPress,
activeSSknComponent: activeSSknComponent,
});
const ctx = { ...getSSknSceneContext(), keyPress: keyPress };
setEventState(event);
const event = handleSSknSceneEvent(ctx);
ssknSceneSetters.forEach((fn) => fn(event));
}
},
[activeSSknComponent]
[ssknSceneSetters]
);
useEffect(() => {
@ -42,12 +33,7 @@ const SSknSceneManager = () => {
};
}, [handleKeyPress]);
return (
<>
<SceneManager eventState={eventState!} />
<SSknComponentManager eventState={eventState!} />
</>
);
return null;
};
export default SSknSceneManager;

View file

@ -1,55 +0,0 @@
import { useCallback, useEffect } from "react";
import { StateManagerProps } from "../EventManager";
import { useStore } from "../../../store";
const SSknComponentManager = (props: StateManagerProps) => {
const toggleComponentMatrixIdx = useStore(
(state) => state.toggleSSknComponentMatrixIdx
);
const resetComponentMatrixIdx = useStore(
(state) => state.resetSSknComponentMatrixIdx
);
const setSSknLoading = useStore((state) => state.setSSknLoading);
const dispatchObject = useCallback(
(eventState: { event: string }) => {
switch (eventState.event) {
case "throw_node_sskn":
case "rip_node_sskn":
return {
action: resetComponentMatrixIdx,
};
case "sskn_ok_down":
case "sskn_cancel_up":
return {
action: toggleComponentMatrixIdx,
};
case "sskn_ok_select":
return {
action: setSSknLoading,
value: true,
};
case "sskn_cancel_select":
return {
action: setSSknLoading,
value: false,
};
}
},
[resetComponentMatrixIdx, setSSknLoading, toggleComponentMatrixIdx]
);
useEffect(() => {
if (props.eventState) {
const dispatchedObject = dispatchObject(props.eventState);
if (dispatchedObject) {
dispatchedObject.action(dispatchedObject.value as any);
}
}
}, [props.eventState, dispatchObject]);
return null;
};
export default SSknComponentManager;

View file

@ -1,212 +1,216 @@
import nodeSelector, { getNode, getNodeById, getNodeHud } from "./nodeSelector";
import { findNode } from "./utils/nodeUtils";
import {findNode, getNodeById} from "../utils/nodeUtils";
const handleMainSceneEvent = (gameContext: any) => {
let event;
const handleMainSceneEvent = (mainSceneContext: any) => {
const {
subscene,
selectedLevel,
pauseMatrixIdx,
activePauseComponent,
gameProgress,
currentSite,
siteRotY,
activeNode,
level,
keyPress,
} = mainSceneContext;
const keyPress = gameContext.keyPress;
const subscene = gameContext.mainSubscene;
const selectedLevel = gameContext.selectedLevel;
const pauseMatrixIdx = gameContext.pauseMatrixIdx;
const activePauseComponent = gameContext.activePauseComponent;
const gameProgress = gameContext.gameProgress;
const currentSite = gameContext.currentSite;
switch (subscene) {
case "site":
switch (keyPress) {
case "LEFT":
case "RIGHT": {
const keyPressToLower = keyPress.toLowerCase();
const siteASaveState = gameContext.siteASaveState;
const siteBSaveState = gameContext.siteBSaveState;
const nodeData = findNode(
keyPressToLower,
activeNode.matrixIndices!,
level,
currentSite,
gameProgress
);
let activeNode = gameContext.activeNode;
let activeHud;
let nodeMatrixIndices = gameContext.nodeMatrixIndices;
let level = parseInt(gameContext.activeLevel);
let siteRotY = gameContext.siteRotY;
let sitePosY = gameContext.sitePosY;
let scene = gameContext.scene;
if (!nodeData) return;
if (subscene === "site") {
switch (keyPress) {
case "LEFT":
case "RIGHT":
const keyPressToLower = keyPress.toLowerCase();
const nodeData = findNode.apply(null, [
keyPressToLower,
nodeMatrixIndices,
level,
currentSite,
gameProgress
]);
if (nodeData) {
return {
event: nodeData.didMove
? `site_${keyPressToLower}`
: "change_node",
siteRotY:
keyPressToLower === "left"
? siteRotY + Math.PI / 4
: siteRotY - Math.PI / 4,
nodeMatrixIndices: nodeData.matrixIndices,
node: getNodeById(nodeData.node, currentSite),
};
}
break;
case "UP":
case "DOWN":
const keyp = keyPress.toLowerCase();
const t = findNode.apply(null, [
keyp,
nodeMatrixIndices,
level,
currentSite,
gameProgress
]);
if (t) {
return {
event: t.didMove ? `site_${keyp}` : "change_node",
nodeMatrixIndices: t.matrixIndices,
level: (keyp === "up" ? level + 1 : level - 1)
.toString()
.padStart(2, "0"),
node: getNodeById(t.node, currentSite),
};
}
break;
case "CIRCLE":
activeNode = getNode(level, nodeMatrixIndices, currentSite);
const nodeType = activeNode.type;
const eventAnimation = Math.random() < 0.4 ? "rip_node" : "throw_node";
switch (nodeType) {
case 0:
case 2:
case 4:
case 3:
case 5:
event = `${eventAnimation}_media`;
scene = "media";
break;
case 6:
if (activeNode.node_name.substr(0, 3) === "TaK") {
event = `${eventAnimation}_tak`;
scene = "tak";
} else {
event = `${eventAnimation}_media`;
scene = "media";
}
break;
case 8:
event = `${eventAnimation}_gate`;
scene = "gate";
break;
case 7:
event = `${eventAnimation}_sskn`;
scene = "sskn";
break;
}
break;
case "L2":
return { event: "toggle_level_selection", level: level };
case "TRIANGLE":
return { event: "pause_game" };
case "SPACE":
return { event: "play_with_hair", siteRotY: siteRotY };
}
return {
event: event,
nodeMatrixIndices: nodeMatrixIndices,
sitePosY: sitePosY,
siteRotY: siteRotY,
level: level.toString().padStart(2, "0"),
scene: scene,
node: activeNode,
hud: activeHud,
};
} else if (subscene === "level_selection") {
switch (keyPress) {
case "UP":
if (currentSite === "a") {
if (selectedLevel + 1 <= 22)
if (nodeData.didMove) {
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
event: keyPressToLower === "left" ? `site_left` : "site_right",
siteRotY:
keyPressToLower === "left"
? siteRotY + Math.PI / 4
: siteRotY - Math.PI / 4,
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
} else if (currentSite === "b") {
if (selectedLevel + 1 <= 13)
} else {
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
event: "change_node",
nodeMatrixIndices: nodeData.matrixIndices,
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
}
}
break;
case "DOWN":
if (selectedLevel - 1 >= 1)
return {
event: `level_selection_down`,
selectedLevelIdx: selectedLevel - 1,
};
break;
case "X":
return {
event: "level_selection_back",
node: getNode(level, nodeMatrixIndices, currentSite),
hud: getNodeHud(nodeMatrixIndices),
};
case "CIRCLE":
const selectedNodeData = nodeSelector({
action: "select_level",
activeId: activeNode,
nodeMatrixIndices: nodeMatrixIndices,
level: selectedLevel,
siteRotY: siteRotY,
sitePosY: sitePosY,
gameProgress: gameProgress,
currentSite: currentSite,
});
case "UP":
case "DOWN": {
const keyPressToLower = keyPress.toLowerCase();
const nodeData = findNode(
keyPressToLower,
activeNode.matrixIndices!,
level,
currentSite,
gameProgress
);
if (level === selectedLevel) break;
if (selectedNodeData) {
const event =
selectedLevel < level ? "select_level_down" : "select_level_up";
return {
event: event,
nodeMatrixIndices: selectedNodeData.nodeMatrixIndices,
sitePosY: -selectedNodeData.sitePosY!,
level: selectedLevel.toString().padStart(2, "0"),
node: selectedNodeData.node,
hud: selectedNodeData.activeHud,
};
if (!nodeData) return;
if (nodeData.didMove) {
return {
event: keyPressToLower === "up" ? "site_up" : "site_down",
level: (keyPressToLower === "up" ? level + 1 : level - 1)
.toString()
.padStart(2, "0"),
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
} else {
return {
event: "change_node",
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
};
}
}
}
} else if (subscene === "pause") {
switch (keyPress) {
case "UP":
if (pauseMatrixIdx - 1 < 0) break;
return {
event: "pause_up",
pauseMatrixIdx: pauseMatrixIdx - 1,
};
case "DOWN":
if (pauseMatrixIdx + 1 > 4) break;
return {
event: "pause_down",
pauseMatrixIdx: pauseMatrixIdx + 1,
};
case "CIRCLE":
return {
event: `pause_${activePauseComponent}_select`,
currentSitePosY: sitePosY,
currentSiteRotY: siteRotY,
currentNodeId: activeNode,
currentNodeMatrixIndices: nodeMatrixIndices,
currentHud: getNodeHud(nodeMatrixIndices),
currentLevel: level.toString().padStart(2, "0"),
site: currentSite === "a" ? "b" : "a",
};
}
case "CIRCLE":
const eventAnimation =
Math.random() < 0.4 ? "rip_node" : "throw_node";
const nodeType = activeNode.type;
switch (nodeType) {
case 0:
case 2:
case 4:
case 3:
case 5:
return {
event: `${eventAnimation}_media`,
scene: "media",
};
case 6:
if (activeNode.node_name.substr(0, 3) === "TaK") {
return {
event: `${eventAnimation}_tak`,
scene: "tak",
};
} else {
return {
event: `${eventAnimation}_media`,
scene: "media",
};
}
case 8:
return {
event: `${eventAnimation}_gate`,
scene: "gate",
};
case 7:
return {
event: `${eventAnimation}_sskn`,
scene: "sskn",
};
}
break;
case "L2":
return { event: "toggle_level_selection", level: level };
case "TRIANGLE":
return { event: "pause_game" };
case "SPACE":
return { event: "play_with_hair", siteRotY: siteRotY };
}
break;
case "level_selection":
switch (keyPress) {
case "UP":
if (currentSite === "a") {
if (selectedLevel + 1 <= 22)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
} else if (currentSite === "b") {
if (selectedLevel + 1 <= 13)
return {
event: `level_selection_up`,
selectedLevelIdx: selectedLevel + 1,
};
}
break;
case "DOWN":
if (selectedLevel - 1 >= 1)
return {
event: `level_selection_down`,
selectedLevelIdx: selectedLevel - 1,
};
break;
case "X":
return {
event: "level_selection_back",
};
case "CIRCLE":
if (level === selectedLevel) return;
const direction = selectedLevel > level ? "up" : "down";
const nodeData = findNode(
direction,
activeNode.matrixIndices!,
selectedLevel,
currentSite,
gameProgress
);
if (nodeData) {
const event =
selectedLevel < level ? "select_level_down" : "select_level_up";
return {
event: event,
node: {
...getNodeById(nodeData.node, currentSite),
matrixIndices: nodeData.matrixIndices,
},
level: selectedLevel.toString().padStart(2, "0"),
};
}
}
break;
case "pause":
switch (keyPress) {
case "UP":
if (pauseMatrixIdx - 1 < 0) break;
return {
event: "pause_up",
pauseMatrixIdx: pauseMatrixIdx - 1,
};
case "DOWN":
if (pauseMatrixIdx + 1 > 4) break;
return {
event: "pause_down",
pauseMatrixIdx: pauseMatrixIdx + 1,
};
case "CIRCLE":
return {
event: `pause_${activePauseComponent}_select`,
};
}
}
};

View file

@ -1,11 +1,10 @@
const handleMediaSceneEvent = (gameContext: any) => {
const keyPress = gameContext.keyPress;
const activeMediaComponent = gameContext.activeMediaComponent;
const wordPosStateIdx = gameContext.wordPosStateIdx;
const rightSideComponentIdx = gameContext.rightSideComponentIdx;
const rightSideComponents = ["fstWord", "sndWord", "thirdWord"];
const handleMediaSceneEvent = (mediaSceneContext: any) => {
const {
keyPress,
activeMediaComponent,
wordPosStateIdx,
rightSideComponentIdx,
} = mediaSceneContext;
const calculateNewRightSide = (
direction: string,
@ -43,7 +42,7 @@ const handleMediaSceneEvent = (gameContext: any) => {
case "DOWN":
case "RIGHT":
case "LEFT":
if (rightSideComponents.includes(activeMediaComponent)) {
if (["fstWord", "sndWord", "thirdWord"].includes(activeMediaComponent)) {
const rightSide = calculateNewRightSide(
keyPress,
wordPosStateIdx,

View file

@ -1,475 +0,0 @@
import node_matrices from "../resources/node_matrices.json";
import site_a from "../resources/site_a.json";
import site_b from "../resources/site_b.json";
import {
NodeDataType,
SiteType,
} from "../components/MainScene/SyncedComponents/Site";
import unlocked_nodes from "../resources/initial_progress.json";
import level_y_values from "../resources/level_y_values.json";
import node_huds from "../resources/node_huds.json";
import filterInvisibleNodes from "./utils/filterInvisibleNodes";
import {
findNode,
getVisibleNodesMatrix,
} from "./utils/nodeUtils";
type NodeSelectorContext = {
action: string;
activeId: string;
nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number };
level: number;
siteRotY: number;
sitePosY: number;
gameProgress: typeof unlocked_nodes;
currentSite: string;
};
const hudAssocs = {
"00": "fg_hud_1",
"10": "fg_hud_2",
"20": "fg_hud_3",
"01": "bg_hud_1",
"11": "bg_hud_2",
"21": "bg_hud_3",
"02": "bg_hud_4",
"12": "bg_hud_5",
"22": "bg_hud_6",
"03": "fg_hud_4",
"13": "fg_hud_5",
"23": "fg_hud_6",
};
export const getNodeById = (id: string, currentSite: string) => {
const siteData = currentSite === "a" ? site_a : site_b;
const level = id.substr(0, 2);
return (siteData as SiteType)[level][id];
};
export const getNode = (
level: number,
nodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
},
currentSite: string
) => {
const siteData = currentSite === "a" ? site_a : site_b;
const formattedLevel = level.toString().padStart(2, "0");
const nodePos =
node_matrices[
nodeMatrixIndices.matrixIdx.toString() as keyof typeof node_matrices
][nodeMatrixIndices.rowIdx][nodeMatrixIndices.colIdx];
const id = formattedLevel + nodePos;
return (siteData as SiteType)[formattedLevel][id];
};
export const getNodeHud = (nodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
}) =>
node_huds[
hudAssocs[
`${nodeMatrixIndices.rowIdx}${nodeMatrixIndices.colIdx}` as keyof typeof hudAssocs
] as keyof typeof node_huds
];
export const isNodeVisible = (
node: NodeDataType,
gameProgress: typeof unlocked_nodes
) => {
if (node) {
const unlockedBy = node.unlocked_by;
let unlocked;
if (unlockedBy === "") unlocked = true;
else
unlocked =
gameProgress[unlockedBy as keyof typeof gameProgress].is_viewed;
// visible = (global_final_viewcount > 0) && (req_final_viewcount <= global_final_viewcount + 1)
return (
unlocked &&
gameProgress[node.node_name as keyof typeof gameProgress].is_visible
);
} else {
return false;
}
};
const tryCol = (col: number, triedCols: number[]) => {
const possibleCols = [0, 1, 2, 3].filter((elem) => elem !== col);
return possibleCols.find((elem) => !triedCols.includes(elem));
};
const tryRow = (row: number, triedRows: number[]) => {
const possibleRows = [0, 1, 2].filter((elem) => elem !== row);
return possibleRows.find((elem) => !triedRows.includes(elem));
};
const findNodeAfterLevelSelection = (
gameProgress: typeof unlocked_nodes,
targetLevel: number,
nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number },
currentSite: string
) => {
let newMatIndices = Object.assign({}, nodeMatrixIndices);
let triedCols: number[] = [];
newMatIndices.rowIdx = 0;
let newNode: NodeDataType | "UNKNOWN" = getNode(
targetLevel,
newMatIndices,
currentSite
);
while (!isNodeVisible(newNode, gameProgress)) {
if (triedCols.length < 4) {
triedCols.push(newMatIndices.colIdx);
const colToTry = tryCol(newMatIndices.colIdx, triedCols);
if (colToTry !== undefined) {
newMatIndices.colIdx = colToTry;
}
} else {
if (newMatIndices.rowIdx === 2) {
newMatIndices.colIdx = nodeMatrixIndices.colIdx;
newNode = "UNKNOWN";
break;
} else {
newMatIndices.rowIdx++;
triedCols = [];
newMatIndices.colIdx = 0;
}
}
newNode = getNode(targetLevel, newMatIndices, currentSite);
}
const newNodeHud = getNodeHud(newMatIndices);
return {
newLevel: targetLevel,
node: newNode,
newNodeHud: newNodeHud,
newNodeMatrixIndices: newMatIndices,
newSitePosY:
level_y_values[
targetLevel.toString().padStart(2, "0") as keyof typeof level_y_values
],
};
};
const findNodeVertical = (
direction: string,
gameProgress: typeof unlocked_nodes,
level: number,
nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number },
currentSite: string
) => {
let newNode: NodeDataType | "UNKNOWN";
let newLevel = level;
let newMatIndices = Object.assign({}, nodeMatrixIndices);
if (direction === "down") {
newMatIndices.rowIdx++;
let triedCols: number[] = [];
if (newMatIndices.rowIdx > 2) {
newMatIndices.rowIdx = 0;
newLevel = level - 1;
}
newNode = getNode(newLevel, newMatIndices, currentSite);
while (!isNodeVisible(newNode, gameProgress)) {
if (triedCols.length < 4) {
triedCols.push(newMatIndices.colIdx);
const colToTry = tryCol(newMatIndices.colIdx, triedCols);
if (colToTry !== undefined) {
newMatIndices.colIdx = colToTry;
}
} else {
if (newMatIndices.rowIdx === 2) {
if (newLevel === level - 1) {
newNode = "UNKNOWN";
newMatIndices.colIdx = nodeMatrixIndices.colIdx;
break;
}
newMatIndices.rowIdx = 0;
newLevel = level - 1;
} else {
newMatIndices.rowIdx++;
newMatIndices.colIdx = 0;
triedCols = [];
}
}
newNode = getNode(newLevel, newMatIndices, currentSite);
}
} else if (direction === "up") {
newMatIndices.rowIdx--;
let triedCols: number[] = [];
if (newMatIndices.rowIdx < 0) {
newMatIndices.rowIdx = 2;
newLevel = level + 1;
}
newNode = getNode(newLevel, newMatIndices, currentSite);
while (!isNodeVisible(newNode, gameProgress)) {
if (triedCols.length < 4) {
triedCols.push(newMatIndices.colIdx);
const colToTry = tryCol(newMatIndices.colIdx, triedCols);
if (colToTry !== undefined) {
newMatIndices.colIdx = colToTry;
}
} else {
if (newMatIndices.rowIdx === 0) {
if (newLevel === level + 1) {
newNode = "UNKNOWN";
newMatIndices.colIdx = nodeMatrixIndices.colIdx;
break;
}
newMatIndices.rowIdx = 2;
newLevel = level + 1;
} else {
newMatIndices.rowIdx--;
newMatIndices.colIdx = 0;
triedCols = [];
}
}
newNode = getNode(newLevel, newMatIndices, currentSite);
}
}
return {
node: newNode!,
newNodeHud: getNodeHud(newMatIndices),
newLevel: newLevel,
newNodeMatrixIndices: newMatIndices,
};
};
const findNodeHorizontal = (
direction: string,
gameProgress: typeof unlocked_nodes,
level: number,
activeId: string,
nodeMatrixIndices: { matrixIdx: number; rowIdx: number; colIdx: number },
currentSite: string
) => {
let newNode: NodeDataType | "UNKNOWN";
let newMatIndices = Object.assign({}, nodeMatrixIndices);
let didMove = false;
if (direction === "left") {
newMatIndices.colIdx--;
let triedRows: number[] = [];
if (newMatIndices.colIdx < 0) {
didMove = true;
newMatIndices.colIdx = 0;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx + 1 > 8 ? 1 : newMatIndices.matrixIdx + 1;
}
newNode = getNode(level, newMatIndices, currentSite);
while (!isNodeVisible(newNode, gameProgress)) {
if (triedRows.length < 3) {
triedRows.push(newMatIndices.rowIdx);
const rowToTry = tryRow(newMatIndices.rowIdx, triedRows);
if (rowToTry !== undefined) {
newMatIndices.rowIdx = rowToTry;
}
} else {
if (newMatIndices.colIdx > 3 && didMove) return;
if (newMatIndices.colIdx < 0) {
if (activeId === "UNKNOWN") {
didMove = true;
newMatIndices.colIdx = nodeMatrixIndices.colIdx;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx + 1 > 8 ? 1 : newMatIndices.matrixIdx + 1;
newNode = "UNKNOWN";
break;
} else {
didMove = true;
newMatIndices.colIdx = 0;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx + 1 > 8 ? 1 : newMatIndices.matrixIdx + 1;
}
} else {
didMove ? newMatIndices.colIdx++ : newMatIndices.colIdx--;
triedRows = [];
newMatIndices.rowIdx = 0;
}
}
newNode = getNode(level, newMatIndices, currentSite);
}
} else if (direction === "right") {
newMatIndices.colIdx++;
let triedRows: number[] = [];
if (newMatIndices.colIdx > 3) {
didMove = true;
newMatIndices.colIdx = 3;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx - 1 < 1 ? 8 : newMatIndices.matrixIdx - 1;
}
newNode = getNode(level, newMatIndices, currentSite);
while (!isNodeVisible(newNode, gameProgress)) {
if (triedRows.length < 3) {
triedRows.push(newMatIndices.rowIdx);
const rowToTry = tryRow(newMatIndices.rowIdx, triedRows);
if (rowToTry !== undefined) {
newMatIndices.rowIdx = rowToTry;
}
} else {
if (newMatIndices.colIdx < 0 && didMove) return;
if (newMatIndices.colIdx > 3) {
if (activeId === "UNKNOWN") {
didMove = true;
newMatIndices.colIdx = nodeMatrixIndices.colIdx;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx - 1 < 1 ? 8 : newMatIndices.matrixIdx - 1;
newNode = "UNKNOWN";
break;
} else {
if (didMove) return;
else {
didMove = true;
newMatIndices.colIdx = 3;
newMatIndices.matrixIdx =
newMatIndices.matrixIdx - 1 < 1
? 8
: newMatIndices.matrixIdx - 1;
}
}
} else {
didMove ? newMatIndices.colIdx-- : newMatIndices.colIdx++;
triedRows = [];
newMatIndices.rowIdx = 0;
}
}
newNode = getNode(level, newMatIndices, currentSite);
}
}
const newNodeHud = getNodeHud(newMatIndices);
return {
didMove: didMove,
node: newNode!,
newNodeHud: newNodeHud,
newNodeMatrixIndices: newMatIndices,
};
};
const nodeSelector = (context: NodeSelectorContext) => {
let newNodeData;
let move;
switch (context.action) {
case "site_left":
case "site_right":
const t = findNode(
context.action === "site_right" ? "right" : "left",
context.nodeMatrixIndices,
context.level,
context.currentSite,
context.gameProgress
);
move = context.action === "site_left" ? "left" : "right";
newNodeData = findNodeHorizontal(
move,
context.gameProgress,
context.level,
context.activeId,
context.nodeMatrixIndices,
context.currentSite
);
console.log(t!.matrixIndices);
if (newNodeData) {
const siteRotYModifier = move === "left" ? Math.PI / 4 : -Math.PI / 4;
return {
event: newNodeData.didMove ? context.action : "change_node",
node: getNodeById(t!.node, context.currentSite),
newActiveHud: newNodeData.newNodeHud,
newNodeMatrixIndices: t!.matrixIndices,
newSiteRotY: newNodeData.didMove
? context.siteRotY + siteRotYModifier
: context.siteRotY,
newSitePosY: context.sitePosY,
newLevel: context.level,
};
}
break;
case "site_up":
case "site_down":
move = context.action === "site_up" ? "up" : "down";
newNodeData = findNodeVertical(
move,
context.gameProgress,
context.level,
context.nodeMatrixIndices,
context.currentSite
);
if (newNodeData) {
const didMove = context.level !== newNodeData.newLevel;
const sitePosYModifier = move === "up" ? -1.5 : 1.5;
return {
event: didMove ? context.action : "change_node",
node: newNodeData.node,
newNodeMatrixIndices: newNodeData.newNodeMatrixIndices,
newActiveHud: newNodeData.newNodeHud,
newSiteRotY: context.siteRotY,
newSitePosY: didMove
? context.sitePosY + sitePosYModifier
: context.sitePosY,
newLevel: newNodeData.newLevel,
};
}
break;
case "select_level":
newNodeData = findNodeAfterLevelSelection(
context.gameProgress,
context.level,
context.nodeMatrixIndices,
context.currentSite
);
if (newNodeData) {
return {
node: newNodeData.node,
nodeMatrixIndices: newNodeData.newNodeMatrixIndices,
activeHud: newNodeData.newNodeHud,
siteRotY: context.siteRotY,
level: newNodeData.newLevel,
sitePosY: newNodeData.newSitePosY,
};
}
}
};
export default nodeSelector;

View file

@ -0,0 +1,29 @@
import { useStore } from "../../../store";
const bootSubsceneManager = (eventState: any) => {
const setBootSubscene = useStore.getState().setBootSubscene;
const dispatchAction = (eventState: { event: string }) => {
switch (eventState.event) {
case "authorize_user_back":
case "load_data_no_select":
return {
action: () => setBootSubscene("main_menu"),
};
case "authorize_user_select":
return {
action: () => setBootSubscene("authorize_user"),
};
case "load_data_select":
return { action: () => setBootSubscene("load_data") };
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default bootSubsceneManager;

View file

@ -0,0 +1,33 @@
import { useStore } from "../../../store";
const idleManager = (eventState: any) => {
const dispatchAction = (eventState: {
media: string;
images?: { "1": string; "2": string; "3": string };
}) => {
if (eventState.images) {
return {
action: () =>
useStore.setState({
idleMedia: eventState.media,
idleImages: eventState.images,
}),
};
} else {
return {
action: () =>
useStore.setState({
idleMedia: eventState.media,
}),
};
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default idleManager;

View file

@ -0,0 +1,26 @@
import { useStore } from "../../../../store";
const levelSelectionManager = (eventState: any) => {
const setSelectedLevel = useStore.getState().setSelectedLevel;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "toggle_level_selection":
return {
action: () => setSelectedLevel(eventState.level),
};
case "level_selection_up":
case "level_selection_down":
return {
action: () => setSelectedLevel(eventState.selectedLevelIdx),
};
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default levelSelectionManager;

View file

@ -0,0 +1,44 @@
import { useStore } from "../../../store";
const mainSubsceneManager = (eventState: any) => {
const setMainSubscene = useStore.getState().setMainSubscene;
const dispatchAction = (eventState: { event: string }) => {
switch (eventState.event) {
case "level_selection_back":
case "select_level_up":
case "select_level_down":
return {
action: () => setMainSubscene("site"),
delay: 0,
};
case "toggle_level_selection":
return {
action: () => setMainSubscene("level_selection"),
delay: 0,
};
case "pause_game":
return {
action: () => setMainSubscene("pause"),
value: "pause",
delay: 0,
};
case "pause_exit_select":
case "pause_change_select":
return {
action: () => setMainSubscene("site"),
delay: 1800,
};
}
};
const { action, delay } = { ...dispatchAction(eventState) };
if (action) {
setTimeout(() => {
action();
}, delay);
}
};
export default mainSubsceneManager;

View file

@ -0,0 +1,31 @@
import { useStore } from "../../../../store";
type PauseManagerProps = { event: string; pauseMatrixIdx: number };
const pauseManager = (eventState: any) => {
const setComponentMatrixIdx = useStore.getState().setPauseComponentMatrixIdx;
const setExitAnimation = useStore.getState().setPauseExitAnimation;
const dispatchAction = (eventState: PauseManagerProps) => {
switch (eventState.event) {
case "pause_up":
case "pause_down":
return {
action: () => setComponentMatrixIdx(eventState.pauseMatrixIdx),
};
case "pause_exit_select":
return {
action: () => setExitAnimation(true),
};
case "pause_game":
return { action: () => setExitAnimation(false) };
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default pauseManager;

View file

@ -0,0 +1,65 @@
import { useStore } from "../../../../store";
const lainManager = (eventState: any) => {
const setLainMoveState = useStore.getState().setLainMoveState;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "site_up":
case "site_down":
case "site_left":
case "site_right":
case "select_level_up":
case "select_level_down":
case "pause_game":
case "knock_node":
case "prayer":
case "touch_sleeve":
case "thinking":
case "stretch_2":
case "stretch":
case "spin":
case "scratch_head":
case "blush":
case "hands_behind_head":
case "hands_on_hips":
case "hands_on_hips_2":
case "hands_together":
case "lean_forward":
case "lean_left":
case "lean_right":
case "look_around":
case "play_with_hair":
return {
action: () => setLainMoveState(eventState.event),
duration: 3900,
};
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return { action: () => setLainMoveState("throw_node"), duration: 3900 };
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return { action: () => setLainMoveState("rip_node"), duration: 6000 };
case "knock_node_and_fall":
return {
action: () => setLainMoveState("knock_node_and_fall"),
duration: 6000,
};
}
};
const { action, duration } = { ...dispatchAction(eventState) };
if (action) {
action();
setTimeout(() => {
setLainMoveState("standing");
}, duration);
}
};
export default lainManager;

View file

@ -0,0 +1,23 @@
import { useStore } from "../../../../store";
const levelManager = (eventState: any) => {
const setActiveLevel = useStore.getState().setActiveLevel;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "site_up":
case "site_down":
case "select_level_up":
case "select_level_down":
return { action: () => setActiveLevel(eventState.level) };
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default levelManager;

View file

@ -0,0 +1,190 @@
import { useStore } from "../../../../store";
import { NodeDataType } from "../../../../components/MainScene/SyncedComponents/Site";
const nodeManager = (eventState: any) => {
const setActiveNode = useStore.getState().setNode;
const setActiveNodePos = useStore.getState().setNodePos;
const setActiveNodeRot = useStore.getState().setNodeRot;
const setActiveNodeState = useStore.getState().setNodeState;
const calculateCoordsBasedOnRotation = (
x: number,
z: number,
rotation: number
) => ({
x: x * Math.cos(rotation) - z * Math.sin(rotation),
z: x * Math.sin(rotation) + z * Math.cos(rotation),
});
const animateActiveNodeThrow = (siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(0.9, 0.3, siteRotY);
const sndCoordSet = calculateCoordsBasedOnRotation(0.5, 0.2, siteRotY);
const thirdCoordSet = calculateCoordsBasedOnRotation(1.55, 0.2, siteRotY);
const fourthCoordSet = calculateCoordsBasedOnRotation(0, 2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodePos([sndCoordSet.x, 0, sndCoordSet.z]);
}, 800);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, 0, sndCoordSet.z]);
setActiveNodeRot([0, 0, -0.005]);
}, 2600);
setTimeout(() => {
setActiveNodePos([fourthCoordSet.x, 0, fourthCoordSet.z]);
setActiveNodeRot([0, 0, -0.5]);
}, 2700);
setTimeout(() => {
setActiveNodeRot([0, 0, 0]);
setActiveNodeState(false, "interactedWith");
}, 3800);
};
const animateNodeKnock = (siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(1.1, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, -0.6, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
}, 2500);
};
const animateNodeKnockAndFall = (siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(1.1, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, -0.6, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(false, "visible");
}, 2300);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
}, 2500);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 3200);
};
const animateNodeTouchAndScare = (siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(-0.6, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodeState(true, "exploding");
setActiveNodeState(false, "visible");
}, 1200);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
setActiveNodeRot([0, 0, 0]);
}, 1400);
setTimeout(() => {
setActiveNodeState(false, "exploding");
}, 3150);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 3500);
};
const animateShrinkAndRip = (siteRotY: number) => {
setActiveNodeState(true, "interactedWith");
const fstCoordSet = calculateCoordsBasedOnRotation(0.9, 0.3, siteRotY);
const sndCoordSet = calculateCoordsBasedOnRotation(0.5, 0.2, siteRotY);
const thirdCoordSet = calculateCoordsBasedOnRotation(0, 0.2, siteRotY);
setActiveNodePos([fstCoordSet.x, 0, fstCoordSet.z]);
setTimeout(() => {
setActiveNodePos([sndCoordSet.x, 0, sndCoordSet.z]);
}, 800);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, -0.4, thirdCoordSet.z]);
}, 2800);
setTimeout(() => {
setActiveNodeState(true, "shrinking");
}, 3000);
setTimeout(() => {
setActiveNodePos([thirdCoordSet.x, -1.5, thirdCoordSet.z]);
}, 3200);
setTimeout(() => {
setActiveNodeState(false, "visible");
}, 3500);
setTimeout(() => {
setActiveNodeState(false, "interactedWith");
setActiveNodeState(false, "shrinking");
setActiveNodeRot([0, 0, 0]);
}, 6400);
setTimeout(() => {
setActiveNodeState(true, "visible");
}, 7500);
};
const updateActiveNode = (node: NodeDataType, delay?: number) => {
setTimeout(() => {
setActiveNode(node);
}, delay);
};
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "site_up":
case "site_down":
case "site_left":
case "site_right":
case "select_level_up":
case "select_level_down":
return {
action: () => updateActiveNode(eventState.node, 3900),
};
case "change_node":
return {
action: () => updateActiveNode(eventState.node),
};
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return {
action: () => animateActiveNodeThrow(eventState.siteRotY),
};
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return {
action: () => animateActiveNodeThrow(eventState.siteRotY),
};
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default nodeManager;

View file

@ -0,0 +1,37 @@
import { useStore } from "../../../../store";
const siteManager = (eventState: any) => {
const setRotY = useStore.getState().setSiteRotY;
const setRotX = useStore.getState().setSiteRotX;
const dispatchAction = (eventState: any) => {
switch (eventState.event) {
case "site_left":
case "site_right":
return {
action: () => setRotY(eventState.siteRotY),
delay: 1100,
};
case "pause_game":
return {
action: () => setRotX(Math.PI / 2),
delay: 3600,
};
case "pause_exit_select":
return {
action: () => setRotX(0),
delay: 0,
};
}
};
const { action, delay } = { ...dispatchAction(eventState) };
if (action) {
setTimeout(() => {
action();
}, delay);
}
};
export default siteManager;

View file

@ -0,0 +1,81 @@
import { useStore } from "../../../store";
import { useCallback } from "react";
import * as THREE from "three";
const mediaManager = (eventState: any) => {
const toggleSide = useStore.getState().toggleMediaSide;
const setLeftComponentMatrixIdx = useStore.getState()
.setMediaLeftComponentMatrixIdx;
const updateRightSide = useStore.getState().updateRightSide;
const resetScene = useStore.getState().resetMediaScene;
const setAudioAnalyser = useStore.getState().setAudioAnalyser;
const playMedia = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement && mediaElement.paused) {
const listener = new THREE.AudioListener();
const audio = new THREE.Audio(listener);
audio.setMediaElementSource(mediaElement);
setAudioAnalyser(new THREE.AudioAnalyser(audio, 2048));
mediaElement.play();
}
};
const exitMedia = () => {
const mediaElement = document.getElementById("media") as HTMLMediaElement;
if (mediaElement) {
mediaElement.pause();
mediaElement.currentTime = 0;
}
resetScene();
};
const dispatchAction = (eventState: {
event: string;
leftSideComponentIdx: 0 | 1;
rightSideComponentIdx: 0 | 1 | 2;
wordPosStateIdx: number;
}) => {
switch (eventState.event) {
case "media_rightside_down":
case "media_rightside_up":
return {
action: () =>
updateRightSide(
eventState.rightSideComponentIdx,
eventState.wordPosStateIdx
),
};
case "media_leftside_down":
case "media_leftside_up":
return {
action: () =>
setLeftComponentMatrixIdx(eventState.leftSideComponentIdx),
};
case "media_leftside_right":
case "media_rightside_left":
return {
action: () => toggleSide,
};
case "media_play_select":
return { action: () => playMedia };
case "media_exit_select":
return { action: () => exitMedia };
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default mediaManager;

View file

@ -0,0 +1,62 @@
import { useStore } from "../../store";
const sceneManager = (eventState: any) => {
const dispatchAction = (eventState: { event: string; scene: string }) => {
switch (eventState.event) {
case "throw_node_media":
case "throw_node_gate":
case "throw_node_sskn":
case "throw_node_tak":
return {
action: () =>
useStore.setState({ currentScene: eventState.scene, intro: false }),
delay: 3450,
};
case "rip_node_media":
case "rip_node_gate":
case "rip_node_sskn":
case "rip_node_tak":
return {
action: () =>
useStore.setState({ currentScene: eventState.scene, intro: false }),
delay: 6000,
};
case "media_exit_select":
case "exit_gate":
case "sskn_cancel_select":
return {
action: () =>
useStore.setState({ currentScene: "main", intro: false }),
delay: 0,
};
case "sskn_ok_select":
return {
action: () =>
useStore.setState({ currentScene: "main", intro: false }),
delay: 6000,
};
case "pause_change_select":
return {
action: () =>
useStore.setState({ currentScene: "change_dic", intro: true }),
delay: 0,
};
case "play_idle_media":
return {
action: () =>
useStore.setState({ currentScene: "idle_media", intro: false }),
delay: 0,
};
}
};
const { action, delay } = { ...dispatchAction(eventState) };
if (action) {
setTimeout(() => {
action();
}, delay);
}
};
export default sceneManager;

View file

@ -0,0 +1,40 @@
import { useStore } from "../../../store";
const ssknManager = (eventState: any) => {
const toggleComponentMatrixIdx = useStore.getState()
.toggleSSknComponentMatrixIdx;
const resetComponentMatrixIdx = useStore.getState()
.resetSSknComponentMatrixIdx;
const setSSknLoading = useStore.getState().setSSknLoading;
const dispatchAction = (eventState: { event: string }) => {
switch (eventState.event) {
case "throw_node_sskn":
case "rip_node_sskn":
return {
action: () => resetComponentMatrixIdx(),
};
case "sskn_ok_down":
case "sskn_cancel_up":
return {
action: () => toggleComponentMatrixIdx(),
};
case "sskn_ok_select":
return {
action: () => setSSknLoading(true),
};
case "sskn_cancel_select":
return {
action: () => setSSknLoading(false),
};
}
};
const { action } = { ...dispatchAction(eventState) };
if (action) {
action();
}
};
export default ssknManager;

View file

@ -1,6 +1,6 @@
const handleSSknSceneEvent = (gameContext: any) => {
const keyPress = gameContext.keyPress;
const activeSSknComponent = gameContext.activeSSknComponent;
const handleSSknSceneEvent = (ssknSceneContext: any) => {
const { keyPress, activeSSknComponent } = ssknSceneContext;
switch (keyPress) {
case "UP":
case "DOWN":

View file

@ -1,36 +0,0 @@
export const calculatePosAfterMove = (
currentData: {
siteRotY: number;
sitePosY: number;
matrixIndices: { rowIdx: number; colIdx: number; matrixIdx: number };
},
prevData: { siteRotY: number; sitePosY: number }
) => {
if (currentData.sitePosY > prevData.sitePosY) {
// todo
// instead of 1 i need to calculate the amplifier value
return { posX: 0, posY: 1 * 1 };
} else if (currentData.sitePosY < prevData.sitePosY) {
return { posX: 0, posY: -1 * 1 };
} else {
if (
currentData.matrixIndices.colIdx === 0 ||
currentData.matrixIndices.colIdx === 3
) {
if (currentData.siteRotY > prevData.siteRotY) {
return { posX: 1.5, posY: 0 };
} else if (currentData.siteRotY < prevData.siteRotY) {
return { posX: -1.5, posY: 0 };
}
} else if (
currentData.matrixIndices.colIdx === 1 ||
currentData.matrixIndices.colIdx === 2
) {
if (currentData.siteRotY > prevData.siteRotY) {
return { posX: 0.5, posY: 0 };
} else if (currentData.siteRotY < prevData.siteRotY) {
return { posX: -0.5, posY: 0 };
}
}
}
};

View file

@ -1,22 +0,0 @@
import { SiteType } from "../../components/MainScene/SyncedComponents/Site";
import game_progress from "../../resources/initial_progress.json";
import { isNodeVisible } from "../nodeSelector";
const filterInvisibleNodes = (
siteData: SiteType,
gameProgress: typeof game_progress
) => {
const visibleNodes: SiteType = {};
Object.entries(siteData).forEach((level) => {
visibleNodes[level[0]] = {};
Object.entries(level[1]).forEach((node) => {
if (isNodeVisible(node[1], gameProgress)) {
visibleNodes[level[0]][node[0]] = node[1];
}
});
});
return visibleNodes;
};
export default filterInvisibleNodes;

View file

@ -1,235 +0,0 @@
import { SiteType } from "../../components/MainScene/SyncedComponents/Site";
import node_matrices from "../../resources/node_matrices.json";
import { getNode, getNodeById, isNodeVisible } from "../nodeSelector";
export const generateInactiveNodes = (
visibleNodes: SiteType,
activeLevel: string
) => {
const obj = {};
const activeLevelNr = parseInt(activeLevel);
const visibleLevels = [
(activeLevelNr - 2).toString().padStart(2, "0"),
(activeLevelNr - 1).toString().padStart(2, "0"),
(activeLevelNr + 1).toString().padStart(2, "0"),
(activeLevelNr + 2).toString().padStart(2, "0"),
];
visibleLevels.forEach((level) => {
Object.assign(obj, visibleNodes[level as keyof typeof visibleNodes]);
});
return obj;
};
export const getVisibleNodesMatrix = (
matrixIdx: number,
activeLevel: number,
currentSite: string,
gameProgress: any
) => {
const formattedLevel = activeLevel.toString().padStart(2, "0");
const currentMatrix =
node_matrices[matrixIdx.toString() as keyof typeof node_matrices];
return currentMatrix.map((row: string[]) =>
row.map((nodePos: string) => {
const nodeId = formattedLevel + nodePos;
if (isNodeVisible(getNodeById(nodeId, currentSite), gameProgress))
return nodeId;
else return undefined;
})
);
};
function reorder<T>(array: T[], order: number[]): T[]
{
return order.map(i => array[i]);
}
function transpose<T>(matrix: T[][]): T[][]
{
return Object.keys(matrix[0])
.map(Number)
.map(
(c: number) => matrix.map(
(r: T[]) => r[c]));
}
function RowPrecedence(rowIdx: number): number[]
{
switch (rowIdx) {
default: case 0: return [0, 1, 2];
case 1: return [1, 0, 2];
case 2: return [2, 1, 0];
}
};
function ColPrecedence(colIdx: number): number[]
{
switch (colIdx) {
default: case 0: return [0, 1, 2, 3];
case 1: return [1, 0, 2, 3];
case 2: return [2, 1, 3, 0];
case 3: return [3, 2, 1, 0];
}
}
function getNodesMatrixWithIndices
(
matrixIdx: number,
activeLevel: number,
currentSite: string,
gameProgress: any
)
: any[][]
{
return getVisibleNodesMatrix(
matrixIdx,
activeLevel,
currentSite,
gameProgress
)
.map(
(r, i) => r.map(
(n, j) => n ? {
node: n,
matrixIndices: {
matrixIdx,
rowIdx: i,
colIdx: j
}
} : null
)
);
}
interface NodeMatrixIndices {
matrixIdx: number;
rowIdx: number;
colIdx: number;
}
function findNode_current
(
direction: string,
{matrixIdx, rowIdx, colIdx}: NodeMatrixIndices,
level: number,
currentSite: string,
gameProgress: any
)
: any | undefined
{
const nodes = getNodesMatrixWithIndices(
matrixIdx,
level,
currentSite,
gameProgress
);
const filters: any = {
left: () => transpose(
reorder(nodes, RowPrecedence(rowIdx))
.map(r => r.slice(0, colIdx).reverse())
).flat(),
right: () => transpose(
reorder(nodes, RowPrecedence(rowIdx))
.map(r => r.slice(colIdx + 1))
).flat(),
up: () => nodes.slice(0, rowIdx).reverse()
.flatMap(r => reorder(r, ColPrecedence(colIdx))),
down: () => nodes.slice(rowIdx + 1)
.flatMap(r => reorder(r, ColPrecedence(colIdx)))
};
const chosen = filters[direction]().find((e: any) => e);
if (chosen) return {...chosen, didMove: false};
}
function findNode_next
(
direction: string,
{matrixIdx, rowIdx, colIdx}: NodeMatrixIndices,
level: number,
currentSite: string,
gameProgress: any
)
: any | undefined
{
const funcs: any = {
left: {
getMatrix: () => getNodesMatrixWithIndices(
matrixIdx + 1 > 8 ? 1 : matrixIdx + 1,
level,
currentSite,
gameProgress
),
filter: (ns: any[]) => transpose(
reorder(ns, RowPrecedence(rowIdx))
).flat()
},
right: {
getMatrix: () => getNodesMatrixWithIndices(
matrixIdx - 1 < 1 ? 8 : matrixIdx - 1,
level,
currentSite,
gameProgress
),
filter: (ns: any[]) => transpose(
reorder(ns, RowPrecedence(rowIdx))
.map(r => [...r].reverse())
).flat()
},
up: {
getMatrix: () => getNodesMatrixWithIndices(
matrixIdx,
level + 1,
currentSite,
gameProgress
),
filter: (ns: any[]) => ns
.reverse()
.flatMap(r => reorder(r, ColPrecedence(colIdx)))
},
down: {
getMatrix: () => getNodesMatrixWithIndices(
matrixIdx,
level - 1,
currentSite,
gameProgress
),
filter: (ns: any[]) => ns
.flatMap(r => reorder(r, ColPrecedence(colIdx)))
}
};
const {getMatrix, filter} = funcs[direction];
const chosen = filter(getMatrix()).find((e: any) => e);
if (chosen) return {...chosen, didMove: true};
}
export function findNode(...args: [
string,
NodeMatrixIndices,
number,
string,
any
])
: any | undefined
{
return (
findNode_current(...args) ??
findNode_next(...args)
);
}

View file

@ -5,6 +5,7 @@ import authorize_user_letters from "./resources/authorize_user_letters.json";
import game_progress from "./resources/initial_progress.json";
import { HUDType } from "./components/MainScene/SyncedComponents/HUD";
import { NodeDataType } from "./components/MainScene/SyncedComponents/Site";
import { useCallback } from "react";
type SiteSaveState = {
a: {
@ -271,12 +272,17 @@ export const useStore = create(
// site setters
setActiveSite: (to: "a" | "b") => set(() => ({ activeSite: to })),
setSiteRot: (to: number[]) => set(() => ({ siteRot: to })),
setSiteRotY: (to: number) =>
set((prev) => {
const nextRot = [...prev.siteRot];
nextRot[1] = to;
return { siteRot: nextRot };
}),
setSiteRotX: (to: number) =>
set((prev) => {
const nextPos = [...prev.siteRot];
nextPos[0] = to;
return { siteRot: nextPos };
const nextRot = [...prev.siteRot];
nextRot[0] = to;
return { siteRot: nextRot };
}),
// level setters
@ -308,28 +314,27 @@ export const useStore = create(
leftSideIdx: to,
},
})),
setMediaRightComponentMatrixIdx: (to: 0 | 1 | 2) =>
updateRightSide: (matrixIdx: 0 | 1 | 2, wordPosIdx: number) =>
set((state) => ({
mediaComponentMatrixIndices: {
...state.mediaComponentMatrixIndices,
rightSideIdx: to,
rightSideIdx: matrixIdx,
},
mediaWordPosStateIdx: wordPosIdx,
})),
resetMediaComponentMatrixIndices: () =>
setPercentageElapsed: (to: number) =>
set(() => ({ mediaPercentageElapsed: to })),
setAudioAnalyser: (to: THREE.AudioAnalyser) =>
set(() => ({ audioAnalyser: to })),
resetMediaScene: () =>
set(() => ({
mediaWordPosStateIdx: 1,
mediaComponentMatrixIndices: {
sideIdx: 0,
leftSideIdx: 0,
rightSideIdx: 0,
},
})),
setPercentageElapsed: (to: number) =>
set(() => ({ mediaPercentageElapsed: to })),
setAudioAnalyser: (to: THREE.AudioAnalyser) =>
set(() => ({ audioAnalyser: to })),
setMediaWordPosStateIdx: (to: number) =>
set(() => ({ mediaWordPosStateIdx: to })),
resetMediaWordPosStateIdx: () => set(() => ({ mediaWordPosStateIdx: 1 })),
// idle media setters
setIdleMedia: (to: any) => set(() => ({ idleMedia: to })),
@ -414,4 +419,42 @@ export const useSiteSaveStore = create(
)
);
export const getMainSceneContext = () => useStore.getState().activeNode;
export const getMainSceneContext = () => {
const state = useStore.getState();
return {
subscene: state.mainSubscene,
selectedLevel: state.selectedLevel,
pauseMatrixIdx: state.pauseComponentMatrixIdx,
activePauseComponent:
state.pauseComponentMatrix[state.pauseComponentMatrixIdx],
gameProgress: state.gameProgress,
currentSite: state.activeSite,
siteRotY: state.siteRot[1],
activeNode: state.activeNode,
level: parseInt(state.activeLevel),
};
};
export const getSSknSceneContext = () => {
const state = useStore.getState();
return {
activeSSknComponent:
state.ssknComponentMatrix[state.ssknComponentMatrixIdx],
};
};
export const getMediaSceneContext = () => {
const state = useStore.getState();
return {
activeMediaComponent:
state.mediaComponentMatrix[state.mediaComponentMatrixIndices.sideIdx][
state.mediaComponentMatrixIndices.sideIdx === 0
? state.mediaComponentMatrixIndices.leftSideIdx
: state.mediaComponentMatrixIndices.rightSideIdx
],
rightSideComponentIdx: state.mediaComponentMatrixIndices.rightSideIdx,
wordPosStateIdx: state.mediaWordPosStateIdx,
};
};

84
src/utils/getIdleMedia.ts Normal file
View file

@ -0,0 +1,84 @@
import site_a from "../resources/site_a.json";
import site_b from "../resources/site_b.json";
import { SiteType } from "../components/MainScene/SyncedComponents/Site";
import { useStore } from "../store";
const getIdleMedia = (site: string) => {
const siteAIdleNodes = {
audio: [
"0000",
"0001",
"0002",
"0003",
"0004",
"0005",
"0006",
"0007",
"0008",
"0009",
],
video: [
"INS01.STR",
"INS02.STR",
"INS03.STR",
"INS04.STR",
"INS05.STR",
"INS06.STR",
"INS07.STR",
"INS08.STR",
"INS09.STR",
"INS10.STR",
"INS11.STR",
"INS12.STR",
"INS13.STR",
"INS14.STR",
"INS15.STR",
"INS16.STR",
"INS17.STR",
"INS18.STR",
"INS19.STR",
"INS20.STR",
"INS21.STR",
"INS22.STR",
],
};
const siteBIdleNodes = {
audio: ["1015", "1219", "0419", "0500", "0501", "0508", "0510", "0513"],
video: [
"INS16.STR",
"INS17.STR",
"INS18.STR",
"INS19.STR",
"INS20.STR",
"INS21.STR",
"INS22.STR",
],
};
const siteData = site === "a" ? site_a : site_b;
const idleNodes = site === "a" ? siteAIdleNodes : siteBIdleNodes;
if (Math.random() < 0.5) {
const nodeToPlay =
idleNodes.audio[Math.floor(Math.random() * idleNodes.audio.length)];
const level = nodeToPlay.substr(0, 2);
const images = (siteData as SiteType)[level][nodeToPlay]
.image_table_indices;
const media = (siteData as SiteType)[level][nodeToPlay].media_file;
useStore.setState({
idleImages: images,
idleMedia: media,
});
} else {
useStore.setState({
idleMedia:
idleNodes.video[Math.floor(Math.random() * idleNodes.video.length)],
});
}
};
export default getIdleMedia;

306
src/utils/nodeUtils.ts Normal file
View file

@ -0,0 +1,306 @@
import {
NodeDataType,
SiteType,
} from "../components/MainScene/SyncedComponents/Site";
import node_matrices from "../resources/node_matrices.json";
import game_progress from "../resources/initial_progress.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_b from "../resources/site_b.json";
export const generateInactiveNodes = (
visibleNodes: SiteType,
activeLevel: string
) => {
const obj = {};
const activeLevelNr = parseInt(activeLevel);
const visibleLevels = [
(activeLevelNr - 2).toString().padStart(2, "0"),
(activeLevelNr - 1).toString().padStart(2, "0"),
(activeLevelNr + 1).toString().padStart(2, "0"),
(activeLevelNr + 2).toString().padStart(2, "0"),
];
visibleLevels.forEach((level) => {
Object.assign(obj, visibleNodes[level as keyof typeof visibleNodes]);
});
return obj;
};
export const getNodeById = (id: string, currentSite: string) => {
const siteData = currentSite === "a" ? site_a : site_b;
const level = id.substr(0, 2);
return (siteData as SiteType)[level][id];
};
export const getNodeHud = (nodeMatrixIndices: {
matrixIdx: number;
rowIdx: number;
colIdx: number;
}) => {
const hudAssocs = {
"00": "fg_hud_1",
"10": "fg_hud_2",
"20": "fg_hud_3",
"01": "bg_hud_1",
"11": "bg_hud_2",
"21": "bg_hud_3",
"02": "bg_hud_4",
"12": "bg_hud_5",
"22": "bg_hud_6",
"03": "fg_hud_4",
"13": "fg_hud_5",
"23": "fg_hud_6",
};
return node_huds[
hudAssocs[
`${nodeMatrixIndices.rowIdx}${nodeMatrixIndices.colIdx}` as keyof typeof hudAssocs
] as keyof typeof node_huds
];
};
export const isNodeVisible = (
node: NodeDataType,
gameProgress: typeof unlocked_nodes
) => {
if (node) {
const unlockedBy = node.unlocked_by;
let unlocked;
if (unlockedBy === "") unlocked = true;
else
unlocked =
gameProgress[unlockedBy as keyof typeof gameProgress].is_viewed;
// visible = (global_final_viewcount > 0) && (req_final_viewcount <= global_final_viewcount + 1)
return (
unlocked &&
gameProgress[node.node_name as keyof typeof gameProgress].is_visible
);
} else {
return false;
}
};
export const getVisibleNodesMatrix = (
matrixIdx: number,
activeLevel: number,
currentSite: string,
gameProgress: any
) => {
const formattedLevel = activeLevel.toString().padStart(2, "0");
const currentMatrix =
node_matrices[matrixIdx.toString() as keyof typeof node_matrices];
return currentMatrix.map((row: string[]) =>
row.map((nodePos: string) => {
const nodeId = formattedLevel + nodePos;
if (isNodeVisible(getNodeById(nodeId, currentSite), gameProgress))
return nodeId;
else return undefined;
})
);
};
function reorder<T>(array: T[], order: number[]): T[] {
return order.map((i) => array[i]);
}
function transpose<T>(matrix: T[][]): T[][] {
return Object.keys(matrix[0])
.map(Number)
.map((c: number) => matrix.map((r: T[]) => r[c]));
}
function RowPrecedence(rowIdx: number): number[] {
switch (rowIdx) {
default:
case 0:
return [0, 1, 2];
case 1:
return [1, 0, 2];
case 2:
return [2, 1, 0];
}
}
function ColPrecedence(colIdx: number): number[] {
switch (colIdx) {
default:
case 0:
return [0, 1, 2, 3];
case 1:
return [1, 0, 2, 3];
case 2:
return [2, 1, 3, 0];
case 3:
return [3, 2, 1, 0];
}
}
function getNodesMatrixWithIndices(
matrixIdx: number,
activeLevel: number,
currentSite: string,
gameProgress: any
): any[][] {
return getVisibleNodesMatrix(
matrixIdx,
activeLevel,
currentSite,
gameProgress
).map((r, i) =>
r.map((n, j) =>
n
? {
node: n,
matrixIndices: {
matrixIdx,
rowIdx: i,
colIdx: j,
},
}
: null
)
);
}
interface NodeMatrixIndices {
matrixIdx: number;
rowIdx: number;
colIdx: number;
}
function findNode_current(
direction: string,
{ matrixIdx, rowIdx, colIdx }: NodeMatrixIndices,
level: number,
currentSite: string,
gameProgress: any
): any | undefined {
const nodes = getNodesMatrixWithIndices(
matrixIdx,
level,
currentSite,
gameProgress
);
const filters: any = {
left: () =>
transpose(
reorder(nodes, RowPrecedence(rowIdx)).map((r) =>
r.slice(0, colIdx).reverse()
)
).flat(),
right: () =>
transpose(
reorder(nodes, RowPrecedence(rowIdx)).map((r) => r.slice(colIdx + 1))
).flat(),
up: () =>
nodes
.slice(0, rowIdx)
.reverse()
.flatMap((r) => reorder(r, ColPrecedence(colIdx))),
down: () =>
nodes.slice(rowIdx + 1).flatMap((r) => reorder(r, ColPrecedence(colIdx))),
};
const chosen = filters[direction]().find((e: any) => e);
if (chosen) return { ...chosen, didMove: false };
}
function findNode_next(
direction: string,
{ matrixIdx, rowIdx, colIdx }: NodeMatrixIndices,
level: number,
currentSite: string,
gameProgress: any
): any | undefined {
const funcs: any = {
left: {
getMatrix: () =>
getNodesMatrixWithIndices(
matrixIdx + 1 > 8 ? 1 : matrixIdx + 1,
level,
currentSite,
gameProgress
),
filter: (ns: any[]) =>
transpose(reorder(ns, RowPrecedence(rowIdx))).flat(),
},
right: {
getMatrix: () =>
getNodesMatrixWithIndices(
matrixIdx - 1 < 1 ? 8 : matrixIdx - 1,
level,
currentSite,
gameProgress
),
filter: (ns: any[]) =>
transpose(
reorder(ns, RowPrecedence(rowIdx)).map((r) => [...r].reverse())
).flat(),
},
up: {
getMatrix: () =>
getNodesMatrixWithIndices(
matrixIdx,
level + 1,
currentSite,
gameProgress
),
filter: (ns: any[]) =>
ns.reverse().flatMap((r) => reorder(r, ColPrecedence(colIdx))),
},
down: {
getMatrix: () =>
getNodesMatrixWithIndices(
matrixIdx,
level - 1,
currentSite,
gameProgress
),
filter: (ns: any[]) =>
ns.flatMap((r) => reorder(r, ColPrecedence(colIdx))),
},
};
const { getMatrix, filter } = funcs[direction];
const chosen = filter(getMatrix()).find((e: any) => e);
if (chosen) return { ...chosen, didMove: true };
}
export function findNode(
...args: [string, NodeMatrixIndices, number, string, any]
): any | undefined {
return findNode_current(...args) ?? findNode_next(...args);
}
export const filterInvisibleNodes = (
siteData: SiteType,
gameProgress: typeof game_progress
) => {
const visibleNodes: SiteType = {};
Object.entries(siteData).forEach((level) => {
visibleNodes[level[0]] = {};
Object.entries(level[1]).forEach((node) => {
if (isNodeVisible(node[1], gameProgress)) {
visibleNodes[level[0]][node[0]] = node[1];
}
});
});
return visibleNodes;
};