mirror of
https://github.com/ad044/lainTSX.git
synced 2024-10-22 23:19:06 +00:00
added custom keybinding support
This commit is contained in:
parent
e170489c46
commit
2f8f30d1ba
11 changed files with 200 additions and 89 deletions
12
src/App.tsx
12
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 (
|
||||
<HashRouter>
|
||||
<Switch>
|
||||
|
@ -14,6 +23,7 @@ const App = () => {
|
|||
<Route path={"/notes"} exact component={Notes} />
|
||||
<Route path={"/game"} exact component={Game} />
|
||||
<Route path={"/guide"} exact component={Guide} />
|
||||
<Route path={"/keybinding"} exact component={Keybinding} />
|
||||
</Switch>
|
||||
</HashRouter>
|
||||
);
|
||||
|
|
|
@ -120,9 +120,6 @@ const InputHandler = memo(() => {
|
|||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyBoardEvent);
|
||||
window.removeEventListener("keyup", () => {
|
||||
firedRef.current = false;
|
||||
});
|
||||
};
|
||||
}, [handleKeyBoardEvent]);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ const Header = () => {
|
|||
<Link to="/game">start</Link>
|
||||
<Link to="/guide">guide</Link>
|
||||
<a href="https://discord.com/invite/W22Ga2R">discord</a>
|
||||
<Link to="/keybinding">keybinding</Link>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
104
src/dom-components/Keybinding.tsx
Normal file
104
src/dom-components/Keybinding.tsx
Normal file
|
@ -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 (
|
||||
<>
|
||||
<Header />
|
||||
<br />
|
||||
<p className="keybinding-note">
|
||||
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.
|
||||
</p>
|
||||
<br />
|
||||
<div className="keybinding">
|
||||
<table className="control-table">
|
||||
<tbody>
|
||||
{Object.entries(bindings).map((pair, idx) => (
|
||||
<tr onClick={() => startKeybindListener(pair[0])} key={idx}>
|
||||
<td>{formatKey(pair[0])}</td>
|
||||
<td>{pair[1]}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<br />
|
||||
<button className="reset-to-default-button" onClick={resetToDefault}>
|
||||
Reset to default bindings
|
||||
</button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Keybinding;
|
|
@ -60,10 +60,7 @@ const Notes = () => {
|
|||
<p>
|
||||
Your setup must support WebGL2 in order to play this game. You
|
||||
can check this directly by going to{" "}
|
||||
<a
|
||||
href={"https://get.webgl.org/webgl2/"}
|
||||
className="webgl-anchor"
|
||||
>
|
||||
<a href={"https://get.webgl.org/webgl2/"} className="notes-a">
|
||||
this website
|
||||
</a>
|
||||
. If it's not supported, this is most likely due to your
|
||||
|
@ -174,6 +171,14 @@ const Notes = () => {
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<span className="text-center">
|
||||
If you'd like to change the keybindings, go{" "}
|
||||
<Link to={"/keybinding"} className="notes-a">
|
||||
here
|
||||
</Link>
|
||||
.
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
22
src/helpers/keybinding-helpers.ts
Normal file
22
src/helpers/keybinding-helpers.ts
Normal file
|
@ -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;
|
||||
}
|
||||
};
|
20
src/static/css/keybinding.css
Normal file
20
src/static/css/keybinding.css
Normal file
|
@ -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;
|
||||
}
|
|
@ -56,7 +56,7 @@
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.webgl-anchor {
|
||||
.notes-a {
|
||||
text-align: unset;
|
||||
font-size: unset;
|
||||
font-weight: unset;
|
||||
|
|
23
src/store.ts
23
src/store.ts
|
@ -105,6 +105,8 @@ type State = {
|
|||
|
||||
siteSaveState: SiteSaveState;
|
||||
|
||||
keybindings: Record<string, string>;
|
||||
|
||||
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<string, string>) =>
|
||||
set(() => ({ keybindings: to })),
|
||||
|
||||
restartGameState: () =>
|
||||
set(() => ({
|
||||
siteSaveState: {
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue