From a97803b99b53ae2bec104571fa3e63e70a21824c Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Sun, 18 Aug 2024 23:27:38 +0200 Subject: [PATCH] [Bug] Fix type-hints for immunity (#3620) * enable mock containers to be found by name * enable mock text to be found by name * add test coverage for type-hints Only for "immunity" and "status moves" --- src/field/pokemon.ts | 6 +- src/test/ui/transfer-item.test.ts | 1 - src/test/ui/type-hints.test.ts | 89 +++++++++++++++++++ src/test/utils/gameManager.ts | 3 + src/test/utils/helpers/settingsHelper.ts | 15 ++++ .../mocks/mocksContainer/mockContainer.ts | 7 +- .../utils/mocks/mocksContainer/mockText.ts | 16 ++-- src/ui/fight-ui-handler.ts | 13 +-- 8 files changed, 131 insertions(+), 19 deletions(-) create mode 100644 src/test/ui/type-hints.test.ts create mode 100644 src/test/utils/helpers/settingsHelper.ts diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 10851451a1a..e38813ed3c0 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1210,11 +1210,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * @param source - The Pokémon using the move. * @param move - The move being used. - * @returns The type damage multiplier or undefined if it's a status move + * @returns The type damage multiplier or 1 if it's a status move */ - getMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier | undefined { + getMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier { if (move.getMove().category === MoveCategory.STATUS) { - return undefined; + return 1; } return this.getAttackMoveEffectiveness(source, move, !this.battleData?.abilityRevealed); diff --git a/src/test/ui/transfer-item.test.ts b/src/test/ui/transfer-item.test.ts index bbb9a823ad9..9315971e484 100644 --- a/src/test/ui/transfer-item.test.ts +++ b/src/test/ui/transfer-item.test.ts @@ -87,7 +87,6 @@ describe("UI - Transfer Items", () => { handler.processInput(Button.ACTION); // select Pokemon expect(handler.optionsContainer.list.some((option) => (option as BBCodeText).text?.includes("Transfer"))).toBe(true); - game.phaseInterceptor.unlock(); }); diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts new file mode 100644 index 00000000000..eb0191812e8 --- /dev/null +++ b/src/test/ui/type-hints.test.ts @@ -0,0 +1,89 @@ +import { Button } from "#app/enums/buttons.js"; +import { Moves } from "#app/enums/moves"; +import { Species } from "#app/enums/species"; +import { CommandPhase } from "#app/phases"; +import FightUiHandler from "#app/ui/fight-ui-handler.js"; +import { Mode } from "#app/ui/ui.js"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import MockText from "../utils/mocks/mocksContainer/mockText"; +import { SPLASH_ONLY } from "../utils/testUtils"; + +describe("UI - Type Hints", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(async () => { + game = new GameManager(phaserGame); + game.settings.typeHints(true); //activate type hints + game.override.battleType("single").startingLevel(100).startingWave(1).enemyMoveset(SPLASH_ONLY); + }); + + it("check immunity color", async () => { + game.override + .battleType("single") + .startingLevel(100) + .startingWave(1) + .enemySpecies(Species.FLORGES) + .enemyMoveset(SPLASH_ONLY) + .moveset([Moves.DRAGON_CLAW]); + game.settings.typeHints(true); //activate type hints + + await game.startBattle([Species.RAYQUAZA]); + + game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + const { ui } = game.scene; + const handler = ui.getHandler(); + handler.processInput(Button.ACTION); // select "Fight" + game.phaseInterceptor.unlock(); + }); + + game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + const { ui } = game.scene; + const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); + const dragonClawText = movesContainer + .getAll() + .find((text) => text.text === "Dragon Claw")! as unknown as MockText; + + expect.soft(dragonClawText.color).toBe("#929292"); + ui.getHandler().processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(CommandPhase); + }); + + it("check status move color", async () => { + game.override.enemySpecies(Species.FLORGES).moveset([Moves.GROWL]); + + await game.startBattle([Species.RAYQUAZA]); + + game.onNextPrompt("CommandPhase", Mode.COMMAND, () => { + const { ui } = game.scene; + const handler = ui.getHandler(); + handler.processInput(Button.ACTION); // select "Fight" + game.phaseInterceptor.unlock(); + }); + + game.onNextPrompt("CommandPhase", Mode.FIGHT, () => { + const { ui } = game.scene; + const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); + const growlText = movesContainer + .getAll() + .find((text) => text.text === "Growl")! as unknown as MockText; + + expect.soft(growlText.color).toBe(undefined); + ui.getHandler().processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(CommandPhase); + }); +}); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 27ba7a215eb..6333179e3b2 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -30,6 +30,7 @@ import { MoveHelper } from "./helpers/moveHelper"; import { vi } from "vitest"; import { ClassicModeHelper } from "./helpers/classicModeHelper"; import { DailyModeHelper } from "./helpers/dailyModeHelper"; +import { SettingsHelper } from "./helpers/settingsHelper"; /** * Class to manage the game state and transitions between phases. @@ -44,6 +45,7 @@ export default class GameManager { public readonly move: MoveHelper; public readonly classicMode: ClassicModeHelper; public readonly dailyMode: DailyModeHelper; + public readonly settings: SettingsHelper; /** * Creates an instance of GameManager. @@ -63,6 +65,7 @@ export default class GameManager { this.move = new MoveHelper(this); this.classicMode = new ClassicModeHelper(this); this.dailyMode = new DailyModeHelper(this); + this.settings = new SettingsHelper(this); } /** diff --git a/src/test/utils/helpers/settingsHelper.ts b/src/test/utils/helpers/settingsHelper.ts new file mode 100644 index 00000000000..dec9e160d51 --- /dev/null +++ b/src/test/utils/helpers/settingsHelper.ts @@ -0,0 +1,15 @@ +import { GameManagerHelper } from "./gameManagerHelper"; + +/** + * Helper to handle settings for tests + */ +export class SettingsHelper extends GameManagerHelper { + + /** + * Disable/Enable type hints settings + * @param enable true to enabled, false to disabled + */ + typeHints(enable: boolean) { + this.game.scene.typeHints = enable; + } +} diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index d3672cb5235..5babd9e71b2 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -1,4 +1,5 @@ import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockContainer implements MockGameObject { @@ -13,6 +14,7 @@ export default class MockContainer implements MockGameObject { public frame; protected textureManager; public list: MockGameObject[] = []; + private name?: string; constructor(textureManager: MockTextureManager, x, y) { this.x = x; @@ -159,9 +161,10 @@ export default class MockContainer implements MockGameObject { // Moves this Game Object to be below the given Game Object in the display list. } - setName(name) { + setName = vi.fn((name: string) => { + this.name = name; // return this.phaserSprite.setName(name); - } + }); bringToTop(obj) { // Brings this Game Object to the top of its parents display list. diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index 5d405efadfd..6b9ecf083fd 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -1,4 +1,5 @@ import UI from "#app/ui/ui"; +import { vi } from "vitest"; import { MockGameObject } from "../mockGameObject"; export default class MockText implements MockGameObject { @@ -10,6 +11,8 @@ export default class MockText implements MockGameObject { public list: MockGameObject[] = []; public style; public text = ""; + private name?: string; + public color?: string; constructor(textureManager, x, y, content, styleOptions) { this.scene = textureManager.scene; @@ -190,10 +193,9 @@ export default class MockText implements MockGameObject { }; } - setColor(color) { - // Sets the tint of this Game Object. - // return this.phaserText.setColor(color); - } + setColor = vi.fn((color: string) => { + this.color = color; + }); setShadowColor(color) { // Sets the shadow color. @@ -219,9 +221,9 @@ export default class MockText implements MockGameObject { // return this.phaserText.setAlpha(alpha); } - setName(name) { - // return this.phaserText.setName(name); - } + setName = vi.fn((name: string) => { + this.name = name; + }); setAlign(align) { // return this.phaserText.setAlign(align); diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index 8279ab72a70..4ade6ca5d20 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -12,6 +12,8 @@ import {Button} from "#enums/buttons"; import Pokemon, { PokemonMove } from "#app/field/pokemon.js"; export default class FightUiHandler extends UiHandler { + public static readonly MOVES_CONTAINER_NAME = "moves"; + private movesContainer: Phaser.GameObjects.Container; private moveInfoContainer: Phaser.GameObjects.Container; private typeIcon: Phaser.GameObjects.Sprite; @@ -35,7 +37,7 @@ export default class FightUiHandler extends UiHandler { const ui = this.getUi(); this.movesContainer = this.scene.add.container(18, -38.7); - this.movesContainer.setName("moves"); + this.movesContainer.setName(FightUiHandler.MOVES_CONTAINER_NAME); ui.add(this.movesContainer); this.moveInfoContainer = this.scene.add.container(1, 0); @@ -271,11 +273,10 @@ export default class FightUiHandler extends UiHandler { return undefined; } - const moveColors = opponents.map((opponent) => { - return opponent.getMoveEffectiveness(pokemon, pokemonMove); - }).filter((eff) => !!eff).sort((a, b) => b - a).map((effectiveness) => { - return getTypeDamageMultiplierColor(effectiveness, "offense"); - }); + const moveColors = opponents + .map((opponent) => opponent.getMoveEffectiveness(pokemon, pokemonMove)) + .sort((a, b) => b - a) + .map((effectiveness) => getTypeDamageMultiplierColor(effectiveness ?? 0, "offense")); return moveColors[0]; }