@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
240 lines (166 loc) • 5.9 kB
JavaScript
import Rectangle from "../../../core/geom/2d/Rectangle.js";
import { SurfacePoint3 } from "../../../core/geom/3d/SurfacePoint3.js";
import Vector3 from "../../../core/geom/Vector3.js";
import { ResourceAccessKind } from "../../../core/model/ResourceAccessKind.js";
import { ResourceAccessSpecification } from "../../../core/model/ResourceAccessSpecification.js";
import { VisualTip } from "../../../view/tooltip/VisualTip.js";
import Mesh from "../../graphics/ecs/mesh/Mesh.js";
import { MeshSystem } from "../../graphics/ecs/mesh/MeshSystem.js";
import { modelHitTest } from "../../graphics/util/modelHitTest.js";
import { System } from "../System.js";
import { TooltipComponent } from "./TooltipComponent.js";
const ray_source = new Vector3();
const ray_direction = new Vector3();
const hit = new SurfacePoint3();
export class TooltipComponentSystem extends System {
/**
*
* @param {GraphicsEngine} graphics
* @param {TooltipManager} tooltips
* @param {PointerDevice} pointer
* @param {Localization} localization
*/
constructor({ graphics, tooltips, pointer, localization }) {
super();
this.dependencies = [TooltipComponent];
this.components_used = [
ResourceAccessSpecification.from(Mesh, ResourceAccessKind.Read)
];
/**
*
* @type {GraphicsEngine}
*/
this.graphics = graphics;
/**
*
* @type {TooltipManager}
*/
this.tooltips = tooltips;
/**
*
* @type {PointerDevice}
*/
this.pointer = pointer;
/**
*
* @type {Localization}
*/
this.localization = localization;
/**
*
* @type {number}
*/
this.time_since_position_update = 0;
/**
* Delay before tooltip appears, in seconds
* @type {number}
*/
this.display_delay = 0.2;
/**
* Screen-space rectangle, used by the tooltip system as the anchor to point to
* @type {Rectangle}
*/
this.target = new Rectangle();
/**
*
* @type {VisualTip}
*/
this.tip = null;
/**
*
* @type {number}
* @private
*/
this.__current_tip_entity = -1;
}
__clearTip() {
if (this.tip !== null) {
this.tooltips.remove(this.tip);
this.tip = null;
}
this.__current_tip_entity = -1;
}
__updateActiveBounds() {
this.time_since_position_update = 0;
if (this.tip !== null) {
const tip_target = this.__computeTooltipTarget();
if (tip_target !== this.__current_tip_entity) {
this.__clearTip();
} else {
this.__updateTipTargetBounds();
}
}
}
__updateTipTargetBounds() {
const pointer_position = this.pointer.position;
this.target.set(pointer_position.x - 10, pointer_position.y - 10, 20, 20);
}
/**
*
* @return {boolean}
* @private
*/
__isOnRenderCanvas() {
return this.graphics.domElement === this.pointer.getTargetElement();
}
__computeTooltipTarget() {
//check what's under pointer
if (!this.__isOnRenderCanvas()) {
//only do work when tapping on game field
return -1;
}
const ecd = this.entityManager.dataset;
if (ecd === null) {
return -1;
}
const pointer_position = this.pointer.position;
this.graphics.viewportProjectionRay(pointer_position.x, pointer_position.y, ray_source, ray_direction);
// test against meshes
const meshSystem = this.entityManager.getSystem(MeshSystem);
const entities = [];
meshSystem.traverseVisible((mesh, entity) => {
if (ecd.getComponent(entity, TooltipComponent) !== undefined) {
entities.push(entity);
}
});
const hit_entity = modelHitTest(hit, entities, ecd, this.graphics, pointer_position.x, pointer_position.y);
return hit_entity;
}
async startup(entityManager) {
this.pointer.position.onChanged.add(this.__updateActiveBounds, this);
}
async shutdown(entityManager) {
this.pointer.position.onChanged.remove(this.__updateActiveBounds, this);
}
update(timeDelta) {
this.time_since_position_update += timeDelta;
if (this.time_since_position_update < this.display_delay) {
return;
}
const ecd = this.entityManager.dataset;
if (ecd === null) {
return;
}
if (this.tip !== null) {
// already presenting a tip
this.__updateActiveBounds();
return;
}
const hit_entity = this.__computeTooltipTarget();
if (hit_entity === -1) {
return;
}
const pointer_position = this.pointer.position;
this.target.set(pointer_position.x - 10, pointer_position.y - 10, 20, 20);
const key = ecd.getComponent(hit_entity, TooltipComponent).key;
const localization = this.localization;
const localized_string = localization.getString(key);
const tip = new VisualTip(this.target, () => {
const view = this.tooltips.getGML().compile(localized_string);
return view;
});
this.tip = tip;
this.tooltips.add(tip);
this.__current_tip_entity = hit_entity;
}
}