diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 0775da48112..21d381a9713 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -55,7 +55,6 @@ import {UiInputs} from "./ui-inputs"; import { NewArenaEvent } from "./events/battle-scene"; import { ArenaFlyout } from "./ui/arena-flyout"; import { EaseType } from "#enums/ease-type"; -import { Abilities } from "#enums/abilities"; import { BattleSpec } from "#enums/battle-spec"; import { BattleStyle } from "#enums/battle-style"; import { Biome } from "#enums/biome"; @@ -1153,12 +1152,6 @@ export default class BattleScene extends SceneBase { playerField.forEach((_, p) => this.pushPhase(new ReturnPhase(this, p))); for (const pokemon of this.getParty()) { - // Only trigger form change when Eiscue is in Noice form - // Hardcoded Eiscue for now in case it is fused with another pokemon - if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) { - this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger); - } - pokemon.resetBattleData(); applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon); } diff --git a/src/data/ability.ts b/src/data/ability.ts index edc213099a9..961c724416a 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -245,25 +245,6 @@ export class PreDefendAbAttr extends AbAttr { } } -export class PreDefendFormChangeAbAttr extends PreDefendAbAttr { - private formFunc: (p: Pokemon) => integer; - - constructor(formFunc: ((p: Pokemon) => integer)) { - super(true); - - this.formFunc = formFunc; - } - - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); - return true; - } - - return false; - } -} export class PreDefendFullHpEndureAbAttr extends PreDefendAbAttr { applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (pokemon.isFullHp() && @@ -330,21 +311,6 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip } } -export class PreDefendMoveDamageToOneAbAttr extends ReceivedMoveDamageMultiplierAbAttr { - constructor(condition: PokemonDefendCondition) { - super(condition, 1); - } - - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.condition(pokemon, attacker, move)) { - (args[0] as Utils.NumberHolder).value = Math.floor(pokemon.getMaxHp() / 8); - return true; - } - - return false; - } -} - /** * Determines whether a Pokemon is immune to a move because of an ability. * @extends PreDefendAbAttr @@ -539,45 +505,6 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { } } -export class PostDefendDisguiseAbAttr extends PostDefendAbAttr { - - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - if (pokemon.formIndex === 0 && pokemon.battleData.hitCount !== 0 && (move.category === MoveCategory.SPECIAL || move.category === MoveCategory.PHYSICAL)) { - - const recoilDamage = Math.ceil((pokemon.getMaxHp() / 8) - attacker.turnData.damageDealt); - if (!recoilDamage) { - return false; - } - pokemon.damageAndUpdate(recoilDamage, HitResult.OTHER); - pokemon.turnData.damageTaken += recoilDamage; - pokemon.scene.queueMessage(i18next.t("abilityTriggers:postDefendDisguise", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - return true; - } - - return false; - } -} - -export class PostDefendFormChangeAbAttr extends PostDefendAbAttr { - private formFunc: (p: Pokemon) => integer; - - constructor(formFunc: ((p: Pokemon) => integer)) { - super(true); - - this.formFunc = formFunc; - } - - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const formIndex = this.formFunc(pokemon); - if (formIndex !== pokemon.formIndex) { - pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); - return true; - } - - return false; - } -} - export class FieldPriorityMoveImmunityAbAttr extends PreDefendAbAttr { applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { const attackPriority = new Utils.IntegerHolder(move.priority); @@ -4045,34 +3972,44 @@ export class PostSummonStatChangeOnArenaAbAttr extends PostSummonStatChangeAbAtt } /** - * Takes no damage from the first hit of a physical move. - * This is used in Ice Face ability. + * Takes no damage from the first hit of a damaging move. + * This is used in the Disguise and Ice Face abilities. + * @extends ReceivedMoveDamageMultiplierAbAttr */ -export class IceFaceBlockPhysicalAbAttr extends ReceivedMoveDamageMultiplierAbAttr { +export class FormBlockDamageAbAttr extends ReceivedMoveDamageMultiplierAbAttr { private multiplier: number; + private tagType: BattlerTagType; + private recoilDamageFunc: ((pokemon: Pokemon) => number) | undefined; + private triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string; - constructor(condition: PokemonDefendCondition, multiplier: number) { + constructor(condition: PokemonDefendCondition, multiplier: number, tagType: BattlerTagType, triggerMessageFunc: (pokemon: Pokemon, abilityName: string) => string, recoilDamageFunc?: (pokemon: Pokemon) => number) { super(condition, multiplier); this.multiplier = multiplier; + this.tagType = tagType; + this.recoilDamageFunc = recoilDamageFunc; + this.triggerMessageFunc = triggerMessageFunc; } /** - * Applies the Ice Face pre-defense ability to the Pokémon. - * Removes BattlerTagType.ICE_FACE when hit by physical attack and is in Ice Face form. + * 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 Ice Face ability. - * @param {boolean} passive - Whether the ability is passive. - * @param {Pokemon} attacker - The attacking Pokémon. - * @param {PokemonMove} move - The move being used. - * @param {Utils.BooleanHolder} cancelled - A holder for whether the move was cancelled. - * @param {any[]} args - Additional arguments. - * @returns {boolean} - Whether the immunity was applied. + * @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. */ applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean { if (this.condition(pokemon, attacker, move)) { (args[0] as Utils.NumberHolder).value = this.multiplier; - pokemon.removeTag(BattlerTagType.ICE_FACE); + pokemon.removeTag(this.tagType); + if (this.recoilDamageFunc) { + pokemon.damageAndUpdate(this.recoilDamageFunc(pokemon), HitResult.OTHER); + } return true; } @@ -4080,14 +4017,14 @@ export class IceFaceBlockPhysicalAbAttr extends ReceivedMoveDamageMultiplierAbAt } /** - * Gets the message triggered when the Pokémon avoids damage using the Ice Face ability. - * @param {Pokemon} pokemon - The Pokémon with the Ice Face ability. - * @param {string} abilityName - The name of the ability. - * @param {...any} args - Additional arguments. - * @returns {string} - The trigger message. + * 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. */ getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { - return i18next.t("abilityTriggers:iceFaceAvoidedDamage", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName }); + return this.triggerMessageFunc(pokemon, abilityName); } } @@ -5004,20 +4941,18 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) .bypassFaint(), new Ability(Abilities.DISGUISE, 7) - .attr(PreDefendMoveDamageToOneAbAttr, (target, user, move) => target.formIndex === 0 && target.getAttackTypeEffectiveness(move.type, user) > 0) - .attr(PostSummonFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) - .attr(PostBattleInitFormChangeAbAttr, () => 0) - .attr(PostDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) - .attr(PreDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) - .attr(PostDefendDisguiseAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .attr(NoFusionAbilityAbAttr) - .bypassFaint() - .ignorable() - .partial(), + // 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.getAttackTypeEffectiveness(move.type, user) > 0, 0, BattlerTagType.DISGUISE, + (pokemon, abilityName) => i18next.t("abilityTriggers:disguiseAvoidedDamage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName: abilityName }), + (pokemon) => Math.floor(pokemon.getMaxHp() / 8)) + .attr(PostBattleInitFormChangeAbAttr, () => 0) + .ignorable(), new Ability(Abilities.BATTLE_BOND, 7) .attr(PostVictoryFormChangeAbAttr, () => 2) .attr(PostBattleInitFormChangeAbAttr, () => 1) @@ -5166,7 +5101,9 @@ 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(IceFaceBlockPhysicalAbAttr, (target, user, move) => move.category === MoveCategory.PHYSICAL && !!target.getTag(BattlerTagType.ICE_FACE), 0) + .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) .ignorable(), new Ability(Abilities.POWER_SPOT, 8) .attr(AllyMoveCategoryPowerBoostAbAttr, [MoveCategory.SPECIAL, MoveCategory.PHYSICAL], 1.3), diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 3b6e005d7e8..898bbbc306b 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1558,36 +1558,25 @@ export class GroundedTag extends BattlerTag { } } -/** - * Provides the Ice Face ability's effects. - */ -export class IceFaceTag extends BattlerTag { - constructor(sourceMove: Moves) { - super(BattlerTagType.ICE_FACE, BattlerTagLapseType.CUSTOM, 1, sourceMove); +/** Common attributes of form change abilities that block damage */ +export class FormBlockDamageTag extends BattlerTag { + constructor(tagType: BattlerTagType) { + super(tagType, BattlerTagLapseType.CUSTOM, 1); } /** - * Determines if the Ice Face tag can be added to the Pokémon. - * @param {Pokemon} pokemon - The Pokémon to which the tag might be added. - * @returns {boolean} - True if the tag can be added, false otherwise. + * Determines if the tag can be added to the Pokémon. + * @param {Pokemon} pokemon The Pokémon to which the tag might be added. + * @returns {boolean} True if the tag can be added, false otherwise. */ canAdd(pokemon: Pokemon): boolean { - const weatherType = pokemon.scene.arena.weather?.weatherType; - const isWeatherSnowOrHail = weatherType === WeatherType.HAIL || weatherType === WeatherType.SNOW; - const isFormIceFace = pokemon.formIndex === 0; - - - // Hard code Eiscue for now, this is to prevent the game from crashing if fused pokemon has Ice Face - if ((pokemon.species.speciesId === Species.EISCUE && isFormIceFace) || isWeatherSnowOrHail) { - return true; - } - return false; + return pokemon.formIndex === 0; } /** - * Applies the Ice Face tag to the Pokémon. - * Triggers a form change to Ice Face if the Pokémon is not in its Ice Face form. - * @param {Pokemon} pokemon - The Pokémon to which the tag is added. + * Applies the tag to the Pokémon. + * Triggers a form change if the Pokémon is not in its defense form. + * @param {Pokemon} pokemon The Pokémon to which the tag is added. */ onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); @@ -1598,9 +1587,9 @@ export class IceFaceTag extends BattlerTag { } /** - * Removes the Ice Face tag from the Pokémon. - * Triggers a form change to Noice when the tag is removed. - * @param {Pokemon} pokemon - The Pokémon from which the tag is removed. + * Removes the tag from the Pokémon. + * Triggers a form change when the tag is removed. + * @param {Pokemon} pokemon The Pokémon from which the tag is removed. */ onRemove(pokemon: Pokemon): void { super.onRemove(pokemon); @@ -1609,6 +1598,24 @@ export class IceFaceTag extends BattlerTag { } } +/** Provides the additional weather-based effects of the Ice Face ability */ +export class IceFaceBlockDamageTag extends FormBlockDamageTag { + constructor(tagType: BattlerTagType) { + super(tagType); + } + + /** + * Determines if the tag can be added to the Pokémon. + * @param {Pokemon} pokemon The Pokémon to which the tag might be added. + * @returns {boolean} True if the tag can be added, false otherwise. + */ + canAdd(pokemon: Pokemon): boolean { + const weatherType = pokemon.scene.arena.weather?.weatherType; + const isWeatherSnowOrHail = weatherType === WeatherType.HAIL || weatherType === WeatherType.SNOW; + + return super.canAdd(pokemon) || isWeatherSnowOrHail; + } +} /** * Battler tag enabling the Stockpile mechanic. This tag handles: @@ -1890,7 +1897,9 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.DESTINY_BOND: return new DestinyBondTag(sourceMove, sourceId); case BattlerTagType.ICE_FACE: - return new IceFaceTag(sourceMove); + return new IceFaceBlockDamageTag(tagType); + case BattlerTagType.DISGUISE: + return new FormBlockDamageTag(tagType); case BattlerTagType.STOCKPILING: return new StockpilingTag(sourceMove); case BattlerTagType.OCTOLOCK: diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index a55b9186839..c4cf4184f57 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -372,6 +372,9 @@ export function getSpeciesFormChangeMessage(pokemon: Pokemon, formChange: Specie if (isRevert) { return i18next.t("battlePokemonForm:revertChange", { pokemonName: getPokemonNameWithAffix(pokemon) }); } + if (pokemon.getAbility().id === Abilities.DISGUISE) { + return i18next.t("battlePokemonForm:disguiseChange"); + } return i18next.t("battlePokemonForm:formChange", { preName }); } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index fd1455eab6c..5aab5036caf 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -60,6 +60,7 @@ export enum BattlerTagType { DESTINY_BOND = "DESTINY_BOND", CENTER_OF_ATTENTION = "CENTER_OF_ATTENTION", ICE_FACE = "ICE_FACE", + DISGUISE = "DISGUISE", STOCKPILING = "STOCKPILING", RECEIVE_DOUBLE_DAMAGE = "RECEIVE_DOUBLE_DAMAGE", ALWAYS_GET_HIT = "ALWAYS_GET_HIT", diff --git a/src/locales/ca_ES/ability-trigger.ts b/src/locales/ca_ES/ability-trigger.ts index ce41a964922..2bdd17baa56 100644 --- a/src/locales/ca_ES/ability-trigger.ts +++ b/src/locales/ca_ES/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", "windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!", "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}'s disguise was busted!", "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", diff --git a/src/locales/ca_ES/pokemon-form.ts b/src/locales/ca_ES/pokemon-form.ts index e8d6fb8df4a..922f23fafea 100644 --- a/src/locales/ca_ES/pokemon-form.ts +++ b/src/locales/ca_ES/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", "revertChange": "{{pokemonName}} reverted\nto its original form!", "formChange": "{{preName}} changed form!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 30433ec9bdd..72023a842b3 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -4,7 +4,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage": "{{pokemonName}} wurde durch {{abilityName}} vor Rückstoß geschützt!", "badDreams": "{{pokemonName}} ist in einem Alptraum gefangen!", "costar": "{{pokemonName}} kopiert die Statusveränderungen von {{allyName}}!", - "iceFaceAvoidedDamage": "{{pokemonName}} wehrt Schaden mit {{abilityName}} ab!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} wehrt Schaden mit {{abilityName}} ab!", "perishBody": "Durch {{abilityName}} von {{pokemonName}} werden beide Pokémon nach drei Runden K.O. gehen!", "poisonHeal": "{{abilityName}} von {{pokemonName}} füllte einige KP auf!", "trace": "{{pokemonName}} kopiert {{abilityName}} von {{targetName}}!", @@ -13,7 +13,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockItemTheft": "{{abilityName}} von {{pokemonNameWithAffix}} verhindert Item-Diebstahl!", "typeImmunityHeal": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} vermeidet Schaden mit {{abilityName}}!", - "postDefendDisguise": "Die Tarnung von {{pokemonNameWithAffix}} ist aufgeflogen!!", + "disguiseAvoidedDamage": "Die Tarnung von {{pokemonNameWithAffix}} ist aufgeflogen!!", "moveImmunity": "Es hat keine Wirkung auf {{pokemonNameWithAffix}}...", "reverseDrain": "{{pokemonNameWithAffix}} saugt Kloakensoße auf!", "postDefendTypeChange": "{{abilityName}} von {{pokemonNameWithAffix}} macht es zu einem {{typeName}}-Typ!", diff --git a/src/locales/de/pokemon-form.ts b/src/locales/de/pokemon-form.ts index 53ecc310411..0aadfc287e0 100644 --- a/src/locales/de/pokemon-form.ts +++ b/src/locales/de/pokemon-form.ts @@ -14,6 +14,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} hat sich zu {{pokemonName}} unendynamaximiert!", "revertChange": "{{pokemonName}} hat seine ursprüngliche Form zurückerlangt!", "formChange": "{{preName}} hat seine Form geändert!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/en/ability-trigger.ts b/src/locales/en/ability-trigger.ts index ce41a964922..035fe8371be 100644 --- a/src/locales/en/ability-trigger.ts +++ b/src/locales/en/ability-trigger.ts @@ -4,16 +4,16 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage": "{{pokemonName}}'s {{abilityName}}\nprotected it from recoil!", "badDreams": "{{pokemonName}} is tormented!", "costar": "{{pokemonName}} copied {{allyName}}'s stat changes!", - "iceFaceAvoidedDamage": "{{pokemonName}} avoided\ndamage with {{abilityName}}!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} avoided\ndamage with {{abilityName}}!", "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!", "poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!", "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", "windPowerCharged": "Being hit by {{moveName}} charged {{pokemonName}} with power!", "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}'s disguise was busted!", "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", - "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", diff --git a/src/locales/en/pokemon-form.ts b/src/locales/en/pokemon-form.ts index e8d6fb8df4a..922f23fafea 100644 --- a/src/locales/en/pokemon-form.ts +++ b/src/locales/en/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", "revertChange": "{{pokemonName}} reverted\nto its original form!", "formChange": "{{preName}} changed form!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/es/ability-trigger.ts b/src/locales/es/ability-trigger.ts index 6b1f66a11e3..60bc186e99d 100644 --- a/src/locales/es/ability-trigger.ts +++ b/src/locales/es/ability-trigger.ts @@ -7,13 +7,13 @@ export const abilityTriggers: SimpleTranslationEntries = { "iceFaceAvoidedDamage": "¡{{pokemonNameWithAffix}} evitó\ndaño con {{abilityName}}!", "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!", "poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!", - "trace": "{{pokemonName}} copied {{targetName}}'s\n{{abilityName}}!", + "trace": "¡{{pokemonName}} ha copiado la habilidad {{abilityName}} \nde {{targetName}}!", "windPowerCharged": "¡{{pokemonName}} se ha cargado de electricidad gracias a {{moveName}}!", - "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "quickDraw": "¡{{pokemonName}} ataca primero gracias a la habilidad Mano Rápida!", + "disguiseAvoidedDamage" : "¡El disfraz de {{pokemonNameWithAffix}} se ha roto!", "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", - "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", diff --git a/src/locales/es/pokemon-form.ts b/src/locales/es/pokemon-form.ts index 96e40bcfbbd..7098ba597f7 100644 --- a/src/locales/es/pokemon-form.ts +++ b/src/locales/es/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", "revertChange": "{{pokemonName}} reverted\nto its original form!", "formChange": "{{preName}} changed form!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/fr/ability-trigger.ts b/src/locales/fr/ability-trigger.ts index f6b9c306cd1..cd077993b4e 100644 --- a/src/locales/fr/ability-trigger.ts +++ b/src/locales/fr/ability-trigger.ts @@ -4,7 +4,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage" : "{{abilityName}}\nde {{pokemonName}} le protège du contrecoup !", "badDreams": "{{pokemonName}} a le sommeil agité !", "costar": "{{pokemonName}} copie les changements de stats\nde {{allyName}} !", - "iceFaceAvoidedDamage": "{{pokemonName}} évite les dégâts\navec {{abilityName}} !", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} évite les dégâts\navec {{abilityName}} !", "perishBody": "{{abilityName}} de {{pokemonName}}\nmettra les deux Pokémon K.O. dans trois tours !", "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaure un peu ses PV !", "trace": "{{pokemonName}} copie le talent {{abilityName}}\nde {{targetName}} !", @@ -13,7 +13,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nempêche son objet d’être volé !", "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} évite\nles dégâts avec {{abilityName}} !", - "postDefendDisguise": "Le déguisement de {{pokemonNameWithAffix}}\ntombe !", + "disguiseAvoidedDamage": "Le déguisement de {{pokemonNameWithAffix}}\ntombe !", "moveImmunity": "Ça n'affecte pas {{pokemonNameWithAffix}}…", "reverseDrain": "{{pokemonNameWithAffix}} aspire\nle suintement !", "postDefendTypeChange": "{{abilityName}} de {{pokemonNameWithAffix}}\nle transforme en type {{typeName}} !", diff --git a/src/locales/fr/pokemon-form.ts b/src/locales/fr/pokemon-form.ts index f96931fd0e9..51158356ef3 100644 --- a/src/locales/fr/pokemon-form.ts +++ b/src/locales/fr/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} devient\n{{pokemonName}} !", "revertChange": "{{pokemonName}} retourne\nà sa forme initiale !", "formChange": "{{preName}} change de forme !", + "disguiseChange": "Le déguisement absorbe l’attaque !", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index 37472dbdeab..95db5cbf26c 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -4,7 +4,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage" : "{{abilityName}} di {{pokemonName}}\nl'ha protetto dal contraccolpo!", "badDreams": "{{pokemonName}} è tormentato dagli incubi!", "costar": "{{pokemonName}} ha copiato le modifiche alle statistiche\ndel suo alleato {{allyName}}!", - "iceFaceAvoidedDamage": "{{pokemonName}} ha evitato\ni danni grazie a {{abilityName}}!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} ha evitato\ni danni grazie a {{abilityName}}!", "perishBody": "{{abilityName}} di {{pokemonName}}\nmanderà KO entrambi i Pokémon dopo 3 turni!", "poisonHeal": "{{pokemonName}} recupera alcuni PS\ncon {{abilityName}}!", "trace": "L'abilità {{abilityName}} di {{targetName}}\nviene copiata da {{pokemonName}} con Traccia!", @@ -13,7 +13,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockItemTheft": "{{abilityName}} di {{pokemonNameWithAffix}}\nlo rende immune ai furti!", "typeImmunityHeal": "{{pokemonName}} recupera alcuni PS\ncon {{abilityName}}!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evita il colpo\ncon {{abilityName}}!", - "postDefendDisguise": "{{pokemonNameWithAffix}} è stato smascherato!", + "disguiseAvoidedDamage": "{{pokemonNameWithAffix}} è stato smascherato!", "moveImmunity": "Non ha effetto su {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} ha assorbito la melma!", "postDefendTypeChange": "{{abilityName}} di {{pokemonNameWithAffix}}\nlo ha reso di tipo {{typeName}}!", diff --git a/src/locales/it/pokemon-form.ts b/src/locales/it/pokemon-form.ts index eb5d132bacd..d8bf9eeaab2 100644 --- a/src/locales/it/pokemon-form.ts +++ b/src/locales/it/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} si Dynamaxxa infinitamente\nin {{pokemonName}}!", "revertChange": "{{pokemonName}} è tornato\nalla sua forma originaria!", "formChange": "{{preName}} ha cambiato forma!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/ja/ability-trigger.ts b/src/locales/ja/ability-trigger.ts index a1ef05407be..7c7d081f645 100644 --- a/src/locales/ja/ability-trigger.ts +++ b/src/locales/ja/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}}は 相手の {{targetName}}の\n{{abilityName}}を トレースした!", "windPowerCharged": "{{pokemonName}}は\n{{moveName}}を 受けて じゅうでんした!", "quickDraw": "{{pokemonName}}は クイックドロウで\n行動が はやくなった!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}'s disguise was busted!", "blockItemTheft": "{{pokemonNameWithAffix}}の {{abilityName}}で\n道具を うばわれない!", "typeImmunityHeal": "{{pokemonNameWithAffix}}は {{abilityName}}で\n体力を 回復した!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}は {{abilityName}}で\nダメージを 受けない。", diff --git a/src/locales/ja/pokemon-form.ts b/src/locales/ja/pokemon-form.ts index e8d6fb8df4a..922f23fafea 100644 --- a/src/locales/ja/pokemon-form.ts +++ b/src/locales/ja/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} Eternamaxed\ninto {{pokemonName}}!", "revertChange": "{{pokemonName}} reverted\nto its original form!", "formChange": "{{preName}} changed form!", + "disguiseChange": "Its disguise served it as a decoy!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/ko/ability-trigger.ts b/src/locales/ko/ability-trigger.ts index 61be21bc7ec..974e6970569 100644 --- a/src/locales/ko/ability-trigger.ts +++ b/src/locales/ko/ability-trigger.ts @@ -4,16 +4,16 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage" : "{{pokemonName}}[[는]] {{abilityName}} 때문에\n반동 데미지를 받지 않는다!", "badDreams": "{{pokemonName}}[[는]]\n나이트메어 때문에 시달리고 있다!", "costar": "{{pokemonName}}[[는]] {{allyName}}의\n능력 변화를 복사했다!", - "iceFaceAvoidedDamage": "{{pokemonName}}[[는]] {{abilityName}} 때문에\n데미지를 받지 않는다!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}[[는]] {{abilityName}} 때문에\n데미지를 받지 않는다!", "perishBody": "{{pokemonName}}의 {{abilityName}} 때문에\n양쪽 포켓몬 모두는 3턴 후에 쓰러져 버린다!", "poisonHeal": "{{pokemonName}}[[는]] {{abilityName}}[[로]]인해\n조금 회복했다.", "trace": "{{pokemonName}}[[는]] 상대 {{targetName}}의 \n{{abilityName}}[[를]] 트레이스했다!", "windPowerCharged": "{{pokemonName}}[[는]]\n{{moveName}}에 맞아 충전되었다!", "quickDraw": "{{pokemonName}}[[는]]\n퀵드로에 의해 행동이 빨라졌다!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}의 정체가 드러났다!", "blockItemTheft": "{{pokemonNameWithAffix}}의 {{abilityName}}에 의해\n도구를 빼앗기지 않는다!", "typeImmunityHeal": "{{pokemonNameWithAffix}}[[는]]\n{{abilityName}}[[로]] 체력이 회복되었다!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}[[는]] {{abilityName}} 때문에\n데미지를 입지 않는다!", - "postDefendDisguise": "{{pokemonNameWithAffix}}의\n정체가 드러났다!", "moveImmunity": "{{pokemonNameWithAffix}}에게는\n효과가 없는 것 같다...", "reverseDrain": "{{pokemonNameWithAffix}}[[는]]\n해감액을 흡수했다!", "postDefendTypeChange": "{{pokemonNameWithAffix}}[[는]] {{abilityName}}[[로]] 인해\n{{typeName}}타입이 됐다!", diff --git a/src/locales/ko/pokemon-form.ts b/src/locales/ko/pokemon-form.ts index 78c9a762233..9f6b9b08408 100644 --- a/src/locales/ko/pokemon-form.ts +++ b/src/locales/ko/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}}[[는]]\n{{pokemonName}}가 되었다!", "revertChange": "{{pokemonName}}[[는]]\n원래 모습으로 되돌아왔다!", "formChange": "{{preName}}[[는]]\n다른 모습으로 변화했다!", + "disguiseChange": "탈이 대타가 되었다!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/pt_BR/ability-trigger.ts b/src/locales/pt_BR/ability-trigger.ts index f5d9511f3f6..fde45e92af3 100644 --- a/src/locales/pt_BR/ability-trigger.ts +++ b/src/locales/pt_BR/ability-trigger.ts @@ -4,16 +4,16 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage": "{{abilityName}} de {{pokemonName}}\nprotegeu-o do dano reverso!", "badDreams": "{{pokemonName}} está tendo pesadelos!", "costar": "{{pokemonName}} copiou as mudanças\nde atributo de {{allyName}}!", - "iceFaceAvoidedDamage": "{{pokemonName}} evitou\ndanos com sua {{abilityName}}!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} evitou\ndanos com sua {{abilityName}}!", "perishBody": "{{abilityName}} de {{pokemonName}}\nirá desmaiar ambos os Pokémon em 3 turnos!", "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaurou seus PS um pouco!", "trace": "{{pokemonName}} copiou {{abilityName}}\nde {{targetName}}!", "windPowerCharged": "Ser atingido por {{moveName}} carregou {{pokemonName}} com poder!", "quickDraw": "{{pokemonName}} pode agir mais rápido que o normal\ngraças ao seu Quick Draw!", + "disguiseAvoidedDamage" : "O disfarce de {{pokemonNameWithAffix}} foi descoberto!", "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nprevine o roubo de itens!", "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaurou um pouco de PS!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evitou dano\ncom {{abilityName}}!", - "postDefendDisguise": "O disfarce de {{pokemonNameWithAffix}} foi descoberto!", "moveImmunity": "Isso não afeta {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} absorveu a gosma líquida!", "postDefendTypeChange": "{{abilityName}} de {{pokemonNameWithAffix}}\ntransformou-o no tipo {{typeName}}!", diff --git a/src/locales/pt_BR/pokemon-form.ts b/src/locales/pt_BR/pokemon-form.ts index 6c7c649862a..01e3f951a9d 100644 --- a/src/locales/pt_BR/pokemon-form.ts +++ b/src/locales/pt_BR/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}} Eternamaxou\npara {{pokemonName}}!", "revertChange": "{{pokemonName}} voltou\npara sua forma original!", "formChange": "{{preName}} mudou de forma!", + "disguiseChange": "O seu disfarce serviu-lhe de isca!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/zh_CN/ability-trigger.ts b/src/locales/zh_CN/ability-trigger.ts index 0f2201049d2..0d69a78f0f7 100644 --- a/src/locales/zh_CN/ability-trigger.ts +++ b/src/locales/zh_CN/ability-trigger.ts @@ -4,16 +4,16 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage" : "{{pokemonName}}的{{abilityName}}\n抵消了反作用力!", "badDreams": "{{pokemonName}}被折磨着!", "costar": "{{pokemonName}}复制了{{allyName}}的能力变化!", - "iceFaceAvoidedDamage": "{{pokemonName}}因为{{abilityName}}\n避免了伤害!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}}因为{{abilityName}}\n避免了伤害!", "perishBody": "因为{{pokemonName}}的{{abilityName}}\n双方将在3回合后灭亡!", "poisonHeal": "{{pokemonName}}因{{abilityName}}\n回复了少许HP!", "trace": "{{pokemonName}}复制了{{targetName}}的\n{{abilityName}}!", "windPowerCharged": "受{{moveName}}的影响,{{pokemonName}}提升了能力!", "quickDraw":"因为速击效果发动,\n{{pokemonName}}比平常出招更快了!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}的画皮脱落了!", "blockItemTheft": "{{pokemonNameWithAffix}}的{{abilityName}}\n阻止了对方夺取道具!", "typeImmunityHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回复了少许HP!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n避免了伤害!", - "postDefendDisguise": "{{pokemonNameWithAffix}}的\n画皮脱落了", "moveImmunity": "对{{pokemonNameWithAffix}}没有效果!", "reverseDrain": "{{pokemonNameWithAffix}}\n吸到了污泥浆!", "postDefendTypeChange": "{{pokemonNameWithAffix}}因{{abilityName}}\n变成了{{typeName}}属性!", diff --git a/src/locales/zh_CN/pokemon-form.ts b/src/locales/zh_CN/pokemon-form.ts index 8fb82712e64..ea0c611e5aa 100644 --- a/src/locales/zh_CN/pokemon-form.ts +++ b/src/locales/zh_CN/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}}无极巨化成了\n{{pokemonName}}!", "revertChange": "{{pokemonName}}变回了\n原本的样子!", "formChange": "{{preName}}变成其他样子了。", + "disguiseChange": "它的画皮被当作诱饵使用了!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/locales/zh_TW/ability-trigger.ts b/src/locales/zh_TW/ability-trigger.ts index baa20614a44..c0d348633bc 100644 --- a/src/locales/zh_TW/ability-trigger.ts +++ b/src/locales/zh_TW/ability-trigger.ts @@ -4,16 +4,16 @@ export const abilityTriggers: SimpleTranslationEntries = { "blockRecoilDamage" : "{{pokemonName}} 的 {{abilityName}}\n抵消了反作用力!", "badDreams": "{{pokemonName}} 被折磨着!", "costar": "{{pokemonName}} 複製了 {{allyName}} 的\n能力變化!", - "iceFaceAvoidedDamage": "{{pokemonName}} 因爲 {{abilityName}}\n避免了傷害!", + "iceFaceAvoidedDamage": "{{pokemonNameWithAffix}} 因爲 {{abilityName}}\n避免了傷害!", "perishBody": "{{pokemonName}}'s {{abilityName}}\nwill faint both pokemon in 3 turns!", "poisonHeal": "{{pokemonName}}'s {{abilityName}}\nrestored its HP a little!", "trace": "{{pokemonName}} 複製了 {{targetName}} 的\n{{abilityName}}!", "windPowerCharged": "受 {{moveName}} 的影響, {{pokemonName}} 提升了能力!", "quickDraw":"{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "disguiseAvoidedDamage" : "{{pokemonNameWithAffix}}的畫皮脫落了!", "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", - "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", "moveImmunity": "It doesn't affect {{pokemonNameWithAffix}}!", "reverseDrain": "{{pokemonNameWithAffix}} sucked up the liquid ooze!", "postDefendTypeChange": "{{pokemonNameWithAffix}}'s {{abilityName}}\nmade it the {{typeName}} type!", diff --git a/src/locales/zh_TW/pokemon-form.ts b/src/locales/zh_TW/pokemon-form.ts index aa30840fcc0..55bb13946b7 100644 --- a/src/locales/zh_TW/pokemon-form.ts +++ b/src/locales/zh_TW/pokemon-form.ts @@ -13,6 +13,7 @@ export const battlePokemonForm: SimpleTranslationEntries = { "eternamaxChange": "{{preName}}無極巨化成了\n{{pokemonName}}!", "revertChange": "{{pokemonName}}變回了\n原本的樣子!", "formChange": "{{preName}}變為其他樣子了。", + "disguiseChange": "它的畫皮被當作誘餌使用了!", } as const; export const pokemonForm: SimpleTranslationEntries = { diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 09a0dbf7f8a..183295f6f41 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -1,18 +1,21 @@ -import { Status, StatusEffect } from "#app/data/status-effect.js"; -import { QuietFormChangePhase } from "#app/form-change-phase.js"; -import { TurnEndPhase } from "#app/phases.js"; -import { Abilities } from "#enums/abilities"; -import { Moves } from "#enums/moves"; -import { Species } from "#enums/species"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import GameManager from "#test/utils/gameManager"; import { getMovePosition } from "#test/utils/gameManagerUtils"; -import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { Moves } from "#enums/moves"; +import { Abilities } from "#enums/abilities"; +import { Species } from "#enums/species"; +import { StatusEffect } from "#app/data/status-effect.js"; +import { MoveEffectPhase, MoveEndPhase, TurnEndPhase, TurnInitPhase } from "#app/phases.js"; +import { BattleStat } from "#app/data/battle-stat.js"; +import { SPLASH_ONLY } from "../utils/testUtils"; const TIMEOUT = 20 * 1000; -describe("Abilities - DISGUISE", () => { +describe("Abilities - Disguise", () => { let phaserGame: Phaser.Game; let game: GameManager; + const bustedForm = 1; + const disguisedForm = 0; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -26,72 +29,134 @@ describe("Abilities - DISGUISE", () => { beforeEach(() => { game = new GameManager(phaserGame); - const moveToUse = Moves.SPLASH; game.override.battleType("single"); - game.override.ability(Abilities.DISGUISE); - game.override.moveset([moveToUse]); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - }); - test( - "check if fainted pokemon switched to base form on arena reset", - async () => { - const baseForm = 0, - bustedForm = 1; - game.override.startingWave(4); - game.override.starterForms({ - [Species.MIMIKYU]: bustedForm, - }); + game.override.enemySpecies(Species.MIMIKYU); + game.override.enemyMoveset(SPLASH_ONLY); - await game.startBattle([Species.MAGIKARP, Species.MIMIKYU]); + game.override.starterSpecies(Species.REGIELEKI); + game.override.moveset([Moves.SHADOW_SNEAK, Moves.VACUUM_WAVE, Moves.TOXIC_THREAD, Moves.SPLASH]); + }, TIMEOUT); - const mimikyu = game.scene.getParty().find((p) => p.species.speciesId === Species.MIMIKYU); - expect(mimikyu).not.toBe(undefined); - expect(mimikyu!.formIndex).toBe(bustedForm); + it("takes no damage from attacking move and transforms to Busted form, taking 1/8 max HP damage from the disguise breaking", async () => { + await game.startBattle(); - mimikyu!.hp = 0; - mimikyu!.status = new Status(StatusEffect.FAINT); - expect(mimikyu!.isFainted()).toBe(true); + const mimikyu = game.scene.getEnemyPokemon()!; + const maxHp = mimikyu.getMaxHp(); + const disguiseDamage = Math.floor(maxHp / 8); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - await game.doKillOpponents(); - await game.phaseInterceptor.to(TurnEndPhase); - game.doSelectModifier(); - await game.phaseInterceptor.to(QuietFormChangePhase); + expect(mimikyu.formIndex).toBe(disguisedForm); - expect(mimikyu!.formIndex).toBe(baseForm); - }, - TIMEOUT - ); + game.doAttack(getMovePosition(game.scene, 0, Moves.SHADOW_SNEAK)); - test( - "damage taken should be equal to 1/8 of its maximum HP, rounded down", - async () => { - const baseForm = 0, - bustedForm = 1; + await game.phaseInterceptor.to(MoveEndPhase); - game.override.enemyMoveset([Moves.DARK_PULSE, Moves.DARK_PULSE, Moves.DARK_PULSE, Moves.DARK_PULSE]); - game.override.startingLevel(20); - game.override.enemyLevel(20); - game.override.enemySpecies(Species.MAGIKARP); - game.override.starterForms({ - [Species.MIMIKYU]: baseForm, - }); + expect(mimikyu.hp).equals(maxHp - disguiseDamage); + expect(mimikyu.formIndex).toBe(bustedForm); + }, TIMEOUT); - await game.startBattle([Species.MIMIKYU]); + it("doesn't break disguise when attacked with ineffective move", async () => { + await game.startBattle(); - const mimikyu = game.scene.getPlayerPokemon()!; - const damage = (Math.floor(mimikyu!.getMaxHp()/8)); + const mimikyu = game.scene.getEnemyPokemon()!; - expect(mimikyu).not.toBe(undefined); - expect(mimikyu!.formIndex).toBe(baseForm); + expect(mimikyu.formIndex).toBe(disguisedForm); - game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - await game.phaseInterceptor.to(TurnEndPhase); + game.doAttack(getMovePosition(game.scene, 0, Moves.VACUUM_WAVE)); - expect(mimikyu!.formIndex).toBe(bustedForm); - expect(game.scene.getEnemyPokemon()!.turnData.currDamageDealt).toBe(damage); - }, - TIMEOUT - ); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(mimikyu.formIndex).toBe(disguisedForm); + }, TIMEOUT); + + it("takes no damage from the first hit of a multihit move and transforms to Busted form, then takes damage from the second hit", async () => { + game.override.moveset([Moves.SURGING_STRIKES]); + game.override.enemyLevel(5); + await game.startBattle(); + + const mimikyu = game.scene.getEnemyPokemon()!; + const maxHp = mimikyu.getMaxHp(); + const disguiseDamage = Math.floor(maxHp / 8); + + expect(mimikyu.formIndex).toBe(disguisedForm); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); + + // First hit + await game.phaseInterceptor.to(MoveEffectPhase); + expect(mimikyu.hp).equals(maxHp - disguiseDamage); + expect(mimikyu.formIndex).toBe(disguisedForm); + + // Second hit + await game.phaseInterceptor.to(MoveEffectPhase); + expect(mimikyu.hp).lessThan(maxHp - disguiseDamage); + expect(mimikyu.formIndex).toBe(bustedForm); + }, TIMEOUT); + + it("takes effects from status moves and damage from status effects", async () => { + await game.startBattle(); + + const mimikyu = game.scene.getEnemyPokemon()!; + expect(mimikyu.hp).toBe(mimikyu.getMaxHp()); + + game.doAttack(getMovePosition(game.scene, 0, Moves.TOXIC_THREAD)); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(mimikyu.formIndex).toBe(disguisedForm); + expect(mimikyu.status?.effect).toBe(StatusEffect.POISON); + expect(mimikyu.summonData.battleStats[BattleStat.SPD]).toBe(-1); + expect(mimikyu.hp).toBeLessThan(mimikyu.getMaxHp()); + }, TIMEOUT); + + it("persists form change when switched out", async () => { + game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); + game.override.starterSpecies(0); + + await game.startBattle([Species.MIMIKYU, Species.FURRET]); + + const mimikyu = game.scene.getPlayerPokemon()!; + const maxHp = mimikyu.getMaxHp(); + const disguiseDamage = Math.floor(maxHp / 8); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(mimikyu.formIndex).toBe(bustedForm); + expect(mimikyu.hp).equals(maxHp - disguiseDamage); + + await game.toNextTurn(); + game.doSwitchPokemon(1); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(mimikyu.formIndex).toBe(bustedForm); + }, TIMEOUT); + + it("reverts to Disguised on arena reset", async () => { + game.override.startingWave(4); + + game.override.starterSpecies(Species.MIMIKYU); + game.override.starterForms({ + [Species.MIMIKYU]: bustedForm + }); + + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.BALL_FETCH); + + await game.startBattle(); + + const mimikyu = game.scene.getPlayerPokemon()!; + + expect(mimikyu.formIndex).toBe(bustedForm); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.doKillOpponents(); + await game.phaseInterceptor.to(TurnEndPhase); + game.doSelectModifier(); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(mimikyu.formIndex).toBe(disguisedForm); + }, TIMEOUT); });