From 453a9391ccf1ab0c7a6869c44664bfcdd1b68fa1 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 9 Nov 2018 13:27:49 +0100 Subject: Allow interacting with entities --- src/controller/entitycontext.ts | 8 +++- src/controller/gamecontext.ts | 54 ++++++++++++++++++++------ src/view/input/directionhandler.ts | 37 ------------------ src/view/input/gameinput.ts | 78 ++++++++++++++++++++++++++++++++++++++ src/view/input/inputhandler.ts | 24 ++++++------ tslint.json | 1 + 6 files changed, 140 insertions(+), 62 deletions(-) delete mode 100644 src/view/input/directionhandler.ts create mode 100644 src/view/input/gameinput.ts diff --git a/src/controller/entitycontext.ts b/src/controller/entitycontext.ts index c11c698..c1eb1fc 100644 --- a/src/controller/entitycontext.ts +++ b/src/controller/entitycontext.ts @@ -17,6 +17,7 @@ export class EntityContext implements CollidableGroup { return new EntityContext( renderer, + name, await loadEntity(renderer, entity), mkCollision(entity.collision), ); @@ -26,8 +27,9 @@ export class EntityContext implements CollidableGroup { private constructor( private readonly renderer: Renderer, + private readonly name: string, private readonly view: SpriteView, - public readonly collision: Collidable[], + private readonly collision: Collidable[], ) {} public render() { @@ -42,4 +44,8 @@ export class EntityContext implements CollidableGroup { public getCollidables(): Collidable[] { return this.collision; } + + public interact() { + alert(`You've interacted with ${this.name}!`); + } } diff --git a/src/controller/gamecontext.ts b/src/controller/gamecontext.ts index 8ec54b5..a12e3bc 100644 --- a/src/controller/gamecontext.ts +++ b/src/controller/gamecontext.ts @@ -3,7 +3,7 @@ import { EntityContext } from './entitycontext'; import { MapData } from '../model/data/map'; -import { DirectionHandler } from '../view/input/directionhandler'; +import { ButtonCode, GameInputHandler } from '../view/input/gameinput'; import { loadMap } from '../view/map'; import { Renderer } from '../view/renderer/renderer'; import { SpriteView } from '../view/sprite'; @@ -44,14 +44,16 @@ export class GameContext implements CollidableGroup { private time: number|null = null; private readonly tick = 10; // ms per tick - private readonly speed = 0.04; // movement per tick + private readonly maxSpeed = 0.04; // movement per tick private readonly maxSkip = 20; // maximum ticks to process in a single render step - private readonly input: DirectionHandler; + private readonly input: GameInputHandler; - private readonly playerMovement: vec2 = vec2.create(); + private readonly playerDir: vec2 = vec2.fromValues(0, 1); + private speed: number = 0; private readonly collisionRadius = 7 / 16; + private readonly interactLength = 1 / 32; private constructor( private readonly renderer: Renderer, @@ -60,12 +62,23 @@ export class GameContext implements CollidableGroup { private readonly entities: EntityContext[], private readonly collision: Collidable[], ) { - this.input = new DirectionHandler(); - this.input.addListener((v) => { - if (vec2.sqrLen(v) > 0) - vec2.normalize(this.playerMovement, v); - else - vec2.copy(this.playerMovement, [0, 0]); + this.input = new GameInputHandler(); + this.input.addListener((input) => { + switch (input.type) { + case 'button': + if (input.button === ButtonCode.Action) + this.interact(); + break; + + case 'direction': + if (vec2.sqrLen(input.direction) > 0) { + vec2.copy(this.playerDir, input.direction); + this.speed = this.maxSpeed; + } else { + this.speed = 0; + } + break; + } }); window.requestAnimationFrame(this.render); @@ -86,6 +99,23 @@ export class GameContext implements CollidableGroup { return diff; } + private canInteract(c: CollidableGroup): boolean { + const dest = vec2.scaleAndAdd(vec2.create(), this.player.pos, this.playerDir, this.interactLength); + const move = new Movement(this.player.pos, dest); + + return collide(c, vec2.create(), move, this.collisionRadius); + } + + private interact(): void { + for (const e of this.entities) { + if (!this.canInteract(e)) + continue; + + e.interact(); + break; + } + } + private updateStepCollide(out: vec2, dest: vec2): boolean { const move = new Movement(this.player.pos, dest); @@ -102,7 +132,7 @@ export class GameContext implements CollidableGroup { } private updateStep(): void { - const dest = vec2.scaleAndAdd(vec2.create(), this.player.pos, this.playerMovement, this.speed); + const dest = vec2.scaleAndAdd(vec2.create(), this.player.pos, this.playerDir, this.speed); const newDest = vec2.create(); while (this.updateStepCollide(newDest, dest)) { @@ -118,7 +148,7 @@ export class GameContext implements CollidableGroup { private update(time: number): void { const diff = Math.min(this.maxSkip, this.updateTime(time)); - if (vec2.sqrLen(this.playerMovement) === 0) { + if (!this.speed) { this.renderer.snapToGrid(this.player.pos, this.player.pos); return; } diff --git a/src/view/input/directionhandler.ts b/src/view/input/directionhandler.ts deleted file mode 100644 index 0a3687e..0000000 --- a/src/view/input/directionhandler.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { InputHandler } from './inputhandler'; - -import { Listenable } from '../../util'; - -import { vec2 } from 'gl-matrix'; - -export const enum Keycode { - Left = 37, - Up = 38, - Right = 39, - Down = 40, -} - -export class DirectionHandler extends Listenable<[vec2]> { - private readonly input: InputHandler; - - constructor() { - super(); - - this.input = new InputHandler(new Set([Keycode.Left, Keycode.Up, Keycode.Right, Keycode.Down])); - - this.input.addListener(() => { - const dir = vec2.create(); - - if (this.input.has(Keycode.Left)) - vec2.add(dir, dir, [-1, 0]); - if (this.input.has(Keycode.Up)) - vec2.add(dir, dir, [0, -1]); - if (this.input.has(Keycode.Right)) - vec2.add(dir, dir, [1, 0]); - if (this.input.has(Keycode.Down)) - vec2.add(dir, dir, [0, 1]); - - this.runListeners(dir); - }); - } -} diff --git a/src/view/input/gameinput.ts b/src/view/input/gameinput.ts new file mode 100644 index 0000000..f3066e3 --- /dev/null +++ b/src/view/input/gameinput.ts @@ -0,0 +1,78 @@ +import { InputHandler } from './inputhandler'; + +import { Listenable } from '../../util'; + +import { vec2 } from 'gl-matrix'; + +export enum ButtonCode { + Action, + Back, + Menu, +} + +const buttonMapping: {[key: string]: ButtonCode} = { + KeyZ: ButtonCode.Action, + KeyX: ButtonCode.Back, + KeyC: ButtonCode.Menu, +}; + +export interface DirectionInput { + type: 'direction'; + direction: vec2; +} + +export interface ButtonInput { + type: 'button'; + button: ButtonCode; +} + +export type GameInput = DirectionInput | ButtonInput; + +export class GameInputHandler extends Listenable<[GameInput]> { + private readonly input: InputHandler; + + constructor() { + super(); + + this.input = new InputHandler( + new Set([ + 'ArrowLeft', + 'ArrowUp', + 'ArrowRight', + 'ArrowDown', + ...Object.keys(buttonMapping), + ])); + + this.input.addListener((key: string, pressed: boolean) => { + const button = buttonMapping[key]; + if (button !== undefined) { + if (pressed) + this.runListeners({ + type: 'button', + button, + }); + + return; + } + + const dir = vec2.create(); + + if (this.input.has('ArrowLeft')) + vec2.add(dir, dir, [-1, 0]); + if (this.input.has('ArrowUp')) + vec2.add(dir, dir, [0, -1]); + if (this.input.has('ArrowRight')) + vec2.add(dir, dir, [1, 0]); + if (this.input.has('ArrowDown')) + vec2.add(dir, dir, [0, 1]); + + if (vec2.sqrLen(dir) > 0) + vec2.normalize(dir, dir); + + this.runListeners({ + type: 'direction', + direction: dir, + }); + }); + } +} diff --git a/src/view/input/inputhandler.ts b/src/view/input/inputhandler.ts index 96f15dd..22fdc28 100644 --- a/src/view/input/inputhandler.ts +++ b/src/view/input/inputhandler.ts @@ -1,39 +1,39 @@ import { Listenable } from '../../util'; -export class InputHandler extends Listenable<[]> { - private readonly keys: Set = new Set(); +export class InputHandler extends Listenable<[string, boolean]> { + private readonly keys: Set = new Set(); - constructor(relevantKeys: Set) { + constructor(relevantKeys: Set) { super(); window.addEventListener('keydown', (ev) => { - if (!relevantKeys.has(ev.keyCode)) + if (!relevantKeys.has(ev.code)) return; ev.preventDefault(); - if (this.keys.has(ev.keyCode)) + if (ev.repeat) return; - this.keys.add(ev.keyCode); - this.runListeners(); + this.keys.add(ev.code); + this.runListeners(ev.code, true); }); window.addEventListener('keyup', (ev) => { - if (!relevantKeys.has(ev.keyCode)) + if (!relevantKeys.has(ev.code)) return; ev.preventDefault(); - if (!this.keys.has(ev.keyCode)) + if (!this.keys.has(ev.code)) return; - this.keys.delete(ev.keyCode); - this.runListeners(); + this.keys.delete(ev.code); + this.runListeners(ev.code, false); }); } - public has(key: number): boolean { + public has(key: string): boolean { return this.keys.has(key); } } diff --git a/tslint.json b/tslint.json index 3d735e4..e3b63f0 100644 --- a/tslint.json +++ b/tslint.json @@ -10,6 +10,7 @@ "interface-name": false, "max-classes-per-file": false, "no-bitwise": false, + "object-literal-sort-keys": false, "one-variable-per-declaration": false, "quotemark": [true, "single", "avoid-escape", "avoid-template"] }, -- cgit v1.2.3