Skip to content

Commit db7fd6f

Browse files
committed
Added gamepad support
1 parent 88f6a96 commit db7fd6f

7 files changed

Lines changed: 254 additions & 30 deletions

File tree

eslint.config.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import js from "@eslint/js";
2+
import globals from "globals";
3+
import tseslint from "typescript-eslint";
4+
import pluginReact from "eslint-plugin-react";
5+
import { defineConfig } from "eslint/config";
6+
7+
export default defineConfig([
8+
{
9+
files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
10+
plugins: { js },
11+
extends: ["js/recommended"],
12+
languageOptions: { globals: globals.browser },
13+
},
14+
tseslint.configs.recommended,
15+
pluginReact.configs.flat.recommended,
16+
]);

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@next/env": "^16.2.6",
2424
"@types/webpack": "^5.28.5",
2525
"@uidotdev/usehooks": "^2.4.1",
26+
"awesome-react-gamepads": "^1.0.1",
2627
"framer-motion": "^12.40.0",
2728
"next": "^16.2.6",
2829
"particles.js": "^2.0.0",
@@ -34,6 +35,7 @@
3435
"webpack": "^5.107.2"
3536
},
3637
"devDependencies": {
38+
"@eslint/js": "^10.0.1",
3739
"@tailwindcss/postcss": "^4.3.0",
3840
"@types/node": "^25.9.1",
3941
"@types/react": "^19.2.15",
@@ -46,6 +48,8 @@
4648
"css-loader": "^7.1.4",
4749
"eslint": "^10.4.1",
4850
"eslint-config-next": "16.2.6",
51+
"eslint-plugin-react": "^7.37.5",
52+
"globals": "^17.6.0",
4953
"html-webpack-plugin": "^5.6.7",
5054
"mini-css-extract-plugin": "^2.10.2",
5155
"postcss": "^8.5.15",
@@ -58,6 +62,7 @@
5862
"tailwindcss": "^4.3.0",
5963
"ts-loader": "^9.6.0",
6064
"typescript": "^6.0.3",
65+
"typescript-eslint": "^8.60.0",
6166
"webpack-cli": "^7.0.3",
6267
"webpack-dev-server": "^5.2.4"
6368
},

src/app/layout.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import React from "react";
2+
import { useState } from "react";
13
import type { Metadata, Viewport } from 'next';
24
import { Inter } from "next/font/google";
35
import { config } from '@fortawesome/fontawesome-svg-core';
46
import '@fortawesome/fontawesome-svg-core/styles.css';
57
import "./globals.css";
68
import Head from 'next/head';
9+
import { useGamepads } from 'awesome-react-gamepads';
710

811
config.autoAddCss = false;
912

@@ -25,13 +28,19 @@ export default function RootLayout({
2528
}: Readonly<{
2629
children: React.ReactNode;
2730
}>) {
31+
const [secret, setSecret] = useState(false);
32+
useGamepads({
33+
onKonamiSuccess: () => {
34+
setSecret(true);
35+
}
36+
});
2837
return (
2938
<html lang="en">
3039
<Head>
3140
<meta name="keywords" content={'gundwn, gundwn.gg, steam, xbox, syntax-tm, github, git, xmb, links, social, profile, games'} />
3241
<meta name="twitter:site" content="@gundwnsrc" />
3342
</Head>
34-
<body className={`${inter.className} text-white`}>{children}</body>
43+
<body className={`${inter.className} text-white ${secret ? 'secret' : ''}`}>{children}</body>
3544
</html>
3645
);
3746
}

src/components/background/background.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ body {
1313
background-attachment: fixed;
1414
}
1515

16+
body.secret {
17+
background: rgb(20,36,80);
18+
background: linear-gradient(60deg, rgb(61, 91, 105) 27%, rgb(92, 74, 173) 47%, rgb(50, 104, 57) 68%);
19+
animation: gradient 15s ease infinite;
20+
background-size: 400% 400%;
21+
background-attachment: fixed;
22+
}
23+
1624
@keyframes gradient {
1725
0% {
1826
background-position: 0% 0%;

src/components/xmb-menu/xmb-menu.tsx

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
/* eslint-disable @next/next/no-img-element */
21
"use client"
32

3+
import React from "react";
44
import { useRef, useState, CSSProperties } from "react";
55
import { useRouter, ReadonlyURLSearchParams } from "next/navigation";
66
import { XmbMenu } from "@models/menu";
@@ -14,6 +14,7 @@ import useQuery from "@/hooks/useQuery";
1414
import build from "@services/menuBuilder";
1515
import "./xmb.css";
1616
import { useWindowSize } from "@uidotdev/usehooks";
17+
import { useGamepads } from 'awesome-react-gamepads';
1718

1819
const config: XmbMenu = build();
1920

@@ -63,8 +64,8 @@ export default function Menu() {
6364
function onEnter() {
6465
play();
6566

66-
let selectedCategory = config.getCurrentCategory();
67-
let selectedItem = selectedCategory.getCurrentItem();
67+
const selectedCategory = config.getCurrentCategory();
68+
const selectedItem = selectedCategory.getCurrentItem();
6869

6970
if (selectedItem.modal) {
7071
router.push(`/?modal=${selectedItem.modal}`);
@@ -84,29 +85,61 @@ export default function Menu() {
8485

8586
function onUp() {
8687
play();
87-
let position = config.moveUp();
88+
const position = config.moveUp();
8889
if (position === null) return;
8990
setY(position.y);
9091
}
9192

93+
function onTop() {
94+
play();
95+
const position = config.moveTop();
96+
if (position === null) return;
97+
setX(position.x);
98+
setY(position.y);
99+
}
100+
92101
function onDown() {
93102
play();
94-
let position = config.moveDown();
103+
const position = config.moveDown();
95104
if (position === null) return;
96105
setY(position.y);
97106
}
98107

108+
function onBottom() {
109+
play();
110+
const position = config.moveBottom();
111+
if (position === null) return;
112+
setX(position.x);
113+
setY(position.y);
114+
}
115+
99116
function onLeft() {
100117
play();
101-
let position = config.moveLeft();
118+
const position = config.moveLeft();
119+
if (position === null) return;
120+
setX(position.x);
121+
setY(position.y);
122+
}
123+
124+
function onFirst() {
125+
play();
126+
const position = config.moveFirst();
102127
if (position === null) return;
103128
setX(position.x);
104129
setY(position.y);
105130
}
106131

107132
function onRight() {
108133
play();
109-
let position = config.moveRight();
134+
const position = config.moveRight();
135+
if (position === null) return;
136+
setX(position.x);
137+
setY(position.y);
138+
}
139+
140+
function onLast() {
141+
play();
142+
const position = config.moveLast();
110143
if (position === null) return;
111144
setX(position.x);
112145
setY(position.y);
@@ -117,7 +150,7 @@ export default function Menu() {
117150
}
118151

119152
useQuery({ onPathChanged: onPathChanged });
120-
153+
121154
const actions = new Map<string, KeyPressAction>();
122155

123156
actions.set('w', { repeat: true, onKeyPress: onUp });
@@ -133,9 +166,44 @@ export default function Menu() {
133166
actions.set('escape', { repeat: false, onKeyPress: onEsc });
134167
actions.set('h', { repeat: false, onKeyPress: onHelp });
135168
actions.set('f1', { repeat: false, onKeyPress: onHelp });
169+
actions.set('q', { repeat: false, onKeyPress: onFirst });
170+
actions.set('e', { repeat: false, onKeyPress: onLast });
171+
actions.set('z', { repeat: false, onKeyPress: onTop });
172+
actions.set('x', { repeat: false, onKeyPress: onBottom });
136173

137174
useKeyboard({ actions: actions, enabledOnModal: false });
138175

176+
useGamepads({
177+
onConnect: (gamepad) => {
178+
console.log(`gamepad connected: ${gamepad.id} (${gamepad.index})`);
179+
console.log('buttons:');
180+
console.log(JSON.stringify(gamepad.buttons, null, 2));
181+
},
182+
onDisconnect: (gamepad) => {
183+
console.log(`gamepad disconnected: ${gamepad.id} (${gamepad.index})`);
184+
},
185+
onA: onEnter,
186+
onB: onEsc,
187+
onDPadUp: onUp,
188+
onDPadDown: onDown,
189+
onDPadLeft: onLeft,
190+
onDPadRight: onRight,
191+
onLeftStickUp: onUp,
192+
onLeftStickDown: onDown,
193+
onLeftStickLeft: onLeft,
194+
onLeftStickRight: onRight,
195+
onRightStickUp: onUp,
196+
onRightStickDown: onDown,
197+
onRightStickLeft: onLeft,
198+
onRightStickRight: onRight,
199+
onStart: onEnter,
200+
onSelect: onHelp,
201+
onLT: onTop,
202+
onRT: onBottom,
203+
onLB: onFirst,
204+
onRB: onLast,
205+
});
206+
139207
const wheelInput: WheelInput = {
140208
onWheelUp: onUp,
141209
onWheelDown: onDown,

0 commit comments

Comments
 (0)