88 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			88 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| type state = 'down' | 'up';
 | |
| 
 | |
| // - TODO - Pull this from the actual game creating the controls
 | |
| const GameControls = {
 | |
|   down: 'down',
 | |
|   up: 'up',
 | |
|   left: 'left',
 | |
|   right: 'right',
 | |
|   shift: 'shift',
 | |
|   ' ': ' ',
 | |
| } as const;
 | |
| 
 | |
| /**
 | |
|  * Abstraction for game controls, the game should say what controls it expects and then we can remap the actual controls separately.  
 | |
|  * i.e. Both "ArrowDown" and "Down" from the keyboard listener should be the games "down" event.  
 | |
|  * Also handles when the keys are pressed or not.
 | |
|  */
 | |
| class Control {
 | |
|   readonly gameControls = GameControls;
 | |
|   private _controlMap: { [key: string]: keyof typeof GameControls } = {
 | |
|     ArrowDown: GameControls.down,
 | |
|     Down: GameControls.down,
 | |
|     ArrowUp: GameControls.up,
 | |
|     Up: GameControls.up,
 | |
|     ArrowLeft: GameControls.left,
 | |
|     Left: GameControls.left,
 | |
|     ArrowRight: GameControls.right,
 | |
|     Right: GameControls.right,
 | |
|     Shift: GameControls.shift,
 | |
|     ' ': GameControls[' '],
 | |
|   };
 | |
| 
 | |
|   private _controlState: Map<keyof typeof GameControls, {
 | |
|     state: state,
 | |
| 
 | |
|     consumed: boolean
 | |
|   }> = new Map();
 | |
| 
 | |
|   constructor() {
 | |
|     this._setKeys();
 | |
|     window.addEventListener('keydown', (event) => {
 | |
|       if (this._controlState.get(this._controlMap[event.key])?.state !== 'down') {
 | |
|         this._controlState.set(this._controlMap[event.key], { state: 'down', consumed: false });
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     window.addEventListener('keyup', (event) => {
 | |
|       if (this._controlState.get(this._controlMap[event.key])?.state !== 'up') {
 | |
|         this._controlState.set(this._controlMap[event.key], { state: 'up', consumed: false });
 | |
|       }
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * - TODO
 | |
|    */
 | |
|   private _setKeys(): void {
 | |
|     Object.keys(GameControls).forEach((key) => {
 | |
|       this._controlState.set(key as keyof typeof GameControls, { state: 'up', consumed: false });
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * - TODO
 | |
|    *
 | |
|    * @param key - TODO
 | |
|    */
 | |
|   isDown(key: keyof typeof GameControls): boolean {
 | |
|     return this._controlState.get(key)?.state === 'down';
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Consume a down key event once
 | |
|    * 
 | |
|    * @param key  - TODO
 | |
|    */
 | |
|   onceDown(key: keyof typeof GameControls): boolean {
 | |
|     const keyState = this._controlState.get(key);
 | |
|     if (keyState && keyState.state === 'down' && !keyState.consumed) {
 | |
|       keyState.consumed = true;
 | |
|       return true;
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| export default Control;
 |