From 862f2ed0e85309cb5fb17784f1a8772b4d96ac58 Mon Sep 17 00:00:00 2001 From: ad044 Date: Tue, 23 Mar 2021 18:23:25 +0400 Subject: [PATCH] added main page and guide, refactored some functions --- package-lock.json | 83 +++++++++++++ package.json | 1 + scripts/extract/convert_sfx.mjs | 33 +++++ src/App.tsx | 6 +- src/components/IdleManager.tsx | 3 +- src/core/handleEvent.ts | 3 +- .../input-handlers/handleMainSceneInput.ts | 29 +++-- src/dom-components/Credit.tsx | 16 +++ src/dom-components/Game.tsx | 54 ++++---- src/dom-components/Guide.tsx | 100 +++++++++++++++ src/dom-components/Header.tsx | 17 +++ src/dom-components/MainPage.tsx | 116 ++++++++++++++++++ src/dom-components/Notes.tsx | 45 +++++-- src/dom-components/QA.tsx | 21 ++++ src/scenes/EndScene.tsx | 3 +- src/scenes/MainScene.tsx | 3 +- src/scenes/MediaScene.tsx | 3 +- src/scenes/TaKScene.tsx | 3 +- src/store.ts | 18 --- src/utils/createAudioAnalyser.ts | 13 ++ src/utils/playAudio.ts | 8 ++ 21 files changed, 506 insertions(+), 72 deletions(-) create mode 100644 scripts/extract/convert_sfx.mjs create mode 100644 src/dom-components/Credit.tsx create mode 100644 src/dom-components/Guide.tsx create mode 100644 src/dom-components/Header.tsx create mode 100644 src/dom-components/MainPage.tsx create mode 100644 src/dom-components/QA.tsx create mode 100644 src/utils/createAudioAnalyser.ts create mode 100644 src/utils/playAudio.ts diff --git a/package-lock.json b/package-lock.json index f57452b..dafb81a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2109,6 +2109,11 @@ "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-7.2.1.tgz", "integrity": "sha512-oZ0Ib5I4Z2pUEcoo95cT1cr6slco9WY7yiPpG+RGNkj8YcYgJnM7pXmYmorNOReh8MIGcKSqXyeGjxnr8YiZbA==" }, + "@tokenizer/token": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.1.1.tgz", + "integrity": "sha512-XO6INPbZCxdprl+9qa/AAbFFOMzzwqYxpjPgLICrMD6C2FCw6qfJOPcBk6JqqPLSaZ/Qx87qn4rpPmPMwaAK6w==" + }, "@types/anymatch": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", @@ -2156,6 +2161,11 @@ "@babel/types": "^7.3.0" } }, + "@types/debug": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.5.tgz", + "integrity": "sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ==" + }, "@types/eslint": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.2.6.tgz", @@ -2307,6 +2317,15 @@ "@types/react-router": "*" } }, + "@types/readable-stream": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", + "integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==", + "requires": { + "@types/node": "*", + "safe-buffer": "*" + } + }, "@types/resolve": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", @@ -6595,6 +6614,17 @@ } } }, + "file-type": { + "version": "16.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.3.0.tgz", + "integrity": "sha512-ZA0hV64611vJT42ltw0T9IDwHApQuxRdrmQZWTeDmeAUtZBBVSQW3nSQqhhW1cAgpXgqcJvm410BYHXJQ9AymA==", + "requires": { + "readable-web-to-node-stream": "^3.0.0", + "strtok3": "^6.0.3", + "token-types": "^2.0.0", + "typedarray-to-buffer": "^3.1.5" + } + }, "file-uri-to-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", @@ -9963,6 +9993,26 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, + "music-metadata": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-7.8.1.tgz", + "integrity": "sha512-Q4PhR788jp2VDh/JgPvEUZ3NCqxgW4+hV0H2XrYKfGwq5aGagm1ek9kbCJvUCvejVnmyn6Rb+ggIPxrIVPfzww==", + "requires": { + "content-type": "^1.0.4", + "debug": "^4.3.1", + "file-type": "^16.2.0", + "media-typer": "^1.1.0", + "strtok3": "^6.0.8", + "token-types": "^2.1.1" + }, + "dependencies": { + "media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==" + } + } + }, "nan": { "version": "2.14.2", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", @@ -10679,6 +10729,11 @@ "sha.js": "^2.4.8" } }, + "peek-readable": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-3.1.3.tgz", + "integrity": "sha512-mpAcysyRJxmICBcBa5IXH7SZPvWkcghm6Fk8RekoS3v+BpbSzlZzuWbMx+GXrlUwESi9qHar4nVEZNMKylIHvg==" + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -12571,6 +12626,15 @@ "util-deprecate": "^1.0.1" } }, + "readable-web-to-node-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.1.tgz", + "integrity": "sha512-4zDC6CvjUyusN7V0QLsXVB7pJCD9+vtrM9bYDRv6uBQ+SKfx36rp5AFNPRgh9auKRul/a1iFZJYXcCbwRL+SaA==", + "requires": { + "@types/readable-stream": "^2.3.9", + "readable-stream": "^3.6.0" + } + }, "readdirp": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", @@ -14406,6 +14470,16 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strtok3": { + "version": "6.0.8", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.0.8.tgz", + "integrity": "sha512-QLgv+oiXwXgCgp2PdPPa+Jpp4D9imK9e/0BsyfeFMr6QL6wMVqoVn9+OXQ9I7MZbmUzN6lmitTJ09uwS2OmGcw==", + "requires": { + "@tokenizer/token": "^0.1.1", + "@types/debug": "^4.1.5", + "peek-readable": "^3.1.3" + } + }, "style-loader": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", @@ -14859,6 +14933,15 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "token-types": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-2.1.1.tgz", + "integrity": "sha512-wnQcqlreS6VjthyHO3Y/kpK/emflxDBNhlNUPfh7wE39KnuDdOituXomIbyI79vBtF0Ninpkh72mcuRHo+RG3Q==", + "requires": { + "@tokenizer/token": "^0.1.1", + "ieee754": "^1.2.1" + } + }, "tough-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", diff --git a/package.json b/package.json index c1dbd64..85919e5 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/react-dom": "^16.9.8", "@types/react-router-dom": "^5.1.7", "@types/three": "^0.126.0", + "music-metadata": "^7.8.1", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router-dom": "^5.2.0", diff --git a/scripts/extract/convert_sfx.mjs b/scripts/extract/convert_sfx.mjs new file mode 100644 index 0000000..7a2709d --- /dev/null +++ b/scripts/extract/convert_sfx.mjs @@ -0,0 +1,33 @@ +import { readdirSync } from "fs"; +import { spawnSync } from "child_process"; +import { join } from "path"; +import * as mm from "music-metadata"; + +// stub implementation of upping the pitch of the sfx, still wip +export async function convert_sfx() { + let i = 0; + const dir = join("..", "..", "src", "static", "sfx"); + for (let file of readdirSync(dir)) { + if (file.includes("snd")) { + console.log(file); + const metaData = await mm.parseFile(`${join(dir, file)}`); + + const sampleRate = metaData.format.sampleRate; + + spawnSync( + "ffmpeg", + [ + "-i", + join(dir, file), + "-filter_complex", + `asetrate=${sampleRate}*2^(6/12),atempo=1/2^(6/12)`, + "-n", + join(dir, "..", "t", file), + ], + { stdio: "inherit" } + ); + } + } +} + +convert_sfx(); diff --git a/src/App.tsx b/src/App.tsx index c84d80c..b9aed70 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,13 +3,17 @@ import "./static/css/page.css"; import Game from "./dom-components/Game"; import { HashRouter, Route, Switch } from "react-router-dom"; import Notes from "./dom-components/Notes"; +import MainPage from "./dom-components/MainPage"; +import Guide from "./dom-components/Guide"; const App = () => { return ( - + + + ); diff --git a/src/components/IdleManager.tsx b/src/components/IdleManager.tsx index 5a36edb..41e7ac8 100644 --- a/src/components/IdleManager.tsx +++ b/src/components/IdleManager.tsx @@ -1,5 +1,6 @@ import { useFrame } from "react-three-fiber"; -import { playAudio, useStore } from "../store"; +import { useStore } from "../store"; +import playAudio from "../utils/playAudio"; import * as audio from "../static/sfx"; import { playIdleAudio, diff --git a/src/core/handleEvent.ts b/src/core/handleEvent.ts index c66a6c2..cecc6aa 100644 --- a/src/core/handleEvent.ts +++ b/src/core/handleEvent.ts @@ -1,4 +1,5 @@ -import { playAudio, useStore } from "../store"; +import { useStore } from "../store"; +import playAudio from "../utils/playAudio"; import sleep from "../utils/sleep"; import { GameEvent } from "../types/types"; diff --git a/src/core/input-handlers/handleMainSceneInput.ts b/src/core/input-handlers/handleMainSceneInput.ts index a13aa7a..79215f5 100644 --- a/src/core/input-handlers/handleMainSceneInput.ts +++ b/src/core/input-handlers/handleMainSceneInput.ts @@ -130,14 +130,6 @@ const handleMainSceneInput = ( if (!nodeData) return resetInputCooldown; - const lainMoveAnimation = `move_${direction}`; - const newSiteRot = [ - 0, - direction === "left" - ? siteRotY + Math.PI / 4 - : siteRotY - Math.PI / 4, - 0, - ]; const newNode = { ...(nodeData.node !== "unknown" ? getNodeById(nodeData.node, activeSite) @@ -147,6 +139,16 @@ const handleMainSceneInput = ( if (nodeData.didMove) { if (!canLainMove) return resetInputCooldown; + + const lainMoveAnimation = `move_${direction}`; + const newSiteRot = [ + 0, + direction === "left" + ? siteRotY + Math.PI / 4 + : siteRotY - Math.PI / 4, + 0, + ]; + return siteMoveHorizontal({ lainMoveAnimation: lainMoveAnimation, siteRot: newSiteRot, @@ -169,11 +171,6 @@ const handleMainSceneInput = ( if (!nodeData) return resetInputCooldown; - const lainMoveAnimation = `jump_${direction}`; - const newLevel = (direction === "up" ? level + 1 : level - 1) - .toString() - .padStart(2, "0"); - const newNode = { ...(nodeData.node !== "unknown" ? getNodeById(nodeData.node, activeSite) @@ -183,6 +180,12 @@ const handleMainSceneInput = ( if (nodeData.didMove) { if (!canLainMove) return resetInputCooldown; + + const lainMoveAnimation = `jump_${direction}`; + const newLevel = (direction === "up" ? level + 1 : level - 1) + .toString() + .padStart(2, "0"); + return siteMoveVertical({ lainMoveAnimation: lainMoveAnimation, activeLevel: newLevel, diff --git a/src/dom-components/Credit.tsx b/src/dom-components/Credit.tsx new file mode 100644 index 0000000..d2b2065 --- /dev/null +++ b/src/dom-components/Credit.tsx @@ -0,0 +1,16 @@ +import React from "react"; + +type CreditProps = { + name: string; + credit: string; +}; + +const Credit = (props: CreditProps) => ( + <> + {props.name} - {props.credit} +
+
+ +); + +export default Credit; diff --git a/src/dom-components/Game.tsx b/src/dom-components/Game.tsx index 1e677b2..27dd7d9 100644 --- a/src/dom-components/Game.tsx +++ b/src/dom-components/Game.tsx @@ -20,6 +20,7 @@ import { Canvas } from "react-three-fiber"; import Preloader from "../components/Preloader"; import InputHandler from "../components/InputHandler"; import MediaPlayer from "../components/MediaPlayer"; +import Header from "./Header"; const Game = () => { const currentScene = useStore((state) => state.currentScene); @@ -68,29 +69,40 @@ const Game = () => { }; }, [handleGameResize]); + useEffect(() => { + document.body.style.overflowY = "hidden"; + + return () => { + document.body.style.overflowY = "visible"; + }; + }, []); + return ( -
- +
+
- - - {dispatchScene[currentScene as keyof typeof dispatchScene]} - - - - {["media", "idle_media", "tak", "end"].includes(currentScene) && ( -
- -
- )} -
+ + + + {dispatchScene[currentScene as keyof typeof dispatchScene]} + + + + {["media", "idle_media", "tak", "end"].includes(currentScene) && ( +
+ +
+ )} +
+ ); }; diff --git a/src/dom-components/Guide.tsx b/src/dom-components/Guide.tsx new file mode 100644 index 0000000..53c261e --- /dev/null +++ b/src/dom-components/Guide.tsx @@ -0,0 +1,100 @@ +import React from "react"; +import Header from "./Header"; +import "../static/css/guide.css"; + +const Guide = () => { + return ( + <> +
+

+
A bit of a disclaimer - as stated on the main page, these are + entirely my thoughts from observations while developing the game, some + of the information may be innacurate.

+ First, let's get this out of the way - Serial Experiments Lain PSX isn't + a "game" in a traditional sense, it's more like a visual novel which you + piece together yourself. The story revolves around Lain and her + interactions with her psychiatrist - Touko. +

A common misconception about the game is that there's no + specific order, and that you just randomly watch stuff and come up with + an explanation yourself. From what I've noticed, this is not entirely + true. Let me explain: +
+
+ The blue orbs that you navigate through (we'll call those blue orbs + "nodes" from now on) contain either: +
+
+ A. Media (audio/video) +
+ B. Collectibles +
+ C. Upgrades +
+
+ There are also multiple "types" of nodes. You can tell them apart by + their names and icons. +
+ Here's a basic list of these nodes according to their names in their + respective categories: +
+
A category (Media) - Tda, Lda, Dia, Cou, TaK, Dc +
B category (Collectibles) - P2 +
C category (Upgrades) - SSkn, GaTE +

+ Let's step through each of these one by one: +
+
+ Category A - Media: +
Tda - Touko's diary - Touko's personal thoughts. +
+ Lda - Lain's diary - Lain's personal thoughts. +
+ Dia - Diagnosis - Lain's diagnosis after their interactions, provided by + Touko. +
+ Cou - Counseling - Lain and Touko's interactions. +
+ TaK - Not sure what TaK stands for - Random quotes by Lain. +
+ Dc - Videos. +
+
+ This is where the no-real-order issue comes into play - each of these + separate types are put in chronological order, meaning After Lda001 + comes Lda002, then Lda003, etc. This is identical for every other node + type I mentioned.

+ What might lead some people to believe that there is no real order is + that the way you UNLOCK them may not be entirely chronological, for + example, the first node you'll most likely interact with from Tda is + Tda028, since that's the closest to where you start from at the + beginning of the game. +

+ There's also another issue - despite these separate types being put in a + specific order, it is unclear how they interact with each other. For + example, there is no way (to my knowledge) to tell which Cou comes after + which Lda judging by their names alone. What might help here are the + "words" you select while you play them (on the right hand side there are + 3 floating things on each audio node which you can select). +

+ Category B - Collectibles:
+ P2 - Polytan - You collect parts of Lain's bear, after you collect all + the pieces, 2 extra idle media files become available. +

+ Category C - Upgrades: +
+ SSkn - Not sure what SSkn stands for - The main upgrade inside the game. + Some nodes have an "upgrade requirement" that you need to meet to be + able to view them, the way you do that is by collecting these. So, the + next time you see Lain try to knock on a node and fall over, know that + you need to collect more SSkn nodes. +
+ GaTE - Gate (I guess) - A "gate pass" as the game calls it - after + collecting all of them, you unlock Site B, which contains + more nodes and is the place where you continue the story. +

+

+ + ); +}; + +export default Guide; diff --git a/src/dom-components/Header.tsx b/src/dom-components/Header.tsx new file mode 100644 index 0000000..f14ea03 --- /dev/null +++ b/src/dom-components/Header.tsx @@ -0,0 +1,17 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import "../static/css/header.css"; + +const Header = () => { + return ( +
+ main + notes + start + guide + discord +
+ ); +}; + +export default Header; diff --git a/src/dom-components/MainPage.tsx b/src/dom-components/MainPage.tsx new file mode 100644 index 0000000..13dd84d --- /dev/null +++ b/src/dom-components/MainPage.tsx @@ -0,0 +1,116 @@ +import React from "react"; +import { Link } from "react-router-dom"; +import "../static/css/mainpage.css"; +import Credit from "./Credit"; +import Header from "./Header"; +import QA from "./QA"; + +const MainPage = () => { + return ( + <> +
+

+ This is a web reimplementation of the Serial Experiments Lain PSX game + with the aim to provide multi-language support. +
+ Please make sure to read the notes before you + start playing. +
+
+

+

+ FAQ: +

+ + guide. Keep in mind though that this is only my interpretation of the game and what I pieced together while developing it, it could be wrong.`} + /> + On my github.`} + /> + discord server and tell us about it!`} + /> +

+

credits

+

+ + + + + + + + + + + + + + + + + + + + {" "} + {" "} + {" "} + Special thanks to + Poimandres for answering all + the dumb questions I had while programming and creating the amazing + libraries used in this project. +

+
+ + ); +}; + +export default MainPage; diff --git a/src/dom-components/Notes.tsx b/src/dom-components/Notes.tsx index 75dcb29..e41664c 100644 --- a/src/dom-components/Notes.tsx +++ b/src/dom-components/Notes.tsx @@ -1,6 +1,7 @@ import React, { useEffect } from "react"; import "../static/css/notes.css"; import { Link } from "react-router-dom"; +import Header from "./Header"; const Notes = () => { useEffect(() => { @@ -9,6 +10,7 @@ const Notes = () => { return ( <> +
@@ -23,6 +25,25 @@ const Notes = () => { This is especially true if you're using a bad setup, and even more true if you're using Linux on a bad setup, since Firefox's WebGL implementation on it has had issues for a while now. +
+
+ If it's your first time playing the game, the first time loading + it might take a while depending on the factors mentioned above. + If you're seeing a black screen for a bit, just wait it out. + Subsequent website visits will be much faster once the browser + caches all the assets. +

+ + + + + @@ -57,19 +78,17 @@ const Notes = () => {

Browser Settings

diff --git a/src/dom-components/QA.tsx b/src/dom-components/QA.tsx new file mode 100644 index 0000000..ff177f2 --- /dev/null +++ b/src/dom-components/QA.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +type QAProps = { + question: string; + answer: string; +}; + +const QA = (props: QAProps) => ( + <> + Q:{" "} + +
+ A: +

+ +); + +export default QA; diff --git a/src/scenes/EndScene.tsx b/src/scenes/EndScene.tsx index 8753c7a..fbef839 100644 --- a/src/scenes/EndScene.tsx +++ b/src/scenes/EndScene.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import * as THREE from "three"; import { useFrame } from "react-three-fiber"; -import { createAudioAnalyser, useStore } from "../store"; +import { useStore } from "../store"; +import createAudioAnalyser from "../utils/createAudioAnalyser"; import EndSelectionScreen from "../components/EndScene/EndSelectionScreen"; import introSpeech from "../static/media/audio/LAIN21.XA[31].mp4"; import outroSpeech from "../static/media/audio/LAIN21.XA[16].mp4"; diff --git a/src/scenes/MainScene.tsx b/src/scenes/MainScene.tsx index 3118f33..5ad81a7 100644 --- a/src/scenes/MainScene.tsx +++ b/src/scenes/MainScene.tsx @@ -1,5 +1,6 @@ import React, { Suspense, useEffect, useRef, useState } from "react"; -import { playAudio, useStore } from "../store"; +import { useStore } from "../store"; +import playAudio from "../utils/playAudio"; import LevelSelection from "../components/MainScene/LevelSelection"; import HUD from "../components/MainScene/HUD"; import MainYellowTextAnimator from "../components/TextRenderer/MainYellowTextAnimator"; diff --git a/src/scenes/MediaScene.tsx b/src/scenes/MediaScene.tsx index 777a677..214f401 100644 --- a/src/scenes/MediaScene.tsx +++ b/src/scenes/MediaScene.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; -import { createAudioAnalyser, useStore } from "../store"; +import { useStore } from "../store"; +import createAudioAnalyser from "../utils/createAudioAnalyser"; import LeftSide from "../components/MediaScene/Selectables/LeftSide"; import RightSide from "../components/MediaScene/Selectables/RightSide"; import AudioVisualizer from "../components/MediaScene/AudioVisualizer/AudioVisualizer"; diff --git a/src/scenes/TaKScene.tsx b/src/scenes/TaKScene.tsx index aa26252..402437a 100644 --- a/src/scenes/TaKScene.tsx +++ b/src/scenes/TaKScene.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from "react"; import LainSpeak from "../components/LainSpeak"; -import { createAudioAnalyser, useStore } from "../store"; +import { useStore } from "../store"; +import createAudioAnalyser from "../utils/createAudioAnalyser"; const TaKScene = () => { const setScene = useStore((state) => state.setScene); diff --git a/src/store.ts b/src/store.ts index b57850e..8bd4e5f 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,5 @@ import create from "zustand"; import { combine } from "zustand/middleware"; -import * as THREE from "three"; import { AudioAnalyser } from "three"; import game_progress from "./resources/initial_progress.json"; import { getNodeById } from "./helpers/node-helpers"; @@ -445,23 +444,6 @@ export const getCurrentUserState = (): UserSaveState => { export const saveUserProgress = (state: UserSaveState) => localStorage.setItem("lainSaveState", JSON.stringify(state)); -export const playAudio = (audio: HTMLAudioElement) => { - audio.currentTime = 0; - audio.volume = 0.5; - audio.loop = false; - audio.play(); -}; - -export const createAudioAnalyser = () => { - const mediaElement = document.getElementById("media") as HTMLMediaElement; - const listener = new THREE.AudioListener(); - const audio = new THREE.Audio(listener); - - audio.setMediaElementSource(mediaElement); - - return new THREE.AudioAnalyser(audio, 2048); -}; - export const isPolytanFullyUnlocked = () => { const polytanProgress = useStore.getState().polytanUnlockedParts; diff --git a/src/utils/createAudioAnalyser.ts b/src/utils/createAudioAnalyser.ts new file mode 100644 index 0000000..4511d07 --- /dev/null +++ b/src/utils/createAudioAnalyser.ts @@ -0,0 +1,13 @@ +import * as THREE from "three"; + +const createAudioAnalyser = () => { + const mediaElement = document.getElementById("media") as HTMLMediaElement; + const listener = new THREE.AudioListener(); + const audio = new THREE.Audio(listener); + + audio.setMediaElementSource(mediaElement); + + return new THREE.AudioAnalyser(audio, 2048); +}; + +export default createAudioAnalyser; diff --git a/src/utils/playAudio.ts b/src/utils/playAudio.ts new file mode 100644 index 0000000..6ce9267 --- /dev/null +++ b/src/utils/playAudio.ts @@ -0,0 +1,8 @@ +const playAudio = (audio: HTMLAudioElement) => { + audio.currentTime = 0; + audio.volume = 0.5; + audio.loop = false; + audio.play(); +}; + +export default playAudio;
+

Sounds

+
+

+ Browsers require user permission to autoplay audio. If you're + not hearing any sound effects, just click somewhere around the + page.

-

- Firefox -

- privacy.resistFingerprinting should be set to false (it should - be by default). Otherwise, it limits the maximum WebGL texture - size to 2048, resulting in poor sprite quality. -
-
- Picture-In-Picture functionality should not be used (you most - likely have it disabled already). Just having it enabled won't - break anything, but actually using it might lead to some funny - visual bugs with media files. -
+ Firefox +

+ privacy.resistFingerprinting should be set to false (it should + be by default). Otherwise, it limits the maximum WebGL texture + size to 2048, resulting in poor sprite quality. +
+
+ Picture-In-Picture functionality should not be used (you most + likely have it disabled already). Just having it enabled won't + break anything, but actually using it might lead to some funny + visual bugs with media files.