humus-old/client/index.js

168 lines
4.3 KiB
JavaScript
Raw Normal View History

2019-03-20 15:28:18 -05:00
import {create as createClient} from '@avocado/client';
2019-03-21 18:33:20 -05:00
import {EntityList} from '@avocado/entity';
2019-03-20 15:28:18 -05:00
import {ActionRegistry} from '@avocado/input';
2019-03-30 05:08:58 -05:00
import {Stage} from '@avocado/graphics';
2019-03-28 20:36:31 -05:00
import {Vector} from '@avocado/math';
2019-03-24 19:22:47 -05:00
import {World} from '@avocado/physics/matter/world';
2019-03-20 15:28:18 -05:00
import {StateSynchronizer} from '@avocado/state';
2019-03-27 01:53:10 -05:00
import {Room, RoomView} from '@avocado/topdown';
2019-03-28 12:45:22 -05:00
import {clearAnimation, setAnimation} from '@avocado/timing';
2019-03-30 05:08:58 -05:00
// DOM.
const appNode = document.querySelector('.app');
// Graphics stage.
const stage = new Stage([640, 360]);
2019-03-27 01:53:10 -05:00
stage.scale = [2, 2];
2019-03-30 07:00:07 -05:00
let isFocused = false;
2019-03-30 05:08:58 -05:00
stage.element.addEventListener('blur', () => {
2019-03-30 07:00:07 -05:00
isFocused = false;
2019-03-30 05:08:58 -05:00
});
stage.element.addEventListener('focus', () => {
2019-03-30 07:00:07 -05:00
isFocused = true;
2019-03-30 05:08:58 -05:00
});
2019-03-30 07:00:07 -05:00
stage.addToDom(appNode);
2019-03-30 05:08:58 -05:00
// Create room.
2019-03-27 01:53:10 -05:00
const room = new Room();
room.world = new World();
2019-03-30 05:08:58 -05:00
const roomView = new RoomView(room, stage.renderer);
2019-03-27 01:53:10 -05:00
stage.addChild(roomView);
2019-03-30 05:08:58 -05:00
// Synchronize state.
2019-03-20 15:28:18 -05:00
const stateSynchronizer = new StateSynchronizer({
2019-03-27 01:53:10 -05:00
room,
2019-03-20 15:28:18 -05:00
});
2019-03-30 05:08:58 -05:00
// Handle "own" entity.
let selfEntity;
2019-03-21 18:33:20 -05:00
function hasSelfEntity() {
return selfEntity && 'string' !== typeof selfEntity;
}
2019-03-27 01:53:10 -05:00
room.on('entityAdded', (entity) => {
2019-03-27 17:43:24 -05:00
entity.removeTrait('controllable');
2019-03-21 18:33:20 -05:00
// Set self entity.
if ('string' === typeof selfEntity) {
2019-03-27 17:37:09 -05:00
if (entity === room.findEntity(selfEntity)) {
2019-03-21 18:33:20 -05:00
selfEntity = entity;
2019-03-27 17:43:24 -05:00
// Only self entity should be controllable.
entity.addTrait('controllable');
2019-03-21 18:33:20 -05:00
}
}
2019-03-20 15:28:18 -05:00
});
2019-03-21 18:33:20 -05:00
// Accept input.
2019-03-28 20:36:31 -05:00
const canvasNode = document.querySelector('.app canvas');
2019-03-20 15:28:18 -05:00
const actionRegistry = new ActionRegistry();
actionRegistry.mapKeysToActions({
'w': 'MoveUp',
'a': 'MoveLeft',
's': 'MoveDown',
'd': 'MoveRight',
});
2019-03-28 21:01:35 -05:00
actionRegistry.listen(canvasNode);
2019-03-30 05:08:58 -05:00
// Mouse/touch movement.
2019-03-28 20:36:31 -05:00
function createMoveToNormal(position) {
2019-03-30 05:08:58 -05:00
if (!selfEntity) {
return;
2019-03-28 20:36:31 -05:00
}
2019-03-30 05:08:58 -05:00
const entityPosition = selfEntity.position;
const magnitude = Vector.magnitude(position, entityPosition);
if (magnitude < 8) {
return;
}
const diff = Vector.sub(position, entityPosition);
return Vector.normalize(diff);
2019-03-28 20:36:31 -05:00
}
let pointingAt = [-1, -1];
function isPointingAtAnything() {
return -1 !== pointingAt[0] && -1 !== pointingAt[1];
}
2019-03-30 05:08:58 -05:00
stage.on('pointerDown', (event) => {
pointingAt = event.position;
2019-03-28 20:36:31 -05:00
});
2019-03-30 05:08:58 -05:00
stage.on('pointerMove', (event) => {
2019-03-28 20:36:31 -05:00
if (!isPointingAtAnything()) {
return;
}
2019-03-30 05:08:58 -05:00
pointingAt = event.position;
2019-03-28 20:36:31 -05:00
});
2019-03-30 05:08:58 -05:00
stage.on('pointerUp', (event) => {
2019-03-28 20:36:31 -05:00
pointingAt = [-1, -1];
});
let actionState = actionRegistry.state;
2019-03-21 18:33:20 -05:00
// Create the socket connection.
2019-03-20 15:28:18 -05:00
const socket = createClient(window.location.href);
2019-03-21 18:33:20 -05:00
// Input messages.
const messageHandle = setInterval(() => {
2019-03-28 20:36:31 -05:00
// Mouse/touch movement.
do {
if (isPointingAtAnything()) {
const normal = createMoveToNormal(pointingAt);
if (normal) {
actionRegistry.state = actionRegistry.state.set('MoveTo', normal);
break;
}
}
actionRegistry.state = actionRegistry.state.delete('MoveTo');
} while (false);
if (actionState !== actionRegistry.state) {
actionState = actionRegistry.state;
2019-03-20 15:28:18 -05:00
socket.send({
type: 'input',
payload: actionState.toJS()
});
}
2019-03-21 18:33:20 -05:00
}, 1000 / 60);
// Prediction.
2019-03-23 23:38:14 -05:00
let lastTime = performance.now();
2019-03-30 07:00:07 -05:00
let nightIntensity = 0;
2019-03-23 23:38:14 -05:00
const predictionHandle = setInterval(() => {
const now = performance.now();
const elapsed = (now - lastTime) / 1000;
lastTime = now;
2019-03-27 17:40:25 -05:00
if (hasSelfEntity()) {
selfEntity.inputState = actionState.toJS();
}
room.tick(elapsed);
2019-03-30 07:00:07 -05:00
stage.removeAllFilters();
const intensity = nightIntensity < 0.8 ? nightIntensity : 0.8;
if (!isFocused) {
stage.paused(intensity);
}
else {
stage.night(intensity);
}
nightIntensity += 0.0001;
2019-03-23 23:38:14 -05:00
}, 1000 / 80);
2019-03-21 18:33:20 -05:00
// State updates.
2019-03-20 15:28:18 -05:00
let dirty = false;
function onMessage({type, payload}) {
switch (type) {
case 'state-update':
2019-03-20 21:09:32 -05:00
if (payload.selfEntity) {
selfEntity = payload.selfEntity;
2019-03-20 21:09:32 -05:00
}
stateSynchronizer.acceptStateChange(payload);
2019-03-20 15:28:18 -05:00
dirty = true;
break;
default:
}
}
socket.on('message', onMessage);
// Render.
function render() {
stage.tick();
if (!dirty) {
return;
}
2019-03-30 05:08:58 -05:00
stage.render();
2019-03-20 15:28:18 -05:00
dirty = false;
}
2019-03-28 12:45:22 -05:00
const renderHandle = setAnimation(render);
2019-03-21 18:33:20 -05:00
// Hot reloading.
if (module.hot) {
module.hot.accept((error) => {
console.error(error);
});
module.hot.dispose(() => {
2019-03-28 12:45:22 -05:00
clearAnimation(renderHandle);
2019-03-28 04:19:42 -05:00
room.destroy();
2019-03-21 18:33:20 -05:00
stage.destroy();
2019-03-28 04:12:14 -05:00
});
}