From 20934e2f193f2f680a5442eac48348560407b987 Mon Sep 17 00:00:00 2001 From: ad044 Date: Sat, 27 Feb 2021 19:39:31 +0400 Subject: [PATCH] phone swipe support, tweaked gate code, fixed idle, better input cooldowns --- src/App.tsx | 21 +- src/components/GateScene/GateMiddleObject.tsx | 34 ++- .../{BlueZero.tsx => BlueDigit.tsx} | 46 ++-- .../GateScene/GateMiddleObject/BlueOne.tsx | 77 ------- src/components/IdleManager.tsx | 77 +++++++ src/components/InputHandler.tsx | 204 ++++++++---------- src/components/MainScene/HUD.tsx | 49 ++--- src/core/eventTemplates.ts | 79 +++++-- .../input-handlers/handleMainSceneInput.ts | 13 +- src/helpers/idle-helpers.ts | 42 ++-- src/resources/initial_progress.json | 14 +- src/resources/site_a.json | 8 +- src/resources/site_b.json | 8 +- src/scenes/ChangeDiscScene.tsx | 7 +- src/scenes/IdleMediaScene.tsx | 1 + src/store.ts | 22 +- src/types/types.ts | 1 + src/utils/getKeyPress.ts | 16 ++ src/utils/parseUserInput.ts | 15 -- 19 files changed, 388 insertions(+), 346 deletions(-) rename src/components/GateScene/GateMiddleObject/{BlueZero.tsx => BlueDigit.tsx} (57%) delete mode 100644 src/components/GateScene/GateMiddleObject/BlueOne.tsx create mode 100644 src/components/IdleManager.tsx create mode 100644 src/utils/getKeyPress.ts delete mode 100644 src/utils/parseUserInput.ts diff --git a/src/App.tsx b/src/App.tsx index b13a9e1..c8cf5bf 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,6 +14,7 @@ import ChangeDiscScene from "./scenes/ChangeDiscScene"; import EndScene from "./scenes/EndScene"; import IdleMediaScene from "./scenes/IdleMediaScene"; import InputHandler from "./components/InputHandler"; +import { Html } from "@react-three/drei"; const App = () => { const currentScene = useStore((state) => state.currentScene); @@ -40,16 +41,16 @@ const App = () => { ); return ( -
- - - - - {/**/} - {dispatchScene[currentScene as keyof typeof dispatchScene]} - - - +
+ + + {/**/} + {dispatchScene[currentScene as keyof typeof dispatchScene]} + + + + + {["media", "idle_media", "tak", "end"].includes(currentScene) && ( )} diff --git a/src/components/GateScene/GateMiddleObject.tsx b/src/components/GateScene/GateMiddleObject.tsx index 79b365a..fe197b5 100644 --- a/src/components/GateScene/GateMiddleObject.tsx +++ b/src/components/GateScene/GateMiddleObject.tsx @@ -1,9 +1,8 @@ import React, { useEffect, useState } from "react"; -import BlueZero from "./GateMiddleObject/BlueZero"; -import BlueOne from "./GateMiddleObject/BlueOne"; import { a, useSpring, useSprings } from "@react-spring/three"; import blue_digit_positions from "../../resources/blue_digit_positions.json"; import Mirror from "./GateMiddleObject/Mirror"; +import BlueDigit from "./GateMiddleObject/BlueDigit"; type GateMiddleObjectProps = { intro: boolean; @@ -16,14 +15,12 @@ const GateMiddleObject = (props: GateMiddleObjectProps) => { const [springs, set] = useSprings(44, (intIdx) => { const idx = intIdx.toString(); return { - type: blue_digit_positions[idx as keyof typeof blue_digit_positions].type, posX: blue_digit_positions[idx as keyof typeof blue_digit_positions] .initial_x, posY: blue_digit_positions[idx as keyof typeof blue_digit_positions] .initial_y, - visibility: false, config: { duration: 150 }, }; }); @@ -40,7 +37,6 @@ const GateMiddleObject = (props: GateMiddleObjectProps) => { .final_y, delay: blue_digit_positions[idx as keyof typeof blue_digit_positions].delay, - visibility: true, }; }); @@ -62,23 +58,17 @@ const GateMiddleObject = (props: GateMiddleObjectProps) => { position-z={middleObjectGroupState.posZ} visible={props.intro} > - {springs.map((item, idx) => - item.type.get() === 1 ? ( - - ) : ( - - ) - )} + {springs.map((item, idx) => ( + + ))} 0} diff --git a/src/components/GateScene/GateMiddleObject/BlueZero.tsx b/src/components/GateScene/GateMiddleObject/BlueDigit.tsx similarity index 57% rename from src/components/GateScene/GateMiddleObject/BlueZero.tsx rename to src/components/GateScene/GateMiddleObject/BlueDigit.tsx index d2069f6..f36a075 100644 --- a/src/components/GateScene/GateMiddleObject/BlueZero.tsx +++ b/src/components/GateScene/GateMiddleObject/BlueDigit.tsx @@ -1,29 +1,41 @@ import React, { useEffect, useMemo, useRef } from "react"; import { useLoader } from "react-three-fiber"; import * as THREE from "three"; -import gateBlueBinarySingularZero from "../../../static/sprites/gate/blue_binary_singular_zero.png"; +import gateBlueBinarySingularOne from "../../../static/sprites/gate/blue_binary_singular_one.png"; import { a, SpringValue } from "@react-spring/three"; +import gateBlueBinarySingularZero from "../../../static/sprites/gate/blue_binary_singular_zero.png"; -type BlueZeroProps = { +type BlueDigitProps = { + type: number; posX: SpringValue; posY: SpringValue; - visibility: SpringValue; }; -const BlueZero = (props: BlueZeroProps) => { +const BlueDigit = (props: BlueDigitProps) => { + const gateBlueBinarySingularOneTex = useLoader( + THREE.TextureLoader, + gateBlueBinarySingularOne + ); const gateBlueBinarySingularZeroTex = useLoader( THREE.TextureLoader, gateBlueBinarySingularZero ); + const objRef = useRef(); const matRef = useRef(); const uniforms = useMemo( () => ({ - zeroTex: { type: "t", value: gateBlueBinarySingularZeroTex }, + tex: { + type: "t", + value: + props.type === 1 + ? gateBlueBinarySingularOneTex + : gateBlueBinarySingularZeroTex, + }, brightnessMultiplier: { value: 1.5 }, }), - [gateBlueBinarySingularZeroTex] + [gateBlueBinarySingularOneTex, gateBlueBinarySingularZeroTex, props.type] ); const vertexShader = ` @@ -35,14 +47,13 @@ const BlueZero = (props: BlueZeroProps) => { } `; - const fragmentShaderZero = ` - uniform sampler2D zeroTex; + const fragmentShader = ` + uniform sampler2D tex; uniform float brightnessMultiplier; - varying vec2 vUv; void main() { - gl_FragColor = texture2D(zeroTex, vUv) * brightnessMultiplier; + gl_FragColor = texture2D(tex, vUv) * brightnessMultiplier; } `; @@ -50,21 +61,26 @@ const BlueZero = (props: BlueZeroProps) => { setTimeout(() => { if (matRef.current) { matRef.current.uniforms.brightnessMultiplier.value = 3.5; + matRef.current.uniformsNeedUpdate = true; } }, 1400); + setTimeout(() => { + if (objRef.current) objRef.current.visible = true; + }, 150); }, []); return ( - + { ); }; -export default BlueZero; +export default BlueDigit; diff --git a/src/components/GateScene/GateMiddleObject/BlueOne.tsx b/src/components/GateScene/GateMiddleObject/BlueOne.tsx deleted file mode 100644 index 950d071..0000000 --- a/src/components/GateScene/GateMiddleObject/BlueOne.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useEffect, useMemo, useRef } from "react"; -import { useLoader } from "react-three-fiber"; -import * as THREE from "three"; -import gateBlueBinarySingularOne from "../../../static/sprites/gate/blue_binary_singular_one.png"; -import { a, SpringValue } from "@react-spring/three"; - -type BlueOneProps = { - posX: SpringValue; - posY: SpringValue; - visibility: SpringValue; -}; - -const BlueOne = (props: BlueOneProps) => { - const gateBlueBinarySingularOneTex = useLoader( - THREE.TextureLoader, - gateBlueBinarySingularOne - ); - - const matRef = useRef(); - - const uniforms = useMemo( - () => ({ - oneTex: { type: "t", value: gateBlueBinarySingularOneTex }, - brightnessMultiplier: { value: 1.5 }, - }), - [gateBlueBinarySingularOneTex] - ); - - const vertexShader = ` - varying vec2 vUv; - - void main() { - vUv = uv; - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `; - - const fragmentShaderOne = ` - uniform sampler2D oneTex; - uniform float brightnessMultiplier; - varying vec2 vUv; - - void main() { - gl_FragColor = texture2D(oneTex, vUv) * brightnessMultiplier; - } - `; - - useEffect(() => { - setTimeout(() => { - if (matRef.current) - matRef.current.uniforms.brightnessMultiplier.value = 3.5; - }, 1400); - }, []); - - return ( - - - - - ); -}; - -export default BlueOne; diff --git a/src/components/IdleManager.tsx b/src/components/IdleManager.tsx new file mode 100644 index 0000000..46f7050 --- /dev/null +++ b/src/components/IdleManager.tsx @@ -0,0 +1,77 @@ +import { useFrame } from "react-three-fiber"; +import { playAudio, useStore } from "../store"; +import * as audio from "../static/audio/sfx"; +import { + playIdleAudio, + playIdleVideo, + playLainIdleAnim, +} from "../core/eventTemplates"; +import { + getRandomIdleLainAnim, + getRandomIdleMedia, +} from "../helpers/idle-helpers"; +import handleEvent from "../core/handleEvent"; + +type IdleManagerProps = { + lainIdleTimerRef: any; + idleSceneTimerRef: any; +}; + +const IdleManager = (props: IdleManagerProps) => { + const mainSubscene = useStore((state) => state.mainSubscene); + const scene = useStore((state) => state.currentScene); + + useFrame(() => { + const now = Date.now(); + if ( + props.lainIdleTimerRef.current !== -1 && + props.idleSceneTimerRef.current !== -1 && + mainSubscene !== "pause" && + mainSubscene !== "level_selection" && + scene === "main" + ) { + if (now > props.lainIdleTimerRef.current + 10000) { + // after one idle animation plays, the second comes sooner than it would after a regular keypress + props.lainIdleTimerRef.current = now - 2500; + + const [idleLainAnim, duration] = getRandomIdleLainAnim(); + + const event = playLainIdleAnim({ + lainMoveState: idleLainAnim, + duration: duration, + }); + + if (event) handleEvent(event); + } + if (now > props.idleSceneTimerRef.current + 500000) { + // put it on lock until the next action, since while the idle media plays, the + // Date.now() value keeps increasing, which can result in another idle media playing right after one finishes + // one way to work around this would be to modify the value depending on the last played idle media's duration + // but i'm way too lazy for that + props.idleSceneTimerRef.current = -1; + + playAudio(audio.sound32); + + const data = getRandomIdleMedia(); + + const { type, nodeName, images, media } = data; + let event; + if (type === "audio" && images && nodeName) { + event = playIdleAudio({ + idleNodeName: nodeName, + idleImages: images, + idleMedia: media, + }); + } else if (type === "video") { + event = playIdleVideo({ idleMedia: media }); + } + + if (event) handleEvent(event); + } + } + }); + + return null; +}; + +export default IdleManager; diff --git a/src/components/InputHandler.tsx b/src/components/InputHandler.tsx index 7dfa790..63254d7 100644 --- a/src/components/InputHandler.tsx +++ b/src/components/InputHandler.tsx @@ -1,154 +1,138 @@ -import { useCallback, useEffect, useRef } from "react"; +import React, { useCallback, useEffect, useRef } from "react"; import { getBootSceneContext, getEndSceneContext, getMainSceneContext, getMediaSceneContext, getSsknSceneContext, - playAudio, useStore, } from "../store"; -import { getKeyCodeAssociation } from "../utils/parseUserInput"; +import getKeyPress from "../utils/getKeyPress"; import handleMediaSceneInput from "../core/input-handlers/handleMediaSceneInput"; import handleSsknSceneInput from "../core/input-handlers/handleSsknSceneInput"; import handleMainSceneInput from "../core/input-handlers/handleMainSceneInput"; import handleBootSceneInput from "../core/input-handlers/handleBootSceneInput"; -import { useFrame } from "react-three-fiber"; -import { getRandomIdleLainAnim } from "../helpers/idle-helpers"; -import * as audio from "../static/audio/sfx"; import handleEndSceneInput from "../core/input-handlers/handleEndSceneInput"; import handleEvent from "../core/handleEvent"; import { GameEvent } from "../types/types"; +import { useSwipeable } from "react-swipeable"; +import IdleManager from "./IdleManager"; +import { Canvas } from "react-three-fiber"; const InputHandler = () => { const scene = useStore((state) => state.currentScene); - const mainSubscene = useStore((state) => state.mainSubscene); const inputCooldown = useStore((state) => state.inputCooldown); - const setLainMoveState = useStore((state) => state.setLainMoveState); - const timeSinceLastKeyPress = useRef(-1); - const lainIdleCounter = useRef(-1); - const idleSceneCounter = useRef(-1); - useFrame(() => { - const now = Date.now(); - if ( - lainIdleCounter.current > -1 && - idleSceneCounter.current > -1 && - mainSubscene !== "pause" && - mainSubscene !== "level_selection" && - scene === "main" - ) { - if (now > lainIdleCounter.current + 10000) { - setLainMoveState(getRandomIdleLainAnim()); - // after one idle animation plays, the second comes sooner than it would after a regular keypress - lainIdleCounter.current = now - 2500; - } - if (now > idleSceneCounter.current + 30000) { - // put it on lock until the next action, since while the idle media plays, the - // Date.now() value keeps increasing, which can result in another idle media playing right after one finishes - // one way to work around this would be to modify the value depending on the last played idle media's duration - // but i'm way too lazy for that - idleSceneCounter.current = -1; - - // idleManager(getRandomIdleMedia()); - playAudio(audio.sound32); - - setTimeout(() => { - // useStore.setState({ event: "play_idle_media" }); - }, 1200); - } - } - }); - - useEffect(() => { - if (scene !== "main") idleSceneCounter.current = -1; - }, [scene]); + const lainIdleTimerRef = useRef(-1); + const idleSceneTimerRef = useRef(-1); const handleKeyPress = useCallback( - (event) => { - const { keyCode } = event; + (keyPress: string) => { + const now = Date.now(); - const keyPress = getKeyCodeAssociation(keyCode); + if (scene === "main") { + timeSinceLastKeyPress.current = now; + lainIdleTimerRef.current = now; + idleSceneTimerRef.current = now; + } + + const sceneFns = (() => { + switch (scene) { + case "main": + return { + contextProvider: getMainSceneContext, + keyPressHandler: handleMainSceneInput, + }; + case "media": + return { + contextProvider: getMediaSceneContext, + keyPressHandler: handleMediaSceneInput, + }; + case "sskn": + return { + contextProvider: getSsknSceneContext, + keyPressHandler: handleSsknSceneInput, + }; + case "boot": + return { + contextProvider: getBootSceneContext, + keyPressHandler: handleBootSceneInput, + }; + case "end": + return { + contextProvider: getEndSceneContext, + keyPressHandler: handleEndSceneInput, + }; + case "gate": + case "polytan": + useStore.setState({ currentScene: "main" }); + break; + case "idle_media": + useStore.setState({ + currentScene: "main", + idleStarting: false, + }); + break; + } + })(); + + if (sceneFns) { + const { contextProvider, keyPressHandler } = sceneFns; + + const ctx = contextProvider(); + const event: GameEvent | undefined = keyPressHandler( + ctx as any, + keyPress + ); + if (event) handleEvent(event); + } + }, + [scene] + ); + + const handlers = useSwipeable({ + onSwiped: (eventData) => handleKeyPress(eventData.dir.toUpperCase()), + onTap: () => handleKeyPress("CIRCLE"), + }); + + const handleKeyBoardEvent = useCallback( + (event) => { + const key = getKeyPress(event.key); const now = Date.now(); if ( - keyPress && + key && now > timeSinceLastKeyPress.current + inputCooldown && inputCooldown !== -1 ) { - if (scene === "main") { - lainIdleCounter.current = now; - idleSceneCounter.current = now; - timeSinceLastKeyPress.current = now; - } - - const sceneFns = (() => { - switch (scene) { - case "main": - return { - contextProvider: getMainSceneContext, - keyPressHandler: handleMainSceneInput, - }; - case "media": - return { - contextProvider: getMediaSceneContext, - keyPressHandler: handleMediaSceneInput, - }; - case "sskn": - return { - contextProvider: getSsknSceneContext, - keyPressHandler: handleSsknSceneInput, - }; - case "boot": - return { - contextProvider: getBootSceneContext, - keyPressHandler: handleBootSceneInput, - }; - case "end": - return { - contextProvider: getEndSceneContext, - keyPressHandler: handleEndSceneInput, - }; - case "gate": - case "polytan": - useStore.setState({ currentScene: "main" }); - break; - case "idle_media": - useStore.setState({ - currentScene: "main", - idleStarting: false, - }); - break; - } - })(); - - if (sceneFns) { - const { contextProvider, keyPressHandler } = sceneFns; - - const ctx = contextProvider(); - const event: GameEvent | undefined = keyPressHandler( - ctx as any, - keyPress - ); - if (event) handleEvent(event); - } + handleKeyPress(key); } }, - [inputCooldown, scene] + [handleKeyPress, inputCooldown] ); useEffect(() => { - window.addEventListener("keydown", handleKeyPress); + window.addEventListener("keydown", handleKeyBoardEvent); return () => { - window.removeEventListener("keydown", handleKeyPress); + window.removeEventListener("keydown", handleKeyBoardEvent); }; - }, [handleKeyPress]); + }, [handleKeyBoardEvent]); - return null; + return ( + <> +
+ + + + + ); }; export default InputHandler; diff --git a/src/components/MainScene/HUD.tsx b/src/components/MainScene/HUD.tsx index fb6f594..9486c7d 100644 --- a/src/components/MainScene/HUD.tsx +++ b/src/components/MainScene/HUD.tsx @@ -25,18 +25,6 @@ const HUD = memo(() => { const scene = useStore((state) => state.currentScene); const prevData = usePrevious({ siteRotY, activeLevel, subscene, scene }); - const lerpObject = ( - obj: THREE.Object3D, - posX: number, - initialPosX: number - ) => { - obj.position.x = lerp( - obj.position.x, - activeRef.current ? posX : initialPosX, - 0.12 - ); - }; - // this part is imperative because it performs a lot better than having a toggleable spring. useFrame(() => { if ( @@ -46,25 +34,30 @@ const HUD = memo(() => { greenTextRef.current ) { const hud = currentHudRef.current; - lerpObject( - longHudRef.current, - hud.long.position[0], - hud.long.initial_position[0] + + longHudRef.current.position.x = lerp( + longHudRef.current.position.x, + activeRef.current ? hud.long.position[0] : hud.long.initial_position[0], + 0.12 ); - lerpObject( - boringHudRef.current, - hud.boring.position[0], - hud.boring.initial_position[0] + boringHudRef.current.position.x = lerp( + boringHudRef.current.position.x, + activeRef.current + ? hud.boring.position[0] + : hud.boring.initial_position[0], + 0.12 ); - lerpObject( - bigHudRef.current, - hud.big.position[0], - hud.big.initial_position[0] + bigHudRef.current.position.x = lerp( + bigHudRef.current.position.x, + activeRef.current ? hud.big.position[0] : hud.big.initial_position[0], + 0.12 ); - lerpObject( - greenTextRef.current, - hud.medium_text.position[0], - hud.medium_text.initial_position[0] + greenTextRef.current.position.x = lerp( + greenTextRef.current.position.x, + activeRef.current + ? hud.medium_text.position[0] + : hud.medium_text.initial_position[0], + 0.12 ); } }); diff --git a/src/core/eventTemplates.ts b/src/core/eventTemplates.ts index c9bacc0..472eae6 100644 --- a/src/core/eventTemplates.ts +++ b/src/core/eventTemplates.ts @@ -204,7 +204,7 @@ export const changeSelectedLevel = (calculatedState: { { mutation: { selectedLevel: calculatedState.selectedLevel, - inputCooldown: 300, + inputCooldown: 100, }, }, ], @@ -263,7 +263,7 @@ export const changePauseComponent = (calculatedState: { { mutation: { activePauseComponent: calculatedState.activePauseComponent, - inputCooldown: 500, + inputCooldown: 700, }, }, ], @@ -279,7 +279,7 @@ export const showPermissionDenied = { }; export const displayPrompt = { - state: [{ mutation: { promptVisible: true, inputCooldown: 500 } }], + state: [{ mutation: { promptVisible: true, inputCooldown: 0 } }], audio: [{ sfx: [audio.sound0] }], }; @@ -311,7 +311,7 @@ export const exitPause = (calculatedState: { siteRot: number[] }) => ({ }); export const exitAbout = { - state: [{ mutation: { showingAbout: false, inputCooldown: 500 } }], + state: [{ mutation: { showingAbout: false, inputCooldown: 0 } }], }; export const changePromptComponent = (calculatedState: { @@ -321,7 +321,7 @@ export const changePromptComponent = (calculatedState: { { mutation: { activePromptComponent: calculatedState.activePromptComponent, - inputCooldown: 500, + inputCooldown: 100, }, }, ], @@ -334,7 +334,7 @@ export const exitPrompt = { mutation: { activePromptComponent: "no", promptVisible: false, - inputCooldown: 500, + inputCooldown: 0, }, }, ], @@ -458,7 +458,7 @@ export const changeMediaSide = (calculatedState: { activeMediaComponent: calculatedState.activeMediaComponent, lastActiveMediaComponents: calculatedState.lastActiveMediaComponents, currentMediaSide: calculatedState.currentMediaSide, - inputCooldown: 500, + inputCooldown: 0, }, }, ], @@ -497,7 +497,7 @@ export const changeRightMediaComponent = (calculatedState: { mutation: { activeMediaComponent: calculatedState.activeComponent, mediaWordPosStateIdx: calculatedState.wordPosStateIdx, - inputCooldown: 500, + inputCooldown: 300, }, }, ], @@ -519,7 +519,7 @@ export const wordNotFound = { }; export const hideWordNotFound = { - state: [{ mutation: { wordNotFound: false, inputCooldown: 300 } }], + state: [{ mutation: { wordNotFound: false, inputCooldown: 0 } }], }; export const selectWord = (calculatedState: { @@ -550,7 +550,7 @@ export const changeSsknComponent = (calculatedState: { { mutation: { activeSsknComponent: calculatedState.activeSsknComponent, - inputCooldown: 500, + inputCooldown: 100, }, }, ], @@ -595,7 +595,7 @@ export const changeEndComponent = (calculatedState: { { mutation: { activeEndComponent: calculatedState.activeEndComponent, - inputCooldown: 500, + inputCooldown: 100, }, }, ], @@ -615,7 +615,7 @@ export const changeMainMenuComponent = (calculatedState: { { mutation: { activeMainMenuComponent: calculatedState.activeMainMenuComponent, - inputCooldown: 500, + inputCooldown: 200, }, }, ], @@ -702,3 +702,58 @@ export const updateAuthorizeUserLetterIdx = (calculatedState: { }, ], }); + +export const playIdleVideo = (calculatedState: { idleMedia: string }) => ({ + state: [ + { + mutation: { + idleStarting: true, + idleMedia: calculatedState.idleMedia, + inputCooldown: -1, + }, + }, + { mutation: { currentScene: "idle_media" }, delay: 1200 }, + ], +}); + +export const playIdleAudio = (calculatedState: { + idleMedia: string; + idleImages: { "1": string; "2": string; "3": string }; + idleNodeName: string; +}) => ({ + state: [ + { + mutation: { + idleStarting: true, + inputCooldown: -1, + idleMedia: calculatedState.idleMedia, + idleImages: calculatedState.idleImages, + idleNodeName: calculatedState.idleNodeName, + }, + }, + { mutation: { currentScene: "idle_media" }, delay: 1200 }, + ], +}); + +export const playLainIdleAnim = (calculatedState: { + lainMoveState: string; + duration: number; +}) => ({ + // todo appropriate disable-move here also + state: [ + { + mutation: { + lainMoveState: calculatedState.lainMoveState, + canLainMove: false, + }, + }, + { + mutation: { lainMoveState: "standing", canLainMove: true }, + delay: calculatedState.duration, + }, + ], +}); + +export const resetInputCooldown = { + state: [{ mutation: { inputCooldown: 0 } }], +}; diff --git a/src/core/input-handlers/handleMainSceneInput.ts b/src/core/input-handlers/handleMainSceneInput.ts index 2c33ae8..98d35a2 100644 --- a/src/core/input-handlers/handleMainSceneInput.ts +++ b/src/core/input-handlers/handleMainSceneInput.ts @@ -24,6 +24,7 @@ import { loadGame, loadGameFail, pauseGame, + resetInputCooldown, ripNode, saveGame, selectLevel, @@ -54,6 +55,7 @@ const handleMainSceneInput = ( activePromptComponent, siteSaveState, wordNotFound, + canLainMove, } = mainSceneContext; if (promptVisible) { @@ -137,6 +139,7 @@ const handleMainSceneInput = ( }; if (nodeData.didMove) { + if (!canLainMove) return resetInputCooldown; return siteMoveHorizontal({ lainMoveAnimation: lainMoveAnimation, siteRot: newSiteRot, @@ -179,15 +182,18 @@ const handleMainSceneInput = ( matrixIndices: nodeData.matrixIndices, }; - if (nodeData.didMove) + if (nodeData.didMove) { + if (!canLainMove) return resetInputCooldown; return siteMoveVertical({ lainMoveAnimation: lainMoveAnimation, activeLevel: newLevel, activeNode: newNode, }); - else return changeNode({ activeNode: newNode }); + } else return changeNode({ activeNode: newNode }); } case "CIRCLE": + if (!canLainMove) return resetInputCooldown; + const eventAnimation = Math.random() < 0.4 ? throwNode : ripNode; if ( @@ -207,6 +213,7 @@ const handleMainSceneInput = ( case "L2": return enterLevelSelection({ selectedLevel: level }); case "TRIANGLE": + if (!canLainMove) return resetInputCooldown; return pauseGame({ siteRot: [Math.PI / 2, siteRotY, 0] }); } break; @@ -225,6 +232,8 @@ const handleMainSceneInput = ( return exitLevelSelection; case "CIRCLE": + if (!canLainMove) return resetInputCooldown; + if (level === selectedLevel) return; const direction = selectedLevel > level ? "up" : "down"; diff --git a/src/helpers/idle-helpers.ts b/src/helpers/idle-helpers.ts index 67f97cc..74aba37 100644 --- a/src/helpers/idle-helpers.ts +++ b/src/helpers/idle-helpers.ts @@ -72,39 +72,39 @@ export const getRandomIdleMedia = () => { const nodeName = siteData[level][nodeToPlay].node_name; return { + type: "audio", images: images, media: media, nodeName: nodeName, }; } else { return { + type: "video", media: idleNodes.video[Math.floor(Math.random() * idleNodes.video.length)], - nodeName: undefined, - images: undefined, }; } }; -export const getRandomIdleLainAnim = () => { - const moves = [ - "prayer", - "touch_sleeve", - "thinking", - "stretch_2", - "stretch", - "spin", - "scratch_head", - "blush", - "hands_behind_head", - "hands_on_hips", - "hands_on_hips_2", - "hands_together", - "lean_forward", - "lean_left", - "lean_right", - "look_around", - "play_with_hair", +export const getRandomIdleLainAnim = (): [string, number] => { + const moves: [string, number][] = [ + ["prayer", 3500], + ["touch_sleeve", 3000], + ["thinking", 3900], + ["stretch_2", 3900], + ["stretch", 3000], + ["spin", 3000], + ["scratch_head", 3900], + ["blush", 3000], + ["hands_behind_head", 2300], + ["hands_on_hips", 3000], + ["hands_on_hips_2", 3900], + ["hands_together", 2500], + ["lean_forward", 2700], + ["lean_left", 2700], + ["lean_right", 3500], + ["look_around", 3000], + ["play_with_hair", 2900], ]; return moves[Math.floor(Math.random() * moves.length)]; diff --git a/src/resources/initial_progress.json b/src/resources/initial_progress.json index af2d3c2..ee6defc 100644 --- a/src/resources/initial_progress.json +++ b/src/resources/initial_progress.json @@ -1747,31 +1747,31 @@ "is_viewed": 0, "is_visible": 1 }, - "Sskn01": { + "SSkn01": { "is_viewed": 0, "is_visible": 1 }, - "Sskn02": { + "SSkn02": { "is_viewed": 0, "is_visible": 1 }, - "Sskn03": { + "SSkn03": { "is_viewed": 0, "is_visible": 1 }, - "Sskn04": { + "SSkn04": { "is_viewed": 0, "is_visible": 1 }, - "Sskn04#": { + "SSkn04#": { "is_viewed": 0, "is_visible": 1 }, - "Sskn05": { + "SSkn05": { "is_viewed": 0, "is_visible": 1 }, - "Sskn06": { + "SSkn06": { "is_viewed": 0, "is_visible": 1 }, diff --git a/src/resources/site_a.json b/src/resources/site_a.json index 2ea5c1d..1d3d969 100644 --- a/src/resources/site_a.json +++ b/src/resources/site_a.json @@ -1644,7 +1644,7 @@ "3": "-1" }, "media_file": "INS01.STR", - "node_name": "Sskn01", + "node_name": "SSkn01", "required_final_video_viewcount": 0, "site": "A", "title": "mT up-date App.", @@ -2748,7 +2748,7 @@ "3": "-1" }, "media_file": "INS02.STR", - "node_name": "Sskn02", + "node_name": "SSkn02", "required_final_video_viewcount": 0, "site": "A", "title": "mT up-date App.", @@ -4892,7 +4892,7 @@ "3": "-1" }, "media_file": "INS03.STR", - "node_name": "Sskn03", + "node_name": "SSkn03", "required_final_video_viewcount": 0, "site": "A", "title": "mT up-date App.", @@ -7302,7 +7302,7 @@ "3": "-1" }, "media_file": "INS04.STR", - "node_name": "Sskn04", + "node_name": "SSkn04", "required_final_video_viewcount": 0, "site": "A", "title": "mT up-date App.", diff --git a/src/resources/site_b.json b/src/resources/site_b.json index e30ecfb..1758350 100644 --- a/src/resources/site_b.json +++ b/src/resources/site_b.json @@ -8,7 +8,7 @@ "3": "-1" }, "media_file": "INS05.STR", - "node_name": "Sskn04#", + "node_name": "SSkn04#", "required_final_video_viewcount": 0, "site": "B", "title": "mT up-date App.", @@ -1510,7 +1510,7 @@ "3": "-1" }, "media_file": "INS06.STR", - "node_name": "Sskn05", + "node_name": "SSkn05", "required_final_video_viewcount": 0, "site": "B", "title": "mT up-date App.", @@ -2968,7 +2968,7 @@ "3": "-1" }, "media_file": "INS07.STR", - "node_name": "Sskn06", + "node_name": "SSkn06", "required_final_video_viewcount": 0, "site": "B", "title": "mT up-date App.", @@ -5811,4 +5811,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/scenes/ChangeDiscScene.tsx b/src/scenes/ChangeDiscScene.tsx index 9e61000..c18828a 100644 --- a/src/scenes/ChangeDiscScene.tsx +++ b/src/scenes/ChangeDiscScene.tsx @@ -28,7 +28,7 @@ const ChangeDiscScene = () => { const disc2Tex = useLoader(THREE.TextureLoader, disc2); useEffect(() => { - // setTimeout(() => setScene("main"), 3500); + setTimeout(() => setScene("main"), 3500); }, [activeSite, setScene]); return ( @@ -70,7 +70,10 @@ const ChangeDiscScene = () => { - + ); diff --git a/src/scenes/IdleMediaScene.tsx b/src/scenes/IdleMediaScene.tsx index 6761657..2b2c805 100644 --- a/src/scenes/IdleMediaScene.tsx +++ b/src/scenes/IdleMediaScene.tsx @@ -15,6 +15,7 @@ const IdleMediaScene = () => { useStore.setState({ currentScene: "main", idleStarting: false, + intro: false, }); }, [mediaPercentageElapsed]); diff --git a/src/store.ts b/src/store.ts index 54588ee..4707318 100644 --- a/src/store.ts +++ b/src/store.ts @@ -46,32 +46,26 @@ type State = { activeNodeRot: number[]; activeNodeAttributes: NodeAttributes; - // lain lainMoveState: string; + canLainMove: boolean; - // site activeSite: ActiveSite; siteRot: number[]; oldSiteRot: number[]; - // level activeLevel: string; oldLevel: string; - // level selection selectedLevel: number; - // end scene activeEndComponent: EndComponent; endSceneSelectionVisible: boolean; - // pause activePauseComponent: PauseComponent; pauseExitAnimation: boolean; showingAbout: boolean; permissionDenied: boolean; - // media/media scene audioAnalyser: AudioAnalyser | undefined; mediaPercentageElapsed: number; currentMediaSide: MediaSide; @@ -84,39 +78,30 @@ type State = { mediaWordPosStateIdx: number; wordSelected: boolean; - // idle scene idleStarting: boolean; idleMedia: string; idleImages: { "1": string; "2": string; "3": string } | undefined; idleNodeName: string | undefined; - // sskn scene activeSsknComponent: SsknComponent; ssknLoading: boolean; - // polytan scene polytanUnlockedParts: PolytanBodyParts; - // player name playerName: string; - // boot scene activeMainMenuComponent: MainMenuComponent; authorizeUserLetterIdx: number; bootSubscene: BootSubscene; - // prompt promptVisible: boolean; activePromptComponent: PromptComponent; - // status notifiers loadSuccessful: boolean | undefined; saveSuccessful: boolean | undefined; - // word not found notification thing wordNotFound: boolean; - // save state siteSaveState: SiteSaveState; inputCooldown: number; @@ -126,7 +111,7 @@ export const useStore = create( combine( { // scene data - currentScene: "change_disc", + currentScene: "main", // game progress gameProgress: game_progress, @@ -153,6 +138,7 @@ export const useStore = create( // lain lainMoveState: "standing", + canLainMove: true, // site activeSite: "a", @@ -324,6 +310,7 @@ export const useStore = create( gate_level: state.gameProgress.gate_level + 1, }, })), + loadUserSaveState: (userState: UserSaveState) => set(() => ({ siteSaveState: userState.siteSaveState, @@ -362,6 +349,7 @@ export const getMainSceneContext = (): MainSceneContext => { showingAbout: state.showingAbout, siteSaveState: state.siteSaveState, wordNotFound: state.wordNotFound, + canLainMove: state.canLainMove, }; }; diff --git a/src/types/types.ts b/src/types/types.ts index a43ef27..ad330de 100644 --- a/src/types/types.ts +++ b/src/types/types.ts @@ -108,6 +108,7 @@ export interface MainSceneContext extends PromptContext { selectedLevel: number; wordNotFound: boolean; siteSaveState: SiteSaveState; + canLainMove: boolean; } export type SsknSceneContext = { diff --git a/src/utils/getKeyPress.ts b/src/utils/getKeyPress.ts new file mode 100644 index 0000000..a04e5c6 --- /dev/null +++ b/src/utils/getKeyPress.ts @@ -0,0 +1,16 @@ +const getKeyPress = (keyCode: string) => { + const keyCodeAssocs = { + ArrowDown: "DOWN", // down arrow + ArrowLeft: "LEFT", // left arrow + ArrowUp: "UP", // up arrow + ArrowRight: "RIGHT", // right arrow + x: "CIRCLE", // x key + z: "X", // z key + d: "TRIANGLE", // d key + e: "L2", // e key + v: "START", // v key + }; + return keyCodeAssocs[keyCode as keyof typeof keyCodeAssocs]; +}; + +export default getKeyPress; diff --git a/src/utils/parseUserInput.ts b/src/utils/parseUserInput.ts deleted file mode 100644 index a4bbdc7..0000000 --- a/src/utils/parseUserInput.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const getKeyCodeAssociation = (keyCode: number) => { - const keyCodeAssocs = { - 40: "DOWN", // down arrow - 37: "LEFT", // left arrow - 38: "UP", // up arrow - 39: "RIGHT", // right arrow - 88: "CIRCLE", // x key - 90: "X", // z key - 68: "TRIANGLE", // d key - 69: "L2", // e key - 86: "START", // v key - 32: "SPACE", - }; - return keyCodeAssocs[keyCode as keyof typeof keyCodeAssocs]; -};