@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
249 lines (190 loc) • 7.66 kB
JavaScript
import { hex2rgb } from "../../src/core/color/hex/hex2rgb.js";
import Vector1 from "../../src/core/geom/Vector1.js";
import Vector2 from "../../src/core/geom/Vector2.js";
import Vector4 from "../../src/core/geom/Vector4.js";
import ObservedValue from "../../src/core/model/ObservedValue.js";
import { pick } from "../../src/engine/ecs/grid/pick.js";
import { obtainTerrain } from "../../src/engine/ecs/terrain/util/obtainTerrain.js";
import TopDownCameraControllerSystem
from "../../src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js";
import GridObstacle from "../../src/engine/grid/obstacle/GridObstacle.js";
import GridPosition from "../../src/engine/grid/position/GridPosition.js";
import { decodeMouseEventButtons } from "../../src/engine/input/devices/mouse/decodeMouseEventButtons.js";
import { PointerDevice } from "../../src/engine/input/devices/PointerDevice.js";
import PaintTerrainOverlayAction from "../actions/concrete/PaintTerrainOverlayAction.js";
import WriteGridValueAction from "../actions/concrete/WriteGridValueAction.js";
import Tool from "./engine/Tool.js";
class GridPaintTool extends Tool {
constructor() {
super();
this.name = "grid_paint";
this.settings.value = new Vector1(1);
this.settings.alphaMultiplier = new Vector1(0.15);
this.settings.color = new ObservedValue("#00FF00");
}
initialize() {
super.initialize();
const engine = this.engine;
const editor = this.editor;
this.terrain = obtainTerrain(engine.entityManager.dataset, function (t, entity) {
self.terrainEntity = entity;
});
this.terrain.overlay.push();
this.paint();
editor.selection.on.added.add(this.paint, this);
editor.selection.on.removed.add(this.paint, this);
this.settings.value.onChanged.add(this.paint, this);
this.settings.alphaMultiplier.onChanged.add(this.paint, this);
this.settings.color.onChanged.add(this.paint, this);
this.cameraController = buildCameraController(engine, engine.entityManager);
this.cameraController.start();
}
buildColor() {
const color = new Vector4();
const baseColor = hex2rgb(this.settings.color.get());
color.set(baseColor.r, baseColor.g, baseColor.b, 0);
color.multiplyScalar(1 / 255);
return color;
}
paint() {
const alphaMultiplier = this.settings.alphaMultiplier.getValue();
const overlay = this.terrain.overlay;
overlay.clear();
const color = this.buildColor();
/**
*
* @param {GridPosition} gridPosition
* @param {GridObstacle} obstacle
* @param {Number} entity
*/
function visit(gridPosition, obstacle, entity) {
obstacle.traverseMask(gridPosition.x, gridPosition.y, function (x, y, value) {
color.w = value * alphaMultiplier;
overlay.clearPoint(x, y);
overlay.paintPoint(x, y, color);
});
}
//initialize overlay
this.traverse(visit);
}
traverse(callback) {
/**
* @type {EntityManager}
*/
const entityManger = this.engine.entityManager;
this.editor.selection.forEach(function (entity) {
/**
*
* @type {GridPosition}
*/
const gp = entityManger.getComponent(entity, GridPosition);
/**
*
* @type {GridObstacle}
*/
const obstacle = entityManger.getComponent(entity, GridObstacle);
if (gp === null || obstacle === null) {
//do nothing
return;
}
callback(gp, obstacle, entity);
});
}
finalize() {
this.terrain.overlay.pop();
this.editor.selection.on.added.remove(this.paint, this);
this.editor.selection.on.removed.remove(this.paint, this);
this.settings.value.onChanged.remove(this.paint, this);
this.settings.alphaMultiplier.onChanged.remove(this.paint, this);
this.settings.color.onChanged.remove(this.paint, this);
this.cameraController.stop();
super.finalize();
}
affectTile(_x, _y) {
const x = _x | 0;
const y = _y | 0;
const self = this;
const alphaMultiplier = this.settings.alphaMultiplier.getValue();
const paintValue = this.settings.value.getValue();
/**
*
* @param gp
* @param {GridObstacle} obstacle
* @param entity
*/
function visit(gp, obstacle, entity) {
const localX = x - gp.x;
const localY = y - gp.y;
if (!obstacle.isPointWithin(localX, localY)) {
//painting outside
return;
}
const actions = self.editor.actions;
const aModifyGrid = new WriteGridValueAction(entity, localX, localY, paintValue);
const color = self.buildColor();
color.w = paintValue * alphaMultiplier;
const aPaintGrid = new PaintTerrainOverlayAction(self.terrainEntity, x, y, color);
actions.do(aModifyGrid);
actions.do(aPaintGrid);
}
this.traverse(visit);
}
affectTileByMousePosition(position) {
const engine = this.engine;
const self = this;
pick(position.x, position.y, engine.graphics, this.terrain, function (gridPosition) {
self.affectTile(gridPosition.x, gridPosition.y);
});
}
start() {
this.editor.actions.mark('Paining Tiles');
this.update();
}
update() {
if (this.isRunning()) {
const p = new Vector2(0, 0);
this.engine.gameView.positionGlobalToLocal(this.engine.devices.pointer.position, p);
this.affectTileByMousePosition(p);
}
}
}
/**
*
* @param {Engine} engine
* @param {EntityManager} em
* @returns {{start: function, stop: function}}
*/
function buildCameraController(engine, em) {
const TopDownCameraController = em.getComponentClassByName("TopDownCameraController");
const system = em.getSystem(TopDownCameraControllerSystem);
system.enabled.set(true);
let cameraController = null;
em.traverseEntities([TopDownCameraController], function (c) {
cameraController = c;
});
const pointerDevice = new PointerDevice(window);
pointerDevice.on.drag.add(function (position, origin, previousPosition, mouseEvent) {
const buttons = mouseEvent.buttons;
const pressedButtons = decodeMouseEventButtons(buttons);
if (pressedButtons[2]) {
const camera = engine.graphics.camera;
TopDownCameraController.pan(previousPosition.clone().sub(position), camera, engine.graphics.domElement, cameraController.distance, camera.fov, cameraController.target);
}
});
pointerDevice.on.wheel.add(function (delta) {
cameraController.distance += delta.y / 70;
});
function start() {
system.enabled.set(true);
pointerDevice.start();
}
function stop() {
system.enabled.set(false);
pointerDevice.stop();
}
return {
start,
stop
};
}
export default GridPaintTool;