[QoL] Add Arena Info Flyout for Weather, Terrain, etc. (#1734)

* Initial Commit

* Add Time of Day Icons and Remove Fight UI Dependancy

* Rename to Match

* Update battle-scene.ts

* Add Settings

* Add Comments
This commit is contained in:
Benjamin Odom 2024-06-03 21:18:09 -05:00 committed by GitHub
parent b9575d3ffc
commit 98cff12fd1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 472 additions and 13 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 581 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

View File

@ -58,6 +58,7 @@ import {InputsController} from "./inputs-controller";
import {UiInputs} from "./ui-inputs";
import { MoneyFormat } from "./enums/money-format";
import { NewArenaEvent } from "./battle-scene-events";
import ArenaFlyout from "./ui/arena-flyout";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -92,6 +93,7 @@ export default class BattleScene extends SceneBase {
public damageNumbersMode: integer = 0;
public reroll: boolean = false;
public showMovesetFlyout: boolean = true;
public showArenaFlyout: boolean = true;
public showLevelUpStats: boolean = true;
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
public enableRetries: boolean = false;
@ -178,6 +180,8 @@ export default class BattleScene extends SceneBase {
private luckText: Phaser.GameObjects.Text;
private modifierBar: ModifierBar;
private enemyModifierBar: ModifierBar;
public arenaFlyout: ArenaFlyout;
private fieldOverlay: Phaser.GameObjects.Rectangle;
private modifiers: PersistentModifier[];
private enemyModifiers: PersistentModifier[];
@ -411,6 +415,10 @@ export default class BattleScene extends SceneBase {
this.luckLabelText.setVisible(false);
this.fieldUI.add(this.luckLabelText);
this.arenaFlyout = new ArenaFlyout(this);
this.fieldUI.add(this.arenaFlyout);
this.fieldUI.moveBelow<Phaser.GameObjects.GameObject>(this.arenaFlyout, this.fieldOverlay);
this.updateUIPositions();
this.damageNumberHandler = new DamageNumberHandler();
@ -1272,6 +1280,13 @@ export default class BattleScene extends SceneBase {
return sprite;
}
moveBelowOverlay<T extends Phaser.GameObjects.GameObject>(gameObject: T) {
this.fieldUI.moveBelow<any>(gameObject, this.fieldOverlay);
}
processInfoButton(pressed: boolean): void {
this.arenaFlyout.toggleFlyout(pressed);
}
showFieldOverlay(duration: integer): Promise<void> {
return new Promise(resolve => {
this.tweens.add({

View File

@ -10,8 +10,10 @@ export enum ArenaEventType {
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
TERRAIN_CHANGED = "onTerrainChanged",
/** Triggers when a {@linkcode ArenaTagType} is added or removed */
TAG_CHANGED = "onTagChanged",
/** Triggers when a {@linkcode ArenaTagType} is added */
TAG_ADDED = "onTagAdded",
/** Triggers when a {@linkcode ArenaTagType} is removed */
TAG_REMOVED = "onTagRemoved",
}
/**
@ -59,17 +61,34 @@ export class TerrainChangedEvent extends ArenaEvent {
this.newTerrainType = newTerrainType;
}
}
/**
* Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
* Container class for {@linkcode ArenaEventType.TAG_ADDED} events
* @extends ArenaEvent
*/
export class TagChangedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being set */
export class TagAddedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being added */
public arenaTagType: ArenaTagType;
/** The {@linkcode ArenaTagSide} the tag is being placed on */
public arenaTagSide: ArenaTagSide;
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
super(ArenaEventType.TAG_CHANGED, duration);
super(ArenaEventType.TAG_ADDED, duration);
this.arenaTagType = arenaTagType;
this.arenaTagSide = arenaTagSide;
}
}
/**
* Container class for {@linkcode ArenaEventType.TAG_REMOVED} events
* @extends ArenaEvent
*/
export class TagRemovedEvent extends ArenaEvent {
/** The {@linkcode ArenaTagType} being removed */
public arenaTagType: ArenaTagType;
/** The {@linkcode ArenaTagSide} the tag was being placed on */
public arenaTagSide: ArenaTagSide;
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
super(ArenaEventType.TAG_REMOVED, duration);
this.arenaTagType = arenaTagType;
this.arenaTagSide = arenaTagSide;

View File

@ -19,7 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
import Pokemon from "./pokemon";
import * as Overrides from "../overrides";
import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "./arena-events";
export class Arena {
public scene: BattleScene;
@ -550,7 +550,7 @@ export class Arena {
this.tags.push(newTag);
newTag.onAdd(this);
this.eventTarget.dispatchEvent(new TagChangedEvent(newTag.tagType, newTag.side, newTag.turnCount));
this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount));
return true;
}
@ -577,6 +577,8 @@ export class Arena {
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
t.onRemove(this);
this.tags.splice(this.tags.indexOf(t), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(t.tagType, t.side, t.turnCount));
});
}
@ -586,6 +588,8 @@ export class Arena {
if (tag) {
tag.onRemove(this);
tags.splice(tags.indexOf(tag), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
}
return !!tag;
}
@ -595,6 +599,8 @@ export class Arena {
if (tag) {
tag.onRemove(this);
this.tags.splice(this.tags.indexOf(tag), 1);
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
}
return !!tag;
}
@ -603,6 +609,8 @@ export class Arena {
removeAllTags(): void {
while (this.tags.length) {
this.tags[0].onRemove(this);
this.eventTarget.dispatchEvent(new TagRemovedEvent(this.tags[0].tagType, this.tags[0].side, this.tags[0].turnCount));
this.tags.splice(0, 1);
}
}

View File

@ -95,6 +95,11 @@ export class LoadingScene extends SceneBase {
this.loadImage("type_tera", "ui");
this.loadAtlas("type_bgs", "ui");
this.loadImage("dawn_icon", "ui");
this.loadImage("day_icon", "ui");
this.loadImage("dusk_icon", "ui");
this.loadImage("night_icon", "ui");
this.loadImage("pb_tray_overlay_player", "ui");
this.loadImage("pb_tray_overlay_enemy", "ui");
this.loadAtlas("pb_tray_ball", "ui");

View File

@ -52,6 +52,7 @@ export const SettingKeys = {
Sprite_Set: "SPRITE_SET",
Move_Animations: "MOVE_ANIMATIONS",
Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT",
Show_Arena_Flyout: "SHOW_ARENA_FLYOUT",
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
EXP_Gains_Speed: "EXP_GAINS_SPEED",
EXP_Party_Display: "EXP_PARTY_DISPLAY",
@ -189,6 +190,13 @@ export const Setting: Array<Setting> = [
default: 1,
type: SettingType.ACCESSIBILITY
},
{
key: SettingKeys.Show_Arena_Flyout,
label: "Show Battle Effects Flyout",
options: OFF_ON,
default: 1,
type: SettingType.ACCESSIBILITY
},
{
key: SettingKeys.Show_Stats_on_Level_Up,
label: "Show Stats on Level Up",
@ -343,6 +351,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer):
case SettingKeys.Show_Moveset_Flyout:
scene.showMovesetFlyout = Setting[index].options[value] === "On";
break;
case SettingKeys.Show_Arena_Flyout:
scene.showArenaFlyout = Setting[index].options[value] === "On";
break;
case SettingKeys.Show_Stats_on_Level_Up:
scene.showLevelUpStats = Setting[index].options[value] === "On";
break;

View File

@ -69,7 +69,7 @@ export class UiInputs {
[Button.CYCLE_GENDER]: () => this.buttonCycleOption(Button.CYCLE_GENDER),
[Button.CYCLE_ABILITY]: () => this.buttonCycleOption(Button.CYCLE_ABILITY),
[Button.CYCLE_NATURE]: () => this.buttonCycleOption(Button.CYCLE_NATURE),
[Button.V]: () => this.buttonCycleOption(Button.V),
[Button.V]: () => this.buttonCycleOption(Button.V),
[Button.SPEED_UP]: () => this.buttonSpeedChange(),
[Button.SLOW_DOWN]: () => this.buttonSpeedChange(false),
};
@ -119,12 +119,14 @@ export class UiInputs {
}
}
buttonInfo(pressed: boolean = true): void {
if (!this.scene.showMovesetFlyout) {
return;
if (this.scene.showMovesetFlyout ) {
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
p.toggleFlyout(pressed);
}
}
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
p.toggleFlyout(pressed);
if (this.scene.showArenaFlyout) {
this.scene.ui.processInfoButton(pressed);
}
}

384
src/ui/arena-flyout.ts Normal file
View File

@ -0,0 +1,384 @@
import * as Utils from "../utils";
import { addTextObject, TextStyle } from "./text";
import BattleScene from "#app/battle-scene.js";
import { ArenaTagSide } from "#app/data/arena-tag.js";
import { WeatherType } from "#app/data/weather.js";
import { TerrainType } from "#app/data/terrain.js";
import { addWindow, WindowVariant } from "./ui-theme";
import { ArenaEvent, ArenaEventType, TagAddedEvent, TagRemovedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
import { BattleSceneEventType, TurnEndEvent } from "#app/battle-scene-events.js";
import { ArenaTagType } from "#app/data/enums/arena-tag-type.js";
import { TimeOfDay } from "#app/data/enums/time-of-day.js";
/** Enum used to differentiate {@linkcode Arena} effects */
enum ArenaEffectType {
PLAYER,
WEATHER,
TERRAIN,
FIELD,
ENEMY,
}
/** Container for info about an {@linkcode Arena}'s effects */
interface ArenaEffectInfo {
/** The enum string representation of the effect */
name: string;
/** {@linkcode ArenaEffectType} type of effect */
type: ArenaEffectType,
/** The maximum duration set by the effect */
maxDuration: number;
/** The current duration left on the effect */
duration: number;
}
export default class ArenaFlyout extends Phaser.GameObjects.Container {
/** An alias for the scene typecast to a {@linkcode BattleScene} */
private battleScene: BattleScene;
/** The restricted width of the flyout which should be drawn to */
private flyoutWidth = 170;
/** The restricted height of the flyout which should be drawn to */
private flyoutHeight = 51;
/** The amount of translation animation on the x-axis */
private translationX: number;
/** The x-axis point where the flyout should sit when activated */
private anchorX: number;
/** The y-axis point where the flyout should sit when activated */
private anchorY: number;
/** The initial container which defines where the flyout should be attached */
private flyoutParent: Phaser.GameObjects.Container;
/** The container which defines the drawable dimensions of the flyout */
private flyoutContainer: Phaser.GameObjects.Container;
/** The background {@linkcode Phaser.GameObjects.NineSlice} window for the flyout */
private flyoutWindow: Phaser.GameObjects.NineSlice;
/** The header {@linkcode Phaser.GameObjects.NineSlice} window for the flyout */
private flyoutWindowHeader: Phaser.GameObjects.NineSlice;
/** The {@linkcode Phaser.GameObjects.Text} that goes inside of the header */
private flyoutTextHeader: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Sprite} that represents the current time of day */
private timeOfDayIcon: Phaser.GameObjects.Sprite;
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the player's effects */
private flyoutTextHeaderPlayer: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate the enemy's effects */
private flyoutTextHeaderEnemy: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} header used to indicate neutral effects */
private flyoutTextHeaderField: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the player's effects */
private flyoutTextPlayer: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate the enemy's effects */
private flyoutTextEnemy: Phaser.GameObjects.Text;
/** The {@linkcode Phaser.GameObjects.Text} used to indicate neutral effects */
private flyoutTextField: Phaser.GameObjects.Text;
/** Container for all field effects observed by this object */
private readonly fieldEffectInfo: ArenaEffectInfo[] = [];
// Stores callbacks in a variable so they can be unsubscribed from when destroyed
private onNewArenaEvent = (event: Event) => this.onNewArena(event);
private onTurnInitEvent = (event: Event) => this.onTurnInit(event);
private onTurnEndEvent = (event: Event) => this.onTurnEnd(event);
private onFieldEffectChangedEvent = (event: Event) => this.onFieldEffectChanged(event);
constructor(scene: Phaser.Scene) {
super(scene, 0, 0);
this.battleScene = this.scene as BattleScene;
this.translationX = this.flyoutWidth;
this.anchorX = 0;
this.anchorY = -98;
this.flyoutParent = this.scene.add.container(this.anchorX - this.translationX, this.anchorY);
this.flyoutParent.setAlpha(0);
this.add(this.flyoutParent);
this.flyoutContainer = this.scene.add.container(0, 0);
this.flyoutParent.add(this.flyoutContainer);
this.flyoutWindow = addWindow(this.scene as BattleScene, 0, 0, this.flyoutWidth, this.flyoutHeight, false, false, 0, 0, WindowVariant.THIN);
this.flyoutContainer.add(this.flyoutWindow);
this.flyoutWindowHeader = addWindow(this.scene as BattleScene, this.flyoutWidth / 2, 0, this.flyoutWidth / 2, 14, false, false, 0, 0, WindowVariant.XTHIN);
this.flyoutWindowHeader.setOrigin();
this.flyoutContainer.add(this.flyoutWindowHeader);
this.flyoutTextHeader = addTextObject(this.scene, this.flyoutWidth / 2, 0, "Active Battle Effects", TextStyle.BATTLE_INFO);
this.flyoutTextHeader.setFontSize(54);
this.flyoutTextHeader.setAlign("center");
this.flyoutTextHeader.setOrigin();
this.flyoutContainer.add(this.flyoutTextHeader);
this.timeOfDayIcon = this.scene.add.sprite((this.flyoutWidth / 2) + (this.flyoutWindowHeader.displayWidth / 2), 0, "dawn_icon").setOrigin();
this.timeOfDayIcon.setVisible(false);
this.flyoutContainer.add(this.timeOfDayIcon);
this.flyoutTextHeaderPlayer = addTextObject(this.scene, 6, 5, "Player", TextStyle.SUMMARY_BLUE);
this.flyoutTextHeaderPlayer.setFontSize(54);
this.flyoutTextHeaderPlayer.setAlign("left");
this.flyoutTextHeaderPlayer.setOrigin(0, 0);
this.flyoutContainer.add(this.flyoutTextHeaderPlayer);
this.flyoutTextHeaderField = addTextObject(this.scene, this.flyoutWidth / 2, 5, "Neutral", TextStyle.SUMMARY_GREEN);
this.flyoutTextHeaderField.setFontSize(54);
this.flyoutTextHeaderField.setAlign("center");
this.flyoutTextHeaderField.setOrigin(0.5, 0);
this.flyoutContainer.add(this.flyoutTextHeaderField);
this.flyoutTextHeaderEnemy = addTextObject(this.scene, this.flyoutWidth - 6, 5, "Enemy", TextStyle.SUMMARY_RED);
this.flyoutTextHeaderEnemy.setFontSize(54);
this.flyoutTextHeaderEnemy.setAlign("right");
this.flyoutTextHeaderEnemy.setOrigin(1, 0);
this.flyoutContainer.add(this.flyoutTextHeaderEnemy);
this.flyoutTextPlayer = addTextObject(this.scene, 6, 13, "", TextStyle.BATTLE_INFO);
this.flyoutTextPlayer.setLineSpacing(-1);
this.flyoutTextPlayer.setFontSize(48);
this.flyoutTextPlayer.setAlign("left");
this.flyoutTextPlayer.setOrigin(0, 0);
this.flyoutContainer.add(this.flyoutTextPlayer);
this.flyoutTextField = addTextObject(this.scene, this.flyoutWidth / 2, 13, "", TextStyle.BATTLE_INFO);
this.flyoutTextField.setLineSpacing(-1);
this.flyoutTextField.setFontSize(48);
this.flyoutTextField.setAlign("center");
this.flyoutTextField.setOrigin(0.5, 0);
this.flyoutContainer.add(this.flyoutTextField);
this.flyoutTextEnemy = addTextObject(this.scene, this.flyoutWidth - 6, 13, "", TextStyle.BATTLE_INFO);
this.flyoutTextEnemy.setLineSpacing(-1);
this.flyoutTextEnemy.setFontSize(48);
this.flyoutTextEnemy.setAlign("right");
this.flyoutTextEnemy.setOrigin(1, 0);
this.flyoutContainer.add(this.flyoutTextEnemy);
this.name = "Fight Flyout";
this.flyoutParent.name = "Fight Flyout Parent";
// Subscribes to required events available on game start
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_INIT, this.onTurnInitEvent);
this.battleScene.eventTarget.addEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
}
private setTimeOfDayIcon() {
this.timeOfDayIcon.setTexture(TimeOfDay[this.battleScene.arena.getTimeOfDay()].toLowerCase() + "_icon");
}
private onTurnInit(event: Event) {
this.setTimeOfDayIcon();
}
private onNewArena(event: Event) {
this.fieldEffectInfo.length = 0;
// Subscribes to required events available on battle start
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.addEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
this.setTimeOfDayIcon();
}
/**
* Formats a string to title case
* @param unformattedText Text to be formatted
* @returns the formatted string
*/
private formatText(unformattedText: string): string {
const text = unformattedText.split("_");
for (let i = 0; i < text.length; i++) {
text[i] = text[i].charAt(0).toUpperCase() + text[i].substring(1).toLowerCase();
}
return text.join(" ");
}
/** Clears out the current string stored in all arena effect texts */
private clearText() {
this.flyoutTextPlayer.text = "";
this.flyoutTextField.text = "";
this.flyoutTextEnemy.text = "";
}
/** Parses through all set Arena Effects and puts them into the proper {@linkcode Phaser.GameObjects.Text} object */
private updateFieldText() {
this.clearText();
this.fieldEffectInfo.sort((infoA, infoB) => infoA.duration - infoB.duration);
for (let i = 0; i < this.fieldEffectInfo.length; i++) {
const fieldEffectInfo = this.fieldEffectInfo[i];
// Creates a proxy object to decide which text object needs to be updated
let textObject: Phaser.GameObjects.Text;
switch (fieldEffectInfo.type) {
case ArenaEffectType.PLAYER:
textObject = this.flyoutTextPlayer;
break;
case ArenaEffectType.WEATHER:
case ArenaEffectType.TERRAIN:
case ArenaEffectType.FIELD:
textObject = this.flyoutTextField;
break;
case ArenaEffectType.ENEMY:
textObject = this.flyoutTextEnemy;
break;
}
textObject.text += this.formatText(fieldEffectInfo.name);
if (fieldEffectInfo.type === ArenaEffectType.TERRAIN) {
textObject.text += " Terrain"; // Adds 'Terrain' since the enum does not contain it
}
if (fieldEffectInfo.maxDuration !== 0) {
textObject.text += " " + fieldEffectInfo.duration + "/" + fieldEffectInfo.maxDuration;
}
textObject.text += "\n";
}
}
/**
* Parses the {@linkcode Event} being passed and updates the state of the fieldEffectInfo array
* @param event {@linkcode Event} being sent
*/
private onFieldEffectChanged(event: Event) {
const arenaEffectChangedEvent = event as ArenaEvent;
if (!arenaEffectChangedEvent) {
return;
}
let foundIndex: number;
switch (arenaEffectChangedEvent.constructor) {
case TagAddedEvent:
const tagAddedEvent = arenaEffectChangedEvent as TagAddedEvent;
this.fieldEffectInfo.push({
name: ArenaTagType[tagAddedEvent.arenaTagType],
type: tagAddedEvent.arenaTagSide === ArenaTagSide.BOTH
? ArenaEffectType.FIELD
: tagAddedEvent.arenaTagSide === ArenaTagSide.PLAYER
? ArenaEffectType.PLAYER
: ArenaEffectType.ENEMY,
maxDuration: tagAddedEvent.duration,
duration: tagAddedEvent.duration});
break;
case TagRemovedEvent:
const tagRemovedEvent = arenaEffectChangedEvent as TagRemovedEvent;
foundIndex = this.fieldEffectInfo.findIndex(info => info.name === ArenaTagType[tagRemovedEvent.arenaTagType]);
if (foundIndex !== -1) { // If the tag was being tracked, remove it
this.fieldEffectInfo.splice(foundIndex, 1);
}
break;
case WeatherChangedEvent:
case TerrainChangedEvent:
const fieldEffectChangedEvent = arenaEffectChangedEvent as WeatherChangedEvent | TerrainChangedEvent;
// Stores the old Weather/Terrain name in case it's in the array already
const oldName =
fieldEffectChangedEvent instanceof WeatherChangedEvent
? WeatherType[fieldEffectChangedEvent.oldWeatherType]
: TerrainType[fieldEffectChangedEvent.oldTerrainType];
// Stores the new Weather/Terrain info
const newInfo = {
name:
fieldEffectChangedEvent instanceof WeatherChangedEvent
? WeatherType[fieldEffectChangedEvent.newWeatherType]
: TerrainType[fieldEffectChangedEvent.newTerrainType],
type: fieldEffectChangedEvent instanceof WeatherChangedEvent
? ArenaEffectType.WEATHER
: ArenaEffectType.TERRAIN,
maxDuration: fieldEffectChangedEvent.duration,
duration: fieldEffectChangedEvent.duration};
foundIndex = this.fieldEffectInfo.findIndex(info => [newInfo.name, oldName].includes(info.name));
if (foundIndex === -1) {
if (newInfo.name !== undefined) {
this.fieldEffectInfo.push(newInfo); // Adds the info to the array if it doesn't already exist and is defined
}
} else if (!newInfo.name) {
this.fieldEffectInfo.splice(foundIndex, 1); // Removes the old info if the new one is undefined
} else {
this.fieldEffectInfo[foundIndex] = newInfo; // Otherwise, replace the old info
}
break;
}
this.updateFieldText();
}
/**
* Iterates through the fieldEffectInfo array and decrements the duration of each item
* @param event {@linkcode Event} being sent
*/
private onTurnEnd(event: Event) {
const turnEndEvent = event as TurnEndEvent;
if (!turnEndEvent) {
return;
}
const fieldEffectInfo: ArenaEffectInfo[] = [];
this.fieldEffectInfo.forEach(i => fieldEffectInfo.push(i));
for (let i = 0; i < fieldEffectInfo.length; i++) {
const info = fieldEffectInfo[i];
if (info.maxDuration === 0) {
continue;
}
--info.duration;
if (info.duration <= 0) { // Removes the item if the duration has expired
this.fieldEffectInfo.splice(this.fieldEffectInfo.indexOf(info), 1);
}
}
this.updateFieldText();
}
/**
* Animates the flyout to either show or hide it by applying a fade and translation
* @param visible Should the flyout be shown?
*/
toggleFlyout(visible: boolean): void {
this.scene.tweens.add({
targets: this.flyoutParent,
x: visible ? this.anchorX : this.anchorX - this.translationX,
duration: Utils.fixedInt(125),
ease: "Sine.easeInOut",
alpha: visible ? 1 : 0,
});
}
destroy(fromScene?: boolean): void {
this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.NEW_ARENA, this.onNewArenaEvent);
this.battleScene.eventTarget.removeEventListener(BattleSceneEventType.TURN_END, this.onTurnEndEvent);
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.WEATHER_CHANGED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TERRAIN_CHANGED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_ADDED, this.onFieldEffectChangedEvent);
this.battleScene.arena.eventTarget.removeEventListener(ArenaEventType.TAG_REMOVED, this.onFieldEffectChangedEvent);
super.destroy();
}
}

View File

@ -222,6 +222,21 @@ export default class UI extends Phaser.GameObjects.Container {
return this.handlers[Mode.MESSAGE] as BattleMessageUiHandler;
}
processInfoButton(pressed: boolean) {
if (this.overlayActive) {
return false;
}
const battleScene = this.scene as BattleScene;
if ([Mode.CONFIRM, Mode.COMMAND, Mode.FIGHT, Mode.MESSAGE].includes(this.mode)) {
battleScene?.processInfoButton(pressed);
return true;
}
battleScene?.processInfoButton(false);
return true;
}
processInput(button: Button): boolean {
if (this.overlayActive) {
return false;