Skip to content

Commit

Permalink
feat: add keyboard input hook to player client
Browse files Browse the repository at this point in the history
  • Loading branch information
hxtree committed Dec 9, 2024
1 parent c5bef5b commit 466e13d
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 34 deletions.
2 changes: 2 additions & 0 deletions clients/design-system/src/game-assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Game assets are intentionally not realistic to help players to suspend belief
due to the [uncanny valley](https://en.wikipedia.org/wiki/Uncanny_valley).
6 changes: 6 additions & 0 deletions clients/design-system/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,11 @@ export default defineConfig({
entryFileNames: '[name].js',
},
},
cssCodeSplit: false,
},
css: {
modules: {
scopeBehaviour: 'global',
},
},
});
2 changes: 2 additions & 0 deletions clients/player-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"dependencies": {
"@galaxyops/design-system": "workspace:*",
"axios": "^1.6.7",
"luxon": "~3.3.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-router-dom": "6.22.1"
Expand All @@ -34,6 +35,7 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/jest": "29.5.12",
"@types/luxon": "~3.2.0",
"@types/react": "18.2.59",
"@types/react-dom": "18.2.19",
"@typescript-eslint/eslint-plugin": "~6.16.0",
Expand Down
6 changes: 6 additions & 0 deletions clients/player-client/src/context/Action.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Actions } from './Actions.type';

export type Action =
| { type: Actions.SET_USER; payload: string }
| { type: Actions.LOGOUT }
| { type: Actions.PAGE_LOADING; payload: boolean };
5 changes: 5 additions & 0 deletions clients/player-client/src/context/Actions.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum Actions {
PAGE_LOADING = 'PAGE_LOADING',
SET_USER = 'SET_USER',
LOGOUT = 'LOGOUT',
}
11 changes: 11 additions & 0 deletions clients/player-client/src/context/AppContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createContext } from 'react';
import { AppState } from './AppState.type';
import { Action } from './Action.type';

export const AppContext = createContext<
| {
state: AppState;
dispatch: React.Dispatch<Action>;
}
| undefined
>(undefined);
39 changes: 5 additions & 34 deletions clients/player-client/src/context/AppProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,10 @@
import React, { createContext, useReducer, useContext } from 'react';

enum Actions {
PAGE_LOADING = 'PAGE_LOADING',
SET_USER = 'SET_USER',
LOGOUT = 'LOGOUT',
}

interface AppState {
user: string | null;
isLoading?: boolean;
}
import React, { useReducer } from 'react';
import { Action } from './Action.type';
import { Actions } from './Actions.type';
import { AppState } from './AppState.type';
import { AppContext } from './AppContext';

// TODO add footnotes

type Action =
| { type: Actions.SET_USER; payload: string }
| { type: Actions.LOGOUT }
| { type: Actions.PAGE_LOADING; payload: boolean };

const reducer = (state: AppState, action: Action): AppState => {
switch (action.type) {
case Actions.PAGE_LOADING:
Expand All @@ -31,14 +18,6 @@ const reducer = (state: AppState, action: Action): AppState => {
}
};

const AppContext = createContext<
| {
state: AppState;
dispatch: React.Dispatch<Action>;
}
| undefined
>(undefined);

export const AppProvider: React.FC<{ children: React.ReactNode }> = props => {
const { children } = props;
const [state, dispatch] = useReducer(reducer, { user: null });
Expand All @@ -49,11 +28,3 @@ export const AppProvider: React.FC<{ children: React.ReactNode }> = props => {
</AppContext.Provider>
);
};

export const useAppContext = () => {
const context = useContext(AppContext);
if (!context) {
throw new Error('useAppContext must be used within an AppProvider');
}
return context;
};
4 changes: 4 additions & 0 deletions clients/player-client/src/context/AppState.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface AppState {
user: string | null;
isLoading?: boolean;
}
10 changes: 10 additions & 0 deletions clients/player-client/src/context/useContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { useContext } from 'react';
import { AppContext } from './AppContext';

export const useAppContext = () => {
const context = useContext(AppContext);
if (!context) {
throw new Error('useAppContext must be used within an AppProvider');
}
return context;
};
16 changes: 16 additions & 0 deletions clients/player-client/src/core/config/keyboard-bindings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// keyboard
export const keyboardBindings = {
up: 'w',
down: 's',
left: 'a',
right: 'd',
jump: ' ',
debug: 'p',
select: 'Enter',
back: 'u',
pause: 'Escape',
};

// other inputs will be touch and based
// may need to display buttons on screen for touch
// move by clicking a tile..
1 change: 1 addition & 0 deletions clients/player-client/src/core/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './player-input-record.type';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type PlayerInputRecord = {
key: string;
timestamp: string;
};
46 changes: 46 additions & 0 deletions clients/player-client/src/core/useHandleInput.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState, useEffect } from 'react';
import { keyboardBindings } from './config/keyboard-bindings';
import { DateTime } from 'luxon';
import { PlayerInputRecord } from './types';

export default function useHandleInput(): PlayerInputRecord[] | null {
const [inputState, setInputState] = useState<PlayerInputRecord[] | null>(
null,
);
const maxRecords = 5; // Set the maximum number of records

useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
const newInputRecords: PlayerInputRecord[] = [];

// Handle different key strokes
Object.entries(keyboardBindings).forEach(([direction, key]) => {
if (event.key === key) {
// Collect key press for the given direction
newInputRecords.push({
key: direction,
timestamp: DateTime.now().toISO(),
});
}
});

if (newInputRecords.length > 0) {
setInputState(prevState => {
const newState = prevState ? [...prevState] : [];
newInputRecords.forEach(record => {
newState.unshift(record);
});
return newState.slice(0, maxRecords);
});
}
};

window.addEventListener('keydown', handleKeyDown);

return () => {
window.removeEventListener('keydown', handleKeyDown);
};
}, []);

return inputState;
}
4 changes: 4 additions & 0 deletions clients/player-client/src/pages/home.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import {
IsometricCanvasProps,
} from '@galaxyops/design-system/dist/main';
import gameState from './game-state.json';
import useHandleInput from '../core/useHandleInput';

export default function HomePage() {
const input = useHandleInput();

return (
<>
{input && <div>Input: {input?.map(i => i.key).join(', ')}</div>}
{/* @ts-expect-error needed for types temp */}
<IsometricCanvas {...(gameState as IsometricCanvasProps)} />
</>
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 466e13d

Please sign in to comment.