AFRAME.registerComponent("super-spawner", { schema: { template: { default: "" }, useCustomSpawnPosition: { default: false }, spawnPosition: { type: "vec3" } }, init: function() { this.entities = new Map(); }, play: function() { this.handleGrabStart = this._handleGrabStart.bind(this); this.el.addEventListener("grab-start", this.handleGrabStart); }, pause: function() { this.el.removeEventListener("grab-start", this.handleGrabStart); }, remove: function() { for (const entity of this.entities.keys()) { const data = this.entities.get(entity); entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("bodyloaded", data.bodyLoadedListener); } this.entities.clear(); }, _handleGrabStart: function(e) { const hand = e.detail.hand; const entity = document.createElement("a-entity"); entity.setAttribute("networked", "template:" + this.data.template); const componentinInitializedListener = this._handleComponentInitialzed.bind(this, entity); const bodyLoadedListener = this._handleBodyLoaded.bind(this, entity); this.entities.set(entity, { hand: hand, componentInitialized: false, bodyLoaded: false, componentinInitializedListener: componentinInitializedListener, bodyLoadedListener: bodyLoadedListener }); entity.addEventListener("componentinitialized", componentinInitializedListener); entity.addEventListener("body-loaded", bodyLoadedListener); const pos = this.data.useCustomSpawnPosition ? this.data.spawnPosition : this.el.getAttribute("position"); entity.setAttribute("position", pos); this.el.sceneEl.appendChild(entity); }, _handleComponentInitialzed: function(entity, e) { if (e.detail.name === "grabbable") { this.entities.get(entity).componentInitialized = true; this._emitEvents.call(this, entity); } }, _handleBodyLoaded: function(entity) { this.entities.get(entity).bodyLoaded = true; this._emitEvents.call(this, entity); }, _emitEvents: function(entity) { const data = this.entities.get(entity); if (data.componentInitialized && data.bodyLoaded) { data.hand.emit("action_primary_down", { targetEntity: entity }); data.hand.emit("action_grab", { targetEntity: entity }); const eventData = { bubbles: true, cancelable: true, detail: { hand: data.hand, target: entity } }; const event = new CustomEvent("grab-start", eventData); entity.dispatchEvent(event); entity.removeEventListener("componentinitialized", data.componentinInitializedListener); entity.removeEventListener("body-loaded", data.bodyLoadedListener); this.entities.delete(entity); } } });