import * as Utils from "../utils"; import { globalScene } from "#app/global-scene"; import { BattleSceneEventType } from "../events/battle-scene"; import { EaseType } from "#enums/ease-type"; import { TimeOfDay } from "#enums/time-of-day"; /** A small self contained UI element that displays the time of day as an icon */ export default class TimeOfDayWidget extends Phaser.GameObjects.Container { /** The {@linkcode Phaser.GameObjects.Sprite} that represents the foreground of the current time of day */ private readonly timeOfDayIconFgs: Phaser.GameObjects.Sprite[] = new Array(2); /** The {@linkcode Phaser.GameObjects.Sprite} that represents the middle-ground of the current time of day */ private readonly timeOfDayIconMgs: Phaser.GameObjects.Sprite[] = new Array(2); /** The {@linkcode Phaser.GameObjects.Sprite} that represents the background of the current time of day */ private readonly timeOfDayIconBgs: Phaser.GameObjects.Sprite[] = new Array(2); /** An array containing all timeOfDayIcon objects for easier iteration */ private timeOfDayIcons: Phaser.GameObjects.Sprite[]; /** A map containing all timeOfDayIcon arrays with a matching string key for easier iteration */ private timeOfDayIconPairs: Map = new Map([ [ "bg", this.timeOfDayIconBgs ], [ "mg", this.timeOfDayIconMgs ], [ "fg", this.timeOfDayIconFgs ],]); /** The current time of day */ private currentTime: TimeOfDay = TimeOfDay.ALL; /** The previous time of day */ private previousTime: TimeOfDay = TimeOfDay.ALL; // Subscribes to required events available on game start private readonly onEncounterPhaseEvent = (event: Event) => this.onEncounterPhase(event); private _parentVisible: boolean; /** Is the parent object visible? */ public get parentVisible(): boolean { return this._parentVisible; } /** On set, resumes any paused tweens if true */ public set parentVisible(visible: boolean) { if (visible && !this._parentVisible) { // Only resume the tweens if parent is newly visible this.timeOfDayIcons?.forEach( icon => globalScene.tweens.getTweensOf(icon).forEach( tween => tween.resume())); } this._parentVisible = visible; } constructor(x: number = 0, y: number = 0) { super(globalScene, x, y); this.setVisible(globalScene.showTimeOfDayWidget); if (!globalScene.showTimeOfDayWidget) { return; } // Initialize all sprites this.timeOfDayIconPairs.forEach( (icons, key) => { for (let i = 0; i < icons.length; i++) { icons[i] = globalScene.add.sprite(0, 0, "dawn_icon_" + key).setOrigin(); } }); // Store a flat array of all icons for later this.timeOfDayIcons = [ this.timeOfDayIconBgs, this.timeOfDayIconMgs, this.timeOfDayIconFgs ].flat(); this.add(this.timeOfDayIcons); globalScene.eventTarget.addEventListener(BattleSceneEventType.ENCOUNTER_PHASE, this.onEncounterPhaseEvent); } /** * Creates a tween animation based on the 'Back' ease algorithm * @returns an array of all tweens in the animation */ private getBackTween(): Phaser.Types.Tweens.TweenBuilderConfig[] { const rotate = { targets: [ this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1] ], angle: "+=90", duration: Utils.fixedInt(1500), ease: "Back.easeOut", paused: !this.parentVisible, }; const fade = { targets: [ this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1] ], alpha: 0, duration: Utils.fixedInt(500), ease: "Linear", paused: !this.parentVisible, }; return [ rotate, fade ]; } /** * Creates a tween animation based on the 'Bounce' ease algorithm * @returns an array of all tweens in the animation */ private getBounceTween(): Phaser.Types.Tweens.TweenBuilderConfig[] { const bounce = { targets: [ this.timeOfDayIconMgs[0], this.timeOfDayIconMgs[1] ], angle: "+=90", duration: Utils.fixedInt(2000), ease: "Bounce.easeOut", paused: !this.parentVisible, }; const fade = { targets: [ this.timeOfDayIconBgs[1], this.timeOfDayIconMgs[1], this.timeOfDayIconFgs[1] ], alpha: 0, duration: Utils.fixedInt(800), ease: "Linear", paused: !this.parentVisible, }; return [ bounce, fade ]; } /** Resets all icons to the proper depth, texture, and alpha so they are ready to tween */ private resetIcons() { this.moveBelow(this.timeOfDayIconBgs[0], this.timeOfDayIconBgs[1]); this.moveBelow(this.timeOfDayIconMgs[0], this.timeOfDayIconBgs[1]); this.moveBelow(this.timeOfDayIconFgs[0], this.timeOfDayIconFgs[1]); this.timeOfDayIconPairs.forEach( (icons, key) => { icons[0].setTexture(TimeOfDay[this.currentTime].toLowerCase() + "_icon_" + key); icons[1].setTexture(TimeOfDay[this.previousTime].toLowerCase() + "_icon_" + key); }); this.timeOfDayIconMgs[0].setRotation(-90 * (3.14 / 180)); this.timeOfDayIcons.forEach(icon => icon.setAlpha(1)); } /** Adds the proper tween for all icons */ private tweenTimeOfDayIcon() { globalScene.tweens.killTweensOf(this.timeOfDayIcons); this.resetIcons(); // Tween based on the player setting (globalScene.timeOfDayAnimation === EaseType.BACK ? this.getBackTween() : this.getBounceTween()) .forEach(tween => globalScene.tweens.add(tween)); // Swaps all elements of the icon arrays by shifting the first element onto the end of the array // This ensures index[0] is always the new time of day icon and index[1] is always the current one this.timeOfDayIconPairs.forEach( icons => { const shifted = icons.shift(); shifted && icons.push(shifted); }); } /** * Grabs the current time of day from the arena and calls {@linkcode tweenTimeOfDayIcon} * @param event {@linkcode Event} being sent */ private onEncounterPhase(event: Event) { const newTime = globalScene.arena.getTimeOfDay(); if (this.currentTime === newTime) { return; } this.currentTime = newTime; this.previousTime = this.currentTime - 1; if (this.previousTime < TimeOfDay.DAWN) { this.previousTime = TimeOfDay.NIGHT; } this.tweenTimeOfDayIcon(); } }