[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
BIN
public/images/ui/dawn_icon.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
public/images/ui/day_icon.png
Normal file
After Width: | Height: | Size: 593 B |
BIN
public/images/ui/dusk_icon.png
Normal file
After Width: | Height: | Size: 534 B |
BIN
public/images/ui/legacy/dawn_icon.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
public/images/ui/legacy/day_icon.png
Normal file
After Width: | Height: | Size: 285 B |
BIN
public/images/ui/legacy/dusk_icon.png
Normal file
After Width: | Height: | Size: 300 B |
BIN
public/images/ui/legacy/night_icon.png
Normal file
After Width: | Height: | Size: 288 B |
BIN
public/images/ui/night_icon.png
Normal file
After Width: | Height: | Size: 686 B |
@ -58,6 +58,7 @@ import {InputsController} from "./inputs-controller";
|
|||||||
import {UiInputs} from "./ui-inputs";
|
import {UiInputs} from "./ui-inputs";
|
||||||
import { MoneyFormat } from "./enums/money-format";
|
import { MoneyFormat } from "./enums/money-format";
|
||||||
import { NewArenaEvent } from "./battle-scene-events";
|
import { NewArenaEvent } from "./battle-scene-events";
|
||||||
|
import ArenaFlyout from "./ui/arena-flyout";
|
||||||
|
|
||||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
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 damageNumbersMode: integer = 0;
|
||||||
public reroll: boolean = false;
|
public reroll: boolean = false;
|
||||||
public showMovesetFlyout: boolean = true;
|
public showMovesetFlyout: boolean = true;
|
||||||
|
public showArenaFlyout: boolean = true;
|
||||||
public showLevelUpStats: boolean = true;
|
public showLevelUpStats: boolean = true;
|
||||||
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
|
public enableTutorials: boolean = import.meta.env.VITE_BYPASS_TUTORIAL === "1";
|
||||||
public enableRetries: boolean = false;
|
public enableRetries: boolean = false;
|
||||||
@ -178,6 +180,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
private luckText: Phaser.GameObjects.Text;
|
private luckText: Phaser.GameObjects.Text;
|
||||||
private modifierBar: ModifierBar;
|
private modifierBar: ModifierBar;
|
||||||
private enemyModifierBar: ModifierBar;
|
private enemyModifierBar: ModifierBar;
|
||||||
|
public arenaFlyout: ArenaFlyout;
|
||||||
|
|
||||||
private fieldOverlay: Phaser.GameObjects.Rectangle;
|
private fieldOverlay: Phaser.GameObjects.Rectangle;
|
||||||
private modifiers: PersistentModifier[];
|
private modifiers: PersistentModifier[];
|
||||||
private enemyModifiers: PersistentModifier[];
|
private enemyModifiers: PersistentModifier[];
|
||||||
@ -411,6 +415,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.luckLabelText.setVisible(false);
|
this.luckLabelText.setVisible(false);
|
||||||
this.fieldUI.add(this.luckLabelText);
|
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.updateUIPositions();
|
||||||
|
|
||||||
this.damageNumberHandler = new DamageNumberHandler();
|
this.damageNumberHandler = new DamageNumberHandler();
|
||||||
@ -1272,6 +1280,13 @@ export default class BattleScene extends SceneBase {
|
|||||||
return sprite;
|
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> {
|
showFieldOverlay(duration: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.tweens.add({
|
this.tweens.add({
|
||||||
|
@ -10,8 +10,10 @@ export enum ArenaEventType {
|
|||||||
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
/** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
|
||||||
TERRAIN_CHANGED = "onTerrainChanged",
|
TERRAIN_CHANGED = "onTerrainChanged",
|
||||||
|
|
||||||
/** Triggers when a {@linkcode ArenaTagType} is added or removed */
|
/** Triggers when a {@linkcode ArenaTagType} is added */
|
||||||
TAG_CHANGED = "onTagChanged",
|
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;
|
this.newTerrainType = newTerrainType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
|
* Container class for {@linkcode ArenaEventType.TAG_ADDED} events
|
||||||
* @extends ArenaEvent
|
* @extends ArenaEvent
|
||||||
*/
|
*/
|
||||||
export class TagChangedEvent extends ArenaEvent {
|
export class TagAddedEvent extends ArenaEvent {
|
||||||
/** The {@linkcode ArenaTagType} being set */
|
/** The {@linkcode ArenaTagType} being added */
|
||||||
public arenaTagType: ArenaTagType;
|
public arenaTagType: ArenaTagType;
|
||||||
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
/** The {@linkcode ArenaTagSide} the tag is being placed on */
|
||||||
public arenaTagSide: ArenaTagSide;
|
public arenaTagSide: ArenaTagSide;
|
||||||
constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
|
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.arenaTagType = arenaTagType;
|
||||||
this.arenaTagSide = arenaTagSide;
|
this.arenaTagSide = arenaTagSide;
|
||||||
|
@ -19,7 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
|
|||||||
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
|
||||||
import Pokemon from "./pokemon";
|
import Pokemon from "./pokemon";
|
||||||
import * as Overrides from "../overrides";
|
import * as Overrides from "../overrides";
|
||||||
import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
|
import { WeatherChangedEvent, TerrainChangedEvent, TagAddedEvent, TagRemovedEvent } from "./arena-events";
|
||||||
|
|
||||||
export class Arena {
|
export class Arena {
|
||||||
public scene: BattleScene;
|
public scene: BattleScene;
|
||||||
@ -550,7 +550,7 @@ export class Arena {
|
|||||||
this.tags.push(newTag);
|
this.tags.push(newTag);
|
||||||
newTag.onAdd(this);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -577,6 +577,8 @@ export class Arena {
|
|||||||
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
|
this.tags.filter(t => !(t.lapse(this))).forEach(t => {
|
||||||
t.onRemove(this);
|
t.onRemove(this);
|
||||||
this.tags.splice(this.tags.indexOf(t), 1);
|
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) {
|
if (tag) {
|
||||||
tag.onRemove(this);
|
tag.onRemove(this);
|
||||||
tags.splice(tags.indexOf(tag), 1);
|
tags.splice(tags.indexOf(tag), 1);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
@ -595,6 +599,8 @@ export class Arena {
|
|||||||
if (tag) {
|
if (tag) {
|
||||||
tag.onRemove(this);
|
tag.onRemove(this);
|
||||||
this.tags.splice(this.tags.indexOf(tag), 1);
|
this.tags.splice(this.tags.indexOf(tag), 1);
|
||||||
|
|
||||||
|
this.eventTarget.dispatchEvent(new TagRemovedEvent(tag.tagType, tag.side, tag.turnCount));
|
||||||
}
|
}
|
||||||
return !!tag;
|
return !!tag;
|
||||||
}
|
}
|
||||||
@ -603,6 +609,8 @@ export class Arena {
|
|||||||
removeAllTags(): void {
|
removeAllTags(): void {
|
||||||
while (this.tags.length) {
|
while (this.tags.length) {
|
||||||
this.tags[0].onRemove(this);
|
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);
|
this.tags.splice(0, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,11 @@ export class LoadingScene extends SceneBase {
|
|||||||
this.loadImage("type_tera", "ui");
|
this.loadImage("type_tera", "ui");
|
||||||
this.loadAtlas("type_bgs", "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_player", "ui");
|
||||||
this.loadImage("pb_tray_overlay_enemy", "ui");
|
this.loadImage("pb_tray_overlay_enemy", "ui");
|
||||||
this.loadAtlas("pb_tray_ball", "ui");
|
this.loadAtlas("pb_tray_ball", "ui");
|
||||||
|
@ -52,6 +52,7 @@ export const SettingKeys = {
|
|||||||
Sprite_Set: "SPRITE_SET",
|
Sprite_Set: "SPRITE_SET",
|
||||||
Move_Animations: "MOVE_ANIMATIONS",
|
Move_Animations: "MOVE_ANIMATIONS",
|
||||||
Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT",
|
Show_Moveset_Flyout: "SHOW_MOVESET_FLYOUT",
|
||||||
|
Show_Arena_Flyout: "SHOW_ARENA_FLYOUT",
|
||||||
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
|
Show_Stats_on_Level_Up: "SHOW_LEVEL_UP_STATS",
|
||||||
EXP_Gains_Speed: "EXP_GAINS_SPEED",
|
EXP_Gains_Speed: "EXP_GAINS_SPEED",
|
||||||
EXP_Party_Display: "EXP_PARTY_DISPLAY",
|
EXP_Party_Display: "EXP_PARTY_DISPLAY",
|
||||||
@ -189,6 +190,13 @@ export const Setting: Array<Setting> = [
|
|||||||
default: 1,
|
default: 1,
|
||||||
type: SettingType.ACCESSIBILITY
|
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,
|
key: SettingKeys.Show_Stats_on_Level_Up,
|
||||||
label: "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:
|
case SettingKeys.Show_Moveset_Flyout:
|
||||||
scene.showMovesetFlyout = Setting[index].options[value] === "On";
|
scene.showMovesetFlyout = Setting[index].options[value] === "On";
|
||||||
break;
|
break;
|
||||||
|
case SettingKeys.Show_Arena_Flyout:
|
||||||
|
scene.showArenaFlyout = Setting[index].options[value] === "On";
|
||||||
|
break;
|
||||||
case SettingKeys.Show_Stats_on_Level_Up:
|
case SettingKeys.Show_Stats_on_Level_Up:
|
||||||
scene.showLevelUpStats = Setting[index].options[value] === "On";
|
scene.showLevelUpStats = Setting[index].options[value] === "On";
|
||||||
break;
|
break;
|
||||||
|
@ -119,15 +119,17 @@ export class UiInputs {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
buttonInfo(pressed: boolean = true): void {
|
buttonInfo(pressed: boolean = true): void {
|
||||||
if (!this.scene.showMovesetFlyout) {
|
if (this.scene.showMovesetFlyout ) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
|
for (const p of this.scene.getField().filter(p => p?.isActive(true))) {
|
||||||
p.toggleFlyout(pressed);
|
p.toggleFlyout(pressed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.scene.showArenaFlyout) {
|
||||||
|
this.scene.ui.processInfoButton(pressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
buttonMenu(): void {
|
buttonMenu(): void {
|
||||||
if (this.scene.disableMenu) {
|
if (this.scene.disableMenu) {
|
||||||
return;
|
return;
|
||||||
|
384
src/ui/arena-flyout.ts
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
15
src/ui/ui.ts
@ -222,6 +222,21 @@ export default class UI extends Phaser.GameObjects.Container {
|
|||||||
return this.handlers[Mode.MESSAGE] as BattleMessageUiHandler;
|
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 {
|
processInput(button: Button): boolean {
|
||||||
if (this.overlayActive) {
|
if (this.overlayActive) {
|
||||||
return false;
|
return false;
|
||||||
|