@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
256 lines (188 loc) • 8 kB
JavaScript
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;