UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

256 lines (188 loc) • 8 kB
import List from '../../../src/core/collection/list/List.js'; import ObservedValue from '../../../src/core/model/ObservedValue.js'; import { isDefined } from "../../../src/core/process/matcher/isDefined.js"; import { EntityManager } from "../../../src/engine/ecs/EntityManager.js"; import { EventType } from "../../../src/engine/ecs/EventType.js"; import LabelView from '../../../src/view/common/LabelView.js'; import dom from "../../../src/view/DOM.js"; import ButtonView from '../../../src/view/elements/button/ButtonView.js'; import DropDownSelectionView from '../../../src/view/elements/DropDownSelectionView.js'; import EmptyView from "../../../src/view/elements/EmptyView.js"; import View from "../../../src/view/View.js"; import ComponentAddAction from "../../actions/concrete/ComponentAddAction.js"; import ComponentRemoveAction from "../../actions/concrete/ComponentRemoveAction.js"; import { buildObjectEditorFromRegistry } from "../../ecs/component/createObjectEditor.js"; import { ComponentControlView } from "./ComponentControlView.js"; import { LineView } from "./components/common/LineView.js"; class EntityEditor extends View { /** * * @param {ComponentControlFactory} componentControlFactory * @param {Editor} editor * @constructor */ constructor(componentControlFactory, editor) { super(); const dRoot = dom('div'); dRoot.addClass('entity-editor-view'); this.el = dRoot.el; const self = this; this.model = new ObservedValue(null); /** * @type {ObservedValue<EntityManager>} */ this.entityManager = new ObservedValue(null); this.components = new List(); const vComponentList = new EmptyView({ classList: ['component-list'] }); /** * * @type {Map<any, TypeEditor>} */ const registry = editor.type_editor_registry; /** * * @type {Map<Object, ComponentControlView>} */ this.componentControllers = new Map(); /** * * @returns {EntityComponentDataset} */ function get_dataset() { return self.entityManager.get().dataset; } function addComponent(event) { if (event.instance === undefined) { return; } self.components.add(event.instance); } function removeComponent(event) { // console.log('removeComponent.Event',event, self.components); if (event.instance === undefined) { return; } try { self.components.removeOneOf(event.instance); } catch (e) { console.error(e); } } function watchEntity(entity) { const dataset = get_dataset(); if (!dataset.entityExists(entity)) { //doesn't exist, nothing to do return; } dataset.addEntityEventListener(entity, EventType.ComponentAdded, addComponent); dataset.addEntityEventListener(entity, EventType.ComponentRemoved, removeComponent); dataset.addEntityEventListener(entity, EventType.EntityRemoved, unwatchEntity); } function unwatchEntity(entity) { const dataset = get_dataset(); if (!dataset.entityExists(entity)) { //doesn't exist, nothing to do return; } dataset.removeEntityEventListener(entity, EventType.ComponentAdded, addComponent); dataset.removeEntityEventListener(entity, EventType.ComponentRemoved, removeComponent); dataset.removeEntityEventListener(entity, EventType.EntityRemoved, unwatchEntity); } this.model.onChanged.add(function (entity, oldEntity) { if (oldEntity !== undefined && oldEntity !== null) { unwatchEntity(oldEntity); } watchEntity(entity); self.components.reset(); /** * * @type {EntityComponentDataset} */ const dataset = get_dataset(); unattachedTypes.reset(); unattachedTypes.addAll(dataset.getComponentTypeMap().map(a => a.typeName).filter(isDefined)); const components = dataset.getAllComponents(entity); components.forEach(function (c) { self.components.add(c); }); }); this.vLabelEntity = new LabelView(this.model, { classList: ['id', 'label'] }); this.addChild(vComponentList); const unattachedTypes = new List(); /** * @template T * @param {T} component */ function handleComponentAdded(component) { let view; try { view = buildObjectEditorFromRegistry(component, registry); } catch (e) { view = new LabelView(`ERROR: ${e.message}\n${e.stack}`); console.warn('Failed to build editor', e); } if (!view) { view = new LabelView('EMPTY'); } /** * * @type {EntityManager} */ const entityManager = self.entityManager.getValue(); const entityId = self.model.getValue(); const vBody = new EmptyView({ classList: ['body'] }); vBody.addChild(view); const controlView = new ComponentControlView(entityId, component, entityManager, vBody, editor.engine); const Klass = component.constructor; controlView.signal.remove.add(function () { editor.actions.mark('Remove Component'); editor.actions.do(new ComponentRemoveAction(entityId, Klass)); }); vComponentList.addChild(controlView); self.componentControllers.set(component, controlView); unattachedTypes.removeOneOf(Klass.typeName); } function handleComponentRemoved(c) { // console.log("handleComponentRemoved",c); if (self.componentControllers.has(c)) { const controlView = self.componentControllers.get(c); self.componentControllers.delete(c); vComponentList.removeChild(controlView); const Klass = c.constructor; unattachedTypes.add(Klass.typeName); } } this.components.on.added.add(handleComponentAdded); this.components.on.removed.add(handleComponentRemoved); const typeSelection = new DropDownSelectionView(unattachedTypes); const buttonView = new ButtonView({ classList: ['add-component'], name: "Add", action: function () { const selectedValue = typeSelection.getSelectedValue(); const em = self.entityManager.get(); const ComponentClass = em.getComponentClassByName(selectedValue); const component = new ComponentClass(); const entityIndex = self.model.get(); editor.actions.mark('Add Component'); editor.actions.do(new ComponentAddAction(entityIndex, component)); } }); this.addChild(new LineView({ elements: [this.vLabelEntity, buttonView, typeSelection], classList: ['component-adder'] })); this.handlers = {}; } link() { super.link() } unlink() { super.unlink(); } } export default EntityEditor;