// @ts-check class MultiCanvasModel { /** * @param {string} key * @param {string} id * @param {string} slot * @returns {MultiCanvasModel} */ static create(key, id, slot) { const model = CanvasModel.create(id, slot); model.options = model.defaultOptions(); const multi = new MultiCanvasModel(slot, model); this.ensureStorage(); T.multiCombatModels[key] = multi; return multi; } static ensureStorage() { if (T.multiCombatModels != null && typeof T.multiCombatModels === "object") { return; } T.multiCombatModels = {}; } /** * @param {string} slot * @param {CanvasModel} model * @param {boolean=} isAnimated */ constructor(slot, model, isAnimated = true) { // Setup defaults this.models = { [slot]: model, }; this.layers = []; this.canvas = model.createCanvas(!isAnimated); this.animatingCanvas = null; this.width = model.width; this.height = model.height; this.animated = isAnimated; this.listener = Renderer.defaultListener; } /** * @param {string} id * @param {string} slot * @param {object=} metadata */ add(id, slot, metadata) { const model = CanvasModel.create(id, slot); model.options = model.defaultOptions(); model.metadata = metadata; this.models[slot] = model; // Maybe check these newly added models with the first model configuration? // Perhaps someone may have messed up widths and heights accidentally? return model; } /** * @param {CanvasRenderingContext2D} context */ setCanvasContext(context) { this.canvas = context; } compile() { this.layers = []; // Compile all models // At end, compile layers into central variable. const models = this.models; for (const slot in models) { if (!Object.hasOwn(models, slot)) { continue; } const model = models[slot]; const layers = model.compile(Object.assign(model.options, model.metadata)); this.layers.push(...layers); } return this.layers; } /** * @param {Renderer.RendererListener=} listener */ animate(listener) { this.animated = true; if (listener != null) this.listener = listener; this.redraw(); } /** * @param {Renderer.RendererListener=} listener */ play(listener) { this.animated = false; if (listener != null) this.listener = listener; this.redraw(); } /** * Recompiles the canvas models' layers, and schedules them on the renderer system. */ redraw() { if (!this.canvas) { Errors.report("MultiCanvasModel.redraw() called but model was never rendered!"); return; } if (this.animated) { this.animatingCanvas = Renderer.animateLayers(this.canvas, this.compile(), this.listener, true); return; } return Renderer.composeLayers(this.canvas, this.compile(), this.canvas.canvas.width / this.width, this.listener); } reset() { const models = this.models; for (const slot in models) { if (!Object.hasOwn(models, slot)) { continue; } const model = models[slot]; model.reset(); model.options = model.defaultOptions(); model.preprocess(model.options); } } refresh() { this.reset(); this.redraw(); } refreshAnimation() { if (this.animatingCanvas == null) { return; } this.animatingCanvas.invalidateCaches(); this.animatingCanvas.redraw(); } } /** @type {string} */ MultiCanvasModel.debugLastInit = "Unspecified"; // @ts-ignore window.MultiCanvasModel = MultiCanvasModel; Macro.add("setup-multi-canvas", { handler() { const key = this.args[0]; const id = this.args[1]; const slot = this.args[2]; MultiCanvasModel.ensureStorage(); if (key in T.multiCombatModels) { Errors.report("MultiCanvasModel already exists: Likely due to two animateCombat uses in the same passage", { key, stack: Utils.GetStack(), last: MultiCanvasModel.debugLastInit, }); } MultiCanvasModel.debugLastInit = Utils.GetStack(); const model = MultiCanvasModel.create(key, id, slot); this.output.append(model.canvas.canvas); }, }); Macro.add("add-multi-canvas", { handler() { const key = this.args[0]; const id = this.args[1]; const slot = this.args[2]; const metadata = this.args[3]; MultiCanvasModel.ensureStorage(); const model = T.multiCombatModels[key]; if (model == null) { Errors.report("No MultiCanvasModel found with given key.", { key, }); return; } model.add(id, slot, metadata); }, }); Macro.add("start-multi-canvas-rendering", { handler() { const key = this.args[0]; MultiCanvasModel.ensureStorage(); const model = T.multiCombatModels[key]; if (model == null) { Errors.report("No MultiCanvasModel found with given key.", { key, }); return; } if (!V.options.combatAnimations) { model.play(); return; } model.animate(); }, }); Macro.add("reset-multi-canvas", { handler() { const key = this.args[0]; MultiCanvasModel.ensureStorage(); const model = T.multiCombatModels[key]; if (model == null) { Errors.report("No MultiCanvasModel found with given key.", { key, }); return; } model.reset(); }, }); Macro.add("refresh-multi-canvas", { handler() { const key = this.args[0]; MultiCanvasModel.ensureStorage(); const model = T.multiCombatModels[key]; if (model == null) { Errors.report("No MultiCanvasModel found with given key.", { key, }); return; } model.refresh(); }, });