From 2f8f30d1ba26e7a8594f3a47ea351e80a0d1fc36 Mon Sep 17 00:00:00 2001 From: ad044 Date: Mon, 5 Apr 2021 17:51:54 +0400 Subject: [PATCH] added custom keybinding support --- src/App.tsx | 12 +++- src/components/InputHandler.tsx | 3 - src/dom-components/Header.tsx | 1 + src/dom-components/Keybinding.tsx | 104 ++++++++++++++++++++++++++++++ src/dom-components/Notes.tsx | 13 ++-- src/helpers/keybinding-helpers.ts | 22 +++++++ src/static/css/keybinding.css | 20 ++++++ src/static/css/notes.css | 2 +- src/store.ts | 23 +++++++ src/utils/CustomTextureLoader.ts | 59 ----------------- src/utils/getKeyPress.ts | 30 +++------ 11 files changed, 200 insertions(+), 89 deletions(-) create mode 100644 src/dom-components/Keybinding.tsx create mode 100644 src/helpers/keybinding-helpers.ts create mode 100644 src/static/css/keybinding.css delete mode 100644 src/utils/CustomTextureLoader.ts diff --git a/src/App.tsx b/src/App.tsx index b9aed70..32f2d13 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,21 @@ -import React from "react"; +import React, { useEffect } from "react"; 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"; +import Keybinding from "./dom-components/Keybinding"; +import { useStore } from "./store"; const App = () => { + const setKeybindings = useStore((state) => state.setKeybindings); + + useEffect(() => { + const keybindingSettings = localStorage.getItem("lainKeybindings"); + if (keybindingSettings) setKeybindings(JSON.parse(keybindingSettings)); + }, [setKeybindings]); + return ( @@ -14,6 +23,7 @@ const App = () => { + ); diff --git a/src/components/InputHandler.tsx b/src/components/InputHandler.tsx index f82cdd0..c42fdaa 100644 --- a/src/components/InputHandler.tsx +++ b/src/components/InputHandler.tsx @@ -120,9 +120,6 @@ const InputHandler = memo(() => { return () => { window.removeEventListener("keydown", handleKeyBoardEvent); - window.removeEventListener("keyup", () => { - firedRef.current = false; - }); }; }, [handleKeyBoardEvent]); diff --git a/src/dom-components/Header.tsx b/src/dom-components/Header.tsx index f14ea03..1aac193 100644 --- a/src/dom-components/Header.tsx +++ b/src/dom-components/Header.tsx @@ -10,6 +10,7 @@ const Header = () => { start guide discord + keybinding ); }; diff --git a/src/dom-components/Keybinding.tsx b/src/dom-components/Keybinding.tsx new file mode 100644 index 0000000..d558bee --- /dev/null +++ b/src/dom-components/Keybinding.tsx @@ -0,0 +1,104 @@ +import React, { useCallback, useEffect } from "react"; +import { formatKey } from "../helpers/keybinding-helpers"; +import "../static/css/keybinding.css"; +import { useStore } from "../store"; +import Header from "./Header"; + +const Keybinding = () => { + const setKeybindings = useStore((state) => state.setKeybindings); + const bindings = useStore((state) => state.keybindings); + + useEffect(() => { + document.title = "< keybinding >"; + }, []); + + const handleRemap = useCallback( + (keyToRemap: string, to: string) => { + if (to.length === 1) to = to.toLowerCase(); + + const exists = Object.values(bindings).includes(to); + + if (!exists) { + const newBindings = { ...bindings, [keyToRemap]: to }; + setKeybindings(newBindings); + localStorage.setItem("lainKeybindings", JSON.stringify(newBindings)); + } else { + const takenKeybind = Object.keys(bindings).find( + (k) => bindings[k] === to + ); + + if (takenKeybind) { + const newBindings = { + ...bindings, + [takenKeybind]: "", + [keyToRemap]: to, + }; + setKeybindings(newBindings); + localStorage.setItem("lainKeybindings", JSON.stringify(newBindings)); + } + } + }, + [bindings, setKeybindings] + ); + + const startKeybindListener = useCallback( + (keyToRemap: string) => { + window.addEventListener( + "keydown", + (event) => handleRemap(keyToRemap, event.key), + { once: true } + ); + }, + [handleRemap] + ); + + const resetToDefault = useCallback(() => { + setKeybindings({ + DOWN: "ArrowDown", + LEFT: "ArrowLeft", + UP: "ArrowUp", + RIGHT: "ArrowRight", + CIRCLE: "x", + CROSS: "z", + TRIANGLE: "d", + SQUARE: "s", + R2: "t", + L2: "e", + L1: "w", + R1: "r", + START: "v", + SELECT: "c", + }); + localStorage.removeItem("lainKeybindings"); + }, [setKeybindings]); + return ( + <> +
+
+

+ This is the keybindings page. To change a keybinding, just click on it + and press the button you wish to bind it to after. In order for this to + take effect, you must refresh the game page. +

+
+
+ + + {Object.entries(bindings).map((pair, idx) => ( + startKeybindListener(pair[0])} key={idx}> + + + + ))} + +
{formatKey(pair[0])}{pair[1]}
+
+
+ + + ); +}; + +export default Keybinding; diff --git a/src/dom-components/Notes.tsx b/src/dom-components/Notes.tsx index d655d88..64da758 100644 --- a/src/dom-components/Notes.tsx +++ b/src/dom-components/Notes.tsx @@ -60,10 +60,7 @@ const Notes = () => {

Your setup must support WebGL2 in order to play this game. You can check this directly by going to{" "} - + this website . If it's not supported, this is most likely due to your @@ -174,6 +171,14 @@ const Notes = () => { +
+ + If you'd like to change the keybindings, go{" "} + + here + + . + diff --git a/src/helpers/keybinding-helpers.ts b/src/helpers/keybinding-helpers.ts new file mode 100644 index 0000000..ef2db63 --- /dev/null +++ b/src/helpers/keybinding-helpers.ts @@ -0,0 +1,22 @@ +export const formatKey = (key: string) => { + switch (key) { + case "DOWN": + return "↓"; + case "LEFT": + return "←"; + case "UP": + return "↑"; + case "RIGHT": + return "→"; + case "CIRCLE": + return "◯"; + case "CROSS": + return "✖"; + case "SQUARE": + return "◼"; + case "TRIANGLE": + return "▲"; + default: + return key; + } +}; diff --git a/src/static/css/keybinding.css b/src/static/css/keybinding.css new file mode 100644 index 0000000..85aaa80 --- /dev/null +++ b/src/static/css/keybinding.css @@ -0,0 +1,20 @@ +.keybinding { + color: white; + display: block; + margin: 0 auto; + text-align: center; + font-size: 1.5rem; +} + +.keybinding-note { + text-align: center; + font-size: 1.7rem; + color: white; + padding-left: 5em; + padding-right: 5em; +} + +.reset-to-default-button { + display: block; + margin: 0 auto; +} diff --git a/src/static/css/notes.css b/src/static/css/notes.css index 0b0dbc7..a23615a 100644 --- a/src/static/css/notes.css +++ b/src/static/css/notes.css @@ -56,7 +56,7 @@ vertical-align: middle; } -.webgl-anchor { +.notes-a { text-align: unset; font-size: unset; font-weight: unset; diff --git a/src/store.ts b/src/store.ts index 10684ca..82815d4 100644 --- a/src/store.ts +++ b/src/store.ts @@ -105,6 +105,8 @@ type State = { siteSaveState: SiteSaveState; + keybindings: Record; + inputCooldown: number; }; @@ -237,6 +239,24 @@ export const useStore = create( }, }, + // keybindings + keybindings: { + DOWN: "ArrowDown", + LEFT: "ArrowLeft", + UP: "ArrowUp", + RIGHT: "ArrowRight", + CIRCLE: "x", + CROSS: "z", + TRIANGLE: "d", + SQUARE: "s", + R2: "t", + L2: "e", + L1: "w", + R1: "r", + START: "v", + SELECT: "c", + }, + inputCooldown: -1, } as State, (set) => ({ @@ -340,6 +360,9 @@ export const useStore = create( playerName: userState.playerName, })), + setKeybindings: (to: Record) => + set(() => ({ keybindings: to })), + restartGameState: () => set(() => ({ siteSaveState: { diff --git a/src/utils/CustomTextureLoader.ts b/src/utils/CustomTextureLoader.ts deleted file mode 100644 index 5b6abac..0000000 --- a/src/utils/CustomTextureLoader.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - Loader, - RGBAFormat, - RGBFormat, - ImageLoader, - Texture, - LinearFilter, - NearestFilter, - ClampToEdgeWrapping, -} from "three"; - -/* - -custom implementation of TextureLoader that automatically sets minFilter to NearestFilter for proper WebGL1 support. - -this is still experimental - -*/ -export class CustomTextureLoader extends Loader { - load = ( - url: string, - onLoad?: (texture: Texture) => void, - onProgress?: (event: ProgressEvent) => void, - onError?: (event: ErrorEvent) => void - ): Texture => { - const texture = new Texture(); - - texture.generateMipmaps = false; - texture.wrapS = texture.wrapT = ClampToEdgeWrapping; - texture.minFilter = LinearFilter; - - const loader = new ImageLoader(this.manager); - loader.setCrossOrigin(this.crossOrigin); - loader.setPath(this.path); - - loader.load( - url, - function (image: HTMLImageElement) { - texture.image = image; - - const isJPEG = - url.search(/\.jpe?g($|\?)/i) > 0 || - url.search(/^data:image\/jpeg/) === 0; - - texture.format = isJPEG ? RGBFormat : RGBAFormat; - texture.needsUpdate = true; - - if (onLoad !== undefined) { - onLoad(texture); - } - }, - onProgress, - onError - ); - return texture; - }; -} - -export default CustomTextureLoader; diff --git a/src/utils/getKeyPress.ts b/src/utils/getKeyPress.ts index 5872288..653db29 100644 --- a/src/utils/getKeyPress.ts +++ b/src/utils/getKeyPress.ts @@ -1,25 +1,13 @@ -const getKeyPress = (key: string) => { - // make the keybinds work with caps lock on aswell - if (["X", "Z", "D", "E", "V", "T", "W", "R", "S", "C"].includes(key)) - key = key.toLowerCase(); +import { useStore } from "../store"; - const keyCodeAssocs = { - ArrowDown: "DOWN", - ArrowLeft: "LEFT", - ArrowUp: "UP", - ArrowRight: "RIGHT", - x: "CIRCLE", - z: "CROSS", - d: "TRIANGLE", - s: "SQUARE", - t: "R2", - e: "L2", - w: "L1", - r: "R1", - v: "START", - c: "SELECT", - }; - return keyCodeAssocs[key as keyof typeof keyCodeAssocs]; +const getKeyPress = (key: string) => { + const keybindings = useStore.getState().keybindings; + + console.log(keybindings); + // make the keybinds work with caps lock on aswell + if (key.length === 1) key = key.toLowerCase(); + + return Object.keys(keybindings).find((k) => keybindings[k] === key); }; export default getKeyPress;