import { Renderer } from '../core/renderer'; import { EngineComponent } from '../types/engine-component'; import { ChangeEvent, EngineEvents, InstanceEvent, RemoveEvent, ReparentEvent, } from '../types/events'; import { EventEmitter } from '../utils/events'; import { Environment } from '../gameobjects/environment.object'; import { assetManager } from '../assets/manager'; import { WorldFile } from '../types/world-file'; import { World } from '../gameobjects/world.object'; import { instancableGameObjects } from '../gameobjects'; import { Object3D } from 'three'; import { GameObject } from '../types/game-object'; /** * Game level management component * @listens change Applies changes to objects * @listens remove Removes objects from scene * @listens reparent Reparents object */ export class LevelComponent extends EngineComponent { private world!: World; private environment!: Environment; private cleanUpEvents?: Function; constructor( protected renderer: Renderer, protected events: EventEmitter ) { super(renderer, events); } initialize(): void { this.world = this.renderer.scene.getObjectByName('World') as World; this.environment = this.renderer.scene.getObjectByName( 'Environment' ) as Environment; this.cleanUpEvents = this.bindEvents(); } update(delta: number): void {} cleanUp(): void { this.cleanUpEvents?.call(this); } public getSceneTree() { return { world: this.world, environment: this.environment, }; } public createObject(object: string, setParent?: Object3D) { const parent = setParent || this.world; const ObjectType = instancableGameObjects[object]; if (!ObjectType) return; const newObject = new ObjectType(); parent.add(newObject); this.events.emit('sceneJoin', newObject); } public serializeLevelSave(name: string): WorldFile { const world = this.world.serialize(); const environment = this.environment.serialize(); const assets = assetManager.serialize(); return { name, world, environment, assets, }; } private bindEvents() { const changeEvent = (event: ChangeEvent) => { if (event.applied || !event.object) return; const prop = (event.object as any)[event.property]; if (prop?.copy) prop.copy(event.value); else (event.object as any)[event.property] = event.value; }; const removeEvent = (event: RemoveEvent) => { if (event.applied || !event.object) return; if ((event.object as GameObject).virtual) return; if (Array.isArray(event.object)) { event.object.forEach((object) => object.removeFromParent()); return; } event.object.removeFromParent(); this.events.emit('sceneLeave', event.object); }; const reparentEvent = (event: ReparentEvent) => { if (event.applied || !event.object || !event.parent) return; if (Array.isArray(event.object)) { event.object.forEach((object) => { object.removeFromParent(); event.parent.add(object); }); return; } event.object.removeFromParent(); event.parent.add(event.object); }; const instanceEvent = (event: InstanceEvent) => this.createObject(event.type, event.parent); this.events.addListener('change', changeEvent); this.events.addListener('remove', removeEvent); this.events.addListener('reparent', reparentEvent); this.events.addListener('instance', instanceEvent); return () => { this.events.removeEventListener('change', changeEvent); this.events.removeEventListener('remove', removeEvent); this.events.removeEventListener('reparent', reparentEvent); this.events.removeEventListener('instance', instanceEvent); }; } }