UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

270 lines (204 loc) • 7 kB
import { assert } from "../../../core/assert.js"; import List from "../../../core/collection/list/List.js"; import { noop } from "../../../core/function/noop.js"; import { SerializationMetadata } from "../../ecs/components/SerializationMetadata.js"; import Entity from "../../ecs/Entity.js"; import { EventType } from "../../ecs/EventType.js"; import GUIElement from "../../ecs/gui/GUIElement.js"; import NotificationLog from "../../notify/NotificationLog.js"; class LogDisplay { /** * * @param {NotificationLog} log * @param {function(log:NotificationLog)} initializer * @param {function(timeDelta:number)} [updateFunction] */ constructor({ log, initializer, updateFunction = noop }) { this.needsUpdate = updateFunction !== noop; this.__update_function = updateFunction; this.log = log; this.initializer = initializer; /** * Allows modifying timing of the view emitter by slowing or speeding up the time * @type {number} */ this.timeScale = 1; } /** * * @param {number} timeDelta */ update(timeDelta) { const td = this.timeScale * timeDelta; this.__update_function(td); } initialize() { this.initializer(this.log); } } export class NotificationManager { constructor() { /** * * @type {Map<string, NotificationLog>} */ this.channels = new Map(); /** * * @type {List<LogDisplay>} */ this.displays = new List(); /** * * @type {string} * @private */ this.defaultArea = undefined; /** * * @type {EntityComponentDataset|null} */ this.ecd = null; } /** * * @param {string} channel */ createChannel(channel) { assert.isString(channel, 'channel'); if (this.channels.has(channel)) { throw new Error(`channel '${channel}' already exists`); } this.channels.set(channel, new NotificationLog()); if (this.defaultArea === undefined) { this.defaultArea = channel; } } /** * * @param {string} channel * @returns {NotificationLog|undefined} */ getChannel(channel) { return this.channels.get(channel); } /** * * @param {string} channel * @returns {LogDisplay[]} */ getDisplays(channel) { const log = this.getChannel(channel); return this.displays.filter(d => d.log === log); } /** * * @param {string} channel * @param {function(log:NotificationLog):View} initializer * @param {function(timeDelta:number)} [updateFunction] */ addDisplay(channel, initializer, updateFunction) { assert.isString(channel, 'channel'); assert.isFunction(initializer, 'initializer'); const log = this.channels.get(channel); if (log === undefined) { throw new Error(`channel '${channel}' doesn't exist`); } const display = new LogDisplay({ log, initializer, updateFunction }); this.displays.add(display); display.initialize(); } /** * * @param {string} channel * @param {ViewEmitter} viewEmitter * @param {String} [grouping] */ addEmitterDisplay(channel, viewEmitter, grouping = null) { /** * * @type {Map<View, Entity>} */ const views = new Map(); const self = this; const managedNotificationChannelClass = `managed-notification-channel-${channel}`; function handleEntityRemoved(entity) { //find existing entry for (const [view, builder] of views) { if (builder.id === entity) { //clear from the map views.delete(view); break; } } } function viewFactory(log) { viewEmitter.objectEmitter.objectFinalizer = function (view) { const entity = views.get(view); if (entity !== undefined) { entity.removeEventListener(EventType.EntityRemoved, handleEntityRemoved); entity.destroy(); //remove from the map views.delete(view); } }; log.elements.on.added.add(function (notification) { assert.notEqual(notification, null, 'notification is null'); assert.notEqual(notification, undefined, 'notification is undefined'); viewEmitter.spawn(notification); }); viewEmitter.on.spanwed.add(function (view) { view.addClass(managedNotificationChannelClass); const eb = new Entity(); //prevent serialization of the notification eb.add(SerializationMetadata.Transient); const ecd = self.ecd; if (ecd !== null) { const guiElement = GUIElement.fromView(view); guiElement.group = grouping; eb.addEventListener(EventType.EntityRemoved, handleEntityRemoved); eb.add(guiElement) .build(ecd); //console.warn('Notification View Spawned:', eb); views.set(view, eb); } }); } function updateFunction(tileDelta) { viewEmitter.tick(tileDelta); } this.addDisplay(channel, viewFactory, updateFunction); } /** * * @param {Notification} notification * @param {string} [areaId] uses default area when not specified */ addNotification(notification, areaId = this.defaultArea) { assert.notEqual(notification, undefined, 'notification is undefined'); assert.notEqual(notification, null, 'notification is null'); const channel = this.channels.get(areaId); if (channel === undefined) { throw new Error(`Area '${areaId}' doesn't exist`); } channel.addNotification(notification); } /** * * @param {number} timeDelta */ tick(timeDelta) { const displays = this.displays; const n = displays.length; for (let i = 0; i < n; i++) { const display = displays.get(i); if (display.needsUpdate) { display.update(timeDelta); } } } }