refactor: input greatness
This commit is contained in:
parent
350ca435c1
commit
fcf09044dc
|
@ -18,6 +18,7 @@ import {clearAnimation, setAnimation} from '@avocado/timing';
|
|||
import {World} from '@avocado/physics/matter/world';
|
||||
import {Room, RoomView} from '@avocado/topdown';
|
||||
// 1st party.
|
||||
import {actionIds} from '../common/action-ids';
|
||||
import {augmentParserWithThroughput} from '../common/parser-throughput';
|
||||
import {SelfEntityPacket} from '../common/packets/self-entity.packet';
|
||||
import {WorldTime} from '../common/world-time.synchronized';
|
||||
|
@ -83,14 +84,21 @@ export class App extends decorate(class {}) {
|
|||
// Input.
|
||||
this.actionRegistry = new ActionRegistry();
|
||||
this.actionRegistry.mapKeysToActions(config.actionKeyMap);
|
||||
this.actionState = this.actionRegistry.state;
|
||||
this.actionRegistry.setActionTransformerFor('UseItem', (type) => {
|
||||
if ('keyDown' === type) {
|
||||
return this.selfEntity.activeSlotIndex;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
})
|
||||
InputPacket.setActionIds(actionIds());
|
||||
this.inputHandle = undefined;
|
||||
this.actionRegistry.listen(this.stage.inputNormalizer);
|
||||
this.onBlur = this.onBlur.bind(this);
|
||||
this.onFocus = this.onFocus.bind(this);
|
||||
// Global keys.
|
||||
this.pointingAt = [-1, -1];
|
||||
this.pointerMovementHandle = undefined;
|
||||
// Net.
|
||||
this.AugmentedParser = augmentParserWithThroughput(SocketIoParser);
|
||||
this.hasReceivedState = false;
|
||||
|
@ -219,7 +227,7 @@ export class App extends decorate(class {}) {
|
|||
}
|
||||
|
||||
createMoveToNormal(position) {
|
||||
if (!this.selfEntity) {
|
||||
if (!this.selfEntity || !this.selfEntity.is('existent')) {
|
||||
return;
|
||||
}
|
||||
const realEntityPosition = Vector.sub(
|
||||
|
@ -286,14 +294,6 @@ export class App extends decorate(class {}) {
|
|||
const slotIndex = (parseInt(key) + 9) % 10;
|
||||
this.setActiveSlotIndex(slotIndex);
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
if (this.selfEntity) {
|
||||
this.actionRegistry.state = this.actionRegistry.state.set(
|
||||
'UseItem',
|
||||
this.selfEntity.activeSlotIndex
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -415,10 +415,12 @@ export class App extends decorate(class {}) {
|
|||
readConfig() {
|
||||
return {
|
||||
actionKeyMap: {
|
||||
'w': 'MoveUp',
|
||||
'a': 'MoveLeft',
|
||||
's': 'MoveDown',
|
||||
'd': 'MoveRight',
|
||||
'MoveUp': 'w',
|
||||
'MoveLeft': 'a',
|
||||
'MoveDown': 's',
|
||||
'MoveRight': 'd',
|
||||
'UseItem': 'ArrowLeft',
|
||||
'Interact': 'Enter',
|
||||
},
|
||||
connectionUrl: window.location.protocol + '//' + window.location.hostname + ':8420/',
|
||||
doPhysicsSimulation: true,
|
||||
|
@ -533,43 +535,15 @@ export class App extends decorate(class {}) {
|
|||
this.stage.element.addEventListener('focus', this.onFocus);
|
||||
// Input messages.
|
||||
this.inputHandle = setInterval(() => {
|
||||
if (this.actionState !== this.actionRegistry.state) {
|
||||
this.actionState = this.actionRegistry.state;
|
||||
this.socket.send(InputPacket.fromState(this.actionState));
|
||||
this.actionRegistry.state = this.actionRegistry.state.delete(
|
||||
'UseItem'
|
||||
);
|
||||
const inputStream = this.actionRegistry.drain();
|
||||
if (inputStream.length > 0) {
|
||||
// Inject input.
|
||||
if (this.selfEntity) {
|
||||
this.selfEntity.inputStream = inputStream;
|
||||
}
|
||||
this.socket.send(new InputPacket(inputStream));
|
||||
}
|
||||
}, 1000 * config.inputFrequency);
|
||||
// Mouse/touch movement.
|
||||
this.pointerMovementHandle = setInterval(() => {
|
||||
do {
|
||||
let normal;
|
||||
if (this.isPointingAtAnything()) {
|
||||
const toVector = Vector.scale(this.pointingAt, this.isDebugging ? 2 : 1);
|
||||
normal = this.createMoveToNormal(toVector);
|
||||
if (normal) {
|
||||
this.actionRegistry.state = this.actionRegistry.state.withMutations((state) => {
|
||||
if (normal[0]) {
|
||||
state.set('MoveToX', Math.floor(normal[0] * 127));
|
||||
}
|
||||
if (normal[1]) {
|
||||
state.set('MoveToY', Math.floor(normal[1] * 127));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.actionRegistry.state = this.actionRegistry.state.withMutations((state) => {
|
||||
if (!normal || 0 === normal[0]) {
|
||||
state.delete('MoveToX');
|
||||
}
|
||||
if (!normal || 0 === normal[1]) {
|
||||
state.delete('MoveToY');
|
||||
}
|
||||
});
|
||||
} while (false);
|
||||
}, 1000 * config.pointerMovementFrequency);
|
||||
// Focus the stage.
|
||||
this.stage.focus();
|
||||
this.isFocused = true;
|
||||
|
@ -606,10 +580,6 @@ export class App extends decorate(class {}) {
|
|||
const now = performance.now();
|
||||
const elapsed = (now - lastTime) / 1000;
|
||||
lastTime = now;
|
||||
// Inject input.
|
||||
if (this.selfEntity) {
|
||||
this.selfEntity.inputState = this.actionState;
|
||||
}
|
||||
// Tick.
|
||||
this.worldTime.tick(elapsed);
|
||||
if (this.room) {
|
||||
|
@ -621,8 +591,6 @@ export class App extends decorate(class {}) {
|
|||
}
|
||||
|
||||
stopProcessingInput() {
|
||||
clearInterval(this.pointerMovementHandle);
|
||||
this.pointerMovementHandle = undefined;
|
||||
clearInterval(this.inputHandle);
|
||||
this.inputHandle = undefined;
|
||||
this.stage.element.removeEventListener('focus', this.onFocus);
|
||||
|
|
11
common/action-ids.js
Normal file
11
common/action-ids.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
export function actionIds() {
|
||||
return {
|
||||
'MoveUp': 0,
|
||||
'MoveLeft': 1,
|
||||
'MoveDown': 2,
|
||||
'MoveRight': 3,
|
||||
'UseItem': 4,
|
||||
'Interact': 5,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import * as I from 'immutable';
|
||||
|
||||
import {TickingPromise} from '@avocado/core';
|
||||
import {Trait} from '@avocado/entity';
|
||||
import {Vector} from '@avocado/math';
|
||||
|
||||
// Input handling.
|
||||
export class Controllable extends Trait {
|
||||
|
@ -12,56 +11,54 @@ export class Controllable extends Trait {
|
|||
|
||||
constructor(entity, params, state) {
|
||||
super(entity, params, state);
|
||||
this._inputState = I.Map();
|
||||
this._itemPromise = undefined;
|
||||
this._movementVector = [0, 0];
|
||||
}
|
||||
|
||||
set inputState(inputState) {
|
||||
this._inputState = I.Map(inputState);
|
||||
set inputStream(inputStream) {
|
||||
for (let i = 0; i < inputStream.length; i++) {
|
||||
const {action, value} = inputStream[i];
|
||||
const normalized = 0 === value ? -1 : 1;
|
||||
switch (action) {
|
||||
case 'MoveUp':
|
||||
this._movementVector[1] -= normalized;
|
||||
break;
|
||||
case 'MoveRight':
|
||||
this._movementVector[0] += normalized;
|
||||
break;
|
||||
case 'MoveDown':
|
||||
this._movementVector[1] += normalized;
|
||||
break;
|
||||
case 'MoveLeft':
|
||||
this._movementVector[0] -= normalized;
|
||||
break;
|
||||
case 'UseItem':
|
||||
if (AVOCADO_SERVER) {
|
||||
if (inputState.has('UseItem')) {
|
||||
const slotIndex = inputState.get('UseItem');
|
||||
this._itemPromise = this.entity.useItemInSlot(slotIndex);
|
||||
if (-1 !== value && !this._itemPromise) {
|
||||
this._itemPromise = this.entity.useItemInSlot(value);
|
||||
Promise.resolve(this._itemPromise).then(() => {
|
||||
this._itemPromise = undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Vector.isZero(this._movementVector)) {
|
||||
this.entity.currentAnimation = 'idle';
|
||||
}
|
||||
else {
|
||||
this.entity.currentAnimation = 'moving';
|
||||
}
|
||||
}
|
||||
|
||||
tick(elapsed) {
|
||||
if (this._itemPromise && this._itemPromise instanceof TickingPromise) {
|
||||
this._itemPromise.tick(elapsed);
|
||||
}
|
||||
const {_inputState: inputState} = this;
|
||||
if (0 === inputState.size) {
|
||||
this.entity.currentAnimation = 'idle';
|
||||
return;
|
||||
if (!Vector.isZero(this._movementVector)) {
|
||||
this.entity.requestMovement(this._movementVector);
|
||||
}
|
||||
const movementVector = [0, 0];
|
||||
if (inputState.has('MoveToX')) {
|
||||
movementVector[0] = inputState.get('MoveToX') / 127;
|
||||
}
|
||||
if (inputState.has('MoveToY')) {
|
||||
movementVector[1] = inputState.get('MoveToY') / 127;
|
||||
}
|
||||
if (inputState.has('MoveUp')) {
|
||||
movementVector[1] -= 1;
|
||||
}
|
||||
if (inputState.has('MoveRight')) {
|
||||
movementVector[0] += 1;
|
||||
}
|
||||
if (inputState.has('MoveDown')) {
|
||||
movementVector[1] += 1;
|
||||
}
|
||||
if (inputState.has('MoveLeft')) {
|
||||
movementVector[0] -= 1;
|
||||
}
|
||||
if (0 === movementVector[0] && 0 === movementVector[1]) {
|
||||
return;
|
||||
}
|
||||
this.entity.requestMovement(movementVector);
|
||||
this.entity.currentAnimation = 'moving';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {World} from '@avocado/physics/matter/world';
|
|||
import {Ticker} from '@avocado/timing';
|
||||
import {Room} from '@avocado/topdown';
|
||||
// 1st party.
|
||||
import {actionIds} from '../common/action-ids';
|
||||
import {SelfEntityPacket} from '../common/packets/self-entity.packet';
|
||||
import {WorldTime} from '../common/world-time.synchronized';
|
||||
import {createEntityForConnection} from './create-entity-for-connection';
|
||||
|
@ -31,6 +32,7 @@ export default class Game {
|
|||
// State synchronization.
|
||||
this.informTicker = new Ticker(config.informInterval);
|
||||
this.informTicker.on('tick', this.inform, this);
|
||||
InputPacket.setActionIds(actionIds());
|
||||
// Simulation.
|
||||
this.mainLoopHandle = setInterval(
|
||||
this.createMainLoop(),
|
||||
|
@ -104,7 +106,7 @@ export default class Game {
|
|||
const {entity} = socket;
|
||||
return (packet) => {
|
||||
if (packet instanceof InputPacket) {
|
||||
entity.inputState = packet.toState();
|
||||
entity.inputStream = packet.data;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user