From 38c682cca788f30da26e1823115835f4101e7341 Mon Sep 17 00:00:00 2001 From: EmberCM Date: Fri, 4 Oct 2024 01:04:50 -0500 Subject: [PATCH 01/70] [QoL] Add fusion options to overrides (#4298) * Add fusion options to overrides * Add fusions overrides to overridesHelper --------- Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 3 ++ src/field/pokemon.ts | 10 +++++- src/overrides.ts | 16 +++++++++ src/phases/select-starter-phase.ts | 2 +- src/test/utils/helpers/overridesHelper.ts | 42 +++++++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 7ee4abd2f27..7f17a666280 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -889,6 +889,9 @@ export default class BattleScene extends SceneBase { } const pokemon = new EnemyPokemon(this, species, level, trainerSlot, boss, dataSource); + if (Overrides.OPP_FUSION_OVERRIDE) { + pokemon.generateFusionSpecies(); + } overrideModifiers(this, false); overrideHeldItems(this, pokemon, false); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 731d71b85d9..fed91d05fd5 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1964,7 +1964,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { && species.speciesId !== this.species.speciesId; }; - this.fusionSpecies = this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true); + let fusionOverride: PokemonSpecies | undefined = undefined; + + if (forStarter && this instanceof PlayerPokemon && Overrides.STARTER_FUSION_SPECIES_OVERRIDE) { + fusionOverride = getPokemonSpecies(Overrides.STARTER_FUSION_SPECIES_OVERRIDE); + } else if (this instanceof EnemyPokemon && Overrides.OPP_FUSION_SPECIES_OVERRIDE) { + fusionOverride = getPokemonSpecies(Overrides.OPP_FUSION_SPECIES_OVERRIDE); + } + + this.fusionSpecies = fusionOverride ?? this.scene.randomSpecies(this.scene.currentBattle?.waveIndex || 0, this.level, false, filter, true); this.fusionAbilityIndex = (this.fusionSpecies.abilityHidden && hasHiddenAbility ? 2 : this.fusionSpecies.ability2 !== this.fusionSpecies.ability1 ? randAbilityIndex : 0); this.fusionShiny = this.shiny; this.fusionVariant = this.variant; diff --git a/src/overrides.ts b/src/overrides.ts index 852961db8d7..27886ded2f2 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -100,6 +100,14 @@ class DefaultOverrides { * @example SPECIES_OVERRIDE = Species.Bulbasaur; */ readonly STARTER_SPECIES_OVERRIDE: Species | number = 0; + /** + * This will force your starter to be a random fusion + */ + readonly STARTER_FUSION_OVERRIDE: boolean = false; + /** + * This will override the species of the fusion + */ + readonly STARTER_FUSION_SPECIES_OVERRIDE: Species | integer = 0; readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; @@ -112,6 +120,14 @@ class DefaultOverrides { // OPPONENT / ENEMY OVERRIDES // -------------------------- readonly OPP_SPECIES_OVERRIDE: Species | number = 0; + /** + * This will make all opponents fused Pokemon + */ + readonly OPP_FUSION_OVERRIDE: boolean = false; + /** + * This will override the species of the fusion only when the opponent is already a fusion + */ + readonly OPP_FUSION_SPECIES_OVERRIDE: Species | integer = 0; readonly OPP_LEVEL_OVERRIDE: number = 0; readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; diff --git a/src/phases/select-starter-phase.ts b/src/phases/select-starter-phase.ts index cd3c112549c..1692b5f2234 100644 --- a/src/phases/select-starter-phase.ts +++ b/src/phases/select-starter-phase.ts @@ -80,7 +80,7 @@ export class SelectStarterPhase extends Phase { starterPokemon.nickname = starter.nickname; } - if (this.scene.gameMode.isSplicedOnly) { + if (this.scene.gameMode.isSplicedOnly || Overrides.STARTER_FUSION_OVERRIDE) { starterPokemon.generateFusionSpecies(true); } starterPokemon.setVisible(false); diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index e41cbad42c5..27fd9552fe4 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -85,6 +85,27 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the player (pokemon) to be a random fusion + * @returns this + */ + enableStarterFusion(): this { + vi.spyOn(Overrides, "STARTER_FUSION_OVERRIDE", "get").mockReturnValue(true); + this.log("Player Pokemon is a random fusion!"); + return this; + } + + /** + * Override the player (pokemon) fusion species + * @param species the fusion species to set + * @returns this + */ + starterFusionSpecies(species: Species | number): this { + vi.spyOn(Overrides, "STARTER_FUSION_SPECIES_OVERRIDE", "get").mockReturnValue(species); + this.log(`Player Pokemon fusion species set to ${Species[species]} (=${species})!`); + return this; + } + /** * Override the player (pokemons) forms * @param forms the (pokemon) forms to set @@ -232,6 +253,27 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the enemy (pokemon) to be a random fusion + * @returns this + */ + enableEnemyFusion(): this { + vi.spyOn(Overrides, "OPP_FUSION_OVERRIDE", "get").mockReturnValue(true); + this.log("Enemy Pokemon is a random fusion!"); + return this; + } + + /** + * Override the enemy (pokemon) fusion species + * @param species the fusion species to set + * @returns this + */ + enemyFusionSpecies(species: Species | number): this { + vi.spyOn(Overrides, "OPP_FUSION_SPECIES_OVERRIDE", "get").mockReturnValue(species); + this.log(`Enemy Pokemon fusion species set to ${Species[species]} (=${species})!`); + return this; + } + /** * Override the enemy (pokemon) {@linkcode Abilities | ability} * @param ability the (pokemon) {@linkcode Abilities | ability} to set From a5db2e1d6df77107d81e9ac1afaeb0cc76875cdc Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:42:05 +0800 Subject: [PATCH 02/70] [Misc] Update readme to include relevant links (#4573) --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index da10290d51d..866687d54b7 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,12 @@ If you have the motivation and experience with Typescript/Javascript (or are wil 2. Run `npm run start:dev` to locally run the project in `localhost:8000` #### Linting -We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. +We're using ESLint as our common linter and formatter. It will run automatically during the pre-commit hook but if you would like to manually run it, use the `npm run eslint` script. To view the complete rules, check out the [eslint.config.js](./eslint.config.js) file. + +### đź“š Documentation +You can find the auto-generated documentation [here](https://pagefaultgames.github.io/pokerogue/main/index.html). +For information on enemy AI, check out the [enemy-ai.md](./docs/enemy-ai.md) file. +For detailed guidelines on documenting your code, refer to the [comments.md](./docs/comments.md) file. ### âť” FAQ From 2bc5f501545d83690e25fe524d2e01698ec216ac Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:42:20 -0400 Subject: [PATCH 03/70] [Test] Fix some test flakiness involving `doKillOpponents()` (#4571) * [Test] Fix some test flakiness involving game.doKillOpponents() * PR Feedback * Fix linting --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/test/daily_mode.test.ts | 14 ++++++-------- src/test/reload.test.ts | 12 ++++-------- 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/test/daily_mode.test.ts b/src/test/daily_mode.test.ts index f832d17cc6c..100cf07f9c0 100644 --- a/src/test/daily_mode.test.ts +++ b/src/test/daily_mode.test.ts @@ -5,6 +5,7 @@ import { Moves } from "#app/enums/moves"; import { Biome } from "#app/enums/biome"; import { Mode } from "#app/ui/ui"; import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Species } from "#enums/species"; //const TIMEOUT = 20 * 1000; @@ -53,12 +54,11 @@ describe("Shop modifications", async () => { game.override .startingWave(9) - .startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather + .startingBiome(Biome.ICE_CAVE) .battleType("single") .startingLevel(100) // Avoid levelling up - .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() .disableTrainerWaves() - .moveset([ Moves.KOWTOW_CLEAVE ]) + .moveset([ Moves.SPLASH ]) .enemyMoveset(Moves.SPLASH); game.modifiers .addCheck("EVIOLITE") @@ -71,9 +71,8 @@ describe("Shop modifications", async () => { }); it("should not have Eviolite and Mini Black Hole available in Classic if not unlocked", async () => { - await game.classicMode.startBattle(); - game.move.select(Moves.KOWTOW_CLEAVE); - await game.phaseInterceptor.to("DamagePhase"); + await game.classicMode.startBattle([ Species.BULBASAUR ]); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { @@ -86,8 +85,7 @@ describe("Shop modifications", async () => { it("should have Eviolite and Mini Black Hole available in Daily", async () => { await game.dailyMode.startBattle(); - game.move.select(Moves.KOWTOW_CLEAVE); - await game.phaseInterceptor.to("DamagePhase"); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to("BattleEndPhase"); game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { diff --git a/src/test/reload.test.ts b/src/test/reload.test.ts index 7849f8f0080..b15e9691ed6 100644 --- a/src/test/reload.test.ts +++ b/src/test/reload.test.ts @@ -44,15 +44,13 @@ describe("Reload", () => { .startingWave(10) .battleType("single") .startingLevel(100) // Avoid levelling up - .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() .disableTrainerWaves() - .moveset([ Moves.KOWTOW_CLEAVE ]) + .moveset([ Moves.SPLASH ]) .enemyMoveset(Moves.SPLASH); await game.dailyMode.startBattle(); // Transition from Wave 10 to Wave 11 in order to trigger biome switch - game.move.select(Moves.KOWTOW_CLEAVE); - await game.phaseInterceptor.to("DamagePhase"); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); game.onNextPrompt("SelectBiomePhase", Mode.OPTION_SELECT, () => { (game.scene.time as MockClock).overrideDelay = null; @@ -79,15 +77,13 @@ describe("Reload", () => { .startingBiome(Biome.ICE_CAVE) // Will lead to Snowy Forest with randomly generated weather .battleType("single") .startingLevel(100) // Avoid levelling up - .enemyLevel(1000) // Avoid opponent dying before game.doKillOpponents() .disableTrainerWaves() - .moveset([ Moves.KOWTOW_CLEAVE ]) + .moveset([ Moves.SPLASH ]) .enemyMoveset(Moves.SPLASH); await game.classicMode.startBattle(); // Apparently daily mode would override the biome // Transition from Wave 10 to Wave 11 in order to trigger biome switch - game.move.select(Moves.KOWTOW_CLEAVE); - await game.phaseInterceptor.to("DamagePhase"); + game.move.select(Moves.SPLASH); await game.doKillOpponents(); await game.toNextWave(); expect(game.phaseInterceptor.log).toContain("NewBiomeEncounterPhase"); From 22442d3aa066f3ebb6700193c7b9d930cb08712c Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 4 Oct 2024 07:50:03 -0700 Subject: [PATCH 04/70] [Refactor] Refactor move phase and add documentation (#3974) * Refactor `MovePhase` to improve readability/maintainability Add tsdocs/comments all over Mark all functions/fields with public/etc Fix multi-hit moves called from Metronome/etc, fixes #3914 Remove unused function `BattleScene.pushMovePhase` Don't use failure text as a condition for move success A move defining potential failure text doesn't mean it failed Replace relative imports with absolute imports in `battle-scene.ts` Change some fields from optional to default `false` * Fix Whirlwind test * Fix linting --- src/battle-scene.ts | 149 ++++----- src/field/arena.ts | 12 +- src/field/pokemon.ts | 10 +- src/phases/move-effect-phase.ts | 2 +- src/phases/move-phase.ts | 553 +++++++++++++++++++------------ src/test/moves/whirlwind.test.ts | 10 +- 6 files changed, 431 insertions(+), 305 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 7f17a666280..cc6934f20d1 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1,57 +1,57 @@ import Phaser from "phaser"; -import UI from "./ui/ui"; -import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon"; -import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species"; +import UI from "#app/ui/ui"; +import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "#app/data/pokemon-species"; import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils"; -import * as Utils from "./utils"; +import * as Utils from "#app/utils"; import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier"; -import { PokeballType } from "./data/pokeball"; -import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims"; -import { Phase } from "./phase"; -import { initGameSpeed } from "./system/game-speed"; -import { Arena, ArenaBase } from "./field/arena"; -import { GameData } from "./system/game-data"; -import { addTextObject, getTextColor, TextStyle } from "./ui/text"; -import { allMoves } from "./data/move"; -import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "./modifier/modifier-type"; -import AbilityBar from "./ui/ability-bar"; -import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, ChangeMovePriorityAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "./data/ability"; -import Battle, { BattleType, FixedBattleConfig } from "./battle"; -import { GameMode, GameModes, getGameMode } from "./game-mode"; -import FieldSpritePipeline from "./pipelines/field-sprite"; -import SpritePipeline from "./pipelines/sprite"; -import PartyExpBar from "./ui/party-exp-bar"; -import { trainerConfigs, TrainerSlot } from "./data/trainer-config"; -import Trainer, { TrainerVariant } from "./field/trainer"; -import TrainerData from "./system/trainer-data"; +import { PokeballType } from "#app/data/pokeball"; +import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "#app/data/battle-anims"; +import { Phase } from "#app/phase"; +import { initGameSpeed } from "#app/system/game-speed"; +import { Arena, ArenaBase } from "#app/field/arena"; +import { GameData } from "#app/system/game-data"; +import { addTextObject, getTextColor, TextStyle } from "#app/ui/text"; +import { allMoves } from "#app/data/move"; +import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type"; +import AbilityBar from "#app/ui/ability-bar"; +import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "#app/data/ability"; +import Battle, { BattleType, FixedBattleConfig } from "#app/battle"; +import { GameMode, GameModes, getGameMode } from "#app/game-mode"; +import FieldSpritePipeline from "#app/pipelines/field-sprite"; +import SpritePipeline from "#app/pipelines/sprite"; +import PartyExpBar from "#app/ui/party-exp-bar"; +import { trainerConfigs, TrainerSlot } from "#app/data/trainer-config"; +import Trainer, { TrainerVariant } from "#app/field/trainer"; +import TrainerData from "#app/system/trainer-data"; import SoundFade from "phaser3-rex-plugins/plugins/soundfade"; -import { pokemonPrevolutions } from "./data/balance/pokemon-evolutions"; -import PokeballTray from "./ui/pokeball-tray"; -import InvertPostFX from "./pipelines/invert"; -import { Achv, achvs, ModifierAchv, MoneyAchv } from "./system/achv"; -import { Voucher, vouchers } from "./system/voucher"; -import { Gender } from "./data/gender"; +import { pokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; +import PokeballTray from "#app/ui/pokeball-tray"; +import InvertPostFX from "#app/pipelines/invert"; +import { Achv, achvs, ModifierAchv, MoneyAchv } from "#app/system/achv"; +import { Voucher, vouchers } from "#app/system/voucher"; +import { Gender } from "#app/data/gender"; import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin"; -import { addUiThemeOverrides } from "./ui/ui-theme"; -import PokemonData from "./system/pokemon-data"; -import { Nature } from "./data/nature"; -import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms"; -import { FormChangePhase } from "./phases/form-change-phase"; -import { getTypeRgb } from "./data/type"; -import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler"; -import CharSprite from "./ui/char-sprite"; -import DamageNumberHandler from "./field/damage-number-handler"; -import PokemonInfoContainer from "./ui/pokemon-info-container"; -import { biomeDepths, getBiomeName } from "./data/balance/biomes"; -import { SceneBase } from "./scene-base"; -import CandyBar from "./ui/candy-bar"; -import { Variant, variantData } from "./data/variant"; +import { addUiThemeOverrides } from "#app/ui/ui-theme"; +import PokemonData from "#app/system/pokemon-data"; +import { Nature } from "#app/data/nature"; +import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "#app/data/pokemon-forms"; +import { FormChangePhase } from "#app/phases/form-change-phase"; +import { getTypeRgb } from "#app/data/type"; +import PokemonSpriteSparkleHandler from "#app/field/pokemon-sprite-sparkle-handler"; +import CharSprite from "#app/ui/char-sprite"; +import DamageNumberHandler from "#app/field/damage-number-handler"; +import PokemonInfoContainer from "#app/ui/pokemon-info-container"; +import { biomeDepths, getBiomeName } from "#app/data/balance/biomes"; +import { SceneBase } from "#app/scene-base"; +import CandyBar from "#app/ui/candy-bar"; +import { Variant, variantData } from "#app/data/variant"; import { Localizable } from "#app/interfaces/locales"; import Overrides from "#app/overrides"; -import { InputsController } from "./inputs-controller"; -import { UiInputs } from "./ui-inputs"; -import { NewArenaEvent } from "./events/battle-scene"; -import { ArenaFlyout } from "./ui/arena-flyout"; +import { InputsController } from "#app/inputs-controller"; +import { UiInputs } from "#app/ui-inputs"; +import { NewArenaEvent } from "#app/events/battle-scene"; +import { ArenaFlyout } from "#app/ui/arena-flyout"; import { EaseType } from "#enums/ease-type"; import { BattleSpec } from "#enums/battle-spec"; import { BattleStyle } from "#enums/battle-style"; @@ -66,27 +66,27 @@ import { TimedEventManager } from "#app/timed-event-manager"; import { PokemonAnimType } from "#enums/pokemon-anim-type"; import i18next from "i18next"; import { TrainerType } from "#enums/trainer-type"; -import { battleSpecDialogue } from "./data/dialogue"; -import { LoadingScene } from "./loading-scene"; -import { LevelCapPhase } from "./phases/level-cap-phase"; -import { LoginPhase } from "./phases/login-phase"; -import { MessagePhase } from "./phases/message-phase"; -import { MovePhase } from "./phases/move-phase"; -import { NewBiomeEncounterPhase } from "./phases/new-biome-encounter-phase"; -import { NextEncounterPhase } from "./phases/next-encounter-phase"; -import { PokemonAnimPhase } from "./phases/pokemon-anim-phase"; -import { QuietFormChangePhase } from "./phases/quiet-form-change-phase"; -import { ReturnPhase } from "./phases/return-phase"; -import { SelectBiomePhase } from "./phases/select-biome-phase"; -import { ShowTrainerPhase } from "./phases/show-trainer-phase"; -import { SummonPhase } from "./phases/summon-phase"; -import { SwitchPhase } from "./phases/switch-phase"; -import { TitlePhase } from "./phases/title-phase"; -import { ToggleDoublePositionPhase } from "./phases/toggle-double-position-phase"; -import { TurnInitPhase } from "./phases/turn-init-phase"; -import { ShopCursorTarget } from "./enums/shop-cursor-target"; -import MysteryEncounter from "./data/mystery-encounters/mystery-encounter"; -import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "./data/mystery-encounters/mystery-encounters"; +import { battleSpecDialogue } from "#app/data/dialogue"; +import { LoadingScene } from "#app/loading-scene"; +import { LevelCapPhase } from "#app/phases/level-cap-phase"; +import { LoginPhase } from "#app/phases/login-phase"; +import { MessagePhase } from "#app/phases/message-phase"; +import { MovePhase } from "#app/phases/move-phase"; +import { NewBiomeEncounterPhase } from "#app/phases/new-biome-encounter-phase"; +import { NextEncounterPhase } from "#app/phases/next-encounter-phase"; +import { PokemonAnimPhase } from "#app/phases/pokemon-anim-phase"; +import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; +import { ReturnPhase } from "#app/phases/return-phase"; +import { SelectBiomePhase } from "#app/phases/select-biome-phase"; +import { ShowTrainerPhase } from "#app/phases/show-trainer-phase"; +import { SummonPhase } from "#app/phases/summon-phase"; +import { SwitchPhase } from "#app/phases/switch-phase"; +import { TitlePhase } from "#app/phases/title-phase"; +import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { ShopCursorTarget } from "#app/enums/shop-cursor-target"; +import MysteryEncounter from "#app/data/mystery-encounters/mystery-encounter"; +import { allMysteryEncounters, ANTI_VARIANCE_WEIGHT_MODIFIER, AVERAGE_ENCOUNTERS_PER_RUN_TARGET, BASE_MYSTERY_ENCOUNTER_SPAWN_WEIGHT, MYSTERY_ENCOUNTER_SPAWN_MAX_WEIGHT, mysteryEncountersByBiome, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; @@ -94,7 +94,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config"; import { ExpPhase } from "#app/phases/exp-phase"; import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { ExpGainsSpeed } from "./enums/exp-gains-speed"; +import { ExpGainsSpeed } from "#enums/exp-gains-speed"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -2359,17 +2359,6 @@ export default class BattleScene extends SceneBase { return false; } - pushMovePhase(movePhase: MovePhase, priorityOverride?: integer): void { - const movePriority = new Utils.IntegerHolder(priorityOverride !== undefined ? priorityOverride : movePhase.move.getMove().priority); - applyAbAttrs(ChangeMovePriorityAbAttr, movePhase.pokemon, null, false, movePhase.move.getMove(), movePriority); - const lowerPriorityPhase = this.phaseQueue.find(p => p instanceof MovePhase && p.move.getMove().priority < movePriority.value); - if (lowerPriorityPhase) { - this.phaseQueue.splice(this.phaseQueue.indexOf(lowerPriorityPhase), 0, movePhase); - } else { - this.pushPhase(movePhase); - } - } - /** * Tries to add the input phase to index before target phase in the phaseQueue, else simply calls unshiftPhase() * @param phase {@linkcode Phase} the phase to be added diff --git a/src/field/arena.ts b/src/field/arena.ts index 9d5f1eb0a4e..1e164903e9d 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -392,16 +392,16 @@ export class Arena { return true; } - isMoveWeatherCancelled(user: Pokemon, move: Move) { - return this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move); + public isMoveWeatherCancelled(user: Pokemon, move: Move): boolean { + return !!this.weather && !this.weather.isEffectSuppressed(this.scene) && this.weather.isMoveWeatherCancelled(user, move); } - isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move) { - return this.terrain && this.terrain.isMoveTerrainCancelled(user, targets, move); + public isMoveTerrainCancelled(user: Pokemon, targets: BattlerIndex[], move: Move): boolean { + return !!this.terrain && this.terrain.isMoveTerrainCancelled(user, targets, move); } - getTerrainType() : TerrainType { - return this.terrain?.terrainType || TerrainType.NONE; + public getTerrainType(): TerrainType { + return this.terrain?.terrainType ?? TerrainType.NONE; } getAttackTypeMultiplier(attackType: Type, grounded: boolean): number { diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fed91d05fd5..05567491a1a 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5027,8 +5027,12 @@ export class PokemonBattleSummonData { export class PokemonTurnData { public flinched: boolean = false; public acted: boolean = false; - public hitCount: number; - public hitsLeft: number; + public hitCount: number = 0; + /** + * - `-1` = Calculate how many hits are left + * - `0` = Move is finished + */ + public hitsLeft: number = -1; public damageDealt: number = 0; public currDamageDealt: number = 0; public damageTaken: number = 0; @@ -5114,7 +5118,7 @@ export class PokemonMove { * @param {boolean} ignoreRestrictionTags If `true`, skips the check for move restriction tags (see {@link MoveRestrictionBattlerTag}) * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. */ - isUsable(pokemon: Pokemon, ignorePp?: boolean, ignoreRestrictionTags?: boolean): boolean { + isUsable(pokemon: Pokemon, ignorePp: boolean = false, ignoreRestrictionTags: boolean = false): boolean { if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) { return false; } diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 93466babb77..b2d429a4313 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -70,7 +70,7 @@ export class MoveEffectPhase extends PokemonPhase { * resolve the move's total hit count. This block combines the * effects of the move itself, Parental Bond, and Multi-Lens to do so. */ - if (user.turnData.hitsLeft === undefined) { + if (user.turnData.hitsLeft === -1) { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 154fbbe410d..807f194bad5 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -1,5 +1,5 @@ -import BattleScene from "#app/battle-scene"; import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs, BlockRedirectAbAttr, IncreasePpAbAttr, PokemonTypeChangeAbAttr, PostMoveUsedAbAttr, RedirectMoveAbAttr } from "#app/data/ability"; import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; @@ -15,236 +15,149 @@ import { StatusEffect } from "#app/enums/status-effect"; import { MoveUsedEvent } from "#app/events/battle-scene"; import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; +import { BattlePhase } from "#app/phases/battle-phase"; +import { CommonAnimPhase } from "#app/phases/common-anim-phase"; +import { MoveEffectPhase } from "#app/phases/move-effect-phase"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; +import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import * as Utils from "#app/utils"; import i18next from "i18next"; -import { BattlePhase } from "./battle-phase"; -import { CommonAnimPhase } from "./common-anim-phase"; -import { MoveEffectPhase } from "./move-effect-phase"; -import { MoveEndPhase } from "./move-end-phase"; -import { ShowAbilityPhase } from "./show-ability-phase"; export class MovePhase extends BattlePhase { - public pokemon: Pokemon; - public move: PokemonMove; - public targets: BattlerIndex[]; + protected _pokemon: Pokemon; + protected _move: PokemonMove; + protected _targets: BattlerIndex[]; protected followUp: boolean; protected ignorePp: boolean; - protected failed: boolean; - protected cancelled: boolean; + protected failed: boolean = false; + protected cancelled: boolean = false; - constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp?: boolean, ignorePp?: boolean) { + public get pokemon(): Pokemon { + return this._pokemon; + } + + protected set pokemon(pokemon: Pokemon) { + this._pokemon = pokemon; + } + + public get move(): PokemonMove { + return this._move; + } + + protected set move(move: PokemonMove) { + this._move = move; + } + + public get targets(): BattlerIndex[] { + return this._targets; + } + + protected set targets(targets: BattlerIndex[]) { + this._targets = targets; + } + + /** + * @param followUp Indicates that the move being uses is a "follow-up" - for example, a move being used by Metronome or Dancer. + * Follow-ups bypass a few failure conditions, including flinches, sleep/paralysis/freeze and volatile status checks, etc. + */ + constructor(scene: BattleScene, pokemon: Pokemon, targets: BattlerIndex[], move: PokemonMove, followUp: boolean = false, ignorePp: boolean = false) { super(scene); this.pokemon = pokemon; this.targets = targets; this.move = move; - this.followUp = followUp ?? false; - this.ignorePp = ignorePp ?? false; - this.failed = false; - this.cancelled = false; + this.followUp = followUp; + this.ignorePp = ignorePp; } - canMove(ignoreDisableTags?: boolean): boolean { + /** + * Checks if the pokemon is active, if the move is usable, and that the move is targetting something. + * @param ignoreDisableTags `true` to not check if the move is disabled + * @returns `true` if all the checks pass + */ + public canMove(ignoreDisableTags: boolean = false): boolean { return this.pokemon.isActive(true) && this.move.isUsable(this.pokemon, this.ignorePp, ignoreDisableTags) && !!this.targets.length; } /**Signifies the current move should fail but still use PP */ - fail(): void { + public fail(): void { this.failed = true; } /**Signifies the current move should cancel and retain PP */ - cancel(): void { + public cancel(): void { this.cancelled = true; } - start() { + public start() { super.start(); console.log(Moves[this.move.moveId]); + // Check if move is unusable (e.g. because it's out of PP due to a mid-turn Spite). if (!this.canMove(true)) { - if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { // if the move PP was reduced from Spite or otherwise, the move fails + if (this.pokemon.isActive(true) && this.move.ppUsed >= this.move.getMovePp()) { this.fail(); this.showMoveText(); this.showFailedText(); } + return this.end(); } + this.pokemon.turnData.acted = true; + + // Reset hit-related turn data when starting follow-up moves (e.g. Metronomed moves, Dancer repeats) + if (this.followUp) { + this.pokemon.turnData.hitsLeft = -1; + this.pokemon.turnData.hitCount = 0; + } + + // Check move to see if arena.ignoreAbilities should be true. if (!this.followUp) { if (this.move.getMove().checkFlag(MoveFlags.IGNORE_ABILITIES, this.pokemon, null)) { this.scene.arena.setIgnoreAbilities(true, this.pokemon.getBattlerIndex()); } + } + + this.resolveRedirectTarget(); + + this.resolveCounterAttackTarget(); + + this.resolvePreMoveStatusEffects(); + + this.lapsePreMoveAndMoveTags(); + + this.resolveFinalPreMoveCancellationChecks(); + + if (this.cancelled || this.failed) { + this.handlePreMoveFailures(); } else { - this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct? - this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct? + this.useMove(); } - // Move redirection abilities (ie. Storm Drain) only support single target moves - const moveTarget = this.targets.length === 1 - ? new Utils.IntegerHolder(this.targets[0]) - : null; - if (moveTarget) { - const oldTarget = moveTarget.value; - this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, moveTarget)); - this.pokemon.getOpponents().forEach(p => { - const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; - if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { - moveTarget.value = p.getBattlerIndex(); - } - }); - //Check if this move is immune to being redirected, and restore its target to the intended target if it is. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) || this.move.getMove().hasAttr(BypassRedirectAttr))) { - //If an ability prevented this move from being redirected, display its ability pop up. - if ((this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr) && !this.move.getMove().hasAttr(BypassRedirectAttr)) && oldTarget !== moveTarget.value) { - this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); - } - moveTarget.value = oldTarget; - } - this.targets[0] = moveTarget.value; + this.end(); + } + + /** Check for cancellation edge cases - no targets remaining, or {@linkcode Moves.NONE} is in the queue */ + protected resolveFinalPreMoveCancellationChecks() { + const targets = this.getActiveTargetPokemon(); + const moveQueue = this.pokemon.getMoveQueue(); + + if (targets.length === 0 || (moveQueue.length && moveQueue[0].move === Moves.NONE)) { + this.showFailedText(); + this.cancelled = true; } + } - // Check for counterattack moves to switch target - if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { - if (this.pokemon.turnData.attacksReceived.length) { - const attack = this.pokemon.turnData.attacksReceived[0]; - this.targets[0] = attack.sourceBattlerIndex; - - // account for metal burst and comeuppance hitting remaining targets in double battles - // counterattack will redirect to remaining ally if original attacker faints - if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { - if (this.scene.getField()[this.targets[0]].hp === 0) { - const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); - //@ts-ignore - this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore - } - } - } - if (this.targets[0] === BattlerIndex.ATTACKER) { - this.fail(); // Marks the move as failed for later in doMove - this.showMoveText(); - this.showFailedText(); - } - } - - const targets = this.scene.getField(true).filter(p => { - if (this.targets.indexOf(p.getBattlerIndex()) > -1) { - return true; - } - return false; - }); - - const doMove = () => { - this.pokemon.turnData.acted = true; // Record that the move was attempted, even if it fails - - this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); - - let ppUsed = 1; - // Filter all opponents to include only those this move is targeting - const targetedOpponents = this.pokemon.getOpponents().filter(o => this.targets.includes(o.getBattlerIndex())); - for (const opponent of targetedOpponents) { - if (this.move.ppUsed + ppUsed >= this.move.getMovePp()) { // If we're already at max PP usage, stop checking - break; - } - if (opponent.hasAbilityWithAttr(IncreasePpAbAttr)) { // Accounting for abilities like Pressure - ppUsed++; - } - } - - if (!this.followUp && this.canMove() && !this.cancelled) { - this.pokemon.lapseTags(BattlerTagLapseType.MOVE); - } - - const moveQueue = this.pokemon.getMoveQueue(); - if (this.cancelled || this.failed) { - if (this.failed) { - this.move.usePp(ppUsed); // Only use PP if the move failed - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); - } - - // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock - this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); - - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. - this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); - moveQueue.shift(); // Remove the second turn of charge moves - return this.end(); - } - - this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger); - - if (this.move.moveId) { - this.showMoveText(); - } - - // This should only happen when there are no valid targets left on the field - if ((moveQueue.length && moveQueue[0].move === Moves.NONE) || !targets.length) { - this.showFailedText(); - this.cancel(); - - // Record a failed move so Abilities like Truant don't trigger next turn and soft-lock - this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); - - this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); // Remove any tags from moves like Fly/Dive/etc. - this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); - - moveQueue.shift(); - return this.end(); - } - - if ((!moveQueue.length || !moveQueue.shift()?.ignorePP) && !this.ignorePp) { // using .shift here clears out two turn moves once they've been used - this.move.usePp(ppUsed); - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); - } - - if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { - this.scene.currentBattle.lastMove = this.move.moveId; - } - - // Assume conditions affecting targets only apply to moves with a single target - let success = this.move.getMove().applyConditions(this.pokemon, targets[0], this.move.getMove()); - const cancelled = new Utils.BooleanHolder(false); - let failedText = this.move.getMove().getFailedText(this.pokemon, targets[0], this.move.getMove(), cancelled); - if (success && this.scene.arena.isMoveWeatherCancelled(this.pokemon, this.move.getMove())) { - success = false; - } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { - success = false; - if (failedText === null) { - failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct? - } - } - - /** - * Trigger pokemon type change before playing the move animation - * Will still change the user's type when using Roar, Whirlwind, Trick-or-Treat, and Forest's Curse, - * regardless of whether the move successfully executes or not. - */ - if (success || [ Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE ].includes(this.move.moveId)) { - applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); - } - - if (success) { - this.scene.unshiftPhase(this.getEffectPhase()); - } else { - this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); - if (!cancelled.value) { - this.showFailedText(failedText); - } - } - // Checks if Dancer ability is triggered - if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) { - // Pokemon with Dancer can be on either side of the battle so we check in both cases - this.scene.getPlayerField().forEach(pokemon => { - applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); - }); - this.scene.getEnemyField().forEach(pokemon => { - applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); - }); - } - this.end(); - }; + public getActiveTargetPokemon() { + return this.scene.getField(true).filter(p => this.targets.includes(p.getBattlerIndex())); + } + /** + * Handles {@link StatusEffect.SLEEP Sleep}/{@link StatusEffect.PARALYSIS Paralysis}/{@link StatusEffect.FREEZE Freeze} rolls and side effects. + */ + protected resolvePreMoveStatusEffects() { if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) { this.pokemon.status.incrementTurn(); let activated = false; @@ -273,25 +186,257 @@ export class MovePhase extends BattlePhase { if (activated) { this.scene.queueMessage(getStatusEffectActivationText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); this.scene.unshiftPhase(new CommonAnimPhase(this.scene, this.pokemon.getBattlerIndex(), undefined, CommonAnim.POISON + (this.pokemon.status.effect - 1))); - doMove(); - } else { - if (healed) { - this.scene.queueMessage(getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); - this.pokemon.resetStatus(); - this.pokemon.updateInfo(); - } - doMove(); + } else if (healed) { + this.scene.queueMessage(getStatusEffectHealText(this.pokemon.status.effect, getPokemonNameWithAffix(this.pokemon))); + this.pokemon.resetStatus(); + this.pokemon.updateInfo(); } - } else { - doMove(); } } - getEffectPhase(): MoveEffectPhase { - return new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move); + /** + * Lapse {@linkcode BattlerTagLapseType.PRE_MOVE PRE_MOVE} tags that trigger before a move is used, regardless of whether or not it failed. + * Also lapse {@linkcode BattlerTagLapseType.MOVE MOVE} tags if the move should be successful. + */ + protected lapsePreMoveAndMoveTags() { + this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); + + // TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked? + if (!this.followUp && this.canMove() && !this.cancelled) { + this.pokemon.lapseTags(BattlerTagLapseType.MOVE); + } } - showMoveText(): void { + protected useMove() { + const targets = this.getActiveTargetPokemon(); + const moveQueue = this.pokemon.getMoveQueue(); + + // form changes happen even before we know that the move wll execute. + this.scene.triggerPokemonFormChange(this.pokemon, SpeciesFormChangePreMoveTrigger); + + this.showMoveText(); + + // TODO: Clean up implementation of two-turn moves. + if (moveQueue.length > 0) { // Using .shift here clears out two turn moves once they've been used + this.ignorePp = moveQueue.shift()?.ignorePP ?? false; + } + + // "commit" to using the move, deducting PP. + if (!this.ignorePp) { + const ppUsed = 1 + this.getPpIncreaseFromPressure(targets); + + this.move.usePp(ppUsed); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + } + + // Update the battle's "last move" pointer, unless we're currently mimicking a move. + if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) { + this.scene.currentBattle.lastMove = this.move.moveId; + } + + /** + * Determine if the move is successful (meaning that its damage/effects can be attempted) + * by checking that all of the following are true: + * - Conditional attributes of the move are all met + * - The target's `ForceSwitchOutImmunityAbAttr` is not triggered (see {@linkcode Move.prototype.applyConditions}) + * - Weather does not block the move + * - Terrain does not block the move + * + * TODO: These steps are straightforward, but the implementation below is extremely convoluted. + */ + + const move = this.move.getMove(); + + /** + * Move conditions assume the move has a single target + * TODO: is this sustainable? + */ + const passesConditions = move.applyConditions(this.pokemon, targets[0], move); + const failedDueToWeather: boolean = this.scene.arena.isMoveWeatherCancelled(this.pokemon, move); + const failedDueToTerrain: boolean = this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, move); + + const success = passesConditions && !failedDueToWeather && !failedDueToTerrain; + + /** + * If the move has not failed, trigger ability-based user type changes and then execute it. + * + * Notably, Roar, Whirlwind, Trick-or-Treat, and Forest's Curse will trigger these type changes even + * if the move fails. + */ + if (success) { + applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); + this.scene.unshiftPhase(new MoveEffectPhase(this.scene, this.pokemon.getBattlerIndex(), this.targets, this.move)); + + } else { + if ([ Moves.ROAR, Moves.WHIRLWIND, Moves.TRICK_OR_TREAT, Moves.FORESTS_CURSE ].includes(this.move.moveId)) { + applyPreAttackAbAttrs(PokemonTypeChangeAbAttr, this.pokemon, null, this.move.getMove()); + } + + this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); + + let failedText: string | undefined; + const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new Utils.BooleanHolder(false)); + + if (failureMessage) { + failedText = failureMessage; + } else if (failedDueToTerrain) { + failedText = getTerrainBlockMessage(this.pokemon, this.scene.arena.getTerrainType()); + } + + this.showFailedText(failedText); + } + + // Handle Dancer, which triggers immediately after a move is used (rather than waiting on `this.end()`). + // Note that the `!this.followUp` check here prevents an infinite Dancer loop. + if (this.move.getMove().hasFlag(MoveFlags.DANCE_MOVE) && !this.followUp) { + this.scene.getField(true).forEach(pokemon => { + applyPostMoveUsedAbAttrs(PostMoveUsedAbAttr, pokemon, this.move, this.pokemon, this.targets); + }); + } + } + + /** + * Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`, + * then ends the phase. + */ + public end() { + if (!this.followUp && this.canMove()) { + this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); + } + + super.end(); + } + + /** + * Applies PP increasing abilities (currently only {@link Abilities.PRESSURE Pressure}) if they exist on the target pokemon. + * Note that targets must include only active pokemon. + * + * TODO: This hardcodes the PP increase at 1 per opponent, rather than deferring to the ability. + */ + public getPpIncreaseFromPressure(targets: Pokemon[]) { + const foesWithPressure = this.pokemon.getOpponents().filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr(IncreasePpAbAttr)); + return foesWithPressure.length; + } + + /** + * Modifies `this.targets` in place, based upon: + * - Move redirection abilities, effects, etc. + * - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`). + */ + protected resolveRedirectTarget() { + if (this.targets.length === 1) { + const currentTarget = this.targets[0]; + const redirectTarget = new Utils.NumberHolder(currentTarget); + + // check move redirection abilities of every pokemon *except* the user. + this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget)); + + // check for center-of-attention tags (note that this will override redirect abilities) + this.pokemon.getOpponents().forEach(p => { + const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; + + // TODO: don't hardcode this interaction. + // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) + if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { + redirectTarget.value = p.getBattlerIndex(); + } + }); + + if (currentTarget !== redirectTarget.value) { + if (this.move.getMove().hasAttr(BypassRedirectAttr)) { + redirectTarget.value = currentTarget; + + } else if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { + redirectTarget.value = currentTarget; + this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); + } + + this.targets[0] = redirectTarget.value; + } + } + } + + /** + * Counter-attacking moves pass in `[`{@linkcode BattlerIndex.ATTACKER}`]` into the constructor's `targets` param. + * This function modifies `this.targets` to reflect the actual battler index of the user's last + * attacker. + * + * If there is no last attacker, or they are no longer on the field, a message is displayed and the + * move is marked for failure. + */ + protected resolveCounterAttackTarget() { + if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { + if (this.pokemon.turnData.attacksReceived.length) { + const attacker = this.pokemon.scene.getPokemonById(this.pokemon.turnData.attacksReceived[0].sourceId); + + if (attacker?.isActive(true)) { + this.targets[0] = attacker.getBattlerIndex(); + } + + // account for metal burst and comeuppance hitting remaining targets in double battles + // counterattack will redirect to remaining ally if original attacker faints + if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { + if (this.scene.getField()[this.targets[0]].hp === 0) { + const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); + this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex() ?? BattlerIndex.ATTACKER; + } + } + } + + if (this.targets[0] === BattlerIndex.ATTACKER) { + this.fail(); + this.showMoveText(); + this.showFailedText(); + } + } + } + + /** + * Handles the case where the move was cancelled or failed: + * - Uses PP if the move failed (not cancelled) and should use PP (failed moves are not affected by {@link Abilities.PRESSURE Pressure}) + * - Records a cancelled OR failed move in move history, so abilities like {@link Abilities.TRUANT Truant} don't trigger on the + * next turn and soft-lock. + * - Lapses `MOVE_EFFECT` tags: + * - Semi-invulnerable battler tags (Fly/Dive/etc.) are intended to lapse on move effects, but also need + * to lapse on move failure/cancellation. + * + * TODO: ...this seems weird. + * - Lapses `AFTER_MOVE` tags: + * - This handles the effects of {@link Moves.SUBSTITUTE Substitute} + * - Removes the second turn of charge moves + * + * TODO: handle charge moves more gracefully + */ + protected handlePreMoveFailures() { + if (this.cancelled || this.failed) { + if (this.failed) { + const ppUsed = this.ignorePp ? 0 : 1; + + if (ppUsed) { + this.move.usePp(); + } + + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + } + + this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); + + this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); + this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE); + + this.pokemon.getMoveQueue().shift(); + } + } + + /** + * Displays the move's usage text to the player, unless it's a charge turn (ie: {@link Moves.SOLAR_BEAM Solar Beam}), + * the pokemon is on a recharge turn (ie: {@link Moves.HYPER_BEAM Hyper Beam}), or a 2-turn move was interrupted (ie: {@link Moves.FLY Fly}). + */ + protected showMoveText(): void { + if (this.move.moveId === Moves.NONE) { + return; + } + if (this.move.getMove().hasAttr(ChargeAttr)) { const lastMove = this.pokemon.getLastXMoves() as TurnMove[]; if (!lastMove.length || lastMove[0].move !== this.move.getMove().id || lastMove[0].result !== MoveResult.OTHER) { @@ -311,18 +456,10 @@ export class MovePhase extends BattlePhase { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), moveName: this.move.getName() }), 500); - applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here? + applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents()[0], this.move.getMove()); } - showFailedText(failedText: string | null = null): void { - this.scene.queueMessage(failedText || i18next.t("battle:attackFailed")); - } - - end() { - if (!this.followUp && this.canMove()) { - this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); - } - - super.end(); + protected showFailedText(failedText?: string): void { + this.scene.queueMessage(failedText ?? i18next.t("battle:attackFailed")); } } diff --git a/src/test/moves/whirlwind.test.ts b/src/test/moves/whirlwind.test.ts index c8ad29a23d7..cc31b2591a2 100644 --- a/src/test/moves/whirlwind.test.ts +++ b/src/test/moves/whirlwind.test.ts @@ -1,12 +1,11 @@ -import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/move"; import { BattlerTagType } from "#app/enums/battler-tag-type"; +import { MoveResult } from "#app/field/pokemon"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - Whirlwind", () => { let phaserGame: Phaser.Game; @@ -40,14 +39,11 @@ describe("Moves - Whirlwind", () => { await game.classicMode.startBattle([ Species.STARAPTOR ]); const staraptor = game.scene.getPlayerPokemon()!; - const whirlwind = allMoves[Moves.WHIRLWIND]; - vi.spyOn(whirlwind, "getFailedText"); game.move.select(move); - await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.toNextTurn(); expect(staraptor.findTag((t) => t.tagType === BattlerTagType.FLYING)).toBeDefined(); - expect(whirlwind.getFailedText).toHaveBeenCalledOnce(); + expect(game.scene.getEnemyPokemon()!.getLastXMoves(1)[0].result).toBe(MoveResult.MISS); }); }); From 2c97b2bda2cfdec84a2561d8ff47e9443c6a93cd Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:51:29 -0400 Subject: [PATCH 05/70] [Sprite] Fix variant Farigiraf icon names (#4572) --- public/images/pokemon_icons_9v.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/images/pokemon_icons_9v.json b/public/images/pokemon_icons_9v.json index 83a47f101fa..06909a8298f 100644 --- a/public/images/pokemon_icons_9v.json +++ b/public/images/pokemon_icons_9v.json @@ -853,14 +853,14 @@ "spriteSourceSize": { "x": 7, "y": 2, "w": 27, "h": 26 }, "sourceSize": { "w": 40, "h": 30 } }, - "981_2.png": { + "981_2": { "frame": { "x": 108, "y": 87, "w": 23, "h": 30 }, "rotated": false, "trimmed": true, "spriteSourceSize": { "x": 9, "y": 0, "w": 23, "h": 30 }, "sourceSize": { "w": 40, "h": 30 } }, - "981_3.png": { + "981_3": { "frame": { "x": 246, "y": 86, "w": 23, "h": 30 }, "rotated": false, "trimmed": true, From 75bd730c0434d6a96c69171285cd53304015abfe Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:55:37 -0400 Subject: [PATCH 06/70] [Balance] Fix TM compatibility on forms, Tera Blast on Indigo Disk mons (#4568) * Fix TM compatibility on forms, Tera Blast on Indigo Disk mons * Additional single strike moves --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/balance/tms.ts | 548 +++++++++------------------------------- 1 file changed, 121 insertions(+), 427 deletions(-) diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts index e08b677c30c..1a509637e05 100644 --- a/src/data/balance/tms.ts +++ b/src/data/balance/tms.ts @@ -1107,12 +1107,7 @@ export const tmSpecies: TmSpecies = { Species.QUILLADIN, Species.CHESNAUGHT, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.FLETCHLING, @@ -2878,12 +2873,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.FLETCHLING, @@ -3910,7 +3900,6 @@ export const tmSpecies: TmSpecies = { Species.YAMPER, Species.BOLTUND, Species.ZAMAZENTA, - Species.URSHIFU, Species.ZARUDE, Species.GLASTRIER, Species.WYRDEER, @@ -3940,6 +3929,10 @@ export const tmSpecies: TmSpecies = { Species.ALOLA_NINETALES, Species.ALOLA_PERSIAN, Species.ALOLA_GOLEM, + [ + Species.URSHIFU, + "single-strike", + ], Species.HISUI_GROWLITHE, Species.HISUI_ARCANINE, Species.HISUI_TYPHLOSION, @@ -6987,14 +6980,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -8301,7 +8287,6 @@ export const tmSpecies: TmSpecies = { [ Species.WORMADAM, "sandy", - "trash", ], Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSLASH, @@ -8612,12 +8597,7 @@ export const tmSpecies: TmSpecies = { Species.CHESNAUGHT, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.LITLEO, @@ -9406,14 +9386,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -9766,14 +9739,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.VIVILLON, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.ESPURR, Species.MEOWSTIC, @@ -11147,14 +11113,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -13657,12 +13616,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.FLETCHLING, Species.FLETCHINDER, Species.TALONFLAME, @@ -15326,14 +15280,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -16934,14 +16881,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -18483,14 +18423,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -20250,14 +20183,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -21583,12 +21509,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.LITLEO, @@ -22516,7 +22437,6 @@ export const tmSpecies: TmSpecies = { [ Species.WORMADAM, "sandy", - "trash", ], Species.ALOLA_DIGLETT, Species.ALOLA_DUGTRIO, @@ -22691,14 +22611,7 @@ export const tmSpecies: TmSpecies = { Species.CHESNAUGHT, Species.VIVILLON, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -23402,12 +23315,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.FLETCHLING, @@ -24134,12 +24042,7 @@ export const tmSpecies: TmSpecies = { Species.KELDEO, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.PANCHAM, Species.PANGORO, Species.HONEDGE, @@ -24842,14 +24745,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -25712,14 +25608,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -26695,14 +26584,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -27845,14 +27727,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -28911,14 +28786,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -29514,14 +29382,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.VIVILLON, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.ESPURR, Species.MEOWSTIC, @@ -31408,14 +31269,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -32327,14 +32181,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -33037,14 +32884,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -33471,7 +33311,6 @@ export const tmSpecies: TmSpecies = { Species.ARCTOVISH, Species.ZACIAN, Species.ZAMAZENTA, - Species.URSHIFU, Species.ZARUDE, Species.REGIDRAGO, Species.GLASTRIER, @@ -33522,6 +33361,10 @@ export const tmSpecies: TmSpecies = { Species.ALOLA_MUK, Species.GALAR_MEOWTH, Species.GALAR_STUNFISK, + [ + Species.URSHIFU, + "single-strike", + ], [ Species.CALYREX, "ice", @@ -36644,14 +36487,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -37389,14 +37225,7 @@ export const tmSpecies: TmSpecies = { Species.BUNNELBY, Species.DIGGERSBY, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -38266,23 +38095,11 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -39323,12 +39140,7 @@ export const tmSpecies: TmSpecies = { Species.CHESPIN, Species.QUILLADIN, Species.CHESNAUGHT, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.SKIDDO, @@ -40356,7 +40168,10 @@ export const tmSpecies: TmSpecies = { Species.FENNEKIN, Species.BRAIXEN, Species.DELPHOX, - Species.MEOWSTIC, + [ + Species.MEOWSTIC, + "male", + ], Species.KLEFKI, Species.PHANTUMP, Species.TREVENANT, @@ -41805,7 +41620,6 @@ export const tmSpecies: TmSpecies = { [ Species.WORMADAM, "sandy", - "trash", ], Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSLASH, @@ -43701,12 +43515,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.SKIDDO, @@ -44160,14 +43969,7 @@ export const tmSpecies: TmSpecies = { Species.QUILLADIN, Species.CHESNAUGHT, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -44377,14 +44179,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.VIVILLON, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.ESPURR, Species.MEOWSTIC, @@ -45256,6 +45051,10 @@ export const tmSpecies: TmSpecies = { Species.IRON_CROWN, Species.TERAPAGOS, Species.ALOLA_EXEGGUTOR, + [ + Species.INDEEDEE, + "male", + ], ], [Moves.GYRO_BALL]: [ Species.SQUIRTLE, @@ -47501,12 +47300,7 @@ export const tmSpecies: TmSpecies = { Species.ACCELGOR, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.SKRELP, Species.DRAGALGE, Species.MAREANIE, @@ -48143,7 +47937,6 @@ export const tmSpecies: TmSpecies = { Species.RUNERIGUS, Species.MORPEKO, Species.DURALUDON, - Species.URSHIFU, Species.ZARUDE, Species.SPECTRIER, Species.OVERQWIL, @@ -48179,6 +47972,10 @@ export const tmSpecies: TmSpecies = { Species.GALAR_WEEZING, Species.GALAR_MOLTRES, Species.GALAR_YAMASK, + [ + Species.URSHIFU, + "single-strike", + ], [ Species.CALYREX, "shadow", @@ -48456,14 +48253,7 @@ export const tmSpecies: TmSpecies = { Species.QUILLADIN, Species.CHESNAUGHT, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -49622,7 +49412,10 @@ export const tmSpecies: TmSpecies = { Species.TORTERRA, Species.BUDEW, Species.ROSERADE, - Species.WORMADAM, + [ + Species.WORMADAM, + "plant", + ], Species.MOTHIM, Species.CHERUBI, Species.CHERRIM, @@ -49698,14 +49491,7 @@ export const tmSpecies: TmSpecies = { Species.CHESNAUGHT, Species.VIVILLON, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -52635,7 +52421,10 @@ export const tmSpecies: TmSpecies = { Species.TORTERRA, Species.BUDEW, Species.ROSERADE, - Species.WORMADAM, + [ + Species.WORMADAM, + "plant", + ], Species.SNOVER, Species.ABOMASNOW, Species.TANGROWTH, @@ -53751,7 +53540,10 @@ export const tmSpecies: TmSpecies = { Species.BIBAREL, Species.BUDEW, Species.ROSERADE, - Species.WORMADAM, + [ + Species.WORMADAM, + "plant", + ], Species.PACHIRISU, Species.CHERUBI, Species.CHERRIM, @@ -53863,14 +53655,7 @@ export const tmSpecies: TmSpecies = { Species.BUNNELBY, Species.DIGGERSBY, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -55590,12 +55375,7 @@ export const tmSpecies: TmSpecies = { Species.CHESPIN, Species.QUILLADIN, Species.CHESNAUGHT, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.PANCHAM, Species.PANGORO, Species.HELIOPTILE, @@ -55877,7 +55657,6 @@ export const tmSpecies: TmSpecies = { Species.MR_RIME, Species.MORPEKO, Species.DURALUDON, - Species.URSHIFU, Species.SPECTRIER, Species.MEOWSCARADA, Species.SQUAWKABILLY, @@ -55918,6 +55697,10 @@ export const tmSpecies: TmSpecies = { Species.GALAR_MOLTRES, Species.GALAR_SLOWKING, Species.GALAR_STUNFISK, + [ + Species.URSHIFU, + "single-strike", + ], [ Species.CALYREX, "shadow", @@ -56577,14 +56360,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -57019,14 +56795,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.PANCHAM, Species.PANGORO, @@ -57354,14 +57123,7 @@ export const tmSpecies: TmSpecies = { Species.BRAIXEN, Species.DELPHOX, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.ESPURR, Species.MEOWSTIC, @@ -58997,7 +58759,6 @@ export const tmSpecies: TmSpecies = { [ Species.WORMADAM, "sandy", - "trash", ], Species.ALOLA_SANDSHREW, Species.ALOLA_SANDSLASH, @@ -60070,7 +59831,6 @@ export const tmSpecies: TmSpecies = { Species.DURALUDON, Species.ZACIAN, Species.ZAMAZENTA, - Species.URSHIFU, Species.ZARUDE, Species.GLASTRIER, Species.SPECTRIER, @@ -60116,6 +59876,10 @@ export const tmSpecies: TmSpecies = { Species.HISUI_ZOROARK, Species.HISUI_BRAVIARY, Species.BLOODMOON_URSALUNA, + [ + Species.URSHIFU, + "single-strike", + ], ], [Moves.PHANTOM_FORCE]: [ Species.HAUNTER, @@ -60446,14 +60210,7 @@ export const tmSpecies: TmSpecies = { Species.QUILLADIN, Species.CHESNAUGHT, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -60529,16 +60286,12 @@ export const tmSpecies: TmSpecies = { Species.WHIMSICOTT, Species.ALOMOMOLA, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, - Species.MEOWSTIC, + [ + Species.MEOWSTIC, + "male", + ], Species.SPRITZEE, Species.AROMATISSE, Species.SYLVEON, @@ -61402,14 +61155,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -61899,14 +61645,7 @@ export const tmSpecies: TmSpecies = { Species.MELOETTA, Species.DELPHOX, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SPRITZEE, Species.AROMATISSE, @@ -62617,7 +62356,6 @@ export const tmSpecies: TmSpecies = { Species.SIRFETCHD, Species.FALINKS, Species.PINCURCHIN, - Species.URSHIFU, Species.ZARUDE, Species.GLASTRIER, Species.TAROUNTULA, @@ -62647,6 +62385,10 @@ export const tmSpecies: TmSpecies = { Species.GALAR_ZAPDOS, Species.GALAR_CORSOLA, Species.GALAR_LINOONE, + [ + Species.URSHIFU, + "single-strike", + ], [ Species.CALYREX, "ice", @@ -63541,12 +63283,7 @@ export const tmSpecies: TmSpecies = { Species.KELDEO, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.INKAY, Species.MALAMAR, Species.BINACLE, @@ -64755,7 +64492,6 @@ export const tmSpecies: TmSpecies = { Species.OBSTAGOON, Species.PERRSERKER, Species.MORPEKO, - Species.URSHIFU, Species.ZARUDE, Species.GLASTRIER, Species.SPECTRIER, @@ -64803,6 +64539,10 @@ export const tmSpecies: TmSpecies = { Species.GALAR_LINOONE, Species.GALAR_DARMANITAN, Species.GALAR_STUNFISK, + [ + Species.URSHIFU, + "single-strike", + ], [ Species.CALYREX, "ice", @@ -65933,12 +65673,7 @@ export const tmSpecies: TmSpecies = { Species.DELPHOX, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.BUNNELBY, Species.DIGGERSBY, Species.FLETCHLING, @@ -65950,14 +65685,7 @@ export const tmSpecies: TmSpecies = { Species.LITLEO, Species.PYROAR, Species.FLABEBE, - [ - Species.FLOETTE, - "red", - "yellow", - "orange", - "blue", - "white", - ], + Species.FLOETTE, Species.FLORGES, Species.SKIDDO, Species.GOGOAT, @@ -66291,6 +66019,13 @@ export const tmSpecies: TmSpecies = { Species.MUNKIDORI, Species.FEZANDIPITI, Species.OGERPON, + Species.ARCHALUDON, + Species.HYDRAPPLE, + Species.GOUGING_FIRE, + Species.RAGING_BOLT, + Species.IRON_BOULDER, + Species.IRON_CROWN, + Species.PECHARUNT, Species.GALAR_MEOWTH, Species.GALAR_SLOWPOKE, Species.GALAR_SLOWBRO, @@ -66429,16 +66164,8 @@ export const tmSpecies: TmSpecies = { Species.PIPLUP, Species.PRINPLUP, Species.EMPOLEON, - [ - Species.SHELLOS, - "east", - "west", - ], - [ - Species.GASTRODON, - "east", - "west", - ], + Species.SHELLOS, + Species.GASTRODON, Species.MISMAGIUS, Species.HAPPINY, Species.SNOVER, @@ -66456,25 +66183,11 @@ export const tmSpecies: TmSpecies = { Species.CUBCHOO, Species.BEARTIC, Species.CRYOGONAL, - [ - Species.TORNADUS, - "incarnate", - "therian", - ], - [ - Species.KYUREM, - "", - "black", - "white", - ], + Species.TORNADUS, + Species.KYUREM, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.SKRELP, Species.DRAGALGE, Species.BERGMITE, @@ -66482,11 +66195,7 @@ export const tmSpecies: TmSpecies = { Species.DIANCIE, Species.PRIMARINA, Species.CRABOMINABLE, - [ - Species.MAGEARNA, - "", - "original", - ], + Species.MAGEARNA, Species.INTELEON, Species.FROSMOTH, Species.EISCUE, @@ -66779,12 +66488,7 @@ export const tmSpecies: TmSpecies = { Species.CHESNAUGHT, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.LITLEO, Species.PYROAR, Species.FLABEBE, @@ -67007,23 +66711,14 @@ export const tmSpecies: TmSpecies = { Species.WEAVILE, Species.GLACEON, Species.FROSLASS, - [ - Species.PALKIA, - "", - "origin", - ], + Species.PALKIA, Species.PHIONE, Species.MANAPHY, Species.ARCEUS, Species.OSHAWOTT, Species.DEWOTT, Species.SAMUROTT, - [ - Species.BASCULIN, - "red-striped", - "blue-striped", - "white-striped", - ], + Species.BASCULIN, Species.MINCCINO, Species.CINCCINO, Species.DUCKLETT, @@ -67036,12 +66731,7 @@ export const tmSpecies: TmSpecies = { Species.KELDEO, Species.FROAKIE, Species.FROGADIER, - [ - Species.GRENINJA, - "", - "battle-bond", - "ash", - ], + Species.GRENINJA, Species.FLABEBE, Species.FLOETTE, Species.FLORGES, @@ -67304,6 +66994,10 @@ export const tmSpecies: TmSpecies = { Species.FEZANDIPITI, Species.ALOLA_RAICHU, Species.ETERNAL_FLOETTE, + [ + Species.INDEEDEE, + "female", + ], ], [Moves.TEMPER_FLARE]: [ Species.CHARMANDER, From 0bd4d6c86bc85907c6194bc723f0220fb855f6ab Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:20:37 -0700 Subject: [PATCH 07/70] [Move] Fully Implement the Pledge Moves (#4511) * Implement Fire/Grass Pledge combo * Add other Pledge combo effects (untested) * Fix missing enums * Pledge moves integration tests * Add turn order manipulation + more tests * Safeguarding against weird Instruct interactions * Update src/test/moves/pledge_moves.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Fix style issues * Delete arena-tag.json * Update package-lock.json * Use `instanceof` for all arg type inference * Add Pledge Move sleep test * Fix linting * Fix linting Apparently GitHub has a limit on how many errors it will show * Pledges now only bypass redirection from abilities --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- package-lock.json | 1 + src/data/arena-tag.ts | 82 +++++++ src/data/move.ts | 203 ++++++++++++++++- src/enums/arena-tag-type.ts | 3 + src/field/pokemon.ts | 12 +- src/phases/move-phase.ts | 16 +- src/test/moves/pledge_moves.test.ts | 337 ++++++++++++++++++++++++++++ 7 files changed, 642 insertions(+), 12 deletions(-) create mode 100644 src/test/moves/pledge_moves.test.ts diff --git a/package-lock.json b/package-lock.json index f633d427d6d..ee2708b38f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "pokemon-rogue-battle", "version": "1.0.4", + "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", "crypto-js": "^4.2.0", diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index abe443cdfa6..b75d23b48d8 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -19,6 +19,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; +import { CommonAnimPhase } from "#app/phases/common-anim-phase"; export enum ArenaTagSide { BOTH, @@ -1025,6 +1026,81 @@ class ImprisonTag extends ArenaTrapTag { } } +/** + * Arena Tag implementing the "sea of fire" effect from the combination + * of {@link https://bulbapedia.bulbagarden.net/wiki/Fire_Pledge_(move) | Fire Pledge} + * and {@link https://bulbapedia.bulbagarden.net/wiki/Grass_Pledge_(move) | Grass Pledge}. + * Damages all non-Fire-type Pokemon on the given side of the field at the end + * of each turn for 4 turns. + */ +class FireGrassPledgeTag extends ArenaTag { + constructor(sourceId: number, side: ArenaTagSide) { + super(ArenaTagType.FIRE_GRASS_PLEDGE, 4, Moves.FIRE_PLEDGE, sourceId, side); + } + + override onAdd(arena: Arena): void { + // "A sea of fire enveloped your/the opposing team!" + arena.scene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } + + override lapse(arena: Arena): boolean { + const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER) + ? arena.scene.getPlayerField() + : arena.scene.getEnemyField(); + + field.filter(pokemon => !pokemon.isOfType(Type.FIRE)).forEach(pokemon => { + // "{pokemonNameWithAffix} was hurt by the sea of fire!" + pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + // TODO: Replace this with a proper animation + pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM)); + pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); + }); + + return super.lapse(arena); + } +} + +/** + * Arena Tag implementing the "rainbow" effect from the combination + * of {@link https://bulbapedia.bulbagarden.net/wiki/Water_Pledge_(move) | Water Pledge} + * and {@link https://bulbapedia.bulbagarden.net/wiki/Fire_Pledge_(move) | Fire Pledge}. + * Doubles the secondary effect chance of moves from Pokemon on the + * given side of the field for 4 turns. + */ +class WaterFirePledgeTag extends ArenaTag { + constructor(sourceId: number, side: ArenaTagSide) { + super(ArenaTagType.WATER_FIRE_PLEDGE, 4, Moves.WATER_PLEDGE, sourceId, side); + } + + override onAdd(arena: Arena): void { + // "A rainbow appeared in the sky on your/the opposing team's side!" + arena.scene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } + + override apply(arena: Arena, args: any[]): boolean { + const moveChance = args[0] as Utils.NumberHolder; + moveChance.value *= 2; + return true; + } +} + +/** + * Arena Tag implementing the "swamp" effect from the combination + * of {@link https://bulbapedia.bulbagarden.net/wiki/Grass_Pledge_(move) | Grass Pledge} + * and {@link https://bulbapedia.bulbagarden.net/wiki/Water_Pledge_(move) | Water Pledge}. + * Quarters the Speed of Pokemon on the given side of the field for 4 turns. + */ +class GrassWaterPledgeTag extends ArenaTag { + constructor(sourceId: number, side: ArenaTagSide) { + super(ArenaTagType.GRASS_WATER_PLEDGE, 4, Moves.GRASS_PLEDGE, sourceId, side); + } + + override onAdd(arena: Arena): void { + // "A swamp enveloped your/the opposing team!" + arena.scene.queueMessage(i18next.t(`arenaTag:grassWaterPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); + } +} + export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: @@ -1076,6 +1152,12 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new SafeguardTag(turnCount, sourceId, side); case ArenaTagType.IMPRISON: return new ImprisonTag(sourceId, side); + case ArenaTagType.FIRE_GRASS_PLEDGE: + return new FireGrassPledgeTag(sourceId, side); + case ArenaTagType.WATER_FIRE_PLEDGE: + return new WaterFirePledgeTag(sourceId, side); + case ArenaTagType.GRASS_WATER_PLEDGE: + return new GrassWaterPledgeTag(sourceId, side); default: return null; } diff --git a/src/data/move.ts b/src/data/move.ts index 2225a457a42..62ac36b28ad 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1010,7 +1010,14 @@ export class MoveEffectAttr extends MoveAttr { */ getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer { const moveChance = new Utils.NumberHolder(move.chance); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility); + + if (!move.hasAttr(FlinchAttr) || moveChance.value <= move.chance) { + const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + user.scene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, moveChance); + } + if (!selfEffect) { applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, target, user, null, null, false, moveChance); } @@ -2687,6 +2694,62 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { } } +/** + * Attribute that cancels the associated move's effects when set to be combined with the user's ally's + * subsequent move this turn. Used for Grass Pledge, Water Pledge, and Fire Pledge. + * @extends OverrideMoveEffectAttr + */ +export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { + constructor() { + super(true); + } + /** + * If the user's ally is set to use a different move with this attribute, + * defer this move's effects for a combined move on the ally's turn. + * @param user the {@linkcode Pokemon} using this move + * @param target n/a + * @param move the {@linkcode Move} being used + * @param args + * - [0] a {@linkcode Utils.BooleanHolder} indicating whether the move's base + * effects should be overridden this turn. + * @returns `true` if base move effects were overridden; `false` otherwise + */ + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + if (user.turnData.combiningPledge) { + // "The two moves have become one!\nIt's a combined move!" + user.scene.queueMessage(i18next.t("moveTriggers:combiningPledge")); + return false; + } + + const overridden = args[0] as Utils.BooleanHolder; + + const allyMovePhase = user.scene.findPhase((phase) => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer()); + if (allyMovePhase) { + const allyMove = allyMovePhase.move.getMove(); + if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) { + [ user, allyMovePhase.pokemon ].forEach((p) => p.turnData.combiningPledge = move.id); + + // "{userPokemonName} is waiting for {allyPokemonName}'s move..." + user.scene.queueMessage(i18next.t("moveTriggers:awaitingPledge", { + userPokemonName: getPokemonNameWithAffix(user), + allyPokemonName: getPokemonNameWithAffix(allyMovePhase.pokemon) + })); + + // Move the ally's MovePhase (if needed) so that the ally moves next + const allyMovePhaseIndex = user.scene.phaseQueue.indexOf(allyMovePhase); + const firstMovePhaseIndex = user.scene.phaseQueue.findIndex((phase) => phase instanceof MovePhase); + if (allyMovePhaseIndex !== firstMovePhaseIndex) { + user.scene.prependToPhase(user.scene.phaseQueue.splice(allyMovePhaseIndex, 1)[0], MovePhase); + } + + overridden.value = true; + return true; + } + } + return false; + } +} + /** * Attribute used for moves that change stat stages * @param stats {@linkcode BattleStat} array of stats to be changed @@ -3762,6 +3825,45 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { } } +/** + * Changes a Pledge move's power to 150 when combined with another unique Pledge + * move from an ally. + */ +export class CombinedPledgePowerAttr extends VariablePowerAttr { + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const power = args[0]; + if (!(power instanceof Utils.NumberHolder)) { + return false; + } + const combinedPledgeMove = user.turnData.combiningPledge; + + if (combinedPledgeMove && combinedPledgeMove !== move.id) { + power.value *= 150 / 80; + return true; + } + return false; + } +} + +/** + * Applies STAB to the given Pledge move if the move is part of a combined attack. + */ +export class CombinedPledgeStabBoostAttr extends MoveAttr { + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const stabMultiplier = args[0]; + if (!(stabMultiplier instanceof Utils.NumberHolder)) { + return false; + } + const combinedPledgeMove = user.turnData.combiningPledge; + + if (combinedPledgeMove && combinedPledgeMove !== move.id) { + stabMultiplier.value = 1.5; + return true; + } + return false; + } +} + export class VariableAtkAttr extends MoveAttr { constructor() { super(); @@ -4358,6 +4460,47 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr { } } +/** + * Changes the type of a Pledge move based on the Pledge move combined with it. + * @extends VariableMoveTypeAttr + */ +export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr { + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const moveType = args[0]; + if (!(moveType instanceof Utils.NumberHolder)) { + return false; + } + + const combinedPledgeMove = user.turnData.combiningPledge; + if (!combinedPledgeMove) { + return false; + } + + switch (move.id) { + case Moves.FIRE_PLEDGE: + if (combinedPledgeMove === Moves.WATER_PLEDGE) { + moveType.value = Type.WATER; + return true; + } + return false; + case Moves.WATER_PLEDGE: + if (combinedPledgeMove === Moves.GRASS_PLEDGE) { + moveType.value = Type.GRASS; + return true; + } + return false; + case Moves.GRASS_PLEDGE: + if (combinedPledgeMove === Moves.FIRE_PLEDGE) { + moveType.value = Type.FIRE; + return true; + } + return false; + default: + return false; + } + } +} + export class VariableMoveTypeMultiplierAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { return false; @@ -4505,7 +4648,15 @@ export class TypelessAttr extends MoveAttr { } * Attribute used for moves which ignore redirection effects, and always target their original target, i.e. Snipe Shot * Bypasses Storm Drain, Follow Me, Ally Switch, and the like. */ -export class BypassRedirectAttr extends MoveAttr { } +export class BypassRedirectAttr extends MoveAttr { + /** `true` if this move only bypasses redirection from Abilities */ + public readonly abilitiesOnly: boolean; + + constructor(abilitiesOnly: boolean = false) { + super(); + this.abilitiesOnly = abilitiesOnly; + } +} export class FrenzyAttr extends MoveEffectAttr { constructor() { @@ -5196,6 +5347,32 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { } } +/** + * Attribute that adds a secondary effect to the field when two unique Pledge moves + * are combined. The effect added varies based on the two Pledge moves combined. + */ +export class AddPledgeEffectAttr extends AddArenaTagAttr { + private readonly requiredPledge: Moves; + + constructor(tagType: ArenaTagType, requiredPledge: Moves, selfSideTarget: boolean = false) { + super(tagType, 4, false, selfSideTarget); + + this.requiredPledge = requiredPledge; + } + + override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + // TODO: add support for `HIT` effect triggering in AddArenaTagAttr to remove the need for this check + if (user.getLastXMoves(1)[0].result !== MoveResult.SUCCESS) { + return false; + } + + if (user.turnData.combiningPledge === this.requiredPledge) { + return super.apply(user, target, move, args); + } + return false; + } +} + /** * Attribute used for Revival Blessing. * @extends MoveEffectAttr @@ -8341,11 +8518,29 @@ export function initMoves() { new AttackMove(Moves.INFERNO, Type.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5) .attr(StatusEffectAttr, StatusEffect.BURN), new AttackMove(Moves.WATER_PLEDGE, Type.WATER, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) - .partial(), + .attr(AwaitCombinedPledgeAttr) + .attr(CombinedPledgeTypeAttr) + .attr(CombinedPledgePowerAttr) + .attr(CombinedPledgeStabBoostAttr) + .attr(AddPledgeEffectAttr, ArenaTagType.WATER_FIRE_PLEDGE, Moves.FIRE_PLEDGE, true) + .attr(AddPledgeEffectAttr, ArenaTagType.GRASS_WATER_PLEDGE, Moves.GRASS_PLEDGE) + .attr(BypassRedirectAttr, true), new AttackMove(Moves.FIRE_PLEDGE, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) - .partial(), + .attr(AwaitCombinedPledgeAttr) + .attr(CombinedPledgeTypeAttr) + .attr(CombinedPledgePowerAttr) + .attr(CombinedPledgeStabBoostAttr) + .attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.GRASS_PLEDGE) + .attr(AddPledgeEffectAttr, ArenaTagType.WATER_FIRE_PLEDGE, Moves.WATER_PLEDGE, true) + .attr(BypassRedirectAttr, true), new AttackMove(Moves.GRASS_PLEDGE, Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5) - .partial(), + .attr(AwaitCombinedPledgeAttr) + .attr(CombinedPledgeTypeAttr) + .attr(CombinedPledgePowerAttr) + .attr(CombinedPledgeStabBoostAttr) + .attr(AddPledgeEffectAttr, ArenaTagType.GRASS_WATER_PLEDGE, Moves.WATER_PLEDGE) + .attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.FIRE_PLEDGE) + .attr(BypassRedirectAttr, true), new AttackMove(Moves.VOLT_SWITCH, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5) .attr(ForceSwitchOutAttr, true), new AttackMove(Moves.STRUGGLE_BUG, Type.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5) diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index c484b2932f1..0ab0d76e880 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -25,4 +25,7 @@ export enum ArenaTagType { NO_CRIT = "NO_CRIT", IMPRISON = "IMPRISON", PLASMA_FISTS = "PLASMA_FISTS", + FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE", + WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE", + GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 05567491a1a..c2ef7d919b0 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3,7 +3,7 @@ import BattleScene, { AnySound } from "#app/battle-scene"; import { Variant, VariantSet, variantColorCache } from "#app/data/variant"; import { variantData } from "#app/data/variant"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "#app/ui/battle-info"; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget } from "#app/data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget, CombinedPledgeStabBoostAttr } from "#app/data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; import { starterPassiveAbilities } from "#app/data/balance/passives"; @@ -924,11 +924,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } break; case Stat.SPD: - // Check both the player and enemy to see if Tailwind should be multiplying the speed of the Pokemon - if ((this.isPlayer() && this.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.PLAYER)) - || (!this.isPlayer() && this.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, ArenaTagSide.ENEMY))) { + const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; + if (this.scene.arena.getTagOnSide(ArenaTagType.TAILWIND, side)) { ret *= 2; } + if (this.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, side)) { + ret >>= 2; + } if (this.getTag(BattlerTagType.SLOW_START)) { ret >>= 1; @@ -2562,6 +2564,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (matchesSourceType) { stabMultiplier.value += 0.5; } + applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier); if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) { stabMultiplier.value += 0.5; } @@ -5041,6 +5044,7 @@ export class PokemonTurnData { public statStagesIncreased: boolean = false; public statStagesDecreased: boolean = false; public moveEffectiveness: TypeDamageMultiplier | null = null; + public combiningPledge?: Moves; } export enum AiType { diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 807f194bad5..6272358aa85 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -331,22 +331,30 @@ export class MovePhase extends BattlePhase { // check move redirection abilities of every pokemon *except* the user. this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget)); + /** `true` if an Ability is responsible for redirecting the move to another target; `false` otherwise */ + let redirectedByAbility = (currentTarget !== redirectTarget.value); + // check for center-of-attention tags (note that this will override redirect abilities) this.pokemon.getOpponents().forEach(p => { - const redirectTag = p.getTag(CenterOfAttentionTag) as CenterOfAttentionTag; + const redirectTag = p.getTag(CenterOfAttentionTag); // TODO: don't hardcode this interaction. // Handle interaction between the rage powder center-of-attention tag and moves used by grass types/overcoat-havers (which are immune to RP's redirect) if (redirectTag && (!redirectTag.powder || (!this.pokemon.isOfType(Type.GRASS) && !this.pokemon.hasAbility(Abilities.OVERCOAT)))) { redirectTarget.value = p.getBattlerIndex(); + redirectedByAbility = false; } }); if (currentTarget !== redirectTarget.value) { - if (this.move.getMove().hasAttr(BypassRedirectAttr)) { - redirectTarget.value = currentTarget; + const bypassRedirectAttrs = this.move.getMove().getAttrs(BypassRedirectAttr); + bypassRedirectAttrs.forEach((attr) => { + if (!attr.abilitiesOnly || redirectedByAbility) { + redirectTarget.value = currentTarget; + } + }); - } else if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { + if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { redirectTarget.value = currentTarget; this.scene.unshiftPhase(new ShowAbilityPhase(this.scene, this.pokemon.getBattlerIndex(), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr))); } diff --git a/src/test/moves/pledge_moves.test.ts b/src/test/moves/pledge_moves.test.ts new file mode 100644 index 00000000000..93fcf57cc60 --- /dev/null +++ b/src/test/moves/pledge_moves.test.ts @@ -0,0 +1,337 @@ +import { BattlerIndex } from "#app/battle"; +import { allAbilities } from "#app/data/ability"; +import { ArenaTagSide } from "#app/data/arena-tag"; +import { allMoves, FlinchAttr } from "#app/data/move"; +import { Type } from "#app/data/type"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { Stat } from "#enums/stat"; +import { toDmgValue } from "#app/utils"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; + +describe("Moves - Pledge Moves", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("double") + .startingLevel(100) + .moveset([ Moves.FIRE_PLEDGE, Moves.GRASS_PLEDGE, Moves.WATER_PLEDGE, Moves.SPLASH ]) + .enemySpecies(Species.SNORLAX) + .enemyLevel(100) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it( + "Fire Pledge - should be an 80-power Fire-type attack outside of combination", + async () => { + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const firePledge = allMoves[Moves.FIRE_PLEDGE]; + vi.spyOn(firePledge, "calculateBattlePower"); + + const playerPokemon = game.scene.getPlayerField(); + vi.spyOn(playerPokemon[0], "getMoveType"); + + game.move.select(Moves.FIRE_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("MoveEndPhase", false); + + expect(firePledge.calculateBattlePower).toHaveLastReturnedWith(80); + expect(playerPokemon[0].getMoveType).toHaveLastReturnedWith(Type.FIRE); + } + ); + + it( + "Fire Pledge - should not combine with an ally using Fire Pledge", + async () => { + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const firePledge = allMoves[Moves.FIRE_PLEDGE]; + vi.spyOn(firePledge, "calculateBattlePower"); + + const playerPokemon = game.scene.getPlayerField(); + playerPokemon.forEach(p => vi.spyOn(p, "getMoveType")); + + const enemyPokemon = game.scene.getEnemyField(); + + game.move.select(Moves.FIRE_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.FIRE_PLEDGE, 0, BattlerIndex.ENEMY_2); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + + await game.phaseInterceptor.to("MoveEndPhase"); + expect(firePledge.calculateBattlePower).toHaveLastReturnedWith(80); + expect(playerPokemon[0].getMoveType).toHaveLastReturnedWith(Type.FIRE); + + await game.phaseInterceptor.to("BerryPhase", false); + expect(firePledge.calculateBattlePower).toHaveLastReturnedWith(80); + expect(playerPokemon[1].getMoveType).toHaveLastReturnedWith(Type.FIRE); + + enemyPokemon.forEach(p => expect(p.hp).toBeLessThan(p.getMaxHp())); + } + ); + + it( + "Fire Pledge - should not combine with an enemy's Pledge move", + async () => { + game.override + .battleType("single") + .enemyMoveset(Moves.GRASS_PLEDGE); + + await game.classicMode.startBattle([ Species.CHARIZARD ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.FIRE_PLEDGE); + + await game.toNextTurn(); + + // Neither Pokemon should defer their move's effects as they would + // if they combined moves, so both should be damaged. + expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp()); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + expect(game.scene.arena.getTag(ArenaTagType.FIRE_GRASS_PLEDGE)).toBeUndefined(); + } + ); + + it( + "Grass Pledge - should combine with Fire Pledge to form a 150-power Fire-type attack that creates a 'sea of fire'", + async () => { + await game.classicMode.startBattle([ Species.CHARIZARD, Species.BLASTOISE ]); + + const grassPledge = allMoves[Moves.GRASS_PLEDGE]; + vi.spyOn(grassPledge, "calculateBattlePower"); + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + vi.spyOn(playerPokemon[1], "getMoveType"); + const baseDmgMock = vi.spyOn(enemyPokemon[0], "getBaseDamage"); + + game.move.select(Moves.FIRE_PLEDGE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.GRASS_PLEDGE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + // advance to the end of PLAYER_2's move this turn + for (let i = 0; i < 2; i++) { + await game.phaseInterceptor.to("MoveEndPhase"); + } + expect(playerPokemon[1].getMoveType).toHaveLastReturnedWith(Type.FIRE); + expect(grassPledge.calculateBattlePower).toHaveLastReturnedWith(150); + + const baseDmg = baseDmgMock.mock.results[baseDmgMock.mock.results.length - 1].value; + expect(enemyPokemon[0].getMaxHp() - enemyPokemon[0].hp).toBe(toDmgValue(baseDmg * 1.5)); + expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); // PLAYER should not have attacked + expect(game.scene.arena.getTagOnSide(ArenaTagType.FIRE_GRASS_PLEDGE, ArenaTagSide.ENEMY)).toBeDefined(); + + const enemyStartingHp = enemyPokemon.map(p => p.hp); + await game.toNextTurn(); + enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(toDmgValue(p.getMaxHp() / 8))); + } + ); + + it( + "Fire Pledge - should combine with Water Pledge to form a 150-power Water-type attack that creates a 'rainbow'", + async () => { + game.override.moveset([ Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE, Moves.FIERY_DANCE, Moves.SPLASH ]); + + await game.classicMode.startBattle([ Species.BLASTOISE, Species.VENUSAUR ]); + + const firePledge = allMoves[Moves.FIRE_PLEDGE]; + vi.spyOn(firePledge, "calculateBattlePower"); + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + + vi.spyOn(playerPokemon[1], "getMoveType"); + + game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.FIRE_PLEDGE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + // advance to the end of PLAYER_2's move this turn + for (let i = 0; i < 2; i++) { + await game.phaseInterceptor.to("MoveEndPhase"); + } + expect(playerPokemon[1].getMoveType).toHaveLastReturnedWith(Type.WATER); + expect(firePledge.calculateBattlePower).toHaveLastReturnedWith(150); + expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); // PLAYER should not have attacked + expect(game.scene.arena.getTagOnSide(ArenaTagType.WATER_FIRE_PLEDGE, ArenaTagSide.PLAYER)).toBeDefined(); + + await game.toNextTurn(); + + game.move.select(Moves.FIERY_DANCE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("MoveEndPhase"); + + // Rainbow effect should increase Fiery Dance's chance of raising Sp. Atk to 100% + expect(playerPokemon[0].getStatStage(Stat.SPATK)).toBe(1); + } + ); + + it( + "Water Pledge - should combine with Grass Pledge to form a 150-power Grass-type attack that creates a 'swamp'", + async () => { + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const waterPledge = allMoves[Moves.WATER_PLEDGE]; + vi.spyOn(waterPledge, "calculateBattlePower"); + + const playerPokemon = game.scene.getPlayerField(); + const enemyPokemon = game.scene.getEnemyField(); + const enemyStartingSpd = enemyPokemon.map(p => p.getEffectiveStat(Stat.SPD)); + + vi.spyOn(playerPokemon[1], "getMoveType"); + + game.move.select(Moves.GRASS_PLEDGE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.WATER_PLEDGE, 1, BattlerIndex.ENEMY); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + // advance to the end of PLAYER_2's move this turn + for (let i = 0; i < 2; i++) { + await game.phaseInterceptor.to("MoveEndPhase"); + } + + expect(playerPokemon[1].getMoveType).toHaveLastReturnedWith(Type.GRASS); + expect(waterPledge.calculateBattlePower).toHaveLastReturnedWith(150); + expect(enemyPokemon[1].hp).toBe(enemyPokemon[1].getMaxHp()); + + expect(game.scene.arena.getTagOnSide(ArenaTagType.GRASS_WATER_PLEDGE, ArenaTagSide.ENEMY)).toBeDefined(); + enemyPokemon.forEach((p, i) => expect(p.getEffectiveStat(Stat.SPD)).toBe(Math.floor(enemyStartingSpd[i] / 4))); + } + ); + + it( + "Pledge Moves - should alter turn order when used in combination", + async () => { + await game.classicMode.startBattle([ Species.CHARIZARD, Species.BLASTOISE ]); + + const enemyPokemon = game.scene.getEnemyField(); + + game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.FIRE_PLEDGE, 1, BattlerIndex.ENEMY_2); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2 ]); + // PLAYER_2 should act with a combined move immediately after PLAYER as the second move in the turn + for (let i = 0; i < 2; i++) { + await game.phaseInterceptor.to("MoveEndPhase"); + } + expect(enemyPokemon[0].hp).toBe(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); + } + ); + + it( + "Pledge Moves - 'rainbow' effect should not stack with Serene Grace when applied to flinching moves", + async () => { + game.override + .ability(Abilities.SERENE_GRACE) + .moveset([ Moves.FIRE_PLEDGE, Moves.WATER_PLEDGE, Moves.IRON_HEAD, Moves.SPLASH ]); + + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const ironHeadFlinchAttr = allMoves[Moves.IRON_HEAD].getAttrs(FlinchAttr)[0]; + vi.spyOn(ironHeadFlinchAttr, "getMoveChance"); + + game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.FIRE_PLEDGE, 1, BattlerIndex.ENEMY_2); + + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(game.scene.arena.getTagOnSide(ArenaTagType.WATER_FIRE_PLEDGE, ArenaTagSide.PLAYER)).toBeDefined(); + + game.move.select(Moves.IRON_HEAD, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("BerryPhase", false); + + expect(ironHeadFlinchAttr.getMoveChance).toHaveLastReturnedWith(60); + } + ); + + it( + "Pledge Moves - should have no effect when the second ally's move is cancelled", + async () => { + game.override + .enemyMoveset([ Moves.SPLASH, Moves.SPORE ]); + + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const enemyPokemon = game.scene.getEnemyField(); + + game.move.select(Moves.FIRE_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.GRASS_PLEDGE, 1, BattlerIndex.ENEMY_2); + + await game.forceEnemyMove(Moves.SPORE, BattlerIndex.PLAYER_2); + await game.forceEnemyMove(Moves.SPLASH); + + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2 ]); + + await game.phaseInterceptor.to("BerryPhase", false); + + enemyPokemon.forEach((p) => expect(p.hp).toBe(p.getMaxHp())); + } + ); + + it( + "Pledge Moves - should ignore redirection from another Pokemon's Storm Drain", + async () => { + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + const enemyPokemon = game.scene.getEnemyField(); + vi.spyOn(enemyPokemon[1], "getAbility").mockReturnValue(allAbilities[Abilities.STORM_DRAIN]); + + game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + + await game.phaseInterceptor.to("MoveEndPhase", false); + + expect(enemyPokemon[0].hp).toBeLessThan(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].getStatStage(Stat.SPATK)).toBe(0); + } + ); + + it( + "Pledge Moves - should not ignore redirection from another Pokemon's Follow Me", + async () => { + game.override.enemyMoveset([ Moves.FOLLOW_ME, Moves.SPLASH ]); + await game.classicMode.startBattle([ Species.BLASTOISE, Species.CHARIZARD ]); + + game.move.select(Moves.WATER_PLEDGE, 0, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, 1); + + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.FOLLOW_ME); + + await game.phaseInterceptor.to("BerryPhase", false); + + const enemyPokemon = game.scene.getEnemyField(); + expect(enemyPokemon[0].hp).toBe(enemyPokemon[0].getMaxHp()); + expect(enemyPokemon[1].hp).toBeLessThan(enemyPokemon[1].getMaxHp()); + } + ); +}); From 27537286b925d3b47d454df8f0d1e5a4154797e8 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:24:52 -0700 Subject: [PATCH 08/70] [Move] Implement Electrify (#4569) * Implement Electrify * ESLint * Fix docs --- src/data/battler-tags.ts | 17 ++++++++ src/data/move.ts | 2 +- src/enums/battler-tag-type.ts | 1 + src/field/pokemon.ts | 3 ++ src/test/moves/electrify.test.ts | 69 ++++++++++++++++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/test/moves/electrify.test.ts diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 6cb33dca306..a54a8c5f519 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2310,6 +2310,21 @@ export class TarShotTag extends BattlerTag { } } +/** + * Battler Tag implementing the type-changing effect of {@link https://bulbapedia.bulbagarden.net/wiki/Electrify_(move) | Electrify}. + * While this tag is in effect, the afflicted Pokemon's moves are changed to Electric type. + */ +export class ElectrifiedTag extends BattlerTag { + constructor() { + super(BattlerTagType.ELECTRIFIED, BattlerTagLapseType.TURN_END, 1, Moves.ELECTRIFY); + } + + override onAdd(pokemon: Pokemon): void { + // "{pokemonNameWithAffix}'s moves have been electrified!" + pokemon.scene.queueMessage(i18next.t("battlerTags:electrifiedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } +} + /** * Battler Tag that keeps track of how many times the user has Autotomized * Each count of Autotomization reduces the weight by 100kg @@ -2811,6 +2826,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new GulpMissileTag(tagType, sourceMove); case BattlerTagType.TAR_SHOT: return new TarShotTag(); + case BattlerTagType.ELECTRIFIED: + return new ElectrifiedTag(); case BattlerTagType.THROAT_CHOPPED: return new ThroatChoppedTag(); case BattlerTagType.GORILLA_TACTICS: diff --git a/src/data/move.ts b/src/data/move.ts index 62ac36b28ad..8095b5a6013 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8732,7 +8732,7 @@ export function initMoves() { .attr(TerrainChangeAttr, TerrainType.MISTY) .target(MoveTarget.BOTH_SIDES), new StatusMove(Moves.ELECTRIFY, Type.ELECTRIC, -1, 20, -1, 0, 6) - .unimplemented(), + .attr(AddBattlerTagAttr, BattlerTagType.ELECTRIFIED, false, true), new AttackMove(Moves.PLAY_ROUGH, Type.FAIRY, MoveCategory.PHYSICAL, 90, 90, 10, 10, 0, 6) .attr(StatStageChangeAttr, [ Stat.ATK ], -1), new AttackMove(Moves.FAIRY_WIND, Type.FAIRY, MoveCategory.SPECIAL, 40, 100, 30, -1, 0, 6) diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index ccd6e9fe314..43c849a78e0 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -85,4 +85,5 @@ export enum BattlerTagType { TAUNT = "TAUNT", IMPRISON = "IMPRISON", SYRUP_BOMB = "SYRUP_BOMB", + ELECTRIFIED = "ELECTRIFIED", } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c2ef7d919b0..94fa050a7bc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1528,6 +1528,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder); this.scene.arena.applyTags(ArenaTagType.PLASMA_FISTS, moveTypeHolder); + if (this.getTag(BattlerTagType.ELECTRIFIED)) { + moveTypeHolder.value = Type.ELECTRIC; + } return moveTypeHolder.value as Type; } diff --git a/src/test/moves/electrify.test.ts b/src/test/moves/electrify.test.ts new file mode 100644 index 00000000000..5d15a825688 --- /dev/null +++ b/src/test/moves/electrify.test.ts @@ -0,0 +1,69 @@ +import { BattlerIndex } from "#app/battle"; +import { Type } from "#app/data/type"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; + +describe("Moves - Electrify", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset(Moves.ELECTRIFY) + .battleType("single") + .startingLevel(100) + .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.TACKLE) + .enemyLevel(100); + }); + + it("should convert attacks to Electric type", async () => { + await game.classicMode.startBattle([ Species.EXCADRILL ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + vi.spyOn(enemyPokemon, "getMoveType"); + + game.move.select(Moves.ELECTRIFY); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC); + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); + }); + + it("should override type changes from abilities", async () => { + game.override.enemyAbility(Abilities.PIXILATE); + + await game.classicMode.startBattle([ Species.EXCADRILL ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(enemyPokemon, "getMoveType"); + + game.move.select(Moves.ELECTRIFY); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.getMoveType).toHaveLastReturnedWith(Type.ELECTRIC); + expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp()); + }); +}); From d36245650197f7c6302456b16b02b28cf01853c5 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 4 Oct 2024 13:29:20 -0700 Subject: [PATCH 09/70] [P2] Diamond Storm should only trigger once when hitting multiple pokemon (#4544) * Diamond Storm should only trigger once when hitting multiple pokemon * Also fix Clangorous Soulblaze just in case * Fix linting * Fix linting Oops missed this one --- src/data/move.ts | 4 +-- src/test/moves/diamond_storm.test.ts | 46 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 src/test/moves/diamond_storm.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index 8095b5a6013..01b300cbae4 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8756,7 +8756,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPATK ], -1) .soundBased(), new AttackMove(Moves.DIAMOND_STORM, Type.ROCK, MoveCategory.PHYSICAL, 100, 95, 5, 50, 0, 6) - .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) + .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true, undefined, undefined, undefined, undefined, true) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.STEAM_ERUPTION, Type.WATER, MoveCategory.SPECIAL, 110, 95, 5, 30, 0, 6) @@ -9183,7 +9183,7 @@ export function initMoves() { .makesContact(false) .ignoresVirtual(), new AttackMove(Moves.CLANGOROUS_SOULBLAZE, Type.DRAGON, MoveCategory.SPECIAL, 185, -1, 1, 100, 0, 7) - .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) + .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES) .partial() diff --git a/src/test/moves/diamond_storm.test.ts b/src/test/moves/diamond_storm.test.ts new file mode 100644 index 00000000000..6e5be2a790d --- /dev/null +++ b/src/test/moves/diamond_storm.test.ts @@ -0,0 +1,46 @@ +import { allMoves } from "#app/data/move"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Diamond Storm", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.DIAMOND_STORM ]) + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should only increase defense once even if hitting 2 pokemon", async () => { + game.override.battleType("double"); + const diamondStorm = allMoves[Moves.DIAMOND_STORM]; + vi.spyOn(diamondStorm, "chance", "get").mockReturnValue(100); + vi.spyOn(diamondStorm, "accuracy", "get").mockReturnValue(100); + await game.classicMode.startBattle([ Species.FEEBAS ]); + + game.move.select(Moves.DIAMOND_STORM); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.DEF)).toBe(2); + }); +}); From 1947472f1c5015de733552ec907e71497550e177 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Fri, 4 Oct 2024 22:47:12 +0200 Subject: [PATCH 10/70] [P3] Fix start button cursor not being cleared properly in starter select (#4558) --- src/ui/starter-select-ui-handler.ts | 71 +++++++++++++++++------------ 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 5cc70abf143..98a563301e4 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -308,13 +308,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterIconsCursorObj: Phaser.GameObjects.Image; private valueLimitLabel: Phaser.GameObjects.Text; private startCursorObj: Phaser.GameObjects.NineSlice; - // private starterValueLabels: Phaser.GameObjects.Text[]; - // private shinyIcons: Phaser.GameObjects.Image[][]; - // private hiddenAbilityIcons: Phaser.GameObjects.Image[]; - // private classicWinIcons: Phaser.GameObjects.Image[]; - // private candyUpgradeIcon: Phaser.GameObjects.Image[]; - // private candyUpgradeOverlayIcon: Phaser.GameObjects.Image[]; - // + private iconAnimHandler: PokemonIconAnimHandler; //variables to keep track of the dynamically rendered list of instruction prompts for starter select @@ -1316,12 +1310,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } break; case Button.UP: + // UP from start button: go to pokemon in team if any, otherwise filter this.startCursorObj.setVisible(false); if (this.starterSpecies.length > 0) { this.starterIconsCursorIndex = this.starterSpecies.length - 1; this.moveStarterIconsCursor(this.starterIconsCursorIndex); } else { - // up from start button with no Pokemon in the team > go to filter this.startCursorObj.setVisible(false); this.filterBarCursor = Math.max(1, this.filterBar.numFilters - 1); this.setFilterMode(true); @@ -1329,29 +1323,27 @@ export default class StarterSelectUiHandler extends MessageUiHandler { success = true; break; case Button.DOWN: + // DOWN from start button: Go to filters this.startCursorObj.setVisible(false); - if (this.starterSpecies.length > 0) { - this.starterIconsCursorIndex = 0; - this.moveStarterIconsCursor(this.starterIconsCursorIndex); - } else { - // down from start button with no Pokemon in the team > go to filter - this.startCursorObj.setVisible(false); - this.filterBarCursor = Math.max(1, this.filterBar.numFilters - 1); - this.setFilterMode(true); - } + this.filterBarCursor = Math.max(1, this.filterBar.numFilters - 1); + this.setFilterMode(true); success = true; break; case Button.LEFT: - this.startCursorObj.setVisible(false); - this.cursorObj.setVisible(true); - success = this.setCursor(onScreenFirstIndex + (onScreenNumberOfRows - 1) * 9 + 8); // set last column - success = true; + if (numberOfStarters > 0) { + this.startCursorObj.setVisible(false); + this.cursorObj.setVisible(true); + this.setCursor(onScreenFirstIndex + (onScreenNumberOfRows - 1) * 9 + 8); // set last column + success = true; + } break; case Button.RIGHT: - this.startCursorObj.setVisible(false); - this.cursorObj.setVisible(true); - success = this.setCursor(onScreenFirstIndex + (onScreenNumberOfRows - 1) * 9); // set first column - success = true; + if (numberOfStarters > 0) { + this.startCursorObj.setVisible(false); + this.cursorObj.setVisible(true); + this.setCursor(onScreenFirstIndex + (onScreenNumberOfRows - 1) * 9); // set first column + success = true; + } break; } } else if (this.filterMode) { @@ -1373,7 +1365,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case Button.UP: if (this.filterBar.openDropDown) { success = this.filterBar.decDropDownCursor(); - // else if there is filtered starters + } else if (this.filterBarCursor === this.filterBar.numFilters - 1 && this.starterSpecies.length > 0) { + // UP from the last filter, move to start button + this.setFilterMode(false); + this.cursorObj.setVisible(false); + this.startCursorObj.setVisible(true); + success = true; } else if (numberOfStarters > 0) { // UP from filter bar to bottom of Pokemon list this.setFilterMode(false); @@ -1392,6 +1389,13 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case Button.DOWN: if (this.filterBar.openDropDown) { success = this.filterBar.incDropDownCursor(); + } else if (this.filterBarCursor === this.filterBar.numFilters - 1 && this.starterSpecies.length > 0) { + // DOWN from the last filter, move to Pokemon in party if any + this.setFilterMode(false); + this.cursorObj.setVisible(false); + this.starterIconsCursorIndex = 0; + this.moveStarterIconsCursor(this.starterIconsCursorIndex); + success = true; } else if (numberOfStarters > 0) { // DOWN from filter bar to top of Pokemon list this.setFilterMode(false); @@ -2656,9 +2660,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonShinyIcon.setTint(tint); this.setSpecies(species); this.updateInstructions(); - } else { - console.warn("Species is undefined for cursor position", cursor); - this.setFilterMode(true); } } @@ -3326,6 +3327,18 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } this.moveStarterIconsCursor(this.starterIconsCursorIndex); + } else if (this.startCursorObj.visible && this.starterSpecies.length === 0) { + // On the start button and no more Pokemon in party + this.startCursorObj.setVisible(false); + if (this.filteredStarterContainers.length > 0) { + // Back to the first Pokemon if there is one + this.cursorObj.setVisible(true); + this.setCursor(0 + this.scrollCursor * 9); + } else { + // Back to filters + this.filterBarCursor = Math.max(1, this.filterBar.numFilters - 1); + this.setFilterMode(true); + } } this.tryUpdateValue(); From c99df9712a383dac44e0782a47c3a91225dfbcf0 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Fri, 4 Oct 2024 14:23:20 -0700 Subject: [PATCH 11/70] [Move] Implement Ion Deluge (#4579) --- src/data/arena-tag.ts | 15 ++++++++------- src/data/move.ts | 6 +++--- src/enums/arena-tag-type.ts | 2 +- src/field/pokemon.ts | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index b75d23b48d8..6407e139a71 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -513,15 +513,16 @@ class WaterSportTag extends WeakenMoveTypeTag { } /** - * Arena Tag class for the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}. + * Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Ion_Deluge_(move) | Ion Deluge} + * and the secondary effect of {@link https://bulbapedia.bulbagarden.net/wiki/Plasma_Fists_(move) | Plasma Fists}. * Converts Normal-type moves to Electric type for the rest of the turn. */ -export class PlasmaFistsTag extends ArenaTag { - constructor() { - super(ArenaTagType.PLASMA_FISTS, 1, Moves.PLASMA_FISTS); +export class IonDelugeTag extends ArenaTag { + constructor(sourceMove?: Moves) { + super(ArenaTagType.ION_DELUGE, 1, sourceMove); } - /** Queues Plasma Fists' on-add message */ + /** Queues an on-add message */ onAdd(arena: Arena): void { arena.scene.queueMessage(i18next.t("arenaTag:plasmaFistsOnAdd")); } @@ -1119,8 +1120,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new MudSportTag(turnCount, sourceId); case ArenaTagType.WATER_SPORT: return new WaterSportTag(turnCount, sourceId); - case ArenaTagType.PLASMA_FISTS: - return new PlasmaFistsTag(); + case ArenaTagType.ION_DELUGE: + return new IonDelugeTag(sourceMove); case ArenaTagType.SPIKES: return new SpikesTag(sourceId, side); case ArenaTagType.TOXIC_SPIKES: diff --git a/src/data/move.ts b/src/data/move.ts index 01b300cbae4..f795d265336 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8688,8 +8688,8 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .soundBased(), new StatusMove(Moves.ION_DELUGE, Type.ELECTRIC, -1, 25, -1, 1, 6) - .target(MoveTarget.BOTH_SIDES) - .unimplemented(), + .attr(AddArenaTagAttr, ArenaTagType.ION_DELUGE) + .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.PARABOLIC_CHARGE, Type.ELECTRIC, MoveCategory.SPECIAL, 65, 100, 20, -1, 0, 6) .attr(HitHealAttr) .target(MoveTarget.ALL_NEAR_OTHERS) @@ -9158,7 +9158,7 @@ export function initMoves() { .attr(HalfSacrificialAttr) .target(MoveTarget.ALL_NEAR_OTHERS), new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7) - .attr(AddArenaTagAttr, ArenaTagType.PLASMA_FISTS, 1) + .attr(AddArenaTagAttr, ArenaTagType.ION_DELUGE, 1) .punchingMove(), new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) .attr(PhotonGeyserCategoryAttr) diff --git a/src/enums/arena-tag-type.ts b/src/enums/arena-tag-type.ts index 0ab0d76e880..c73f4ec2ae5 100644 --- a/src/enums/arena-tag-type.ts +++ b/src/enums/arena-tag-type.ts @@ -24,7 +24,7 @@ export enum ArenaTagType { SAFEGUARD = "SAFEGUARD", NO_CRIT = "NO_CRIT", IMPRISON = "IMPRISON", - PLASMA_FISTS = "PLASMA_FISTS", + ION_DELUGE = "ION_DELUGE", FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE", WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE", GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE", diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 94fa050a7bc..d6f73e1b5bc 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1527,7 +1527,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder); - this.scene.arena.applyTags(ArenaTagType.PLASMA_FISTS, moveTypeHolder); + this.scene.arena.applyTags(ArenaTagType.ION_DELUGE, moveTypeHolder); if (this.getTag(BattlerTagType.ELECTRIFIED)) { moveTypeHolder.value = Type.ELECTRIC; } From f562a76332fcf76601ed215b48b11e2b484efaef Mon Sep 17 00:00:00 2001 From: Xavion3 Date: Sat, 5 Oct 2024 17:10:32 +1000 Subject: [PATCH 12/70] Make repeat abilities not stack (#4588) If due to fusions you have the same ability as both passive and normal, it'll no longer stack with itself. --- src/data/ability.ts | 2 +- .../abilities/ability_duplication.test.ts | 58 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/test/abilities/ability_duplication.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 62e6e772411..43d02da1733 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4623,7 +4623,7 @@ async function applyAbAttrsInternal( messages: string[] = [], ) { for (const passive of [ false, true ]) { - if (!pokemon?.canApplyAbility(passive)) { + if (!pokemon?.canApplyAbility(passive) || (passive && pokemon.getPassiveAbility().id === pokemon.getAbility().id)) { continue; } diff --git a/src/test/abilities/ability_duplication.test.ts b/src/test/abilities/ability_duplication.test.ts new file mode 100644 index 00000000000..f9122b3259c --- /dev/null +++ b/src/test/abilities/ability_duplication.test.ts @@ -0,0 +1,58 @@ +import { Stat } from "#app/enums/stat"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; + +describe("Ability Duplication", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.SPLASH ]) + .battleType("single") + .ability(Abilities.HUGE_POWER) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("huge power should only be applied once if both normal and passive", async () => { + game.override.passiveAbility(Abilities.HUGE_POWER); + + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const [ magikarp ] = game.scene.getPlayerField(); + const magikarpAttack = magikarp.getEffectiveStat(Stat.ATK); + + magikarp.summonData.abilitySuppressed = true; + + expect(magikarp.getEffectiveStat(Stat.ATK)).toBe(magikarpAttack / 2); + }); + + it("huge power should stack with pure power", async () => { + game.override.passiveAbility(Abilities.PURE_POWER); + + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const [ magikarp ] = game.scene.getPlayerField(); + const magikarpAttack = magikarp.getEffectiveStat(Stat.ATK); + + magikarp.summonData.abilitySuppressed = true; + + expect(magikarp.getEffectiveStat(Stat.ATK)).toBe(magikarpAttack / 4); + }); +}); From 42b75e8440c8b298f0ee7f404f7918f499da6574 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Sat, 5 Oct 2024 14:01:41 -0700 Subject: [PATCH 13/70] [Qol] Make i18n money formatter controlled by translators (#4550) * fix: i18n money formatter * fix wrongful console.warn on i18n money formatter * update locales submodule update reference to `56eeb809eb5a2de40cfc5bc6128a78bef14deea9` (from `3ccef8472dd7cc7c362538489954cb8fdad27e5f`) --- public/locales | 2 +- src/plugins/i18n.ts | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/public/locales b/public/locales index 3ccef8472dd..56eeb809eb5 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 3ccef8472dd7cc7c362538489954cb8fdad27e5f +Subproject commit 56eeb809eb5a2de40cfc5bc6128a78bef14deea9 diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 676e47c19b8..be4c6983c0a 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -100,6 +100,22 @@ async function initFonts(language: string | undefined) { } } +/** + * I18n money formatter with. (useful for BBCode coloring of text)\ + * *If you don't want the BBCode tag applied, just use 'number' formatter* + * @example Input: `{{myMoneyValue, money}}` + * Output: `@[MONEY]{â‚˝100,000,000}` + * @param amount the money amount + * @returns a money formatted string + */ +function i18nMoneyFormatter(amount: any): string { + if (isNaN(Number(amount))) { + console.warn(`i18nMoneyFormatter: value "${amount}" is not a number!`); + } + + return `@[MONEY]{${i18next.t("common:money", { amount })}}`; +} + //#region Exports /** @@ -249,24 +265,10 @@ export async function initI18n(): Promise { postProcess: [ "korean-postposition" ], }); - // Input: {{myMoneyValue, money}} - // Output: @[MONEY]{â‚˝100,000,000} (useful for BBCode coloring of text) - // If you don't want the BBCode tag applied, just use 'number' formatter - i18next.services.formatter?.add("money", (value, lng, options) => { - const numberFormattedString = Intl.NumberFormat(lng, options).format(value); - switch (lng) { - case "ja": - return `@[MONEY]{${numberFormattedString}}円`; - case "de": - case "es": - case "fr": - case "it": - return `@[MONEY]{${numberFormattedString} â‚˝}`; - default: - // English and other languages that use same format - return `@[MONEY]{â‚˝${numberFormattedString}}`; - } - }); + + if (i18next.services.formatter) { + i18next.services.formatter.add("money", i18nMoneyFormatter); + } await initFonts(localStorage.getItem("prLang") ?? undefined); } From e8f40c10c948606e61a6660d8978e17e92491f4a Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:52:13 -0700 Subject: [PATCH 14/70] [Test] Update `create-test` script for linting changes (#4587) Add additional boilerplate code Change prompt to be slightly more accurate Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- create-test-boilerplate.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/create-test-boilerplate.js b/create-test-boilerplate.js index 5f4bbc41198..a365999c623 100644 --- a/create-test-boilerplate.js +++ b/create-test-boilerplate.js @@ -49,7 +49,7 @@ async function promptFileName(selectedType) { { type: "input", name: "userInput", - message: `Please provide a file name for the ${selectedType} test:`, + message: `Please provide the name of the ${selectedType}:`, }, ]); @@ -110,7 +110,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("${description}", () => { let phaserGame: Phaser.Game; @@ -129,15 +129,22 @@ describe("${description}", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([Moves.SPLASH]) + .moveset([ Moves.SPLASH ]) + .ability(Abilities.BALL_FETCH) .battleType("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH); }); - it("test case", async () => { - // await game.classicMode.startBattle([Species.MAGIKARP]); - // game.move.select(Moves.SPLASH); + it("should do X", async () => { + await game.classicMode.startBattle([ Species.FEEBAS ]); + + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("BerryPhase"); + + expect(true).toBe(true); }); }); `; From f629a3e45332176bee4db0ba5d03c31b80c6ffbf Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Sat, 5 Oct 2024 16:52:53 -0700 Subject: [PATCH 15/70] [P2] Stop G-Max Pokemon from evolving (#4581) --- src/field/pokemon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d6f73e1b5bc..a0ad4e8a52f 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1707,7 +1707,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (pokemonEvolutions.hasOwnProperty(this.species.speciesId)) { const evolutions = pokemonEvolutions[this.species.speciesId]; for (const e of evolutions) { - if (!e.item && this.level >= e.level && (!e.preFormKey || this.getFormKey() === e.preFormKey)) { + if (!e.item && this.level >= e.level && (isNullOrUndefined(e.preFormKey) || this.getFormKey() === e.preFormKey)) { if (e.condition === null || (e.condition as SpeciesEvolutionCondition).predicate(this)) { return e; } @@ -1718,7 +1718,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.isFusion() && this.fusionSpecies && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { const fusionEvolutions = pokemonEvolutions[this.fusionSpecies.speciesId].map(e => new FusionSpeciesFormEvolution(this.species.speciesId, e)); for (const fe of fusionEvolutions) { - if (!fe.item && this.level >= fe.level && (!fe.preFormKey || this.getFusionFormKey() === fe.preFormKey)) { + if (!fe.item && this.level >= fe.level && (isNullOrUndefined(fe.preFormKey) || this.getFusionFormKey() === fe.preFormKey)) { if (fe.condition === null || (fe.condition as SpeciesEvolutionCondition).predicate(this)) { return fe; } From c2c41d9be8b6f73b50ae22024280b8dcaffbdb9f Mon Sep 17 00:00:00 2001 From: Frederico Santos Date: Sun, 6 Oct 2024 02:49:03 +0100 Subject: [PATCH 16/70] Update subproject commit reference for locales --- public/locales | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/locales b/public/locales index 56eeb809eb5..b44ee217378 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 56eeb809eb5a2de40cfc5bc6128a78bef14deea9 +Subproject commit b44ee2173788018ffd5dc6b7b7fa159be5b9d514 From f9691b872b38e615cb46b09aa06a9dfd7d0f81c8 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:47:34 -0700 Subject: [PATCH 17/70] Change deploy script to specify "main" instead of `default_branch` (#4557) --- .github/workflows/deploy.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 70dc2bfa502..e40b18eb69b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,8 +1,12 @@ -name: Deploy +name: Deploy Main on: - push: {} - pull_request: {} + push: + branches: + - main + pull_request: + branches: + - main jobs: deploy: @@ -22,7 +26,7 @@ jobs: env: NODE_ENV: production - name: Set up SSH - if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch + if: github.event_name == 'push' && github.ref_name == 'main' run: | mkdir ~/.ssh echo "${{ secrets.SSH_PUBLIC_KEY }}" > ~/.ssh/id_ed25519.pub @@ -30,12 +34,12 @@ jobs: chmod 600 ~/.ssh/* ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts - name: Deploy build on server - if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch + if: github.event_name == 'push' && github.ref_name == 'main' run: | rsync --del --no-times --checksum -vrm dist/* ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }}:${{ secrets.DESTINATION_DIR }} ssh -t ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "~/prmanifest --inpath ${{ secrets.DESTINATION_DIR }} --outpath ${{ secrets.DESTINATION_DIR }}/manifest.json" - name: Purge Cloudflare Cache - if: github.event_name == 'push' && github.ref_name == github.event.repository.default_branch + if: github.event_name == 'push' && github.ref_name == 'main' id: purge-cache uses: NathanVaughn/actions-cloudflare-purge@v3.1.0 with: From a7157bbe9ab356092284fd1b986b26b75b5ae0b9 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Sun, 6 Oct 2024 16:51:34 -0400 Subject: [PATCH 18/70] fix shop option cursor indexing (#4601) Co-authored-by: ImperialSympathizer --- src/ui/modifier-select-ui-handler.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 9f2fb731022..f7e57b53193 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -577,6 +577,10 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { this.getUi().clearText(); this.eraseCursor(); + // Reset cursor positions + this.cursor = 0; + this.rowCursor = 0; + /* Multiplies the fade time duration by the speed parameter so that it is always constant, and avoids "flashbangs" at game speed x5 */ this.scene.hideShopOverlay(750 * this.scene.gameSpeed); this.scene.hideLuckText(250); From 1226ab37e127c06e4fe1c0ba0bfc081b2c9905b3 Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Sun, 6 Oct 2024 17:29:39 -0400 Subject: [PATCH 19/70] [Sprite] Compress Bronzor animation (#4593) * [Sprite] Compress Bronzor front animation * [Sprite] Compress Bronzor back animation * [Sprite] Compress shiny Bronzor back animation * [Sprite] Compress shiny Bronzor front animation --- public/images/pokemon/436.json | 3685 ++++++--------------- public/images/pokemon/436.png | Bin 769 -> 411 bytes public/images/pokemon/back/436.json | 3685 ++++++--------------- public/images/pokemon/back/436.png | Bin 637 -> 399 bytes public/images/pokemon/back/shiny/436.json | 3685 ++++++--------------- public/images/pokemon/back/shiny/436.png | Bin 637 -> 399 bytes public/images/pokemon/shiny/436.json | 3685 ++++++--------------- public/images/pokemon/shiny/436.png | Bin 769 -> 411 bytes 8 files changed, 4076 insertions(+), 10664 deletions(-) diff --git a/public/images/pokemon/436.json b/public/images/pokemon/436.json index dc6c1d23770..6206c5e66cb 100644 --- a/public/images/pokemon/436.json +++ b/public/images/pokemon/436.json @@ -1,2666 +1,1019 @@ -{ - "textures": [ - { - "image": "436.png", - "format": "RGBA8888", - "size": { - "w": 97, - "h": 97 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 1, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 1, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 13, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 13, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 1, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 1, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 11, - "y": 4, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 13, - "y": 14, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 13, - "y": 14, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 17, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 10, - "y": 17, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 6, - "y": 18, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 15, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 15, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 8, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 8, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 10, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 33, - "h": 39 - }, - "frame": { - "x": 0, - "y": 0, - "w": 33, - "h": 39 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 32, - "h": 39 - }, - "frame": { - "x": 33, - "y": 0, - "w": 32, - "h": 39 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 32, - "h": 39 - }, - "frame": { - "x": 33, - "y": 0, - "w": 32, - "h": 39 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 15, - "y": 9, - "w": 32, - "h": 39 - }, - "frame": { - "x": 65, - "y": 0, - "w": 32, - "h": 39 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 15, - "y": 9, - "w": 32, - "h": 39 - }, - "frame": { - "x": 65, - "y": 0, - "w": 32, - "h": 39 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 33, - "h": 38 - }, - "frame": { - "x": 0, - "y": 39, - "w": 33, - "h": 38 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 33, - "h": 38 - }, - "frame": { - "x": 0, - "y": 39, - "w": 33, - "h": 38 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 21, - "w": 33, - "h": 38 - }, - "frame": { - "x": 33, - "y": 39, - "w": 33, - "h": 38 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 47, - "h": 59 - }, - "spriteSourceSize": { - "x": 5, - "y": 21, - "w": 33, - "h": 38 - }, - "frame": { - "x": 33, - "y": 39, - "w": 33, - "h": 38 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:2b46b60048375cd75fb24de5f2dd05a3:de64de523e63943f7f5dc5a59d03e108:0a3bacf3d680738b160c4c8ace3cda59$" - } -} +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0002.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0003.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0004.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0005.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0006.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0007.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0008.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0009.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0010.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0011.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0012.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0013.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0014.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0015.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0016.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0017.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0018.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0019.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0020.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0021.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0022.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0023.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0024.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0025.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0026.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0027.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0028.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0029.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0030.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0031.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0032.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0033.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0034.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0035.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0036.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0037.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0038.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0039.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0040.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0041.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0042.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0043.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0044.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0045.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0046.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0047.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0048.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0049.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0050.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0051.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0052.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0053.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0054.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0055.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0056.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0057.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0058.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0059.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 6, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0060.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 6, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0061.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0062.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0063.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0064.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0065.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0066.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0067.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0068.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0069.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0070.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0071.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0072.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0073.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0074.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0075.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0076.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0077.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 4, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0078.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 4, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0079.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 3, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0080.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 3, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0081.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 4, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0082.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 4, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0083.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0084.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0085.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0086.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0087.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0088.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 12, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0089.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0090.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0091.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0092.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0093.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0094.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0095.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 5, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0096.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 5, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0097.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 1, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0098.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 1, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0099.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 0, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0100.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 0, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0101.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 5, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0102.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 5, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0103.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 16, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0104.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 16, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0105.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 15, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0106.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 15, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0107.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 18, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0108.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 18, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0109.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 22, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0110.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 22, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0111.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 19, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0112.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 19, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0113.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 16, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0114.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 16, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0115.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0116.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0117.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0118.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 10, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0119.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0120.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 7, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0121.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0122.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 9, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0123.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0124.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 11, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0125.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + }, + { + "filename": "0126.png", + "frame": { "x": 0, "y": 0, "w": 31, "h": 37 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 31, "h": 37 }, + "sourceSize": { "w": 47, "h": 59 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.7-dev", + "image": "436.png", + "format": "RGBA8888", + "size": { "w": 31, "h": 37 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/436.png b/public/images/pokemon/436.png index 1ca185baecdffac962d473bac667007fc1689054..0308cb7303a1cbefb9ae467dd7774273be8ae7f6 100644 GIT binary patch delta 402 zcmV;D0d4+)2AcyeiBL{Q4GJ0x0000DNk~Le0000V0000b2m=5B0F9BtzyJUM0drDE zLIAGL9O;oE6o3ETb18(SLZ!uH#paZ?jF8(6%K!iX33O6UQvm<}|NsC0|Nl1vo74aR z0U1d|K~y-6ol{E|#2^Thpmgqk;^AxU$5{-U_8AmNLTi_RM%xF*VBRNSh~i8?uQP3M zEzK6hZ#@7a!xKnf!Tk+ZR0ANguiTnoN0(R=wD5R@aDP!@T}OhqR|$SRheo0^gZ-H1 z&?g)Td8~L2tzqmV6DW>bB-nKkKZ1QTA!Z=**AwT&g^Fm1%Qnj+DsV~3@309L~{|HNlex>gze_D3Yf8_NkFOAb)=`T$ATY38C&dZ`j0+*3ZI=bvx)y(a9 w?kK8dmiHA#I(ECkNigm9JE_=!KE?Cy7o$cCK(e)JzW@LL07*qoM6N<$g8bvPCIA2c literal 769 zcmV+c1OEJpP)dYJ3`0#)YV-eJe67XVn(&}@=1w2Xmt+Kt2y3B;W%=LXYaGFOf>I)UKQ2sAS3=lc zTp@Ja_Tofv_h@19dMODff_tr{Gpv+w`z?-ySm8C?&Dw8$UQ5D-3Cd$UF$f2OyO$qP z<9f0Q=Ly=f^})VR8>0^^xG+IG!#?3FCHk;2t`PbezTty0hZz?L^9--y?(UOeW0-L! zbVgH9YajFWI~fwrgvG3X;v~c*R5KB0g4bRVudC?eGh?fggfk&t6Y54FT%6!?xy0C~ z_HPnQ!I{v%@%!Qz|N1K7?ceu3f$-ymPiAPllVx~~i-fj#K6dGO83|Vj;Spn_wo4fg z8Q$YOK`*^`hwXOOMo|EqCx|lY;{A<`{#v<$3xrl=t(2(Md+nNcK4x_%Tp^V4c8}7y z72mt(A;$&6><-Ffb&u=w05}sG!^-UqoR6U2m;V%J!m>3WPtUlFGoe@bp<(M8YJ{8p z0%yXqE!}LVel%Q!Ghy*EcRy%x0eDS8LcRgH*OVl{ z2}-@DBmpl$VkHF$(j~~eCP|5j%GlDo&mk)kql@hGYMJ-^qPqTsdgt`lO<5i04pg-5Q=wdB~gM>ymPM! z5~SjtdQFm`q#!Q}ZdN!<;6>5RN>4;)B_%hDK~|F8tZ=4sE6HwF*beZb=w`*HBk`i> zX2q+qUxL90LMeN&N4W%Q6}b=)1ug`{g7Tr$0X|R55VBPx*q#jOat{FPFGUVQxA#z@ z!{H5-ddAZ_syl{BUnYQVFK+vX0RpmD6rxQ9NdWWMskcimz!iTm0pjPJRyM#0 zv#(ilH!0)3^$3;CgI3V*3-D%n)p{$nepbdX9DrSxWj}$s12tSZAby`m!z{phPR^Da ziA5IpYd>1QV63Uj^kXjlSTNUhGF8`EpdX)1C&v7za?FsW_8~9eOAq3rTS*T62T}YA UASeW>hyVZp07*qoM6N<$f@g!9)c^nh literal 637 zcmeAS@N?(olHy`uVBq!ia0vp^DImQPr9)OP!V; zO+NZ;LhrS1paf${kY6x^!?PP{3=B+So-U3d6>)E`9`wBvAkvyBEhTX~nBm5RuRmhV z8TKt{&&sRtzWEM`LSfy|8yC_{~TK+%Q@a@xAioA*k>g8 zwB<+igEZs&_v}Ou9e5mkv3c#?g*Als^$ z$LsVS*M>A31o*nVJl>I2r^pi$yi7yzah|}P>HF8-58!R8PClvlD`?r>IU;Pc^q%kg zlKfKX^S=cK1;YGazb7frFLsm2=*#|T;O!^L&eAyRMEPV(7a0MTS&FBgvc6>lIxBhC zp35~|k_Y!*l>O{f!p>5@bZ%(3XZ&FaGwL_0VxpA>^5J*Vjwk( pR0?V)QTiCHT%L66@B8t{e2U8I9gA+bn*fswgQu&X%Q~loCIC|n7-Rqd diff --git a/public/images/pokemon/back/shiny/436.json b/public/images/pokemon/back/shiny/436.json index 4091e1a2e49..3342cc6577c 100644 --- a/public/images/pokemon/back/shiny/436.json +++ b/public/images/pokemon/back/shiny/436.json @@ -1,2666 +1,1019 @@ -{ - "textures": [ - { - "image": "436.png", - "format": "RGBA8888", - "size": { - "w": 100, - "h": 100 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 5, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 5, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 13, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 13, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 10, - "y": 3, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 2, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 3, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 1, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 1, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 11, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 13, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0097.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0098.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 11, - "y": 0, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0101.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0102.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0105.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 1, - "y": 14, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0106.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 1, - "y": 14, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0107.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 17, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0108.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 4, - "y": 17, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0111.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 18, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0112.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 8, - "y": 18, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0113.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 15, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0114.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 15, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0115.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0116.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 6, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0117.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0118.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 9, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0119.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0120.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 6, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0121.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0122.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 8, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0123.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0124.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 10, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0125.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0126.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 12, - "w": 34, - "h": 40 - }, - "frame": { - "x": 0, - "y": 0, - "w": 34, - "h": 40 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 15, - "y": 4, - "w": 33, - "h": 40 - }, - "frame": { - "x": 34, - "y": 0, - "w": 33, - "h": 40 - } - }, - { - "filename": "0096.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 15, - "y": 4, - "w": 33, - "h": 40 - }, - "frame": { - "x": 34, - "y": 0, - "w": 33, - "h": 40 - } - }, - { - "filename": "0103.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 33, - "h": 40 - }, - "frame": { - "x": 67, - "y": 0, - "w": 33, - "h": 40 - } - }, - { - "filename": "0104.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 0, - "y": 9, - "w": 33, - "h": 40 - }, - "frame": { - "x": 67, - "y": 0, - "w": 33, - "h": 40 - } - }, - { - "filename": "0099.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 34, - "h": 39 - }, - "frame": { - "x": 0, - "y": 40, - "w": 34, - "h": 39 - } - }, - { - "filename": "0100.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 7, - "y": 0, - "w": 34, - "h": 39 - }, - "frame": { - "x": 0, - "y": 40, - "w": 34, - "h": 39 - } - }, - { - "filename": "0109.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 21, - "w": 34, - "h": 39 - }, - "frame": { - "x": 34, - "y": 40, - "w": 34, - "h": 39 - } - }, - { - "filename": "0110.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 48, - "h": 60 - }, - "spriteSourceSize": { - "x": 9, - "y": 21, - "w": 34, - "h": 39 - }, - "frame": { - "x": 34, - "y": 40, - "w": 34, - "h": 39 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:810e866f20256a6beca964ab2fe2f997:4d42b12e9664d852f2391c8b56845681:0a3bacf3d680738b160c4c8ace3cda59$" - } -} +{ "frames": [ + { + "filename": "0001.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0002.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0003.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0004.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0005.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0006.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0007.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0008.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0009.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0010.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0011.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0012.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0013.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0014.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0015.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0016.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0017.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0018.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0019.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0020.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0021.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0022.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0023.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0024.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0025.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0026.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0027.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0028.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0029.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0030.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0031.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0032.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0033.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0034.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0035.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0036.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0037.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0038.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0039.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0040.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0041.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0042.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0043.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0044.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0045.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0046.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0047.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0048.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0049.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0050.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0051.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0052.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0053.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0054.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0055.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0056.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0057.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0058.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0059.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 6, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0060.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 6, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0061.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0062.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0063.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0064.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0065.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0066.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0067.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0068.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 6, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0069.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0070.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0071.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0072.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0073.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0074.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0075.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0076.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0077.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 4, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0078.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 11, "y": 4, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0079.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 3, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0080.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 3, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0081.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 4, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0082.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 4, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0083.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0084.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0085.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0086.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0087.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0088.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 12, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0089.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0090.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0091.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0092.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0093.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0094.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 14, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0095.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 16, "y": 5, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0096.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 16, "y": 5, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0097.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 1, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0098.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 12, "y": 1, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0099.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 0, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0100.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 0, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0101.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 5, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0102.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 4, "y": 5, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0103.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0104.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 0, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0105.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 15, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0106.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 2, "y": 15, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0107.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 18, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0108.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 5, "y": 18, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0109.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 22, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0110.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 10, "y": 22, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0111.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 19, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0112.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 9, "y": 19, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0113.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 16, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0114.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 16, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0115.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0116.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 7, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0117.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0118.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 10, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0119.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0120.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 7, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0121.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0122.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 9, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0123.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0124.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 11, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0125.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + }, + { + "filename": "0126.png", + "frame": { "x": 0, "y": 0, "w": 32, "h": 38 }, + "rotated": false, + "trimmed": true, + "spriteSourceSize": { "x": 8, "y": 13, "w": 32, "h": 38 }, + "sourceSize": { "w": 48, "h": 60 } + } + ], + "meta": { + "app": "https://www.aseprite.org/", + "version": "1.3.7-dev", + "image": "436.png", + "format": "I8", + "size": { "w": 32, "h": 38 }, + "scale": "1" + } +} diff --git a/public/images/pokemon/back/shiny/436.png b/public/images/pokemon/back/shiny/436.png index b3d6dc4df6185bfaf3c2ffa14c1bfa243e0fb529..d766a912555771ff696cae5fedcfc7c92b872c4d 100644 GIT binary patch delta 374 zcmV-+0g3+o1djtQiBL{Q4GJ0x0000DNk~Le0000W0000c2m=5B0FwP_+W-In0drDE zLIAGL9O;oEB!35VQchC<|NsC0|No_bruzT@0ToF^K~y-6eUn)h#UKbnP0;Rt$xjA{ z`lx)g9&>90LMeN&N4W%Q6}b=)1ug`{g7Tr$0X|R55VBPx*q#jOat{FPFGUVQxA#z@ z!{H5-ddAZ_syl{BUnYQVFK+vX0RpmD6rxQ9NdWWMskcimz!iTm0pjPJRyM#0 zv#(ilH!0)3^$3;CgI3V*3-D%n)p{$nepbdX9DrSxWj}$s12tSZAby`m!z{phPR^Da ziA5IpYd>1QV63Uj^kXjlSTNUhGF8`EpdX)1C&v7za?FsW_8~9eOAq3rTS*T62T}YA UASeW>hyVZp07*qoM6N<$f@g!9)c^nh literal 637 zcmeAS@N?(olHy`uVBq!ia0vp^DImhE&A8y?W61PJl>jqO_F6?O=u*6Tbe4 zHD}njq(LXhLEv&d=SSn73r{8sX=c~0f46?Ia^=U8S^v{z1pjkvku2wUr`^`m^kJWo z;M0~L(GSv$@87c%J#^r4@R7$mwOY%c8g735`RSjo2m4B%Sl>Tiy#7Ppt-6!^_xfJT z9&#|A;=4w0_WzA*SQ8h-K8|n=Juhh7;SY|1nddm8i4d|@o zU3)IqbV(lEdr|hYQwcju`O>`|rFkOvrYe43xn7pN_4~QAhW)Q+T-4m7zbg9L43T^D zw+Jr$@b9pUz@2a&L**UoGj}sLNi*EYU@)7_m$3OQh$=YplEHlrmD zQ%e@aAPAEnWAA_B;cM;3Sqz)@85BrDYnOmV+Xs4Q-Y1}o;!HoUGi`7!%@)LOJpdxz z6G&gd{S8)B10b@m+?rrVmsk_DaDRkwQDI$2g11)*et$fNMxrx={g~#^CmacRtauKs zVeBIlD2`hs*mV&J?@}p$T6WRD=k+Ns_0wMIFHHGcdHUwg%c4aBmyu06y6joi%dYJ3`0#)YV-eJe67XVn(&}@ z=1w2Xmt+Kt2y3B;W%=LXYaGFOf>I)UKQ2sAS3=lcTp@Ja_Tofv_h@19dMODff_tr{ zGpv+w`z?-ySm8C?&Dw8$UQ5D-e+kNCJTV9dg1eU=QR8~D3Fisgvh~5fPaC5TE4VO0 zJHtNVD<%4{F|H8$8NT6zF^3r!2=ffD;qLB}VPlwaCUizqP-`Fa_B$C8&V zBvdmIXM)#W5wEN0<1=Hcl7urMUK8p@AY7c_a=FCVr}l3WOu?DZzw!Ixe;5DyD&g(l z_dS8|3JCmR|(+}W23f984nrW<2*qxy?2N0cGgBw0Gua? zGV0>}jg0+=9O z6B@(H?G2odpx>AO6lcP+e>EUa&$x^;p;!2!Ve1)cgq!^WXTq{A-E60RG+cx;Vev9| zKWgm3nXve54&F7-gfn6G3}Nq@bHbT0Pj+Hp0B6EtW`}Sl`~@$1JOO#lqY}_c3KE1* z$h{^>pgqXFrXV4oAH1d{Aqn)FiiA7?cuhe5j%GlDo&mk) zkql@hGYMJ-^qPqTsdgt`lO<5i04pg-5Q=wdB~gM>ymPM!5~SjtdQFm`q#!Q}ZdN!< z;6>5RN>4;)B_%hDNI_PT-K=n?ax2MhR@e^kqUdJDrX%s9=w`*Mv0sALbl4FIABaEU kX3sd|j5E$S(^b From c01fff49c4e653de11bde37a52a9fb61f393e29d Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sun, 6 Oct 2024 14:31:11 -0700 Subject: [PATCH 20/70] [Beta P1] Fix regression in Metal Burst caused by #3974 (#4589) Also adds a regression test for the scenario --- src/phases/move-phase.ts | 6 +-- src/test/moves/metal_burst.test.ts | 80 ++++++++++++++++++++++++++++ src/test/utils/helpers/moveHelper.ts | 8 +-- 3 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 src/test/moves/metal_burst.test.ts diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 6272358aa85..10cc062ea3b 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -375,11 +375,7 @@ export class MovePhase extends BattlePhase { protected resolveCounterAttackTarget() { if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { if (this.pokemon.turnData.attacksReceived.length) { - const attacker = this.pokemon.scene.getPokemonById(this.pokemon.turnData.attacksReceived[0].sourceId); - - if (attacker?.isActive(true)) { - this.targets[0] = attacker.getBattlerIndex(); - } + this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; // account for metal burst and comeuppance hitting remaining targets in double battles // counterattack will redirect to remaining ally if original attacker faints diff --git a/src/test/moves/metal_burst.test.ts b/src/test/moves/metal_burst.test.ts new file mode 100644 index 00000000000..3b32dd322a3 --- /dev/null +++ b/src/test/moves/metal_burst.test.ts @@ -0,0 +1,80 @@ +import { BattlerIndex } from "#app/battle"; +import { MoveResult } from "#app/field/pokemon"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Metal Burst", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.METAL_BURST, Moves.FISSURE, Moves.PRECIPICE_BLADES ]) + .ability(Abilities.PURE_POWER) + .startingLevel(10) + .battleType("double") + .disableCrits() + .enemySpecies(Species.PICHU) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.TACKLE); + }); + + it("should redirect target if intended target faints", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.FEEBAS ]); + + const [ , enemy2 ] = game.scene.getEnemyField(); + + game.move.select(Moves.METAL_BURST); + game.move.select(Moves.FISSURE, 1, BattlerIndex.ENEMY); + + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER); + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2 ]); + + await game.phaseInterceptor.to("MoveEndPhase"); + await game.move.forceHit(); + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to("MoveEndPhase"); + + expect(enemy2.isFullHp()).toBe(false); + }); + + it("should not crash if both opponents faint before the move is used", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.ARCEUS ]); + + const [ enemy1, enemy2 ] = game.scene.getEnemyField(); + + game.move.select(Moves.METAL_BURST); + game.move.select(Moves.PRECIPICE_BLADES, 1); + + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER); + await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2); + + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2 ]); + + await game.phaseInterceptor.to("MoveEndPhase"); + await game.move.forceHit(); + await game.phaseInterceptor.to("MoveEndPhase"); + await game.phaseInterceptor.to("BerryPhase"); + + expect(enemy1.isFainted()).toBe(true); + expect(enemy2.isFainted()).toBe(true); + expect(game.scene.getPlayerField()[0].getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); + }); +}); diff --git a/src/test/utils/helpers/moveHelper.ts b/src/test/utils/helpers/moveHelper.ts index a53fa521785..a0667d91f4c 100644 --- a/src/test/utils/helpers/moveHelper.ts +++ b/src/test/utils/helpers/moveHelper.ts @@ -13,8 +13,8 @@ import { GameManagerHelper } from "./gameManagerHelper"; */ export class MoveHelper extends GameManagerHelper { /** - * Intercepts `MoveEffectPhase` and mocks the hitCheck's - * return value to `true` {@linkcode MoveEffectPhase.hitCheck}. + * Intercepts {@linkcode MoveEffectPhase} and mocks the + * {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `true`. * Used to force a move to hit. */ async forceHit(): Promise { @@ -23,8 +23,8 @@ export class MoveHelper extends GameManagerHelper { } /** - * Intercepts `MoveEffectPhase` and mocks the hitCheck's - * return value to `false` {@linkcode MoveEffectPhase.hitCheck}. + * Intercepts {@linkcode MoveEffectPhase} and mocks the + * {@linkcode MoveEffectPhase.hitCheck | hitCheck}'s return value to `false`. * Used to force a move to miss. * @param firstTargetOnly Whether the move should force miss on the first target only, in the case of multi-target moves. */ From a259ccfc34fbca7b9fdc7dc9f5eee5172791b091 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Sun, 6 Oct 2024 23:29:57 -0400 Subject: [PATCH 21/70] [Beta][Test] Fix Scale Shot flaky test (#4564) Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- src/test/moves/scale_shot.test.ts | 57 ++++++++++++++++++------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/test/moves/scale_shot.test.ts b/src/test/moves/scale_shot.test.ts index 2730d05306d..e4d768fa13a 100644 --- a/src/test/moves/scale_shot.test.ts +++ b/src/test/moves/scale_shot.test.ts @@ -1,3 +1,5 @@ +import { BattlerIndex } from "#app/battle"; +import { allMoves } from "#app/data/move"; import { DamagePhase } from "#app/phases/damage-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; @@ -8,7 +10,7 @@ import { Species } from "#enums/species"; import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, it, expect } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; describe("Moves - Scale Shot", () => { let phaserGame: Phaser.Game; @@ -30,45 +32,54 @@ describe("Moves - Scale Shot", () => { .moveset([ Moves.SCALE_SHOT ]) .battleType("single") .disableCrits() - .starterSpecies(Species.MINCCINO) .ability(Abilities.NO_GUARD) .passiveAbility(Abilities.SKILL_LINK) - .enemyAbility(Abilities.SHEER_FORCE) - .enemyPassiveAbility(Abilities.STALL) - .enemyMoveset(Moves.SKILL_SWAP) - .enemyLevel(5); + .enemyMoveset(Moves.SPLASH) + .enemyLevel(3); }); it("applies stat changes after last hit", async () => { - await game.classicMode.startBattle([ Species.FORRETRESS ]); + game.override.enemySpecies(Species.FORRETRESS); + + await game.classicMode.startBattle([ Species.MINCCINO ]); const minccino = game.scene.getPlayerPokemon()!; game.move.select(Moves.SCALE_SHOT); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to(DamagePhase); + + //check that stats haven't changed after one or two hits have occurred await game.phaseInterceptor.to(MoveEffectPhase); - expect (minccino?.getStatStage(Stat.DEF)).toBe(0); - expect (minccino?.getStatStage(Stat.SPD)).toBe(0); + expect(minccino.getStatStage(Stat.DEF)).toBe(0); + expect(minccino.getStatStage(Stat.SPD)).toBe(0); + + //check that stats changed on last hit await game.phaseInterceptor.to(MoveEndPhase); - expect (minccino.getStatStage(Stat.DEF)).toBe(-1); - expect (minccino.getStatStage(Stat.SPD)).toBe(1); + expect(minccino.getStatStage(Stat.DEF)).toBe(-1); + expect(minccino.getStatStage(Stat.SPD)).toBe(1); }); it("unaffected by sheer force", async () => { - await game.classicMode.startBattle([ Species.WOBBUFFET ]); + const moveToCheck = allMoves[Moves.SCALE_SHOT]; + const basePower = moveToCheck.power; + + game.override.enemySpecies(Species.WOBBUFFET); + + vi.spyOn(moveToCheck, "calculateBattlePower"); + + await game.classicMode.startBattle([ Species.MINCCINO ]); const minccino = game.scene.getPlayerPokemon()!; - const wobbuffet = game.scene.getEnemyPokemon()!; - wobbuffet.setStat(Stat.HP, 100, true); - wobbuffet.hp = 100; + game.move.select(Moves.SCALE_SHOT); await game.phaseInterceptor.to(TurnEndPhase); - const hpafter1 = wobbuffet.hp; + //effect not nullified by sheer force - expect (minccino.getStatStage(Stat.DEF)).toBe(-1); - expect (minccino.getStatStage(Stat.SPD)).toBe(1); - game.move.select(Moves.SCALE_SHOT); - await game.phaseInterceptor.to(MoveEndPhase); - const hpafter2 = wobbuffet.hp; - //check damage not boosted- make damage before sheer force a little lower than theoretical boosted sheer force damage - expect (100 - hpafter1).toBe(hpafter1 - hpafter2); + expect(minccino.getStatStage(Stat.DEF)).toBe(-1); + expect(minccino.getStatStage(Stat.SPD)).toBe(1); + + //power not boosted by sheer force + expect(moveToCheck.calculateBattlePower).toHaveReturnedWith(basePower); }); }); From f5fa478eb8afbfda7f7468e6fc535abe7fd1e03c Mon Sep 17 00:00:00 2001 From: Acelynn Zhang <102631387+acelynnzhang@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:01:15 -0500 Subject: [PATCH 22/70] [P1] Fix crash when starting a challenge run after revisiting challenge select screen (#4603) Ensure EncounterPhase initializes correctly at the start of the game after revisting the challenge selection screen. Fixes #4520 --- src/ui/starter-select-ui-handler.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 98a563301e4..9623d78c51e 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -46,6 +46,7 @@ import { StarterContainer } from "#app/ui/starter-container"; import { DropDownColumn, FilterBar } from "#app/ui/filter-bar"; import { ScrollBar } from "#app/ui/scroll-bar"; import { SelectChallengePhase } from "#app/phases/select-challenge-phase"; +import { EncounterPhase } from "#app/phases/encounter-phase"; import { TitlePhase } from "#app/phases/title-phase"; import { Abilities } from "#enums/abilities"; import { getPassiveCandyCount, getValueReductionCandyCounts, getSameSpeciesEggCandyCounts } from "#app/data/balance/starters"; @@ -3468,6 +3469,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.clearPhaseQueue(); if (this.scene.gameMode.isChallenge) { this.scene.pushPhase(new SelectChallengePhase(this.scene)); + this.scene.pushPhase(new EncounterPhase(this.scene, false)); } else { this.scene.pushPhase(new TitlePhase(this.scene)); } From a1ca7e632b7054fb7f03fae6b4f5fe2d560dbd2c Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Tue, 8 Oct 2024 05:32:51 -0700 Subject: [PATCH 23/70] [Move] Triple Arrows effect chance for stat change is now 50% (#4543) * Triple Arrows effect chance for stat change is now properly 50% * Add tsdocs to `StatStageChangeAttr` * Add test for Serene Grace interaction * Fix linting --------- Co-authored-by: Mumble <171087428+frutescens@users.noreply.github.com> --- src/data/move.ts | 49 +++++++++++++---------- src/test/moves/triple_arrows.test.ts | 60 ++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 src/test/moves/triple_arrows.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index f795d265336..08c00829b48 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -970,13 +970,16 @@ export class MoveEffectAttr extends MoveAttr { public lastHitOnly: boolean; /** Should this effect only apply on the first target hit? */ public firstTargetOnly: boolean; + /** Overrides the secondary effect chance for this attr if set. */ + public effectChanceOverride?: number; - constructor(selfTarget?: boolean, trigger?: MoveEffectTrigger, firstHitOnly: boolean = false, lastHitOnly: boolean = false, firstTargetOnly: boolean = false) { + constructor(selfTarget?: boolean, trigger?: MoveEffectTrigger, firstHitOnly: boolean = false, lastHitOnly: boolean = false, firstTargetOnly: boolean = false, effectChanceOverride?: number) { super(selfTarget); - this.trigger = trigger !== undefined ? trigger : MoveEffectTrigger.POST_APPLY; + this.trigger = trigger ?? MoveEffectTrigger.POST_APPLY; this.firstHitOnly = firstHitOnly; this.lastHitOnly = lastHitOnly; this.firstTargetOnly = firstTargetOnly; + this.effectChanceOverride = effectChanceOverride; } /** @@ -1001,15 +1004,15 @@ export class MoveEffectAttr extends MoveAttr { /** * Gets the used move's additional effect chance. - * If user's ability has MoveEffectChanceMultiplierAbAttr or IgnoreMoveEffectsAbAttr modifies the base chance. + * Chance is modified by {@linkcode MoveEffectChanceMultiplierAbAttr} and {@linkcode IgnoreMoveEffectsAbAttr}. * @param user {@linkcode Pokemon} using this move - * @param target {@linkcode Pokemon} target of this move + * @param target {@linkcode Pokemon | Target} of this move * @param move {@linkcode Move} being used - * @param selfEffect {@linkcode Boolean} if move targets user. - * @returns Move chance value. + * @param selfEffect `true` if move targets user. + * @returns Move effect chance value. */ getMoveChance(user: Pokemon, target: Pokemon, move: Move, selfEffect?: Boolean, showAbility?: Boolean): integer { - const moveChance = new Utils.NumberHolder(move.chance); + const moveChance = new Utils.NumberHolder(this.effectChanceOverride ?? move.chance); applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, moveChance, move, target, selfEffect, showAbility); @@ -2752,14 +2755,17 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { /** * Attribute used for moves that change stat stages - * @param stats {@linkcode BattleStat} array of stats to be changed - * @param stages stages by which to change the stats, from -6 to 6 - * @param selfTarget whether the changes are applied to the user (true) or the target (false) - * @param condition {@linkcode MoveConditionFunc} optional condition to trigger the stat change - * @param firstHitOnly whether the stat change only applies on the first hit of a multi hit move - * @param moveEffectTrigger {@linkcode MoveEffectTrigger} the trigger for the effect to take place - * @param firstTargetOnly whether, if this is a multi target move, to only apply the effect after the first target is hit, rather than once for each target - * @param lastHitOnly whether the effect should only apply after the last hit of a multi hit move + * + * @param stats {@linkcode BattleStat} Array of stat(s) to change + * @param stages How many stages to change the stat(s) by, [-6, 6] + * @param selfTarget `true` if the move is self-targetting + * @param condition {@linkcode MoveConditionFunc} Optional condition to be checked in order to apply the changes + * @param showMessage `true` to display a message; default `true` + * @param firstHitOnly `true` if only the first hit of a multi hit move should cause a stat stage change; default `false` + * @param moveEffectTrigger {@linkcode MoveEffectTrigger} When the stat change should trigger; default {@linkcode MoveEffectTrigger.HIT} + * @param firstTargetOnly `true` if a move that hits multiple pokemon should only trigger the stat change if it hits at least one pokemon, rather than once per hit pokemon; default `false` + * @param lastHitOnly `true` if the effect should only apply after the last hit of a multi hit move; default `false` + * @param effectChanceOverride Will override the move's normal secondary effect chance if specified * * @extends MoveEffectAttr * @see {@linkcode apply} @@ -2767,14 +2773,14 @@ export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr { export class StatStageChangeAttr extends MoveEffectAttr { public stats: BattleStat[]; public stages: integer; - private condition: MoveConditionFunc | null; + private condition?: MoveConditionFunc | null; private showMessage: boolean; - constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false, lastHitOnly: boolean = false) { - super(selfTarget, moveEffectTrigger, firstHitOnly, lastHitOnly, firstTargetOnly); + constructor(stats: BattleStat[], stages: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false, lastHitOnly: boolean = false, effectChanceOverride?: number) { + super(selfTarget, moveEffectTrigger, firstHitOnly, lastHitOnly, firstTargetOnly, effectChanceOverride); this.stats = stats; this.stages = stages; - this.condition = condition!; // TODO: is this bang correct? + this.condition = condition; this.showMessage = showMessage; } @@ -9556,9 +9562,8 @@ export function initMoves() { new AttackMove(Moves.TRIPLE_ARROWS, Type.FIGHTING, MoveCategory.PHYSICAL, 90, 100, 10, 30, 0, 8) .makesContact(false) .attr(HighCritAttr) - .attr(StatStageChangeAttr, [ Stat.DEF ], -1) - .attr(FlinchAttr) - .partial(), + .attr(StatStageChangeAttr, [ Stat.DEF ], -1, undefined, undefined, undefined, undefined, undefined, undefined, undefined, 50) + .attr(FlinchAttr), new AttackMove(Moves.INFERNAL_PARADE, Type.GHOST, MoveCategory.SPECIAL, 60, 100, 15, 30, 0, 8) .attr(StatusEffectAttr, StatusEffect.BURN) .attr(MovePowerMultiplierAttr, (user, target, move) => target.status ? 2 : 1), diff --git a/src/test/moves/triple_arrows.test.ts b/src/test/moves/triple_arrows.test.ts new file mode 100644 index 00000000000..98ad29997df --- /dev/null +++ b/src/test/moves/triple_arrows.test.ts @@ -0,0 +1,60 @@ +import { allMoves, FlinchAttr, StatStageChangeAttr } from "#app/data/move"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; + +describe("Moves - Triple Arrows", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const tripleArrows = allMoves[Moves.TRIPLE_ARROWS]; + const flinchAttr = tripleArrows.getAttrs(FlinchAttr)[0]; + const defDropAttr = tripleArrows.getAttrs(StatStageChangeAttr)[0]; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(Abilities.BALL_FETCH) + .moveset([ Moves.TRIPLE_ARROWS ]) + .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyAbility(Abilities.STURDY) + .enemyMoveset(Moves.SPLASH); + + vi.spyOn(flinchAttr, "getMoveChance"); + vi.spyOn(defDropAttr, "getMoveChance"); + }); + + it("has a 30% flinch chance and 50% defense drop chance", async () => { + await game.classicMode.startBattle([ Species.FEEBAS ]); + + game.move.select(Moves.TRIPLE_ARROWS); + await game.phaseInterceptor.to("BerryPhase"); + + expect(flinchAttr.getMoveChance).toHaveReturnedWith(30); + expect(defDropAttr.getMoveChance).toHaveReturnedWith(50); + }); + + it("is affected normally by Serene Grace", async () => { + game.override.ability(Abilities.SERENE_GRACE); + await game.classicMode.startBattle([ Species.FEEBAS ]); + + game.move.select(Moves.TRIPLE_ARROWS); + await game.phaseInterceptor.to("BerryPhase"); + + expect(flinchAttr.getMoveChance).toHaveReturnedWith(60); + expect(defDropAttr.getMoveChance).toHaveReturnedWith(100); + }); +}); From 9bb6398385847ac5594100597b87e87ed09fe279 Mon Sep 17 00:00:00 2001 From: chaosgrimmon <31082757+chaosgrimmon@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:03:47 -0400 Subject: [PATCH 24/70] [Sprite] Fix stray pixels in Kirlia's animation (#4612) * [Sprite] Fix Kirlia padding bleedover * [Sprite] Fix shiny Kirlia padding bleedover --- public/images/pokemon/281.json | 8 ++++---- public/images/pokemon/shiny/281.json | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/images/pokemon/281.json b/public/images/pokemon/281.json index 8e865cdc935..cb1a43f256f 100644 --- a/public/images/pokemon/281.json +++ b/public/images/pokemon/281.json @@ -399,13 +399,13 @@ "x": 0, "y": 6, "w": 36, - "h": 55 + "h": 54 }, "frame": { "x": 72, "y": 55, "w": 36, - "h": 55 + "h": 54 } }, { @@ -420,13 +420,13 @@ "x": 0, "y": 6, "w": 36, - "h": 55 + "h": 54 }, "frame": { "x": 72, "y": 55, "w": 36, - "h": 55 + "h": 54 } }, { diff --git a/public/images/pokemon/shiny/281.json b/public/images/pokemon/shiny/281.json index 684be77edf9..64e10c7f9a6 100644 --- a/public/images/pokemon/shiny/281.json +++ b/public/images/pokemon/shiny/281.json @@ -399,13 +399,13 @@ "x": 0, "y": 6, "w": 36, - "h": 55 + "h": 54 }, "frame": { "x": 72, "y": 55, "w": 36, - "h": 55 + "h": 54 } }, { @@ -420,13 +420,13 @@ "x": 0, "y": 6, "w": 36, - "h": 55 + "h": 54 }, "frame": { "x": 72, "y": 55, "w": 36, - "h": 55 + "h": 54 } }, { From 6e10f6600fe30793c7ff56ce98f27ae1b3486d5e Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:08:25 +0200 Subject: [PATCH 25/70] [P2] Fix damage achievements not awarding (#4613) --- src/field/pokemon.ts | 2 +- src/phases/level-up-phase.ts | 2 +- src/system/achv.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a0ad4e8a52f..241524df1b9 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2758,7 +2758,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (damage > 0) { if (source.isPlayer()) { - this.scene.validateAchvs(DamageAchv, damage); + this.scene.validateAchvs(DamageAchv, new Utils.NumberHolder(damage)); if (damage > this.scene.gameData.gameStats.highestDamage) { this.scene.gameData.gameStats.highestDamage = damage; } diff --git a/src/phases/level-up-phase.ts b/src/phases/level-up-phase.ts index a99e038acba..a2fa8a16533 100644 --- a/src/phases/level-up-phase.ts +++ b/src/phases/level-up-phase.ts @@ -28,7 +28,7 @@ export class LevelUpPhase extends PlayerPartyMemberPokemonPhase { this.scene.gameData.gameStats.highestLevel = this.level; } - this.scene.validateAchvs(LevelAchv, new Utils.IntegerHolder(this.level)); + this.scene.validateAchvs(LevelAchv, new Utils.NumberHolder(this.level)); const pokemon = this.getPokemon(); const prevStats = pokemon.stats.slice(0); diff --git a/src/system/achv.ts b/src/system/achv.ts index 7bac631d7aa..7329dd41fd5 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -109,7 +109,7 @@ export class DamageAchv extends Achv { damageAmount: integer; constructor(localizationKey: string, name: string, damageAmount: integer, iconImage: string, score: integer) { - super(localizationKey, name, "", iconImage, score, (_scene: BattleScene, args: any[]) => (args[0] as Utils.NumberHolder).value >= this.damageAmount); + super(localizationKey, name, "", iconImage, score, (_scene: BattleScene, args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.damageAmount); this.damageAmount = damageAmount; } } @@ -118,7 +118,7 @@ export class HealAchv extends Achv { healAmount: integer; constructor(localizationKey: string, name: string, healAmount: integer, iconImage: string, score: integer) { - super(localizationKey, name, "", iconImage, score, (_scene: BattleScene, args: any[]) => (args[0] as Utils.NumberHolder).value >= this.healAmount); + super(localizationKey, name, "", iconImage, score, (_scene: BattleScene, args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.healAmount); this.healAmount = healAmount; } } @@ -127,7 +127,7 @@ export class LevelAchv extends Achv { level: integer; constructor(localizationKey: string, name: string, level: integer, iconImage: string, score: integer) { - super(localizationKey, name, "", iconImage, score, (scene: BattleScene, args: any[]) => (args[0] as Utils.IntegerHolder).value >= this.level); + super(localizationKey, name, "", iconImage, score, (scene: BattleScene, args: any[]) => (args[0] instanceof Utils.NumberHolder ? args[0].value : args[0]) >= this.level); this.level = level; } } From 0ede7b057d9699e6c3a1d00ae2b2d55e6363f927 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:10:54 +0200 Subject: [PATCH 26/70] [P3][UI] Fix egg gacha overlay not getting cleared properly (#4600) --- src/overrides.ts | 1 + src/ui/egg-gacha-ui-handler.ts | 49 ++++++++++++++++++++--------- src/ui/starter-select-ui-handler.ts | 3 +- 3 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/overrides.ts b/src/overrides.ts index 27886ded2f2..211d430a835 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -156,6 +156,7 @@ class DefaultOverrides { readonly EGG_VARIANT_OVERRIDE: VariantTier | null = null; readonly EGG_FREE_GACHA_PULLS_OVERRIDE: boolean = false; readonly EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; + readonly UNLIMITED_EGG_COUNT_OVERRIDE: boolean = false; // ------------------------- // MYSTERY ENCOUNTER OVERRIDES diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 56cd5299949..366f1604740 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -34,6 +34,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { private cursorObj: Phaser.GameObjects.Image; private transitioning: boolean; private transitionCancelled: boolean; + private summaryFinished: boolean; private defaultText: string; private scale: number = 0.1666666667; @@ -479,7 +480,12 @@ export default class EggGachaUiHandler extends MessageUiHandler { } showSummary(eggs: Egg[]): void { - this.transitioning = false; + // the overlay will appear faster if the egg pulling animation was skipped + const overlayEaseInDuration = this.getDelayValue(750); + + this.summaryFinished = false; + this.transitionCancelled = false; + this.setTransitioning(true); this.eggGachaSummaryContainer.setVisible(true); const eggScale = eggs.length < 20 ? 1 : 0.5; @@ -488,12 +494,14 @@ export default class EggGachaUiHandler extends MessageUiHandler { targets: this.eggGachaOverlay, alpha: 0.5, ease: "Sine.easeOut", - duration: 750, + duration: overlayEaseInDuration, onComplete: () => { const rowItems = 5; const rows = Math.ceil(eggs.length / rowItems); const cols = Math.min(eggs.length, rowItems); const height = this.eggGachaOverlay.displayHeight - this.eggGachaMessageBox.displayHeight; + + // Create sprites for each egg const eggContainers = eggs.map((egg, t) => { const col = t % rowItems; const row = Math.floor(t / rowItems); @@ -515,14 +523,24 @@ export default class EggGachaUiHandler extends MessageUiHandler { return ret; }); - eggContainers.forEach((eggContainer, e) => { - this.scene.tweens.add({ - targets: eggContainer, - delay: this.getDelayValue(e * 100), - duration: this.getDelayValue(350), - scale: eggScale, - ease: "Sine.easeOut" - }); + // If action/cancel was pressed when the overlay was easing in, show all eggs at once + // Otherwise show the eggs one by one with a small delay between each + eggContainers.forEach((eggContainer, index) => { + const delay = !this.transitionCancelled ? this.getDelayValue(index * 100) : 0; + this.scene.time.delayedCall(delay, () => + this.scene.tweens.add({ + targets: eggContainer, + duration: this.getDelayValue(350), + scale: eggScale, + ease: "Sine.easeOut", + onComplete: () => { + if (index === eggs.length - 1) { + this.setTransitioning(false); + this.summaryFinished = true; + } + } + })); + }); } }); @@ -540,6 +558,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaSummaryContainer.setAlpha(1); this.eggGachaSummaryContainer.removeAll(true); this.setTransitioning(false); + this.summaryFinished = false; this.eggGachaOptionsContainer.setVisible(true); } }); @@ -613,7 +632,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { } else { if (this.eggGachaSummaryContainer.visible) { - if (button === Button.ACTION || button === Button.CANCEL) { + if (this.summaryFinished && (button === Button.ACTION || button === Button.CANCEL)) { this.hideSummary(); success = true; } @@ -625,7 +644,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (!this.scene.gameData.voucherCounts[VoucherType.REGULAR] && !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { error = true; this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (this.scene.gameData.eggs.length < 99) { + } else if (this.scene.gameData.eggs.length < 99 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { this.consumeVouchers(VoucherType.REGULAR, 1); } @@ -640,7 +659,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (!this.scene.gameData.voucherCounts[VoucherType.PLUS] && !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { error = true; this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (this.scene.gameData.eggs.length < 95) { + } else if (this.scene.gameData.eggs.length < 95 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { this.consumeVouchers(VoucherType.PLUS, 1); } @@ -657,7 +676,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { || (this.cursor === 3 && !this.scene.gameData.voucherCounts[VoucherType.PREMIUM] && !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE)) { error = true; this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (this.scene.gameData.eggs.length < 90) { + } else if (this.scene.gameData.eggs.length < 90 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { if (this.cursor === 3) { if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { this.consumeVouchers(VoucherType.PREMIUM, 1); @@ -678,7 +697,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (!this.scene.gameData.voucherCounts[VoucherType.GOLDEN] && !Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { error = true; this.showError(i18next.t("egg:notEnoughVouchers")); - } else if (this.scene.gameData.eggs.length < 75) { + } else if (this.scene.gameData.eggs.length < 75 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) { if (!Overrides.EGG_FREE_GACHA_PULLS_OVERRIDE) { this.consumeVouchers(VoucherType.GOLDEN, 1); } diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 9623d78c51e..5bbe765947e 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -1789,7 +1789,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: `x${sameSpeciesEggCost} ${i18next.t("starterSelectUiHandler:sameSpeciesEgg")}`, handler: () => { - if (this.scene.gameData.eggs.length < 99 && (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= sameSpeciesEggCost)) { + if ((this.scene.gameData.eggs.length < 99 || Overrides.UNLIMITED_EGG_COUNT_OVERRIDE) + && (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= sameSpeciesEggCost)) { if (!Overrides.FREE_CANDY_UPGRADE_OVERRIDE) { starterData.candyCount -= sameSpeciesEggCost; } From 57a967890a40e2625f2de3b8ba6a97dcc7688388 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:11:21 +0200 Subject: [PATCH 27/70] [Offline P1] Fix wrong local save being deleted when creating a new run (#4598) --- src/system/game-data.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/system/game-data.ts b/src/system/game-data.ts index eada3d270ef..0d2f35ae728 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -1125,10 +1125,16 @@ export class GameData { }); } + /** + * Delete the session data at the given slot when overwriting a save file + * For deleting the session of a finished run, use {@linkcode tryClearSession} + * @param slotId the slot to clear + * @returns Promise with result `true` if the session was deleted successfully, `false` otherwise + */ deleteSession(slotId: integer): Promise { return new Promise(resolve => { if (bypassLogin) { - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); return resolve(true); } @@ -1139,7 +1145,7 @@ export class GameData { Utils.apiFetch(`savedata/session/delete?slot=${slotId}&clientSessionId=${clientSessionId}`, true).then(response => { if (response.ok) { loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); resolve(true); } return response.text(); @@ -1190,7 +1196,9 @@ export class GameData { /** - * Attempt to clear session data. After session data is removed, attempt to update user info so the menu updates + * Attempt to clear session data after the end of a run + * After session data is removed, attempt to update user info so the menu updates + * To delete an unfinished run instead, use {@linkcode deleteSession} */ async tryClearSession(scene: BattleScene, slotId: integer): Promise<[success: boolean, newClear: boolean]> { let result: [boolean, boolean] = [ false, false ]; @@ -1204,7 +1212,7 @@ export class GameData { if (response.ok) { loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); } const jsonResponse: PokerogueApiClearSessionData = await response.json(); From 39cebb76d01f801377841bdc1a65161cee4835b6 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:30:48 -0700 Subject: [PATCH 28/70] [Bug] i18n messages files fix (#4611) * fix matching for i18n messages files * update public/locales head --- public/locales | 2 +- src/plugins/i18n.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/public/locales b/public/locales index b44ee217378..fc4a1effd51 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit b44ee2173788018ffd5dc6b7b7fa159be5b9d514 +Subproject commit fc4a1effd5170def3c8314208a52cd0d8e6913ef diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index be4c6983c0a..84c12b91df3 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -81,6 +81,8 @@ const namespaceMap = { miscDialogue: "dialogue-misc", battleSpecDialogue: "dialogue-final-boss", doubleBattleDialogue: "dialogue-double-battle", + splashMessages: "splash-texts", + mysteryEncounterMessages: "mystery-encounter-messages", }; //#region Functions From d8c914c7686c0fa4c74609ebed97c03f65a46f3e Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Tue, 8 Oct 2024 10:44:23 -0700 Subject: [PATCH 29/70] [Beta P3] Fix i18n namespaces map for `mysteryEncounterMessages` from `mystery-encounter-messages` -> `mystery-encounter-texts` (#4617) Something I missed in #4611 --- src/plugins/i18n.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 84c12b91df3..d24484bbf9d 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -82,7 +82,7 @@ const namespaceMap = { battleSpecDialogue: "dialogue-final-boss", doubleBattleDialogue: "dialogue-double-battle", splashMessages: "splash-texts", - mysteryEncounterMessages: "mystery-encounter-messages", + mysteryEncounterMessages: "mystery-encounter-texts", }; //#region Functions From deb2035610091eedb884c60ee5f2f4cd14365f50 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:30:28 +0200 Subject: [PATCH 30/70] [Beta][P2] Fix Grip Claw (#4614) * [Beta][P2] Fix Grip Claw * Add test for Grip Claw * [test] improve grip claw's test readability * PR feedback --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/modifier/modifier.ts | 7 ++- src/test/items/grip_claw.test.ts | 96 +++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 29 deletions(-) diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index b658d3b5277..6c9b5db1bca 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3084,11 +3084,12 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { * Steals an item from a set of target Pokemon. * This prioritizes high-tier held items when selecting the item to steal. * @param pokemon The {@linkcode Pokemon} holding this item + * @param target The {@linkcode Pokemon} to steal from (optional) * @param _args N/A * @returns `true` if an item was stolen; false otherwise. */ - override apply(pokemon: Pokemon, ..._args: unknown[]): boolean { - const opponents = this.getTargets(pokemon); + override apply(pokemon: Pokemon, target?: Pokemon, ..._args: unknown[]): boolean { + const opponents = this.getTargets(pokemon, target); if (!opponents.length) { return false; @@ -3187,7 +3188,7 @@ export class TurnHeldItemTransferModifier extends HeldItemTransferModifier { * @see {@linkcode HeldItemTransferModifier} */ export class ContactHeldItemTransferChanceModifier extends HeldItemTransferModifier { - private chance: number; + public readonly chance: number; constructor(type: ModifierType, pokemonId: number, chancePercent: number, stackCount?: number) { super(type, pokemonId, stackCount); diff --git a/src/test/items/grip_claw.test.ts b/src/test/items/grip_claw.test.ts index 9d44a9e4672..2909549af87 100644 --- a/src/test/items/grip_claw.test.ts +++ b/src/test/items/grip_claw.test.ts @@ -1,16 +1,14 @@ import { BattlerIndex } from "#app/battle"; -import { allMoves } from "#app/data/move"; -import { Abilities } from "#app/enums/abilities"; -import { BerryType } from "#app/enums/berry-type"; -import { Moves } from "#app/enums/moves"; -import { Species } from "#app/enums/species"; -import { MoveEndPhase } from "#app/phases/move-end-phase"; +import Pokemon from "#app/field/pokemon"; +import { ContactHeldItemTransferChanceModifier } from "#app/modifier/modifier"; +import { Abilities } from "#enums/abilities"; +import { BerryType } from "#enums/berry-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phase from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -// 20 seconds - describe("Items - Grip Claw", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -30,39 +28,85 @@ describe("Items - Grip Claw", () => { game.override .battleType("double") - .moveset([ Moves.POPULATION_BOMB, Moves.SPLASH ]) + .moveset([ Moves.TACKLE, Moves.SPLASH, Moves.ATTRACT ]) .startingHeldItems([ - { name: "GRIP_CLAW", count: 5 }, // TODO: Find a way to mock the steal chance of grip claw - { name: "MULTI_LENS", count: 3 }, + { name: "GRIP_CLAW", count: 1 }, ]) .enemySpecies(Species.SNORLAX) - .ability(Abilities.KLUTZ) + .enemyAbility(Abilities.UNNERVE) + .ability(Abilities.UNNERVE) .enemyMoveset(Moves.SPLASH) .enemyHeldItems([ { name: "BERRY", type: BerryType.SITRUS, count: 2 }, { name: "BERRY", type: BerryType.LUM, count: 2 }, ]) - .startingLevel(100) .enemyLevel(100); - vi.spyOn(allMoves[Moves.POPULATION_BOMB], "accuracy", "get").mockReturnValue(100); }); - it( - "should only steal items from the attack target", - async () => { - await game.startBattle([ Species.PANSEAR, Species.ROWLET ]); + it("should steal items on contact and only from the attack target", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); - const enemyPokemon = game.scene.getEnemyField(); + const [ playerPokemon, ] = game.scene.getPlayerField(); - const enemyHeldItemCt = enemyPokemon.map(p => p.getHeldItems.length); + const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier; + vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100); - game.move.select(Moves.POPULATION_BOMB, 0, BattlerIndex.ENEMY); - game.move.select(Moves.SPLASH, 1); + const enemyPokemon = game.scene.getEnemyField(); - await game.phaseInterceptor.to(MoveEndPhase, false); + const playerHeldItemCount = getHeldItemCount(playerPokemon); + const enemy1HeldItemCount = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCount = getHeldItemCount(enemyPokemon[1]); + expect(enemy2HeldItemCount).toBeGreaterThan(0); - expect(enemyPokemon[1].getHeldItems.length).toBe(enemyHeldItemCt[1]); - } - ); + game.move.select(Moves.TACKLE, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("BerryPhase", false); + + const playerHeldItemCountAfter = getHeldItemCount(playerPokemon); + const enemy1HeldItemCountsAfter = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCountsAfter = getHeldItemCount(enemyPokemon[1]); + + expect(playerHeldItemCountAfter).toBe(playerHeldItemCount + 1); + expect(enemy1HeldItemCountsAfter).toBe(enemy1HeldItemCount); + expect(enemy2HeldItemCountsAfter).toBe(enemy2HeldItemCount - 1); + }); + + it("should not steal items when using a targetted, non attack move", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); + + const [ playerPokemon, ] = game.scene.getPlayerField(); + + const gripClaw = playerPokemon.getHeldItems()[0] as ContactHeldItemTransferChanceModifier; + vi.spyOn(gripClaw, "chance", "get").mockReturnValue(100); + + const enemyPokemon = game.scene.getEnemyField(); + + const playerHeldItemCount = getHeldItemCount(playerPokemon); + const enemy1HeldItemCount = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCount = getHeldItemCount(enemyPokemon[1]); + expect(enemy2HeldItemCount).toBeGreaterThan(0); + + game.move.select(Moves.ATTRACT, 0, BattlerIndex.ENEMY_2); + game.move.select(Moves.SPLASH, 1); + + await game.phaseInterceptor.to("BerryPhase", false); + + const playerHeldItemCountAfter = getHeldItemCount(playerPokemon); + const enemy1HeldItemCountsAfter = getHeldItemCount(enemyPokemon[0]); + const enemy2HeldItemCountsAfter = getHeldItemCount(enemyPokemon[1]); + + expect(playerHeldItemCountAfter).toBe(playerHeldItemCount); + expect(enemy1HeldItemCountsAfter).toBe(enemy1HeldItemCount); + expect(enemy2HeldItemCountsAfter).toBe(enemy2HeldItemCount); + }); }); + +/* + * Gets the total number of items a Pokemon holds + */ +function getHeldItemCount(pokemon: Pokemon) { + return pokemon.getHeldItems().reduce((currentTotal, item) => currentTotal + item.getStackCount(), 0); +} + From d2c579cf2a2d22639ba1c3998f36e27f4e5d5b3a Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Wed, 9 Oct 2024 20:32:20 +0200 Subject: [PATCH 31/70] [P2] Prevent generating Pokemon with duplicate IDs in daily runs (#4623) --- src/phases/title-phase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases/title-phase.ts b/src/phases/title-phase.ts index 115e4f640a2..58683cf8ec8 100644 --- a/src/phases/title-phase.ts +++ b/src/phases/title-phase.ts @@ -196,7 +196,7 @@ export class TitlePhase extends Phase { this.scene.gameMode = getGameMode(GameModes.DAILY); this.scene.setSeed(seed); - this.scene.resetSeed(1); + this.scene.resetSeed(0); this.scene.money = this.scene.gameMode.getStartingMoney(); From ffe941d235f6f4923bc5b87e0d29701a609c4bba Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:04:13 -0700 Subject: [PATCH 32/70] [Feature][UI] Save Preview (#4410) * Making 3 Option UI real * idk anymore * Revert "Making 3 Option UI real" This reverts commit beaad44c1eb098a09cfd2d04043d878d24f494c1. * Let's see * Current issues - scrolling upwards and correct cursor landing * argh * Fixed reactive scrolling * Adding ME handling * set up descriptions * Cleaned up UI i think * stupid alder * Added double trainer handling + changed enum name * Apply suggestions from code review Thank you Moka! Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> * Arrow Visibility now depends on Session Slot hasData * documentation * Simplified calls to revertSessionSlot + changed function name per feedback * Fixed scrollCursor issue. * added comment * Update src/ui/save-slot-select-ui-handler.ts Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> * Fixed sound played + added better conditional * Balance Team.... * ME related changes * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> * Update src/data/mystery-encounters/mystery-encounter.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/data/mystery-encounters/mystery-encounter.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Sending Doubles-fix * eslint.. --------- Co-authored-by: frutescens Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/battle-scene.ts | 6 +- .../encounters/a-trainers-test-encounter.ts | 1 + .../encounters/absolute-avarice-encounter.ts | 1 + .../an-offer-you-cant-refuse-encounter.ts | 1 + .../encounters/berries-abound-encounter.ts | 1 + .../encounters/bug-type-superfan-encounter.ts | 1 + .../encounters/clowning-around-encounter.ts | 1 + .../encounters/dancing-lessons-encounter.ts | 1 + .../encounters/dark-deal-encounter.ts | 1 + .../encounters/delibirdy-encounter.ts | 1 + .../department-store-sale-encounter.ts | 1 + .../encounters/field-trip-encounter.ts | 1 + .../encounters/fiery-fallout-encounter.ts | 1 + .../encounters/fight-or-flight-encounter.ts | 1 + .../encounters/fun-and-games-encounter.ts | 1 + .../global-trade-system-encounter.ts | 1 + .../encounters/lost-at-sea-encounter.ts | 1 + .../mysterious-challengers-encounter.ts | 1 + .../encounters/mysterious-chest-encounter.ts | 1 + .../encounters/part-timer-encounter.ts | 1 + .../encounters/safari-zone-encounter.ts | 1 + .../shady-vitamin-dealer-encounter.ts | 1 + .../slumbering-snorlax-encounter.ts | 1 + .../teleporting-hijinks-encounter.ts | 1 + .../the-expert-pokemon-breeder-encounter.ts | 1 + .../the-pokemon-salesman-encounter.ts | 1 + .../encounters/the-strong-stuff-encounter.ts | 1 + .../the-winstrate-challenge-encounter.ts | 1 + .../encounters/training-session-encounter.ts | 1 + .../encounters/trash-to-treasure-encounter.ts | 1 + .../encounters/uncommon-breed-encounter.ts | 1 + .../encounters/weird-dream-encounter.ts | 1 + .../mystery-encounters/mystery-encounter.ts | 14 +- src/ui/run-history-ui-handler.ts | 3 +- src/ui/run-info-ui-handler.ts | 136 +++++++++++++++--- src/ui/save-slot-select-ui-handler.ts | 94 +++++++++--- 36 files changed, 246 insertions(+), 38 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index cc6934f20d1..a586b565e13 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -3161,13 +3161,17 @@ export default class BattleScene extends SceneBase { /** * Loads or generates a mystery encounter * @param encounterType used to load session encounter when restarting game, etc. + * @param canBypass optional boolean to indicate that the request is coming from a function that needs to access a Mystery Encounter outside of gameplay requirements * @returns */ - getMysteryEncounter(encounterType?: MysteryEncounterType): MysteryEncounter { + getMysteryEncounter(encounterType?: MysteryEncounterType, canBypass?: boolean): MysteryEncounter { // Loading override or session encounter let encounter: MysteryEncounter | null; if (!isNullOrUndefined(Overrides.MYSTERY_ENCOUNTER_OVERRIDE) && allMysteryEncounters.hasOwnProperty(Overrides.MYSTERY_ENCOUNTER_OVERRIDE)) { encounter = allMysteryEncounters[Overrides.MYSTERY_ENCOUNTER_OVERRIDE]; + } else if (canBypass) { + encounter = allMysteryEncounters[encounterType ?? -1]; + return encounter; } else { encounter = !isNullOrUndefined(encounterType) ? allMysteryEncounters[encounterType] : null; } diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index 13e187179d4..f3b886ac0ac 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -128,6 +128,7 @@ export const ATrainersTestEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts index c98947a3f93..70b2d50fe99 100644 --- a/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts +++ b/src/data/mystery-encounters/encounters/absolute-avarice-encounter.ts @@ -166,6 +166,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts index e445a8f481d..ab892ae00f2 100644 --- a/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts +++ b/src/data/mystery-encounters/encounters/an-offer-you-cant-refuse-encounter.ts @@ -64,6 +64,7 @@ export const AnOfferYouCantRefuseEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 3e5d75727b1..095f8a8473b 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -110,6 +110,7 @@ export const BerriesAboundEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts index 20c0569c725..b5d47cf6912 100644 --- a/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts +++ b/src/data/mystery-encounters/encounters/bug-type-superfan-encounter.ts @@ -276,6 +276,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts index 6c028d4619a..be52ab42c9d 100644 --- a/src/data/mystery-encounters/encounters/clowning-around-encounter.ts +++ b/src/data/mystery-encounters/encounters/clowning-around-encounter.ts @@ -148,6 +148,7 @@ export const ClowningAroundEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts index cb07bf06a81..0f784739777 100644 --- a/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts +++ b/src/data/mystery-encounters/encounters/dancing-lessons-encounter.ts @@ -102,6 +102,7 @@ export const DancingLessonsEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts index fc8c8088d58..5ad6630386f 100644 --- a/src/data/mystery-encounters/encounters/dark-deal-encounter.ts +++ b/src/data/mystery-encounters/encounters/dark-deal-encounter.ts @@ -117,6 +117,7 @@ export const DarkDealEncounter: MysteryEncounter = .withSceneWaveRangeRequirement(30, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1]) .withScenePartySizeRequirement(2, 6, true) // Must have at least 2 pokemon in party .withCatchAllowed(true) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index a11dc8cbe72..5686d0f6ce5 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -84,6 +84,7 @@ export const DelibirdyEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts index 1505768f968..10034d19263 100644 --- a/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts +++ b/src/data/mystery-encounters/encounters/department-store-sale-encounter.ts @@ -51,6 +51,7 @@ export const DepartmentStoreSaleEncounter: MysteryEncounter = }, ]) .withAutoHideIntroVisuals(false) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/field-trip-encounter.ts b/src/data/mystery-encounters/encounters/field-trip-encounter.ts index a75e5ef6a77..bf5fb28163b 100644 --- a/src/data/mystery-encounters/encounters/field-trip-encounter.ts +++ b/src/data/mystery-encounters/encounters/field-trip-encounter.ts @@ -52,6 +52,7 @@ export const FieldTripEncounter: MysteryEncounter = }, ]) .withAutoHideIntroVisuals(false) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts index 9e7652e24ea..d44e7bae596 100644 --- a/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts +++ b/src/data/mystery-encounters/encounters/fiery-fallout-encounter.ts @@ -122,6 +122,7 @@ export const FieryFalloutEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index a04521839fe..380662ca817 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -120,6 +120,7 @@ export const FightOrFlightEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts index 2b103e0a293..549faa01fa1 100644 --- a/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts +++ b/src/data/mystery-encounters/encounters/fun-and-games-encounter.ts @@ -76,6 +76,7 @@ export const FunAndGamesEncounter: MysteryEncounter = text: `${namespace}:intro_dialogue`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index 7b929ea5e7b..bafc1901e5e 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -96,6 +96,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts index 6ca131543b4..8fd46982dc1 100644 --- a/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts +++ b/src/data/mystery-encounters/encounters/lost-at-sea-encounter.ts @@ -50,6 +50,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts index 08536f44245..fb25976ebd8 100644 --- a/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-challengers-encounter.ts @@ -125,6 +125,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts index 2b44f6ee33d..1eb1c4cb13e 100644 --- a/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts +++ b/src/data/mystery-encounters/encounters/mysterious-chest-encounter.ts @@ -61,6 +61,7 @@ export const MysteriousChestEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/part-timer-encounter.ts b/src/data/mystery-encounters/encounters/part-timer-encounter.ts index 2f41aa96677..17a3a366569 100644 --- a/src/data/mystery-encounters/encounters/part-timer-encounter.ts +++ b/src/data/mystery-encounters/encounters/part-timer-encounter.ts @@ -69,6 +69,7 @@ export const PartTimerEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index d029460e617..c6b04b7aca6 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -54,6 +54,7 @@ export const SafariZoneEncounter: MysteryEncounter = text: `${namespace}:intro`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts index 89cb572962c..c70048ade07 100644 --- a/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts +++ b/src/data/mystery-encounters/encounters/shady-vitamin-dealer-encounter.ts @@ -62,6 +62,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index d0b4cc13301..3a4bf465a78 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -88,6 +88,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index 63ab178c52a..01e241f63d4 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -58,6 +58,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index aca46f1598b..4515736b30a 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -196,6 +196,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 2720653e654..95f359547e4 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -53,6 +53,7 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter = speaker: `${namespace}:speaker`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index d1d5b484129..7ee57d36027 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -117,6 +117,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts index ad4f2dd8498..c7cb23fe6f8 100644 --- a/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-winstrate-challenge-encounter.ts @@ -94,6 +94,7 @@ export const TheWinstrateChallengeEncounter: MysteryEncounter = return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/training-session-encounter.ts b/src/data/mystery-encounters/encounters/training-session-encounter.ts index ff993f339cb..10bb956636b 100644 --- a/src/data/mystery-encounters/encounters/training-session-encounter.ts +++ b/src/data/mystery-encounters/encounters/training-session-encounter.ts @@ -52,6 +52,7 @@ export const TrainingSessionEncounter: MysteryEncounter = text: `${namespace}:intro`, } ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts index be2cd796386..c2a0426bceb 100644 --- a/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts +++ b/src/data/mystery-encounters/encounters/trash-to-treasure-encounter.ts @@ -54,6 +54,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = text: `${namespace}:intro`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index 51c1d5f963f..13594f273d9 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -125,6 +125,7 @@ export const UncommonBreedEncounter: MysteryEncounter = scene.time.delayedCall(500, () => scene.playSound("battle_anims/PRSFX- Spotlight2")); return true; }) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 17f33c27645..6e2f8352480 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -125,6 +125,7 @@ export const WeirdDreamEncounter: MysteryEncounter = text: `${namespace}:intro_dialogue`, }, ]) + .setLocalizationKey(`${namespace}`) .withTitle(`${namespace}:title`) .withDescription(`${namespace}:description`) .withQuery(`${namespace}:query`) diff --git a/src/data/mystery-encounters/mystery-encounter.ts b/src/data/mystery-encounters/mystery-encounter.ts index aabb0a3311a..7e175957e21 100644 --- a/src/data/mystery-encounters/mystery-encounter.ts +++ b/src/data/mystery-encounters/mystery-encounter.ts @@ -190,7 +190,7 @@ export default class MysteryEncounter implements IMysteryEncounter { secondaryPokemon?: PlayerPokemon[]; // #region Post-construct / Auto-populated params - + localizationKey: string; /** * Dialogue object containing all the dialogue, messages, tooltips, etc. for an encounter */ @@ -264,6 +264,7 @@ export default class MysteryEncounter implements IMysteryEncounter { Object.assign(this, encounter); } this.encounterTier = this.encounterTier ?? MysteryEncounterTier.COMMON; + this.localizationKey = this.localizationKey ?? ""; this.dialogue = this.dialogue ?? {}; this.spriteConfigs = this.spriteConfigs ? [ ...this.spriteConfigs ] : []; // Default max is 1 for ROGUE encounters, 2 for others @@ -528,6 +529,7 @@ export class MysteryEncounterBuilder implements Partial { options: [MysteryEncounterOption, MysteryEncounterOption, ...MysteryEncounterOption[]]; enemyPartyConfigs: EnemyPartyConfig[] = []; + localizationKey: string = ""; dialogue: MysteryEncounterDialogue = {}; requirements: EncounterSceneRequirement[] = []; primaryPokemonRequirements: EncounterPokemonRequirement[] = []; @@ -632,6 +634,16 @@ export class MysteryEncounterBuilder implements Partial { return this.withIntroSpriteConfigs(spriteConfigs).withIntroDialogue(dialogue); } + /** + * Sets the localization key used by the encounter + * @param localizationKey the string used as the key + * @returns `this` + */ + setLocalizationKey(localizationKey: string): this { + this.localizationKey = localizationKey; + return this; + } + /** * OPTIONAL */ diff --git a/src/ui/run-history-ui-handler.ts b/src/ui/run-history-ui-handler.ts index f4de9b21963..20de7fd832c 100644 --- a/src/ui/run-history-ui-handler.ts +++ b/src/ui/run-history-ui-handler.ts @@ -12,6 +12,7 @@ import { BattleType } from "../battle"; import { RunEntry } from "../system/game-data"; import { PlayerGender } from "#enums/player-gender"; import { TrainerVariant } from "../field/trainer"; +import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; export type RunSelectCallback = (cursor: number) => void; @@ -104,7 +105,7 @@ export default class RunHistoryUiHandler extends MessageUiHandler { if (button === Button.ACTION) { const cursor = this.cursor + this.scrollCursor; if (this.runs[cursor]) { - this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.runs[cursor].entryData, true); + this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.runs[cursor].entryData, RunDisplayMode.RUN_HISTORY, true); } else { return false; } diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index d5f04f90e5b..39927f8e071 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -5,6 +5,7 @@ import { SessionSaveData } from "../system/game-data"; import { TextStyle, addTextObject, addBBCodeTextObject, getTextColor } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import { getPokeballAtlasKey } from "#app/data/pokeball"; import * as Utils from "../utils"; import PokemonData from "../system/pokemon-data"; import i18next from "i18next"; @@ -22,6 +23,8 @@ import * as Modifier from "../modifier/modifier"; import { Species } from "#enums/species"; import { PlayerGender } from "#enums/player-gender"; import { SettingKeyboard } from "#app/system/settings/settings-keyboard"; +import { getBiomeName } from "#app/data/balance/biomes"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; /** * RunInfoUiMode indicates possible overlays of RunInfoUiHandler. @@ -34,6 +37,11 @@ enum RunInfoUiMode { ENDING_ART } +export enum RunDisplayMode { + RUN_HISTORY, + SESSION_PREVIEW +} + /** * Some variables are protected because this UI class will most likely be extended in the future to display more information. * These variables will most likely be shared across 'classes' aka pages. @@ -41,6 +49,7 @@ enum RunInfoUiMode { * For now, I leave as is. */ export default class RunInfoUiHandler extends UiHandler { + protected runDisplayMode: RunDisplayMode; protected runInfo: SessionSaveData; protected isVictory: boolean; protected pageMode: RunInfoUiMode; @@ -66,6 +75,7 @@ export default class RunInfoUiHandler extends UiHandler { // The import of the modifiersModule is loaded here to sidestep async/await issues. this.modifiersModule = Modifier; this.runContainer.setVisible(false); + this.scene.loadImage("encounter_exclaim", "mystery-encounters"); } /** @@ -87,9 +97,15 @@ export default class RunInfoUiHandler extends UiHandler { this.runContainer.add(gameStatsBg); const run = args[0]; + this.runDisplayMode = args[1]; + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + this.runInfo = this.scene.gameData.parseSessionData(JSON.stringify(run.entry)); + this.isVictory = run.isVictory ?? false; + } else if (this.runDisplayMode === RunDisplayMode.SESSION_PREVIEW) { + this.runInfo = args[0]; + } // Assigning information necessary for the UI's creation - this.runInfo = this.scene.gameData.parseSessionData(JSON.stringify(run.entry)); - this.isVictory = run.isVictory; + this.pageMode = RunInfoUiMode.MAIN; // Creates Header and adds to this.runContainer @@ -102,7 +118,11 @@ export default class RunInfoUiHandler extends UiHandler { const runResultWindow = addWindow(this.scene, 0, 0, this.statsBgWidth - 11, 65); runResultWindow.setOrigin(0, 0); this.runResultContainer.add(runResultWindow); - this.parseRunResult(); + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + this.parseRunResult(); + } else if (this.runDisplayMode === RunDisplayMode.SESSION_PREVIEW) { + this.parseRunStatus(); + } // Creates Run Info Container this.runInfoContainer = this.scene.add.container(0, 89); @@ -226,6 +246,66 @@ export default class RunInfoUiHandler extends UiHandler { this.runContainer.add(this.runResultContainer); } + /** + * This function is used when the Run Info UI is used to preview a Session. + * It edits {@linkcode runResultContainer}, but most importantly - does not display the negative results of a Mystery Encounter or any details of a trainer's party. + * Trainer Parties are replaced with their sprites, names, and their party size. + * Mystery Encounters contain sprites associated with MEs + the title of the specific ME. + */ + private parseRunStatus() { + const runStatusText = addTextObject(this.scene, 6, 5, `${i18next.t("saveSlotSelectUiHandler:wave")} ${this.runInfo.waveIndex} - ${getBiomeName(this.runInfo.arena.biome)}`, TextStyle.WINDOW, { fontSize : "65px", lineSpacing: 0.1 }); + + const enemyContainer = this.scene.add.container(0, 0); + this.runResultContainer.add(enemyContainer); + if (this.runInfo.battleType === BattleType.WILD) { + if (this.runInfo.enemyParty.length === 1) { + this.parseWildSingleDefeat(enemyContainer); + } else if (this.runInfo.enemyParty.length === 2) { + this.parseWildDoubleDefeat(enemyContainer); + } + } else if (this.runInfo.battleType === BattleType.TRAINER) { + this.showTrainerSprites(enemyContainer); + const row_limit = 3; + this.runInfo.enemyParty.forEach((p, i) => { + const pokeball = this.scene.add.sprite(0, 0, "pb"); + pokeball.setFrame(getPokeballAtlasKey(p.pokeball)); + pokeball.setScale(0.5); + pokeball.setPosition(52 + ((i % row_limit) * 8), (i <= 2) ? 18 : 25); + enemyContainer.add(pokeball); + }); + const trainerObj = this.runInfo.trainer.toTrainer(this.scene); + const RIVAL_TRAINER_ID_THRESHOLD = 375; + let trainerName = ""; + if (this.runInfo.trainer.trainerType >= RIVAL_TRAINER_ID_THRESHOLD) { + trainerName = (trainerObj.variant === TrainerVariant.FEMALE) ? i18next.t("trainerNames:rival_female") : i18next.t("trainerNames:rival"); + } else { + trainerName = trainerObj.getName(0, true); + } + const boxString = i18next.t(trainerObj.variant !== TrainerVariant.DOUBLE ? "battle:trainerAppeared" : "battle:trainerAppearedDouble", { trainerName: trainerName }).replace(/\n/g, " "); + const descContainer = this.scene.add.container(0, 0); + const textBox = addTextObject(this.scene, 0, 0, boxString, TextStyle.WINDOW, { fontSize : "35px", wordWrap: { width: 200 }}); + descContainer.add(textBox); + descContainer.setPosition(52, 29); + this.runResultContainer.add(descContainer); + } else if (this.runInfo.battleType === BattleType.MYSTERY_ENCOUNTER) { + const encounterExclaim = this.scene.add.sprite(0, 0, "encounter_exclaim"); + encounterExclaim.setPosition(34, 26); + encounterExclaim.setScale(0.65); + const subSprite = this.scene.add.sprite(56, -106, "pkmn__sub"); + subSprite.setScale(0.65); + subSprite.setPosition(34, 46); + const mysteryEncounterTitle = i18next.t(this.scene.getMysteryEncounter(this.runInfo.mysteryEncounterType as MysteryEncounterType, true).localizationKey + ":title"); + const descContainer = this.scene.add.container(0, 0); + const textBox = addTextObject(this.scene, 0, 0, mysteryEncounterTitle, TextStyle.WINDOW, { fontSize : "45px", wordWrap: { width: 160 }}); + descContainer.add(textBox); + descContainer.setPosition(47, 37); + this.runResultContainer.add([ encounterExclaim, subSprite, descContainer ]); + } + + this.runResultContainer.add(runStatusText); + this.runContainer.add(this.runResultContainer); + } + /** * This function is called to edit an enemyContainer to represent a loss from a defeat by a wild single Pokemon battle. * @param enemyContainer - container holding enemy visual and level information @@ -278,40 +358,58 @@ export default class RunInfoUiHandler extends UiHandler { } /** - * This edits a container to represent a loss from a defeat by a trainer battle. - * @param enemyContainer - container holding enemy visuals and level information - * The trainers are placed to the left of their party. - * Depending on the trainer icon, there may be overlap between the edges of the box or their party. (Capes...) - * - * Party Pokemon have their icons, terastalization status, and level shown. + * This loads the enemy sprites, positions, and scales them according to the current display mode of the RunInfo UI and then adds them to the container parameter. + * Used by {@linkcode parseRunStatus} and {@linkcode parseTrainerDefeat} + * @param enemyContainer a Phaser Container that should hold enemy sprites */ - private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) { + private showTrainerSprites(enemyContainer: Phaser.GameObjects.Container) { // Creating the trainer sprite and adding it to enemyContainer const tObj = this.runInfo.trainer.toTrainer(this.scene); - // Loads trainer assets on demand, as they are not loaded by default in the scene tObj.config.loadAssets(this.scene, this.runInfo.trainer.variant).then(() => { const tObjSpriteKey = tObj.config.getSpriteKey(this.runInfo.trainer.variant === TrainerVariant.FEMALE, false); const tObjSprite = this.scene.add.sprite(0, 5, tObjSpriteKey); - if (this.runInfo.trainer.variant === TrainerVariant.DOUBLE) { + if (this.runInfo.trainer.variant === TrainerVariant.DOUBLE && !tObj.config.doubleOnly) { const doubleContainer = this.scene.add.container(5, 8); tObjSprite.setPosition(-3, -3); const tObjPartnerSpriteKey = tObj.config.getSpriteKey(true, true); const tObjPartnerSprite = this.scene.add.sprite(5, -3, tObjPartnerSpriteKey); // Double Trainers have smaller sprites than Single Trainers - tObjPartnerSprite.setScale(0.20); - tObjSprite.setScale(0.20); - doubleContainer.add(tObjSprite); - doubleContainer.add(tObjPartnerSprite); - doubleContainer.setPosition(12, 38); + if (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) { + tObjPartnerSprite.setScale(0.20); + tObjSprite.setScale(0.20); + doubleContainer.add(tObjSprite); + doubleContainer.add(tObjPartnerSprite); + doubleContainer.setPosition(12, 38); + } else { + tObjSprite.setScale(0.55); + tObjSprite.setPosition(-9, -3); + tObjPartnerSprite.setScale(0.55); + doubleContainer.add([ tObjSprite, tObjPartnerSprite ]); + doubleContainer.setPosition(28, 40); + } enemyContainer.add(doubleContainer); } else { - tObjSprite.setScale(0.35, 0.35); - tObjSprite.setPosition(12, 28); + const scale = (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) ? 0.35 : 0.65; + const position = (this.runDisplayMode === RunDisplayMode.RUN_HISTORY) ? [ 12, 28 ] : [ 32, 36 ]; + tObjSprite.setScale(scale, scale); + tObjSprite.setPosition(position[0], position[1]); enemyContainer.add(tObjSprite); } }); + } + /** + * This edits a container to represent a loss from a defeat by a trainer battle. + * The trainers are placed to the left of their party. + * Depending on the trainer icon, there may be overlap between the edges of the box or their party. (Capes...) + * + * Party Pokemon have their icons, terastalization status, and level shown. + * @param enemyContainer - container holding enemy visuals and level information + */ + private parseTrainerDefeat(enemyContainer: Phaser.GameObjects.Container) { + // Loads and adds trainer sprites to the UI + this.showTrainerSprites(enemyContainer); // Determining which Terastallize Modifier belongs to which Pokemon // Creates a dictionary {PokemonId: TeraShardType} const teraPokemon = {}; diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 89b20322a68..bd1a7dd9ac4 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -10,6 +10,7 @@ import MessageUiHandler from "./message-ui-handler"; import { TextStyle, addTextObject } from "./text"; import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; +import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; const sessionSlotCount = 5; @@ -33,7 +34,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { private scrollCursor: integer = 0; - private cursorObj: Phaser.GameObjects.NineSlice | null; + private cursorObj: Phaser.GameObjects.Container | null; private sessionSlotsContainerInitialY: number; @@ -83,9 +84,11 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectCallback = args[1] as SaveSlotSelectCallback; this.saveSlotSelectContainer.setVisible(true); - this.populateSessionSlots(); - this.setScrollCursor(0); - this.setCursor(0); + this.populateSessionSlots() + .then(() => { + this.setScrollCursor(0); + this.setCursor(0); + }); return true; } @@ -147,21 +150,28 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { success = true; } } else { + const cursorPosition = this.cursor + this.scrollCursor; switch (button) { case Button.UP: if (this.cursor) { - success = this.setCursor(this.cursor - 1); + // Check to prevent cursor from accessing a negative index + success = (this.cursor === 0) ? this.setCursor(this.cursor) : this.setCursor(this.cursor - 1, cursorPosition); } else if (this.scrollCursor) { - success = this.setScrollCursor(this.scrollCursor - 1); + success = this.setScrollCursor(this.scrollCursor - 1, cursorPosition); } break; case Button.DOWN: if (this.cursor < 2) { - success = this.setCursor(this.cursor + 1); + success = this.setCursor(this.cursor + 1, this.cursor); } else if (this.scrollCursor < sessionSlotCount - 3) { - success = this.setScrollCursor(this.scrollCursor + 1); + success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition); } break; + case Button.RIGHT: + if (this.sessionSlots[cursorPosition].hasData && this.sessionSlots[cursorPosition].saveData) { + this.scene.ui.setOverlayMode(Mode.RUN_INFO, this.sessionSlots[cursorPosition].saveData, RunDisplayMode.SESSION_PREVIEW); + success = true; + } } } @@ -174,10 +184,10 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { return success || error; } - populateSessionSlots() { + async populateSessionSlots() { for (let s = 0; s < sessionSlotCount; s++) { const sessionSlot = new SessionSlot(this.scene, s); - sessionSlot.load(); + await sessionSlot.load(); this.scene.add.existing(sessionSlot); this.sessionSlotsContainer.add(sessionSlot); this.sessionSlots.push(sessionSlot); @@ -198,25 +208,74 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectMessageBoxContainer.setVisible(!!text?.length); } - setCursor(cursor: integer): boolean { + /** + * setCursor takes user navigation as an input and positions the cursor accordingly + * @param cursor the index provided to the cursor + * @param prevCursor the previous index occupied by the cursor - optional + * @returns `true` if the cursor position has changed | `false` if it has not + */ + override setCursor(cursor: integer, prevCursor?: integer): boolean { const changed = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); - this.cursorObj.setOrigin(0, 0); + this.cursorObj = this.scene.add.container(0, 0); + const cursorBox = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); + const rightArrow = this.scene.add.image(0, 0, "cursor"); + rightArrow.setPosition(160, 0); + rightArrow.setName("rightArrow"); + this.cursorObj.add([ cursorBox, rightArrow ]); this.sessionSlotsContainer.add(this.cursorObj); } - this.cursorObj.setPosition(4, 4 + (cursor + this.scrollCursor) * 56); + const cursorPosition = cursor + this.scrollCursor; + const cursorIncrement = cursorPosition * 56; + if (this.sessionSlots[cursorPosition] && this.cursorObj) { + const hasData = this.sessionSlots[cursorPosition].hasData; + // If the session slot lacks session data, it does not move from its default, central position. + // Only session slots with session data will move leftwards and have a visible arrow. + if (!hasData) { + this.cursorObj.setPosition(151, 26 + cursorIncrement); + this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement); + } else { + this.cursorObj.setPosition(145, 26 + cursorIncrement); + this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement); + } + this.setArrowVisibility(hasData); + } + if (!Utils.isNullOrUndefined(prevCursor)) { + this.revertSessionSlot(prevCursor); + } return changed; } - setScrollCursor(scrollCursor: integer): boolean { + /** + * Helper function that resets the session slot position to its default central position + * @param prevCursor the previous location of the cursor + */ + revertSessionSlot(prevCursor: integer): void { + const sessionSlot = this.sessionSlots[prevCursor]; + if (sessionSlot) { + sessionSlot.setPosition(0, prevCursor * 56); + } + } + + /** + * Helper function that checks if the session slot involved holds data or not + * @param hasData `true` if session slot contains data | 'false' if not + */ + setArrowVisibility(hasData: boolean): void { + if (this.cursorObj) { + const rightArrow = this.cursorObj?.getByName("rightArrow") as Phaser.GameObjects.Image; + rightArrow.setVisible(hasData); + } + } + + setScrollCursor(scrollCursor: integer, priorCursor?: integer): boolean { const changed = scrollCursor !== this.scrollCursor; if (changed) { this.scrollCursor = scrollCursor; - this.setCursor(this.cursor); + this.setCursor(this.cursor, priorCursor); this.scene.tweens.add({ targets: this.sessionSlotsContainer, y: this.sessionSlotsContainerInitialY - 56 * scrollCursor, @@ -254,6 +313,8 @@ class SessionSlot extends Phaser.GameObjects.Container { public hasData: boolean; private loadingLabel: Phaser.GameObjects.Text; + public saveData: SessionSaveData; + constructor(scene: BattleScene, slotId: integer) { super(scene, 0, slotId * 56); @@ -337,6 +398,7 @@ class SessionSlot extends Phaser.GameObjects.Container { return; } this.hasData = true; + this.saveData = sessionData; await this.setupWithData(sessionData); resolve(true); }); From f180b6070e8f61ca06fc50a12665bab961070653 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Wed, 9 Oct 2024 13:01:49 -0700 Subject: [PATCH 33/70] [Qol] Load i18n en locales during tests (#4553) * add: i18n backend support the backend is being supported by using msw which will import the correct file from the local locales folder * fix: tests to no longer rely on static i18n keys * Update src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/ui/type-hints.test.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Update src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> * Fix typos Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> * Fix linting * update locales submodule update reference to `56eeb809eb5a2de40cfc5bc6128a78bef14deea9` (from `3ccef8472dd7cc7c362538489954cb8fdad27e5f`) --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- global.d.ts | 14 +++ src/test/abilities/ability_timing.test.ts | 10 +- src/test/items/toxic_orb.test.ts | 15 ++- .../a-trainers-test-encounter.test.ts | 4 +- .../absolute-avarice-encounter.test.ts | 3 +- ...an-offer-you-cant-refuse-encounter.test.ts | 5 +- .../encounters/field-trip-encounter.test.ts | 32 +++---- .../fiery-fallout-encounter.test.ts | 3 +- .../encounters/lost-at-sea-encounter.test.ts | 5 +- .../teleporting-hijinks-encounter.test.ts | 33 +++---- .../phases/mystery-encounter-phase.test.ts | 7 +- src/test/system/game_data.test.ts | 5 +- src/test/ui/starter-select.test.ts | 91 ++++++++++--------- src/test/ui/type-hints.test.ts | 7 +- src/test/vitest.setup.ts | 58 +++++++----- 15 files changed, 166 insertions(+), 126 deletions(-) create mode 100644 global.d.ts diff --git a/global.d.ts b/global.d.ts new file mode 100644 index 00000000000..f4dfa7d4cb2 --- /dev/null +++ b/global.d.ts @@ -0,0 +1,14 @@ +import type { SetupServerApi } from "msw/node"; + +export {}; + +declare global { + /** + * Only used in testing. + * Can technically be undefined/null but for ease of use we are going to assume it is always defined. + * Used to load i18n files exclusively. + * + * To set up your own server in a test see `game_data.test.ts` + */ + var i18nServer: SetupServerApi; +} diff --git a/src/test/abilities/ability_timing.test.ts b/src/test/abilities/ability_timing.test.ts index 1472f9eb429..e3264c2c1a8 100644 --- a/src/test/abilities/ability_timing.test.ts +++ b/src/test/abilities/ability_timing.test.ts @@ -1,13 +1,13 @@ import { BattleStyle } from "#app/enums/battle-style"; import { CommandPhase } from "#app/phases/command-phase"; import { TurnInitPhase } from "#app/phases/turn-init-phase"; -import i18next, { initI18n } from "#app/plugins/i18n"; +import i18next from "#app/plugins/i18n"; import { Mode } from "#app/ui/ui"; import { Abilities } from "#enums/abilities"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Ability Timing", () => { @@ -32,11 +32,10 @@ describe("Ability Timing", () => { .enemySpecies(Species.MAGIKARP) .enemyAbility(Abilities.INTIMIDATE) .ability(Abilities.BALL_FETCH); + vi.spyOn(i18next, "t"); }); it("should trigger after switch check", async () => { - initI18n(); - i18next.changeLanguage("en"); game.settings.battleStyle = BattleStyle.SWITCH; await game.classicMode.runToSummon([ Species.EEVEE, Species.FEEBAS ]); @@ -46,7 +45,6 @@ describe("Ability Timing", () => { }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(TurnInitPhase)); await game.phaseInterceptor.to("MessagePhase"); - const message = game.textInterceptor.getLatestMessage(); - expect(message).toContain("battle:statFell"); + expect(i18next.t).toHaveBeenCalledWith("battle:statFell", expect.objectContaining({ count: 1 })); }, 5000); }); diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index 35d6e77b209..a83fd3655e5 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -2,13 +2,13 @@ import { StatusEffect } from "#app/data/status-effect"; import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; import { MessagePhase } from "#app/phases/message-phase"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; -import i18next, { initI18n } from "#app/plugins/i18n"; +import i18next from "#app/plugins/i18n"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Items - Toxic orb", () => { @@ -39,11 +39,11 @@ describe("Items - Toxic orb", () => { game.override.startingHeldItems([{ name: "TOXIC_ORB", }]); + + vi.spyOn(i18next, "t"); }); it("TOXIC ORB", async () => { - initI18n(); - i18next.changeLanguage("en"); const moveToUse = Moves.GROWTH; await game.startBattle([ Species.MIGHTYENA, @@ -57,11 +57,10 @@ describe("Items - Toxic orb", () => { await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); // Toxic orb should trigger here await game.phaseInterceptor.run(MessagePhase); - const message = game.textInterceptor.getLatestMessage(); - expect(message).toContain("statusEffect:toxic.obtainSource"); + expect(i18next.t).toHaveBeenCalledWith("statusEffect:toxic.obtainSource", expect.anything()); + await game.phaseInterceptor.run(MessagePhase); - const message2 = game.textInterceptor.getLatestMessage(); - expect(message2).toBe("statusEffect:toxic.activation"); + expect(i18next.t).toHaveBeenCalledWith("statusEffect:toxic.activation", expect.anything()); expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC); }, 20000); }); diff --git a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index f24800eaa71..b1aa378d82a 100644 --- a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -16,6 +16,7 @@ import { EggTier } from "#enums/egg-type"; import { CommandPhase } from "#app/phases/command-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/aTrainersTest"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -106,7 +107,8 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(enemyField.length).toBe(1); expect(scene.currentBattle.trainer).toBeDefined(); - expect([ "trainerNames:buck", "trainerNames:cheryl", "trainerNames:marley", "trainerNames:mira", "trainerNames:riley" ].includes(scene.currentBattle.trainer!.config.name)).toBeTruthy(); + expect([ i18next.t("trainerNames:buck"), i18next.t("trainerNames:cheryl"), i18next.t("trainerNames:marley"), i18next.t("trainerNames:mira"), i18next.t("trainerNames:riley") ] + .map(name => name.toLowerCase()).includes(scene.currentBattle.trainer!.config.name)).toBeTruthy(); expect(enemyField[0]).toBeDefined(); }); diff --git a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts index 99a835cb6ae..a72a9fbb5a3 100644 --- a/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/absolute-avarice-encounter.test.ts @@ -16,6 +16,7 @@ import { Moves } from "#enums/moves"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/absoluteAvarice"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -146,7 +147,7 @@ describe("Absolute Avarice - Mystery Encounter", () => { const pokemonId = partyPokemon.id; const pokemonItems = scene.findModifiers(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId, true) as PokemonHeldItemModifier[]; - const revSeed = pokemonItems.find(i => i.type.name === "modifierType:ModifierType.REVIVER_SEED.name"); + const revSeed = pokemonItems.find(i => i.type.name === i18next.t("modifierType:ModifierType.REVIVER_SEED.name")); expect(revSeed).toBeDefined; expect(revSeed?.stackCount).toBe(1); } diff --git a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 77d5a842b47..9883b4332b9 100644 --- a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -17,6 +17,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species"; import { Moves } from "#enums/moves"; import { ShinyRateBoosterModifier } from "#app/modifier/modifier"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/anOfferYouCantRefuse"; /** Gyarados for Indimidate */ @@ -93,8 +94,8 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.strongestPokemon).toBeDefined(); expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.price).toBeDefined(); - expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.option2PrimaryAbility).toBe("ability:intimidate.name"); - expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe("ability:intimidate.name"); + expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.option2PrimaryAbility).toBe(i18next.t("ability:intimidate.name")); + expect(AnOfferYouCantRefuseEncounter.dialogueTokens?.moveOrAbility).toBe(i18next.t("ability:intimidate.name")); expect(AnOfferYouCantRefuseEncounter.misc.pokemon instanceof PlayerPokemon).toBeTruthy(); expect(AnOfferYouCantRefuseEncounter.misc?.price?.toString()).toBe(AnOfferYouCantRefuseEncounter.dialogueTokens?.price); expect(onInitResult).toBe(true); diff --git a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts index 232bad3c2b8..a6f925274c3 100644 --- a/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/field-trip-encounter.test.ts @@ -103,11 +103,11 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_attack"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_defense"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.DIRE_HIT.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_attack")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_defense")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.DIRE_HIT.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { @@ -150,11 +150,11 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_sp_atk"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_sp_def"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.DIRE_HIT.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_sp_atk")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_sp_def")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.DIRE_HIT.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { @@ -198,12 +198,12 @@ describe("Field Trip - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; expect(modifierSelectHandler.options.length).toEqual(5); - expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_accuracy"); - expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe("modifierType:TempStatStageBoosterItem.x_speed"); - expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe("modifierType:ModifierType.AddPokeballModifierType.name"); - expect(i18next.t).toHaveBeenCalledWith("modifierType:ModifierType.AddPokeballModifierType.name", expect.objectContaining({ modifierCount: 5 })); - expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe("modifierType:ModifierType.IV_SCANNER.name"); - expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe("modifierType:ModifierType.RARER_CANDY.name"); + expect(modifierSelectHandler.options[0].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_accuracy")); + expect(modifierSelectHandler.options[1].modifierTypeOption.type.name).toBe(i18next.t("modifierType:TempStatStageBoosterItem.x_speed")); + expect(modifierSelectHandler.options[2].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.AddPokeballModifierType.name", { modifierCount: 5, pokeballName: i18next.t("pokeball:greatBall") })); + expect(i18next.t).toHaveBeenCalledWith(("modifierType:ModifierType.AddPokeballModifierType.name"), expect.objectContaining({ modifierCount: 5 })); + expect(modifierSelectHandler.options[3].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.IV_SCANNER.name")); + expect(modifierSelectHandler.options[4].modifierTypeOption.type.name).toBe(i18next.t("modifierType:ModifierType.RARER_CANDY.name")); }); it("should leave encounter without battle", async () => { diff --git a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts index 3d2533c0817..a4f303d121f 100644 --- a/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/fiery-fallout-encounter.test.ts @@ -22,6 +22,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; import { CommandPhase } from "#app/phases/command-phase"; import { MovePhase } from "#app/phases/move-phase"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/fieryFallout"; /** Arcanine and Ninetails for 2 Fire types. Lapras, Gengar, Abra for burnable mon. */ @@ -205,7 +206,7 @@ describe("Fiery Fallout - Mystery Encounter", () => { const burnablePokemon = party.filter((pkm) => pkm.isAllowedInBattle() && !pkm.getTypes().includes(Type.FIRE)); const notBurnablePokemon = party.filter((pkm) => !pkm.isAllowedInBattle() || pkm.getTypes().includes(Type.FIRE)); - expect(scene.currentBattle.mysteryEncounter?.dialogueTokens["burnedPokemon"]).toBe("pokemon:gengar"); + expect(scene.currentBattle.mysteryEncounter?.dialogueTokens["burnedPokemon"]).toBe(i18next.t("pokemon:gengar")); burnablePokemon.forEach((pkm) => { expect(pkm.hp, `${pkm.name} should have received 20% damage: ${pkm.hp} / ${pkm.getMaxHp()} HP`).toBe(pkm.getMaxHp() - Math.floor(pkm.getMaxHp() * 0.2)); }); diff --git a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts index 456c18c572d..dec14d46cc8 100644 --- a/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/lost-at-sea-encounter.test.ts @@ -14,6 +14,7 @@ import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; import BattleScene from "#app/battle-scene"; import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import i18next from "i18next"; const namespace = "mysteryEncounters/lostAtSea"; @@ -86,8 +87,8 @@ describe("Lost at Sea - Mystery Encounter", () => { const onInitResult = onInit!(scene); expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25"); - expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe("move:surf.name"); - expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe("move:fly.name"); + expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(i18next.t("move:surf.name")); + expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(i18next.t("move:fly.name")); expect(onInitResult).toBe(true); }); diff --git a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 2411752baa7..02375d83b98 100644 --- a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -1,21 +1,22 @@ -import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; -import { Biome } from "#app/enums/biome"; -import { MysteryEncounterType } from "#app/enums/mystery-encounter-type"; -import { Species } from "#app/enums/species"; -import GameManager from "#app/test/utils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils"; import BattleScene from "#app/battle-scene"; +import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; +import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters"; +import { Abilities } from "#enums/abilities"; +import { Biome } from "#enums/biome"; +import { MysteryEncounterType } from "#enums/mystery-encounter-type"; +import { Species } from "#enums/species"; +import { CommandPhase } from "#app/phases/command-phase"; +import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; +import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; +import GameManager from "#test/utils/gameManager"; +import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; +import { Mode } from "#app/ui/ui"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import { runMysteryEncounterToEnd, runSelectMysteryEncounterOption, skipBattleRunMysteryEncounterRewardsPhase } from "#test/mystery-encounter/encounter-test-utils"; import { initSceneWithoutEncounterPhase } from "#test/utils/gameManagerUtils"; -import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases"; -import { CommandPhase } from "#app/phases/command-phase"; -import { TeleportingHijinksEncounter } from "#app/data/mystery-encounters/encounters/teleporting-hijinks-encounter"; -import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; -import { Mode } from "#app/ui/ui"; -import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler"; -import { Abilities } from "#app/enums/abilities"; +import i18next from "i18next"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; const namespace = "mysteryEncounters/teleportingHijinks"; const defaultParty = [ Species.LAPRAS, Species.GENGAR, Species.ABRA ]; @@ -300,8 +301,8 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT); const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler; - expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === "modifierType:AttackTypeBoosterItem.metal_coat")).toBe(true); - expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === "modifierType:AttackTypeBoosterItem.magnet")).toBe(true); + expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.metal_coat"))).toBe(true); + expect(modifierSelectHandler.options.some(opt => opt.modifierTypeOption.type.name === i18next.t("modifierType:AttackTypeBoosterItem.magnet"))).toBe(true); }); }); }); diff --git a/src/test/phases/mystery-encounter-phase.test.ts b/src/test/phases/mystery-encounter-phase.test.ts index 4468045756b..32e31ce1c94 100644 --- a/src/test/phases/mystery-encounter-phase.test.ts +++ b/src/test/phases/mystery-encounter-phase.test.ts @@ -9,6 +9,7 @@ import MysteryEncounterUiHandler from "#app/ui/mystery-encounter-ui-handler"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import MessageUiHandler from "#app/ui/message-ui-handler"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; +import i18next from "i18next"; describe("Mystery Encounter Phases", () => { let phaserGame: Phaser.Game; @@ -78,9 +79,9 @@ describe("Mystery Encounter Phases", () => { expect(ui.getMode()).toBe(Mode.MESSAGE); expect(ui.showDialogue).toHaveBeenCalledTimes(1); expect(ui.showText).toHaveBeenCalledTimes(2); - expect(ui.showDialogue).toHaveBeenCalledWith("battle:mysteryEncounterAppeared", "???", null, expect.any(Function)); - expect(ui.showText).toHaveBeenCalledWith("mysteryEncounters/mysteriousChallengers:intro", null, expect.any(Function), 750, true); - expect(ui.showText).toHaveBeenCalledWith("mysteryEncounters/mysteriousChallengers:option.selected", null, expect.any(Function), 300, true); + expect(ui.showDialogue).toHaveBeenCalledWith(i18next.t("battle:mysteryEncounterAppeared"), "???", null, expect.any(Function)); + expect(ui.showText).toHaveBeenCalledWith(i18next.t("mysteryEncounters/mysteriousChallengers:intro"), null, expect.any(Function), 750, true); + expect(ui.showText).toHaveBeenCalledWith(i18next.t("mysteryEncounters/mysteriousChallengers:option.selected"), null, expect.any(Function), 300, true); }); }); diff --git a/src/test/system/game_data.test.ts b/src/test/system/game_data.test.ts index 5eb4dea3910..fcb7e9067a3 100644 --- a/src/test/system/game_data.test.ts +++ b/src/test/system/game_data.test.ts @@ -11,13 +11,15 @@ import * as account from "../../account"; const apiBase = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8001"; -export const server = setupServer(); +/** We need a custom server. For some reasons I can't extend the listeners of {@linkcode global.i18nServer} with {@linkcode global.i18nServer.use} */ +const server = setupServer(); describe("System - Game Data", () => { let phaserGame: Phaser.Game; let game: GameManager; beforeAll(() => { + global.i18nServer.close(); server.listen(); phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -26,6 +28,7 @@ describe("System - Game Data", () => { afterAll(() => { server.close(); + global.i18nServer.listen(); }); beforeEach(() => { diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index dd0761be392..94370ca1b74 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -14,6 +14,7 @@ import { Abilities } from "#enums/abilities"; import { Button } from "#enums/buttons"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; +import i18next from "i18next"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -66,11 +67,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -127,11 +128,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -191,11 +192,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -254,11 +255,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -315,11 +316,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -376,11 +377,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -436,11 +437,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { @@ -496,11 +497,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler; @@ -561,11 +562,11 @@ describe("UI - Starter select", () => { resolve(); }); }); - expect(options.some(option => option.label === "starterSelectUiHandler:addToParty")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:toggleIVs")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:manageMoves")).toBe(true); - expect(options.some(option => option.label === "starterSelectUiHandler:useCandies")).toBe(true); - expect(options.some(option => option.label === "menu:cancel")).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:addToParty"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:toggleIVs"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:manageMoves"))).toBe(true); + expect(options.some(option => option.label === i18next.t("starterSelectUiHandler:useCandies"))).toBe(true); + expect(options.some(option => option.label === i18next.t("menu:cancel"))).toBe(true); optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler | undefined; diff --git a/src/test/ui/type-hints.test.ts b/src/test/ui/type-hints.test.ts index 450f43f1263..2977262dda7 100644 --- a/src/test/ui/type-hints.test.ts +++ b/src/test/ui/type-hints.test.ts @@ -7,7 +7,8 @@ import { Mode } from "#app/ui/ui"; 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 MockText from "#test/utils/mocks/mocksContainer/mockText"; +import i18next from "i18next"; describe("UI - Type Hints", () => { let phaserGame: Phaser.Game; @@ -53,7 +54,7 @@ describe("UI - Type Hints", () => { const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const dragonClawText = movesContainer .getAll() - .find((text) => text.text === "move:dragonClaw.name")! as unknown as MockText; + .find((text) => text.text === i18next.t("move:dragonClaw.name"))! as unknown as MockText; expect.soft(dragonClawText.color).toBe("#929292"); ui.getHandler().processInput(Button.ACTION); @@ -78,7 +79,7 @@ describe("UI - Type Hints", () => { const movesContainer = ui.getByName(FightUiHandler.MOVES_CONTAINER_NAME); const growlText = movesContainer .getAll() - .find((text) => text.text === "move:growl.name")! as unknown as MockText; + .find((text) => text.text === i18next.t("move:growl.name"))! as unknown as MockText; expect.soft(growlText.color).toBe(undefined); ui.getHandler().processInput(Button.ACTION); diff --git a/src/test/vitest.setup.ts b/src/test/vitest.setup.ts index 0d67d6787c4..8438f607db2 100644 --- a/src/test/vitest.setup.ts +++ b/src/test/vitest.setup.ts @@ -4,16 +4,17 @@ import { initLoggedInUser } from "#app/account"; import { initAbilities } from "#app/data/ability"; import { initBiomes } from "#app/data/balance/biomes"; import { initEggMoves } from "#app/data/balance/egg-moves"; +import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { initMoves } from "#app/data/move"; import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters"; -import { initPokemonPrevolutions } from "#app/data/balance/pokemon-evolutions"; import { initPokemonForms } from "#app/data/pokemon-forms"; import { initSpecies } from "#app/data/pokemon-species"; import { initAchievements } from "#app/system/achv"; import { initVouchers } from "#app/system/voucher"; import { initStatsKeys } from "#app/ui/game-stats-ui-handler"; -import { beforeAll, vi } from "vitest"; +import { afterAll, beforeAll, vi } from "vitest"; +/** Set the timezone to UTC for tests. */ process.env.TZ = "UTC"; /** Mock the override import to always return default values, ignoring any custom overrides. */ @@ -26,26 +27,36 @@ vi.mock("#app/overrides", async (importOriginal) => { } satisfies typeof import("#app/overrides"); }); -vi.mock("i18next", () => ({ - default: { - use: () => {}, - t: (key: string) => key, - changeLanguage: () => Promise.resolve(), - init: () => Promise.resolve(), - resolvedLanguage: "en", - exists: () => true, - getDataByLanguage:() => ({ - en: { - keys: [ "foo" ] - }, - }), - services: { - formatter: { - add: () => {}, +/** + * This is a hacky way to mock the i18n backend requests (with the help of {@link https://mswjs.io/ | msw}). + * The reason to put it inside of a mock is to elevate it. + * This is necessary because how our code is structured. + * Do NOT try to put any of this code into external functions, it won't work as it's elevated during runtime. + */ +vi.mock("i18next", async (importOriginal) => { + console.log("Mocking i18next"); + const { setupServer } = await import("msw/node"); + const { http, HttpResponse } = await import("msw"); + + global.i18nServer = setupServer( + http.get("/locales/en/*", async (req) => { + const filename = req.params[0]; + + try { + const json = await import(`../../public/locales/en/${req.params[0]}`); + console.log("Loaded locale", filename); + return HttpResponse.json(json); + } catch (err) { + console.log(`Failed to load locale ${filename}!`, err); + return HttpResponse.json({}); } - }, - }, -})); + }) + ); + global.i18nServer.listen({ onUnhandledRequest: "error" }); + console.log("i18n MSW server listening!"); + + return await importOriginal(); +}); initVouchers(); initAchievements(); @@ -70,3 +81,8 @@ beforeAll(() => { }, }); }); + +afterAll(() => { + global.i18nServer.close(); + console.log("Closing i18n MSW server!"); +}); From ca3cc3c9c6a569572942516f72edd8610c76b6c5 Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:28:26 -0400 Subject: [PATCH 34/70] [P1 Bug] Fix infinite recursion from abilities disabled by Sheer Force (#4631) --- src/field/pokemon.ts | 4 ++-- src/test/abilities/sheer_force.test.ts | 27 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 241524df1b9..35f389b58a4 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1417,10 +1417,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns {boolean} Whether the ability is present and active */ hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean { - if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).id === ability) { + if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) { return true; } - if (this.hasPassive() && (!canApply || this.canApplyAbility(true)) && this.getPassiveAbility().id === ability) { + if (this.getPassiveAbility().id === ability && this.hasPassive() && (!canApply || this.canApplyAbility(true))) { return true; } return false; diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index a3add0a9964..a2600476d6d 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -9,6 +9,7 @@ import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { allMoves } from "#app/data/move"; describe("Abilities - Sheer Force", () => { @@ -174,5 +175,31 @@ describe("Abilities - Sheer Force", () => { }, 20000); + it("Two Pokemon with abilities disabled by Sheer Force hitting each other should not cause a crash", async () => { + const moveToUse = Moves.CRUNCH; + game.override.enemyAbility(Abilities.COLOR_CHANGE) + .ability(Abilities.COLOR_CHANGE) + .moveset(moveToUse) + .enemyMoveset(moveToUse); + + await game.classicMode.startBattle([ + Species.PIDGEOT + ]); + + const pidgeot = game.scene.getParty()[0]; + const onix = game.scene.getEnemyParty()[0]; + + pidgeot.stats[Stat.DEF] = 10000; + onix.stats[Stat.DEF] = 10000; + + game.move.select(moveToUse); + await game.toNextTurn(); + + // Check that both Pokemon's Color Change activated + const expectedTypes = [ allMoves[moveToUse].type ]; + expect(pidgeot.getTypes()).toStrictEqual(expectedTypes); + expect(onix.getTypes()).toStrictEqual(expectedTypes); + }); + //TODO King's Rock Interaction Unit Test }); From 52257def2fa65aa09018800b29e89864572fc8b0 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:30:19 -0700 Subject: [PATCH 35/70] [P3] Fix enemy used PP flyout, fixes #4622 (#4629) Also add missing function return types --- src/phases/move-phase.ts | 41 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 10cc062ea3b..94093188571 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -8,10 +8,6 @@ import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; import { Type } from "#app/data/type"; import { getTerrainBlockMessage } from "#app/data/weather"; -import { Abilities } from "#app/enums/abilities"; -import { BattlerTagType } from "#app/enums/battler-tag-type"; -import { Moves } from "#app/enums/moves"; -import { StatusEffect } from "#app/enums/status-effect"; import { MoveUsedEvent } from "#app/events/battle-scene"; import Pokemon, { MoveResult, PokemonMove, TurnMove } from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; @@ -20,7 +16,11 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; -import * as Utils from "#app/utils"; +import { BooleanHolder, NumberHolder } from "#app/utils"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { Moves } from "#enums/moves"; +import { StatusEffect } from "#enums/status-effect"; import i18next from "i18next"; export class MovePhase extends BattlePhase { @@ -89,7 +89,7 @@ export class MovePhase extends BattlePhase { this.cancelled = true; } - public start() { + public start(): void { super.start(); console.log(Moves[this.move.moveId]); @@ -140,7 +140,7 @@ export class MovePhase extends BattlePhase { } /** Check for cancellation edge cases - no targets remaining, or {@linkcode Moves.NONE} is in the queue */ - protected resolveFinalPreMoveCancellationChecks() { + protected resolveFinalPreMoveCancellationChecks(): void { const targets = this.getActiveTargetPokemon(); const moveQueue = this.pokemon.getMoveQueue(); @@ -150,14 +150,14 @@ export class MovePhase extends BattlePhase { } } - public getActiveTargetPokemon() { + public getActiveTargetPokemon(): Pokemon[] { return this.scene.getField(true).filter(p => this.targets.includes(p.getBattlerIndex())); } /** * Handles {@link StatusEffect.SLEEP Sleep}/{@link StatusEffect.PARALYSIS Paralysis}/{@link StatusEffect.FREEZE Freeze} rolls and side effects. */ - protected resolvePreMoveStatusEffects() { + protected resolvePreMoveStatusEffects(): void { if (!this.followUp && this.pokemon.status && !this.pokemon.status.isPostTurn()) { this.pokemon.status.incrementTurn(); let activated = false; @@ -198,7 +198,7 @@ export class MovePhase extends BattlePhase { * Lapse {@linkcode BattlerTagLapseType.PRE_MOVE PRE_MOVE} tags that trigger before a move is used, regardless of whether or not it failed. * Also lapse {@linkcode BattlerTagLapseType.MOVE MOVE} tags if the move should be successful. */ - protected lapsePreMoveAndMoveTags() { + protected lapsePreMoveAndMoveTags(): void { this.pokemon.lapseTags(BattlerTagLapseType.PRE_MOVE); // TODO: does this intentionally happen before the no targets/Moves.NONE on queue cancellation case is checked? @@ -207,7 +207,7 @@ export class MovePhase extends BattlePhase { } } - protected useMove() { + protected useMove(): void { const targets = this.getActiveTargetPokemon(); const moveQueue = this.pokemon.getMoveQueue(); @@ -217,7 +217,8 @@ export class MovePhase extends BattlePhase { this.showMoveText(); // TODO: Clean up implementation of two-turn moves. - if (moveQueue.length > 0) { // Using .shift here clears out two turn moves once they've been used + if (moveQueue.length > 0) { + // Using .shift here clears out two turn moves once they've been used this.ignorePp = moveQueue.shift()?.ignorePP ?? false; } @@ -226,7 +227,7 @@ export class MovePhase extends BattlePhase { const ppUsed = 1 + this.getPpIncreaseFromPressure(targets); this.move.usePp(ppUsed); - this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); + this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } // Update the battle's "last move" pointer, unless we're currently mimicking a move. @@ -275,7 +276,7 @@ export class MovePhase extends BattlePhase { this.pokemon.pushMoveHistory({ move: this.move.moveId, targets: this.targets, result: MoveResult.FAIL, virtual: this.move.virtual }); let failedText: string | undefined; - const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new Utils.BooleanHolder(false)); + const failureMessage = move.getFailedText(this.pokemon, targets[0], move, new BooleanHolder(false)); if (failureMessage) { failedText = failureMessage; @@ -299,7 +300,7 @@ export class MovePhase extends BattlePhase { * Queues a {@linkcode MoveEndPhase} if the move wasn't a {@linkcode followUp} and {@linkcode canMove()} returns `true`, * then ends the phase. */ - public end() { + public end(): void { if (!this.followUp && this.canMove()) { this.scene.unshiftPhase(new MoveEndPhase(this.scene, this.pokemon.getBattlerIndex())); } @@ -313,7 +314,7 @@ export class MovePhase extends BattlePhase { * * TODO: This hardcodes the PP increase at 1 per opponent, rather than deferring to the ability. */ - public getPpIncreaseFromPressure(targets: Pokemon[]) { + public getPpIncreaseFromPressure(targets: Pokemon[]): number { const foesWithPressure = this.pokemon.getOpponents().filter(o => targets.includes(o) && o.isActive(true) && o.hasAbilityWithAttr(IncreasePpAbAttr)); return foesWithPressure.length; } @@ -323,10 +324,10 @@ export class MovePhase extends BattlePhase { * - Move redirection abilities, effects, etc. * - Counterattacks, which pass a special value into the `targets` constructor param (`[`{@linkcode BattlerIndex.ATTACKER}`]`). */ - protected resolveRedirectTarget() { + protected resolveRedirectTarget(): void { if (this.targets.length === 1) { const currentTarget = this.targets[0]; - const redirectTarget = new Utils.NumberHolder(currentTarget); + const redirectTarget = new NumberHolder(currentTarget); // check move redirection abilities of every pokemon *except* the user. this.scene.getField(true).filter(p => p !== this.pokemon).forEach(p => applyAbAttrs(RedirectMoveAbAttr, p, null, false, this.move.moveId, redirectTarget)); @@ -372,7 +373,7 @@ export class MovePhase extends BattlePhase { * If there is no last attacker, or they are no longer on the field, a message is displayed and the * move is marked for failure. */ - protected resolveCounterAttackTarget() { + protected resolveCounterAttackTarget(): void { if (this.targets.length === 1 && this.targets[0] === BattlerIndex.ATTACKER) { if (this.pokemon.turnData.attacksReceived.length) { this.targets[0] = this.pokemon.turnData.attacksReceived[0].sourceBattlerIndex; @@ -411,7 +412,7 @@ export class MovePhase extends BattlePhase { * * TODO: handle charge moves more gracefully */ - protected handlePreMoveFailures() { + protected handlePreMoveFailures(): void { if (this.cancelled || this.failed) { if (this.failed) { const ppUsed = this.ignorePp ? 0 : 1; From e9906ea2293171aa8b32b81ee1d1a0a77254b3f2 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:31:10 -0700 Subject: [PATCH 36/70] [P2] Obstruct/Kings Shield/etc no longer reduce stats through Clear Body/etc (#4627) * bug fix * Add test --------- Co-authored-by: frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battler-tags.ts | 2 +- src/test/moves/obstruct.test.ts | 26 +++++++++++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index a54a8c5f519..6307b3d28be 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1376,7 +1376,7 @@ export class ContactStatStageChangeProtectedTag extends DamageProtectedTag { const effectPhase = pokemon.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase && effectPhase.move.getMove().hasFlag(MoveFlags.MAKES_CONTACT)) { const attacker = effectPhase.getPokemon(); - pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), true, [ this.stat ], this.levels)); + pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, attacker.getBattlerIndex(), false, [ this.stat ], this.levels)); } } diff --git a/src/test/moves/obstruct.test.ts b/src/test/moves/obstruct.test.ts index fbb5437b43a..1649c199e32 100644 --- a/src/test/moves/obstruct.test.ts +++ b/src/test/moves/obstruct.test.ts @@ -1,6 +1,7 @@ -import { Moves } from "#app/enums/moves"; -import { Stat } from "#app/enums/stat"; import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -22,13 +23,15 @@ describe("Moves - Obstruct", () => { game = new GameManager(phaserGame); game.override .battleType("single") + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.TACKLE) .enemyAbility(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH) - .moveset([ Moves.OBSTRUCT ]); + .moveset([ Moves.OBSTRUCT ]) + .starterSpecies(Species.FEEBAS); }); it("protects from contact damaging moves and lowers the opponent's defense by 2 stages", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -42,7 +45,6 @@ describe("Moves - Obstruct", () => { }); it("bypasses accuracy checks when applying protection and defense reduction", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH)); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -59,7 +61,7 @@ describe("Moves - Obstruct", () => { ); it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN)); + game.override.enemyMoveset(Moves.WATER_GUN); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -73,7 +75,7 @@ describe("Moves - Obstruct", () => { }); it("doesn't protect from status moves", async () => { - game.override.enemyMoveset(Array(4).fill(Moves.GROWL)); + game.override.enemyMoveset(Moves.GROWL); await game.classicMode.startBattle(); game.move.select(Moves.OBSTRUCT); @@ -83,4 +85,14 @@ describe("Moves - Obstruct", () => { expect(player.getStatStage(Stat.ATK)).toBe(-1); }); + + it("doesn't reduce the stats of an opponent with Clear Body/etc", async () => { + game.override.enemyAbility(Abilities.CLEAR_BODY); + await game.classicMode.startBattle(); + + game.move.select(Moves.OBSTRUCT); + await game.phaseInterceptor.to("BerryPhase"); + + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(0); + }); }); From 51894d46c265116ee391146a10bedb16eccbc056 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:38:17 -0700 Subject: [PATCH 37/70] [P2] Pollen Puff ally behavior fixed (#4615) * pollen puff fix * bcvbvcbfd * integerholder to numberholder * moved it back --------- Co-authored-by: frutescens --- src/data/move.ts | 4 ++-- src/field/pokemon.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 08c00829b48..4924341870d 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4107,11 +4107,11 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr { * @param user {@linkcode Pokemon} using the move * @param target {@linkcode Pokemon} target of the move * @param move {@linkcode Move} with this attribute - * @param args [0] {@linkcode Utils.IntegerHolder} The category of the move + * @param args [0] {@linkcode Utils.NumberHolder} The category of the move * @returns true if the function succeeds */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const category = (args[0] as Utils.IntegerHolder); + const category = (args[0] as Utils.NumberHolder); if (user.getAlly() === target) { category.value = MoveCategory.STATUS; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 35f389b58a4..4d85d5b8e1e 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2684,7 +2684,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ apply(source: Pokemon, move: Move): HitResult { const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - if (move.category === MoveCategory.STATUS) { + const moveCategory = new Utils.NumberHolder(move.category); + applyMoveAttrs(VariableMoveCategoryAttr, source, this, move, moveCategory); + if (moveCategory.value === MoveCategory.STATUS) { const cancelled = new Utils.BooleanHolder(false); const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled); From 64147e44145faa8e8f14730279e764d146797841 Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:40:14 -0400 Subject: [PATCH 38/70] [P2] Fix Battle Bond continuing to affect Water Shuriken after Greninja returns to base form (#4602) * [Bug] Fix Battle Bond continuing to buff Water Shuriken after Greninja returns to base form * Test cleanup * PR feedback * Update test to use getMultiHitType() * PR Feedback --- src/data/move.ts | 13 +++- src/test/abilities/battle_bond.test.ts | 94 +++++++++++++++++--------- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index 4924341870d..bae8eea0d8a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1938,12 +1938,21 @@ export class IncrementMovePriorityAttr extends MoveAttr { * @see {@linkcode apply} */ export class MultiHitAttr extends MoveAttr { + /** This move's intrinsic multi-hit type. It should never be modified. */ + private readonly intrinsicMultiHitType: MultiHitType; + /** This move's current multi-hit type. It may be temporarily modified by abilities (e.g., Battle Bond). */ private multiHitType: MultiHitType; constructor(multiHitType?: MultiHitType) { super(); - this.multiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5; + this.intrinsicMultiHitType = multiHitType !== undefined ? multiHitType : MultiHitType._2_TO_5; + this.multiHitType = this.intrinsicMultiHitType; + } + + // Currently used by `battle_bond.test.ts` + getMultiHitType(): MultiHitType { + return this.multiHitType; } /** @@ -1957,7 +1966,7 @@ export class MultiHitAttr extends MoveAttr { * @returns True */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const hitType = new Utils.NumberHolder(this.multiHitType); + const hitType = new Utils.NumberHolder(this.intrinsicMultiHitType); applyMoveAttrs(ChangeMultiHitTypeAttr, user, target, move, hitType); this.multiHitType = hitType.value; diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index c7dffeb150a..283fb0d0f14 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -1,17 +1,19 @@ +import { allMoves, MultiHitAttr, MultiHitType } from "#app/data/move"; import { Status, StatusEffect } from "#app/data/status-effect"; -import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - BATTLE BOND", () => { let phaserGame: Phaser.Game; let game: GameManager; + const baseForm = 1; + const ashForm = 2; + beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -24,40 +26,68 @@ describe("Abilities - BATTLE BOND", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.SPLASH; - game.override.battleType("single"); - game.override.ability(Abilities.BATTLE_BOND); - game.override.moveset([ moveToUse ]); - game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); + game.override.battleType("single") + .startingWave(4) // Leads to arena reset on Wave 5 trainer battle + .ability(Abilities.BATTLE_BOND) + .starterForms({ [Species.GRENINJA]: ashForm, }) + .moveset([ Moves.SPLASH, Moves.WATER_SHURIKEN ]) + .enemySpecies(Species.BULBASAUR) + .enemyMoveset(Moves.SPLASH) + .startingLevel(100) // Avoid levelling up + .enemyLevel(1000); // Avoid opponent dying before `doKillOpponents()` }); - test( - "check if fainted pokemon switches to base form on arena reset", - async () => { - const baseForm = 1; - const ashForm = 2; - game.override.startingWave(4); - game.override.starterForms({ - [Species.GRENINJA]: ashForm, - }); + it("check if fainted pokemon switches to base form on arena reset", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP, Species.GRENINJA ]); - await game.startBattle([ Species.MAGIKARP, Species.GRENINJA ]); + const greninja = game.scene.getParty()[1]; + expect(greninja.formIndex).toBe(ashForm); - const greninja = game.scene.getParty().find((p) => p.species.speciesId === Species.GRENINJA); - expect(greninja).toBeDefined(); - expect(greninja!.formIndex).toBe(ashForm); + greninja.hp = 0; + greninja.status = new Status(StatusEffect.FAINT); + expect(greninja.isFainted()).toBe(true); - greninja!.hp = 0; - greninja!.status = new Status(StatusEffect.FAINT); - expect(greninja!.isFainted()).toBe(true); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("TurnEndPhase"); + game.doSelectModifier(); + await game.phaseInterceptor.to("QuietFormChangePhase"); - game.move.select(Moves.SPLASH); - await game.doKillOpponents(); - await game.phaseInterceptor.to(TurnEndPhase); - game.doSelectModifier(); - await game.phaseInterceptor.to(QuietFormChangePhase); + expect(greninja.formIndex).toBe(baseForm); + }); - expect(greninja!.formIndex).toBe(baseForm); - }, - ); + it("should not keep buffing Water Shuriken after Greninja switches to base form", async () => { + await game.classicMode.startBattle([ Species.GRENINJA ]); + + const waterShuriken = allMoves[Moves.WATER_SHURIKEN]; + vi.spyOn(waterShuriken, "calculateBattlePower"); + + let actualMultiHitType: MultiHitType | null = null; + const multiHitAttr = waterShuriken.getAttrs(MultiHitAttr)[0]; + vi.spyOn(multiHitAttr, "getHitCount").mockImplementation(() => { + actualMultiHitType = multiHitAttr.getMultiHitType(); + return 3; + }); + + // Wave 4: Use Water Shuriken in Ash form + let expectedBattlePower = 20; + let expectedMultiHitType = MultiHitType._3; + + game.move.select(Moves.WATER_SHURIKEN); + await game.phaseInterceptor.to("BerryPhase", false); + expect(waterShuriken.calculateBattlePower).toHaveLastReturnedWith(expectedBattlePower); + expect(actualMultiHitType).toBe(expectedMultiHitType); + + await game.doKillOpponents(); + await game.toNextWave(); + + // Wave 5: Use Water Shuriken in base form + expectedBattlePower = 15; + expectedMultiHitType = MultiHitType._2_TO_5; + + game.move.select(Moves.WATER_SHURIKEN); + await game.phaseInterceptor.to("BerryPhase", false); + expect(waterShuriken.calculateBattlePower).toHaveLastReturnedWith(expectedBattlePower); + expect(actualMultiHitType).toBe(expectedMultiHitType); + }); }); From a778537ccadcfe23a39766abcf8afee7b287ca09 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:43:50 -0700 Subject: [PATCH 39/70] [P2] Sketch Failure Bug involving multiple Sketch-s in a moveset (#4618) * Sketch bug fix * Added test --------- Co-authored-by: frutescens --- src/phases/turn-start-phase.ts | 2 +- src/test/moves/sketch.test.ts | 53 ++++++++++++++++++++++++++++++ src/test/utils/gameManagerUtils.ts | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/test/moves/sketch.test.ts diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 95d55986185..53623f933f2 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -158,7 +158,7 @@ export class TurnStartPhase extends FieldPhase { if (!queuedMove) { continue; } - const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move && m?.ppUsed < m?.getMovePp()) || new PokemonMove(queuedMove.move); if (move.getMove().hasAttr(MoveHeaderAttr)) { this.scene.unshiftPhase(new MoveHeaderPhase(this.scene, pokemon, move)); } diff --git a/src/test/moves/sketch.test.ts b/src/test/moves/sketch.test.ts new file mode 100644 index 00000000000..2e3eb97a76c --- /dev/null +++ b/src/test/moves/sketch.test.ts @@ -0,0 +1,53 @@ +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Moves - Sketch", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .ability(Abilities.BALL_FETCH) + .battleType("single") + .disableCrits() + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("Sketch should not fail even if a previous Sketch failed to retrieve a valid move and ran out of PP", async () => { + game.override.moveset([ Moves.SKETCH, Moves.SKETCH ]); + + await game.classicMode.startBattle([ Species.REGIELEKI ]); + const playerPokemon = game.scene.getPlayerPokemon(); + + game.move.select(Moves.SKETCH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon?.getLastXMoves()[0].result).toBe(MoveResult.FAIL); + const moveSlot0 = playerPokemon?.getMoveset()[0]; + expect(moveSlot0?.moveId).toBe(Moves.SKETCH); + expect(moveSlot0?.getPpRatio()).toBe(0); + + await game.toNextTurn(); + game.move.select(Moves.SKETCH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon?.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + // Can't verify if the player Pokemon's moveset was successfully changed because of overrides. + }); +}); diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index 700d93082d8..543ee9627fe 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -86,7 +86,7 @@ export function waitUntil(truth) { export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves) { const playerPokemon = scene.getPlayerField()[pokemonIndex]; const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex((m) => m?.moveId === move); + const index = moveSet.findIndex((m) => m?.moveId === move && m?.ppUsed < m?.getMovePp()); console.log(`Move position for ${Moves[move]} (=${move}):`, index); return index; } From ba7e26152e560032792e94257b11510160ea184f Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 10 Oct 2024 08:45:02 -0700 Subject: [PATCH 40/70] [Bug] Fix substitute interactions with `PostDefendAbAttr`s (#4570) * Fixes some Substitute interactions Specifically with Disguise/Ice Face and Gulp Missile * Add tests * Fix linting * Add `hitsSubstitute()` checks to all `PostDefendAbAttr`s Also fix comment indentation in `MoveEffectPhase` * Revert `move-effect-phase.ts` changes --- src/data/ability.ts | 140 +++++++++++++----------- src/data/battler-tags.ts | 4 + src/data/move.ts | 12 +- src/test/abilities/disguise.test.ts | 16 ++- src/test/abilities/gulp_missile.test.ts | 31 +++++- src/test/abilities/ice_face.test.ts | 38 +++++-- 6 files changed, 152 insertions(+), 89 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 43d02da1733..6a391818866 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -634,15 +634,15 @@ export class ReverseDrainAbAttr extends PostDefendAbAttr { * Examples include: Absorb, Draining Kiss, Bitter Blade, etc. * Also displays a message to show this ability was activated. * @param pokemon {@linkcode Pokemon} with this ability - * @param passive N/A + * @param _passive N/A * @param attacker {@linkcode Pokemon} that is attacking this Pokemon * @param move {@linkcode PokemonMove} that is being used - * @param hitResult N/A - * @args N/A + * @param _hitResult N/A + * @param _args N/A * @returns true if healing should be reversed on a healing move, false otherwise. */ - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.hasAttr(HitHealAttr)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.hasAttr(HitHealAttr) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { pokemon.scene.queueMessage(i18next.t("abilityTriggers:reverseDrain", { pokemonNameWithAffix: getPokemonNameWithAffix(attacker) })); } @@ -669,8 +669,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr { this.allOthers = allOthers; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { if (simulated) { return true; } @@ -707,13 +707,13 @@ export class PostDefendHpGatedStatStageChangeAbAttr extends PostDefendAbAttr { this.selfTarget = selfTarget; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const hpGateFlat: integer = Math.ceil(pokemon.getMaxHp() * this.hpGate); + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + const hpGateFlat: number = Math.ceil(pokemon.getMaxHp() * this.hpGate); const lastAttackReceived = pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1]; const damageReceived = lastAttackReceived?.damage || 0; - if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat)) { - if (!simulated ) { + if (this.condition(pokemon, attacker, move) && (pokemon.hp <= hpGateFlat && (pokemon.hp + damageReceived) > hpGateFlat) && !move.hitsSubstitute(attacker, pokemon)) { + if (!simulated) { pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, (this.selfTarget ? pokemon : attacker).getBattlerIndex(), true, this.stats, this.stages)); } return true; @@ -734,8 +734,8 @@ export class PostDefendApplyArenaTrapTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { const tag = pokemon.scene.arena.getTag(this.tagType) as ArenaTrapTag; if (!pokemon.scene.arena.getTag(this.tagType) || tag.layers < tag.maxLayers) { if (!simulated) { @@ -758,8 +758,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { this.tagType = tagType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { if (!pokemon.getTag(this.tagType) && !simulated) { pokemon.addTag(this.tagType, undefined, undefined, pokemon.id); pokemon.scene.queueMessage(i18next.t("abilityTriggers:windPowerCharged", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: move.name })); @@ -771,8 +771,8 @@ export class PostDefendApplyBattlerTagAbAttr extends PostDefendAbAttr { } export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean { + if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) { if (simulated) { return true; } @@ -787,7 +787,7 @@ export class PostDefendTypeChangeAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendTypeChange", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, @@ -805,8 +805,8 @@ export class PostDefendTerrainChangeAbAttr extends PostDefendAbAttr { this.terrainType = terrainType; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (hitResult < HitResult.NO_EFFECT) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): boolean { + if (hitResult < HitResult.NO_EFFECT && !move.hitsSubstitute(attacker, pokemon)) { if (simulated) { return pokemon.scene.arena.terrain?.terrainType !== (this.terrainType || undefined); } else { @@ -829,8 +829,9 @@ export class PostDefendContactApplyStatusEffectAbAttr extends PostDefendAbAttr { this.effects = effects; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.status + && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !move.hitsSubstitute(attacker, pokemon)) { const effect = this.effects.length === 1 ? this.effects[0] : this.effects[pokemon.randSeedInt(this.effects.length)]; if (simulated) { return attacker.canSetStatus(effect, true, false, pokemon); @@ -869,8 +870,8 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { this.turnCount = turnCount; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && pokemon.randSeedInt(100) < this.chance && !move.hitsSubstitute(attacker, pokemon)) { if (simulated) { return attacker.canAddTag(this.tagType); } else { @@ -893,7 +894,11 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { this.stages = stages; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.hitsSubstitute(attacker, pokemon)) { + return false; + } + if (!simulated) { pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.stages)); } @@ -901,7 +906,7 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr { return true; } - getCondition(): AbAttrCondition { + override getCondition(): AbAttrCondition { return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; } } @@ -915,8 +920,9 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { this.damageRatio = damageRatio; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (!simulated && move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + && !attacker.hasAbilityWithAttr(BlockNonDirectDamageAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { attacker.damageAndUpdate(Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER); attacker.turnData.damageTaken += Utils.toDmgValue(attacker.getMaxHp() * (1 / this.damageRatio)); return true; @@ -925,7 +931,7 @@ export class PostDefendContactDamageAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendContactDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName @@ -948,8 +954,8 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { this.turns = turns; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) { if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { @@ -963,24 +969,24 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:perishBody", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: abilityName }); } } export class PostDefendWeatherChangeAbAttr extends PostDefendAbAttr { private weatherType: WeatherType; - protected condition: PokemonDefendCondition | null; + protected condition?: PokemonDefendCondition; constructor(weatherType: WeatherType, condition?: PokemonDefendCondition) { super(); this.weatherType = weatherType; - this.condition = condition ?? null; + this.condition = condition; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (this.condition !== null && !this.condition(pokemon, attacker, move)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (this.condition && !this.condition(pokemon, attacker, move) || move.hitsSubstitute(attacker, pokemon)) { return false; } if (!pokemon.scene.arena.weather?.isImmutable()) { @@ -999,8 +1005,9 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { super(); } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, args: any[]): boolean { + if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) + && !attacker.getAbility().hasAttr(UnswappableAbilityAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { const tempAbilityId = attacker.getAbility().id; attacker.summonData.ability = pokemon.getAbility().id; @@ -1012,7 +1019,7 @@ export class PostDefendAbilitySwapAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, _abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendAbilitySwap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }); } } @@ -1025,8 +1032,9 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { this.ability = ability; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr)) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !attacker.getAbility().hasAttr(UnsuppressableAbilityAbAttr) + && !attacker.getAbility().hasAttr(PostDefendAbilityGiveAbAttr) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { attacker.summonData.ability = this.ability; } @@ -1037,7 +1045,7 @@ export class PostDefendAbilityGiveAbAttr extends PostDefendAbAttr { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + override getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return i18next.t("abilityTriggers:postDefendAbilityGive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName @@ -1056,8 +1064,8 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr { this.chance = chance; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (attacker.getTag(BattlerTagType.DISABLED) === null) { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { + if (attacker.getTag(BattlerTagType.DISABLED) === null && !move.hitsSubstitute(attacker, pokemon)) { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) { if (simulated) { return true; @@ -1724,17 +1732,17 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { } export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { - private condition: PokemonDefendCondition | null; + private condition?: PokemonDefendCondition; constructor(condition?: PokemonDefendCondition) { super(); - this.condition = condition ?? null; + this.condition = condition; } - applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { + override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, _args: any[]): Promise { return new Promise(resolve => { - if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move))) { + if (!simulated && hitResult < HitResult.NO_EFFECT && (!this.condition || this.condition(pokemon, attacker, move)) && !move.hitsSubstitute(attacker, pokemon)) { const heldItems = this.getTargetHeldItems(attacker).filter(i => i.isTransferable); if (heldItems.length) { const stolenItem = heldItems[pokemon.randSeedInt(heldItems.length)]; @@ -4476,7 +4484,7 @@ export class PostSummonStatStageChangeOnArenaAbAttr extends PostSummonStatStageC export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; private tagType: BattlerTagType; - private recoilDamageFunc: ((pokemon: Pokemon) => number) | undefined; + private recoilDamageFunc?: ((pokemon: Pokemon) => number); private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string; constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) { @@ -4492,16 +4500,16 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { * Applies the pre-defense ability to the PokĂ©mon. * Removes the appropriate `BattlerTagType` when hit by an attack and is in its defense form. * - * @param {Pokemon} pokemon The PokĂ©mon with the ability. - * @param {boolean} passive n/a - * @param {Pokemon} attacker The attacking PokĂ©mon. - * @param {PokemonMove} move The move being used. - * @param {Utils.BooleanHolder} cancelled n/a - * @param {any[]} args Additional arguments. - * @returns {boolean} Whether the immunity was applied. + * @param pokemon The PokĂ©mon with the ability. + * @param _passive n/a + * @param attacker The attacking PokĂ©mon. + * @param move The move being used. + * @param _cancelled n/a + * @param args Additional arguments. + * @returns `true` if the immunity was applied. */ - applyPreDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { + override applyPreDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _cancelled: Utils.BooleanHolder, args: any[]): boolean { + if (this.condition(pokemon, attacker, move) && !move.hitsSubstitute(attacker, pokemon)) { if (!simulated) { (args[0] as Utils.NumberHolder).value = this.multiplier; pokemon.removeTag(this.tagType); @@ -4517,12 +4525,12 @@ export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { /** * Gets the message triggered when the PokĂ©mon avoids damage using the form-changing ability. - * @param {Pokemon} pokemon The PokĂ©mon with the ability. - * @param {string} abilityName The name of the ability. - * @param {...any} args n/a - * @returns {string} The trigger message. + * @param pokemon The PokĂ©mon with the ability. + * @param abilityName The name of the ability. + * @param _args n/a + * @returns The trigger message. */ - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(pokemon: Pokemon, abilityName: string, ..._args: any[]): string { return this.triggerMessageFunc(pokemon, abilityName); } } @@ -5503,7 +5511,8 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) // Add BattlerTagType.DISGUISE if the pokemon is in its disguised form .conditionalAttr(pokemon => pokemon.formIndex === 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.DISGUISE, 0, false) - .attr(FormBlockDamageAbAttr, (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, + .attr(FormBlockDamageAbAttr, + (target, user, move) => !!target.getTag(BattlerTagType.DISGUISE) && target.getMoveEffectiveness(user, move) > 0, 0, BattlerTagType.DISGUISE, (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), (pokemon) => Utils.toDmgValue(pokemon.getMaxHp() / 8)) .attr(PostBattleInitFormChangeAbAttr, () => 0) @@ -5665,7 +5674,8 @@ export function initAbilities() { .conditionalAttr(getWeatherCondition(WeatherType.HAIL, WeatherType.SNOW), PostSummonAddBattlerTagAbAttr, BattlerTagType.ICE_FACE, 0) // When weather changes to HAIL or SNOW while pokemon is fielded, add BattlerTagType.ICE_FACE .attr(PostWeatherChangeAddBattlerTagAttr, BattlerTagType.ICE_FACE, 0, WeatherType.HAIL, WeatherType.SNOW) - .attr(FormBlockDamageAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, + .attr(FormBlockDamageAbAttr, + (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0, BattlerTagType.ICE_FACE, (pokemon, abilityName) => i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName })) .attr(PostBattleInitFormChangeAbAttr, () => 0) .bypassFaint() diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 6307b3d28be..24c82e54427 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2139,6 +2139,10 @@ export class GulpMissileTag extends BattlerTag { return false; } + if (moveEffectPhase.move.getMove().hitsSubstitute(attacker, pokemon)) { + return true; + } + const cancelled = new Utils.BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, attacker, cancelled); diff --git a/src/data/move.ts b/src/data/move.ts index bae8eea0d8a..ff0c24f5032 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4844,14 +4844,14 @@ export class GulpMissileTagAttr extends MoveEffectAttr { /** * Adds BattlerTagType from GulpMissileTag based on the Pokemon's HP ratio. - * @param {Pokemon} user The Pokemon using the move. - * @param {Pokemon} target The Pokemon being targeted by the move. - * @param {Move} move The move being used. - * @param {any[]} args Additional arguments, if any. + * @param user The Pokemon using the move. + * @param _target N/A + * @param move The move being used. + * @param _args N/A * @returns Whether the BattlerTag is applied. */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { - if (!super.apply(user, target, move, args)) { + apply(user: Pokemon, _target: Pokemon, move: Move, _args: any[]): boolean { + if (!super.apply(user, _target, move, _args)) { return false; } diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index a295dd61443..0241aa4b9ea 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,8 +1,9 @@ +import { BattlerIndex } from "#app/battle"; +import { StatusEffect } from "#app/data/status-effect"; import { toDmgValue } from "#app/utils"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; -import { StatusEffect } from "#app/data/status-effect"; import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -222,4 +223,17 @@ describe("Abilities - Disguise", () => { expect(mimikyu.formIndex).toBe(bustedForm); expect(mimikyu.hp).toBe(maxHp - disguiseDamage); }); + + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyMoveset(Moves.SUBSTITUTE) + .moveset(Moves.POWER_TRIP); + await game.classicMode.startBattle(); + + game.move.select(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.formIndex).toBe(disguisedForm); + }); }); diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index 1ca208996b5..01b68d0c89d 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,13 +1,14 @@ -import { BattlerTagType } from "#enums/battler-tag-type"; -import { StatusEffect } from "#enums/status-effect"; +import { BattlerIndex } from "#app/battle"; import Pokemon from "#app/field/pokemon"; -import GameManager from "#test/utils/gameManager"; import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; +import { Stat } from "#enums/stat"; +import { StatusEffect } from "#enums/status-effect"; +import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -import { Stat } from "#enums/stat"; describe("Abilities - Gulp Missile", () => { let phaserGame: Phaser.Game; @@ -40,8 +41,9 @@ describe("Abilities - Gulp Missile", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override + .disableCrits() .battleType("single") - .moveset([ Moves.SURF, Moves.DIVE, Moves.SPLASH ]) + .moveset([ Moves.SURF, Moves.DIVE, Moves.SPLASH, Moves.SUBSTITUTE ]) .enemySpecies(Species.SNORLAX) .enemyAbility(Abilities.BALL_FETCH) .enemyMoveset(Moves.SPLASH) @@ -234,6 +236,25 @@ describe("Abilities - Gulp Missile", () => { expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.DEF)).toBe(-1); }); + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyAbility(Abilities.STURDY) + .enemyMoveset([ Moves.SPLASH, Moves.POWER_TRIP ]); + await game.classicMode.startBattle([ Species.CRAMORANT ]); + + game.move.select(Moves.SURF); + await game.forceEnemyMove(Moves.SPLASH); + await game.toNextTurn(); + + expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + + game.move.select(Moves.SUBSTITUTE); + await game.forceEnemyMove(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.toNextTurn(); + + expect(game.scene.getPlayerPokemon()!.formIndex).toBe(GULPING_FORM); + }); it("cannot be suppressed", async () => { game.override.enemyMoveset(Moves.GASTRO_ACID); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index 723d5e8d855..1c7f7bd6093 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -1,3 +1,4 @@ +import { BattlerIndex } from "#app/battle"; import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase"; @@ -36,7 +37,7 @@ describe("Abilities - Ice Face", () => { }); it("takes no damage from physical move and transforms to Noice", async () => { - await game.startBattle([ Species.HITMONLEE ]); + await game.classicMode.startBattle([ Species.HITMONLEE ]); game.move.select(Moves.TACKLE); @@ -52,7 +53,7 @@ describe("Abilities - Ice Face", () => { it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => { game.override.moveset([ Moves.SURGING_STRIKES ]); game.override.enemyLevel(1); - await game.startBattle([ Species.HITMONLEE ]); + await game.classicMode.startBattle([ Species.HITMONLEE ]); game.move.select(Moves.SURGING_STRIKES); @@ -78,7 +79,7 @@ describe("Abilities - Ice Face", () => { }); it("takes damage from special moves", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.ICE_BEAM); @@ -92,7 +93,7 @@ describe("Abilities - Ice Face", () => { }); it("takes effects from status moves", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.TOXIC_THREAD); @@ -108,7 +109,7 @@ describe("Abilities - Ice Face", () => { game.override.moveset([ Moves.QUICK_ATTACK ]); game.override.enemyMoveset([ Moves.HAIL, Moves.HAIL, Moves.HAIL, Moves.HAIL ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.QUICK_ATTACK); @@ -130,7 +131,7 @@ describe("Abilities - Ice Face", () => { game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); game.override.moveset([ Moves.SNOWSCAPE ]); - await game.startBattle([ Species.EISCUE, Species.NINJASK ]); + await game.classicMode.startBattle([ Species.EISCUE, Species.NINJASK ]); game.move.select(Moves.SNOWSCAPE); @@ -157,7 +158,7 @@ describe("Abilities - Ice Face", () => { game.override.enemySpecies(Species.SHUCKLE); game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); - await game.startBattle([ Species.EISCUE ]); + await game.classicMode.startBattle([ Species.EISCUE ]); game.move.select(Moves.HAIL); const eiscue = game.scene.getPlayerPokemon()!; @@ -176,7 +177,7 @@ describe("Abilities - Ice Face", () => { it("persists form change when switched out", async () => { game.override.enemyMoveset([ Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK, Moves.QUICK_ATTACK ]); - await game.startBattle([ Species.EISCUE, Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.EISCUE, Species.MAGIKARP ]); game.move.select(Moves.ICE_BEAM); @@ -205,7 +206,7 @@ describe("Abilities - Ice Face", () => { [Species.EISCUE]: noiceForm, }); - await game.startBattle([ Species.EISCUE ]); + await game.classicMode.startBattle([ Species.EISCUE ]); const eiscue = game.scene.getPlayerPokemon()!; @@ -222,10 +223,23 @@ describe("Abilities - Ice Face", () => { expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); }); + it("doesn't trigger if user is behind a substitute", async () => { + game.override + .enemyMoveset(Moves.SUBSTITUTE) + .moveset(Moves.POWER_TRIP); + await game.classicMode.startBattle(); + + game.move.select(Moves.POWER_TRIP); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.formIndex).toBe(icefaceForm); + }); + it("cannot be suppressed", async () => { game.override.moveset([ Moves.GASTRO_ACID ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.GASTRO_ACID); @@ -241,7 +255,7 @@ describe("Abilities - Ice Face", () => { it("cannot be swapped with another ability", async () => { game.override.moveset([ Moves.SKILL_SWAP ]); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.SKILL_SWAP); @@ -257,7 +271,7 @@ describe("Abilities - Ice Face", () => { it("cannot be copied", async () => { game.override.ability(Abilities.TRACE); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); game.move.select(Moves.SIMPLE_BEAM); From 0996789ee6f232c45cf4d6597c78906d8e7c2cdc Mon Sep 17 00:00:00 2001 From: "Adrian T." <68144167+torranx@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:54:43 +0800 Subject: [PATCH 41/70] [Refactor] Improve typing in `phaseInterceptor.ts` (#4560) * improve typing in phaseInterceptor * add more param typings --------- Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/test/utils/phaseInterceptor.ts | 126 +++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 8 deletions(-) diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index d108c4cb2ea..ec9309e2405 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -53,6 +53,7 @@ import { } from "#app/phases/mystery-encounter-phases"; import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import { PartyExpPhase } from "#app/phases/party-exp-phase"; +import { ExpPhase } from "#app/phases/exp-phase"; export interface PromptHandler { phaseTarget?: string; @@ -61,7 +62,114 @@ export interface PromptHandler { expireFn?: () => void; awaitingActionInput?: boolean; } -import { ExpPhase } from "#app/phases/exp-phase"; + +type PhaseClass = + | typeof LoginPhase + | typeof TitlePhase + | typeof SelectGenderPhase + | typeof EncounterPhase + | typeof NewBiomeEncounterPhase + | typeof SelectStarterPhase + | typeof PostSummonPhase + | typeof SummonPhase + | typeof ToggleDoublePositionPhase + | typeof CheckSwitchPhase + | typeof ShowAbilityPhase + | typeof MessagePhase + | typeof TurnInitPhase + | typeof CommandPhase + | typeof EnemyCommandPhase + | typeof TurnStartPhase + | typeof MovePhase + | typeof MoveEffectPhase + | typeof DamagePhase + | typeof FaintPhase + | typeof BerryPhase + | typeof TurnEndPhase + | typeof BattleEndPhase + | typeof EggLapsePhase + | typeof SelectModifierPhase + | typeof NextEncounterPhase + | typeof NewBattlePhase + | typeof VictoryPhase + | typeof LearnMovePhase + | typeof MoveEndPhase + | typeof StatStageChangePhase + | typeof ShinySparklePhase + | typeof SelectTargetPhase + | typeof UnavailablePhase + | typeof QuietFormChangePhase + | typeof SwitchPhase + | typeof SwitchSummonPhase + | typeof PartyHealPhase + | typeof EvolutionPhase + | typeof EndEvolutionPhase + | typeof LevelCapPhase + | typeof AttemptRunPhase + | typeof SelectBiomePhase + | typeof MysteryEncounterPhase + | typeof MysteryEncounterOptionSelectedPhase + | typeof MysteryEncounterBattlePhase + | typeof MysteryEncounterRewardsPhase + | typeof PostMysteryEncounterPhase + | typeof ModifierRewardPhase + | typeof PartyExpPhase + | typeof ExpPhase; + +type PhaseString = + | "LoginPhase" + | "TitlePhase" + | "SelectGenderPhase" + | "EncounterPhase" + | "NewBiomeEncounterPhase" + | "SelectStarterPhase" + | "PostSummonPhase" + | "SummonPhase" + | "ToggleDoublePositionPhase" + | "CheckSwitchPhase" + | "ShowAbilityPhase" + | "MessagePhase" + | "TurnInitPhase" + | "CommandPhase" + | "EnemyCommandPhase" + | "TurnStartPhase" + | "MovePhase" + | "MoveEffectPhase" + | "DamagePhase" + | "FaintPhase" + | "BerryPhase" + | "TurnEndPhase" + | "BattleEndPhase" + | "EggLapsePhase" + | "SelectModifierPhase" + | "NextEncounterPhase" + | "NewBattlePhase" + | "VictoryPhase" + | "LearnMovePhase" + | "MoveEndPhase" + | "StatStageChangePhase" + | "ShinySparklePhase" + | "SelectTargetPhase" + | "UnavailablePhase" + | "QuietFormChangePhase" + | "SwitchPhase" + | "SwitchSummonPhase" + | "PartyHealPhase" + | "EvolutionPhase" + | "EndEvolutionPhase" + | "LevelCapPhase" + | "AttemptRunPhase" + | "SelectBiomePhase" + | "MysteryEncounterPhase" + | "MysteryEncounterOptionSelectedPhase" + | "MysteryEncounterBattlePhase" + | "MysteryEncounterRewardsPhase" + | "PostMysteryEncounterPhase" + | "ModifierRewardPhase" + | "PartyExpPhase" + | "ExpPhase"; + +type PhaseInterceptorPhase = PhaseClass | PhaseString; export default class PhaseInterceptor { public scene; @@ -172,7 +280,7 @@ export default class PhaseInterceptor { * @param phaseFrom - The phase to start from. * @returns The instance of the PhaseInterceptor. */ - runFrom(phaseFrom) { + runFrom(phaseFrom: PhaseInterceptorPhase): PhaseInterceptor { this.phaseFrom = phaseFrom; return this; } @@ -180,9 +288,10 @@ export default class PhaseInterceptor { /** * Method to transition to a target phase. * @param phaseTo - The phase to transition to. + * @param runTarget - Whether or not to run the target phase. * @returns A promise that resolves when the transition is complete. */ - async to(phaseTo, runTarget: boolean = true): Promise { + async to(phaseTo: PhaseInterceptorPhase, runTarget: boolean = true): Promise { return new Promise(async (resolve, reject) => { ErrorInterceptor.getInstance().add(this); if (this.phaseFrom) { @@ -219,7 +328,7 @@ export default class PhaseInterceptor { * @param skipFn - Optional skip function. * @returns A promise that resolves when the phase is run. */ - run(phaseTarget, skipFn?): Promise { + run(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; this.scene.moveAnimations = null; // Mandatory to avoid crash return new Promise(async (resolve, reject) => { @@ -253,7 +362,7 @@ export default class PhaseInterceptor { }); } - whenAboutToRun(phaseTarget, skipFn?): Promise { + whenAboutToRun(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; this.scene.moveAnimations = null; // Mandatory to avoid crash return new Promise(async (resolve, reject) => { @@ -311,7 +420,7 @@ export default class PhaseInterceptor { * Method to start a phase and log it. * @param phase - The phase to start. */ - startPhase(phase) { + startPhase(phase: PhaseClass) { this.log.push(phase.name); const instance = this.scene.getCurrentPhase(); this.onHold.push({ @@ -340,9 +449,10 @@ export default class PhaseInterceptor { /** * m2m to set mode. - * @param phase - The phase to start. + * @param mode - The {@linkcode Mode} to set. + * @param args - Additional arguments to pass to the original method. */ - setMode(mode: Mode, ...args: any[]): Promise { + setMode(mode: Mode, ...args: unknown[]): Promise { const currentPhase = this.scene.getCurrentPhase(); const instance = this.scene.ui; console.log("setMode", `${Mode[mode]} (=${mode})`, args); From 6ad5ba972cc7eafc451ed264bae3e1c153463fd8 Mon Sep 17 00:00:00 2001 From: ImperialSympathizer <110984302+ben-lear@users.noreply.github.com> Date: Thu, 10 Oct 2024 12:29:26 -0400 Subject: [PATCH 42/70] [Enhancement] Refactor Starter Species to use separate EggTier map (#4591) * creates table for tracking species egg tiers * creates table for tracking species egg tiers * rename EggTier enum values * replace clamp util function with Phaser function --------- Co-authored-by: ImperialSympathizer --- src/data/balance/species-egg-tiers.ts | 603 ++++++++++++++++++ src/data/egg.ts | 91 ++- .../encounters/a-trainers-test-encounter.ts | 4 +- .../the-expert-pokemon-breeder-encounter.ts | 2 +- src/enums/egg-type.ts | 6 +- src/field/pokemon.ts | 2 +- src/test/eggs/egg.test.ts | 36 +- .../a-trainers-test-encounter.test.ts | 4 +- .../the-expert-breeder-encounter.test.ts | 6 +- src/ui/battle-info.ts | 2 +- src/ui/egg-gacha-ui-handler.ts | 6 +- src/ui/text.ts | 6 +- src/utils.ts | 4 - 13 files changed, 676 insertions(+), 96 deletions(-) create mode 100644 src/data/balance/species-egg-tiers.ts diff --git a/src/data/balance/species-egg-tiers.ts b/src/data/balance/species-egg-tiers.ts new file mode 100644 index 00000000000..cd266dfcf54 --- /dev/null +++ b/src/data/balance/species-egg-tiers.ts @@ -0,0 +1,603 @@ +import { Species } from "#enums/species"; +import { EggTier } from "#enums/egg-type"; + +/** + * Map of all starters and their respective {@linkcode EggTier}, which determines the type of egg the starter hatches from. + */ +export const speciesEggTiers = { + [Species.BULBASAUR]: EggTier.COMMON, + [Species.CHARMANDER]: EggTier.COMMON, + [Species.SQUIRTLE]: EggTier.COMMON, + [Species.CATERPIE]: EggTier.COMMON, + [Species.WEEDLE]: EggTier.COMMON, + [Species.PIDGEY]: EggTier.COMMON, + [Species.RATTATA]: EggTier.COMMON, + [Species.SPEAROW]: EggTier.COMMON, + [Species.EKANS]: EggTier.COMMON, + [Species.PIKACHU]: EggTier.COMMON, + [Species.SANDSHREW]: EggTier.COMMON, + [Species.NIDORAN_F]: EggTier.COMMON, + [Species.NIDORAN_M]: EggTier.COMMON, + [Species.CLEFAIRY]: EggTier.COMMON, + [Species.VULPIX]: EggTier.COMMON, + [Species.JIGGLYPUFF]: EggTier.COMMON, + [Species.ZUBAT]: EggTier.COMMON, + [Species.ODDISH]: EggTier.COMMON, + [Species.PARAS]: EggTier.COMMON, + [Species.VENONAT]: EggTier.COMMON, + [Species.DIGLETT]: EggTier.COMMON, + [Species.MEOWTH]: EggTier.COMMON, + [Species.PSYDUCK]: EggTier.COMMON, + [Species.MANKEY]: EggTier.RARE, + [Species.GROWLITHE]: EggTier.RARE, + [Species.POLIWAG]: EggTier.COMMON, + [Species.ABRA]: EggTier.RARE, + [Species.MACHOP]: EggTier.COMMON, + [Species.BELLSPROUT]: EggTier.COMMON, + [Species.TENTACOOL]: EggTier.COMMON, + [Species.GEODUDE]: EggTier.COMMON, + [Species.PONYTA]: EggTier.COMMON, + [Species.SLOWPOKE]: EggTier.COMMON, + [Species.MAGNEMITE]: EggTier.RARE, + [Species.FARFETCHD]: EggTier.COMMON, + [Species.DODUO]: EggTier.COMMON, + [Species.SEEL]: EggTier.COMMON, + [Species.GRIMER]: EggTier.COMMON, + [Species.SHELLDER]: EggTier.RARE, + [Species.GASTLY]: EggTier.RARE, + [Species.ONIX]: EggTier.COMMON, + [Species.DROWZEE]: EggTier.COMMON, + [Species.KRABBY]: EggTier.COMMON, + [Species.VOLTORB]: EggTier.COMMON, + [Species.EXEGGCUTE]: EggTier.COMMON, + [Species.CUBONE]: EggTier.COMMON, + [Species.HITMONLEE]: EggTier.RARE, + [Species.HITMONCHAN]: EggTier.RARE, + [Species.LICKITUNG]: EggTier.COMMON, + [Species.KOFFING]: EggTier.COMMON, + [Species.RHYHORN]: EggTier.COMMON, + [Species.CHANSEY]: EggTier.COMMON, + [Species.TANGELA]: EggTier.COMMON, + [Species.KANGASKHAN]: EggTier.RARE, + [Species.HORSEA]: EggTier.COMMON, + [Species.GOLDEEN]: EggTier.COMMON, + [Species.STARYU]: EggTier.COMMON, + [Species.MR_MIME]: EggTier.COMMON, + [Species.SCYTHER]: EggTier.RARE, + [Species.JYNX]: EggTier.RARE, + [Species.ELECTABUZZ]: EggTier.RARE, + [Species.MAGMAR]: EggTier.RARE, + [Species.PINSIR]: EggTier.RARE, + [Species.TAUROS]: EggTier.RARE, + [Species.MAGIKARP]: EggTier.RARE, + [Species.LAPRAS]: EggTier.RARE, + [Species.DITTO]: EggTier.COMMON, + [Species.EEVEE]: EggTier.COMMON, + [Species.PORYGON]: EggTier.RARE, + [Species.OMANYTE]: EggTier.COMMON, + [Species.KABUTO]: EggTier.COMMON, + [Species.AERODACTYL]: EggTier.RARE, + [Species.SNORLAX]: EggTier.RARE, + [Species.ARTICUNO]: EggTier.EPIC, + [Species.ZAPDOS]: EggTier.EPIC, + [Species.MOLTRES]: EggTier.EPIC, + [Species.DRATINI]: EggTier.RARE, + [Species.MEWTWO]: EggTier.LEGENDARY, + [Species.MEW]: EggTier.EPIC, + + [Species.CHIKORITA]: EggTier.COMMON, + [Species.CYNDAQUIL]: EggTier.COMMON, + [Species.TOTODILE]: EggTier.COMMON, + [Species.SENTRET]: EggTier.COMMON, + [Species.HOOTHOOT]: EggTier.COMMON, + [Species.LEDYBA]: EggTier.COMMON, + [Species.SPINARAK]: EggTier.COMMON, + [Species.CHINCHOU]: EggTier.COMMON, + [Species.PICHU]: EggTier.COMMON, + [Species.CLEFFA]: EggTier.COMMON, + [Species.IGGLYBUFF]: EggTier.COMMON, + [Species.TOGEPI]: EggTier.COMMON, + [Species.NATU]: EggTier.COMMON, + [Species.MAREEP]: EggTier.COMMON, + [Species.MARILL]: EggTier.RARE, + [Species.SUDOWOODO]: EggTier.COMMON, + [Species.HOPPIP]: EggTier.COMMON, + [Species.AIPOM]: EggTier.COMMON, + [Species.SUNKERN]: EggTier.COMMON, + [Species.YANMA]: EggTier.COMMON, + [Species.WOOPER]: EggTier.COMMON, + [Species.MURKROW]: EggTier.COMMON, + [Species.MISDREAVUS]: EggTier.COMMON, + [Species.UNOWN]: EggTier.COMMON, + [Species.WOBBUFFET]: EggTier.COMMON, + [Species.GIRAFARIG]: EggTier.COMMON, + [Species.PINECO]: EggTier.COMMON, + [Species.DUNSPARCE]: EggTier.COMMON, + [Species.GLIGAR]: EggTier.COMMON, + [Species.SNUBBULL]: EggTier.COMMON, + [Species.QWILFISH]: EggTier.COMMON, + [Species.SHUCKLE]: EggTier.COMMON, + [Species.HERACROSS]: EggTier.RARE, + [Species.SNEASEL]: EggTier.RARE, + [Species.TEDDIURSA]: EggTier.RARE, + [Species.SLUGMA]: EggTier.COMMON, + [Species.SWINUB]: EggTier.COMMON, + [Species.CORSOLA]: EggTier.COMMON, + [Species.REMORAID]: EggTier.COMMON, + [Species.DELIBIRD]: EggTier.COMMON, + [Species.MANTINE]: EggTier.COMMON, + [Species.SKARMORY]: EggTier.RARE, + [Species.HOUNDOUR]: EggTier.COMMON, + [Species.PHANPY]: EggTier.COMMON, + [Species.STANTLER]: EggTier.COMMON, + [Species.SMEARGLE]: EggTier.COMMON, + [Species.TYROGUE]: EggTier.COMMON, + [Species.SMOOCHUM]: EggTier.COMMON, + [Species.ELEKID]: EggTier.COMMON, + [Species.MAGBY]: EggTier.COMMON, + [Species.MILTANK]: EggTier.RARE, + [Species.RAIKOU]: EggTier.EPIC, + [Species.ENTEI]: EggTier.EPIC, + [Species.SUICUNE]: EggTier.EPIC, + [Species.LARVITAR]: EggTier.RARE, + [Species.LUGIA]: EggTier.LEGENDARY, + [Species.HO_OH]: EggTier.LEGENDARY, + [Species.CELEBI]: EggTier.EPIC, + + [Species.TREECKO]: EggTier.COMMON, + [Species.TORCHIC]: EggTier.RARE, + [Species.MUDKIP]: EggTier.COMMON, + [Species.POOCHYENA]: EggTier.COMMON, + [Species.ZIGZAGOON]: EggTier.COMMON, + [Species.WURMPLE]: EggTier.COMMON, + [Species.LOTAD]: EggTier.COMMON, + [Species.SEEDOT]: EggTier.COMMON, + [Species.TAILLOW]: EggTier.COMMON, + [Species.WINGULL]: EggTier.COMMON, + [Species.RALTS]: EggTier.COMMON, + [Species.SURSKIT]: EggTier.COMMON, + [Species.SHROOMISH]: EggTier.COMMON, + [Species.SLAKOTH]: EggTier.RARE, + [Species.NINCADA]: EggTier.RARE, + [Species.WHISMUR]: EggTier.COMMON, + [Species.MAKUHITA]: EggTier.COMMON, + [Species.AZURILL]: EggTier.RARE, + [Species.NOSEPASS]: EggTier.COMMON, + [Species.SKITTY]: EggTier.COMMON, + [Species.SABLEYE]: EggTier.COMMON, + [Species.MAWILE]: EggTier.COMMON, + [Species.ARON]: EggTier.COMMON, + [Species.MEDITITE]: EggTier.COMMON, + [Species.ELECTRIKE]: EggTier.COMMON, + [Species.PLUSLE]: EggTier.COMMON, + [Species.MINUN]: EggTier.COMMON, + [Species.VOLBEAT]: EggTier.COMMON, + [Species.ILLUMISE]: EggTier.COMMON, + [Species.ROSELIA]: EggTier.COMMON, + [Species.GULPIN]: EggTier.COMMON, + [Species.CARVANHA]: EggTier.COMMON, + [Species.WAILMER]: EggTier.COMMON, + [Species.NUMEL]: EggTier.COMMON, + [Species.TORKOAL]: EggTier.COMMON, + [Species.SPOINK]: EggTier.COMMON, + [Species.SPINDA]: EggTier.COMMON, + [Species.TRAPINCH]: EggTier.COMMON, + [Species.CACNEA]: EggTier.COMMON, + [Species.SWABLU]: EggTier.COMMON, + [Species.ZANGOOSE]: EggTier.RARE, + [Species.SEVIPER]: EggTier.COMMON, + [Species.LUNATONE]: EggTier.COMMON, + [Species.SOLROCK]: EggTier.COMMON, + [Species.BARBOACH]: EggTier.COMMON, + [Species.CORPHISH]: EggTier.COMMON, + [Species.BALTOY]: EggTier.COMMON, + [Species.LILEEP]: EggTier.COMMON, + [Species.ANORITH]: EggTier.COMMON, + [Species.FEEBAS]: EggTier.RARE, + [Species.CASTFORM]: EggTier.COMMON, + [Species.KECLEON]: EggTier.COMMON, + [Species.SHUPPET]: EggTier.COMMON, + [Species.DUSKULL]: EggTier.COMMON, + [Species.TROPIUS]: EggTier.COMMON, + [Species.CHIMECHO]: EggTier.COMMON, + [Species.ABSOL]: EggTier.RARE, + [Species.WYNAUT]: EggTier.COMMON, + [Species.SNORUNT]: EggTier.COMMON, + [Species.SPHEAL]: EggTier.COMMON, + [Species.CLAMPERL]: EggTier.COMMON, + [Species.RELICANTH]: EggTier.COMMON, + [Species.LUVDISC]: EggTier.COMMON, + [Species.BAGON]: EggTier.RARE, + [Species.BELDUM]: EggTier.RARE, + [Species.REGIROCK]: EggTier.EPIC, + [Species.REGICE]: EggTier.EPIC, + [Species.REGISTEEL]: EggTier.EPIC, + [Species.LATIAS]: EggTier.EPIC, + [Species.LATIOS]: EggTier.EPIC, + [Species.KYOGRE]: EggTier.LEGENDARY, + [Species.GROUDON]: EggTier.LEGENDARY, + [Species.RAYQUAZA]: EggTier.LEGENDARY, + [Species.JIRACHI]: EggTier.EPIC, + [Species.DEOXYS]: EggTier.EPIC, + + [Species.TURTWIG]: EggTier.COMMON, + [Species.CHIMCHAR]: EggTier.COMMON, + [Species.PIPLUP]: EggTier.COMMON, + [Species.STARLY]: EggTier.COMMON, + [Species.BIDOOF]: EggTier.COMMON, + [Species.KRICKETOT]: EggTier.COMMON, + [Species.SHINX]: EggTier.COMMON, + [Species.BUDEW]: EggTier.COMMON, + [Species.CRANIDOS]: EggTier.COMMON, + [Species.SHIELDON]: EggTier.COMMON, + [Species.BURMY]: EggTier.COMMON, + [Species.COMBEE]: EggTier.COMMON, + [Species.PACHIRISU]: EggTier.COMMON, + [Species.BUIZEL]: EggTier.COMMON, + [Species.CHERUBI]: EggTier.COMMON, + [Species.SHELLOS]: EggTier.COMMON, + [Species.DRIFLOON]: EggTier.COMMON, + [Species.BUNEARY]: EggTier.COMMON, + [Species.GLAMEOW]: EggTier.COMMON, + [Species.CHINGLING]: EggTier.COMMON, + [Species.STUNKY]: EggTier.COMMON, + [Species.BRONZOR]: EggTier.COMMON, + [Species.BONSLY]: EggTier.COMMON, + [Species.MIME_JR]: EggTier.COMMON, + [Species.HAPPINY]: EggTier.COMMON, + [Species.CHATOT]: EggTier.COMMON, + [Species.SPIRITOMB]: EggTier.RARE, + [Species.GIBLE]: EggTier.RARE, + [Species.MUNCHLAX]: EggTier.RARE, + [Species.RIOLU]: EggTier.COMMON, + [Species.HIPPOPOTAS]: EggTier.COMMON, + [Species.SKORUPI]: EggTier.COMMON, + [Species.CROAGUNK]: EggTier.COMMON, + [Species.CARNIVINE]: EggTier.COMMON, + [Species.FINNEON]: EggTier.COMMON, + [Species.MANTYKE]: EggTier.COMMON, + [Species.SNOVER]: EggTier.COMMON, + [Species.ROTOM]: EggTier.RARE, + [Species.UXIE]: EggTier.EPIC, + [Species.MESPRIT]: EggTier.EPIC, + [Species.AZELF]: EggTier.EPIC, + [Species.DIALGA]: EggTier.LEGENDARY, + [Species.PALKIA]: EggTier.LEGENDARY, + [Species.HEATRAN]: EggTier.EPIC, + [Species.REGIGIGAS]: EggTier.EPIC, + [Species.GIRATINA]: EggTier.LEGENDARY, + [Species.CRESSELIA]: EggTier.EPIC, + [Species.PHIONE]: EggTier.RARE, + [Species.MANAPHY]: EggTier.EPIC, + [Species.DARKRAI]: EggTier.EPIC, + [Species.SHAYMIN]: EggTier.EPIC, + [Species.ARCEUS]: EggTier.LEGENDARY, + + [Species.VICTINI]: EggTier.EPIC, + [Species.SNIVY]: EggTier.COMMON, + [Species.TEPIG]: EggTier.COMMON, + [Species.OSHAWOTT]: EggTier.COMMON, + [Species.PATRAT]: EggTier.COMMON, + [Species.LILLIPUP]: EggTier.COMMON, + [Species.PURRLOIN]: EggTier.COMMON, + [Species.PANSAGE]: EggTier.COMMON, + [Species.PANSEAR]: EggTier.COMMON, + [Species.PANPOUR]: EggTier.COMMON, + [Species.MUNNA]: EggTier.COMMON, + [Species.PIDOVE]: EggTier.COMMON, + [Species.BLITZLE]: EggTier.COMMON, + [Species.ROGGENROLA]: EggTier.COMMON, + [Species.WOOBAT]: EggTier.COMMON, + [Species.DRILBUR]: EggTier.RARE, + [Species.AUDINO]: EggTier.COMMON, + [Species.TIMBURR]: EggTier.RARE, + [Species.TYMPOLE]: EggTier.COMMON, + [Species.THROH]: EggTier.RARE, + [Species.SAWK]: EggTier.RARE, + [Species.SEWADDLE]: EggTier.COMMON, + [Species.VENIPEDE]: EggTier.COMMON, + [Species.COTTONEE]: EggTier.COMMON, + [Species.PETILIL]: EggTier.COMMON, + [Species.BASCULIN]: EggTier.RARE, + [Species.SANDILE]: EggTier.RARE, + [Species.DARUMAKA]: EggTier.RARE, + [Species.MARACTUS]: EggTier.COMMON, + [Species.DWEBBLE]: EggTier.COMMON, + [Species.SCRAGGY]: EggTier.COMMON, + [Species.SIGILYPH]: EggTier.RARE, + [Species.YAMASK]: EggTier.COMMON, + [Species.TIRTOUGA]: EggTier.COMMON, + [Species.ARCHEN]: EggTier.COMMON, + [Species.TRUBBISH]: EggTier.COMMON, + [Species.ZORUA]: EggTier.COMMON, + [Species.MINCCINO]: EggTier.COMMON, + [Species.GOTHITA]: EggTier.COMMON, + [Species.SOLOSIS]: EggTier.COMMON, + [Species.DUCKLETT]: EggTier.COMMON, + [Species.VANILLITE]: EggTier.COMMON, + [Species.DEERLING]: EggTier.COMMON, + [Species.EMOLGA]: EggTier.COMMON, + [Species.KARRABLAST]: EggTier.COMMON, + [Species.FOONGUS]: EggTier.COMMON, + [Species.FRILLISH]: EggTier.COMMON, + [Species.ALOMOMOLA]: EggTier.RARE, + [Species.JOLTIK]: EggTier.COMMON, + [Species.FERROSEED]: EggTier.COMMON, + [Species.KLINK]: EggTier.COMMON, + [Species.TYNAMO]: EggTier.COMMON, + [Species.ELGYEM]: EggTier.COMMON, + [Species.LITWICK]: EggTier.COMMON, + [Species.AXEW]: EggTier.RARE, + [Species.CUBCHOO]: EggTier.COMMON, + [Species.CRYOGONAL]: EggTier.RARE, + [Species.SHELMET]: EggTier.COMMON, + [Species.STUNFISK]: EggTier.COMMON, + [Species.MIENFOO]: EggTier.COMMON, + [Species.DRUDDIGON]: EggTier.RARE, + [Species.GOLETT]: EggTier.COMMON, + [Species.PAWNIARD]: EggTier.RARE, + [Species.BOUFFALANT]: EggTier.RARE, + [Species.RUFFLET]: EggTier.COMMON, + [Species.VULLABY]: EggTier.COMMON, + [Species.HEATMOR]: EggTier.COMMON, + [Species.DURANT]: EggTier.RARE, + [Species.DEINO]: EggTier.RARE, + [Species.LARVESTA]: EggTier.RARE, + [Species.COBALION]: EggTier.EPIC, + [Species.TERRAKION]: EggTier.EPIC, + [Species.VIRIZION]: EggTier.EPIC, + [Species.TORNADUS]: EggTier.EPIC, + [Species.THUNDURUS]: EggTier.EPIC, + [Species.RESHIRAM]: EggTier.LEGENDARY, + [Species.ZEKROM]: EggTier.LEGENDARY, + [Species.LANDORUS]: EggTier.EPIC, + [Species.KYUREM]: EggTier.LEGENDARY, + [Species.KELDEO]: EggTier.EPIC, + [Species.MELOETTA]: EggTier.EPIC, + [Species.GENESECT]: EggTier.EPIC, + + [Species.CHESPIN]: EggTier.COMMON, + [Species.FENNEKIN]: EggTier.COMMON, + [Species.FROAKIE]: EggTier.RARE, + [Species.BUNNELBY]: EggTier.COMMON, + [Species.FLETCHLING]: EggTier.COMMON, + [Species.SCATTERBUG]: EggTier.COMMON, + [Species.LITLEO]: EggTier.COMMON, + [Species.FLABEBE]: EggTier.COMMON, + [Species.SKIDDO]: EggTier.COMMON, + [Species.PANCHAM]: EggTier.COMMON, + [Species.FURFROU]: EggTier.COMMON, + [Species.ESPURR]: EggTier.COMMON, + [Species.HONEDGE]: EggTier.RARE, + [Species.SPRITZEE]: EggTier.COMMON, + [Species.SWIRLIX]: EggTier.COMMON, + [Species.INKAY]: EggTier.COMMON, + [Species.BINACLE]: EggTier.COMMON, + [Species.SKRELP]: EggTier.COMMON, + [Species.CLAUNCHER]: EggTier.COMMON, + [Species.HELIOPTILE]: EggTier.COMMON, + [Species.TYRUNT]: EggTier.COMMON, + [Species.AMAURA]: EggTier.COMMON, + [Species.HAWLUCHA]: EggTier.RARE, + [Species.DEDENNE]: EggTier.COMMON, + [Species.CARBINK]: EggTier.COMMON, + [Species.GOOMY]: EggTier.RARE, + [Species.KLEFKI]: EggTier.COMMON, + [Species.PHANTUMP]: EggTier.COMMON, + [Species.PUMPKABOO]: EggTier.COMMON, + [Species.BERGMITE]: EggTier.COMMON, + [Species.NOIBAT]: EggTier.COMMON, + [Species.XERNEAS]: EggTier.LEGENDARY, + [Species.YVELTAL]: EggTier.LEGENDARY, + [Species.ZYGARDE]: EggTier.LEGENDARY, + [Species.DIANCIE]: EggTier.EPIC, + [Species.HOOPA]: EggTier.EPIC, + [Species.VOLCANION]: EggTier.EPIC, + [Species.ETERNAL_FLOETTE]: EggTier.RARE, + + [Species.ROWLET]: EggTier.COMMON, + [Species.LITTEN]: EggTier.COMMON, + [Species.POPPLIO]: EggTier.RARE, + [Species.PIKIPEK]: EggTier.COMMON, + [Species.YUNGOOS]: EggTier.COMMON, + [Species.GRUBBIN]: EggTier.COMMON, + [Species.CRABRAWLER]: EggTier.COMMON, + [Species.ORICORIO]: EggTier.COMMON, + [Species.CUTIEFLY]: EggTier.COMMON, + [Species.ROCKRUFF]: EggTier.COMMON, + [Species.WISHIWASHI]: EggTier.COMMON, + [Species.MAREANIE]: EggTier.COMMON, + [Species.MUDBRAY]: EggTier.COMMON, + [Species.DEWPIDER]: EggTier.COMMON, + [Species.FOMANTIS]: EggTier.COMMON, + [Species.MORELULL]: EggTier.COMMON, + [Species.SALANDIT]: EggTier.COMMON, + [Species.STUFFUL]: EggTier.COMMON, + [Species.BOUNSWEET]: EggTier.COMMON, + [Species.COMFEY]: EggTier.RARE, + [Species.ORANGURU]: EggTier.RARE, + [Species.PASSIMIAN]: EggTier.RARE, + [Species.WIMPOD]: EggTier.COMMON, + [Species.SANDYGAST]: EggTier.COMMON, + [Species.PYUKUMUKU]: EggTier.COMMON, + [Species.TYPE_NULL]: EggTier.RARE, + [Species.MINIOR]: EggTier.RARE, + [Species.KOMALA]: EggTier.COMMON, + [Species.TURTONATOR]: EggTier.RARE, + [Species.TOGEDEMARU]: EggTier.COMMON, + [Species.MIMIKYU]: EggTier.RARE, + [Species.BRUXISH]: EggTier.RARE, + [Species.DRAMPA]: EggTier.RARE, + [Species.DHELMISE]: EggTier.RARE, + [Species.JANGMO_O]: EggTier.RARE, + [Species.TAPU_KOKO]: EggTier.EPIC, + [Species.TAPU_LELE]: EggTier.EPIC, + [Species.TAPU_BULU]: EggTier.EPIC, + [Species.TAPU_FINI]: EggTier.EPIC, + [Species.COSMOG]: EggTier.EPIC, + [Species.NIHILEGO]: EggTier.EPIC, + [Species.BUZZWOLE]: EggTier.EPIC, + [Species.PHEROMOSA]: EggTier.EPIC, + [Species.XURKITREE]: EggTier.EPIC, + [Species.CELESTEELA]: EggTier.EPIC, + [Species.KARTANA]: EggTier.EPIC, + [Species.GUZZLORD]: EggTier.EPIC, + [Species.NECROZMA]: EggTier.LEGENDARY, + [Species.MAGEARNA]: EggTier.EPIC, + [Species.MARSHADOW]: EggTier.EPIC, + [Species.POIPOLE]: EggTier.EPIC, + [Species.STAKATAKA]: EggTier.EPIC, + [Species.BLACEPHALON]: EggTier.EPIC, + [Species.ZERAORA]: EggTier.EPIC, + [Species.MELTAN]: EggTier.EPIC, + [Species.ALOLA_RATTATA]: EggTier.COMMON, + [Species.ALOLA_SANDSHREW]: EggTier.COMMON, + [Species.ALOLA_VULPIX]: EggTier.COMMON, + [Species.ALOLA_DIGLETT]: EggTier.COMMON, + [Species.ALOLA_MEOWTH]: EggTier.COMMON, + [Species.ALOLA_GEODUDE]: EggTier.COMMON, + [Species.ALOLA_GRIMER]: EggTier.COMMON, + + [Species.GROOKEY]: EggTier.COMMON, + [Species.SCORBUNNY]: EggTier.RARE, + [Species.SOBBLE]: EggTier.COMMON, + [Species.SKWOVET]: EggTier.COMMON, + [Species.ROOKIDEE]: EggTier.COMMON, + [Species.BLIPBUG]: EggTier.COMMON, + [Species.NICKIT]: EggTier.COMMON, + [Species.GOSSIFLEUR]: EggTier.COMMON, + [Species.WOOLOO]: EggTier.COMMON, + [Species.CHEWTLE]: EggTier.COMMON, + [Species.YAMPER]: EggTier.COMMON, + [Species.ROLYCOLY]: EggTier.COMMON, + [Species.APPLIN]: EggTier.COMMON, + [Species.SILICOBRA]: EggTier.COMMON, + [Species.CRAMORANT]: EggTier.COMMON, + [Species.ARROKUDA]: EggTier.COMMON, + [Species.TOXEL]: EggTier.COMMON, + [Species.SIZZLIPEDE]: EggTier.COMMON, + [Species.CLOBBOPUS]: EggTier.COMMON, + [Species.SINISTEA]: EggTier.COMMON, + [Species.HATENNA]: EggTier.COMMON, + [Species.IMPIDIMP]: EggTier.COMMON, + [Species.MILCERY]: EggTier.COMMON, + [Species.FALINKS]: EggTier.RARE, + [Species.PINCURCHIN]: EggTier.COMMON, + [Species.SNOM]: EggTier.COMMON, + [Species.STONJOURNER]: EggTier.COMMON, + [Species.EISCUE]: EggTier.COMMON, + [Species.INDEEDEE]: EggTier.RARE, + [Species.MORPEKO]: EggTier.COMMON, + [Species.CUFANT]: EggTier.COMMON, + [Species.DRACOZOLT]: EggTier.RARE, + [Species.ARCTOZOLT]: EggTier.RARE, + [Species.DRACOVISH]: EggTier.RARE, + [Species.ARCTOVISH]: EggTier.RARE, + [Species.DURALUDON]: EggTier.RARE, + [Species.DREEPY]: EggTier.RARE, + [Species.ZACIAN]: EggTier.LEGENDARY, + [Species.ZAMAZENTA]: EggTier.LEGENDARY, + [Species.ETERNATUS]: EggTier.COMMON, + [Species.KUBFU]: EggTier.EPIC, + [Species.ZARUDE]: EggTier.EPIC, + [Species.REGIELEKI]: EggTier.EPIC, + [Species.REGIDRAGO]: EggTier.EPIC, + [Species.GLASTRIER]: EggTier.EPIC, + [Species.SPECTRIER]: EggTier.EPIC, + [Species.CALYREX]: EggTier.LEGENDARY, + [Species.GALAR_MEOWTH]: EggTier.COMMON, + [Species.GALAR_PONYTA]: EggTier.COMMON, + [Species.GALAR_SLOWPOKE]: EggTier.COMMON, + [Species.GALAR_FARFETCHD]: EggTier.COMMON, + [Species.GALAR_CORSOLA]: EggTier.COMMON, + [Species.GALAR_ZIGZAGOON]: EggTier.COMMON, + [Species.GALAR_DARUMAKA]: EggTier.RARE, + [Species.GALAR_YAMASK]: EggTier.COMMON, + [Species.GALAR_STUNFISK]: EggTier.COMMON, + [Species.GALAR_MR_MIME]: EggTier.COMMON, + [Species.GALAR_ARTICUNO]: EggTier.EPIC, + [Species.GALAR_ZAPDOS]: EggTier.EPIC, + [Species.GALAR_MOLTRES]: EggTier.EPIC, + [Species.HISUI_GROWLITHE]: EggTier.RARE, + [Species.HISUI_VOLTORB]: EggTier.COMMON, + [Species.HISUI_QWILFISH]: EggTier.RARE, + [Species.HISUI_SNEASEL]: EggTier.RARE, + [Species.HISUI_ZORUA]: EggTier.COMMON, + [Species.ENAMORUS]: EggTier.EPIC, + + [Species.SPRIGATITO]: EggTier.RARE, + [Species.FUECOCO]: EggTier.RARE, + [Species.QUAXLY]: EggTier.RARE, + [Species.LECHONK]: EggTier.COMMON, + [Species.TAROUNTULA]: EggTier.COMMON, + [Species.NYMBLE]: EggTier.COMMON, + [Species.PAWMI]: EggTier.COMMON, + [Species.TANDEMAUS]: EggTier.RARE, + [Species.FIDOUGH]: EggTier.COMMON, + [Species.SMOLIV]: EggTier.COMMON, + [Species.SQUAWKABILLY]: EggTier.COMMON, + [Species.NACLI]: EggTier.RARE, + [Species.CHARCADET]: EggTier.RARE, + [Species.TADBULB]: EggTier.COMMON, + [Species.WATTREL]: EggTier.COMMON, + [Species.MASCHIFF]: EggTier.COMMON, + [Species.SHROODLE]: EggTier.COMMON, + [Species.BRAMBLIN]: EggTier.COMMON, + [Species.TOEDSCOOL]: EggTier.COMMON, + [Species.KLAWF]: EggTier.COMMON, + [Species.CAPSAKID]: EggTier.COMMON, + [Species.RELLOR]: EggTier.COMMON, + [Species.FLITTLE]: EggTier.COMMON, + [Species.TINKATINK]: EggTier.RARE, + [Species.WIGLETT]: EggTier.COMMON, + [Species.BOMBIRDIER]: EggTier.COMMON, + [Species.FINIZEN]: EggTier.COMMON, + [Species.VAROOM]: EggTier.RARE, + [Species.CYCLIZAR]: EggTier.RARE, + [Species.ORTHWORM]: EggTier.RARE, + [Species.GLIMMET]: EggTier.RARE, + [Species.GREAVARD]: EggTier.COMMON, + [Species.FLAMIGO]: EggTier.RARE, + [Species.CETODDLE]: EggTier.COMMON, + [Species.VELUZA]: EggTier.RARE, + [Species.DONDOZO]: EggTier.RARE, + [Species.TATSUGIRI]: EggTier.RARE, + [Species.GREAT_TUSK]: EggTier.EPIC, + [Species.SCREAM_TAIL]: EggTier.EPIC, + [Species.BRUTE_BONNET]: EggTier.EPIC, + [Species.FLUTTER_MANE]: EggTier.EPIC, + [Species.SLITHER_WING]: EggTier.EPIC, + [Species.SANDY_SHOCKS]: EggTier.EPIC, + [Species.IRON_TREADS]: EggTier.EPIC, + [Species.IRON_BUNDLE]: EggTier.EPIC, + [Species.IRON_HANDS]: EggTier.EPIC, + [Species.IRON_JUGULIS]: EggTier.EPIC, + [Species.IRON_MOTH]: EggTier.EPIC, + [Species.IRON_THORNS]: EggTier.EPIC, + [Species.FRIGIBAX]: EggTier.RARE, + [Species.GIMMIGHOUL]: EggTier.RARE, + [Species.WO_CHIEN]: EggTier.EPIC, + [Species.CHIEN_PAO]: EggTier.EPIC, + [Species.TING_LU]: EggTier.EPIC, + [Species.CHI_YU]: EggTier.EPIC, + [Species.ROARING_MOON]: EggTier.EPIC, + [Species.IRON_VALIANT]: EggTier.EPIC, + [Species.KORAIDON]: EggTier.LEGENDARY, + [Species.MIRAIDON]: EggTier.LEGENDARY, + [Species.WALKING_WAKE]: EggTier.EPIC, + [Species.IRON_LEAVES]: EggTier.EPIC, + [Species.POLTCHAGEIST]: EggTier.RARE, + [Species.OKIDOGI]: EggTier.EPIC, + [Species.MUNKIDORI]: EggTier.EPIC, + [Species.FEZANDIPITI]: EggTier.EPIC, + [Species.OGERPON]: EggTier.EPIC, + [Species.GOUGING_FIRE]: EggTier.EPIC, + [Species.RAGING_BOLT]: EggTier.EPIC, + [Species.IRON_BOULDER]: EggTier.EPIC, + [Species.IRON_CROWN]: EggTier.EPIC, + [Species.TERAPAGOS]: EggTier.LEGENDARY, + [Species.PECHARUNT]: EggTier.EPIC, + [Species.PALDEA_TAUROS]: EggTier.RARE, + [Species.PALDEA_WOOPER]: EggTier.COMMON, + [Species.BLOODMOON_URSALUNA]: EggTier.EPIC, +}; diff --git a/src/data/egg.ts b/src/data/egg.ts index 5fffe4fcece..c475fc729e6 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -11,6 +11,7 @@ import { EggTier } from "#enums/egg-type"; import { Species } from "#enums/species"; import { EggSourceType } from "#enums/egg-source-types"; import { MANAPHY_EGG_MANAPHY_RATE, SAME_SPECIES_EGG_HA_RATE, GACHA_EGG_HA_RATE, GACHA_DEFAULT_RARE_EGGMOVE_RATE, SAME_SPECIES_EGG_RARE_EGGMOVE_RATE, GACHA_MOVE_UP_RARE_EGGMOVE_RATE, GACHA_DEFAULT_SHINY_RATE, GACHA_SHINY_UP_SHINY_RATE, SAME_SPECIES_EGG_SHINY_RATE, EGG_PITY_LEGENDARY_THRESHOLD, EGG_PITY_EPIC_THRESHOLD, EGG_PITY_RARE_THRESHOLD, SHINY_VARIANT_CHANCE, SHINY_EPIC_CHANCE, GACHA_DEFAULT_COMMON_EGG_THRESHOLD, GACHA_DEFAULT_RARE_EGG_THRESHOLD, GACHA_DEFAULT_EPIC_EGG_THRESHOLD, GACHA_LEGENDARY_UP_THRESHOLD_OFFSET, HATCH_WAVES_MANAPHY_EGG, HATCH_WAVES_COMMON_EGG, HATCH_WAVES_RARE_EGG, HATCH_WAVES_EPIC_EGG, HATCH_WAVES_LEGENDARY_EGG } from "#app/data/balance/rates"; +import { speciesEggTiers } from "#app/data/balance/species-egg-tiers"; export const EGG_SEED = 1073741824; @@ -160,7 +161,7 @@ export class Egg { // Override egg tier and hatchwaves if species was given if (eggOptions?.species) { - this._tier = this.getEggTierFromSpeciesStarterValue(); + this._tier = this.getEggTier(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); } // If species has no variant, set variantTier to common. This needs to @@ -261,11 +262,11 @@ export class Egg { return "Manaphy"; } switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: return i18next.t("egg:greatTier"); - case EggTier.ULTRA: + case EggTier.EPIC: return i18next.t("egg:ultraTier"); - case EggTier.MASTER: + case EggTier.LEGENDARY: return i18next.t("egg:masterTier"); default: return i18next.t("egg:defaultTier"); @@ -336,9 +337,9 @@ export class Egg { switch (eggTier ?? this._tier) { case EggTier.COMMON: return HATCH_WAVES_COMMON_EGG; - case EggTier.GREAT: + case EggTier.RARE: return HATCH_WAVES_RARE_EGG; - case EggTier.ULTRA: + case EggTier.EPIC: return HATCH_WAVES_EPIC_EGG; } return HATCH_WAVES_LEGENDARY_EGG; @@ -347,7 +348,7 @@ export class Egg { private rollEggTier(): EggTier { const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; const tierValue = Utils.randInt(256); - return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.GREAT : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; + return tierValue >= GACHA_DEFAULT_COMMON_EGG_THRESHOLD + tierValueOffset ? EggTier.COMMON : tierValue >= GACHA_DEFAULT_RARE_EGG_THRESHOLD + tierValueOffset ? EggTier.RARE : tierValue >= GACHA_DEFAULT_EPIC_EGG_THRESHOLD + tierValueOffset ? EggTier.EPIC : EggTier.LEGENDARY; } private rollSpecies(scene: BattleScene): Species | null { @@ -367,7 +368,7 @@ export class Egg { */ const rand = (Utils.randSeedInt(MANAPHY_EGG_MANAPHY_RATE) !== 1); return rand ? Species.PHIONE : Species.MANAPHY; - } else if (this.tier === EggTier.MASTER + } else if (this.tier === EggTier.LEGENDARY && this._sourceType === EggSourceType.GACHA_LEGENDARY) { if (!Utils.randSeedInt(2)) { return getLegendaryGachaSpeciesForTimestamp(scene, this.timestamp); @@ -378,15 +379,15 @@ export class Egg { let maxStarterValue: integer; switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: minStarterValue = 4; maxStarterValue = 5; break; - case EggTier.ULTRA: + case EggTier.EPIC: minStarterValue = 6; maxStarterValue = 7; break; - case EggTier.MASTER: + case EggTier.LEGENDARY: minStarterValue = 8; maxStarterValue = 9; break; @@ -398,8 +399,8 @@ export class Egg { const ignoredSpecies = [ Species.PHIONE, Species.MANAPHY, Species.ETERNATUS ]; - let speciesPool = Object.keys(speciesStarterCosts) - .filter(s => speciesStarterCosts[s] >= minStarterValue && speciesStarterCosts[s] <= maxStarterValue) + let speciesPool = Object.keys(speciesEggTiers) + .filter(s => speciesEggTiers[s] === this.tier) .map(s => parseInt(s) as Species) .filter(s => !pokemonPrevolutions.hasOwnProperty(s) && getPokemonSpecies(s).isObtainable() && ignoredSpecies.indexOf(s) === -1); @@ -430,7 +431,9 @@ export class Egg { let totalWeight = 0; const speciesWeights : number[] = []; for (const speciesId of speciesPool) { - let weight = Math.floor((((maxStarterValue - speciesStarterCosts[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); + // Accounts for species that have starter costs outside of the normal range for their EggTier + const speciesCostClamped = Phaser.Math.Clamp(speciesStarterCosts[speciesId], minStarterValue, maxStarterValue); + let weight = Math.floor((((maxStarterValue - speciesCostClamped) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); const species = getPokemonSpecies(speciesId); if (species.isRegional()) { weight = Math.floor(weight / 2); @@ -498,16 +501,16 @@ export class Egg { private checkForPityTierOverrides(scene: BattleScene): void { const tierValueOffset = this._sourceType === EggSourceType.GACHA_LEGENDARY ? GACHA_LEGENDARY_UP_THRESHOLD_OFFSET : 0; - scene.gameData.eggPity[EggTier.GREAT] += 1; - scene.gameData.eggPity[EggTier.ULTRA] += 1; - scene.gameData.eggPity[EggTier.MASTER] += 1 + tierValueOffset; + scene.gameData.eggPity[EggTier.RARE] += 1; + scene.gameData.eggPity[EggTier.EPIC] += 1; + scene.gameData.eggPity[EggTier.LEGENDARY] += 1 + tierValueOffset; // These numbers are roughly the 80% mark. That is, 80% of the time you'll get an egg before this gets triggered. - if (scene.gameData.eggPity[EggTier.MASTER] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.MASTER; - } else if (scene.gameData.eggPity[EggTier.ULTRA] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.ULTRA; - } else if (scene.gameData.eggPity[EggTier.GREAT] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { - this._tier = EggTier.GREAT; + if (scene.gameData.eggPity[EggTier.LEGENDARY] >= EGG_PITY_LEGENDARY_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.LEGENDARY; + } else if (scene.gameData.eggPity[EggTier.EPIC] >= EGG_PITY_EPIC_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.EPIC; + } else if (scene.gameData.eggPity[EggTier.RARE] >= EGG_PITY_RARE_THRESHOLD && this._tier === EggTier.COMMON) { + this._tier = EggTier.RARE; } scene.gameData.eggPity[this._tier] = 0; } @@ -516,38 +519,24 @@ export class Egg { scene.gameData.gameStats.eggsPulled++; if (this.isManaphyEgg()) { scene.gameData.gameStats.manaphyEggsPulled++; - this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.ULTRA); + this._hatchWaves = this.getEggTierDefaultHatchWaves(EggTier.EPIC); return; } switch (this.tier) { - case EggTier.GREAT: + case EggTier.RARE: scene.gameData.gameStats.rareEggsPulled++; break; - case EggTier.ULTRA: + case EggTier.EPIC: scene.gameData.gameStats.epicEggsPulled++; break; - case EggTier.MASTER: + case EggTier.LEGENDARY: scene.gameData.gameStats.legendaryEggsPulled++; break; } } - private getEggTierFromSpeciesStarterValue(): EggTier { - const speciesStartValue = speciesStarterCosts[this.species]; - if (speciesStartValue >= 1 && speciesStartValue <= 3) { - return EggTier.COMMON; - } - if (speciesStartValue >= 4 && speciesStartValue <= 5) { - return EggTier.GREAT; - } - if (speciesStartValue >= 6 && speciesStartValue <= 7) { - return EggTier.ULTRA; - } - if (speciesStartValue >= 8) { - return EggTier.MASTER; - } - - return EggTier.COMMON; + private getEggTier(): EggTier { + return speciesEggTiers[this.species]; } //// @@ -556,8 +545,8 @@ export class Egg { } export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timestamp: number): Species { - const legendarySpecies = Object.entries(speciesStarterCosts) - .filter(s => s[1] >= 8 && s[1] <= 9) + const legendarySpecies = Object.entries(speciesEggTiers) + .filter(s => s[1] === EggTier.LEGENDARY) .map(s => parseInt(s[0])) .filter(s => getPokemonSpecies(s).isObtainable()); @@ -579,17 +568,9 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta /** * Check for a given species EggTier Value - * @param species - Species for wich we will check the egg tier it belongs to + * @param pokemonSpecies - Species for wich we will check the egg tier it belongs to * @returns The egg tier of a given pokemon species */ export function getEggTierForSpecies(pokemonSpecies :PokemonSpecies): EggTier { - const speciesBaseValue = speciesStarterCosts[pokemonSpecies.getRootSpeciesId()]; - if (speciesBaseValue <= 3) { - return EggTier.COMMON; - } else if (speciesBaseValue <= 5) { - return EggTier.GREAT; - } else if (speciesBaseValue <= 7) { - return EggTier.ULTRA; - } - return EggTier.MASTER; + return speciesEggTiers[pokemonSpecies.getRootSpeciesId()]; } diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index f3b886ac0ac..4f3420f5194 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -150,7 +150,7 @@ export const ATrainersTestEncounter: MysteryEncounter = pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: encounter.misc.trainerEggDescription, - tier: EggTier.ULTRA + tier: EggTier.EPIC }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.epic`)); setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [ modifierTypes.SACRED_ASH ], guaranteedModifierTiers: [ ModifierTier.ROGUE, ModifierTier.ULTRA ], fillRemaining: true }, [ eggOptions ]); @@ -172,7 +172,7 @@ export const ATrainersTestEncounter: MysteryEncounter = pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: encounter.misc.trainerEggDescription, - tier: EggTier.GREAT + tier: EggTier.RARE }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}:eggTypes.rare`)); setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [ eggOptions ]); diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 4515736b30a..0ac82243862 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -494,7 +494,7 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number) pulled: false, sourceType: EggSourceType.EVENT, eggDescriptor: eggDescription, - tier: EggTier.GREAT + tier: EggTier.RARE }); } } diff --git a/src/enums/egg-type.ts b/src/enums/egg-type.ts index d8d0facb020..901e60b3c76 100644 --- a/src/enums/egg-type.ts +++ b/src/enums/egg-type.ts @@ -1,6 +1,6 @@ export enum EggTier { COMMON, - GREAT, - ULTRA, - MASTER + RARE, + EPIC, + LEGENDARY } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 4d85d5b8e1e..d8fcc281d1b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -983,7 +983,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, s, statHolder); } - statHolder.value = Utils.clampInt(statHolder.value, 1, Number.MAX_SAFE_INTEGER); + statHolder.value = Phaser.Math.Clamp(statHolder.value, 1, Number.MAX_SAFE_INTEGER); this.setStat(s, statHolder.value); } diff --git a/src/test/eggs/egg.test.ts b/src/test/eggs/egg.test.ts index cf53cca5af8..6f57af63e6b 100644 --- a/src/test/eggs/egg.test.ts +++ b/src/test/eggs/egg.test.ts @@ -55,7 +55,7 @@ describe("Egg Generation Tests", () => { let gachaSpeciesCount = 0; for (let i = 0; i < EGG_HATCH_COUNT; i++) { - const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.MASTER }).generatePlayerPokemon(scene).species.speciesId; + const result = new Egg({ scene, timestamp, sourceType: EggSourceType.GACHA_LEGENDARY, tier: EggTier.LEGENDARY }).generatePlayerPokemon(scene).species.speciesId; if (result === expectedSpecies) { gachaSpeciesCount++; } @@ -82,7 +82,7 @@ describe("Egg Generation Tests", () => { }); it("should return an rare tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.GREAT; + const expectedTier = EggTier.RARE; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -90,7 +90,7 @@ describe("Egg Generation Tests", () => { }); it("should return an epic tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.ULTRA; + const expectedTier = EggTier.EPIC; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -98,7 +98,7 @@ describe("Egg Generation Tests", () => { }); it("should return an legendary tier egg", () => { const scene = game.scene; - const expectedTier = EggTier.MASTER; + const expectedTier = EggTier.LEGENDARY; const result = new Egg({ scene, tier: expectedTier }).tier; @@ -200,7 +200,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const expectedEggTier = EggTier.COMMON; - const result = new Egg({ scene, tier: EggTier.MASTER, species: Species.BULBASAUR }).tier; + const result = new Egg({ scene, tier: EggTier.LEGENDARY, species: Species.BULBASAUR }).tier; expect(result).toBe(expectedEggTier); }); @@ -208,7 +208,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const expectedHatchWaves = 10; - const result = new Egg({ scene, tier: EggTier.MASTER, species: Species.BULBASAUR }).hatchWaves; + const result = new Egg({ scene, tier: EggTier.LEGENDARY, species: Species.BULBASAUR }).hatchWaves; expect(result).toBe(expectedHatchWaves); }); @@ -229,7 +229,7 @@ describe("Egg Generation Tests", () => { const result = new EggData(legacyEgg).toEgg(); - expect(result.tier).toBe(EggTier.GREAT); + expect(result.tier).toBe(EggTier.RARE); expect(result.id).toBe(legacyEgg.id); expect(result.timestamp).toBe(legacyEgg.timestamp); expect(result.hatchWaves).toBe(legacyEgg.hatchWaves); @@ -241,9 +241,9 @@ describe("Egg Generation Tests", () => { new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.COMMON }); - expect(scene.gameData.eggPity[EggTier.GREAT]).toBe(startPityValues[EggTier.GREAT] + 1); - expect(scene.gameData.eggPity[EggTier.ULTRA]).toBe(startPityValues[EggTier.ULTRA] + 1); - expect(scene.gameData.eggPity[EggTier.MASTER]).toBe(startPityValues[EggTier.MASTER] + 1); + expect(scene.gameData.eggPity[EggTier.RARE]).toBe(startPityValues[EggTier.RARE] + 1); + expect(scene.gameData.eggPity[EggTier.EPIC]).toBe(startPityValues[EggTier.EPIC] + 1); + expect(scene.gameData.eggPity[EggTier.LEGENDARY]).toBe(startPityValues[EggTier.LEGENDARY] + 1); }); it("should increase legendary egg pity by two", () => { const scene = game.scene; @@ -251,9 +251,9 @@ describe("Egg Generation Tests", () => { new Egg({ scene, sourceType: EggSourceType.GACHA_LEGENDARY, pulled: true, tier: EggTier.COMMON }); - expect(scene.gameData.eggPity[EggTier.GREAT]).toBe(startPityValues[EggTier.GREAT] + 1); - expect(scene.gameData.eggPity[EggTier.ULTRA]).toBe(startPityValues[EggTier.ULTRA] + 1); - expect(scene.gameData.eggPity[EggTier.MASTER]).toBe(startPityValues[EggTier.MASTER] + 2); + expect(scene.gameData.eggPity[EggTier.RARE]).toBe(startPityValues[EggTier.RARE] + 1); + expect(scene.gameData.eggPity[EggTier.EPIC]).toBe(startPityValues[EggTier.EPIC] + 1); + expect(scene.gameData.eggPity[EggTier.LEGENDARY]).toBe(startPityValues[EggTier.LEGENDARY] + 2); }); it("should not increase manaphy egg count if bulbasaurs are pulled", () => { const scene = game.scene; @@ -277,7 +277,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingRareEggsPulled = scene.gameData.gameStats.rareEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.GREAT }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.RARE }); expect(scene.gameData.gameStats.rareEggsPulled).toBe(startingRareEggsPulled + 1); }); @@ -285,7 +285,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingEpicEggsPulled = scene.gameData.gameStats.epicEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.ULTRA }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.EPIC }); expect(scene.gameData.gameStats.epicEggsPulled).toBe(startingEpicEggsPulled + 1); }); @@ -293,7 +293,7 @@ describe("Egg Generation Tests", () => { const scene = game.scene; const startingLegendaryEggsPulled = scene.gameData.gameStats.legendaryEggsPulled; - new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.MASTER }); + new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true, tier: EggTier.LEGENDARY }); expect(scene.gameData.gameStats.legendaryEggsPulled).toBe(startingLegendaryEggsPulled + 1); }); @@ -301,8 +301,8 @@ describe("Egg Generation Tests", () => { vi.spyOn(Utils, "randInt").mockReturnValue(1); const scene = game.scene; - const expectedTier1 = EggTier.MASTER; - const expectedTier2 = EggTier.ULTRA; + const expectedTier1 = EggTier.LEGENDARY; + const expectedTier2 = EggTier.EPIC; const result1 = new Egg({ scene, sourceType: EggSourceType.GACHA_LEGENDARY, pulled: true }).tier; const result2 = new Egg({ scene, sourceType: EggSourceType.GACHA_MOVE, pulled: true }).tier; diff --git a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts index b1aa378d82a..7d783958422 100644 --- a/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/a-trainers-test-encounter.test.ts @@ -128,7 +128,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + 1).toBe(eggsAfter.length); const eggTier = eggsAfter[eggsAfter.length - 1].tier; - expect(eggTier === EggTier.ULTRA || eggTier === EggTier.MASTER).toBeTruthy(); + expect(eggTier === EggTier.EPIC || eggTier === EggTier.LEGENDARY).toBeTruthy(); }); }); @@ -176,7 +176,7 @@ describe("A Trainer's Test - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + 1).toBe(eggsAfter.length); const eggTier = eggsAfter[eggsAfter.length - 1].tier; - expect(eggTier).toBe(EggTier.GREAT); + expect(eggTier).toBe(EggTier.RARE); }); it("should leave encounter without battle", async () => { diff --git a/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts index 7e445ac1fe2..bbb4f249feb 100644 --- a/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-expert-breeder-encounter.test.ts @@ -155,7 +155,7 @@ describe("The Expert PokĂ©mon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); @@ -213,7 +213,7 @@ describe("The Expert PokĂ©mon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); @@ -271,7 +271,7 @@ describe("The Expert PokĂ©mon Breeder - Mystery Encounter", () => { expect(eggsAfter).toBeDefined(); expect(eggsBeforeLength + commonEggs + rareEggs).toBe(eggsAfter.length); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); - expect(eggsAfter.filter(egg => egg.tier === EggTier.GREAT).length).toBe(rareEggs); + expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); game.phaseInterceptor.superEndPhase(); await game.phaseInterceptor.to(PostMysteryEncounterPhase); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 79b51ba6c44..1d97998f491 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -593,7 +593,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { }; const updatePokemonHp = () => { - let duration = !instant ? Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000) : 0; + let duration = !instant ? Phaser.Math.Clamp(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000) : 0; const speed = (this.scene as BattleScene).hpBarSpeed; if (speed) { duration = speed >= 3 ? 0 : duration / Math.pow(2, speed); diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 366f1604740..3aa009b1b31 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -471,9 +471,9 @@ export default class EggGachaUiHandler extends MessageUiHandler { getGuaranteedEggTierFromPullCount(pullCount: number): EggTier { switch (pullCount) { case 10: - return EggTier.GREAT; + return EggTier.RARE; case 25: - return EggTier.ULTRA; + return EggTier.EPIC; default: return EggTier.COMMON; } @@ -516,7 +516,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const eggText = addTextObject(this.scene, 0, 14, egg.getEggDescriptor(), TextStyle.PARTY, { align: "center" }); eggText.setOrigin(0.5, 0); - eggText.setTint(getEggTierTextTint(!egg.isManaphyEgg() ? egg.tier : EggTier.ULTRA)); + eggText.setTint(getEggTierTextTint(!egg.isManaphyEgg() ? egg.tier : EggTier.EPIC)); ret.add(eggText); this.eggGachaSummaryContainer.addAt(ret, 0); diff --git a/src/ui/text.ts b/src/ui/text.ts index e6e1978118b..22dd3f4cd6a 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -356,11 +356,11 @@ export function getEggTierTextTint(tier: EggTier): integer { switch (tier) { case EggTier.COMMON: return getModifierTierTextTint(ModifierTier.COMMON); - case EggTier.GREAT: + case EggTier.RARE: return getModifierTierTextTint(ModifierTier.GREAT); - case EggTier.ULTRA: + case EggTier.EPIC: return getModifierTierTextTint(ModifierTier.ULTRA); - case EggTier.MASTER: + case EggTier.LEGENDARY: return getModifierTierTextTint(ModifierTier.MASTER); } } diff --git a/src/utils.ts b/src/utils.ts index 9cc95b00826..c2ee7100909 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -38,10 +38,6 @@ export function shiftCharCodes(str: string, shiftCount: integer) { return newStr; } -export function clampInt(value: integer, min: integer, max: integer): integer { - return Math.min(Math.max(value, min), max); -} - export function randGauss(stdev: number, mean: number = 0): number { if (!stdev) { return 0; From 5d0b36132061bae767dafdd3f27c6c1be12264df Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:19:05 -0700 Subject: [PATCH 43/70] [P2] Syrup Bomb effect is removed when user leaves the field (#4606) * Syrup Bomb's effect expires when the move user leaves the field * Add test * Remove check for the affected pokemon being switched out --- src/data/battler-tags.ts | 29 +++++++++++++---------------- src/test/moves/syrup_bomb.test.ts | 26 ++++++++++++++++++++------ 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 24c82e54427..3cc109df264 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2640,16 +2640,16 @@ export class ImprisonTag extends MoveRestrictionBattlerTag { /** * Battler Tag that applies the effects of Syrup Bomb to the target Pokemon. * For three turns, starting from the turn of hit, at the end of each turn, the target Pokemon's speed will decrease by 1. - * The tag can also expire by taking the target Pokemon off the field. + * The tag can also expire by taking the target Pokemon off the field, or the Pokemon that originally used the move. */ export class SyrupBombTag extends BattlerTag { - constructor() { - super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB); + constructor(sourceId: number) { + super(BattlerTagType.SYRUP_BOMB, BattlerTagLapseType.TURN_END, 3, Moves.SYRUP_BOMB, sourceId); } /** * Adds the Syrup Bomb battler tag to the target Pokemon. - * @param {Pokemon} pokemon the target Pokemon + * @param pokemon - The target {@linkcode Pokemon} */ override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); @@ -2658,15 +2658,16 @@ export class SyrupBombTag extends BattlerTag { /** * Applies the single-stage speed down to the target Pokemon and decrements the tag's turn count - * @param {Pokemon} pokemon the target Pokemon - * @param {BattlerTagLapseType} _lapseType - * @returns `true` if the turnCount is still greater than 0 | `false` if the turnCount is 0 or the target Pokemon has been removed from the field + * @param pokemon - The target {@linkcode Pokemon} + * @param _lapseType - N/A + * @returns `true` if the `turnCount` is still greater than `0`; `false` if the `turnCount` is `0` or the target or source Pokemon has been removed from the field */ override lapse(pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - if (!pokemon.isActive(true)) { + if (this.sourceId && !pokemon.scene.getPokemonById(this.sourceId)?.isActive(true)) { return false; } - pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // Custom message in lieu of an animation in mainline + // Custom message in lieu of an animation in mainline + pokemon.scene.queueMessage(i18next.t("battlerTags:syrupBombLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.scene.unshiftPhase(new StatStageChangePhase( pokemon.scene, pokemon.getBattlerIndex(), true, [ Stat.SPD ], -1, true, false, true @@ -2677,12 +2678,8 @@ export class SyrupBombTag extends BattlerTag { /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. - * - * @param {BattlerTagType} tagType the type of the {@linkcode BattlerTagType}. - * @param turnCount the turn count. - * @param {Moves} sourceMove the source {@linkcode Moves}. - * @param sourceId the source ID. - * @returns {BattlerTag} the corresponding {@linkcode BattlerTag} object. + * @param sourceId - The ID of the pokemon adding the tag + * @returns The corresponding {@linkcode BattlerTag} object. */ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, sourceMove: Moves, sourceId: number): BattlerTag { switch (tagType) { @@ -2851,7 +2848,7 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.IMPRISON: return new ImprisonTag(sourceId); case BattlerTagType.SYRUP_BOMB: - return new SyrupBombTag(); + return new SyrupBombTag(sourceId); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/test/moves/syrup_bomb.test.ts b/src/test/moves/syrup_bomb.test.ts index 7f914e45cc6..ea2f8b6bab3 100644 --- a/src/test/moves/syrup_bomb.test.ts +++ b/src/test/moves/syrup_bomb.test.ts @@ -1,4 +1,3 @@ -import { allMoves } from "#app/data/move"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { Abilities } from "#enums/abilities"; @@ -7,7 +6,7 @@ import { Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { BattlerIndex } from "#app/battle"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; describe("Moves - SYRUP BOMB", () => { let phaserGame: Phaser.Game; @@ -26,20 +25,21 @@ describe("Moves - SYRUP BOMB", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .starterSpecies(Species.MAGIKARP) + .battleType("single") .enemySpecies(Species.SNORLAX) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) .startingLevel(30) .enemyLevel(100) .moveset([ Moves.SYRUP_BOMB, Moves.SPLASH ]) .enemyMoveset(Moves.SPLASH); - vi.spyOn(allMoves[Moves.SYRUP_BOMB], "accuracy", "get").mockReturnValue(100); }); //Bulbapedia Reference: https://bulbapedia.bulbagarden.net/wiki/syrup_bomb_(move) it("decreases the target Pokemon's speed stat once per turn for 3 turns", async () => { - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); const targetPokemon = game.scene.getEnemyPokemon()!; expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); @@ -66,7 +66,7 @@ describe("Moves - SYRUP BOMB", () => { it("does not affect Pokemon with the ability Bulletproof", async () => { game.override.enemyAbility(Abilities.BULLETPROOF); - await game.startBattle([ Species.MAGIKARP ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); const targetPokemon = game.scene.getEnemyPokemon()!; @@ -79,4 +79,18 @@ describe("Moves - SYRUP BOMB", () => { expect(targetPokemon.getStatStage(Stat.SPD)).toBe(0); } ); + + it("stops lowering the target's speed if the user leaves the field", async () => { + await game.classicMode.startBattle([ Species.FEEBAS, Species.MILOTIC ]); + + game.move.select(Moves.SYRUP_BOMB); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.move.forceHit(); + await game.toNextTurn(); + + game.doSwitchPokemon(1); + await game.toNextTurn(); + + expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPD)).toBe(-1); + }); }); From 3f63c147a38ef9afb05c54bfe0983ed572d6f35b Mon Sep 17 00:00:00 2001 From: "Amani H." <109637146+xsn34kzx@users.noreply.github.com> Date: Thu, 10 Oct 2024 15:44:51 -0400 Subject: [PATCH 44/70] [P3] Fix "Stat Won't Go Any Lower/Higher" Not Appearing (#4635) --- src/enums/stat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enums/stat.ts b/src/enums/stat.ts index a12d53e8559..6b3f7dc6d79 100644 --- a/src/enums/stat.ts +++ b/src/enums/stat.ts @@ -50,7 +50,7 @@ export function getStatStageChangeDescriptionKey(stages: number, isIncrease: boo return isIncrease ? "battle:statRose" : "battle:statFell"; } else if (stages === 2) { return isIncrease ? "battle:statSharplyRose" : "battle:statHarshlyFell"; - } else if (stages <= 6) { + } else if (stages > 2 && stages <= 6) { return isIncrease ? "battle:statRoseDrastically" : "battle:statSeverelyFell"; } return isIncrease ? "battle:statWontGoAnyHigher" : "battle:statWontGoAnyLower"; From 407cd65dcbe7ba343b2f01e17ed67750c888e218 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Fri, 11 Oct 2024 00:20:28 -0700 Subject: [PATCH 45/70] [Misc] Enemy item override will now apply to all enemies (#4620) * Enemy item override will now apply to all enemies * Update tsdocs --- src/battle-scene.ts | 9 +++++---- src/modifier/modifier.ts | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index a586b565e13..40e3971b7fc 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2676,7 +2676,7 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PersistentModifier type + * Removes all modifiers from enemy pokemon of {@linkcode PersistentModifier} type */ clearEnemyModifiers(): void { const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PersistentModifier); @@ -2687,10 +2687,11 @@ export default class BattleScene extends SceneBase { } /** - * Removes all modifiers from enemy of PokemonHeldItemModifier type + * Removes all modifiers from enemy pokemon of {@linkcode PokemonHeldItemModifier} type + * @param pokemon - If specified, only removes held items from that {@linkcode Pokemon} */ - clearEnemyHeldItemModifiers(): void { - const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier); + clearEnemyHeldItemModifiers(pokemon?: Pokemon): void { + const modifiersToRemove = this.enemyModifiers.filter(m => m instanceof PokemonHeldItemModifier && (!pokemon || m.getPokemon(this) === pokemon)); for (const m of modifiersToRemove) { this.enemyModifiers.splice(this.enemyModifiers.indexOf(m), 1); } diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 6c9b5db1bca..dd8c82357a7 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -3635,7 +3635,7 @@ export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, isPlayer } if (!isPlayer) { - scene.clearEnemyHeldItemModifiers(); + scene.clearEnemyHeldItemModifiers(pokemon); } heldItemsOverride.forEach(item => { From 80a784ac8d83c90af4ef16490346f24380da5f86 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:24:53 +0200 Subject: [PATCH 46/70] [P3 Beta][UI] Fix loading behavior introduced with save preview (#4633) * [ui] partially revert loading behavior introduced with save preview * [beta][ui] fix scrolling issue in Load Game menu --- src/ui/save-slot-select-ui-handler.ts | 65 ++++++++++++++++----------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index bd1a7dd9ac4..2e664db8d43 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -12,7 +12,8 @@ import { Mode } from "./ui"; import { addWindow } from "./ui-theme"; import { RunDisplayMode } from "#app/ui/run-info-ui-handler"; -const sessionSlotCount = 5; +const SESSION_SLOTS_COUNT = 5; +const SLOTS_ON_SCREEN = 3; export enum SaveSlotUiMode { LOAD, @@ -84,12 +85,10 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { this.saveSlotSelectCallback = args[1] as SaveSlotSelectCallback; this.saveSlotSelectContainer.setVisible(true); - this.populateSessionSlots() - .then(() => { - this.setScrollCursor(0); - this.setCursor(0); - }); + this.populateSessionSlots(); + this.setScrollCursor(0); + this.setCursor(0); return true; } @@ -161,9 +160,9 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } break; case Button.DOWN: - if (this.cursor < 2) { - success = this.setCursor(this.cursor + 1, this.cursor); - } else if (this.scrollCursor < sessionSlotCount - 3) { + if (this.cursor < (SLOTS_ON_SCREEN - 1)) { + success = this.setCursor(this.cursor + 1, cursorPosition); + } else if (this.scrollCursor < SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN) { success = this.setScrollCursor(this.scrollCursor + 1, cursorPosition); } break; @@ -184,13 +183,19 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { return success || error; } - async populateSessionSlots() { - for (let s = 0; s < sessionSlotCount; s++) { + populateSessionSlots() { + for (let s = 0; s < SESSION_SLOTS_COUNT; s++) { const sessionSlot = new SessionSlot(this.scene, s); - await sessionSlot.load(); this.scene.add.existing(sessionSlot); this.sessionSlotsContainer.add(sessionSlot); this.sessionSlots.push(sessionSlot); + sessionSlot.load().then((success) => { + // If the cursor was moved to this slot while the session was loading + // call setCursor again to shift the slot position and show the arrow for save preview + if (success && (this.cursor + this.scrollCursor) === s) { + this.setCursor(s); + } + }); } } @@ -209,12 +214,12 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } /** - * setCursor takes user navigation as an input and positions the cursor accordingly - * @param cursor the index provided to the cursor - * @param prevCursor the previous index occupied by the cursor - optional + * Move the cursor to a new position and update the view accordingly + * @param cursor the new cursor position, between `0` and `SLOTS_ON_SCREEN - 1` + * @param prevSlotIndex index of the previous session occupied by the cursor, between `0` and `SESSION_SLOTS_COUNT - 1` - optional * @returns `true` if the cursor position has changed | `false` if it has not */ - override setCursor(cursor: integer, prevCursor?: integer): boolean { + override setCursor(cursor: integer, prevSlotIndex?: integer): boolean { const changed = super.setCursor(cursor); if (!this.cursorObj) { @@ -241,21 +246,20 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } this.setArrowVisibility(hasData); } - if (!Utils.isNullOrUndefined(prevCursor)) { - this.revertSessionSlot(prevCursor); + if (!Utils.isNullOrUndefined(prevSlotIndex)) { + this.revertSessionSlot(prevSlotIndex); } return changed; } /** - * Helper function that resets the session slot position to its default central position - * @param prevCursor the previous location of the cursor + * Helper function that resets the given session slot to its default central position */ - revertSessionSlot(prevCursor: integer): void { - const sessionSlot = this.sessionSlots[prevCursor]; + revertSessionSlot(slotIndex: integer): void { + const sessionSlot = this.sessionSlots[slotIndex]; if (sessionSlot) { - sessionSlot.setPosition(0, prevCursor * 56); + sessionSlot.setPosition(0, slotIndex * 56); } } @@ -270,12 +274,18 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } } - setScrollCursor(scrollCursor: integer, priorCursor?: integer): boolean { + /** + * Move the scrolling cursor to a new position and update the view accordingly + * @param scrollCursor the new cursor position, between `0` and `SESSION_SLOTS_COUNT - SLOTS_ON_SCREEN` + * @param prevSlotIndex index of the previous slot occupied by the cursor, between `0` and `SESSION_SLOTS_COUNT-1` - optional + * @returns `true` if the cursor position has changed | `false` if it has not + */ + setScrollCursor(scrollCursor: integer, prevSlotIndex?: integer): boolean { const changed = scrollCursor !== this.scrollCursor; if (changed) { this.scrollCursor = scrollCursor; - this.setCursor(this.cursor, priorCursor); + this.setCursor(this.cursor, prevSlotIndex); this.scene.tweens.add({ targets: this.sessionSlotsContainer, y: this.sessionSlotsContainerInitialY - 56 * scrollCursor, @@ -290,6 +300,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { clear() { super.clear(); this.saveSlotSelectContainer.setVisible(false); + this.setScrollCursor(0); this.eraseCursor(); this.saveSlotSelectCallback = null; this.clearSessionSlots(); @@ -391,6 +402,10 @@ class SessionSlot extends Phaser.GameObjects.Container { load(): Promise { return new Promise(resolve => { this.scene.gameData.getSession(this.slotId).then(async sessionData => { + // Ignore the results if the view was exited + if (!this.active) { + return; + } if (!sessionData) { this.hasData = false; this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty")); From 4f456339f45038cd8df5b9c788251aa6c66bdde1 Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Fri, 11 Oct 2024 16:26:47 +0200 Subject: [PATCH 47/70] [UI] Remove score display in voucher menu (#4616) --- src/ui/achvs-ui-handler.ts | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index f90732d1fae..4e0e2feea81 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -40,7 +40,9 @@ export default class AchvsUiHandler extends MessageUiHandler { private iconsBg: Phaser.GameObjects.NineSlice; private icons: Phaser.GameObjects.Sprite[]; + private titleBg: Phaser.GameObjects.NineSlice; private titleText: Phaser.GameObjects.Text; + private scoreContainer: Phaser.GameObjects.Container; private scoreText: Phaser.GameObjects.Text; private unlockText: Phaser.GameObjects.Text; @@ -114,29 +116,31 @@ export default class AchvsUiHandler extends MessageUiHandler { const titleBg = addWindow(this.scene, 0, this.headerBg.height + this.iconsBg.height, 174, 24); titleBg.setOrigin(0, 0); + this.titleBg = titleBg; this.titleText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); const textSize = languageSettings[i18next.language]?.TextSize ?? this.titleText.style.fontSize; this.titleText.setFontSize(textSize); - this.titleText.setOrigin(0, 0); const titleBgCenterX = titleBg.x + titleBg.width / 2; const titleBgCenterY = titleBg.y + titleBg.height / 2; this.titleText.setOrigin(0.5, 0.5); this.titleText.setPosition(titleBgCenterX, titleBgCenterY); - const scoreBg = addWindow(this.scene, titleBg.x + titleBg.width, titleBg.y, 46, 24); + this.scoreContainer = this.scene.add.container(titleBg.x + titleBg.width, titleBg.y); + const scoreBg = addWindow(this.scene, 0, 0, 46, 24); scoreBg.setOrigin(0, 0); + this.scoreContainer.add(scoreBg); - this.scoreText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); - this.scoreText.setOrigin(0, 0); - this.scoreText.setPositionRelative(scoreBg, 8, 4); + this.scoreText = addTextObject(this.scene, scoreBg.width / 2, scoreBg.height / 2, "", TextStyle.WINDOW); + this.scoreText.setOrigin(0.5, 0.5); + this.scoreContainer.add(this.scoreText); - const unlockBg = addWindow(this.scene, scoreBg.x + scoreBg.width, scoreBg.y, 98, 24); + const unlockBg = addWindow(this.scene, this.scoreContainer.x + scoreBg.width, titleBg.y, 98, 24); unlockBg.setOrigin(0, 0); this.unlockText = addTextObject(this.scene, 0, 0, "", TextStyle.WINDOW); - this.unlockText.setOrigin(0, 0); - this.unlockText.setPositionRelative(unlockBg, 8, 4); + this.unlockText.setOrigin(0.5, 0.5); + this.unlockText.setPositionRelative(unlockBg, unlockBg.width / 2, unlockBg.height / 2); const descriptionBg = addWindow(this.scene, 0, titleBg.y + titleBg.height, (this.scene.game.canvas.width / 6) - 2, 42); descriptionBg.setOrigin(0, 0); @@ -157,8 +161,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.mainContainer.add(this.iconsContainer); this.mainContainer.add(titleBg); this.mainContainer.add(this.titleText); - this.mainContainer.add(scoreBg); - this.mainContainer.add(this.scoreText); + this.mainContainer.add(this.scoreContainer); this.mainContainer.add(unlockBg); this.mainContainer.add(this.unlockText); this.mainContainer.add(descriptionBg); @@ -167,8 +170,6 @@ export default class AchvsUiHandler extends MessageUiHandler { ui.add(this.mainContainer); this.currentPage = Page.ACHIEVEMENTS; - this.setCursor(0); - this.setScrollCursor(0); this.mainContainer.setVisible(false); } @@ -316,9 +317,19 @@ export default class AchvsUiHandler extends MessageUiHandler { if (update || pageChange) { switch (this.currentPage) { case Page.ACHIEVEMENTS: + if (pageChange) { + this.titleBg.width = 174; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(true); + } this.showAchv(achvs[Object.keys(achvs)[cursor + this.scrollCursor * this.COLS]]); break; case Page.VOUCHERS: + if (pageChange) { + this.titleBg.width = 220; + this.titleText.x = this.titleBg.width / 2; + this.scoreContainer.setVisible(false); + } this.showVoucher(vouchers[Object.keys(vouchers)[cursor + this.scrollCursor * this.COLS]]); break; } @@ -442,6 +453,7 @@ export default class AchvsUiHandler extends MessageUiHandler { this.currentPage = Page.ACHIEVEMENTS; this.mainContainer.setVisible(false); this.setScrollCursor(0); + this.setCursor(0, true); this.eraseCursor(); } From 70b9a43c8b72a555ae4e5379f07d0c4a480ca4e2 Mon Sep 17 00:00:00 2001 From: Mason S <132116525+ElizaAlex@users.noreply.github.com> Date: Fri, 11 Oct 2024 10:41:54 -0400 Subject: [PATCH 48/70] [P2] Fix first-turn status damage and arena hazards (#3528) * [Bug] Toxic Spikes implementation issues fixed Adjusted MoveEffectPhase.start() so that ENEMY_SIDE targeted moves no longer occur twice per use in double battles. Updated Toxic Orb test to no longer expect a tick of damage turn 1. Fixed Toxic/Poison dealing damage immediately when applied. Fixed Hazards not persisting through save Added unit tests Fixed flyout not displaying correct number of Spikes/Toxic Spikes after a refresh * Update Toxic Orb test * Updates Toxic Spikes tests * Apply suggestions from code review * Fix merge issues Replace `integer` with `number` in `arena-tag.ts` * Remove partial Magic Bounce implementation * Remove stray newline * Remove extra change in safeguard test --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/arena-tag.ts | 122 ++++++++++++-------- src/phases/check-status-effect-phase.ts | 23 ++++ src/phases/obtain-status-effect-phase.ts | 4 - src/phases/turn-start-phase.ts | 9 +- src/system/arena-data.ts | 8 +- src/system/game-data.ts | 17 ++- src/test/items/toxic_orb.test.ts | 54 ++++----- src/test/moves/toxic_spikes.test.ts | 136 +++++++++++++++++++++++ 8 files changed, 282 insertions(+), 91 deletions(-) create mode 100644 src/phases/check-status-effect-phase.ts create mode 100644 src/test/moves/toxic_spikes.test.ts diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 6407e139a71..45d64249296 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -28,20 +28,13 @@ export enum ArenaTagSide { } export abstract class ArenaTag { - public tagType: ArenaTagType; - public turnCount: integer; - public sourceMove?: Moves; - public sourceId?: integer; - public side: ArenaTagSide; - - - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { - this.tagType = tagType; - this.turnCount = turnCount; - this.sourceMove = sourceMove; - this.sourceId = sourceId; - this.side = side; - } + constructor( + public tagType: ArenaTagType, + public turnCount: number, + public sourceMove?: Moves, + public sourceId?: number, + public side: ArenaTagSide = ArenaTagSide.BOTH + ) {} apply(arena: Arena, args: any[]): boolean { return true; @@ -66,6 +59,18 @@ export abstract class ArenaTag { ? allMoves[this.sourceMove].name : null; } + + /** + * When given a arena tag or json representing one, load the data for it. + * This is meant to be inherited from by any arena tag with custom attributes + * @param {ArenaTag | any} source An arena tag + */ + loadTag(source : ArenaTag | any) : void { + this.turnCount = source.turnCount; + this.sourceMove = source.sourceMove; + this.sourceId = source.sourceId; + this.side = source.side; + } } /** @@ -73,7 +78,7 @@ export abstract class ArenaTag { * Prevents PokĂ©mon on the opposing side from lowering the stats of the PokĂ©mon in the Mist. */ export class MistTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.MIST, turnCount, Moves.MIST, sourceId, side); } @@ -117,7 +122,7 @@ export class WeakenMoveScreenTag extends ArenaTag { * @param side - The side (player or enemy) the tag affects. * @param weakenedCategories - The categories of moves that are weakened by this tag. */ - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, weakenedCategories: MoveCategory[]) { + constructor(tagType: ArenaTagType, turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide, weakenedCategories: MoveCategory[]) { super(tagType, turnCount, sourceMove, sourceId, side); this.weakenedCategories = weakenedCategories; @@ -148,7 +153,7 @@ export class WeakenMoveScreenTag extends ArenaTag { * Used by {@linkcode Moves.REFLECT} */ class ReflectTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.REFLECT, turnCount, Moves.REFLECT, sourceId, side, [ MoveCategory.PHYSICAL ]); } @@ -164,7 +169,7 @@ class ReflectTag extends WeakenMoveScreenTag { * Used by {@linkcode Moves.LIGHT_SCREEN} */ class LightScreenTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.LIGHT_SCREEN, turnCount, Moves.LIGHT_SCREEN, sourceId, side, [ MoveCategory.SPECIAL ]); } @@ -180,7 +185,7 @@ class LightScreenTag extends WeakenMoveScreenTag { * Used by {@linkcode Moves.AURORA_VEIL} */ class AuroraVeilTag extends WeakenMoveScreenTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.AURORA_VEIL, turnCount, Moves.AURORA_VEIL, sourceId, side, [ MoveCategory.SPECIAL, MoveCategory.PHYSICAL ]); } @@ -203,7 +208,7 @@ export class ConditionalProtectTag extends ArenaTag { /** Does this apply to all moves, including those that ignore other forms of protection? */ protected ignoresBypass: boolean; - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) { + constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, condition: ProtectConditionFunc, ignoresBypass: boolean = false) { super(tagType, 1, sourceMove, sourceId, side); this.protectConditionFunc = condition; @@ -265,7 +270,7 @@ export class ConditionalProtectTag extends ArenaTag { */ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const move = allMoves[moveId]; - const priority = new Utils.IntegerHolder(move.priority); + const priority = new Utils.NumberHolder(move.priority); const effectPhase = arena.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase) { @@ -281,7 +286,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { * Condition: The incoming move has increased priority. */ class QuickGuardTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.QUICK_GUARD, Moves.QUICK_GUARD, sourceId, side, QuickGuardConditionFunc); } } @@ -312,7 +317,7 @@ const WideGuardConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean = * can be an ally or enemy. */ class WideGuardTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.WIDE_GUARD, Moves.WIDE_GUARD, sourceId, side, WideGuardConditionFunc); } } @@ -334,7 +339,7 @@ const MatBlockConditionFunc: ProtectConditionFunc = (arena, moveId) : boolean => * Condition: The incoming move is a Physical or Special attack move. */ class MatBlockTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.MAT_BLOCK, Moves.MAT_BLOCK, sourceId, side, MatBlockConditionFunc); } @@ -372,7 +377,7 @@ const CraftyShieldConditionFunc: ProtectConditionFunc = (arena, moveId) => { * not target all Pokemon or sides of the field. */ class CraftyShieldTag extends ConditionalProtectTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.CRAFTY_SHIELD, Moves.CRAFTY_SHIELD, sourceId, side, CraftyShieldConditionFunc, true); } } @@ -384,12 +389,12 @@ class CraftyShieldTag extends ConditionalProtectTag { export class NoCritTag extends ArenaTag { /** * Constructor method for the NoCritTag class - * @param turnCount `integer` the number of turns this effect lasts + * @param turnCount `number` the number of turns this effect lasts * @param sourceMove {@linkcode Moves} the move that created this effect - * @param sourceId `integer` the ID of the {@linkcode Pokemon} that created this effect + * @param sourceId `number` the ID of the {@linkcode Pokemon} that created this effect * @param side {@linkcode ArenaTagSide} the side to which this effect belongs */ - constructor(turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceMove: Moves, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.NO_CRIT, turnCount, sourceMove, sourceId, side); } @@ -419,7 +424,7 @@ class WishTag extends ArenaTag { private triggerMessage: string; private healHp: number; - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.WISH, turnCount, Moves.WISH, sourceId, side); } @@ -460,7 +465,7 @@ export class WeakenMoveTypeTag extends ArenaTag { * @param sourceMove - The move that created the tag. * @param sourceId - The ID of the source of the tag. */ - constructor(tagType: ArenaTagType, turnCount: integer, type: Type, sourceMove: Moves, sourceId: integer) { + constructor(tagType: ArenaTagType, turnCount: number, type: Type, sourceMove: Moves, sourceId: number) { super(tagType, turnCount, sourceMove, sourceId); this.weakenedType = type; @@ -481,7 +486,7 @@ export class WeakenMoveTypeTag extends ArenaTag { * Weakens Electric type moves for a set amount of turns, usually 5. */ class MudSportTag extends WeakenMoveTypeTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.MUD_SPORT, turnCount, Type.ELECTRIC, Moves.MUD_SPORT, sourceId); } @@ -499,7 +504,7 @@ class MudSportTag extends WeakenMoveTypeTag { * Weakens Fire type moves for a set amount of turns, usually 5. */ class WaterSportTag extends WeakenMoveTypeTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.WATER_SPORT, turnCount, Type.FIRE, Moves.WATER_SPORT, sourceId); } @@ -550,8 +555,8 @@ export class IonDelugeTag extends ArenaTag { * Abstract class to implement arena traps. */ export class ArenaTrapTag extends ArenaTag { - public layers: integer; - public maxLayers: integer; + public layers: number; + public maxLayers: number; /** * Creates a new instance of the ArenaTrapTag class. @@ -562,7 +567,7 @@ export class ArenaTrapTag extends ArenaTag { * @param side - The side (player or enemy) the tag affects. * @param maxLayers - The maximum amount of layers this tag can have. */ - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, side: ArenaTagSide, maxLayers: integer) { + constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: number, side: ArenaTagSide, maxLayers: number) { super(tagType, 0, sourceMove, sourceId, side); this.layers = 1; @@ -593,6 +598,12 @@ export class ArenaTrapTag extends ArenaTag { getMatchupScoreMultiplier(pokemon: Pokemon): number { return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2); } + + loadTag(source: any): void { + super.loadTag(source); + this.layers = source.layers; + this.maxLayers = source.maxLayers; + } } /** @@ -601,7 +612,7 @@ export class ArenaTrapTag extends ArenaTag { * in damage for 1, 2, or 3 layers of Spikes respectively if they are summoned into this trap. */ class SpikesTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.SPIKES, Moves.SPIKES, sourceId, side, 3); } @@ -645,7 +656,7 @@ class SpikesTag extends ArenaTrapTag { class ToxicSpikesTag extends ArenaTrapTag { private neutralized: boolean; - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.TOXIC_SPIKES, Moves.TOXIC_SPIKES, sourceId, side, 2); this.neutralized = false; } @@ -703,7 +714,7 @@ class ToxicSpikesTag extends ArenaTrapTag { class DelayedAttackTag extends ArenaTag { public targetIndex: BattlerIndex; - constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: integer, targetIndex: BattlerIndex) { + constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: number, targetIndex: BattlerIndex) { super(tagType, 3, sourceMove, sourceId); this.targetIndex = targetIndex; @@ -728,7 +739,7 @@ class DelayedAttackTag extends ArenaTag { * who is summoned into the trap, based on the Rock type's type effectiveness. */ class StealthRockTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.STEALTH_ROCK, Moves.STEALTH_ROCK, sourceId, side, 1); } @@ -804,7 +815,7 @@ class StealthRockTag extends ArenaTrapTag { * to any PokĂ©mon who is summoned into this trap. */ class StickyWebTag extends ArenaTrapTag { - constructor(sourceId: integer, side: ArenaTagSide) { + constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.STICKY_WEB, Moves.STICKY_WEB, sourceId, side, 1); } @@ -838,7 +849,7 @@ class StickyWebTag extends ArenaTrapTag { * also reversing the turn order for all PokĂ©mon on the field as well. */ export class TrickRoomTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer) { + constructor(turnCount: number, sourceId: number) { super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId); } @@ -866,7 +877,7 @@ export class TrickRoomTag extends ArenaTag { * {@linkcode Abilities.LEVITATE} for the duration of the arena tag, usually 5 turns. */ export class GravityTag extends ArenaTag { - constructor(turnCount: integer) { + constructor(turnCount: number) { super(ArenaTagType.GRAVITY, turnCount, Moves.GRAVITY); } @@ -890,7 +901,7 @@ export class GravityTag extends ArenaTag { * Applies this arena tag for 4 turns (including the turn the move was used). */ class TailwindTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.TAILWIND, turnCount, Moves.TAILWIND, sourceId, side); } @@ -928,7 +939,7 @@ class TailwindTag extends ArenaTag { * Doubles the prize money from trainers and money moves like {@linkcode Moves.PAY_DAY} and {@linkcode Moves.MAKE_IT_RAIN}. */ class HappyHourTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.HAPPY_HOUR, turnCount, Moves.HAPPY_HOUR, sourceId, side); } @@ -942,7 +953,7 @@ class HappyHourTag extends ArenaTag { } class SafeguardTag extends ArenaTag { - constructor(turnCount: integer, sourceId: integer, side: ArenaTagSide) { + constructor(turnCount: number, sourceId: number, side: ArenaTagSide) { super(ArenaTagType.SAFEGUARD, turnCount, Moves.SAFEGUARD, sourceId, side); } @@ -955,6 +966,11 @@ class SafeguardTag extends ArenaTag { } } +class NoneTag extends ArenaTag { + constructor() { + super(ArenaTagType.NONE, 0); + } +} /** * This arena tag facilitates the application of the move Imprison * Imprison remains in effect as long as the source Pokemon is active and present on the field. @@ -1102,7 +1118,8 @@ class GrassWaterPledgeTag extends ArenaTag { } } -export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { +// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter +export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: return new MistTag(turnCount, sourceId, side); @@ -1163,3 +1180,16 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return null; } } + +/** + * When given a battler tag or json representing one, creates an actual ArenaTag object with the same data. + * @param {ArenaTag | any} source An arena tag + * @return {ArenaTag} The valid arena tag + */ +export function loadArenaTag(source: ArenaTag | any): ArenaTag { + const tag = getArenaTag(source.tagType, source.turnCount, source.sourceMove, source.sourceId, source.targetIndex, source.side) + ?? new NoneTag(); + tag.loadTag(source); + return tag; +} + diff --git a/src/phases/check-status-effect-phase.ts b/src/phases/check-status-effect-phase.ts new file mode 100644 index 00000000000..44918b54966 --- /dev/null +++ b/src/phases/check-status-effect-phase.ts @@ -0,0 +1,23 @@ +import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase"; +import { Phase } from "#app/phase"; +import { BattlerIndex } from "#app/battle"; +import BattleScene from "#app/battle-scene"; + +export class CheckStatusEffectPhase extends Phase { + private order : BattlerIndex[]; + constructor(scene : BattleScene, order : BattlerIndex[]) { + super(scene); + this.scene = scene; + this.order = order; + } + + start() { + const field = this.scene.getField(); + for (const o of this.order) { + if (field[o].status && field[o].status.isPostTurn()) { + this.scene.unshiftPhase(new PostTurnStatusEffectPhase(this.scene, o)); + } + } + this.end(); + } +} diff --git a/src/phases/obtain-status-effect-phase.ts b/src/phases/obtain-status-effect-phase.ts index bf38c432394..c396fa7ba59 100644 --- a/src/phases/obtain-status-effect-phase.ts +++ b/src/phases/obtain-status-effect-phase.ts @@ -6,7 +6,6 @@ import { StatusEffect } from "#app/enums/status-effect"; import Pokemon from "#app/field/pokemon"; import { getPokemonNameWithAffix } from "#app/messages"; import { PokemonPhase } from "./pokemon-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; export class ObtainStatusEffectPhase extends PokemonPhase { private statusEffect?: StatusEffect | undefined; @@ -33,9 +32,6 @@ export class ObtainStatusEffectPhase extends PokemonPhase { pokemon.updateInfo(true); new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, false, () => { this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined)); - if (pokemon.status?.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.battlerIndex)); - } this.end(); }); return; diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 53623f933f2..627cee4b06a 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -13,10 +13,10 @@ import { BerryPhase } from "./berry-phase"; import { FieldPhase } from "./field-phase"; import { MoveHeaderPhase } from "./move-header-phase"; import { MovePhase } from "./move-phase"; -import { PostTurnStatusEffectPhase } from "./post-turn-status-effect-phase"; import { SwitchSummonPhase } from "./switch-summon-phase"; import { TurnEndPhase } from "./turn-end-phase"; import { WeatherEffectPhase } from "./weather-effect-phase"; +import { CheckStatusEffectPhase } from "#app/phases/check-status-effect-phase"; import { BattlerIndex } from "#app/battle"; import { TrickRoomTag } from "#app/data/arena-tag"; import { SwitchType } from "#enums/switch-type"; @@ -206,11 +206,8 @@ export class TurnStartPhase extends FieldPhase { this.scene.pushPhase(new WeatherEffectPhase(this.scene)); - for (const o of moveOrder) { - if (field[o].status && field[o].status.isPostTurn()) { - this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, o)); - } - } + /** Add a new phase to check who should be taking status damage */ + this.scene.pushPhase(new CheckStatusEffectPhase(this.scene, moveOrder)); this.scene.pushPhase(new BerryPhase(this.scene)); this.scene.pushPhase(new TurnEndPhase(this.scene)); diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index 5b907805372..ba37de0ed0e 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -1,5 +1,5 @@ import { Arena } from "../field/arena"; -import { ArenaTag } from "../data/arena-tag"; +import { ArenaTag, loadArenaTag } from "../data/arena-tag"; import { Biome } from "#enums/biome"; import { Weather } from "../data/weather"; import { Terrain } from "#app/data/terrain"; @@ -15,6 +15,10 @@ export default class ArenaData { this.biome = sourceArena ? sourceArena.biomeType : source.biome; this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; - this.tags = sourceArena ? sourceArena.tags : []; + this.tags = []; + + if (source.tags) { + this.tags = source.tags.map(t => loadArenaTag(t)); + } } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 0d2f35ae728..b162962fac6 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -31,7 +31,7 @@ import { TrainerVariant } from "#app/field/trainer"; import { Variant } from "#app/data/variant"; import { setSettingGamepad, SettingGamepad, settingGamepadDefaults } from "#app/system/settings/settings-gamepad"; import { setSettingKeyboard, SettingKeyboard } from "#app/system/settings/settings-keyboard"; -import { TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; +import { TagAddedEvent, TerrainChangedEvent, WeatherChangedEvent } from "#app/events/arena"; import * as Modifier from "#app/modifier/modifier"; import { StatusEffect } from "#app/data/status-effect"; import ChallengeData from "#app/system/challenge-data"; @@ -50,6 +50,7 @@ import { applySessionDataPatches, applySettingsDataPatches, applySystemDataPatch import { MysteryEncounterSaveData } from "#app/data/mystery-encounters/mystery-encounter-save-data"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import { PokerogueApiClearSessionData } from "#app/@types/pokerogue-api"; +import { ArenaTrapTag } from "#app/data/arena-tag"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -1085,8 +1086,18 @@ export class GameData { scene.arena.terrain = sessionData.arena.terrain; scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct? - // TODO - //scene.arena.tags = sessionData.arena.tags; + + scene.arena.tags = sessionData.arena.tags; + if (scene.arena.tags) { + for (const tag of scene.arena.tags) { + if (tag instanceof ArenaTrapTag) { + const { tagType, side, turnCount, layers, maxLayers } = tag as ArenaTrapTag; + scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tagType, side, turnCount, layers, maxLayers)); + } else { + scene.arena.eventTarget.dispatchEvent(new TagAddedEvent(tag.tagType, tag.side, tag.turnCount)); + } + } + } for (const modifierData of sessionData.modifiers) { const modifier = modifierData.toModifier(scene, Modifier[modifierData.className]); diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index a83fd3655e5..63c7b6245f5 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -1,7 +1,4 @@ import { StatusEffect } from "#app/data/status-effect"; -import { EnemyCommandPhase } from "#app/phases/enemy-command-phase"; -import { MessagePhase } from "#app/phases/message-phase"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import i18next from "#app/plugins/i18n"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; @@ -10,6 +7,7 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +const TIMEOUT = 20 * 1000; describe("Items - Toxic orb", () => { let phaserGame: Phaser.Game; @@ -27,40 +25,36 @@ describe("Items - Toxic orb", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.GROWTH; - const oppMoveToUse = Moves.TACKLE; - game.override.battleType("single"); - game.override.enemySpecies(Species.RATTATA); - game.override.ability(Abilities.INSOMNIA); - game.override.enemyAbility(Abilities.INSOMNIA); - game.override.startingLevel(2000); - game.override.moveset([ moveToUse ]); - game.override.enemyMoveset([ oppMoveToUse, oppMoveToUse, oppMoveToUse, oppMoveToUse ]); - game.override.startingHeldItems([{ - name: "TOXIC_ORB", - }]); + game.override + .battleType("single") + .enemySpecies(Species.RATTATA) + .ability(Abilities.BALL_FETCH) + .enemyAbility(Abilities.BALL_FETCH) + .moveset([ Moves.SPLASH ]) + .enemyMoveset(Moves.SPLASH) + .startingHeldItems([{ + name: "TOXIC_ORB", + }]); vi.spyOn(i18next, "t"); }); - it("TOXIC ORB", async () => { - const moveToUse = Moves.GROWTH; - await game.startBattle([ - Species.MIGHTYENA, - Species.MIGHTYENA, - ]); - expect(game.scene.modifiers[0].type.id).toBe("TOXIC_ORB"); + it("badly poisons the holder", async () => { + await game.classicMode.startBattle([ Species.MIGHTYENA ]); - game.move.select(moveToUse); + const player = game.scene.getPlayerField()[0]; - // will run the 13 phase from enemyCommandPhase to TurnEndPhase - await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); + game.move.select(Moves.SPLASH); + + await game.phaseInterceptor.to("TurnEndPhase"); // Toxic orb should trigger here - await game.phaseInterceptor.run(MessagePhase); + await game.phaseInterceptor.run("MessagePhase"); expect(i18next.t).toHaveBeenCalledWith("statusEffect:toxic.obtainSource", expect.anything()); - await game.phaseInterceptor.run(MessagePhase); - expect(i18next.t).toHaveBeenCalledWith("statusEffect:toxic.activation", expect.anything()); - expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC); - }, 20000); + await game.toNextTurn(); + + expect(player.status?.effect).toBe(StatusEffect.TOXIC); + // Damage should not have ticked yet. + expect(player.status?.turnCount).toBe(0); + }, TIMEOUT); }); diff --git a/src/test/moves/toxic_spikes.test.ts b/src/test/moves/toxic_spikes.test.ts new file mode 100644 index 00000000000..bac1ccdccd8 --- /dev/null +++ b/src/test/moves/toxic_spikes.test.ts @@ -0,0 +1,136 @@ +import { ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; +import { StatusEffect } from "#app/data/status-effect"; +import { decrypt, encrypt, GameData, SessionSaveData } from "#app/system/game-data"; +import { Abilities } from "#enums/abilities"; +import { ArenaTagType } from "#enums/arena-tag-type"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +const TIMEOUT = 20 * 1000; + +describe("Moves - Toxic Spikes", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .startingWave(5) + .enemySpecies(Species.RATTATA) + .enemyAbility(Abilities.BALL_FETCH) + .ability(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .moveset([ Moves.TOXIC_SPIKES, Moves.SPLASH, Moves.ROAR ]); + }); + + it("should not affect the opponent if they do not switch", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA ]); + + const enemy = game.scene.getEnemyField()[0]; + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemy.hp).toBe(enemy.getMaxHp()); + expect(enemy.status?.effect).toBeUndefined(); + }, TIMEOUT); + + it("should poison the opponent if they switch into 1 layer", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + expect(enemy.status?.effect).toBe(StatusEffect.POISON); + }, TIMEOUT); + + it("should badly poison the opponent if they switch into 2 layers", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); + expect(enemy.status?.effect).toBe(StatusEffect.TOXIC); + }, TIMEOUT); + + it("should be removed if a grounded poison pokemon switches in", async() => { + game.override.enemySpecies(Species.GRIMER); + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.ROAR); + await game.phaseInterceptor.to("TurnEndPhase"); + + const enemy = game.scene.getEnemyField()[0]; + expect(enemy.hp).toBe(enemy.getMaxHp()); + expect(enemy.status?.effect).toBeUndefined(); + + expect(game.scene.arena.tags.length).toBe(0); + }, TIMEOUT); + + it("shouldn't create multiple layers per use in doubles", async() => { + await game.classicMode.runToSummon([ Species.MIGHTYENA, Species.POOCHYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + + const arenaTags = (game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY) as ArenaTrapTag); + expect(arenaTags.tagType).toBe(ArenaTagType.TOXIC_SPIKES); + expect(arenaTags.layers).toBe(1); + }, TIMEOUT); + + it("should persist through reload", async() => { + game.override.startingWave(1); + const scene = game.scene; + const gameData = new GameData(scene); + + await game.classicMode.runToSummon([ Species.MIGHTYENA ]); + + game.move.select(Moves.TOXIC_SPIKES); + await game.phaseInterceptor.to("TurnEndPhase"); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.phaseInterceptor.to("BattleEndPhase"); + await game.toNextWave(); + + const sessionData : SessionSaveData = gameData["getSessionSaveData"](game.scene); + localStorage.setItem("sessionTestData", encrypt(JSON.stringify(sessionData), true)); + const recoveredData : SessionSaveData = gameData.parseSessionData(decrypt(localStorage.getItem("sessionTestData")!, true)); + gameData.loadSession(game.scene, 0, recoveredData); + + expect(sessionData.arena.tags).toEqual(recoveredData.arena.tags); + localStorage.removeItem("sessionTestData"); + }, TIMEOUT); +}); From 89d7e7ea655b8a0f103cee1a6ab696a26da4896a Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Fri, 11 Oct 2024 21:46:00 +0200 Subject: [PATCH 49/70] [P3][UI] Fix tooltip bugs in Starter Select screen (#4641) * [UI] Fix candy friendship tooltip bug in Starter Select * [UI] remove tooltip when exiting starter select screen --- src/ui/starter-select-ui-handler.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 5bbe765947e..2be89052bc1 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -2963,8 +2963,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.natureCursor = -1; if (this.activeTooltip === "CANDY") { - const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); - this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`); + if (this.lastSpecies) { + const { currentFriendship, friendshipCap } = this.getFriendship(this.lastSpecies.speciesId); + this.scene.ui.editTooltip("", `${currentFriendship}/${friendshipCap}`); + } else { + this.scene.ui.hideTooltip(); + } } if (species?.forms?.find(f => f.formKey === "female")) { @@ -3655,6 +3659,9 @@ export default class StarterSelectUiHandler extends MessageUiHandler { StarterPrefs.save(this.starterPreferences); this.cursor = -1; this.hideInstructions(); + this.activeTooltip = undefined; + this.scene.ui.hideTooltip(); + this.starterSelectContainer.setVisible(false); this.blockInput = false; From f0cc1fc88bba1015d47ec5a34d83b224952b09c6 Mon Sep 17 00:00:00 2001 From: pom-eranian Date: Fri, 11 Oct 2024 16:08:39 -0400 Subject: [PATCH 50/70] [Sprite] Hisui Goodra, Sliggoo, Shiftry (#4642) * [Sprite][Anim] 275 Shiftree- cropped ear fix - @hamez * 705 6706 Sliggoo Hisuian Goodra [Color Fixes] - @ rival_kieran - Fix to Kalosian Sligoo (705). Front exp should now use shaders properly. - Fix to all of Hisuian Goodra's base sprites + Variants to allow them to use shaders properly (as they didn't before). .json file included. - Fix to Hisuian Goodra's back static Rare Variant which used colors from the Epic variant on accident on cheeks + spots and drips. - Hisuian Goodra's eyes on Variant (Rare, Epic) exps were mapped to the wrong color compared to the statics, this has been fixed. - All images should be indexed. * Set variants to palette, removed old files --- public/images/pokemon/275.png | Bin 10555 -> 11106 bytes public/images/pokemon/6706.png | Bin 1040 -> 1103 bytes public/images/pokemon/back/6706.png | Bin 879 -> 942 bytes public/images/pokemon/exp/6706.png | Bin 24796 -> 27836 bytes public/images/pokemon/exp/705.png | Bin 1851 -> 1913 bytes public/images/pokemon/exp/back/6706.png | Bin 8135 -> 8808 bytes public/images/pokemon/female/275.png | Bin 10686 -> 11186 bytes public/images/pokemon/shiny/275.png | Bin 10555 -> 11107 bytes public/images/pokemon/shiny/female/275.png | Bin 10686 -> 11186 bytes public/images/pokemon/variant/6706.json | 40 + public/images/pokemon/variant/6706_2.json | 41 - public/images/pokemon/variant/6706_2.png | Bin 5170 -> 0 bytes public/images/pokemon/variant/6706_3.json | 41 - public/images/pokemon/variant/6706_3.png | Bin 5177 -> 0 bytes .../images/pokemon/variant/_masterlist.json | 20 +- public/images/pokemon/variant/back/6706.json | 38 + .../images/pokemon/variant/back/6706_2.json | 41 - public/images/pokemon/variant/back/6706_2.png | Bin 4762 -> 0 bytes .../images/pokemon/variant/back/6706_3.json | 41 - public/images/pokemon/variant/back/6706_3.png | Bin 4765 -> 0 bytes public/images/pokemon/variant/exp/6706.json | 40 + public/images/pokemon/variant/exp/6706_2.json | 2015 ----------------- public/images/pokemon/variant/exp/6706_2.png | Bin 55978 -> 0 bytes public/images/pokemon/variant/exp/6706_3.json | 2015 ----------------- public/images/pokemon/variant/exp/6706_3.png | Bin 56190 -> 0 bytes public/images/pokemon/variant/exp/705.json | 33 + public/images/pokemon/variant/exp/705_2.json | 272 --- public/images/pokemon/variant/exp/705_2.png | Bin 4111 -> 0 bytes public/images/pokemon/variant/exp/705_3.json | 272 --- public/images/pokemon/variant/exp/705_3.png | Bin 4119 -> 0 bytes .../images/pokemon/variant/exp/back/6706.json | 38 + .../pokemon/variant/exp/back/6706_2.json | 776 ------- .../pokemon/variant/exp/back/6706_2.png | Bin 22444 -> 0 bytes .../pokemon/variant/exp/back/6706_3.json | 776 ------- .../pokemon/variant/exp/back/6706_3.png | Bin 22483 -> 0 bytes 35 files changed, 199 insertions(+), 6300 deletions(-) create mode 100644 public/images/pokemon/variant/6706.json delete mode 100644 public/images/pokemon/variant/6706_2.json delete mode 100644 public/images/pokemon/variant/6706_2.png delete mode 100644 public/images/pokemon/variant/6706_3.json delete mode 100644 public/images/pokemon/variant/6706_3.png create mode 100644 public/images/pokemon/variant/back/6706.json delete mode 100644 public/images/pokemon/variant/back/6706_2.json delete mode 100644 public/images/pokemon/variant/back/6706_2.png delete mode 100644 public/images/pokemon/variant/back/6706_3.json delete mode 100644 public/images/pokemon/variant/back/6706_3.png create mode 100644 public/images/pokemon/variant/exp/6706.json delete mode 100644 public/images/pokemon/variant/exp/6706_2.json delete mode 100644 public/images/pokemon/variant/exp/6706_2.png delete mode 100644 public/images/pokemon/variant/exp/6706_3.json delete mode 100644 public/images/pokemon/variant/exp/6706_3.png create mode 100644 public/images/pokemon/variant/exp/705.json delete mode 100644 public/images/pokemon/variant/exp/705_2.json delete mode 100644 public/images/pokemon/variant/exp/705_2.png delete mode 100644 public/images/pokemon/variant/exp/705_3.json delete mode 100644 public/images/pokemon/variant/exp/705_3.png create mode 100644 public/images/pokemon/variant/exp/back/6706.json delete mode 100644 public/images/pokemon/variant/exp/back/6706_2.json delete mode 100644 public/images/pokemon/variant/exp/back/6706_2.png delete mode 100644 public/images/pokemon/variant/exp/back/6706_3.json delete mode 100644 public/images/pokemon/variant/exp/back/6706_3.png diff --git a/public/images/pokemon/275.png b/public/images/pokemon/275.png index 07a6fe725adfc61aad7a862a12678b088b533a37..61f98ed16555660d553a699e2dda6d8c0b2feb6f 100644 GIT binary patch literal 11106 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 diff --git a/public/images/pokemon/6706.png b/public/images/pokemon/6706.png index 44f627acd1011076a81973d1fa47e325aa12239b..e967b5508713a8aa1802e00ca8963727797f1275 100644 GIT binary patch delta 1082 zcmV-A1jYN12+s(RB!2;OQb$4nuFf3k0000yP)t-s0000F5e-X4Pk?iOn2VUdtik5f z>G%2eVp4~ZaJjdU@>61Sj+Uy#%;DhV^fo|DQD1OYW_XK}rp3kplbCiSpRd+&l*v8AKWycm{nM zyV(7=!MrMQAY(`3IQekSw2Nqw>8#I4B|}Iz$~dKjPjB(V=jQDHm?>$f!~79p>Y$oP zFi*&tJ@S~546o5FXJU*gWtBXb87aeC6tW95sefyv zemu<_d`@o!)-!T}@`bL*f-POhXmLetoVu<)e8;B1i0zqJit33()sCaP!P$7GIj z5XE-8lUZKqf|t*O%aENK+$vI%y|nh|k=P>XGVI7nnu6@J4qeyFkjP;TTok7!Tv?v& zkbih9^%2Y51ig$OUiCKHL`38{JtsTAx~7+8ozx_t1sHpC9MnM&=r zc*ro+Le%D`;N;jKaihAsb=YlUA@0WJygnyea}pL!T95GNYR6UC%}+#)JdoJr^-{!@ zcnf$U5gjFOxs6S}5<%iRbV3w?O#3l0GJoa#X^e7E2p^^#d=Ocz7VS(N6q` zOQchADe>kvWHd$o4d0L*C5wXifrxj6oDg4=a61TBA}-{qKZ>XhzeSN*{P6Qei8FHg ziEq2I{1V9hTT&Q2gMLh^AB*XinZNq0zxs9k2jQQ9d?va*lmGw#07*qoM6N<$f>Zqx AD*ylh delta 1018 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 Hu0mjfSn$d2 diff --git a/public/images/pokemon/exp/705.png b/public/images/pokemon/exp/705.png index 3413bcd9fa35db5d1cd102176e6664c2fbfbd6e6..670e8be5d51fd8645ca363e13dcc9eca01115e36 100644 GIT binary patch delta 1886 zcmV-k2ch`84*3p{B!2;OQb$4nuFf3k0000gP)t-s0000G5D-m8O@MQNzpTIV+VW;u zfRBB%xS{1unLTFAQcj#cXU|i%@~Zf82(thH01k9gPE!E?|NsC0|NsC0|NsC0S4!Iy z000K#NklvG&G5QMdWkTy=<|8*now;87KCg)VDn13I(V}EoHh}BA~&T;%} zrB~P`c(s#3;KU{LxdeaM+v5gHIM%4YApE)Aa%(Wm37`s&Rj}KFFl(n!JI5Xks$G39 z+z!;m>^SBEE!+ITt;|U`r)6QrF6_|Jj#!DzmR~DZV%Y`Pkp#&E*oD?>Xy{S5Ey zsFf&evI`sZ%zv#_$1P64eN&{ck$Q6{DDTwSkt-2dZ9J<@Y)ZL${?%+KZXQM0Ag!6v z?j#g4d-_#=P^I!VQc8`{5-aRa*9sjw$%w`wI+!#AabYnSU66#BJ&4I3J%`KMC1PCt(C0 zN5Avt)RI7E{c=cdtMeGf|5$KK@cSKJxI$}jBG5c2v)}@`CHNgWgyw-MQ^QSgdn0Zc z{t;UgI_s2I7j4CDgqP;Ax@zb9#N9F+j?r`?eQV3hmfy`Fw*h|=qwJ*AAlc0tS{?dnU0b+~}^(%LnJ&`b>a3V4BGh$u!pru0L3GlF zCNh?e8#U}$(M|i8-P0OECv9-r#OPvn0(Y(9eDDJ3R(t&tJiVL5)}*_nj01G9{mssv z1@~uf(Ch^03Wla#%33%2ekz-3{<6r?9nRAPEKj={e&5#KvC{DH83RrGFD=f{jr1)~ zdw(2WSDqT%S#h()j_d5H*PzW!dlYySuExx_#+xi0#0d5K<=mIX|nCDPlRUXunUcLfCt z^){#1q{p)NINU3c3%^9KNfX2KK8-(=m(?h+M6XFRtyM#w3JMo>1N=8>qPOz47=H)6 zLjC77DG=)arzgceIsXm_FtDG$^Mx)#5fET{gijT~Nel!Wn9;XGW~qYjw?<4C@NEz> zRZx@dXA;Z;_aJ9m7&^^RBbrVX6truMS>P6=r`s^JZ;f~jGFQ-pTx^2^DchEzZTrsB z0;=|av?Lqb$Y2S`P_}+rD)Oz7-O3(z0%}qzmF-XEX99v*oIG7Qpb-E z9L7M|@(h+UM}(_e(~}%U)fxv?mh(VH6%!GjShsl+L~pW*5X&m=BD9u3Gk=YfG#G%) zEl)wJ0maY;fT2q93?{}zbUM?CkLX1Xl1k@uIVO_HAj-ewWe$=`0TwwXQpkC11K=>J zA@9PsmdW`n01lJVa85*nu?>KOr0|>wN&*}vRVXPZQjycx2EbubIl?05L@F|TXC8(F z943XwL>h9E+5k9E3Xh2t(tlsMvZ)Jz!=!Y9riL{cR+o!i030Z#D`;Zak+awZz=2Y_ z$-7K5LZ-+~Ls2pZO4WH6AtMw>F}4A4s1%+PiKLkC0XR@fVTMPW+xU>fq)@{ufATqp zNtM8h^m)v3YrJBGOkAsYjAZG^^AsnMIPQM0Mdr7dJ|`k4SmGJ!C4c-!gNkE68Y>9T zw0Rfdey@;76xHyl767RzIq!m!K;Q<->uuM8l^5RYN+xnXh)+zcTK z32`6j_wg7?C($4|25yFYhlGT1%@|=J4Bc~yMA{kY!Ssx!7`I6U8EMiAu{Sy%(>!jn z6S4;>-#TT(AYHnqIDay&#b;mlH6aHuaCL?TKzyaVOKR?9vN+ae(w222NC)0E=r~m$ zc()vOkW$0mNQ>*DI9X_DtT_yo9{DOD5;U*wSP@dQhn7~#yHw;oBY^}BCWS{>P!ixE zDLldglK_ZlEksyg6agUykFcO9!huXvlMD-rA{?Z(kYPblgp=3?5L3x}@t#LmP!i!F zEj+=3k_d-s;RzO$L^x0jPq3gQ!u_$_%&uL#cJ11=YuBz_yLRo`wQJX|UAuPe+V$_$ YZ@>AKSD6@q_5c6?07*qoM6N<$f^O`ZumAu6 delta 1812 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 literal 8135 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 diff --git a/public/images/pokemon/female/275.png b/public/images/pokemon/female/275.png index 7f251793a15764d2a7bb5849e65045ad7884eae3..c3c358716b2d919fc2a882d2f69e3ad524d57761 100644 GIT binary patch literal 11186 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≪pFCQ%=xE@hNoXB9038F;6HZ(ytRK>+ik2e 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 VwS#>?K2`v{tSucZ-XOf={|_}1*(Lx0 diff --git a/public/images/pokemon/variant/exp/705.json b/public/images/pokemon/variant/exp/705.json new file mode 100644 index 00000000000..a29b8f124dc --- /dev/null +++ b/public/images/pokemon/variant/exp/705.json @@ -0,0 +1,33 @@ +{ + "1": { + "101010":"101010", + "4d454d":"8a2166", + "807380":"b93f84", + "bfacbf":"e56ca6", + "f2daf2":"fbb3d2", + "665980":"4e4094", + "8f7db3":"8b69c3", + "b8a1e5":"c7a1e5", + "4d993d":"aa6a00", + "66cc52":"ffd047", + "4e9c3e":"0c5474", + "67cf53":"3aa8c4", + "b6f2aa":"63cee1" + }, + "2": { + "101010":"101010", + "4d454d":"194f51", + "807380":"2b736f", + "bfacbf":"5db6a9", + "f2daf2":"9cead8", + "665980":"274159", + "8f7db3":"2f667c", + "b8a1e5":"4a9699", + "4d993d":"007d61", + "66cc52":"49ffbf", + "4e9c3e":"842401", + "67cf53":"a34205", + "b6f2aa":"d27e26" + } +} + diff --git a/public/images/pokemon/variant/exp/705_2.json b/public/images/pokemon/variant/exp/705_2.json deleted file mode 100644 index bf9fd104c5d..00000000000 --- a/public/images/pokemon/variant/exp/705_2.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "textures": [ - { - "image": "705_2.png", - "format": "RGBA8888", - "size": { - "w": 154, - "h": 154 - }, - "scale": 1, - "frames": [ - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 91, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 35, - "h": 58 - }, - "frame": { - "x": 119, - "y": 58, - "w": 35, - "h": 58 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/705_2.png b/public/images/pokemon/variant/exp/705_2.png deleted file mode 100644 index 8256ebc7fdbebeaff9d693aed989c4fbce61b77e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4111 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 diff --git a/public/images/pokemon/variant/exp/705_3.json b/public/images/pokemon/variant/exp/705_3.json deleted file mode 100644 index 199d7bc9c3e..00000000000 --- a/public/images/pokemon/variant/exp/705_3.json +++ /dev/null @@ -1,272 +0,0 @@ -{ - "textures": [ - { - "image": "705_3.png", - "format": "RGBA8888", - "size": { - "w": 154, - "h": 154 - }, - "scale": 1, - "frames": [ - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 46, - "h": 58 - }, - "frame": { - "x": 0, - "y": 0, - "w": 46, - "h": 58 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 46, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 45, - "h": 58 - }, - "frame": { - "x": 91, - "y": 0, - "w": 45, - "h": 58 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 1, - "y": 0, - "w": 42, - "h": 58 - }, - "frame": { - "x": 0, - "y": 58, - "w": 42, - "h": 58 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 41, - "h": 58 - }, - "frame": { - "x": 42, - "y": 58, - "w": 41, - "h": 58 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 36, - "h": 58 - }, - "frame": { - "x": 83, - "y": 58, - "w": 36, - "h": 58 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 49, - "h": 58 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 35, - "h": 58 - }, - "frame": { - "x": 119, - "y": 58, - "w": 35, - "h": 58 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:4bf155254b23c88780e7eee282256589:82bb727988054c3064e203b6908ff464:6b57e983626c7fc9144ab67f30c66814$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/705_3.png b/public/images/pokemon/variant/exp/705_3.png deleted file mode 100644 index 66b43956bdf70a75057437c951b98f1c55eef48c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4119 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 Lf*Zay@Obb);ds$n diff --git a/public/images/pokemon/variant/exp/back/6706.json b/public/images/pokemon/variant/exp/back/6706.json new file mode 100644 index 00000000000..2de5352e936 --- /dev/null +++ b/public/images/pokemon/variant/exp/back/6706.json @@ -0,0 +1,38 @@ +{ + "1": { + "566678": "197497", + "8e96aa": "3b235c", + "929aad": "3aa8c4", + "625287": "4e4094", + "536273": "301848", + "988b98": "b24c86", + "36404c": "0c5474", + "c4cce1": "513981", + "e6d3e9": "f1a4c5", + "bfacc1": "d074a0", + "546475": "0e6296", + "c5cee3": "63cee1", + "80737f": "8a2166", + "4b454f": "6f1357", + "9170b9": "8b69c3", + "b791f2": "c7a1e5" + }, + "2": { + "566678": "a34205", + "8e96aa": "073338", + "929aad": "d27e26", + "625287": "0e3f47", + "536273": "042329", + "988b98": "2b736f", + "36404c": "842401", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "546475": "8e480b", + "c5cee3": "f7af58", + "80737f": "194f51", + "4b454f": "274159", + "9170b9": "2f667c", + "b791f2": "4a9699" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_2.json b/public/images/pokemon/variant/exp/back/6706_2.json deleted file mode 100644 index 5c916aeb664..00000000000 --- a/public/images/pokemon/variant/exp/back/6706_2.json +++ /dev/null @@ -1,776 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 358, - "h": 358 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 0, - "y": 69, - "w": 83, - "h": 72 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_2.png b/public/images/pokemon/variant/exp/back/6706_2.png deleted file mode 100644 index cb79347842094431759bdf08b8d5c76eac99023a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22444 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 XiwmH!3^jqR&X^1hO!TYt>?8gU6->{8 diff --git a/public/images/pokemon/variant/exp/back/6706_3.json b/public/images/pokemon/variant/exp/back/6706_3.json deleted file mode 100644 index 3bb1dc426b2..00000000000 --- a/public/images/pokemon/variant/exp/back/6706_3.json +++ /dev/null @@ -1,776 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 358, - "h": 358 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 4, - "w": 84, - "h": 69 - }, - "frame": { - "x": 0, - "y": 0, - "w": 84, - "h": 69 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 84, - "y": 0, - "w": 83, - "h": 72 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 1, - "w": 83, - "h": 72 - }, - "frame": { - "x": 0, - "y": 69, - "w": 83, - "h": 72 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 167, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 3, - "w": 83, - "h": 70 - }, - "frame": { - "x": 250, - "y": 0, - "w": 83, - "h": 70 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 167, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 83, - "y": 72, - "w": 82, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 3, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 0, - "y": 141, - "w": 82, - "h": 73 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 4, - "y": 0, - "w": 82, - "h": 73 - }, - "frame": { - "x": 249, - "y": 70, - "w": 82, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 0, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 0, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 5, - "y": 0, - "w": 81, - "h": 73 - }, - "frame": { - "x": 81, - "y": 214, - "w": 81, - "h": 73 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 1, - "y": 2, - "w": 81, - "h": 71 - }, - "frame": { - "x": 81, - "y": 287, - "w": 81, - "h": 71 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 165, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 2, - "y": 1, - "w": 82, - "h": 72 - }, - "frame": { - "x": 247, - "y": 143, - "w": 82, - "h": 72 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 162, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 3, - "w": 81, - "h": 70 - }, - "frame": { - "x": 162, - "y": 288, - "w": 81, - "h": 70 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 86, - "h": 73 - }, - "spriteSourceSize": { - "x": 6, - "y": 0, - "w": 80, - "h": 73 - }, - "frame": { - "x": 242, - "y": 215, - "w": 80, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:5d65e2c5a6a97b7c7014a175ce3592af:3255e87f637a475d82734fc7d93baf71:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/back/6706_3.png b/public/images/pokemon/variant/exp/back/6706_3.png deleted file mode 100644 index 6390c20799f368a4253062379b4624b3f1f0ea69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22483 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 Date: Fri, 11 Oct 2024 13:09:15 -0700 Subject: [PATCH 51/70] [P3] Fix "falsex" type hint for Sheer Cold against ice types (#4638) Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/move.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index ff0c24f5032..5aa6b2623c0 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4556,18 +4556,19 @@ export class WaterSuperEffectTypeMultiplierAttr extends VariableMoveTypeMultipli export class IceNoEffectTypeAttr extends VariableMoveTypeMultiplierAttr { /** * Checks to see if the Target is Ice-Type or not. If so, the move will have no effect. - * @param {Pokemon} user N/A - * @param {Pokemon} target Pokemon that is being checked whether Ice-Type or not. - * @param {Move} move N/A - * @param {any[]} args Sets to false if the target is Ice-Type, so it should do no damage/no effect. - * @returns {boolean} Returns true if move is successful, false if Ice-Type. + * @param user n/a + * @param target The {@linkcode Pokemon} targeted by the move + * @param move n/a + * @param args `[0]` a {@linkcode Utils.NumberHolder | NumberHolder} containing a type effectiveness multiplier + * @returns `true` if this Ice-type immunity applies; `false` otherwise */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { + const multiplier = args[0] as Utils.NumberHolder; if (target.isOfType(Type.ICE)) { - (args[0] as Utils.BooleanHolder).value = false; - return false; + multiplier.value = 0; + return true; } - return true; + return false; } } From cfb92b4e0874d1f6344578d5abb689e78b9c8fbf Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:44:16 -0700 Subject: [PATCH 52/70] [Move] Telekinesis + [Bug] Ingrain (#4506) * some early set up * localization * Added Wiglett family to restrictions * Added Smack Down + 1000 Arrows Interactions * Added checks for certain tags * Gravity removes telekinesis from all pokemon on the field * need to check something else real quick * mmmmmm * think this is fine? * ingrain fixes * more ingrain * Telekinesis Test + Move Fix * Test Name change * another day another try... * Test Cleanup * fsfdsfds * Revert "fsfdsfds" This reverts commit cb7abcfd9f69342ae7b1864e2e4e124029f9f67e. * whoops * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Missed one * Update src/data/move.ts Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * Add separate battler tags in move attr * Update src/data/battler-tags.ts Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> * removed onRemove * Documentation * Update src/data/battler-tags.ts --------- Co-authored-by: frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> --- src/data/arena-tag.ts | 3 +- src/data/battler-tags.ts | 39 ++++++-- src/data/move.ts | 20 ++-- src/enums/battler-tag-type.ts | 3 +- src/field/pokemon.ts | 2 +- src/phases/move-effect-phase.ts | 6 +- src/test/moves/telekinesis.test.ts | 124 +++++++++++++++++++++++++ src/test/moves/thousand_arrows.test.ts | 4 +- 8 files changed, 182 insertions(+), 19 deletions(-) create mode 100644 src/test/moves/telekinesis.test.ts diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 45d64249296..11d28ab7e25 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -885,7 +885,8 @@ export class GravityTag extends ArenaTag { arena.scene.queueMessage(i18next.t("arenaTag:gravityOnAdd")); arena.scene.getField(true).forEach((pokemon) => { if (pokemon !== null) { - pokemon.removeTag(BattlerTagType.MAGNET_RISEN); + pokemon.removeTag(BattlerTagType.FLOATING); + pokemon.removeTag(BattlerTagType.TELEKINESIS); } }); } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 3cc109df264..18f03ada941 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1713,7 +1713,12 @@ export class TypeImmuneTag extends BattlerTag { } } -export class MagnetRisenTag extends TypeImmuneTag { +/** + * Battler Tag that lifts the affected Pokemon into the air and provides immunity to Ground type moves. + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Magnet_Rise_(move) | Moves.MAGNET_RISE} + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS} + */ +export class FloatingTag extends TypeImmuneTag { constructor(tagType: BattlerTagType, sourceMove: Moves) { super(tagType, sourceMove, Type.GROUND, 5); } @@ -1721,13 +1726,17 @@ export class MagnetRisenTag extends TypeImmuneTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + if (this.sourceMove === Moves.MAGNET_RISE) { + pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } + } onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); - - pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + if (this.sourceMove === Moves.MAGNET_RISE) { + pokemon.scene.queueMessage(i18next.t("battlerTags:magnetRisenOnRemove", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } } } @@ -2676,6 +2685,22 @@ export class SyrupBombTag extends BattlerTag { } } +/** + * Telekinesis raises the target into the air for three turns and causes all moves used against the target (aside from OHKO moves) to hit the target unless the target is in a semi-invulnerable state from Fly/Dig. + * The first effect is provided by {@linkcode FloatingTag}, the accuracy-bypass effect is provided by TelekinesisTag + * The effects of Telekinesis can be baton passed to a teammate. Unlike the mainline games, Telekinesis can be baton-passed to Mega Gengar. + * @see {@link https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move) | Moves.TELEKINESIS} + */ +export class TelekinesisTag extends BattlerTag { + constructor(sourceMove: Moves) { + super(BattlerTagType.TELEKINESIS, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 3, sourceMove, undefined, true); + } + + override onAdd(pokemon: Pokemon) { + pokemon.scene.queueMessage(i18next.t("battlerTags:telekinesisOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } +} + /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * @param sourceId - The ID of the pokemon adding the tag @@ -2802,8 +2827,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new CursedTag(sourceId); case BattlerTagType.CHARGED: return new TypeBoostTag(tagType, sourceMove, Type.ELECTRIC, 2, true); - case BattlerTagType.MAGNET_RISEN: - return new MagnetRisenTag(tagType, sourceMove); + case BattlerTagType.FLOATING: + return new FloatingTag(tagType, sourceMove); case BattlerTagType.MINIMIZED: return new MinimizeTag(); case BattlerTagType.DESTINY_BOND: @@ -2849,6 +2874,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new ImprisonTag(sourceId); case BattlerTagType.SYRUP_BOMB: return new SyrupBombTag(sourceId); + case BattlerTagType.TELEKINESIS: + return new TelekinesisTag(sourceMove); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/move.ts b/src/data/move.ts index 5aa6b2623c0..bd3706545f9 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -7843,7 +7843,9 @@ export function initMoves() { .attr(RandomMovesetMoveAttr, true) .ignoresVirtual(), new SelfStatusMove(Moves.INGRAIN, Type.GRASS, -1, 20, -1, 0, 3) - .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true), + .attr(AddBattlerTagAttr, BattlerTagType.INGRAIN, true, true) + .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, true, true) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLOATING ], true), new AttackMove(Moves.SUPERPOWER, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 3) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF ], -1, true), new SelfStatusMove(Moves.MAGIC_COAT, Type.PSYCHIC, -1, 15, -1, 4, 3) @@ -8177,8 +8179,8 @@ export function initMoves() { new SelfStatusMove(Moves.AQUA_RING, Type.WATER, -1, 20, -1, 0, 4) .attr(AddBattlerTagAttr, BattlerTagType.AQUA_RING, true, true), new SelfStatusMove(Moves.MAGNET_RISE, Type.ELECTRIC, -1, 10, -1, 0, 4) - .attr(AddBattlerTagAttr, BattlerTagType.MAGNET_RISEN, true, true) - .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.MAGNET_RISEN, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))), + .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, true, true) + .condition((user, target, move) => !user.scene.arena.getTag(ArenaTagType.GRAVITY) && [ BattlerTagType.FLOATING, BattlerTagType.IGNORE_FLYING, BattlerTagType.INGRAIN ].every((tag) => !user.getTag(tag))), new AttackMove(Moves.FLARE_BLITZ, Type.FIRE, MoveCategory.PHYSICAL, 120, 100, 15, 10, 0, 4) .attr(RecoilAttr, false, 0.33) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) @@ -8403,7 +8405,11 @@ export function initMoves() { .attr(AddBattlerTagAttr, BattlerTagType.CENTER_OF_ATTENTION, true), new StatusMove(Moves.TELEKINESIS, Type.PSYCHIC, -1, 15, -1, 0, 5) .condition(failOnGravityCondition) - .unimplemented(), + .condition((_user, target, _move) => ![ Species.DIGLETT, Species.DUGTRIO, Species.ALOLA_DIGLETT, Species.ALOLA_DUGTRIO, Species.SANDYGAST, Species.PALOSSAND, Species.WIGLETT, Species.WUGTRIO ].includes(target.species.speciesId)) + .condition((_user, target, _move) => !(target.species.speciesId === Species.GENGAR && target.getFormKey() === "mega")) + .condition((_user, target, _move) => Utils.isNullOrUndefined(target.getTag(BattlerTagType.INGRAIN)) && Utils.isNullOrUndefined(target.getTag(BattlerTagType.IGNORE_FLYING))) + .attr(AddBattlerTagAttr, BattlerTagType.TELEKINESIS, false, true, 3) + .attr(AddBattlerTagAttr, BattlerTagType.FLOATING, false, true, 3), new StatusMove(Moves.MAGIC_ROOM, Type.PSYCHIC, -1, 10, -1, 0, 5) .ignoresProtect() .target(MoveTarget.BOTH_SIDES) @@ -8411,7 +8417,7 @@ export function initMoves() { new AttackMove(Moves.SMACK_DOWN, Type.ROCK, MoveCategory.PHYSICAL, 50, 100, 15, 100, 0, 5) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) .attr(HitsTagAttr, BattlerTagType.FLYING) .makesContact(false), new AttackMove(Moves.STORM_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 5) @@ -8844,9 +8850,9 @@ export function initMoves() { .attr(NeutralDamageAgainstFlyingTypeMultiplierAttr) .attr(AddBattlerTagAttr, BattlerTagType.IGNORE_FLYING, false, false, 1, 1, true) .attr(HitsTagAttr, BattlerTagType.FLYING) - .attr(HitsTagAttr, BattlerTagType.MAGNET_RISEN) + .attr(HitsTagAttr, BattlerTagType.FLOATING) .attr(AddBattlerTagAttr, BattlerTagType.INTERRUPTED) - .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.MAGNET_RISEN ]) + .attr(RemoveBattlerTagAttr, [ BattlerTagType.FLYING, BattlerTagType.FLOATING, BattlerTagType.TELEKINESIS ]) .makesContact(false) .target(MoveTarget.ALL_NEAR_ENEMIES), new AttackMove(Moves.THOUSAND_WAVES, Type.GROUND, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 6) diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 43c849a78e0..2efae9ad359 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -54,7 +54,7 @@ export enum BattlerTagType { CURSED = "CURSED", CHARGED = "CHARGED", ROOSTED = "ROOSTED", - MAGNET_RISEN = "MAGNET_RISEN", + FLOATING = "FLOATING", MINIMIZED = "MINIMIZED", DESTINY_BOND = "DESTINY_BOND", CENTER_OF_ATTENTION = "CENTER_OF_ATTENTION", @@ -86,4 +86,5 @@ export enum BattlerTagType { IMPRISON = "IMPRISON", SYRUP_BOMB = "SYRUP_BOMB", ELECTRIFIED = "ELECTRIFIED", + TELEKINESIS = "TELEKINESIS" } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d8fcc281d1b..c495e0833cd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1488,7 +1488,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isGrounded(): boolean { - return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.MAGNET_RISEN) && !this.getTag(SemiInvulnerableTag)); + return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag)); } /** diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index b2d429a4313..581cd5ff017 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -4,7 +4,7 @@ import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag"; import { MoveAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags"; -import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move"; +import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, OneHitKOAttr, MoveEffectTrigger, ChargeAttr, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move"; import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Moves } from "#app/enums/moves"; @@ -404,6 +404,10 @@ export class MoveEffectPhase extends PokemonPhase { return true; } + if (target.getTag(BattlerTagType.TELEKINESIS) && !target.getTag(SemiInvulnerableTag) && !this.move.getMove().hasAttr(OneHitKOAttr)) { + return true; + } + const semiInvulnerableTag = target.getTag(SemiInvulnerableTag); if (semiInvulnerableTag && !this.move.getMove().getAttrs(HitsTagAttr).some(hta => hta.tagType === semiInvulnerableTag.tagType) diff --git a/src/test/moves/telekinesis.test.ts b/src/test/moves/telekinesis.test.ts new file mode 100644 index 00000000000..76c0d001f00 --- /dev/null +++ b/src/test/moves/telekinesis.test.ts @@ -0,0 +1,124 @@ +import { BattlerTagType } from "#enums/battler-tag-type"; +import { allMoves } from "#app/data/move"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import { MoveResult } from "#app/field/pokemon"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; + +describe("Moves - Telekinesis", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset([ Moves.TELEKINESIS, Moves.TACKLE, Moves.MUD_SHOT, Moves.SMACK_DOWN ]) + .battleType("single") + .enemySpecies(Species.SNORLAX) + .enemyLevel(60) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset([ Moves.SPLASH ]); + }); + + it("Telekinesis makes the affected vulnerable to most attacking moves regardless of accuracy", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.TACKLE], "accuracy", "get").mockReturnValue(0); + game.move.select(Moves.TACKLE); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.isFullHp()).toBe(false); + }); + + it("Telekinesis makes the affected airborne and immune to most Ground-moves", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(100); + game.move.select(Moves.MUD_SHOT); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.isFullHp()).toBe(true); + }); + + it("Telekinesis can still affect Pokemon that have been transformed into invalid Pokemon", async () => { + game.override.enemyMoveset(Moves.TRANSFORM); + await game.classicMode.startBattle([ Species.DIGLETT ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + expect(enemyOpponent.summonData.speciesForm?.speciesId).toBe(Species.DIGLETT); + }); + + it("Moves like Smack Down and 1000 Arrows remove all effects of Telekinesis from the target Pokemon", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + game.move.select(Moves.SMACK_DOWN); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeUndefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined(); + }); + + it("Ingrain will remove the floating effect of Telekinesis, but not the 100% hit", async () => { + game.override.enemyMoveset([ Moves.SPLASH, Moves.INGRAIN ]); + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyOpponent = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.TELEKINESIS); + await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeDefined(); + + await game.toNextTurn(); + vi.spyOn(allMoves[Moves.MUD_SHOT], "accuracy", "get").mockReturnValue(0); + game.move.select(Moves.MUD_SHOT); + await game.forceEnemyMove(Moves.INGRAIN); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(enemyOpponent.getTag(BattlerTagType.TELEKINESIS)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.INGRAIN)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(enemyOpponent.getTag(BattlerTagType.FLOATING)).toBeUndefined(); + expect(playerPokemon.getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); + }); +}); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index 112be476955..976b4352ee4 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -85,13 +85,13 @@ describe("Moves - Thousand Arrows", () => { const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); + enemyPokemon.addTag(BattlerTagType.FLOATING, undefined, Moves.MAGNET_RISE); game.move.select(Moves.THOUSAND_ARROWS); await game.phaseInterceptor.to(BerryPhase, false); - expect(enemyPokemon.getTag(BattlerTagType.MAGNET_RISEN)).toBeUndefined(); + expect(enemyPokemon.getTag(BattlerTagType.FLOATING)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); } From b7eb95b7614d0df1674f4bd9e000c0e1cd80b04e Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Sat, 12 Oct 2024 11:22:26 -0400 Subject: [PATCH 53/70] [Test] Fix several flaky tests (#4639) --- src/test/moves/safeguard.test.ts | 2 +- .../an-offer-you-cant-refuse-encounter.test.ts | 11 +++++++---- .../encounters/berries-abound-encounter.test.ts | 12 ++++++++---- .../the-pokemon-salesman-encounter.test.ts | 4 ++-- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/test/moves/safeguard.test.ts b/src/test/moves/safeguard.test.ts index c180ff338a5..6505162fd04 100644 --- a/src/test/moves/safeguard.test.ts +++ b/src/test/moves/safeguard.test.ts @@ -33,7 +33,7 @@ describe("Moves - Safeguard", () => { .enemyLevel(5) .starterSpecies(Species.DRATINI) .moveset([ Moves.NUZZLE, Moves.SPORE, Moves.YAWN, Moves.SPLASH ]) - .ability(Abilities.BALL_FETCH); + .ability(Abilities.UNNERVE); // Stop wild Pokemon from potentially eating Lum Berry }); it("protects from damaging moves with additional effects", async () => { diff --git a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts index 9883b4332b9..0585b4ce72b 100644 --- a/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/an-offer-you-cant-refuse-encounter.test.ts @@ -18,6 +18,7 @@ import { Moves } from "#enums/moves"; import { ShinyRateBoosterModifier } from "#app/modifier/modifier"; import { SelectModifierPhase } from "#app/phases/select-modifier-phase"; import i18next from "i18next"; +import { Abilities } from "#enums/abilities"; const namespace = "mysteryEncounters/anOfferYouCantRefuse"; /** Gyarados for Indimidate */ @@ -37,10 +38,11 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { beforeEach(async () => { game = new GameManager(phaserGame); scene = game.scene; - game.override.mysteryEncounterChance(100); - game.override.startingWave(defaultWave); - game.override.startingBiome(defaultBiome); - game.override.disableTrainerWaves(); + game.override.mysteryEncounterChance(100) + .startingWave(defaultWave) + .startingBiome(defaultBiome) + .disableTrainerWaves() + .ability(Abilities.INTIMIDATE); // Extortion ability const biomeMap = new Map([ [ Biome.VOLCANO, [ MysteryEncounterType.MYSTERIOUS_CHALLENGERS ]], @@ -195,6 +197,7 @@ describe("An Offer You Can't Refuse - Mystery Encounter", () => { }); it("should award EXP to a pokemon with a move in EXTORTION_MOVES", async () => { + game.override.ability(Abilities.SYNCHRONIZE); // Not an extortion ability, so we can test extortion move await game.runToMysteryEncounter(MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, [ Species.ABRA ]); const party = scene.getParty(); const abra = party.find((pkm) => pkm.species.speciesId === Species.ABRA)!; diff --git a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts index 8e286468bea..bfa3d428bc0 100644 --- a/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/berries-abound-encounter.test.ts @@ -176,10 +176,12 @@ describe("Berries Abound - Mystery Encounter", () => { const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText"); await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); + scene.getParty().forEach(pkm => { + vi.spyOn(pkm, "getStat").mockReturnValue(1); // for ease return for every stat + }); + const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; - // Setting enemy's level arbitrarily high to outspeed - config.pokemonConfigs![0].dataSource!.level = 1000; await runMysteryEncounterToEnd(game, 2, undefined, true); @@ -198,10 +200,12 @@ describe("Berries Abound - Mystery Encounter", () => { const encounterTextSpy = vi.spyOn(EncounterDialogueUtils, "showEncounterText"); await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); + scene.getParty().forEach(pkm => { + vi.spyOn(pkm, "getStat").mockReturnValue(1); // for ease return for every stat + }); + const config = game.scene.currentBattle.mysteryEncounter!.enemyPartyConfigs[0]; const speciesToSpawn = config.pokemonConfigs?.[0].species.speciesId; - // Setting enemy's level arbitrarily high to outspeed - config.pokemonConfigs![0].dataSource!.level = 1000; await runMysteryEncounterToEnd(game, 2, undefined, true); diff --git a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts index f8d1ffd3ded..040381c4ac3 100644 --- a/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/the-pokemon-salesman-encounter.test.ts @@ -147,8 +147,8 @@ describe("The Pokemon Salesman - Mystery Encounter", () => { expect(scene.getParty().length).toBe(initialPartySize + 1); - const newlyPurchasedPokemon = scene.getParty().find(p => p.name === pokemonName); - expect(newlyPurchasedPokemon).toBeDefined(); + const newlyPurchasedPokemon = scene.getParty()[scene.getParty().length - 1]; + expect(newlyPurchasedPokemon.name).toBe(pokemonName); expect(newlyPurchasedPokemon!.moveset.length > 0).toBeTruthy(); }); From 2ac688de4bf0d4715a1aa394b3c12162073ae0fe Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Sat, 12 Oct 2024 16:06:26 -0400 Subject: [PATCH 54/70] [Misc] More complete phase logging (#4651) --- src/battle-scene.ts | 6 +++++- src/phase.ts | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 40e3971b7fc..850d0baab5d 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -2316,7 +2316,10 @@ export default class BattleScene extends SceneBase { } } - this.currentPhase?.start(); + if (this.currentPhase) { + console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;"); + this.currentPhase.start(); + } } overridePhase(phase: Phase): boolean { @@ -2326,6 +2329,7 @@ export default class BattleScene extends SceneBase { this.standbyPhase = this.currentPhase; this.currentPhase = phase; + console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;"); phase.start(); return true; diff --git a/src/phase.ts b/src/phase.ts index 02939757112..5cf91f2c478 100644 --- a/src/phase.ts +++ b/src/phase.ts @@ -8,7 +8,6 @@ export class Phase { } start() { - console.log(`%cStart Phase ${this.constructor.name}`, "color:green;"); if (this.scene.abilityBar.shown) { this.scene.abilityBar.resetAutoHideTimer(); } From ebb76129990751aa3e92784c179a838edaaefd96 Mon Sep 17 00:00:00 2001 From: PrabbyDD <147005742+PrabbyDD@users.noreply.github.com> Date: Sat, 12 Oct 2024 15:29:36 -0700 Subject: [PATCH 55/70] [Bug] Stat Stages are now changed individually instead of all at once (#4457) Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- src/phases/stat-stage-change-phase.ts | 10 ++++ src/test/abilities/competitive.test.ts | 72 ++++++++++++++++++++++++++ src/test/abilities/defiant.test.ts | 70 +++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 src/test/abilities/competitive.test.ts create mode 100644 src/test/abilities/defiant.test.ts diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index 4418c38c849..bfe19ea9ca5 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -36,6 +36,16 @@ export class StatStageChangePhase extends PokemonPhase { } start() { + + // Check if multiple stats are being changed at the same time, then run SSCPhase for each of them + if (this.stats.length > 1) { + for (let i = 0; i < this.stats.length; i++) { + const stat = [ this.stats[i] ]; + this.scene.unshiftPhase(new StatStageChangePhase(this.scene, this.battlerIndex, this.selfTarget, stat, this.stages, this.showMessage, this.ignoreAbilities, this.canBeCopied, this.onChange)); + } + return this.end(); + } + const pokemon = this.getPokemon(); if (!pokemon.isActive(true)) { diff --git a/src/test/abilities/competitive.test.ts b/src/test/abilities/competitive.test.ts new file mode 100644 index 00000000000..ecb276a1b8d --- /dev/null +++ b/src/test/abilities/competitive.test.ts @@ -0,0 +1,72 @@ +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Competitive", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override.battleType("single") + .enemySpecies(Species.BEEDRILL) + .enemyMoveset(Moves.TICKLE) + .startingLevel(1) + .moveset([ Moves.SPLASH, Moves.CLOSE_COMBAT ]) + .ability(Abilities.COMPETITIVE); + }); + + it("lower atk and def by 1 via tickle, then increase spatk by 4 via competitive", async () => { + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(4); + }); + + it("lowering your own stats should not trigger competitive", async () => { + game.override.enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.CLOSE_COMBAT); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); + }); + + it("white herb should remove only the negative effects", async () => { + game.override.startingHeldItems([{ name: "WHITE_HERB" }]); + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(4); + }); +}); diff --git a/src/test/abilities/defiant.test.ts b/src/test/abilities/defiant.test.ts new file mode 100644 index 00000000000..aa8d250dad7 --- /dev/null +++ b/src/test/abilities/defiant.test.ts @@ -0,0 +1,70 @@ +import { Stat } from "#enums/stat"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; + +describe("Abilities - Defiant", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + + game.override.battleType("single") + .enemySpecies(Species.BEEDRILL) + .enemyMoveset(Moves.TICKLE) + .startingLevel(1) + .moveset([ Moves.SPLASH, Moves.CLOSE_COMBAT ]) + .ability(Abilities.DEFIANT); + }); + + it("lower atk and def by 1 via tickle, then increase atk by 4 via defiant", async () => { + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(3); + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1); + }); + + it("lowering your own stats should not trigger defiant", async () => { + game.override.enemyMoveset(Moves.SPLASH); + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.CLOSE_COMBAT); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.SPDEF)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(-1); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); + }); + + it("white herb should remove only the negative effects", async () => { + game.override.startingHeldItems([{ name: "WHITE_HERB" }]); + await game.classicMode.startBattle([ Species.FLYGON ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.SPLASH); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(playerPokemon.getStatStage(Stat.DEF)).toBe(0); + expect(playerPokemon.getStatStage(Stat.ATK)).toBe(3); + }); +}); From caf29e2ce3fe5bd194a0a42eac9b91f32b65dc89 Mon Sep 17 00:00:00 2001 From: Tempoanon <163687446+Tempo-anon@users.noreply.github.com> Date: Sat, 12 Oct 2024 23:42:20 -0400 Subject: [PATCH 56/70] [Documentation] Document all (P) moves (#4650) * Document all (P) moves * Fix some typos * Fix more typos * Address innerthunder comments * Add circle throw and dragon tail (P) --- src/data/move.ts | 81 +++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 35 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index bd3706545f9..8e9977337cc 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -365,6 +365,14 @@ export default class Move implements Localizable { return this; } + /** + * Internal dev flag for documenting edge cases. When using this, please document the known edge case. + * @returns the called object {@linkcode Move} + */ + edgeCase(): this { + return this; + } + /** * Marks the move as "partial": appends texts to the move name * @returns the called object {@linkcode Move} @@ -7084,7 +7092,8 @@ export function initMoves() { .attr(ForceSwitchOutAttr) .ignoresSubstitute() .hidesTarget() - .windMove(), + .windMove() + .partial(), // Should force random switches new AttackMove(Moves.FLY, Type.FLYING, MoveCategory.PHYSICAL, 90, 95, 15, -1, 0, 1) .attr(ChargeAttr, ChargeAnim.FLY_CHARGING, i18next.t("moveTriggers:flewUpHigh", { pokemonName: "{USER}" }), BattlerTagType.FLYING) .condition(failOnGravityCondition) @@ -7161,7 +7170,8 @@ export function initMoves() { new StatusMove(Moves.ROAR, Type.NORMAL, -1, 20, -1, -6, 1) .attr(ForceSwitchOutAttr) .soundBased() - .hidesTarget(), + .hidesTarget() + .partial(), // Should force random switching new StatusMove(Moves.SING, Type.NORMAL, 55, 15, -1, 0, 1) .attr(StatusEffectAttr, StatusEffect.SLEEP) .soundBased(), @@ -7302,7 +7312,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), new AttackMove(Moves.QUICK_ATTACK, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 30, -1, 1, 1), new AttackMove(Moves.RAGE, Type.NORMAL, MoveCategory.PHYSICAL, 20, 100, 20, -1, 0, 1) - .partial(), + .partial(), // No effect implemented new SelfStatusMove(Moves.TELEPORT, Type.PSYCHIC, -1, 20, -1, -6, 1) .attr(ForceSwitchOutAttr, true) .hidesUser(), @@ -7617,7 +7627,7 @@ export function initMoves() { new StatusMove(Moves.CHARM, Type.FAIRY, 100, 20, -1, 0, 2) .attr(StatStageChangeAttr, [ Stat.ATK ], -2), new AttackMove(Moves.ROLLOUT, Type.ROCK, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 2) - .partial() + .partial() // Does not lock the user, also does not increase damage properly .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL), new AttackMove(Moves.FALSE_SWIPE, Type.NORMAL, MoveCategory.PHYSICAL, 40, 100, 40, -1, 0, 2) .attr(SurviveDamageAttr), @@ -7688,7 +7698,7 @@ export function initMoves() { .ignoresSubstitute() .condition((user, target, move) => new EncoreTag(user.id).canAdd(target)), new AttackMove(Moves.PURSUIT, Type.DARK, MoveCategory.PHYSICAL, 40, 100, 20, -1, 0, 2) - .partial(), + .partial(), // No effect implemented new AttackMove(Moves.RAPID_SPIN, Type.NORMAL, MoveCategory.PHYSICAL, 50, 100, 40, 100, 0, 2) .attr(StatStageChangeAttr, [ Stat.SPD ], 1, true) .attr(RemoveBattlerTagAttr, [ @@ -7753,7 +7763,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPDEF ], -1) .ballBombMove(), new AttackMove(Moves.FUTURE_SIGHT, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 2) - .partial() + .partial() // Complete buggy mess .attr(DelayedAttackAttr, ArenaTagType.FUTURE_SIGHT, ChargeAnim.FUTURE_SIGHT_CHARGING, i18next.t("moveTriggers:foresawAnAttack", { pokemonName: "{USER}" })), new AttackMove(Moves.ROCK_SMASH, Type.FIGHTING, MoveCategory.PHYSICAL, 40, 100, 15, 50, 0, 2) .attr(StatStageChangeAttr, [ Stat.DEF ], -1), @@ -7771,7 +7781,7 @@ export function initMoves() { .ignoresVirtual() .soundBased() .target(MoveTarget.RANDOM_NEAR_ENEMY) - .partial(), + .partial(), // Does not lock the user, does not stop Pokemon from sleeping new SelfStatusMove(Moves.STOCKPILE, Type.NORMAL, -1, 20, -1, 0, 3) .condition(user => (user.getTag(StockpilingTag)?.stockpiledCount ?? 0) < 3) .attr(AddBattlerTagAttr, BattlerTagType.STOCKPILING, true), @@ -7794,7 +7804,7 @@ export function initMoves() { .target(MoveTarget.BOTH_SIDES), new StatusMove(Moves.TORMENT, Type.DARK, 100, 15, -1, 0, 3) .ignoresSubstitute() - .partial() // Incomplete implementation because of Uproar's partial implementation + .edgeCase() // Incomplete implementation because of Uproar's partial implementation .attr(AddBattlerTagAttr, BattlerTagType.TORMENT, false, true, 1), new StatusMove(Moves.FLATTER, Type.DARK, 100, 15, -1, 0, 3) .attr(StatStageChangeAttr, [ Stat.SPATK ], 1) @@ -7883,7 +7893,7 @@ export function initMoves() { .unimplemented(), new AttackMove(Moves.SECRET_POWER, Type.NORMAL, MoveCategory.PHYSICAL, 70, 100, 20, 30, 0, 3) .makesContact(false) - .partial(), + .partial(), // No effect implemented new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3) .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", { pokemonName: "{USER}" }), BattlerTagType.UNDERWATER, true) .attr(GulpMissileTagAttr) @@ -7914,7 +7924,7 @@ export function initMoves() { .attr(AddArenaTagAttr, ArenaTagType.MUD_SPORT, 5) .target(MoveTarget.BOTH_SIDES), new AttackMove(Moves.ICE_BALL, Type.ICE, MoveCategory.PHYSICAL, 30, 90, 20, -1, 0, 3) - .partial() + .partial() // Does not lock the user properly, does not increase damage correctly .attr(ConsecutiveUseDoublePowerAttr, 5, true, true, Moves.DEFENSE_CURL) .ballBombMove(), new AttackMove(Moves.NEEDLE_ARM, Type.GRASS, MoveCategory.PHYSICAL, 60, 100, 15, 30, 0, 3) @@ -8057,7 +8067,7 @@ export function initMoves() { .attr(ConfuseAttr) .pulseMove(), new AttackMove(Moves.DOOM_DESIRE, Type.STEEL, MoveCategory.SPECIAL, 140, 100, 5, -1, 0, 3) - .partial() + .partial() // Complete buggy mess .attr(DelayedAttackAttr, ArenaTagType.DOOM_DESIRE, ChargeAnim.DOOM_DESIRE_CHARGING, i18next.t("moveTriggers:choseDoomDesireAsDestiny", { pokemonName: "{USER}" })), new AttackMove(Moves.PSYCHO_BOOST, Type.PSYCHIC, MoveCategory.SPECIAL, 140, 90, 5, -1, 0, 3) .attr(StatStageChangeAttr, [ Stat.SPATK ], -2, true), @@ -8467,7 +8477,7 @@ export function initMoves() { .attr(AfterYouAttr), new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) .soundBased() - .partial(), + .partial(), // No effect implemented new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5) .attr(ConsecutiveUseMultiBasePowerAttr, 5, false) .soundBased(), @@ -8509,7 +8519,8 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.ATK ], 1, true) .attr(StatStageChangeAttr, [ Stat.SPD ], 2, true), new AttackMove(Moves.CIRCLE_THROW, Type.FIGHTING, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) - .attr(ForceSwitchOutAttr), + .attr(ForceSwitchOutAttr) + .partial(), // Should force random switches new AttackMove(Moves.INCINERATE, Type.FIRE, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5) .target(MoveTarget.ALL_NEAR_ENEMIES) .attr(RemoveHeldItemAttr, true), @@ -8577,7 +8588,8 @@ export function initMoves() { .attr(CritOnlyAttr), new AttackMove(Moves.DRAGON_TAIL, Type.DRAGON, MoveCategory.PHYSICAL, 60, 90, 10, -1, -6, 5) .attr(ForceSwitchOutAttr) - .hidesTarget(), + .hidesTarget() + .partial(), // Should force random switches new SelfStatusMove(Moves.WORK_UP, Type.NORMAL, -1, 30, -1, 0, 5) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, true), new AttackMove(Moves.ELECTROWEB, Type.ELECTRIC, MoveCategory.SPECIAL, 55, 95, 15, 100, 0, 5) @@ -8705,7 +8717,7 @@ export function initMoves() { .ignoresVirtual(), new StatusMove(Moves.TRICK_OR_TREAT, Type.GHOST, 100, 20, -1, 0, 6) .attr(AddTypeAttr, Type.GHOST) - .partial(), + .edgeCase(), // Weird interaction with Forest's Curse, reflect type, burn up new StatusMove(Moves.NOBLE_ROAR, Type.NORMAL, 100, 30, -1, 0, 6) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1) .soundBased(), @@ -8718,7 +8730,7 @@ export function initMoves() { .triageMove(), new StatusMove(Moves.FORESTS_CURSE, Type.GRASS, 100, 20, -1, 0, 6) .attr(AddTypeAttr, Type.GRASS) - .partial(), + .edgeCase(), // Weird interaction with Trick or Treat, reflect type, burn up new AttackMove(Moves.PETAL_BLIZZARD, Type.GRASS, MoveCategory.PHYSICAL, 90, 100, 15, -1, 0, 6) .windMove() .makesContact(false) @@ -8726,7 +8738,7 @@ export function initMoves() { new AttackMove(Moves.FREEZE_DRY, Type.ICE, MoveCategory.SPECIAL, 70, 100, 20, 10, 0, 6) .attr(StatusEffectAttr, StatusEffect.FREEZE) .attr(WaterSuperEffectTypeMultiplierAttr) - .partial(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell. + .edgeCase(), // This currently just multiplies the move's power instead of changing its effectiveness. It also doesn't account for abilities that modify type effectiveness such as tera shell. new AttackMove(Moves.DISARMING_VOICE, Type.FAIRY, MoveCategory.SPECIAL, 40, -1, 15, -1, 0, 6) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES), @@ -9104,15 +9116,15 @@ export function initMoves() { /* Unused */ new AttackMove(Moves.SINISTER_ARROW_RAID, Type.GHOST, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7) .makesContact(false) - .partial() + .edgeCase() // I assume it's because the user needs spirit shackle and decidueye .ignoresVirtual(), new AttackMove(Moves.MALICIOUS_MOONSAULT, Type.DARK, MoveCategory.PHYSICAL, 180, -1, 1, -1, 0, 7) .attr(AlwaysHitMinimizeAttr) .attr(HitsTagAttr, BattlerTagType.MINIMIZED, true) - .partial() + .edgeCase() // I assume it's because it needs darkest lariat and incineroar .ignoresVirtual(), new AttackMove(Moves.OCEANIC_OPERETTA, Type.WATER, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) - .partial() + .edgeCase() // I assume it's because it needs sparkling aria and primarina .ignoresVirtual(), new AttackMove(Moves.GUARDIAN_OF_ALOLA, Type.FAIRY, MoveCategory.SPECIAL, -1, -1, 1, -1, 0, 7) .unimplemented() @@ -9121,10 +9133,10 @@ export function initMoves() { .unimplemented() .ignoresVirtual(), new AttackMove(Moves.STOKED_SPARKSURFER, Type.ELECTRIC, MoveCategory.SPECIAL, 175, -1, 1, 100, 0, 7) - .partial() + .edgeCase() // I assume it's because it needs thunderbolt and Alola Raichu .ignoresVirtual(), new AttackMove(Moves.PULVERIZING_PANCAKE, Type.NORMAL, MoveCategory.PHYSICAL, 210, -1, 1, -1, 0, 7) - .partial() + .edgeCase() // I assume it's because it needs giga impact and snorlax .ignoresVirtual(), new SelfStatusMove(Moves.EXTREME_EVOBOOST, Type.NORMAL, -1, 1, -1, 0, 7) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 2, true) @@ -9155,13 +9167,13 @@ export function initMoves() { .attr(RechargeAttr), new AttackMove(Moves.SPECTRAL_THIEF, Type.GHOST, MoveCategory.PHYSICAL, 90, 100, 10, -1, 0, 7) .ignoresSubstitute() - .partial(), + .partial(), // Does not steal stats new AttackMove(Moves.SUNSTEEL_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 7) .ignoresAbilities() - .partial(), + .edgeCase(), // Should not ignore abilities when called virtually (metronome) new AttackMove(Moves.MOONGEIST_BEAM, Type.GHOST, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) .ignoresAbilities() - .partial(), + .edgeCase(), // Should not ignore abilities when called virtually (metronome) new StatusMove(Moves.TEARFUL_LOOK, Type.NORMAL, -1, 20, -1, 0, 7) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], -1), new AttackMove(Moves.ZING_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 80, 100, 10, 30, 0, 7) @@ -9172,7 +9184,7 @@ export function initMoves() { .attr(FormChangeItemTypeAttr), /* Unused */ new AttackMove(Moves.TEN_MILLION_VOLT_THUNDERBOLT, Type.ELECTRIC, MoveCategory.SPECIAL, 195, -1, 1, -1, 0, 7) - .partial() + .edgeCase() // I assume it's because it needs thunderbolt and pikachu in a cap .ignoresVirtual(), /* End Unused */ new AttackMove(Moves.MIND_BLOWN, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 7) @@ -9185,7 +9197,7 @@ export function initMoves() { new AttackMove(Moves.PHOTON_GEYSER, Type.PSYCHIC, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 7) .attr(PhotonGeyserCategoryAttr) .ignoresAbilities() - .partial(), + .edgeCase(), // Should not ignore abilities when called virtually (metronome) /* Unused */ new AttackMove(Moves.LIGHT_THAT_BURNS_THE_SKY, Type.PSYCHIC, MoveCategory.SPECIAL, 200, -1, 1, -1, 0, 7) .attr(PhotonGeyserCategoryAttr) @@ -9198,7 +9210,7 @@ export function initMoves() { .ignoresAbilities() .ignoresVirtual(), new AttackMove(Moves.LETS_SNUGGLE_FOREVER, Type.FAIRY, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7) - .partial() + .edgeCase() // I assume it needs play rough and mimikyu .ignoresVirtual(), new AttackMove(Moves.SPLINTERED_STORMSHARDS, Type.ROCK, MoveCategory.PHYSICAL, 190, -1, 1, -1, 0, 7) .attr(ClearTerrainAttr) @@ -9208,7 +9220,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true, undefined, undefined, undefined, undefined, true) .soundBased() .target(MoveTarget.ALL_NEAR_ENEMIES) - .partial() + .edgeCase() // I assume it needs clanging scales and Kommo-O .ignoresVirtual(), /* End Unused */ new AttackMove(Moves.ZIPPY_ZAP, Type.ELECTRIC, MoveCategory.PHYSICAL, 50, 100, 15, -1, 2, 7) //LGPE Implementation @@ -9271,14 +9283,14 @@ export function initMoves() { new AttackMove(Moves.JAW_LOCK, Type.DARK, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 8) .attr(JawLockAttr) .bitingMove(), - new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) // TODO: Stuff Cheeks should not be selectable when the user does not have a berry, see wiki + new SelfStatusMove(Moves.STUFF_CHEEKS, Type.NORMAL, -1, 10, -1, 0, 8) .attr(EatBerryAttr) .attr(StatStageChangeAttr, [ Stat.DEF ], 2, true) .condition((user) => { const userBerries = user.scene.findModifiers(m => m instanceof BerryModifier, user.isPlayer()); return userBerries.length > 0; }) - .partial(), + .edgeCase(), // Stuff Cheeks should not be selectable when the user does not have a berry, see wiki new SelfStatusMove(Moves.NO_RETREAT, Type.FIGHTING, -1, 5, -1, 0, 8) .attr(StatStageChangeAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD ], 1, true) .attr(AddBattlerTagAttr, BattlerTagType.NO_RETREAT, true, false) @@ -9292,7 +9304,7 @@ export function initMoves() { new AttackMove(Moves.DRAGON_DARTS, Type.DRAGON, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 8) .attr(MultiHitAttr, MultiHitType._2) .makesContact(false) - .partial(), + .partial(), // smart targetting is unimplemented new StatusMove(Moves.TEATIME, Type.NORMAL, -1, 10, -1, 0, 8) .attr(EatBerryAttr) .target(MoveTarget.ALL), @@ -9731,7 +9743,7 @@ export function initMoves() { .attr(StatStageChangeAttr, [ Stat.SPDEF ], -2), new AttackMove(Moves.ORDER_UP, Type.DRAGON, MoveCategory.PHYSICAL, 80, 100, 10, 100, 0, 9) .makesContact(false) - .partial(), + .partial(), // No effect implemented (requires Commander) new AttackMove(Moves.JET_PUNCH, Type.WATER, MoveCategory.PHYSICAL, 60, 100, 15, -1, 1, 9) .punchingMove(), new StatusMove(Moves.SPICY_EXTRACT, Type.GRASS, -1, 15, -1, 0, 9) @@ -9943,8 +9955,7 @@ export function initMoves() { new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) .attr(FlinchAttr) .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].priority > 0 ) // TODO: is this bang correct? - //TODO: Should also apply when target move priority increased by ability ex. gale wings - .partial(), + .partial(), // Should also apply when target move priority increased by ability ex. gale wings new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) .attr(StatusEffectAttr, StatusEffect.TOXIC) ); From 8e7aea0f8990450d5d06e273f83188fffa87857e Mon Sep 17 00:00:00 2001 From: damocleas Date: Sat, 12 Oct 2024 23:51:14 -0400 Subject: [PATCH 57/70] Fixed Charizard and Kingler BST, fixed ability indexing for gmax forms (#4652) --- src/data/pokemon-species.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 1ceb5971f6a..8bb23cfc208 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -975,7 +975,7 @@ export function initSpecies() { new PokemonSpecies(Species.VENUSAUR, 1, false, false, false, "Seed Pokémon", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, GrowthRate.MEDIUM_SLOW, 87.5, true, true, new PokemonForm("Normal", "", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, true, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GRASS, Type.POISON, 2.4, 155.5, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 625, 80, 100, 123, 122, 120, 80, 45, 50, 263, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.EFFECT_SPORE, Abilities.EFFECT_SPORE, Abilities.EFFECT_SPORE, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.EFFECT_SPORE, Abilities.NONE, Abilities.EFFECT_SPORE, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true), ), new PokemonSpecies(Species.CHARMANDER, 1, false, false, false, "Lizard Pokémon", Type.FIRE, null, 0.6, 8.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 309, 39, 52, 43, 60, 50, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.CHARMELEON, 1, false, false, false, "Flame Pokémon", Type.FIRE, null, 1.1, 19, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 405, 58, 64, 58, 80, 65, 80, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false), @@ -983,20 +983,20 @@ export function initSpecies() { new PokemonForm("Normal", "", Type.FIRE, Type.FLYING, 1.7, 90.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 534, 78, 84, 78, 109, 85, 100, 45, 50, 267, false, null, true), new PokemonForm("Mega X", SpeciesFormKey.MEGA_X, Type.FIRE, Type.DRAGON, 1.7, 110.5, Abilities.TOUGH_CLAWS, Abilities.NONE, Abilities.TOUGH_CLAWS, 634, 78, 130, 111, 130, 85, 100, 45, 50, 267), new PokemonForm("Mega Y", SpeciesFormKey.MEGA_Y, Type.FIRE, Type.FLYING, 1.7, 100.5, Abilities.DROUGHT, Abilities.NONE, Abilities.DROUGHT, 634, 78, 104, 78, 159, 115, 100, 45, 50, 267), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.BERSERK, Abilities.BERSERK, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.NONE, Abilities.BERSERK, 634, 118, 84, 93, 139, 100, 100, 45, 50, 267), ), new PokemonSpecies(Species.SQUIRTLE, 1, false, false, false, "Tiny Turtle Pokémon", Type.WATER, null, 0.5, 9, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 314, 44, 48, 65, 50, 64, 43, 45, 50, 63, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.WARTORTLE, 1, false, false, false, "Turtle Pokémon", Type.WATER, null, 1, 22.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 405, 59, 63, 80, 65, 80, 58, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.BLASTOISE, 1, false, false, false, "Shellfish Pokémon", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.WATER, null, 1.6, 101.1, Abilities.MEGA_LAUNCHER, Abilities.NONE, Abilities.MEGA_LAUNCHER, 630, 79, 103, 120, 135, 115, 78, 45, 50, 265), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 135, 115, 110, 68, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.NONE, Abilities.SHELL_ARMOR, 630, 119, 83, 135, 115, 110, 68, 45, 50, 265), ), new PokemonSpecies(Species.CATERPIE, 1, false, false, false, "Worm Pokémon", Type.BUG, null, 0.3, 2.9, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 45, 30, 35, 20, 20, 45, 255, 50, 39, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true, new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true), ), new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false), @@ -1025,7 +1025,7 @@ export function initSpecies() { new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form ), new PokemonSpecies(Species.RAICHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.8, 30, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 485, 60, 90, 55, 90, 80, 110, 75, 50, 243, GrowthRate.MEDIUM_FAST, 50, true), new PokemonSpecies(Species.SANDSHREW, 1, false, false, false, "Mouse Pokémon", Type.GROUND, null, 0.6, 12, Abilities.SAND_VEIL, Abilities.NONE, Abilities.SAND_RUSH, 300, 50, 75, 85, 20, 30, 40, 255, 50, 60, GrowthRate.MEDIUM_FAST, 50, false), @@ -1110,7 +1110,7 @@ export function initSpecies() { new PokemonSpecies(Species.GENGAR, 1, false, false, false, "Shadow Pokémon", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, GrowthRate.MEDIUM_SLOW, 50, false, true, new PokemonForm("Normal", "", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, false, null, true), new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GHOST, Type.POISON, 1.4, 40.5, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.NONE, 600, 60, 65, 80, 170, 95, 130, 45, 50, 250), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250), ), new PokemonSpecies(Species.ONIX, 1, false, false, false, "Rock Snake Pokémon", Type.ROCK, Type.GROUND, 8.8, 210, Abilities.ROCK_HEAD, Abilities.STURDY, Abilities.WEAK_ARMOR, 385, 35, 45, 160, 30, 45, 70, 45, 50, 77, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.DROWZEE, 1, false, false, false, "Hypnosis Pokémon", Type.PSYCHIC, null, 1, 32.4, Abilities.INSOMNIA, Abilities.FOREWARN, Abilities.INNER_FOCUS, 328, 60, 48, 45, 43, 90, 42, 190, 70, 66, GrowthRate.MEDIUM_FAST, 50, false), @@ -1118,7 +1118,7 @@ export function initSpecies() { new PokemonSpecies(Species.KRABBY, 1, false, false, false, "River Crab Pokémon", Type.WATER, null, 0.4, 6.5, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 325, 30, 105, 90, 25, 25, 50, 225, 50, 65, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.KINGLER, 1, false, false, false, "Pincer Pokémon", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 70, 70, 60, 50, 166), ), new PokemonSpecies(Species.VOLTORB, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 0.5, 10.4, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 330, 40, 30, 50, 55, 55, 100, 190, 70, 66, GrowthRate.MEDIUM_FAST, null, false), new PokemonSpecies(Species.ELECTRODE, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 1.2, 66.6, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 490, 60, 50, 70, 80, 80, 150, 60, 70, 172, GrowthRate.MEDIUM_FAST, null, false), @@ -2298,25 +2298,25 @@ export function initSpecies() { new PokemonSpecies(Species.MELTAN, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 0.2, 8, Abilities.MAGNET_PULL, Abilities.NONE, Abilities.NONE, 300, 46, 65, 65, 55, 35, 34, 3, 0, 150, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.MELMETAL, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, GrowthRate.SLOW, null, false, true, new PokemonForm("Normal", "", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300), ), new PokemonSpecies(Species.GROOKEY, 8, false, false, false, "Chimp Pokémon", Type.GRASS, null, 0.3, 5, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 310, 50, 65, 50, 40, 40, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.NONE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265), ), new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.NONE, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265), ), new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false), new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true, new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.NONE, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265), ), new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false), @@ -2422,7 +2422,7 @@ export function initSpecies() { new PokemonForm("Ruby Swirl", "ruby-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), new PokemonForm("Caramel Swirl", "caramel-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), new PokemonForm("Rainbow Swirl", "rainbow-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.NONE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173), ), new PokemonSpecies(Species.FALINKS, 8, false, false, false, "Formation Pokémon", Type.FIGHTING, null, 3, 62, Abilities.BATTLE_ARMOR, Abilities.NONE, Abilities.DEFIANT, 470, 65, 100, 100, 70, 60, 75, 45, 50, 165, GrowthRate.MEDIUM_FAST, null, false), new PokemonSpecies(Species.PINCURCHIN, 8, false, false, false, "Sea Urchin Pokémon", Type.ELECTRIC, null, 0.3, 1, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.ELECTRIC_SURGE, 435, 48, 101, 95, 91, 85, 15, 75, 50, 152, GrowthRate.MEDIUM_FAST, 50, false), @@ -2444,7 +2444,7 @@ export function initSpecies() { new PokemonSpecies(Species.CUFANT, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 1.2, 100, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 330, 72, 80, 49, 40, 49, 40, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false), new PokemonSpecies(Species.COPPERAJAH, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, true, new PokemonForm("Normal", "", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, false, null, true), - new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175), + new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.NONE, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175), ), new PokemonSpecies(Species.DRACOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.DRAGON, 1.8, 190, Abilities.VOLT_ABSORB, Abilities.HUSTLE, Abilities.SAND_RUSH, 505, 90, 100, 90, 80, 70, 75, 45, 50, 177, GrowthRate.SLOW, null, false), new PokemonSpecies(Species.ARCTOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.ICE, 2.3, 150, Abilities.VOLT_ABSORB, Abilities.STATIC, Abilities.SLUSH_RUSH, 505, 90, 100, 90, 90, 80, 55, 45, 50, 177, GrowthRate.SLOW, null, false), From 391f38c3c8bd6106f7c38f4a451883add3bac310 Mon Sep 17 00:00:00 2001 From: Tempoanon <163687446+Tempo-anon@users.noreply.github.com> Date: Sun, 13 Oct 2024 00:45:38 -0400 Subject: [PATCH 58/70] [Documentation] Document all (P) abilities (#4649) * Document partial abilities * Fix typo * Address comments * Fix typo Terapagos -> Ogerpon --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/ability.ts | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 6a391818866..07fd48e2f91 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -118,6 +118,14 @@ export class Ability implements Localizable { this.nameAppend += " (N)"; return this; } + + /** + * Internal flag used for developers to document edge cases. When using this, please be sure to document the edge case. + * @returns the ability + */ + edgeCase(): this { + return this; + } } type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => boolean | Promise; @@ -4906,7 +4914,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.SHIELD_DUST, 3) .attr(IgnoreMoveEffectsAbAttr) - .partial(), + .edgeCase(), // Does not work with secret power (unimplemented) new Ability(Abilities.OWN_TEMPO, 3) .attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED) .attr(IntimidateImmunityAbAttr) @@ -4951,7 +4959,7 @@ export function initAbilities() { .ignorable(), new Ability(Abilities.SERENE_GRACE, 3) .attr(MoveEffectChanceMultiplierAbAttr, 2) - .partial(), + .edgeCase(), // does not work with secret power (unimplemented) new Ability(Abilities.SWIFT_SWIM, 3) .attr(StatMultiplierAbAttr, Stat.SPD, 2) .condition(getWeatherCondition(WeatherType.RAIN, WeatherType.HEAVY_RAIN)), @@ -5235,7 +5243,8 @@ export function initAbilities() { new Ability(Abilities.SHEER_FORCE, 5) .attr(MovePowerBoostAbAttr, (user, target, move) => move.chance >= 1, 5461 / 4096) .attr(MoveEffectChanceMultiplierAbAttr, 0) - .partial(), + .edgeCase() // Should disable shell bell and Meloetta's relic song transformation + .edgeCase(), // Should disable life orb, eject button, red card, kee/maranga berry if they get implemented new Ability(Abilities.CONTRARY, 5) .attr(StatStageChangeMultiplierAbAttr, -1) .ignorable(), @@ -5278,7 +5287,7 @@ export function initAbilities() { /** Rate is doubled when under sun {@link https://dex.pokemonshowdown.com/abilities/harvest} */ (pokemon) => 0.5 * (getWeatherCondition(WeatherType.SUNNY, WeatherType.HARSH_SUN)(pokemon) ? 2 : 1) ) - .partial(), + .edgeCase(), // Cannot recover berries used up by fling or natural gift (unimplemented) new Ability(Abilities.TELEPATHY, 5) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon.getAlly() === attacker && move instanceof AttackMove) .ignorable(), @@ -5357,7 +5366,7 @@ export function initAbilities() { .bypassFaint(), new Ability(Abilities.VICTORY_STAR, 5) .attr(StatMultiplierAbAttr, Stat.ACC, 1.1) - .partial(), + .partial(), // Does not boost ally's accuracy new Ability(Abilities.TURBOBLAZE, 5) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTurboblaze", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) .attr(MoveAbilityBypassAbAttr), @@ -5468,7 +5477,7 @@ export function initAbilities() { .attr(UnsuppressableAbilityAbAttr) .attr(NoFusionAbilityAbAttr) .bypassFaint() - .partial(), + .partial(), // Meteor form should protect against status effects and yawn new Ability(Abilities.STAKEOUT, 7) .attr(MovePowerBoostAbAttr, (user, target, move) => user?.scene.currentBattle.turnCommands[target?.getBattlerIndex() ?? BattlerIndex.ATTACKER]?.command === Command.POKEMON, 2), new Ability(Abilities.WATER_BUBBLE, 7) @@ -5536,9 +5545,9 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) .bypassFaint() .partial(), - new Ability(Abilities.CORROSION, 7) // TODO: Test Corrosion against Magic Bounce once it is implemented + new Ability(Abilities.CORROSION, 7) .attr(IgnoreTypeStatusEffectImmunityAbAttr, [ StatusEffect.POISON, StatusEffect.TOXIC ], [ Type.STEEL, Type.POISON ]) - .partial(), + .edgeCase(), // Should interact correctly with magic coat/bounce (not yet implemented), fling with toxic orb (not implemented yet), and synchronize (not fully implemented yet) new Ability(Abilities.COMATOSE, 7) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) @@ -5693,7 +5702,7 @@ export function initAbilities() { new Ability(Abilities.WANDERING_SPIRIT, 8) .attr(PostDefendAbilitySwapAbAttr) .bypassFaint() - .partial(), + .edgeCase(), // interacts incorrectly with rock head. It's meant to switch abilities before recoil would apply so that a pokemon with rock head would lose rock head first and still take the recoil new Ability(Abilities.GORILLA_TACTICS, 8) .attr(GorillaTacticsAbAttr), new Ability(Abilities.NEUTRALIZING_GAS, 8) @@ -5702,7 +5711,7 @@ export function initAbilities() { .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) - .partial(), + .partial(), // A bunch of weird interactions with other abilities being suppressed then unsuppressed new Ability(Abilities.PASTEL_VEIL, 8) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) @@ -5807,7 +5816,7 @@ export function initAbilities() { new Ability(Abilities.GOOD_AS_GOLD, 9) .attr(MoveImmunityAbAttr, (pokemon, attacker, move) => pokemon !== attacker && move.category === MoveCategory.STATUS) .ignorable() - .partial(), + .partial(), // Lots of weird interactions with moves and abilities such as negating status moves that target the field new Ability(Abilities.VESSEL_OF_RUIN, 9) .attr(FieldMultiplyStatAbAttr, Stat.SPATK, 0.75) .attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonVesselOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPATK)) })) @@ -5840,7 +5849,7 @@ export function initAbilities() { .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), new Ability(Abilities.SUPREME_OVERLORD, 9) .attr(VariableMovePowerBoostAbAttr, (user, target, move) => 1 + 0.1 * Math.min(user.isPlayer() ? user.scene.currentBattle.playerFaints : user.scene.currentBattle.enemyFaints, 5)) - .partial(), + .partial(), // Counter resets every wave new Ability(Abilities.COSTAR, 9) .attr(PostSummonCopyAllyStatsAbAttr), new Ability(Abilities.TOXIC_DEBRIS, 9) @@ -5873,25 +5882,25 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) - .partial(), + .partial(), // Ogerpon tera interactions new Ability(Abilities.EMBODY_ASPECT_WELLSPRING, 9) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.SPDEF ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) - .partial(), + .partial(), // Ogerpon tera interactions new Ability(Abilities.EMBODY_ASPECT_HEARTHFLAME, 9) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.ATK ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) - .partial(), + .partial(), // Ogerpon tera interactions new Ability(Abilities.EMBODY_ASPECT_CORNERSTONE, 9) .attr(PostBattleInitStatStageChangeAbAttr, [ Stat.DEF ], 1, true) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) - .partial(), + .partial(), // Ogerpon tera interactions new Ability(Abilities.TERA_SHIFT, 9) .attr(PostSummonFormChangeAbAttr, p => p.getFormKey() ? 0 : 1) .attr(UncopiableAbilityAbAttr) From 470f9e4e19276ec341736df795949743acbcee71 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Sat, 12 Oct 2024 21:46:41 -0700 Subject: [PATCH 59/70] [P3] Fix Substitute visual error on wave transition (#4648) --- src/field/pokemon.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index c495e0833cd..136c1eb1685 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -749,9 +749,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const relX = newOffset[0] - initialOffset[0]; const relY = newOffset[1] - initialOffset[1]; + const subTag = this.getTag(SubstituteTag); + if (duration) { + // TODO: can this use stricter typing? + const targets: any[] = [ this ]; + if (subTag?.sprite) { + targets.push(subTag.sprite); + } this.scene.tweens.add({ - targets: this, + targets: targets, x: (_target, _key, value: number) => value + relX, y: (_target, _key, value: number) => value + relY, duration: duration, @@ -761,6 +768,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } else { this.x += relX; this.y += relY; + if (subTag?.sprite) { + subTag.sprite.x += relX; + subTag.sprite.y += relY; + } } }); } From 8a355d500a27ca8c74482665a8680528007ab727 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 13 Oct 2024 00:30:04 -0700 Subject: [PATCH 60/70] [Bug] Move Restriction Battler Tag bugs (#4536) * Added fixes * Revert "Added fixes" This reverts commit 3feccd792ddef0b32ddc40782b60f23f952cad23. * Added loadTag functions * Fixes * typeodcs * Torment * yawn * hsldklahdlhalhdlahldhlah * Imprison Fixes * Fixed imprison not interrupting PRE_MOVE * just kidding * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Fixing what broke * added scp[es * missed a scope * Update src/data/battler-tags.ts Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * good tp go * merge * battler tags * Apply suggestions from code review * Changed function names * publics --------- Co-authored-by: frutescens Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/arena-tag.ts | 68 ++++++++++++++++++++------------- src/data/battler-tags.ts | 75 ++++++++++++++++++++++++------------- src/data/move.ts | 3 +- src/field/pokemon.ts | 8 ++-- src/phases/command-phase.ts | 4 +- 5 files changed, 99 insertions(+), 59 deletions(-) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 11d28ab7e25..71cf11fa06f 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -4,7 +4,7 @@ import { Type } from "#app/data/type"; import * as Utils from "#app/utils"; import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "#app/data/move"; import { getPokemonNameWithAffix } from "#app/messages"; -import Pokemon, { HitResult, PlayerPokemon, PokemonMove, EnemyPokemon } from "#app/field/pokemon"; +import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon"; import { StatusEffect } from "#app/data/status-effect"; import { BattlerIndex } from "#app/battle"; import { BlockNonDirectDamageAbAttr, ChangeMovePriorityAbAttr, ProtectStatAbAttr, applyAbAttrs } from "#app/data/ability"; @@ -71,6 +71,32 @@ export abstract class ArenaTag { this.sourceId = source.sourceId; this.side = source.side; } + + /** + * Helper function that retrieves the source Pokemon + * @param scene medium to retrieve the source Pokemon + * @returns The source {@linkcode Pokemon} or `null` if none is found + */ + public getSourcePokemon(scene: BattleScene): Pokemon | null { + return this.sourceId ? scene.getPokemonById(this.sourceId) : null; + } + + /** + * Helper function that retrieves the Pokemon affected + * @param scene - medium to retrieve the involved Pokemon + * @returns list of PlayerPokemon or EnemyPokemon on the field + */ + public getAffectedPokemon(scene: BattleScene): Pokemon[] { + switch (this.side) { + case ArenaTagSide.PLAYER: + return scene.getPlayerField() ?? []; + case ArenaTagSide.ENEMY: + return scene.getEnemyField() ?? []; + case ArenaTagSide.BOTH: + default: + return scene.getField(true) ?? []; + } + } } /** @@ -978,36 +1004,24 @@ class NoneTag extends ArenaTag { * Imprison will apply to any opposing Pokemon that switch onto the field as well. */ class ImprisonTag extends ArenaTrapTag { - private source: Pokemon; - constructor(sourceId: number, side: ArenaTagSide) { super(ArenaTagType.IMPRISON, Moves.IMPRISON, sourceId, side, 1); } - /** - * Helper function that retrieves the Pokemon affected - * @param {BattleScene} scene medium to retrieve the involved Pokemon - * @returns list of PlayerPokemon or EnemyPokemon on the field - */ - private retrieveField(scene: BattleScene): PlayerPokemon[] | EnemyPokemon[] { - if (!this.source.isPlayer()) { - return scene.getPlayerField() ?? []; - } - return scene.getEnemyField() ?? []; - } - /** * This function applies the effects of Imprison to the opposing Pokemon already present on the field. * @param arena */ override onAdd({ scene }: Arena) { - this.source = scene.getPokemonById(this.sourceId!)!; - if (this.source) { - const party = this.retrieveField(scene); - party?.forEach((p: PlayerPokemon | EnemyPokemon ) => { - p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); + const source = this.getSourcePokemon(scene); + if (source) { + const party = this.getAffectedPokemon(scene); + party?.forEach((p: Pokemon ) => { + if (p.isAllowedInBattle()) { + p.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); + } }); - scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(this.source) })); + scene.queueMessage(i18next.t("battlerTags:imprisonOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); } } @@ -1016,8 +1030,9 @@ class ImprisonTag extends ArenaTrapTag { * @param _arena * @returns `true` if the source of the tag is still active on the field | `false` if not */ - override lapse(_arena: Arena): boolean { - return this.source.isActive(true); + override lapse({ scene }: Arena): boolean { + const source = this.getSourcePokemon(scene); + return source ? source.isActive(true) : false; } /** @@ -1026,7 +1041,8 @@ class ImprisonTag extends ArenaTrapTag { * @returns `true` */ override activateTrap(pokemon: Pokemon): boolean { - if (this.source.isActive(true)) { + const source = this.getSourcePokemon(pokemon.scene); + if (source && source.isActive(true) && pokemon.isAllowedInBattle()) { pokemon.addTag(BattlerTagType.IMPRISON, 1, Moves.IMPRISON, this.sourceId); } return true; @@ -1037,8 +1053,8 @@ class ImprisonTag extends ArenaTrapTag { * @param arena */ override onRemove({ scene }: Arena): void { - const party = this.retrieveField(scene); - party?.forEach((p: PlayerPokemon | EnemyPokemon) => { + const party = this.getAffectedPokemon(scene); + party?.forEach((p: Pokemon) => { p.removeTag(BattlerTagType.IMPRISON); }); } diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 18f03ada941..a5016746013 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -23,6 +23,7 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { StatStageChangePhase, StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import { PokemonAnimType } from "#app/enums/pokemon-anim-type"; +import BattleScene from "#app/battle-scene"; export enum BattlerTagLapseType { FAINT, @@ -90,6 +91,15 @@ export class BattlerTag { this.sourceMove = source.sourceMove; this.sourceId = source.sourceId; } + + /** + * Helper function that retrieves the source Pokemon object + * @param scene medium to retrieve the source Pokemon + * @returns The source {@linkcode Pokemon} or `null` if none is found + */ + public getSourcePokemon(scene: BattleScene): Pokemon | null { + return this.sourceId ? scene.getPokemonById(this.sourceId) : null; + } } export interface WeatherBattlerTag { @@ -120,7 +130,7 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag { const phase = pokemon.scene.getCurrentPhase() as MovePhase; const move = phase.move; - if (this.isMoveRestricted(move.moveId)) { + if (this.isMoveRestricted(move.moveId, pokemon)) { if (this.interruptedText(pokemon, move.moveId)) { pokemon.scene.queueMessage(this.interruptedText(pokemon, move.moveId)); } @@ -136,10 +146,11 @@ export abstract class MoveRestrictionBattlerTag extends BattlerTag { /** * Gets whether this tag is restricting a move. * - * @param {Moves} move {@linkcode Moves} ID to check restriction for. - * @returns {boolean} `true` if the move is restricted by this tag, otherwise `false`. + * @param move - {@linkcode Moves} ID to check restriction for. + * @param user - The {@linkcode Pokemon} involved + * @returns `true` if the move is restricted by this tag, otherwise `false`. */ - abstract isMoveRestricted(move: Moves): boolean; + public abstract isMoveRestricted(move: Moves, user?: Pokemon): boolean; /** * Checks if this tag is restricting a move based on a user's decisions during the target selection phase @@ -327,6 +338,16 @@ export class GorillaTacticsTag extends MoveRestrictionBattlerTag { pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.ATK, false) * 1.5, false); } + /** + * Loads the Gorilla Tactics Battler Tag along with its unique class variable moveId + * @override + * @param source Gorilla Tactics' {@linkcode BattlerTag} information + */ + public override loadTag(source: BattlerTag | any): void { + super.loadTag(source); + this.moveId = source.moveId; + } + /** * * @override @@ -2510,8 +2531,6 @@ export class MysteryEncounterPostSummonTag extends BattlerTag { * Torment does not interrupt the move if the move is performed consecutively in the same turn and right after Torment is applied */ export class TormentTag extends MoveRestrictionBattlerTag { - private target: Pokemon; - constructor(sourceId: number) { super(BattlerTagType.TORMENT, BattlerTagLapseType.AFTER_MOVE, 1, Moves.TORMENT, sourceId); } @@ -2523,7 +2542,6 @@ export class TormentTag extends MoveRestrictionBattlerTag { */ override onAdd(pokemon: Pokemon) { super.onAdd(pokemon); - this.target = pokemon; pokemon.scene.queueMessage(i18next.t("battlerTags:tormentOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), 1500); } @@ -2542,15 +2560,18 @@ export class TormentTag extends MoveRestrictionBattlerTag { * @param {Moves} move the move under investigation * @returns `true` if there is valid consecutive usage | `false` if the moves are different from each other */ - override isMoveRestricted(move: Moves): boolean { - const lastMove = this.target.getLastXMoves(1)[0]; + public override isMoveRestricted(move: Moves, user: Pokemon): boolean { + if (!user) { + return false; + } + const lastMove = user.getLastXMoves(1)[0]; if ( !lastMove ) { return false; } // This checks for locking / momentum moves like Rollout and Hydro Cannon + if the user is under the influence of BattlerTagType.FRENZY // Because Uproar's unique behavior is not implemented, it does not check for Uproar. Torment has been marked as partial in moves.ts const moveObj = allMoves[lastMove.move]; - const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || this.target.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr); + const isUnaffected = moveObj.hasAttr(ConsecutiveUseDoublePowerAttr) || user.getTag(BattlerTagType.FRENZY) || moveObj.hasAttr(ChargeAttr); const validLastMoveResult = (lastMove.result === MoveResult.SUCCESS) || (lastMove.result === MoveResult.MISS); if (lastMove.move === move && validLastMoveResult && lastMove.move !== Moves.STRUGGLE && !isUnaffected) { return true; @@ -2602,37 +2623,39 @@ export class TauntTag extends MoveRestrictionBattlerTag { * The tag is only removed when the source-user is removed from the field. */ export class ImprisonTag extends MoveRestrictionBattlerTag { - private source: Pokemon | null; - constructor(sourceId: number) { super(BattlerTagType.IMPRISON, [ BattlerTagLapseType.PRE_MOVE, BattlerTagLapseType.AFTER_MOVE ], 1, Moves.IMPRISON, sourceId); } - override onAdd(pokemon: Pokemon) { - if (this.sourceId) { - this.source = pokemon.scene.getPokemonById(this.sourceId); - } - } - /** * Checks if the source of Imprison is still active - * @param _pokemon - * @param _lapseType + * @override + * @param pokemon The pokemon this tag is attached to * @returns `true` if the source is still active */ - override lapse(_pokemon: Pokemon, _lapseType: BattlerTagLapseType): boolean { - return this.source?.isActive(true) ?? false; + public override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { + const source = this.getSourcePokemon(pokemon.scene); + if (source) { + if (lapseType === BattlerTagLapseType.PRE_MOVE) { + return super.lapse(pokemon, lapseType) && source.isActive(true); + } else { + return source.isActive(true); + } + } + return false; } /** * Checks if the source of the tag has the parameter move in its moveset and that the source is still active + * @override * @param {Moves} move the move under investigation * @returns `false` if either condition is not met */ - override isMoveRestricted(move: Moves): boolean { - if (this.source) { - const sourceMoveset = this.source.getMoveset().map(m => m!.moveId); - return sourceMoveset?.includes(move) && this.source.isActive(true); + public override isMoveRestricted(move: Moves, user: Pokemon): boolean { + const source = this.getSourcePokemon(user.scene); + if (source) { + const sourceMoveset = source.getMoveset().map(m => m!.moveId); + return sourceMoveset?.includes(move) && source.isActive(true); } return false; } diff --git a/src/data/move.ts b/src/data/move.ts index 8e9977337cc..d4f3b2ce3ee 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -7883,7 +7883,8 @@ export function initMoves() { .attr(SwitchAbilitiesAttr), new StatusMove(Moves.IMPRISON, Type.PSYCHIC, 100, 10, -1, 0, 3) .ignoresSubstitute() - .attr(AddArenaTagAttr, ArenaTagType.IMPRISON, 1, true, false), + .attr(AddArenaTagAttr, ArenaTagType.IMPRISON, 1, true, false) + .target(MoveTarget.ENEMY_SIDE), new SelfStatusMove(Moves.REFRESH, Type.NORMAL, -1, 20, -1, 0, 3) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN) .condition((user, target, move) => !!user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 136c1eb1685..9ae83753e62 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3062,8 +3062,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * * @see {@linkcode MoveRestrictionBattlerTag} */ - isMoveRestricted(moveId: Moves): boolean { - return this.getRestrictingTag(moveId) !== null; + public isMoveRestricted(moveId: Moves, pokemon?: Pokemon): boolean { + return this.getRestrictingTag(moveId, pokemon) !== null; } /** @@ -3096,7 +3096,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getRestrictingTag(moveId: Moves, user?: Pokemon, target?: Pokemon): MoveRestrictionBattlerTag | null { for (const tag of this.findTags(t => t instanceof MoveRestrictionBattlerTag)) { - if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId)) { + if ((tag as MoveRestrictionBattlerTag).isMoveRestricted(moveId, user)) { return tag as MoveRestrictionBattlerTag; } else if (user && target && (tag as MoveRestrictionBattlerTag).isMoveTargetRestricted(moveId, user, target)) { return tag as MoveRestrictionBattlerTag; @@ -5139,7 +5139,7 @@ export class PokemonMove { * @returns `true` if the move can be selected and used by the Pokemon, otherwise `false`. */ isUsable(pokemon: Pokemon, ignorePp: boolean = false, ignoreRestrictionTags: boolean = false): boolean { - if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId)) { + if (this.moveId && !ignoreRestrictionTags && pokemon.isMoveRestricted(this.moveId, pokemon)) { return false; } diff --git a/src/phases/command-phase.ts b/src/phases/command-phase.ts index e85c66543ac..cf66631bd96 100644 --- a/src/phases/command-phase.ts +++ b/src/phases/command-phase.ts @@ -114,8 +114,8 @@ export class CommandPhase extends FieldPhase { // Decides between a Disabled, Not Implemented, or No PP translation message const errorMessage = - playerPokemon.isMoveRestricted(move.moveId) - ? playerPokemon.getRestrictingTag(move.moveId)!.selectionDeniedText(playerPokemon, move.moveId) + playerPokemon.isMoveRestricted(move.moveId, playerPokemon) + ? playerPokemon.getRestrictingTag(move.moveId, playerPokemon)!.selectionDeniedText(playerPokemon, move.moveId) : move.getName().endsWith(" (N)") ? "battle:moveNotImplemented" : "battle:moveNoPP"; const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator From e340abe75d2d27b17922cb12e90bf19a9b9a0f70 Mon Sep 17 00:00:00 2001 From: PigeonBar <56974298+PigeonBar@users.noreply.github.com> Date: Sun, 13 Oct 2024 20:08:47 -0400 Subject: [PATCH 61/70] [P1 Bug] Fix softlock when a phazing attack activates a reviver seed (#4654) * [P1 Bug] Fix softlock when a phazing attack activates a reviver seed * Polishing tests * Change approach to respect Parting Shot's targeting * Tests: Added checks for correct number of Pokemon on field --- src/data/move.ts | 13 +++---- src/test/moves/dragon_tail.test.ts | 54 ++++++++++++++++++++++++++++++ src/test/moves/u_turn.test.ts | 19 +++++++++++ 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index d4f3b2ce3ee..a77e8096672 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5484,37 +5484,38 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { */ const switchOutTarget = this.selfSwitch ? user : target; if (switchOutTarget instanceof PlayerPokemon) { + // Switch out logic for the player's Pokemon if (switchOutTarget.scene.getParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { return false; } - switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); if (switchOutTarget.hp > 0) { + switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); user.scene.prependToPhase(new SwitchPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase); return true; } return false; } else if (user.scene.currentBattle.battleType !== BattleType.WILD) { + // Switch out logic for trainer battles if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) { return false; } - // Switch out logic for trainer battles - switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); if (switchOutTarget.hp > 0) { // for opponent switching out + switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH); user.scene.prependToPhase(new SwitchSummonPhase(user.scene, this.switchType, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, false), MoveEndPhase); } } else { + // Switch out logic for everything else (eg: WILD battles) if (user.scene.currentBattle.waveIndex % 10 === 0) { return false; } - // Switch out logic for everything else (eg: WILD battles) - switchOutTarget.leaveField(false); - if (switchOutTarget.hp) { + if (switchOutTarget.hp > 0) { + switchOutTarget.leaveField(false); user.scene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500); // in double battles redirect potential moves off fled pokemon diff --git a/src/test/moves/dragon_tail.test.ts b/src/test/moves/dragon_tail.test.ts index eb02b09fbb4..cf801eb42c1 100644 --- a/src/test/moves/dragon_tail.test.ts +++ b/src/test/moves/dragon_tail.test.ts @@ -139,4 +139,58 @@ describe("Moves - Dragon Tail", () => { expect(enemy.isFullHp()).toBe(false); }); + + it("should force a switch upon fainting an opponent normally", async () => { + game.override.startingWave(5) + .startingLevel(1000); // To make sure Dragon Tail KO's the opponent + await game.classicMode.startBattle([ Species.DRATINI ]); + + game.move.select(Moves.DRAGON_TAIL); + + await game.toNextTurn(); + + // Make sure the enemy switched to a healthy Pokemon + const enemy = game.scene.getEnemyPokemon()!; + expect(enemy).toBeDefined(); + expect(enemy.isFullHp()).toBe(true); + + // Make sure the enemy has a fainted Pokemon in their party and not on the field + const faintedEnemy = game.scene.getEnemyParty().find(p => !p.isAllowedInBattle()); + expect(faintedEnemy).toBeDefined(); + expect(game.scene.getEnemyField().length).toBe(1); + }); + + it("should not cause a softlock when activating an opponent trainer's reviver seed", async () => { + game.override.startingWave(5) + .enemyHeldItems([{ name: "REVIVER_SEED" }]) + .startingLevel(1000); // To make sure Dragon Tail KO's the opponent + await game.classicMode.startBattle([ Species.DRATINI ]); + + game.move.select(Moves.DRAGON_TAIL); + + await game.toNextTurn(); + + // Make sure the enemy field is not empty and has a revived Pokemon + const enemy = game.scene.getEnemyPokemon()!; + expect(enemy).toBeDefined(); + expect(enemy.hp).toBe(Math.floor(enemy.getMaxHp() / 2)); + expect(game.scene.getEnemyField().length).toBe(1); + }); + + it("should not cause a softlock when activating a player's reviver seed", async () => { + game.override.startingHeldItems([{ name: "REVIVER_SEED" }]) + .enemyMoveset(Moves.DRAGON_TAIL) + .enemyLevel(1000); // To make sure Dragon Tail KO's the player + await game.classicMode.startBattle([ Species.DRATINI, Species.BULBASAUR ]); + + game.move.select(Moves.SPLASH); + + await game.toNextTurn(); + + // Make sure the player's field is not empty and has a revived Pokemon + const dratini = game.scene.getPlayerPokemon()!; + expect(dratini).toBeDefined(); + expect(dratini.hp).toBe(Math.floor(dratini.getMaxHp() / 2)); + expect(game.scene.getPlayerField().length).toBe(1); + }); }); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts index 17e02cb50ef..b995c20f503 100644 --- a/src/test/moves/u_turn.test.ts +++ b/src/test/moves/u_turn.test.ts @@ -96,4 +96,23 @@ describe("Moves - U-turn", () => { expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }, 20000); + + it("still forces a switch if u-turn KO's the opponent", async () => { + game.override.startingLevel(1000); // Ensure that U-Turn KO's the opponent + await game.classicMode.startBattle([ + Species.RAICHU, + Species.SHUCKLE + ]); + const enemy = game.scene.getEnemyPokemon()!; + + // KO the opponent with U-Turn + game.move.select(Moves.U_TURN); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to(TurnEndPhase); + expect(enemy.isFainted()).toBe(true); + + // Check that U-Turn forced a switch + expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); + expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE); + }); }); From 8981f0e7a8ee63743b60f1665967859940b1a4b8 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Sun, 13 Oct 2024 19:20:55 -0700 Subject: [PATCH 62/70] Trainer party de-duplication checks static pokemon too (#4585) Co-authored-by: frutescens Co-authored-by: innerthunder <168692175+innerthunder@users.noreply.github.com> --- src/field/trainer.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 2ec9d07e474..5110787452f 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -383,7 +383,7 @@ export default class Trainer extends Phaser.GameObjects.Container { const battle = this.scene.currentBattle; const template = this.getPartyTemplate(); - let species: PokemonSpecies; + let baseSpecies: PokemonSpecies; if (this.config.speciesPools) { const tierValue = Utils.randSeedInt(512); let tier = tierValue >= 156 ? TrainerPoolTier.COMMON : tierValue >= 32 ? TrainerPoolTier.UNCOMMON : tierValue >= 6 ? TrainerPoolTier.RARE : tierValue >= 1 ? TrainerPoolTier.SUPER_RARE : TrainerPoolTier.ULTRA_RARE; @@ -393,17 +393,17 @@ export default class Trainer extends Phaser.GameObjects.Container { tier--; } const tierPool = this.config.speciesPools[tier]; - species = getPokemonSpecies(Utils.randSeedItem(tierPool)); + baseSpecies = getPokemonSpecies(Utils.randSeedItem(tierPool)); } else { - species = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter); + baseSpecies = this.scene.randomSpecies(battle.waveIndex, level, false, this.config.speciesFilter); } - let ret = getPokemonSpecies(species.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); + let ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); let retry = false; console.log(ret.getName()); - if (pokemonPrevolutions.hasOwnProperty(species.speciesId) && ret.speciesId !== species.speciesId) { + if (pokemonPrevolutions.hasOwnProperty(baseSpecies.speciesId) && ret.speciesId !== baseSpecies.speciesId) { retry = true; } else if (template.isBalanced(battle.enemyParty.length)) { const partyMemberTypes = battle.enemyParty.map(p => p.getTypes(true)).flat(); @@ -417,7 +417,7 @@ export default class Trainer extends Phaser.GameObjects.Container { console.log("Attempting reroll of species evolution to fit specialty type..."); let evoAttempt = 0; while (retry && evoAttempt++ < 10) { - ret = getPokemonSpecies(species.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); + ret = getPokemonSpecies(baseSpecies.getTrainerSpeciesForLevel(level, true, strength, this.scene.currentBattle.waveIndex)); console.log(ret.name); if (this.config.specialtyTypes.find(t => ret.isOfType(t))) { retry = false; @@ -426,7 +426,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } // Prompts reroll of party member species if species already present in the enemy party - if (this.checkDuplicateSpecies(ret)) { + if (this.checkDuplicateSpecies(ret, baseSpecies)) { console.log("Duplicate species detected, prompting reroll..."); retry = true; } @@ -442,13 +442,16 @@ export default class Trainer extends Phaser.GameObjects.Container { /** * Checks if the enemy trainer already has the Pokemon species in their party * @param {PokemonSpecies} species {@linkcode PokemonSpecies} + * @param {PokemonSpecies} baseSpecies {@linkcode PokemonSpecies} - baseSpecies of the Pokemon if species is forced to evolve * @returns `true` if the species is already present in the party */ - checkDuplicateSpecies(species: PokemonSpecies): boolean { + checkDuplicateSpecies(species: PokemonSpecies, baseSpecies: PokemonSpecies): boolean { + const staticPartyPokemon = (signatureSpecies[TrainerType[this.config.trainerType]] ?? []).flat(1); + const currentPartySpecies = this.scene.getEnemyParty().map(p => { return p.species.speciesId; }); - return currentPartySpecies.includes(species.speciesId); + return currentPartySpecies.includes(species.speciesId) || staticPartyPokemon.includes(baseSpecies.speciesId); } getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE, forSwitch: boolean = false): [integer, integer][] { From 676322e80072518bd667d0513aa18136eed0e82b Mon Sep 17 00:00:00 2001 From: MokaStitcher <54149968+MokaStitcher@users.noreply.github.com> Date: Mon, 14 Oct 2024 16:42:59 +0200 Subject: [PATCH 63/70] [QOL] Add input delay for skipping egg summary (#4644) --- src/phases/egg-lapse-phase.ts | 5 +++-- src/phases/egg-summary-phase.ts | 3 --- src/ui/abstact-option-select-ui-handler.ts | 2 ++ src/ui/confirm-ui-handler.ts | 5 +++-- src/ui/egg-summary-ui-handler.ts | 24 +++++++++++++++++----- 5 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index d81d63696a5..4c57be09b79 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -33,7 +33,7 @@ export class EggLapsePhase extends Phase { if (eggsToHatchCount > 0) { if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) { this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => { - // show prompt for skip + // show prompt for skip, blocking inputs for 1 second this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0); this.scene.ui.setModeWithoutClear(Mode.CONFIRM, () => { this.hatchEggsSkipped(eggsToHatch); @@ -41,7 +41,8 @@ export class EggLapsePhase extends Phase { }, () => { this.hatchEggsRegular(eggsToHatch); this.end(); - } + }, + null, null, null, 1000, true ); }, 100, true); } else if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 2) { diff --git a/src/phases/egg-summary-phase.ts b/src/phases/egg-summary-phase.ts index 75c6939daf1..b673eb4887b 100644 --- a/src/phases/egg-summary-phase.ts +++ b/src/phases/egg-summary-phase.ts @@ -1,7 +1,6 @@ import BattleScene from "#app/battle-scene"; import { Phase } from "#app/phase"; import { Mode } from "#app/ui/ui"; -import EggHatchSceneHandler from "#app/ui/egg-hatch-scene-handler"; import { EggHatchData } from "#app/data/egg-hatch-data"; /** @@ -11,7 +10,6 @@ import { EggHatchData } from "#app/data/egg-hatch-data"; */ export class EggSummaryPhase extends Phase { private eggHatchData: EggHatchData[]; - private eggHatchHandler: EggHatchSceneHandler; constructor(scene: BattleScene, eggHatchData: EggHatchData[]) { super(scene); @@ -26,7 +24,6 @@ export class EggSummaryPhase extends Phase { if (i >= this.eggHatchData.length) { this.scene.ui.setModeForceTransition(Mode.EGG_HATCH_SUMMARY, this.eggHatchData).then(() => { this.scene.fadeOutBgm(undefined, false); - this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; }); } else { diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index 9dffd3b4ad0..a12ffbc46bd 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -165,6 +165,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { if (this.config.delay) { this.blockInput = true; this.optionSelectText.setAlpha(0.5); + this.cursorObj?.setAlpha(0.8); this.scene.time.delayedCall(Utils.fixedInt(this.config.delay), () => this.unblockInput()); } @@ -256,6 +257,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.blockInput = false; this.optionSelectText.setAlpha(1); + this.cursorObj?.setAlpha(1); } getOptionsWithScroll(): OptionSelectItem[] { diff --git a/src/ui/confirm-ui-handler.ts b/src/ui/confirm-ui-handler.ts index 16dae82d091..2022508fc0d 100644 --- a/src/ui/confirm-ui-handler.ts +++ b/src/ui/confirm-ui-handler.ts @@ -76,7 +76,8 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { } } ], - delay: args.length >= 6 && args[5] !== null ? args[5] as integer : 0 + delay: args.length >= 6 && args[5] !== null ? args[5] as number : 0, + noCancel: args.length >= 7 && args[6] !== null ? args[6] as boolean : false, }; super.show([ config ]); @@ -96,7 +97,7 @@ export default class ConfirmUiHandler extends AbstractOptionSelectUiHandler { } processInput(button: Button): boolean { - if (button === Button.CANCEL && this.blockInput) { + if (button === Button.CANCEL && this.blockInput && !this.config?.noCancel) { this.unblockInput(); } diff --git a/src/ui/egg-summary-ui-handler.ts b/src/ui/egg-summary-ui-handler.ts index 519722b1505..da93168926e 100644 --- a/src/ui/egg-summary-ui-handler.ts +++ b/src/ui/egg-summary-ui-handler.ts @@ -42,6 +42,9 @@ export default class EggSummaryUiHandler extends MessageUiHandler { private scrollGridHandler : ScrollableGridUiHandler; private cursorObj: Phaser.GameObjects.Image; + /** used to add a delay before which it is not possible to exit the summary */ + private blockExit: boolean; + /** * Allows subscribers to listen for events * @@ -168,6 +171,13 @@ export default class EggSummaryUiHandler extends MessageUiHandler { this.setCursor(0); this.scene.playSoundWithoutBgm("evolution_fanfare"); + + // Prevent exiting the egg summary for 2 seconds if the egg hatching + // was skipped automatically and for 1 second otherwise + const exitBlockingDuration = (this.scene.eggSkipPreference === 2) ? 2000 : 1000; + this.blockExit = true; + this.scene.time.delayedCall(exitBlockingDuration, () => this.blockExit = false); + return true; } @@ -203,13 +213,17 @@ export default class EggSummaryUiHandler extends MessageUiHandler { const ui = this.getUi(); let success = false; - const error = false; + let error = false; if (button === Button.CANCEL) { - const phase = this.scene.getCurrentPhase(); - if (phase instanceof EggSummaryPhase) { - phase.end(); + if (!this.blockExit) { + const phase = this.scene.getCurrentPhase(); + if (phase instanceof EggSummaryPhase) { + phase.end(); + } + success = true; + } else { + error = true; } - success = true; } else { this.scrollGridHandler.processInput(button); } From e7a4d4055f2b48d1d78c1dc2cb6f3794e0c34dd2 Mon Sep 17 00:00:00 2001 From: cadi Date: Tue, 15 Oct 2024 04:39:34 +0900 Subject: [PATCH 64/70] [Move] Implement Power Trick (#2658) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add `PowerTrickTag` * modify getStat() with PowerTrickTag * implement `PowerTrickAttr` * add unit test * enhance docs and tag apply logic --------- Co-authored-by: Lugiad' Co-authored-by: José Ricardo Fleury Oliveira Co-authored-by: Jannik Tappert <38758606+CodeTappert@users.noreply.github.com> Co-authored-by: Enoch Co-authored-by: Yonmaru40 <47717431+40chyan@users.noreply.github.com> Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> Co-authored-by: Niccolò <123510358+NicusPulcis@users.noreply.github.com> Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battler-tags.ts | 40 ++++++++++ src/data/move.ts | 5 +- src/enums/battler-tag-type.ts | 1 + src/field/pokemon.ts | 6 +- src/test/moves/power_trick.test.ts | 113 +++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 src/test/moves/power_trick.test.ts diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index a5016746013..8491307fc76 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -2724,6 +2724,44 @@ export class TelekinesisTag extends BattlerTag { } } +/** + * Tag that swaps the user's base ATK stat with its base DEF stat. + * @extends BattlerTag + */ +export class PowerTrickTag extends BattlerTag { + constructor(sourceMove: Moves, sourceId: number) { + super(BattlerTagType.POWER_TRICK, BattlerTagLapseType.CUSTOM, 0, sourceMove, sourceId, true); + } + + onAdd(pokemon: Pokemon): void { + this.swapStat(pokemon); + pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } + + onRemove(pokemon: Pokemon): void { + this.swapStat(pokemon); + pokemon.scene.queueMessage(i18next.t("battlerTags:powerTrickActive", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); + } + + /** + * Removes the Power Trick tag and reverts any stat changes if the tag is already applied. + * @param {Pokemon} pokemon The {@linkcode Pokemon} that already has the Power Trick tag. + */ + onOverlap(pokemon: Pokemon): void { + pokemon.removeTag(this.tagType); + } + + /** + * Swaps the user's base ATK stat with its base DEF stat. + * @param {Pokemon} pokemon The {@linkcode Pokemon} whose stats will be swapped. + */ + swapStat(pokemon: Pokemon): void { + const temp = pokemon.getStat(Stat.ATK, false); + pokemon.setStat(Stat.ATK, pokemon.getStat(Stat.DEF, false), false); + pokemon.setStat(Stat.DEF, temp, false); + } +} + /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * @param sourceId - The ID of the pokemon adding the tag @@ -2899,6 +2937,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source return new SyrupBombTag(sourceId); case BattlerTagType.TELEKINESIS: return new TelekinesisTag(sourceMove); + case BattlerTagType.POWER_TRICK: + return new PowerTrickTag(sourceMove, sourceId); case BattlerTagType.NONE: default: return new BattlerTag(tagType, BattlerTagLapseType.CUSTOM, turnCount, sourceMove, sourceId); diff --git a/src/data/move.ts b/src/data/move.ts index a77e8096672..2d91363955a 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6430,6 +6430,9 @@ export class TransformAttr extends MoveEffectAttr { user.summonData.gender = target.getGender(); user.summonData.fusionGender = target.getFusionGender(); + // Power Trick's effect will not preserved after using Transform + user.removeTag(BattlerTagType.POWER_TRICK); + // Copy all stats (except HP) for (const s of EFFECTIVE_STATS) { user.setStat(s, target.getStat(s, false), false); @@ -8153,7 +8156,7 @@ export function initMoves() { .attr(OpponentHighHpPowerAttr, 120) .makesContact(), new SelfStatusMove(Moves.POWER_TRICK, Type.PSYCHIC, -1, 10, -1, 0, 4) - .unimplemented(), + .attr(AddBattlerTagAttr, BattlerTagType.POWER_TRICK, true), new StatusMove(Moves.GASTRO_ACID, Type.POISON, 100, 10, -1, 0, 4) .attr(SuppressAbilitiesAttr), new StatusMove(Moves.LUCKY_CHANT, Type.NORMAL, -1, 30, -1, 0, 4) diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 2efae9ad359..680dedb93cc 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -80,6 +80,7 @@ export enum BattlerTagType { DOUBLE_SHOCKED = "DOUBLE_SHOCKED", AUTOTOMIZED = "AUTOTOMIZED", MYSTERY_ENCOUNTER_POST_SUMMON = "MYSTERY_ENCOUNTER_POST_SUMMON", + POWER_TRICK = "POWER_TRICK", HEAL_BLOCK = "HEAL_BLOCK", TORMENT = "TORMENT", TAUNT = "TAUNT", diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 9ae83753e62..0204672cabd 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -19,7 +19,7 @@ import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims"; import { Status, StatusEffect, getRandomStatus } from "#app/data/status-effect"; import { pokemonEvolutions, pokemonPrevolutions, SpeciesFormEvolution, SpeciesEvolutionCondition, FusionSpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions"; import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/tms"; -import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag } from "../data/battler-tags"; +import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags"; import { WeatherType } from "#app/data/weather"; import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag"; import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs } from "#app/data/ability"; @@ -3048,6 +3048,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { continue; } + if (tag instanceof PowerTrickTag) { + tag.swapStat(this); + } + this.summonData.tags.push(tag); } diff --git a/src/test/moves/power_trick.test.ts b/src/test/moves/power_trick.test.ts new file mode 100644 index 00000000000..a064a43dec4 --- /dev/null +++ b/src/test/moves/power_trick.test.ts @@ -0,0 +1,113 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Moves } from "#enums/moves"; +import { Stat } from "#enums/stat"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; + +describe("Moves - Power Trick", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.MEW) + .enemyLevel(200) + .moveset([ Moves.POWER_TRICK ]) + .ability(Abilities.BALL_FETCH); + }); + + it("swaps the user's ATK and DEF stats", async () => { + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + const player = game.scene.getPlayerPokemon()!; + const baseATK = player.getStat(Stat.ATK, false); + const baseDEF = player.getStat(Stat.DEF, false); + + game.move.select(Moves.POWER_TRICK); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(baseDEF); + expect(player.getStat(Stat.DEF, false)).toBe(baseATK); + expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeDefined(); + }); + + it("resets initial ATK and DEF stat swap when used consecutively", async () => { + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + const player = game.scene.getPlayerPokemon()!; + const baseATK = player.getStat(Stat.ATK, false); + const baseDEF = player.getStat(Stat.DEF, false); + + game.move.select(Moves.POWER_TRICK); + + await game.phaseInterceptor.to(TurnEndPhase); + + game.move.select(Moves.POWER_TRICK); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(player.getStat(Stat.ATK, false)).toBe(baseATK); + expect(player.getStat(Stat.DEF, false)).toBe(baseDEF); + expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeUndefined(); + }); + + it("should pass effect when using BATON_PASS", async () => { + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.override.moveset([ Moves.POWER_TRICK, Moves.BATON_PASS ]); + + const player = game.scene.getPlayerPokemon()!; + player.addTag(BattlerTagType.POWER_TRICK); + + game.move.select(Moves.BATON_PASS); + game.doSelectPartyPokemon(1); + + await game.phaseInterceptor.to(TurnEndPhase); + + const switchedPlayer = game.scene.getPlayerPokemon()!; + const baseATK = switchedPlayer.getStat(Stat.ATK); + const baseDEF = switchedPlayer.getStat(Stat.DEF); + + expect(switchedPlayer.getStat(Stat.ATK, false)).toBe(baseDEF); + expect(switchedPlayer.getStat(Stat.DEF, false)).toBe(baseATK); + expect(switchedPlayer.getTag(BattlerTagType.POWER_TRICK)).toBeDefined(); + }); + + it("should remove effect after using Transform", async () => { + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.override.moveset([ Moves.POWER_TRICK, Moves.TRANSFORM ]); + + const player = game.scene.getPlayerPokemon()!; + player.addTag(BattlerTagType.POWER_TRICK); + + game.move.select(Moves.TRANSFORM); + + await game.phaseInterceptor.to(TurnEndPhase); + + const enemy = game.scene.getEnemyPokemon()!; + const baseATK = enemy.getStat(Stat.ATK); + const baseDEF = enemy.getStat(Stat.DEF); + + expect(player.getStat(Stat.ATK, false)).toBe(baseATK); + expect(player.getStat(Stat.DEF, false)).toBe(baseDEF); + expect(player.getTag(BattlerTagType.POWER_TRICK)).toBeUndefined(); + }); +}); From e962ac1f182d33e6f06fef1858c49b7ffb90c4b9 Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Mon, 14 Oct 2024 14:47:23 -0700 Subject: [PATCH 65/70] [Beta Bug] Prevent duplicate move failure message (#4662) --- src/phases/move-phase.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 94093188571..d3a2a3329fd 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -128,7 +128,9 @@ export class MovePhase extends BattlePhase { this.lapsePreMoveAndMoveTags(); - this.resolveFinalPreMoveCancellationChecks(); + if (!(this.failed || this.cancelled)) { + this.resolveFinalPreMoveCancellationChecks(); + } if (this.cancelled || this.failed) { this.handlePreMoveFailures(); From bb98bc2f8e296de5b27d644193d284be40062625 Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Tue, 15 Oct 2024 04:17:20 -0500 Subject: [PATCH 66/70] [Balance] Evil Team Update / Penny Adjustments (#4577) * Update trainer-config.ts * Update trainer-config.ts * Update trainer-config.ts * Fixed Flare Grunt's having Noivern > Noibat * Revert Inkay change, Change Penny * Give Admin aces canonical genders * Update trainer-config.ts * Update trainer-config.ts --------- Co-authored-by: Madmadness65 Co-authored-by: damocleas --- src/data/trainer-config.ts | 63 ++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index bc6596c74bd..bbeecba84e6 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -574,13 +574,13 @@ export class TrainerConfig { case "magma": { return { [TrainerPoolTier.COMMON]: [ Species.GROWLITHE, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.BALTOY, Species.ROLYCOLY, Species.GLIGAR, Species.TORKOAL, Species.HOUNDOUR, Species.MAGBY ], - [TrainerPoolTier.UNCOMMON]: [ Species.TRAPINCH, Species.SILICOBRA, Species.RHYHORN, Species.ANORITH, Species.LILEEP, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.BARBOACH ], + [TrainerPoolTier.UNCOMMON]: [ Species.TRAPINCH, Species.SILICOBRA, Species.RHYHORN, Species.ANORITH, Species.LILEEP, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ], [TrainerPoolTier.RARE]: [ Species.CAPSAKID, Species.CHARCADET ] }; } case "aqua": { return { - [TrainerPoolTier.COMMON]: [ Species.CORPHISH, Species.SPHEAL, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.LOTAD, Species.WAILMER, Species.REMORAID ], + [TrainerPoolTier.COMMON]: [ Species.CORPHISH, Species.SPHEAL, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.LOTAD, Species.WAILMER, Species.REMORAID, Species.BARBOACH ], [TrainerPoolTier.UNCOMMON]: [ Species.MANTYKE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.DHELMISE, Species.CLOBBOPUS, Species.FEEBAS, Species.PALDEA_WOOPER, Species.HORSEA, Species.SKRELP ], [TrainerPoolTier.RARE]: [ Species.DONDOZO, Species.BASCULEGION ] }; @@ -601,9 +601,9 @@ export class TrainerConfig { } case "flare": { return { - [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY ], + [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.FOONGUS, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY ], [TrainerPoolTier.UNCOMMON]: [ Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO ], - [TrainerPoolTier.RARE]: [ Species.NOIVERN, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO ] + [TrainerPoolTier.RARE]: [ Species.NOIBAT, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO ] }; } case "aether": { @@ -1504,7 +1504,7 @@ export const trainerConfigs: TrainerConfigs = { .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.GROWLITHE, Species.BALTOY ], [TrainerPoolTier.UNCOMMON]: [ Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR, Species.RHYHORN, Species.HEATMOR ], - [TrainerPoolTier.RARE]: [ Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON ], + [TrainerPoolTier.RARE]: [ Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.TOEDSCOOL ], [TrainerPoolTier.SUPER_RARE]: [ Species.CAPSAKID, Species.CHARCADET ] }), [TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [ Species.CAMERUPT ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), @@ -1540,9 +1540,9 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ [TrainerPoolTier.COMMON]: [ Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR ], - [TrainerPoolTier.UNCOMMON]: [ Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP ], + [TrainerPoolTier.UNCOMMON]: [ Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP, Species.FOONGUS ], [TrainerPoolTier.RARE]: [ Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.SLIGGOO ], - [TrainerPoolTier.SUPER_RARE]: [ Species.NOIVERN, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG ] + [TrainerPoolTier.SUPER_RARE]: [ Species.NOIBAT, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG ] }), [TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [ Species.LIEPARD ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [ Species.MALAMAR ]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), @@ -1893,7 +1893,10 @@ export const trainerConfigs: TrainerConfigs = { }), [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN, Species.ALOLA_PERSIAN ])) + .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.PERSIAN, Species.ALOLA_PERSIAN ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.gender = Gender.MALE; + })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.DUGTRIO, Species.ALOLA_DUGTRIO ])) .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.HONCHKROW ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.NIDOKING, Species.NIDOQUEEN ])) @@ -1945,6 +1948,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Camerupt p.generateName(); + p.gender = Gender.MALE; })), [TrainerType.MAXIE_2]: new TrainerConfig(++t).setName("Maxie").initForEvilTeamLeader("Magma Boss", [], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SOLROCK, Species.TYPHLOSION ], TrainerSlot.TRAINER, true, p => { @@ -1967,6 +1971,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Camerupt p.generateName(); + p.gender = Gender.MALE; })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.GROUDON ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -1985,6 +1990,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Sharpedo p.generateName(); + p.gender = Gender.MALE; })), [TrainerType.ARCHIE_2]: new TrainerConfig(++t).setName("Archie").initForEvilTeamLeader("Aqua Boss", [], true).setMixedBattleBgm("battle_aqua_magma_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.EMPOLEON, Species.LUDICOLO ], TrainerSlot.TRAINER, true, p => { @@ -2010,6 +2016,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Sharpedo p.generateName(); + p.gender = Gender.MALE; })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KYOGRE ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2031,6 +2038,7 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.MALE; })), [TrainerType.CYRUS_2]: new TrainerConfig(++t).setName("Cyrus").initForEvilTeamLeader("Galactic Boss", [], true).setMixedBattleBgm("battle_galactic_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.AZELF, Species.UXIE, Species.MESPRIT ], TrainerSlot.TRAINER, true, p => { @@ -2049,6 +2057,7 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.MALE; })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.DARKRAI ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2065,6 +2074,7 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.MALE; })), [TrainerType.GHETSIS_2]: new TrainerConfig(++t).setName("Ghetsis").initForEvilTeamLeader("Plasma Boss", [], true).setMixedBattleBgm("battle_plasma_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.GENESECT ], TrainerSlot.TRAINER, true, p => { @@ -2084,6 +2094,11 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; + if (p.species.speciesId === Species.HYDREIGON) { + p.gender = Gender.MALE; + } else if (p.species.speciesId === Species.IRON_JUGULIS) { + p.gender = Gender.GENDERLESS; + } })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.KYUREM ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2105,6 +2120,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Gyarados p.generateName(); + p.gender = Gender.MALE; })), [TrainerType.LYSANDRE_2]: new TrainerConfig(++t).setName("Lysandre").initForEvilTeamLeader("Flare Boss", [], true).setMixedBattleBgm("battle_flare_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SCREAM_TAIL, Species.FLUTTER_MANE ], TrainerSlot.TRAINER, true, p => { @@ -2124,6 +2140,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; p.formIndex = 1; // Mega Gyardos p.generateName(); + p.gender = Gender.MALE; })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.YVELTAL ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2131,7 +2148,10 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.MASTER_BALL; })), [TrainerType.LUSAMINE]: new TrainerConfig(++t).setName("Lusamine").initForEvilTeamLeader("Aether Boss", []).setMixedBattleBgm("battle_aether_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLEFABLE ])) + .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.CLEFABLE ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.gender = Gender.FEMALE; + })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.LILLIGANT, Species.HISUI_LILLIGANT ])) .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.MILOTIC, Species.PRIMARINA ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.GALAR_SLOWBRO, Species.GALAR_SLOWKING ])) @@ -2148,7 +2168,10 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ROGUE_BALL; })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.MILOTIC, Species.PRIMARINA ])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.CLEFABLE ])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.CLEFABLE ], TrainerSlot.TRAINER, true, p => { + p.generateAndPopulateMoveset(); + p.gender = Gender.FEMALE; + })) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.STAKATAKA, Species.CELESTEELA, Species.GUZZLORD ], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ROGUE_BALL; @@ -2191,6 +2214,7 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.GOLISOPOD ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); + p.gender = Gender.MALE; p.pokeball = PokeballType.ULTRA_BALL; })), [TrainerType.GUZMA_2]: new TrainerConfig(++t).setName("Guzma").initForEvilTeamLeader("Skull Boss", [], true).setMixedBattleBgm("battle_skull_boss").setVictoryBgm("victory_team_plasma") @@ -2198,6 +2222,7 @@ export const trainerConfigs: TrainerConfigs = { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.abilityIndex = 2; //Anticipation + p.gender = Gender.MALE; p.pokeball = PokeballType.ULTRA_BALL; })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.SCIZOR, Species.KLEAVOR ], TrainerSlot.TRAINER, true, p => { @@ -2239,6 +2264,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; // G-Max Copperajah p.generateName(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.FEMALE; })), [TrainerType.ROSE_2]: new TrainerConfig(++t).setName("Rose").initForEvilTeamLeader("Macro Boss", [], true).setMixedBattleBgm("battle_macro_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.ARCHALUDON ], TrainerSlot.TRAINER, true, p => { @@ -2262,6 +2288,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; // G-Max Copperajah p.generateName(); p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.FEMALE; })), [TrainerType.PENNY]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", []).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.VAPOREON, Species.JOLTEON, Species.FLAREON ])) @@ -2275,8 +2302,9 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = Utils.randSeedInt(5, 1); // Heat, Wash, Frost, Fan, or Mow })) .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { - p.generateAndPopulateMoveset(); p.abilityIndex = 2; // Pixilate + p.generateAndPopulateMoveset(); + p.gender = Gender.FEMALE; })) .setPartyMemberFunc(5, getRandomPartyMemberFunc([ Species.EEVEE ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2290,20 +2318,21 @@ export const trainerConfigs: TrainerConfigs = { return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ]; //TODO: is the bang correct? }), [TrainerType.PENNY_2]: new TrainerConfig(++t).setName("Cassiopeia").initForEvilTeamLeader("Star Boss", [], true).setMixedBattleBgm("battle_star_boss").setVictoryBgm("victory_team_plasma") - .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); - p.formIndex = Utils.randSeedInt(5, 1); //Random Starmobile form + p.abilityIndex = 2; // Pixilate p.generateAndPopulateMoveset(); - p.pokeball = PokeballType.ULTRA_BALL; + p.gender = Gender.FEMALE; })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.ENTEI, Species.RAIKOU, Species.SUICUNE ], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; })) .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.WALKING_WAKE, Species.GOUGING_FIRE, Species.RAGING_BOLT ])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.SYLVEON ], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.REVAVROOM ], TrainerSlot.TRAINER, true, p => { + p.formIndex = Utils.randSeedInt(5, 1); //Random Starmobile form p.generateAndPopulateMoveset(); - p.abilityIndex = 2; // Pixilate + p.pokeball = PokeballType.ROGUE_BALL; })) .setPartyMemberFunc(4, getRandomPartyMemberFunc([ Species.EEVEE ], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); @@ -2318,7 +2347,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.MASTER_BALL; })) .setGenModifiersFunc(party => { - const teraPokemon = party[3]; + const teraPokemon = party[0]; return [ modifierTypes.TERA_SHARD().generateType([], [ teraPokemon.species.type1 ])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(teraPokemon) as PersistentModifier ]; //TODO: is the bang correct? }), [TrainerType.BUCK]: new TrainerConfig(++t).setName("Buck").initForStatTrainer([], true) From d5f87bbea76428677003276c033cf07beb85c151 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Tue, 15 Oct 2024 07:02:02 -0700 Subject: [PATCH 67/70] [P3][Beta] Fix missing move text when a move fails (#4664) * Fix missing move text when a move fails * Use `cancel` function instead of setting `this.cancelled` --- src/phases/move-phase.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index d3a2a3329fd..f50cfbd78ac 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -147,8 +147,9 @@ export class MovePhase extends BattlePhase { const moveQueue = this.pokemon.getMoveQueue(); if (targets.length === 0 || (moveQueue.length && moveQueue[0].move === Moves.NONE)) { + this.showMoveText(); this.showFailedText(); - this.cancelled = true; + this.cancel(); } } From 21b71595e0292a6a30503dc9d93905c8ade179bc Mon Sep 17 00:00:00 2001 From: PrabbyDD <147005742+PrabbyDD@users.noreply.github.com> Date: Tue, 15 Oct 2024 07:04:26 -0700 Subject: [PATCH 68/70] [P2] Attacks that miss against pokemon in semi invul state that have abilities such as volt absorb will not trigger (#4663) * fixing issue where abilities trigger in semi invul state * fixing targets --- src/phases/move-effect-phase.ts | 10 ++++++---- src/test/abilities/volt_absorb.test.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 581cd5ff017..e9bff882367 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -99,8 +99,9 @@ export class MoveEffectPhase extends PokemonPhase { const targetHitChecks = Object.fromEntries(targets.map(p => [ p.getBattlerIndex(), this.hitCheck(p) ])); const hasActiveTargets = targets.some(t => t.isActive(true)); - /** Check if the target is immune via ability to the attacking move */ - const isImmune = targets[0].hasAbilityWithAttr(TypeImmunityAbAttr) && (targets[0].getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move)); + /** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */ + const isImmune = targets[0].hasAbilityWithAttr(TypeImmunityAbAttr) && (targets[0].getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move)) + && !targets[0].getTag(SemiInvulnerableTag); /** * If no targets are left for the move to hit (FAIL), or the invoked move is single-target @@ -148,8 +149,9 @@ export class MoveEffectPhase extends PokemonPhase { && (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType))) || (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType)))); - /** Is the pokemon immune due to an ablility? */ - const isImmune = target.hasAbilityWithAttr(TypeImmunityAbAttr) && (target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move)); + /** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */ + const isImmune = target.hasAbilityWithAttr(TypeImmunityAbAttr) && (target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move)) + && !target.getTag(SemiInvulnerableTag); /** * If the move missed a target, stop all future hits against that target diff --git a/src/test/abilities/volt_absorb.test.ts b/src/test/abilities/volt_absorb.test.ts index 07907a34566..ec82b00ec5a 100644 --- a/src/test/abilities/volt_absorb.test.ts +++ b/src/test/abilities/volt_absorb.test.ts @@ -71,4 +71,23 @@ describe("Abilities - Volt Absorb", () => { await game.phaseInterceptor.to("BerryPhase", false); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); }); + it("regardless of accuracy should not trigger on pokemon in semi invulnerable state", async () => { + game.override.moveset(Moves.THUNDERBOLT); + game.override.enemyMoveset(Moves.DIVE); + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.VOLT_ABSORB); + + await game.classicMode.startBattle(); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.THUNDERBOLT); + enemyPokemon.hp = enemyPokemon.hp - 1; + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.phaseInterceptor.to("MoveEffectPhase"); + + await game.move.forceMiss(); + await game.phaseInterceptor.to("BerryPhase", false); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + }); }); From d01d85689833dea8acb023478e42b6f6d0b2cef2 Mon Sep 17 00:00:00 2001 From: Mumble <171087428+frutescens@users.noreply.github.com> Date: Tue, 15 Oct 2024 07:05:21 -0700 Subject: [PATCH 69/70] [Refactor] Default case to display challenge name (#4656) Co-authored-by: frutescens --- src/ui/run-info-ui-handler.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ui/run-info-ui-handler.ts b/src/ui/run-info-ui-handler.ts index 39927f8e071..1976d5a997b 100644 --- a/src/ui/run-info-ui-handler.ts +++ b/src/ui/run-info-ui-handler.ts @@ -587,13 +587,13 @@ export default class RunInfoUiHandler extends UiHandler { const typeText = typeTextColor + typeShadowColor + i18next.t(`pokemonInfo:Type.${typeRule}`)! + "[/color]" + "[/shadow]"; rules.push(typeText); break; - case Challenges.FRESH_START: - rules.push(i18next.t("challenges:freshStart.name")); - break; case Challenges.INVERSE_BATTLE: - // rules.push(i18next.t("challenges:inverseBattle.shortName")); break; + default: + const localisationKey = Challenges[this.runInfo.challenges[i].id].split("_").map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join(""); + rules.push(i18next.t(`challenges:${localisationKey}.name`)); + break; } } } From 196633562775b04920956d913c5059ddb9f39a31 Mon Sep 17 00:00:00 2001 From: innerthunder <168692175+innerthunder@users.noreply.github.com> Date: Tue, 15 Oct 2024 10:13:54 -0700 Subject: [PATCH 70/70] [Refactor] Add type inference and support for simulated calls to `ArenaTag.apply` (#4659) * Add simulated support for Arena Tag application * Add type inference to ArenaTag.apply * Fix screen tests * back to `any` again lol * fix missing spread syntax (maybe) * updated docs * named imports for `Utils` --- src/data/arena-tag.ts | 189 ++++++++++++++++---------- src/data/move.ts | 4 +- src/field/arena.ts | 10 +- src/field/pokemon.ts | 4 +- src/phases/move-effect-phase.ts | 2 +- src/phases/post-summon-phase.ts | 2 +- src/phases/stat-stage-change-phase.ts | 3 +- src/phases/turn-start-phase.ts | 2 +- src/test/moves/aurora_veil.test.ts | 2 +- src/test/moves/light_screen.test.ts | 2 +- src/test/moves/reflect.test.ts | 2 +- 11 files changed, 135 insertions(+), 87 deletions(-) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 71cf11fa06f..2bd6ae09877 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -1,7 +1,7 @@ import { Arena } from "#app/field/arena"; import BattleScene from "#app/battle-scene"; import { Type } from "#app/data/type"; -import * as Utils from "#app/utils"; +import { BooleanHolder, NumberHolder, toDmgValue } from "#app/utils"; import { MoveCategory, allMoves, MoveTarget, IncrementMovePriorityAttr, applyMoveAttrs } from "#app/data/move"; import { getPokemonNameWithAffix } from "#app/messages"; import Pokemon, { HitResult, PokemonMove } from "#app/field/pokemon"; @@ -36,7 +36,7 @@ export abstract class ArenaTag { public side: ArenaTagSide = ArenaTagSide.BOTH ) {} - apply(arena: Arena, args: any[]): boolean { + apply(arena: Arena, simulated: boolean, ...args: unknown[]): boolean { return true; } @@ -122,10 +122,20 @@ export class MistTag extends ArenaTag { } } - apply(arena: Arena, args: any[]): boolean { - (args[0] as Utils.BooleanHolder).value = true; + /** + * Cancels the lowering of stats + * @param arena the {@linkcode Arena} containing this effect + * @param simulated `true` if the effect should be applied quietly + * @param cancelled a {@linkcode BooleanHolder} whose value is set to `true` + * to flag the stat reduction as cancelled + * @returns `true` if a stat reduction was cancelled; `false` otherwise + */ + override apply(arena: Arena, simulated: boolean, cancelled: BooleanHolder): boolean { + cancelled.value = true; - arena.scene.queueMessage(i18next.t("arenaTag:mistApply")); + if (!simulated) { + arena.scene.queueMessage(i18next.t("arenaTag:mistApply")); + } return true; } @@ -157,17 +167,15 @@ export class WeakenMoveScreenTag extends ArenaTag { /** * Applies the weakening effect to the move. * - * @param arena - The arena where the move is applied. - * @param args - The arguments for the move application. - * @param args[0] - The category of the move. - * @param args[1] - A boolean indicating whether it is a double battle. - * @param args[2] - An object of type `Utils.NumberHolder` that holds the damage multiplier - * - * @returns True if the move was weakened, otherwise false. + * @param arena the {@linkcode Arena} where the move is applied. + * @param simulated n/a + * @param moveCategory the attacking move's {@linkcode MoveCategory}. + * @param damageMultiplier A {@linkcode NumberHolder} containing the damage multiplier + * @returns `true` if the attacking move was weakened; `false` otherwise. */ - apply(arena: Arena, args: any[]): boolean { - if (this.weakenedCategories.includes((args[0] as MoveCategory))) { - (args[2] as Utils.NumberHolder).value = (args[1] as boolean) ? 2732 / 4096 : 0.5; + override apply(arena: Arena, simulated: boolean, moveCategory: MoveCategory, damageMultiplier: NumberHolder): boolean { + if (this.weakenedCategories.includes(moveCategory)) { + damageMultiplier.value = arena.scene.currentBattle.double ? 2732 / 4096 : 0.5; return true; } return false; @@ -249,38 +257,34 @@ export class ConditionalProtectTag extends ArenaTag { onRemove(arena: Arena): void { } /** - * apply(): Checks incoming moves against the condition function + * Checks incoming moves against the condition function * and protects the target if conditions are met - * @param arena The arena containing this tag - * @param args\[0\] (Utils.BooleanHolder) Signals if the move is cancelled - * @param args\[1\] (Pokemon) The Pokemon using the move - * @param args\[2\] (Pokemon) The intended target of the move - * @param args\[3\] (Moves) The parameters to the condition function - * @param args\[4\] (Utils.BooleanHolder) Signals if the applied protection supercedes protection-ignoring effects - * @returns + * @param arena the {@linkcode Arena} containing this tag + * @param simulated `true` if the tag is applied quietly; `false` otherwise. + * @param isProtected a {@linkcode BooleanHolder} used to flag if the move is protected against + * @param attacker the attacking {@linkcode Pokemon} + * @param defender the defending {@linkcode Pokemon} + * @param moveId the {@linkcode Moves | identifier} for the move being used + * @param ignoresProtectBypass a {@linkcode BooleanHolder} used to flag if a protection effect supercedes effects that ignore protection + * @returns `true` if this tag protected against the attack; `false` otherwise */ - apply(arena: Arena, args: any[]): boolean { - const [ cancelled, user, target, moveId, ignoresBypass ] = args; + override apply(arena: Arena, simulated: boolean, isProtected: BooleanHolder, attacker: Pokemon, defender: Pokemon, + moveId: Moves, ignoresProtectBypass: BooleanHolder): boolean { - if (cancelled instanceof Utils.BooleanHolder - && user instanceof Pokemon - && target instanceof Pokemon - && typeof moveId === "number" - && ignoresBypass instanceof Utils.BooleanHolder) { + if ((this.side === ArenaTagSide.PLAYER) === defender.isPlayer() + && this.protectConditionFunc(arena, moveId)) { + if (!isProtected.value) { + isProtected.value = true; + if (!simulated) { + attacker.stopMultiHit(defender); - if ((this.side === ArenaTagSide.PLAYER) === target.isPlayer() - && this.protectConditionFunc(arena, moveId)) { - if (!cancelled.value) { - cancelled.value = true; - user.stopMultiHit(target); - - new CommonBattleAnim(CommonAnim.PROTECT, target).play(arena.scene); - arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(target) })); + new CommonBattleAnim(CommonAnim.PROTECT, defender).play(arena.scene); + arena.scene.queueMessage(i18next.t("arenaTag:conditionalProtectApply", { moveName: super.getMoveName(), pokemonNameWithAffix: getPokemonNameWithAffix(defender) })); } - - ignoresBypass.value = ignoresBypass.value || this.ignoresBypass; - return true; } + + ignoresProtectBypass.value = ignoresProtectBypass.value || this.ignoresBypass; + return true; } return false; } @@ -296,7 +300,7 @@ export class ConditionalProtectTag extends ArenaTag { */ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => { const move = allMoves[moveId]; - const priority = new Utils.NumberHolder(move.priority); + const priority = new NumberHolder(move.priority); const effectPhase = arena.scene.getCurrentPhase(); if (effectPhase instanceof MoveEffectPhase) { @@ -460,7 +464,7 @@ class WishTag extends ArenaTag { if (user) { this.battlerIndex = user.getBattlerIndex(); this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); - this.healHp = Utils.toDmgValue(user.getMaxHp() / 2); + this.healHp = toDmgValue(user.getMaxHp() / 2); } else { console.warn("Failed to get source for WishTag onAdd"); } @@ -497,12 +501,19 @@ export class WeakenMoveTypeTag extends ArenaTag { this.weakenedType = type; } - apply(arena: Arena, args: any[]): boolean { - if ((args[0] as Type) === this.weakenedType) { - (args[1] as Utils.NumberHolder).value *= 0.33; + /** + * Reduces an attack's power by 0.33x if it matches this tag's weakened type. + * @param arena n/a + * @param simulated n/a + * @param type the attack's {@linkcode Type} + * @param power a {@linkcode NumberHolder} containing the attack's power + * @returns `true` if the attack's power was reduced; `false` otherwise. + */ + override apply(arena: Arena, simulated: boolean, type: Type, power: NumberHolder): boolean { + if (type === this.weakenedType) { + power.value *= 0.33; return true; } - return false; } } @@ -563,13 +574,12 @@ export class IonDelugeTag extends ArenaTag { /** * Converts Normal-type moves to Electric type * @param arena n/a - * @param args - * - `[0]` {@linkcode Utils.NumberHolder} A container with a move's {@linkcode Type} + * @param simulated n/a + * @param moveType a {@linkcode NumberHolder} containing a move's {@linkcode Type} * @returns `true` if the given move type changed; `false` otherwise. */ - apply(arena: Arena, args: any[]): boolean { - const moveType = args[0]; - if (moveType instanceof Utils.NumberHolder && moveType.value === Type.NORMAL) { + override apply(arena: Arena, simulated: boolean, moveType: NumberHolder): boolean { + if (moveType.value === Type.NORMAL) { moveType.value = Type.ELECTRIC; return true; } @@ -608,16 +618,22 @@ export class ArenaTrapTag extends ArenaTag { } } - apply(arena: Arena, args: any[]): boolean { - const pokemon = args[0] as Pokemon; + /** + * Activates the hazard effect onto a Pokemon when it enters the field + * @param arena the {@linkcode Arena} containing this tag + * @param simulated if `true`, only checks if the hazard would activate. + * @param pokemon the {@linkcode Pokemon} triggering this hazard + * @returns `true` if this hazard affects the given Pokemon; `false` otherwise. + */ + override apply(arena: Arena, simulated: boolean, pokemon: Pokemon): boolean { if (this.sourceId === pokemon.id || (this.side === ArenaTagSide.PLAYER) !== pokemon.isPlayer()) { return false; } - return this.activateTrap(pokemon); + return this.activateTrap(pokemon, simulated); } - activateTrap(pokemon: Pokemon): boolean { + activateTrap(pokemon: Pokemon, simulated: boolean): boolean { return false; } @@ -651,14 +667,18 @@ class SpikesTag extends ArenaTrapTag { } } - activateTrap(pokemon: Pokemon): boolean { + override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { if (pokemon.isGrounded()) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); + if (simulated) { + return !cancelled.value; + } + if (!cancelled.value) { const damageHpRatio = 1 / (10 - 2 * this.layers); - const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); + const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:spikesActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); @@ -702,8 +722,11 @@ class ToxicSpikesTag extends ArenaTrapTag { } } - activateTrap(pokemon: Pokemon): boolean { + override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { if (pokemon.isGrounded()) { + if (simulated) { + return true; + } if (pokemon.isOfType(Type.POISON)) { this.neutralized = true; if (pokemon.scene.arena.removeTag(this.tagType)) { @@ -807,8 +830,8 @@ class StealthRockTag extends ArenaTrapTag { return damageHpRatio; } - activateTrap(pokemon: Pokemon): boolean { - const cancelled = new Utils.BooleanHolder(false); + override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { + const cancelled = new BooleanHolder(false); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (cancelled.value) { @@ -818,12 +841,16 @@ class StealthRockTag extends ArenaTrapTag { const damageHpRatio = this.getDamageHpRatio(pokemon); if (damageHpRatio) { - const damage = Utils.toDmgValue(pokemon.getMaxHp() * damageHpRatio); + if (simulated) { + return true; + } + const damage = toDmgValue(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(i18next.t("arenaTag:stealthRockActivateTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); pokemon.damageAndUpdate(damage, HitResult.OTHER); if (pokemon.turnData) { pokemon.turnData.damageTaken += damage; } + return true; } return false; @@ -853,14 +880,20 @@ class StickyWebTag extends ArenaTrapTag { } } - activateTrap(pokemon: Pokemon): boolean { + override activateTrap(pokemon: Pokemon, simulated: boolean): boolean { if (pokemon.isGrounded()) { - const cancelled = new Utils.BooleanHolder(false); + const cancelled = new BooleanHolder(false); applyAbAttrs(ProtectStatAbAttr, pokemon, cancelled); + + if (simulated) { + return !cancelled.value; + } + if (!cancelled.value) { pokemon.scene.queueMessage(i18next.t("arenaTag:stickyWebActivateTrap", { pokemonName: pokemon.getNameToRender() })); - const stages = new Utils.NumberHolder(-1); + const stages = new NumberHolder(-1); pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), false, [ Stat.SPD ], stages.value)); + return true; } } @@ -879,8 +912,15 @@ export class TrickRoomTag extends ArenaTag { super(ArenaTagType.TRICK_ROOM, turnCount, Moves.TRICK_ROOM, sourceId); } - apply(arena: Arena, args: any[]): boolean { - const speedReversed = args[0] as Utils.BooleanHolder; + /** + * Reverses Speed-based turn order for all Pokemon on the field + * @param arena n/a + * @param simulated n/a + * @param speedReversed a {@linkcode BooleanHolder} used to flag if Speed-based + * turn order should be reversed. + * @returns `true` if turn order is successfully reversed; `false` otherwise + */ + override apply(arena: Arena, simulated: boolean, speedReversed: BooleanHolder): boolean { speedReversed.value = !speedReversed.value; return true; } @@ -1087,7 +1127,7 @@ class FireGrassPledgeTag extends ArenaTag { pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); // TODO: Replace this with a proper animation pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM)); - pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8)); + pokemon.damageAndUpdate(toDmgValue(pokemon.getMaxHp() / 8)); }); return super.lapse(arena); @@ -1111,8 +1151,15 @@ class WaterFirePledgeTag extends ArenaTag { arena.scene.queueMessage(i18next.t(`arenaTag:waterFirePledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); } - override apply(arena: Arena, args: any[]): boolean { - const moveChance = args[0] as Utils.NumberHolder; + /** + * Doubles the chance for the given move's secondary effect(s) to trigger + * @param arena the {@linkcode Arena} containing this tag + * @param simulated n/a + * @param moveChance a {@linkcode NumberHolder} containing + * the move's current effect chance + * @returns `true` if the move's effect chance was doubled (currently always `true`) + */ + override apply(arena: Arena, simulated: boolean, moveChance: NumberHolder): boolean { moveChance.value *= 2; return true; } diff --git a/src/data/move.ts b/src/data/move.ts index 2d91363955a..b0078c32f12 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -808,7 +808,7 @@ export default class Move implements Localizable { source.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power); if (!this.hasAttr(TypelessAttr)) { - source.scene.arena.applyTags(WeakenMoveTypeTag, this.type, power); + source.scene.arena.applyTags(WeakenMoveTypeTag, simulated, this.type, power); source.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, this.type, power); } @@ -1026,7 +1026,7 @@ export class MoveEffectAttr extends MoveAttr { if (!move.hasAttr(FlinchAttr) || moveChance.value <= move.chance) { const userSide = user.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; - user.scene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, moveChance); + user.scene.arena.applyTagsForSide(ArenaTagType.WATER_FIRE_PLEDGE, userSide, false, moveChance); } if (!selfEffect) { diff --git a/src/field/arena.ts b/src/field/arena.ts index 1e164903e9d..ad1846884fc 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -579,26 +579,28 @@ export class Arena { * Applies each `ArenaTag` in this Arena, based on which side (self, enemy, or both) is passed in as a parameter * @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply * @param side {@linkcode ArenaTagSide} which side's arena tags to apply + * @param simulated if `true`, this applies arena tags without changing game state * @param args array of parameters that the called upon tags may need */ - applyTagsForSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide, ...args: unknown[]): void { + applyTagsForSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide, simulated: boolean, ...args: unknown[]): void { let tags = typeof tagType === "string" ? this.tags.filter(t => t.tagType === tagType) : this.tags.filter(t => t instanceof tagType); if (side !== ArenaTagSide.BOTH) { tags = tags.filter(t => t.side === side); } - tags.forEach(t => t.apply(this, args)); + tags.forEach(t => t.apply(this, simulated, ...args)); } /** * Applies the specified tag to both sides (ie: both user and trainer's tag that match the Tag specified) * by calling {@linkcode applyTagsForSide()} * @param tagType Either an {@linkcode ArenaTagType} string, or an actual {@linkcode ArenaTag} class to filter which ones to apply + * @param simulated if `true`, this applies arena tags without changing game state * @param args array of parameters that the called upon tags may need */ - applyTags(tagType: ArenaTagType | Constructor, ...args: unknown[]): void { - this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); + applyTags(tagType: ArenaTagType | Constructor, simulated: boolean, ...args: unknown[]): void { + this.applyTagsForSide(tagType, ArenaTagSide.BOTH, simulated, ...args); } /** diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 0204672cabd..8eca37f38ac 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1538,7 +1538,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder); applyPreAttackAbAttrs(MoveTypeChangeAbAttr, this, null, move, simulated, moveTypeHolder); - this.scene.arena.applyTags(ArenaTagType.ION_DELUGE, moveTypeHolder); + this.scene.arena.applyTags(ArenaTagType.ION_DELUGE, simulated, moveTypeHolder); if (this.getTag(BattlerTagType.ELECTRIFIED)) { moveTypeHolder.value = Type.ELECTRIC; } @@ -2605,7 +2605,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Reduces damage if this Pokemon has a relevant screen (e.g. Light Screen for special attacks) */ const screenMultiplier = new Utils.NumberHolder(1); - this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier); + this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, simulated, moveCategory, screenMultiplier); /** * For each {@linkcode HitsTagAttr} the move has, doubles the damage of the move if: diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index e9bff882367..dc880f85e23 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -141,7 +141,7 @@ export class MoveEffectPhase extends PokemonPhase { const bypassIgnoreProtect = new Utils.BooleanHolder(false); /** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */ if (!this.move.getMove().isAllyTarget()) { - this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect); + this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, false, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect); } /** Is the target protected by Protect, etc. or a relevant conditional protection effect? */ diff --git a/src/phases/post-summon-phase.ts b/src/phases/post-summon-phase.ts index b99c0b90fd8..617bb8b1cfe 100644 --- a/src/phases/post-summon-phase.ts +++ b/src/phases/post-summon-phase.ts @@ -20,7 +20,7 @@ export class PostSummonPhase extends PokemonPhase { if (pokemon.status?.effect === StatusEffect.TOXIC) { pokemon.status.turnCount = 0; } - this.scene.arena.applyTags(ArenaTrapTag, pokemon); + this.scene.arena.applyTags(ArenaTrapTag, false, pokemon); // If this is mystery encounter and has post summon phase tag, apply post summon effects if (this.scene.currentBattle.isBattleMysteryEncounter() && pokemon.findTags(t => t instanceof MysteryEncounterPostSummonTag).length > 0) { diff --git a/src/phases/stat-stage-change-phase.ts b/src/phases/stat-stage-change-phase.ts index bfe19ea9ca5..4c13b883445 100644 --- a/src/phases/stat-stage-change-phase.ts +++ b/src/phases/stat-stage-change-phase.ts @@ -64,8 +64,7 @@ export class StatStageChangePhase extends PokemonPhase { const cancelled = new BooleanHolder(false); if (!this.selfTarget && stages.value < 0) { - // TODO: Include simulate boolean when tag applications can be simulated - this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, cancelled); + this.scene.arena.applyTagsForSide(MistTag, pokemon.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, false, cancelled); } if (!cancelled.value && !this.selfTarget && stages.value < 0) { diff --git a/src/phases/turn-start-phase.ts b/src/phases/turn-start-phase.ts index 627cee4b06a..25c079007fd 100644 --- a/src/phases/turn-start-phase.ts +++ b/src/phases/turn-start-phase.ts @@ -45,7 +45,7 @@ export class TurnStartPhase extends FieldPhase { // Next, a check for Trick Room is applied to determine sort order. const speedReversed = new Utils.BooleanHolder(false); - this.scene.arena.applyTags(TrickRoomTag, speedReversed); + this.scene.arena.applyTags(TrickRoomTag, false, speedReversed); // Adjust the sort function based on whether Trick Room is active. orderedTargets.sort((a: Pokemon, b: Pokemon) => { diff --git a/src/test/moves/aurora_veil.test.ts b/src/test/moves/aurora_veil.test.ts index e71d4ab9d11..243ba3a3269 100644 --- a/src/test/moves/aurora_veil.test.ts +++ b/src/test/moves/aurora_veil.test.ts @@ -111,7 +111,7 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (defender.scene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side)) { - defender.scene.arena.applyTagsForSide(ArenaTagType.AURORA_VEIL, side, move.category, defender.scene.currentBattle.double, multiplierHolder); + defender.scene.arena.applyTagsForSide(ArenaTagType.AURORA_VEIL, side, false, move.category, multiplierHolder); } return move.power * multiplierHolder.value; diff --git a/src/test/moves/light_screen.test.ts b/src/test/moves/light_screen.test.ts index 2308458003d..11b8144bb4e 100644 --- a/src/test/moves/light_screen.test.ts +++ b/src/test/moves/light_screen.test.ts @@ -94,7 +94,7 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (defender.scene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side)) { - defender.scene.arena.applyTagsForSide(ArenaTagType.LIGHT_SCREEN, side, move.category, defender.scene.currentBattle.double, multiplierHolder); + defender.scene.arena.applyTagsForSide(ArenaTagType.LIGHT_SCREEN, side, false, move.category, multiplierHolder); } return move.power * multiplierHolder.value; diff --git a/src/test/moves/reflect.test.ts b/src/test/moves/reflect.test.ts index 41a10988552..b18b2423895 100644 --- a/src/test/moves/reflect.test.ts +++ b/src/test/moves/reflect.test.ts @@ -94,7 +94,7 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (defender.scene.arena.getTagOnSide(ArenaTagType.REFLECT, side)) { - defender.scene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, move.category, defender.scene.currentBattle.double, multiplierHolder); + defender.scene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, move.category, multiplierHolder); } return move.power * multiplierHolder.value;

v^J8bg(f%?PGJxL)) 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 literal 10686 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%0Dy!50Qvv` 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 diff --git a/public/images/pokemon/variant/6706_3.json b/public/images/pokemon/variant/6706_3.json deleted file mode 100644 index 615ca90e004..00000000000 --- a/public/images/pokemon/variant/6706_3.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 82, - "h": 82 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 82, - "h": 72 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - }, - "frame": { - "x": 0, - "y": 0, - "w": 82, - "h": 72 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:02eb46aa66ac70df612e129b7801a85c:a77cca14b23f4f3aece64d1a82449a0f:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/6706_3.png b/public/images/pokemon/variant/6706_3.png deleted file mode 100644 index 001cab641f1c1c4009d6264baa14114a1a1b140c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5177 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%0Dy!50Qvv` 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%0Dy!50Qvv` z0D$NK0Cg|`0P0`>06Lfe02gqax=}m;000SaNLh0L01FZT01FZU(%pXi0000RbVXQn zQ*UN;cVTj607GSLb9r+hQ*?D?X>TA@Z*OeDr{R1600*W?L_t(|obB9iXjOF_$MN?K zD=OR8kcC>aS;8JjB}ud_gUaHL{!tVo3R^vppmISyh*C<89u#OM#2%E11YYTsvf;vLAetUqL~Fcd^Ycz6(bPl%hM_?`I5oZNc7y_2;Z+k(QY;oY zaBR2ljr7dxa)Qvn3|IVNg6-RPyVD!qe8#ukc@Hk*?+*@y0!HH10-Qt#)Zrf8|C(q0 z$Ij`byE_j++qFwPXzF&ug;Btd7zG%Lmo=PvNfMQyv$p`MaDsMx`h@2V_RQ;Y1P|br zcb;bFiWdT3bGR^StisrMP0bC?t?kbRzL0I=fCWW|EEWrFSiTaNfz#2z40n9`M6Rj1 z!2tphg8_#2VCY#1Jx0TbahH8(g*oA0HiWeJm$6V@NODut$|rDcf` zFAf}JjCX7Mb1q!azmN>9RGRY5yXc~Mv44)l1Q-_&{D86Xr0V3C3ImKAK{G*xEwjnk z^q>or1P;7-iB1?Q9{2&1rc|1;g0WpskKiSRgJ?Vx9E9Lyk+>j1>n}fk3V`pL&NXhg z{P-!7z?r%)K~=Y~q0x7toO2w9rsf9crLJAP|KY2tjzMaYRFY{M9Y+5HCxKI2Q)-3= zMp(0PwP)V?wOi@#&NDPH;^Ij^(1Qo?(NM4Tn+;0RT~Ob4Y zNzGSpKlL*?<=6gM7TvLMJz>lX!nxYaFZp&0(E>pa$;Ur_z zgT8s{u`diS3=X34)MMcKwOaw)GjCnsMI{I?3QiIQc;v*N0RHQ|tnkuamZUjWn(%Dl zB%gfL&0Y6x_d$kslBTv0?*l2PbPTPf`fEbcWC@506&hu=;AeA_zD242737U#s2>fo-N#=@uU2F z`OWV1$vrFG-S+DCS6MaCL&u`~dH90X@LP}nH@rQiD~`h(t^@E-+ozu80;lJ! z$Fqe4*qop1+HgoQwUr4TR30!%)xjCOw|hV4_UjkA>wW(15uV-sSN!pLYD)@gAI}yJ%zZvN z?#b>en3>=~ZST5c(}SKtJYflHlByL5#D;?m@t`t*W7C7|T=4>}?F%B;4eKtecuC-( z&hX;EL3C?=bAGOq6b`hzXSEDiDowFr`ATazm}%XcZW(5k%<&+5z*1?7_a5nry#W!lI4{$PzIY9@IYI(7*_HJaD6LzUi(Qq(clG&|S}fGBm?x zctzAxK4D3O2el0-t@gwH+kj}qpz(K634IPD3J~lNFFidoT;lgRcH{rj;Hmd0HuZJ7 z-4}p`4UNWKI__XdN$3+maR9-58FK0AL9Up8o;&@`A9>F*Fry{~G~vOS4l=;aZyob& zlLQVj!jqT~gYd#oAs(KbBviPKKW*~dd(b<`Nt&N}A|Kp~3n&rD#pmzO3@_+R3Nt5( z%N}~lomQ(3ye+7-ttg4rBcn+H0Sdc1U8G-+{msFXD}Z2BiHf(UbR~!Tw?#HT1yHX# ziFA-FiU-L7b-a$wUB>x>D!JuUqoG^jVK55k!a&sl&m{#jg4|XBc25;h+q7YE(!F#|(l4 z^Oq^;h=<0H27XyXxl&dehVPBA-LfGI{y6T_?@q#KS z46qt-P-Q&uqO`Uz2t4Qq;Z+L`Y7h_3+=B?)b%zV1y!Y>}s=+}G;K7*_i9P~KtwJ5I zniVIh2|S5@Hdb*=6`J(kLsqFNys((?!2U_^a-?qXAb9tN>QrG};Ymyv+q0EdXPL!I oky*SHnZ-+yS-cdP#Y>U@0GLZqmFWb_C;$Ke07*qoM6N<$g0>MU1ONa4 diff --git a/public/images/pokemon/variant/back/6706_3.json b/public/images/pokemon/variant/back/6706_3.json deleted file mode 100644 index 9a97ce27059..00000000000 --- a/public/images/pokemon/variant/back/6706_3.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 79, - "h": 79 - }, - "scale": 1, - "frames": [ - { - "filename": "0001.png", - "rotated": false, - "trimmed": false, - "sourceSize": { - "w": 79, - "h": 73 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - }, - "frame": { - "x": 0, - "y": 0, - "w": 79, - "h": 73 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:64f7e6dfa489012922487e45ba53d557:4d24652b372939abe499497c4b6647b0:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/back/6706_3.png b/public/images/pokemon/variant/back/6706_3.png deleted file mode 100644 index fb780d01499a23a4f4ab714732d1d56d7e094f2d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4765 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%0Dy!50Qvv` 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 diff --git a/public/images/pokemon/variant/exp/6706.json b/public/images/pokemon/variant/exp/6706.json new file mode 100644 index 00000000000..2a5f8e112ee --- /dev/null +++ b/public/images/pokemon/variant/exp/6706.json @@ -0,0 +1,40 @@ +{ + "1": { + "566678": "0e6296", + "e0e4f4": "513981", + "625287": "4e4094", + "536273": "1f1233", + "988b98": "b24c86", + "36404c": "0c5474", + "c4cce1": "3b235c", + "e6d3e9": "f1a4c5", + "bfacc1": "da75a5", + "515f70": "197497", + "c5cee3": "63cee1", + "b791f2": "c7a1e5", + "8b93a6": "3aa8c4", + "80737f": "8a2166", + "4b454f": "6f1357", + "9170b9": "8b69c3", + "8e96aa": "301848" + }, + "2": { + "566678": "8e480b", + "e0e4f4": "176463", + "625287": "274159", + "536273": "02262c", + "988b98": "2a6563", + "36404c": "842401", + "c4cce1": "0d484a", + "e6d3e9": "9cead8", + "bfacc1": "5db6a9", + "515f70": "a34205", + "c5cee3": "f7af58", + "b791f2": "4a9699", + "8b93a6": "d27e26", + "80737f": "2b736f", + "4b454f": "194f51", + "9170b9": "2f667c", + "8e96aa": "073338" + } +} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_2.json b/public/images/pokemon/variant/exp/6706_2.json deleted file mode 100644 index cb2ddfb1a12..00000000000 --- a/public/images/pokemon/variant/exp/6706_2.json +++ /dev/null @@ -1,2015 +0,0 @@ -{ - "textures": [ - { - "image": "6706_2.png", - "format": "RGBA8888", - "size": { - "w": 508, - "h": 508 - }, - "scale": 1, - "frames": [ - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 26, - "w": 59, - "h": 61 - }, - "frame": { - "x": 0, - "y": 0, - "w": 59, - "h": 61 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 18, - "w": 87, - "h": 71 - }, - "frame": { - "x": 405, - "y": 68, - "w": 87, - "h": 71 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 19, - "w": 86, - "h": 72 - }, - "frame": { - "x": 0, - "y": 131, - "w": 86, - "h": 72 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 72 - }, - "frame": { - "x": 0, - "y": 203, - "w": 85, - "h": 72 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 83, - "h": 72 - }, - "frame": { - "x": 85, - "y": 206, - "w": 83, - "h": 72 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 18, - "w": 84, - "h": 73 - }, - "frame": { - "x": 168, - "y": 206, - "w": 84, - "h": 73 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 252, - "y": 210, - "w": 85, - "h": 73 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 85, - "h": 75 - }, - "frame": { - "x": 157, - "y": 428, - "w": 85, - "h": 75 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:62a4a665074efb5def1545546995dc5b:de2788ebeab6b42f331926f332da5125:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_2.png b/public/images/pokemon/variant/exp/6706_2.png deleted file mode 100644 index 7e7dfa8e05a910d741fea8e3952947d1e304738f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55978 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 diff --git a/public/images/pokemon/variant/exp/6706_3.json b/public/images/pokemon/variant/exp/6706_3.json deleted file mode 100644 index 8c9b16b80ab..00000000000 --- a/public/images/pokemon/variant/exp/6706_3.json +++ /dev/null @@ -1,2015 +0,0 @@ -{ - "textures": [ - { - "image": "6706_3.png", - "format": "RGBA8888", - "size": { - "w": 508, - "h": 508 - }, - "scale": 1, - "frames": [ - { - "filename": "0074.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 26, - "w": 59, - "h": 61 - }, - "frame": { - "x": 0, - "y": 0, - "w": 59, - "h": 61 - } - }, - { - "filename": "0073.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0075.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 26, - "y": 25, - "w": 56, - "h": 63 - }, - "frame": { - "x": 59, - "y": 0, - "w": 56, - "h": 63 - } - }, - { - "filename": "0064.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0084.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 115, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0065.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0083.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 29, - "y": 21, - "w": 54, - "h": 65 - }, - "frame": { - "x": 168, - "y": 0, - "w": 54, - "h": 65 - } - }, - { - "filename": "0066.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0082.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 28, - "y": 21, - "w": 53, - "h": 65 - }, - "frame": { - "x": 222, - "y": 0, - "w": 53, - "h": 65 - } - }, - { - "filename": "0067.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0081.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 55, - "h": 66 - }, - "frame": { - "x": 275, - "y": 0, - "w": 55, - "h": 66 - } - }, - { - "filename": "0072.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0076.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 23, - "w": 54, - "h": 66 - }, - "frame": { - "x": 330, - "y": 0, - "w": 54, - "h": 66 - } - }, - { - "filename": "0063.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0085.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 25, - "y": 21, - "w": 54, - "h": 67 - }, - "frame": { - "x": 384, - "y": 0, - "w": 54, - "h": 67 - } - }, - { - "filename": "0062.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0086.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 23, - "y": 21, - "w": 52, - "h": 68 - }, - "frame": { - "x": 438, - "y": 0, - "w": 52, - "h": 68 - } - }, - { - "filename": "0068.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0080.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 68 - }, - "frame": { - "x": 0, - "y": 61, - "w": 53, - "h": 68 - } - }, - { - "filename": "0071.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0077.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 18, - "y": 22, - "w": 52, - "h": 68 - }, - "frame": { - "x": 53, - "y": 63, - "w": 52, - "h": 68 - } - }, - { - "filename": "0060.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0088.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 19, - "y": 21, - "w": 55, - "h": 69 - }, - "frame": { - "x": 105, - "y": 65, - "w": 55, - "h": 69 - } - }, - { - "filename": "0061.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0087.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 21, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 160, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0069.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0079.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 16, - "y": 21, - "w": 53, - "h": 69 - }, - "frame": { - "x": 213, - "y": 65, - "w": 53, - "h": 69 - } - }, - { - "filename": "0070.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0078.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 15, - "y": 21, - "w": 52, - "h": 70 - }, - "frame": { - "x": 266, - "y": 66, - "w": 52, - "h": 70 - } - }, - { - "filename": "0006.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0022.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0038.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 71 - }, - "frame": { - "x": 318, - "y": 67, - "w": 87, - "h": 71 - } - }, - { - "filename": "0051.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 18, - "w": 87, - "h": 71 - }, - "frame": { - "x": 405, - "y": 68, - "w": 87, - "h": 71 - } - }, - { - "filename": "0003.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 19, - "w": 86, - "h": 72 - }, - "frame": { - "x": 0, - "y": 131, - "w": 86, - "h": 72 - } - }, - { - "filename": "0004.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0020.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0036.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 86, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0005.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 19, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0021.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0037.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 173, - "y": 134, - "w": 87, - "h": 72 - } - }, - { - "filename": "0007.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0023.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0039.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 260, - "y": 138, - "w": 87, - "h": 72 - } - }, - { - "filename": "0008.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0024.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0040.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 18, - "w": 87, - "h": 72 - }, - "frame": { - "x": 347, - "y": 139, - "w": 87, - "h": 72 - } - }, - { - "filename": "0059.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0089.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 5, - "w": 74, - "h": 80 - }, - "frame": { - "x": 434, - "y": 139, - "w": 74, - "h": 80 - } - }, - { - "filename": "0050.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 72 - }, - "frame": { - "x": 0, - "y": 203, - "w": 85, - "h": 72 - } - }, - { - "filename": "0052.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 13, - "w": 83, - "h": 72 - }, - "frame": { - "x": 85, - "y": 206, - "w": 83, - "h": 72 - } - }, - { - "filename": "0001.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 18, - "w": 84, - "h": 73 - }, - "frame": { - "x": 168, - "y": 206, - "w": 84, - "h": 73 - } - }, - { - "filename": "0002.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 252, - "y": 210, - "w": 85, - "h": 73 - } - }, - { - "filename": "0009.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0025.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0041.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 337, - "y": 211, - "w": 86, - "h": 73 - } - }, - { - "filename": "0018.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0034.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 85, - "h": 73 - }, - "frame": { - "x": 423, - "y": 219, - "w": 85, - "h": 73 - } - }, - { - "filename": "0011.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0027.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0043.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 1, - "y": 16, - "w": 85, - "h": 74 - }, - "frame": { - "x": 0, - "y": 275, - "w": 85, - "h": 74 - } - }, - { - "filename": "0010.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0026.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0042.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 85, - "y": 279, - "w": 86, - "h": 73 - } - }, - { - "filename": "0053.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0095.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 4, - "w": 81, - "h": 74 - }, - "frame": { - "x": 171, - "y": 279, - "w": 81, - "h": 74 - } - }, - { - "filename": "0017.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0033.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 3, - "y": 16, - "w": 84, - "h": 74 - }, - "frame": { - "x": 252, - "y": 283, - "w": 84, - "h": 74 - } - }, - { - "filename": "0019.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0035.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 17, - "w": 86, - "h": 73 - }, - "frame": { - "x": 336, - "y": 284, - "w": 86, - "h": 73 - } - }, - { - "filename": "0054.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0094.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 422, - "y": 292, - "w": 80, - "h": 74 - } - }, - { - "filename": "0055.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0093.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 0, - "w": 79, - "h": 74 - }, - "frame": { - "x": 0, - "y": 349, - "w": 79, - "h": 74 - } - }, - { - "filename": "0056.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0092.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 80, - "h": 74 - }, - "frame": { - "x": 79, - "y": 352, - "w": 80, - "h": 74 - } - }, - { - "filename": "0012.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0028.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0044.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 83, - "h": 75 - }, - "frame": { - "x": 159, - "y": 353, - "w": 83, - "h": 75 - } - }, - { - "filename": "0013.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0029.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0045.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 242, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0015.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0031.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0047.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 81, - "h": 75 - }, - "frame": { - "x": 323, - "y": 357, - "w": 81, - "h": 75 - } - }, - { - "filename": "0016.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0032.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0048.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 4, - "y": 15, - "w": 82, - "h": 75 - }, - "frame": { - "x": 404, - "y": 366, - "w": 82, - "h": 75 - } - }, - { - "filename": "0057.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0091.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 79, - "h": 75 - }, - "frame": { - "x": 0, - "y": 423, - "w": 79, - "h": 75 - } - }, - { - "filename": "0058.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0090.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 0, - "y": 0, - "w": 78, - "h": 75 - }, - "frame": { - "x": 79, - "y": 426, - "w": 78, - "h": 75 - } - }, - { - "filename": "0049.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 2, - "y": 15, - "w": 85, - "h": 75 - }, - "frame": { - "x": 157, - "y": 428, - "w": 85, - "h": 75 - } - }, - { - "filename": "0014.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0030.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - }, - { - "filename": "0046.png", - "rotated": false, - "trimmed": true, - "sourceSize": { - "w": 88, - "h": 91 - }, - "spriteSourceSize": { - "x": 5, - "y": 14, - "w": 79, - "h": 76 - }, - "frame": { - "x": 242, - "y": 432, - "w": 79, - "h": 76 - } - } - ] - } - ], - "meta": { - "app": "https://www.codeandweb.com/texturepacker", - "version": "3.0", - "smartupdate": "$TexturePacker:SmartUpdate:62a4a665074efb5def1545546995dc5b:de2788ebeab6b42f331926f332da5125:d60cc2e5ae2bd18de8ee3ab0649593ee$" - } -} \ No newline at end of file diff --git a/public/images/pokemon/variant/exp/6706_3.png b/public/images/pokemon/variant/exp/6706_3.png deleted file mode 100644 index 3ad44f4bbf52818265762c20047ff43d445b9c85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56190 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