UNPKG

@drincs/pixi-vn

Version:

Pixi'VN is a npm package that provides various features for creating visual novels.

1,512 lines (1,485 loc) 100 kB
import { Sprite, Container, Application, Text, Assets, Texture } from 'pixi.js'; import { diff } from 'deep-diff'; import sha1 from 'crypto-js/sha1'; import { initDevtools } from '@pixi/devtools'; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/functions/CanvasUtility.ts function getTextureMemory(texture) { let sourceTexture = texture.source; let textureMemory = { image: sourceTexture.label }; return textureMemory; } function exportCanvasElement(element) { return element.memory; } function importCanvasElement(memory) { let element = getCanvasElementInstanceById(memory.pixivnId); if (element) { element.memory = memory; } else { throw new Error("[Pixi'VN] The element " + memory.pixivnId + " could not be created"); } return element; } // src/classes/canvas/CanvasContainer.ts var CANVAS_CONTAINER_ID = "CanvasContainer"; var CanvasContainer = class extends Container { constructor(options) { super(options); this.pixivnId = CANVAS_CONTAINER_ID; this.pixivnId = this.constructor.prototype.pixivnId || CANVAS_CONTAINER_ID; } get memory() { let memory = getMemoryContainer(this); this.children.forEach((child) => { memory.elements.push(exportCanvasElement(child)); }); return memory; } set memory(value) { setMemoryContainer(this, value); value.elements.forEach((child) => { this.addChild(importCanvasElement(child)); }); } }; function getMemoryContainer(element) { let className = "CanvasContainer"; if (element.hasOwnProperty("pixivnId")) { className = element.pixivnId; } return { pixivnId: className, elements: [], width: element.width, height: element.height, isRenderGroup: element.isRenderGroup, blendMode: element.blendMode, tint: element.tint, alpha: element.alpha, angle: element.angle, renderable: element.renderable, rotation: element.rotation, scale: { x: element.scale.x, y: element.scale.y }, pivot: { x: element.pivot.x, y: element.pivot.y }, position: { x: element.position.x, y: element.position.y }, skew: { x: element.skew.x, y: element.skew.y }, visible: element.visible, x: element.x, y: element.y, boundsArea: element.boundsArea, cursor: element.cursor, eventMode: element.eventMode, interactive: element.interactive, interactiveChildren: element.interactiveChildren, hitArea: element.hitArea }; } function setMemoryContainer(element, memory) { memory.isRenderGroup && (element.isRenderGroup = memory.isRenderGroup); memory.blendMode && (element.blendMode = memory.blendMode); memory.tint && (element.tint = memory.tint); memory.alpha && (element.alpha = memory.alpha); memory.angle && (element.angle = memory.angle); memory.renderable && (element.renderable = memory.renderable); memory.rotation && (element.rotation = memory.rotation); if (memory.scale) { if (typeof memory.scale === "number") { element.scale.set(memory.scale, memory.scale); } else { element.scale.set(memory.scale.x, memory.scale.y); } } if (memory.pivot) { if (typeof memory.pivot === "number") { element.pivot.set(memory.pivot, memory.pivot); } else { element.pivot.set(memory.pivot.x, memory.pivot.y); } } memory.position && element.position.set(memory.position.x, memory.position.y); memory.skew && element.skew.set(memory.skew.x, memory.skew.y); memory.visible && (element.visible = memory.visible); memory.x && (element.x = memory.x); memory.y && (element.y = memory.y); memory.boundsArea && (element.boundsArea = memory.boundsArea); memory.cursor && (element.cursor = memory.cursor); memory.eventMode && (element.eventMode = memory.eventMode); memory.interactive && (element.interactive = memory.interactive); memory.interactiveChildren && (element.interactiveChildren = memory.interactiveChildren); memory.hitArea && (element.hitArea = memory.hitArea); memory.width && (element.width = memory.width); memory.height && (element.height = memory.height); } function getTexture(imageUrl) { return __async(this, null, function* () { if (Assets.cache.has(imageUrl)) { return Assets.get(imageUrl); } return Assets.load(imageUrl).then((texture) => { if (!texture) { console.error("[Pixi'VN] Texture not found", imageUrl); return; } if (!(texture instanceof Texture)) { console.error("[Pixi'VN] File not is a image", imageUrl); return; } return texture; }).catch((e) => { console.error("[Pixi'VN] Error loading image", e); return; }); }); } function getFillGradientFillPattern(prop, propName) { if (!(prop instanceof Object)) { return prop; } console.warn(`[Pixi'VN] CanvasText.style.${propName} is a FillGradient or FillPattern, this is not supported yet.`, prop); return void 0; } function getTextStyle(style) { return { align: style.align, breakWords: style.breakWords, dropShadow: style.dropShadow, fill: getFillGradientFillPattern(style.stroke, "fill"), fontFamily: style.fontFamily, fontSize: style.fontSize, fontStyle: style.fontStyle, fontVariant: style.fontVariant, fontWeight: style.fontWeight, leading: style.leading, letterSpacing: style.letterSpacing, lineHeight: style.lineHeight, padding: style.padding, stroke: getFillGradientFillPattern(style.stroke, "stroke"), textBaseline: style.textBaseline, trim: style.trim, whiteSpace: style.whiteSpace, wordWrap: style.wordWrap, wordWrapWidth: style.wordWrapWidth }; } // src/decorators/EventDecorator.ts var registeredEvents = {}; function getEventTypeById(eventId) { try { let eventType = registeredEvents[eventId]; if (!eventType) { console.error(`[Pixi'VN] Event ${eventId} not found`); return; } new eventType(); return eventType; } catch (e) { console.error(`[Pixi'VN] Error while getting Event ${eventId}`, e); return; } } function getEventInstanceById(eventId) { try { let eventType = registeredEvents[eventId]; if (!eventType) { console.error(`[Pixi'VN] Event ${eventId} not found`); return; } let event = new eventType(); return event; } catch (e) { console.error(`[Pixi'VN] Error while getting Event ${eventId}`, e); return; } } // src/classes/canvas/CanvasSprite.ts var CANVAS_SPRITE_ID = "CanvasSprite"; var CanvasSprite = class _CanvasSprite extends Sprite { constructor(options) { super(options); this.pixivnId = CANVAS_SPRITE_ID; this._onEvents = {}; this.pixivnId = this.constructor.prototype.pixivnId || CANVAS_SPRITE_ID; } get memory() { return getMemorySprite(this); } set memory(value) { setMemorySprite(this, value); } get onEvents() { return this._onEvents; } /** * is same function as on(), but it keeps in memory the children. * @param event The event type, e.g., 'click', 'mousedown', 'mouseup', 'pointerdown', etc. * @param eventClass The class that extends CanvasEvent. * @returns * @example * ```typescript * \@eventDecorator() * export class EventTest extends CanvasEvent<CanvasSprite> { * override fn(event: CanvasEventNamesType, sprite: CanvasSprite): void { * if (event === 'pointerdown') { * sprite.scale.x *= 1.25; * sprite.scale.y *= 1.25; * } * } * } * ``` * * ```typescript * let sprite = addImage("alien", 'https://pixijs.com/assets/eggHead.png') * await sprite.load() * * sprite.eventMode = 'static'; * sprite.cursor = 'pointer'; * sprite.onEvent('pointerdown', EventTest); * * GameWindowManager.addCanvasElement("bunny", sprite); * ``` */ onEvent(event, eventClass) { let id = eventClass.prototype.id; let instance = getEventInstanceById(id); this._onEvents[event] = id; if (instance) { super.on(event, () => { instance.fn(event, this); }); } return this; } /** * on() does not keep in memory the event class, use onEvent() instead * @deprecated * @private * @param event * @param fn * @param context */ on(event, fn, context) { return super.on(event, fn, context); } static from(source, skipCache) { let sprite = Sprite.from(source, skipCache); let mySprite = new _CanvasSprite(); mySprite.texture = sprite.texture; return mySprite; } }; function getMemorySprite(element) { let temp = getMemoryContainer(element); return __spreadProps(__spreadValues({}, temp), { pixivnId: element.pixivnId, textureImage: getTextureMemory(element.texture), anchor: { x: element.anchor.x, y: element.anchor.y }, roundPixels: element.roundPixels, onEvents: element.onEvents }); } function setMemorySprite(element, memory) { setMemoryContainer(element, memory); getTexture(memory.textureImage.image).then((texture) => { if (texture) { element.texture = texture; } }); if (memory.anchor) { if (typeof memory.anchor === "number") { element.anchor.set(memory.anchor, memory.anchor); } else { element.anchor.set(memory.anchor.x, memory.anchor.y); } } memory.roundPixels && (element.roundPixels = memory.roundPixels); for (let event in memory.onEvents) { let id = memory.onEvents[event]; let instance = getEventTypeById(id); if (instance) { element.onEvent(event, instance); } } } // src/classes/canvas/CanvasImage.ts var CANVAS_IMAGE_ID = "CanvasImage"; var CanvasImage = class _CanvasImage extends CanvasSprite { constructor(options, imageLink) { super(options); this.pixivnId = CANVAS_IMAGE_ID; this.imageLink = ""; if (imageLink) { this.imageLink = imageLink; } } get memory() { return __spreadProps(__spreadValues({}, getMemorySprite(this)), { pixivnId: this.pixivnId, imageLink: this.imageLink }); } set memory(memory) { setMemorySprite(this, memory); this.imageLink = memory.imageLink; } static from(source, skipCache) { let sprite = Sprite.from(source, skipCache); let mySprite = new _CanvasImage(); mySprite.texture = sprite.texture; return mySprite; } /** * Load the image from the link and set the texture of the sprite. * @param image The link of the image. If it is not set, it will use the imageLink property. * @returns A promise that resolves when the image is loaded. */ load(image) { return __async(this, null, function* () { if (!image) { image = this.imageLink; } return getTexture(this.imageLink).then((texture) => { if (texture) { this.texture = texture; } }).catch((e) => { console.error("[Pixi'VN] Error into CanvasImage.load()", e); }); }); } }; var CANVAS_TEXT_ID = "CanvasText"; var CanvasText = class extends Text { constructor(options) { super(options); this.pixivnId = CANVAS_TEXT_ID; this._onEvents = {}; this.pixivnId = this.constructor.prototype.pixivnId || CANVAS_TEXT_ID; } get memory() { return getMemoryText(this); } set memory(value) { setMemoryText(this, value); } get onEvents() { return this._onEvents; } /** * is same function as on(), but it keeps in memory the children. * @param event The event type, e.g., 'click', 'mousedown', 'mouseup', 'pointerdown', etc. * @param eventClass The class that extends CanvasEvent. * @returns * @example * ```typescript * \@eventDecorator() * export class EventTest extends CanvasEvent<CanvasText> { * override fn(event: CanvasEventNamesType, text: CanvasText): void { * if (event === 'pointerdown') { * text.scale.x *= 1.25; * text.scale.y *= 1.25; * } * } * } * ``` * * ```typescript * const text = new CanvasText(); * text.text = "Hello World" * * text.eventMode = 'static'; * text.cursor = 'pointer'; * text.onEvent('pointerdown', EventTest); * * GameWindowManager.addCanvasElement("text", text); * ``` */ onEvent(event, eventClass) { let id = eventClass.prototype.id; let instance = getEventInstanceById(id); this._onEvents[event] = id; if (instance) { super.on(event, () => { instance.fn(event, this); }); } return this; } /** * on() does not keep in memory the event class, use onEvent() instead * @deprecated * @private * @param event * @param fn * @param context */ on(event, fn, context) { return super.on(event, fn, context); } }; function getMemoryText(element) { let temp = getMemoryContainer(element); return __spreadProps(__spreadValues({}, temp), { pixivnId: element.pixivnId, anchor: { x: element.anchor.x, y: element.anchor.y }, text: element.text, resolution: element.resolution, style: getTextStyle(element.style), roundPixels: element.roundPixels, onEvents: element.onEvents }); } function setMemoryText(element, memory) { setMemoryContainer(element, memory); if (memory.anchor) { if (typeof memory.anchor === "number") { element.anchor.set(memory.anchor, memory.anchor); } else { element.anchor.set(memory.anchor.x, memory.anchor.y); } } memory.text && (element.text = memory.text); memory.resolution && (element.resolution = memory.resolution); memory.style && (element.style = memory.style); memory.roundPixels && (element.roundPixels = memory.roundPixels); for (let event in memory.onEvents) { let id = memory.onEvents[event]; let instance = getEventTypeById(id); if (instance) { element.onEvent(event, instance); } } } // src/decorators/CanvasElementDecorator.ts var registeredCanvasElement = {}; function getCanvasElementInstanceById(canvasId) { try { let eventType = registeredCanvasElement[canvasId]; if (!eventType) { if (canvasId === CANVAS_CONTAINER_ID) { eventType = CanvasContainer; } else if (canvasId === CANVAS_IMAGE_ID) { eventType = CanvasImage; } else if (canvasId === CANVAS_SPRITE_ID) { eventType = CanvasSprite; } else if (canvasId === CANVAS_TEXT_ID) { eventType = CanvasText; } } if (!eventType) { console.error(`[Pixi'VN] CanvasElement ${canvasId} not found`); return; } let canvasElement = new eventType(); return canvasElement; } catch (e) { console.error(`[Pixi'VN] Error while getting CanvasElement ${canvasId}`, e); return; } } // src/decorators/CharacterDecorator.ts var registeredCharacters = {}; function saveCharacter(character) { if (Array.isArray(character)) { character.forEach((c) => saveCharacter(c)); return; } if (registeredCharacters[character.id]) { console.info(`[Pixi'VN] Character id ${character.id} already exists, it will be overwritten`); } registeredCharacters[character.id] = character; } function getStepSha1(step) { let sha1String = sha1(step.toString().toLocaleLowerCase()); return sha1String.toString(); } function checkIfStepsIsEqual(step1, step2) { return step1 === step2; } // src/classes/LabelAbstract.ts var LabelAbstract = class { /** * @param id is the id of the label * @param props is the properties of the label */ constructor(id, props) { this._id = id; this._onStepStart = props == null ? void 0 : props.onStepStart; this._onLoadStep = props == null ? void 0 : props.onLoadStep; this._onStepEnd = props == null ? void 0 : props.onStepEnd; this._choiseIndex = props == null ? void 0 : props.choiseIndex; } /** * Get the id of the label. This variable is used in the system to get the label by id, {@link getLabelById} */ get id() { return this._id; } /** * Get the corresponding steps number * @param externalSteps * @returns Numer of corresponding steps, for example, if externalSteps is [ABC, DEF, GHI] and the steps of the label is [ABC, GHT], the result will be 1 */ getCorrespondingStepsNumber(externalSteps) { if (externalSteps.length === 0) { return 0; } let res = 0; externalSteps.forEach((step, index) => { if (checkIfStepsIsEqual(step, this.steps[index])) { res = index; } }); return res; } /** * Is a function that will be executed in {@link Label#onStepStart} and when the user goes back to it or when the user laods a save file. * @returns Promise<void> or void */ get onStepStart() { return (stepIndex, label) => __async(this, null, function* () { if (this._onLoadStep) { yield this._onLoadStep(stepIndex, label); } if (this._onStepStart) { return yield this._onStepStart(stepIndex, label); } }); } /** * Get the function that will be executed a old step is reloaded. A step is reloaded when the user goes back to it or when the user laods a save file. * @returns Promise<void> or void */ get onLoadStep() { return this._onLoadStep; } /** * Is a function that will be executed when the step ends. * @returns Promise<void> or void */ get onStepEnd() { return this._onStepEnd; } get choiseIndex() { return this._choiseIndex; } }; // src/classes/Label.ts var Label = class extends LabelAbstract { /** * @param id is the id of the label * @param steps is the list of steps that the label will perform * @param props is the properties of the label */ constructor(id, steps, props) { super(id, props); this._steps = steps; } /** * Get the steps of the label. */ get steps() { if (typeof this._steps === "function") { return this._steps(); } return this._steps; } }; // src/classes/CloseLabel.ts var CLOSE_LABEL_ID = "__close-label-id__"; function newCloseLabel(choiseIndex) { return new Label(CLOSE_LABEL_ID, [], { choiseIndex }); } // src/types/CloseType.ts var Close = "close"; // src/classes/ChoiceMenuOption.ts var ChoiceMenuOption = class { /** * @param text Text to be displayed in the menu * @param label Label to be opened when the option is selected or the id of the label * @param props Properties to be passed to the label and olther parameters that you can use when get all the choice menu options. It be converted to a JSON string, so it cannot contain functions or classes. * @param type Type of the label to be opened. @default "call" */ constructor(text, label, props, type = "call") { /** * Properties to be passed to the label and olther parameters that you can use when get all the choice menu options. * @example * ```tsx * setChoiceMenuOptions([ * new ChoiceMenuOption("Hello", helloLabel, { disabled: true }), * ]) * return <List> * {getChoiceMenuOptions()?.map((item, index) => { * return ( * <ChoiceButton * disabled={item.props.disabled} * onClick={() => { * afterSelectChoice(item) * }} * > * {item.text} * </ChoiceButton> * ) * })} * </List> * ``` */ this.props = {}; this.text = text; this._label = label; this.type = type; if (props) { this.props = props; } } /** * Label to be opened when the option is selected */ get label() { let label = this._label; if (typeof label === "string") { let res = getLabelById(label); if (res) { label = res; } else { console.error(`Label ${label} not found, so it will be closed`); label = newCloseLabel(); } } return label; } }; var ChoiceMenuOptionClose = class { /** * @param text Text to be displayed in the menu * @param closeCurrentLabel If true, the current label will be closed. @default false */ constructor(text, closeCurrentLabel = false) { /** * Label to be opened when the option is selected */ this.label = newCloseLabel(); /** * Type of the label to be opened */ this.type = Close; /** * Properties to be passed to the label and olther parameters that you can use when get all the choice menu options. * @example * ```tsx * setChoiceMenuOptions([ * new ChoiceMenuOption("Hello", helloLabel, { disabled: true }), * ]) * return <List> * {getChoiceMenuOptions()?.map((item, index) => { * return ( * <ChoiceButton * disabled={item.props.disabled} * onClick={() => { * afterSelectChoice(item) * }} * > * {item.text} * </ChoiceButton> * ) * })} * </List> * ``` */ this.props = {}; this.text = text; this.closeCurrentLabel = closeCurrentLabel; } }; // src/functions/FlagsUtility.ts function setFlag(name, value) { let flags = GameStorageManager.getVariable(GameStorageManager.keysSystem.FLAGS_CATEGORY_KEY) || []; { let index = flags.indexOf(name); if (index > -1) { flags.splice(index, 1); } } GameStorageManager.setVariable(GameStorageManager.keysSystem.FLAGS_CATEGORY_KEY, flags); } function getFlag(name) { let flags = GameStorageManager.getVariable(GameStorageManager.keysSystem.FLAGS_CATEGORY_KEY) || []; return flags.includes(name); } // src/functions/DialogueUtility.ts function setDialogue(props) { let text = ""; let character = void 0; let dialogue; if (typeof props === "string") { text = props; dialogue = new DialogueBaseModel(text, character); } else if (Array.isArray(props)) { text = props.join(); dialogue = new DialogueBaseModel(text, character); } else if (!(props instanceof DialogueBaseModel)) { if (Array.isArray(props.text)) { text = props.text.join(); } else { text = props.text; } if (props.character) { if (typeof props.character === "string") { character = props.character; } else { character = props.character.id; } } dialogue = new DialogueBaseModel(text, character); } else { dialogue = props; } if (getFlag(GameStorageManager.keysSystem.ADD_NEXT_DIALOG_TEXT_INTO_THE_CURRENT_DIALOG_FLAG_KEY)) { let glueDialogue = getDialogue(); if (glueDialogue) { dialogue.text = `${glueDialogue.text}${dialogue.text}`; } setFlag(GameStorageManager.keysSystem.ADD_NEXT_DIALOG_TEXT_INTO_THE_CURRENT_DIALOG_FLAG_KEY); } GameStorageManager.setVariable(GameStorageManager.keysSystem.CURRENT_DIALOGUE_MEMORY_KEY, dialogue); GameStorageManager.setVariable(GameStorageManager.keysSystem.LAST_DIALOGUE_ADDED_IN_STEP_MEMORY_KEY, GameStepManager.lastStepIndex); } function getDialogue() { return GameStorageManager.getVariable(GameStorageManager.keysSystem.CURRENT_DIALOGUE_MEMORY_KEY); } function getChoiceMenuOptions() { let d = GameStorageManager.getVariable(GameStorageManager.keysSystem.CURRENT_MENU_OPTIONS_MEMORY_KEY); if (d) { let options = []; d.forEach((option, index) => { if (option.type === Close) { let itemLabel = newCloseLabel(index); let choice = new ChoiceMenuOptionClose(option.text, option.closeCurrentLabel); choice.label = itemLabel; options.push(choice); return; } let label = getLabelById(option.label); if (label) { let itemLabel = new Label(label.id, label.steps, { onStepStart: label.onStepStart, choiseIndex: index }); options.push(new ChoiceMenuOption(option.text, itemLabel, option.props, option.type)); } }); return options; } return void 0; } // src/classes/ticker/TickerBase.ts var TickerBase = class { /** * @param args The arguments that you want to pass to the ticker. * @param duration The duration of the ticker in seconds. If is undefined, the step will end only when the animation is finished (if the animation doesn't have a goal to reach then it won't finish). @default undefined * @param priority The priority of the ticker. @default UPDATE_PRIORITY.NORMAL */ constructor(args, duration, priority) { /** * Get the id of the ticker. This variable is used in the system to get the ticker by id, {@link geTickerInstanceById} */ this.id = "ticker_id_not_set"; this.args = args; this.duration = duration; this.priority = priority; this.id = this.constructor.prototype.id; } /** * The method that will be called every frame. * This method should be overridden and you can use GameWindowManager.addCanvasElement() to get the canvas element of the canvas, and edit them. * @param _ticker The ticker that is calling this method * @param _args The arguments that you passed when you added the ticker * @param _tags The tags of the canvas elements that are connected to this ticker * @param _tickerId The id of the ticker. You can use this to get the ticker from the {@link GameWindowManager.currentTickers} */ fn(_ticker, _args, _tags, _tickerId) { throw new Error("[Pixi'VN] The method TickerBase.fn() must be overridden"); } }; // src/classes/ticker/FadeAlphaTicker.ts var FadeAlphaTicker = class extends TickerBase { fn(ticker, args, tags, tickerId) { let type = args.type === void 0 ? "hide" : args.type; let duration = args.duration === void 0 ? 1 : args.duration; let speed = 1 / (duration * 60); let limit = args.limit === void 0 ? type === "hide" ? 0 : 1 : args.limit; let tagToRemoveAfter2 = args.tagToRemoveAfter || []; if (typeof tagToRemoveAfter2 === "string") { tagToRemoveAfter2 = [tagToRemoveAfter2]; } if (type === "hide" && limit < 0) { limit = 0; } if (type === "show" && limit > 1) { limit = 1; } tags.filter((tag) => { var _a; let element = GameWindowManager.getCanvasElement(tag); if (args.startOnlyIfHaveTexture) { if (element && element instanceof Sprite && ((_a = element.texture) == null ? void 0 : _a.label) == "EMPTY") { return false; } } return true; }).forEach((tag) => { let element = GameWindowManager.getCanvasElement(tag); if (element && element instanceof Container) { if (type === "show" && element.alpha < limit) { element.alpha += speed * ticker.deltaTime; } else if (type === "hide" && element.alpha > limit) { element.alpha -= speed * ticker.deltaTime; } if (type === "show" && element.alpha >= limit) { element.alpha = limit; GameWindowManager.onEndOfTicker(tag, this, tagToRemoveAfter2, tickerId); } else if (type === "hide" && element.alpha <= limit) { element.alpha = limit; GameWindowManager.onEndOfTicker(tag, this, tagToRemoveAfter2, tickerId); } } }); } }; FadeAlphaTicker = __decorateClass([ tickerDecorator() ], FadeAlphaTicker); // src/functions/TickerUtility.ts function updateTickerProgression(args, propertyName, progression, valueConvert) { let limit = valueConvert && progression.limit ? valueConvert(progression.limit) : progression.limit; if (args[propertyName] === void 0 || !progression || args[propertyName] === limit) { return; } if (typeof args[propertyName] === "number") { if (progression.type === "linear") { args[propertyName] = getLinearProgression(args[propertyName], progression); } else if (progression.type === "exponential") { args[propertyName] = getExponentialProgression(args[propertyName], progression); } } else if (args[propertyName] !== void 0 && typeof args[propertyName] === "object" && args[propertyName].haveOwnProperty("x") && args[propertyName].haveOwnProperty("y") && typeof args[propertyName].x === "number" && typeof args[propertyName].y === "number") { if (progression.type === "linear") { args[propertyName].x = getLinearProgression(args[propertyName].x, progression); args[propertyName].y = getLinearProgression(args[propertyName].y, progression); } else if (progression.type === "exponential") { args[propertyName].x = getExponentialProgression(args[propertyName].x, progression); args[propertyName].y = getExponentialProgression(args[propertyName].y, progression); } } } function getLinearProgression(number, progression, valueConvert) { let limit = valueConvert && progression.limit ? valueConvert(progression.limit) : progression.limit; let amt = valueConvert ? valueConvert(progression.amt) : progression.amt; if (limit !== void 0) { if (number > limit && amt > 0) { return limit; } else if (number < limit && amt < 0) { return limit; } } return number + amt; } function getExponentialProgression(number, progression, valueConvert) { let limit = valueConvert && progression.limit ? valueConvert(progression.limit) : progression.limit; if (limit !== void 0) { if (number > limit && progression.percentage > 0) { return limit; } else if (number < limit && progression.percentage < 0) { return limit; } } return number + number * progression.percentage; } // src/classes/ticker/MoveTicker.ts var MoveTicker = class extends TickerBase { fn(ticker, args, tags, tickerId) { let xSpeed = 1; let ySpeed = 1; if (args.speed) { if (typeof args.speed === "number") { xSpeed = this.speedConvert(args.speed); ySpeed = this.speedConvert(args.speed); } else { xSpeed = this.speedConvert(args.speed.x); ySpeed = this.speedConvert(args.speed.y); } } let destination = args.destination; let tagToRemoveAfter2 = args.tagToRemoveAfter || []; if (typeof tagToRemoveAfter2 === "string") { tagToRemoveAfter2 = [tagToRemoveAfter2]; } tags.filter((tag) => { var _a; let element = GameWindowManager.getCanvasElement(tag); if (args.startOnlyIfHaveTexture) { if (element && element instanceof Sprite && ((_a = element.texture) == null ? void 0 : _a.label) == "EMPTY") { return false; } } return true; }).forEach((tag) => { let element = GameWindowManager.getCanvasElement(tag); if (element && element instanceof Container) { let xDistance = destination.x - element.x > 0 ? 1 : -1; if (xDistance != 0) { element.x += xDistance * xSpeed * ticker.deltaTime; let newDistance = destination.x - element.x; if (xDistance < 0 && newDistance > 0 || xDistance > 0 && newDistance < 0) { element.x = destination.x; } } let yDistance = destination.y - element.y > 0 ? 1 : -1; if (yDistance != 0) { element.y += yDistance * ySpeed * ticker.deltaTime; let newDistance = destination.y - element.y; if (yDistance < 0 && newDistance > 0 || yDistance > 0 && newDistance < 0) { element.y = destination.y; } } if (element.x == destination.x && element.y == destination.y) { GameWindowManager.onEndOfTicker(tag, this, tagToRemoveAfter2, tickerId); } } }); if (args.speedProgression) updateTickerProgression(args, "speed", args.speedProgression, this.speedConvert); } speedConvert(speed) { return speed / 6; } }; MoveTicker = __decorateClass([ tickerDecorator() ], MoveTicker); var RotateTicker = class extends TickerBase { fn(ticker, args, tags, tickerId) { let speed = this.speedConvert(args.speed === void 0 ? 1 : args.speed); let clockwise = args.clockwise === void 0 ? true : args.clockwise; let tagToRemoveAfter2 = args.tagToRemoveAfter || []; if (typeof tagToRemoveAfter2 === "string") { tagToRemoveAfter2 = [tagToRemoveAfter2]; } tags.filter((tag) => { var _a; let element = GameWindowManager.getCanvasElement(tag); if (args.startOnlyIfHaveTexture) { if (element && element instanceof Sprite && ((_a = element.texture) == null ? void 0 : _a.label) == "EMPTY") { return false; } } return true; }).forEach((tag) => { let element = GameWindowManager.getCanvasElement(tag); if (element && element instanceof Container) { if (clockwise) element.rotation += speed * ticker.deltaTime; else element.rotation -= speed * ticker.deltaTime; if (speed < 1e-5 && !(args.speedProgression && args.speedProgression.type == "linear" && args.speedProgression.amt != 0)) { GameWindowManager.onEndOfTicker(tag, this, tagToRemoveAfter2, tickerId); } } }); if (args.speedProgression) updateTickerProgression(args, "speed", args.speedProgression, this.speedConvert); } speedConvert(speed) { return speed / 60; } }; RotateTicker = __decorateClass([ tickerDecorator() ], RotateTicker); var ZoomTicker = class extends TickerBase { fn(ticker, args, tags, tickerId) { let xSpeed = 0.1; let ySpeed = 0.1; if (args.speed) { if (typeof args.speed === "number") { xSpeed = this.speedConvert(args.speed); ySpeed = this.speedConvert(args.speed); } else { xSpeed = this.speedConvert(args.speed.x); ySpeed = this.speedConvert(args.speed.y); } } let tagToRemoveAfter2 = args.tagToRemoveAfter || []; if (typeof tagToRemoveAfter2 === "string") { tagToRemoveAfter2 = [tagToRemoveAfter2]; } let type = args.type || "zoom"; let xLimit = type === "zoom" ? Infinity : 0; let yLimit = type === "zoom" ? Infinity : 0; if (args.limit) { if (typeof args.limit === "number") { xLimit = args.limit; yLimit = args.limit; } else { xLimit = args.limit.x; yLimit = args.limit.y; } } tags.filter((tag) => { var _a; let element = GameWindowManager.getCanvasElement(tag); if (args.startOnlyIfHaveTexture) { if (element && element instanceof Sprite && ((_a = element.texture) == null ? void 0 : _a.label) == "EMPTY") { return false; } } return true; }).forEach((tag) => { let element = GameWindowManager.getCanvasElement(tag); if (element && element instanceof Container) { if (type === "zoom" && (element.scale.x < xLimit || element.scale.y < yLimit)) { element.scale.x += xSpeed * ticker.deltaTime; element.scale.y += ySpeed * ticker.deltaTime; } else if (type === "unzoom" && (element.scale.x > xLimit || element.scale.y > yLimit)) { element.scale.x -= xSpeed * ticker.deltaTime; element.scale.y -= ySpeed * ticker.deltaTime; } if (type === "zoom") { if (element.scale.x > xLimit) { element.scale.x = xLimit; } if (element.scale.y > yLimit) { element.scale.y = yLimit; } if (element.scale.x >= xLimit && element.scale.y >= yLimit) { element.scale.x = xLimit; element.scale.y = yLimit; this.onEndOfTicker(tag, tickerId, element, tagToRemoveAfter2); } } else if (type === "unzoom") { if (element.scale.x < xLimit) { element.scale.x = xLimit; } if (element.scale.y < yLimit) { element.scale.y = yLimit; } if (element.scale.x <= xLimit && element.scale.y <= yLimit) { element.scale.x = xLimit; element.scale.y = yLimit; this.onEndOfTicker(tag, tickerId, element, tagToRemoveAfter2); } } if (xSpeed < 1e-5 && ySpeed < 1e-5 && !(args.speedProgression && args.speedProgression.type == "linear" && args.speedProgression.amt != 0)) { this.onEndOfTicker(tag, tickerId, element, tagToRemoveAfter2); } } }); if (args.speedProgression) updateTickerProgression(args, "speed", args.speedProgression, this.speedConvert); } speedConvert(speed) { return speed / 60; } onEndOfTicker(tag, tickerId, _element, tagToRemoveAfter2) { GameWindowManager.onEndOfTicker(tag, this, tagToRemoveAfter2, tickerId); } }; ZoomTicker = __decorateClass([ tickerDecorator() ], ZoomTicker); // src/constants.ts var Repeat = "repeat"; // src/functions/ExportUtility.ts function createExportableElement(element) { try { let elementString = JSON.stringify(element); return JSON.parse(elementString); } catch (e) { console.error("[Pixi'VN] Error creating exportable element", e); throw new Error("[Pixi'VN] Error creating exportable element"); } } // src/functions/DiffUtility.ts function restoreDeepDiffChanges(data, differences) { let result = createExportableElement(data); differences.forEach((diff2) => { let dataToEdit = result; if (diff2.path && diff2.path.length > 0) { diff2.path.forEach((path, index) => { if (diff2.path && index === diff2.path.length - 1) { if (diff2.kind === "E" || diff2.kind === "D") { dataToEdit[path] = diff2.lhs; } else if (diff2.kind === "N") { if (Number.isInteger(path)) { if (Array.isArray(dataToEdit)) { dataToEdit.splice(path, 1); } } else if (typeof path === "string") { delete dataToEdit[path]; } } else if (diff2.kind === "A") { let index2 = diff2.index; if (diff2.item.kind === "N") { dataToEdit[path].splice(index2, 1); } else if (diff2.item.kind === "E" || diff2.item.kind === "D") { dataToEdit[path][index2] = diff2.item.lhs; } else if (diff2.item.kind === "A") { console.warn("[Pixi'VN] Nested array found, skipping diff", diff2); } else { console.warn("[Pixi'VN] No array found, skipping diff", diff2); } } } else { dataToEdit = dataToEdit[path]; } }); } else { console.warn("[Pixi'VN] No path found, skipping diff", diff2); } }); return result; } // src/managers/StorageManager.ts var _GameStorageManager = class _GameStorageManager { constructor() { } static get keysSystem() { return { /** * The key of the current dialogue memory */ CURRENT_DIALOGUE_MEMORY_KEY: "___current_dialogue_memory___", /** * The key of the last dialogue added in the step memory */ LAST_DIALOGUE_ADDED_IN_STEP_MEMORY_KEY: "___last_dialogue_added_in_step_memory___", /** * The key of the current menu options memory */ CURRENT_MENU_OPTIONS_MEMORY_KEY: "___current_menu_options_memory___", /** * The key of the last menu options added in the step memory */ LAST_MENU_OPTIONS_ADDED_IN_STEP_MEMORY_KEY: "___last_menu_options_added_in_step_memory___", /** * The key of the characters memory */ CHARACTER_CATEGORY_KEY: "___character___", /** * The key of the flags memory */ FLAGS_CATEGORY_KEY: "___flags___", /** * This variable is used to add the next dialog text into the current dialog memory. * This value was added to introduce Ink Glue functionality https://github.com/inkle/ink/blob/master/Documentation/WritingWithInk.md#glue */ ADD_NEXT_DIALOG_TEXT_INTO_THE_CURRENT_DIALOG_FLAG_KEY: "___glue___" }; } /** * Set a variable in the storage * @param key The key of the variable * @param value The value of the variable. If undefined, the variable will be removed * @returns */ static setVariable(key, value) { key = key.toLowerCase(); if (value === void 0 || value === null) { if (_GameStorageManager.storage.hasOwnProperty(key)) { delete _GameStorageManager.storage[key]; } return; } _GameStorageManager.storage[key] = value; } /** * Get a variable from the storage * @param key The key of the variable * @returns The value of the variable. If the variable does not exist, it will return undefined */ static getVariable(key) { key = key.toLowerCase(); if (_GameStorageManager.storage.hasOwnProperty(key)) { return _GameStorageManager.storage[key]; } return void 0; } /** * Remove a variable from the storage * @param key The key of the variable * @returns */ static removeVariable(key) { key = key.toLowerCase(); if (_GameStorageManager.storage.hasOwnProperty(key)) { delete _GameStorageManager.storage[key]; } } /** * Clear the storage and the oidsUsed * @returns */ static clear() { _GameStorageManager.storage = {}; } static exportJson() { return JSON.stringify(this.export()); } static export() { return createExportableElement(_GameStorageManager.storage); } static importJson(dataString) { _GameStorageManager.import(JSON.parse(dataString)); } static import(data) { _GameStorageManager.clear(); try { if (data) { _GameStorageManager.storage = data; } else { console.warn("[Pixi'VN] No storage data found"); } } catch (e) { console.error("[Pixi'VN] Error importing data", e); } } }; _GameStorageManager.storage = {}; var GameStorageManager = _GameStorageManager; // src/decorators/TickerDecorator.ts var registeredTickers = {}; function tickerDecorator(name) { return function(target) { if (!name) { name = target.name; } if (registeredTickers[name]) { console.info(`[Pixi'VN] Ticker ${name} already exists, it will be overwritten`); } target.prototype.id = name; registeredTickers[name] = target; }; } function geTickerInstanceById(tickerId, args, duration, priority) { try { let ticker = registeredTickers[tickerId]; if (!ticker) { console.error(`[Pixi'VN] Ticker ${tickerId} not found`); return; } return new ticker(args, duration, priority); } catch (e) { console.error(`[Pixi'VN] Error while getting Ticker ${tickerId}`, e); return; } } // src/functions/EasterEgg.ts function asciiArtLog() { console.info(` ____ _ _ ___ ___ _ | _ \\(_)_ _(_| ) \\ / / \\ | | | |_) | \\ \\/ / |/ \\ \\ / /| \\| | | __/| |> <| | \\ V / | |\\ | |_| |_/_/\\_\\_| \\_/ |_| \\_| `); } // src/types/ticker/TagToRemoveAfterType.ts var tagToRemoveAfter = "tagToRemoveAfter"; // src/managers/WindowManager.ts var _GameWindowManager = class _GameWindowManager { constructor() { } /** * The PIXI Application instance. * It not recommended to use this property directly. */ static get app() { if (!_GameWindowManager._app) { throw new Error("[Pixi'VN] GameWindowManager.app is undefined"); } return _GameWindowManager._app; } /** * If the manager is initialized. */ static get isInitialized() { return _GameWindowManager._isInitialized; } static get screen() { return _GameWindowManager.app.screen; } /** * Initialize the PIXI Application and the interface div. * This method should be called before any other method. * @param element The html element where I will put the canvas. Example: document.body * @param width The width of the canvas * @param height The height of the canvas * @param options The options of PIXI Application * @example * ```typescript * const body = document.body * if (!body) { * throw new Error('body element not found') * } * await GameWindowManager.initialize(body, 1920, 1080, { * backgroundColor: "#303030" * }) * ``` */ static initialize(element, width, height, options) { return __async(this, null, function* () { _GameWindowManager.canvasWidth = width; _GameWindowManager.canvasHeight = height; _GameWindowManager._app = new Application(); return _GameWindowManager.app.init(__spreadValues({ resolution: window.devicePixelRatio || 1, autoDensity: true, width, height }, options)).then(() => { initDevtools({ app: _GameWindowManager._app }); _GameWindowManager._isInitialized = true; this.addCanvasIntoHTMLElement(element); window.addEventListener("resize", _GameWindowManager.resize); _GameWindowManager.resize(); asciiArtLog(); }); }); } /** * Add the canvas into a html element. * @param element it is the html element where I will put the canvas. Example: document.body */ static addCanvasIntoHTMLElement(element) { if (_GameWindowManager.isInitialized) { element.appendChild(_GameWindowManager.app.canvas); } else { console.error("[Pixi'VN] GameWindowManager is not initialized"); } } /** * Initialize the interface div and add it into a html element. * @param element it is the html element where I will put the interface div. Example: document.getElementById('root') * @example * ```tsx * const root = document.getElementById('root') * if (!root) { * throw new Error('root element not found') * } * GameWindowManager.initializeHTMLLayout(root) * const reactRoot = createRoot(GameWindowManager.htmlLayout) * reactRoot.render( * <App /> * ) * ``` */ static initializeHTMLLayout(element) { let div = document.createElement("div"); div.style.position = "absolute"; div.style.pointerEvents = "none"; element.appendChild(div); _GameWindowManager.htmlLayout = div; _GameWindowManager.resize(); } /* Resize Metods */ /** * This method returns the scale of the screen. */ static get screenScale() { let screenWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); let screenHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); return Math.min(screenWidth / _GameWindowManager.canvasWidth, screenHeight / _GameWindowManager.canvasHeight); } /** * This method returns the width of the screen enlarged by the scale. */ static get screenWidth() { return Math.floor(_GameWindowManager.screenScale * _GameWindowManager.canvasWidth); } /** * This method returns the height of the screen enlarged by the scale. */ static get screenHeight() { return Math.floor(_GameWindowManager.screenScale * _GameWindowManager.canvasHeight); } /** * This method returns the horizontal margin of the screen. */ static get horizontalMargin() { let screenWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0); return (screenWidth - _GameWindowManager.screenWidth) / 2; } /** * This method returns the vertical margin of the screen. */ static get verticalMargin() { let screenHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0); return (screenHeight - _GameWindowManager.screenHeight) / 2; } /** * This method is called when the screen is resized. */ static resize() { if (_GameWindowManager.isInitialized) { let style = _GameWindowManager.app.canvas.style; style.width = `${_GameWindowManager.screenWidth}px`; style.height = `${_GameWindowManager.screenHeight}px`; style.marginLeft = `${_GameWindowManager.horizontalMargin}px`; style.marginRight = `${_GameWindowManager.horizontalMargin}px`; style.marginTop = `${_GameWindowManager.verticalMargin}px`; style.marginBottom = `${_GameWindowManager.verticalMargin}px`; } if (_GameWindowManager.htmlLayout) { _GameWindowManager.htmlLayout.style.width = `${_GameWindowManager.screenWidth}px`; _GameWindowManager.htmlLayout.style.height = `${_GameWindowManager.screenHeight}px`; _GameWindowManager.htmlLayout