/* dev:start */ import { makeDiv, removeAllChildren } from "../../../core/utils"; import { Vector } from "../../../core/vector"; import { Entity } from "../../entity"; import { BaseHUDPart } from "../base_hud_part"; import { DynamicDomAttach } from "../dynamic_dom_attach"; /** * Allows to inspect entities by pressing F8 while hovering them */ export class HUDEntityDebugger extends BaseHUDPart { createElements(parent) { this.element = makeDiv( parent, "ingame_HUD_EntityDebugger", [], ` Use F8 to toggle this overlay
` ); this.componentsElem = this.element.querySelector(".entityComponents"); } initialize() { this.root.gameState.inputReciever.keydown.add(key => { if (key.keyCode === 119) { // F8 this.pickEntity(); } }); /** * The currently selected entity * @type {Entity} */ this.selectedEntity = null; this.lastUpdate = 0; this.domAttach = new DynamicDomAttach(this.root, this.element); } pickEntity() { const mousePos = this.root.app.mousePosition; if (!mousePos) { return; } const worldPos = this.root.camera.screenToWorld(mousePos); const worldTile = worldPos.toTileSpace(); const entity = this.root.map.getTileContent(worldTile, this.root.currentLayer); this.selectedEntity = entity; if (entity) { this.rerenderFull(entity); } } /** * * @param {string} name * @param {any} val * @param {number} indent * @param {Array} recursion */ propertyToHTML(name, val, indent = 0, recursion = []) { if (indent > 20) { return; } if (val !== null && typeof val === "object") { // Array is displayed like object, with indexes recursion.push(val); // Get type class name (like Array, Object, Vector...) let typeName = `(${val.constructor ? val.constructor.name : "unknown"})`; if (Array.isArray(val)) { typeName = `(Array[${val.length}])`; } if (val instanceof Vector) { typeName = `(Vector[${val.x}, ${val.y}])`; } const colorStyle = `color: hsl(${30 * indent}, 100%, 80%)`; let html = `
${name} ${typeName}
`; for (const property in val) { let hiddenValue = null; if (val[property] == this.root) { hiddenValue = ""; } else if (val[property] instanceof Node) { hiddenValue = `<${val[property].constructor.name}>`; } else if (recursion.includes(val[property])) { // Avoid recursion by not "expanding" object more than once hiddenValue = ""; } html += this.propertyToHTML( property, hiddenValue ? hiddenValue : val[property], indent + 1, [...recursion] // still expand same value in other "branches" ); } html += "
"; return html; } const displayValue = (val + "") .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">"); return ` ${displayValue}`; } /** * Rerenders the whole container * @param {Entity} entity */ rerenderFull(entity) { removeAllChildren(this.componentsElem); let html = ""; const property = (strings, val) => ` ${val}`; html += property`registered ${!!entity.registered}`; html += property`uid ${entity.uid}`; html += property`destroyed ${!!entity.destroyed}`; for (const componentId in entity.components) { const data = entity.components[componentId]; html += "
"; html += "" + componentId + "
"; for (const property in data) { // Put entity into recursion list, so it won't get "expanded" html += this.propertyToHTML(property, data[property], 0, [entity]); } html += "
"; } this.componentsElem.innerHTML = html; } update() { this.domAttach.update(!!this.selectedEntity); } } /* dev:end */