From 9f26ea983bfae1241c6b0a9588ccf560bda2ad8a Mon Sep 17 00:00:00 2001 From: Lylian Date: Wed, 31 Jul 2024 22:32:20 +0200 Subject: [PATCH 01/24] implement illusion ability with unit test and localizations --- package-lock.json | 2 +- public/images/items.json | 2 +- src/battle-scene.ts | 2 +- src/data/ability.ts | 120 +++++++- src/data/move.ts | 13 +- src/field/pokemon.ts | 396 +++++++++++++++++++++++---- src/locales/de/ability-trigger.ts | 1 + src/locales/en/ability-trigger.ts | 1 + src/locales/es/ability-trigger.ts | 1 + src/locales/fr/ability-trigger.ts | 1 + src/locales/it/ability-trigger.ts | 1 + src/locales/ko/ability-trigger.ts | 1 + src/locales/pt_BR/ability-trigger.ts | 1 + src/locales/pt_BR/config.ts | 1 + src/locales/zh_CN/ability-trigger.ts | 1 + src/locales/zh_TW/ability-trigger.ts | 1 + src/messages.ts | 14 +- src/phases.ts | 32 ++- src/test/abilities/illusion.test.ts | 127 +++++++++ src/ui/battle-info.ts | 28 +- src/ui/party-ui-handler.ts | 28 +- src/ui/rename-form-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 16 +- 23 files changed, 674 insertions(+), 118 deletions(-) mode change 100644 => 100755 src/data/ability.ts create mode 100644 src/test/abilities/illusion.test.ts diff --git a/package-lock.json b/package-lock.json index 0605b299dab8..35089ea0f627 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6148,4 +6148,4 @@ } } } -} +} \ No newline at end of file diff --git a/public/images/items.json b/public/images/items.json index 86792e23cc07..f6d14d18dbb5 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -8396,4 +8396,4 @@ "version": "3.0", "smartupdate": "$TexturePacker:SmartUpdate:b317e2cd3502364fcdae296cd439ac4d:ae80196191516a8cb098a8467c6faa2f:110e074689c9edd2c54833ce2e4d9270$" } -} +} \ No newline at end of file diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 93b05a39ecb3..b0967dfebdae 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -851,7 +851,7 @@ export default class BattleScene extends SceneBase { container.add(icon); - if (pokemon.isFusion()) { + if (pokemon.isFusion(true)) { const fusionIcon = this.add.sprite(0, 0, pokemon.getFusionIconAtlasKey(ignoreOverride)); fusionIcon.setName("sprite-fusion-icon"); fusionIcon.setOrigin(0.5, 0); diff --git a/src/data/ability.ts b/src/data/ability.ts old mode 100644 new mode 100755 index 491a14ba621d..f3e8270f17b4 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1885,7 +1885,6 @@ export class PostSummonRemoveArenaTagAbAttr extends PostSummonAbAttr { return true; } } - export class PostSummonMessageAbAttr extends PostSummonAbAttr { private messageFunc: (pokemon: Pokemon) => string; @@ -1898,6 +1897,11 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { pokemon.scene.queueMessage(this.messageFunc(pokemon)); + pokemon.scene.getField(true).map(pokemon => { + if (pokemon.breakIllusion()) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); + } + }); return true; } } @@ -2262,6 +2266,10 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; } + if (target.illusion.active) { + return false; + } + pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); pokemon.summonData.ability = target.getAbility().id; @@ -3554,8 +3562,8 @@ export class MaxMultiHitAbAttr extends AbAttr { } export class PostBattleAbAttr extends AbAttr { - constructor() { - super(true); + constructor(showAbility: boolean = true) { + super(showAbility); } applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { @@ -4041,6 +4049,97 @@ export class IceFaceBlockPhysicalAbAttr extends ReceivedMoveDamageMultiplierAbAt } } +export class PreSummonAbAttr extends AbAttr { + applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + return false; + } +} + +export class IllusionPreSummonAbAttr extends PreSummonAbAttr { + /** + * Apply a new illusion when summoning Zoroark if the illusion is available + * + * @param {Pokemon} pokemon - The Pokémon with the Illusion ability. + * @param {boolean} passive - N/A + * @param {...any} args - N/A + * @returns {boolean} - Whether the illusion was applied. + */ + applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + let suppressed = false; + pokemon.scene.getField(true).filter(p => p !== pokemon).map(p => { + if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) { + suppressed = true; + } + if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) { + suppressed = true; + } + }); + + if (pokemon.illusion.available && !suppressed) { + return pokemon.generateIllusion(); + } else { + return false; + } + } +} + +export class IllusionBreakAbAttr extends PostDefendAbAttr { + /** + * Destroy illusion if attack move deals damage to zoroark + * + * @param {Pokemon} pokemon - The Pokémon with the Illusion ability. + * @param {boolean} passive - N/A + * @param {Pokemon} attacker - The attacking Pokémon. + * @param {PokemonMove} move - The move being used. + * @param {PokemonMove} hitResult - The type of hitResult the pokemon got + * @param {...any} args - N/A + * @returns {boolean} - Whether the illusion was destroyed. + */ + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { + + const breakIllusion: HitResult[] = [HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, HitResult.NOT_VERY_EFFECTIVE, HitResult.ONE_HIT_KO]; + if (!breakIllusion.includes(hitResult)) { + return false; + } + pokemon.breakIllusion(); + pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); + return true; + } +} + +export class IllusionAfterBattle extends PostBattleAbAttr { + /** + * Illusion will be available again after a battle and apply the illusion of the pokemon is already on field + * + * @param {Pokemon} pokemon - The Pokémon with the Illusion ability. + * @param {boolean} passive - N/A + * @param {...any} args - N/A + * @returns {boolean} - Whether the illusion was applied. + */ + applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + pokemon.breakIllusion(); + pokemon.illusion.available = true; + return true; + } +} + +export class IllusionDisableAbAttr extends PostSummonAbAttr { + /** + * Illusion will be disabled if the pokemon is summoned with an illusion. + * So the pokemon can use 1 illusion per battle. + * + * @param {Pokemon} pokemon - The Pokémon with the Illusion ability. + * @param {boolean} passive - N/A + * @param {...any} args - N/A + * @returns {boolean} + */ + applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { + pokemon.illusion.available = false; + return true; + } +} + + /** * If a Pokémon with this Ability selects a damaging move, it has a 30% chance of going first in its priority bracket. If the Ability activates, this is announced at the start of the turn (after move selection). * @@ -4216,6 +4315,11 @@ export function applyPostSummonAbAttrs(attrType: Constructor, return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostSummon(pokemon, passive, args), args); } +export function applyPreSummonAbAttrs(attrType: Constructor, + pokemon: Pokemon, ...args: any[]) { + return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSummon(pokemon, passive, args), args); +} + export function applyPreSwitchOutAbAttrs(attrType: Constructor, pokemon: Pokemon, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSwitchOut(pokemon, passive, args), args, true); @@ -4759,7 +4863,15 @@ export function initAbilities() { new Ability(Abilities.ILLUSION, 5) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .unimplemented(), + //The pokemon generate an illusion if it's available + .conditionalAttr((pokemon) => pokemon.illusion.available, IllusionPreSummonAbAttr, false) + //The pokemon loses his illusion when he is damaged by a move + .conditionalAttr((pokemon) => pokemon.illusion.active, IllusionBreakAbAttr, true) + //Illusion is available again after a battle + .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionAfterBattle, false) + //Illusion is not available after summon + .attr(IllusionDisableAbAttr, false) + .bypassFaint(), new Ability(Abilities.IMPOSTER, 5) .attr(PostSummonTransformAbAttr) .attr(UncopiableAbilityAbAttr), diff --git a/src/data/move.ts b/src/data/move.ts index f90aef585d67..d68267dff37b 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5486,10 +5486,12 @@ export class AbilityChangeAttr extends MoveEffectAttr { return false; } - (this.selfTarget ? user : target).summonData.ability = this.ability; - - user.scene.queueMessage(i18next.t("moveTriggers:acquiredAbility", {pokemonName: getPokemonNameWithAffix((this.selfTarget ? user : target)), abilityName: allAbilities[this.ability].name})); - + const pokemon: Pokemon = this.selfTarget ? user : target; + pokemon.summonData.ability = this.ability; + if (pokemon.breakIllusion()) { + pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); + } + user.scene.queueMessage(i18next.t("moveTriggers:acquiredAbility", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: allAbilities[this.ability].name})); return true; } @@ -5641,7 +5643,8 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { export class TransformAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - if (!super.apply(user, target, move, args)) { + if (!super.apply(user, target, move, args) || target.illusion.active || user.illusion.active) { + user.scene.queueMessage(i18next.t("battle:attackFailed")); return resolve(false); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 71e89d60cbd1..9224bd98cbb0 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -63,6 +63,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public name: string; public nickname: string; public species: PokemonSpecies; + public illusion: Illusion; public formIndex: integer; public abilityIndex: integer; public passive: boolean; @@ -126,6 +127,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const randAbilityIndex = Utils.randSeedInt(2); this.species = species; + this.illusion = {active: false, available: true}; + this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; // Determine the ability index @@ -240,15 +243,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } - getNameToRender() { + getNameToRender(useIllusion: boolean = true) { + const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name : this.name; + const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname : this.nickname; try { - if (this.nickname) { - return decodeURIComponent(escape(atob(this.nickname))); + if (nickname) { + return decodeURIComponent(escape(atob(nickname))); } - return this.name; + return name; } catch (err) { - console.error(`Failed to decode nickname for ${this.name}`, err); - return this.name; + console.error(`Failed to decode nickname for ${name}`, err); + return name; } } @@ -276,7 +281,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.addAt(sprite, 0); this.addAt(tintSprite, 1); - if (this.isShiny() && !this.shinySparkle) { + if (this.isShiny(true) && !this.shinySparkle) { this.initShinySparkle(); } } @@ -337,6 +342,113 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + /** + * Generate an illusion of the last pokemon in the party, as other wild pokemon in the area. + * + * @param {Pokemon} pokemon - The Pokemon that will create an illusion. + * @param {Pokemon[]} party - The party of the trainer's pokemon. + */ + generateIllusion(): boolean { + if (this.hasTrainer()) { + const party: Pokemon[] = (this.isPlayer() ? this.scene.getParty() : this.scene.getEnemyParty()).filter(p => p.isAllowedInBattle()); + const lastPokemon: Pokemon = party.at(-1); + const speciesId = lastPokemon.species.speciesId; + + if ( lastPokemon === this || this.illusion.active || + ((speciesId === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized() || this.isTerastallized()))) { + return false; + } + + this.illusion = { + active: true, + available: true, + name: this.name, + nickname: this.nickname, + species: getPokemonSpecies(speciesId), + formIndex: lastPokemon.formIndex, + shiny: this.shiny, + variant: this.variant, + shinySparkle: this.shinySparkle, + gender: lastPokemon.gender, + pokeball: lastPokemon.pokeball, + fusionFormIndex: lastPokemon.fusionFormIndex, + fusionSpecies: lastPokemon.fusionSpecies, + fusionVariant: this.fusionVariant, + fusionShiny: this.fusionShiny, + fusionGender: lastPokemon.fusionGender + }; + + this.name = lastPokemon.name; + this.nickname = lastPokemon.nickname; + this.shiny = lastPokemon.shiny; + this.variant = lastPokemon.variant; + this.fusionVariant = lastPokemon.fusionVariant; + this.fusionShiny = lastPokemon.fusionShiny; + if (this.shiny) { + this.initShinySparkle(); + } + this.loadAssets(false, true).then(() => this.playAnim()); + } else { + let availables: Species[] = []; + if (this.isBoss()) { + availables = [Species.ENTEI, Species.RAIKOU, Species.SUICUNE]; + } else { + const area: Species[] = [ + Species.HOUNDOUR, Species.SABLEYE, Species.PURRLOIN, Species.PAWNIARD, Species.NICKIT, + Species.IMPIDIMP, Species.MASCHIFF, Species.ABSOL, Species.SPIRITOMB, Species.DEINO, + ]; + + for (let species of area) { + for (const evolutionLevel of getPokemonSpecies(species).getEvolutionLevels()) { + if (evolutionLevel[1] && evolutionLevel[1] <= this.level) { + species = evolutionLevel[0]; + } + } + availables.push(species); + } + availables.push(this.species.name === getPokemonSpecies(Species.ZORUA).name ? Species.MURKROW : Species.HONCHKROW); + } + const randomIllusion: PokemonSpecies = getPokemonSpecies(availables[this.randSeedInt(availables.length)]); + + this.illusion = { + active: true, + available: true, + species: randomIllusion, + name: this.name, + shiny: this.shiny, + variant: this.variant, + shinySparkle: this.shinySparkle, + gender: this.gender + }; + this.name = randomIllusion.name; + this.loadAssets(false, true).then(() => this.playAnim()); + } + return true; + } + + breakIllusion(): boolean { + if (!this.illusion.active) { + return false; + } + + this.name = this.illusion.name; + this.nickname = this.illusion.nickname; + this.shiny = this.illusion.shiny; + this.variant = this.illusion.variant; + this.fusionVariant = this.illusion.fusionVariant; + this.fusionShiny = this.illusion.fusionShiny; + this.illusion = {active: false, available: false}; + if (this.isOnField) { + this.scene.playSound("PRSFX- Transform"); + } + if (this.shiny) { + this.initShinySparkle(); + } + this.loadAssets(false).then(() => this.playAnim()); + this.updateInfo(true); + return true; + } + abstract isPlayer(): boolean; abstract hasTrainer(): boolean; @@ -345,18 +457,25 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract getBattlerIndex(): BattlerIndex; - loadAssets(ignoreOverride: boolean = true): Promise { + /** + * @param {boolean} useIllusion - Whether we want the illusion or not. + */ + loadAssets(ignoreOverride: boolean = true, useIllusion: boolean = false): Promise { return new Promise(resolve => { const moveIds = this.getMoveset().map(m => m.getMove().id); Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); - this.getSpeciesForm().loadAssets(this.scene, this.getGender() === Gender.FEMALE, this.formIndex, this.shiny, this.variant); - if (this.isPlayer() || this.getFusionSpeciesForm()) { + const formIndex = this.illusion.active && useIllusion ? this.illusion.formIndex : this.formIndex; + this.getSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), this.getVariant(useIllusion)); + if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { this.scene.loadPokemonAtlas(this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride)); } - if (this.getFusionSpeciesForm()) { - this.getFusionSpeciesForm().loadAssets(this.scene, this.getFusionGender() === Gender.FEMALE, this.fusionFormIndex, this.fusionShiny, this.fusionVariant); + if (this.getFusionSpeciesForm(false, useIllusion)) { + const fusionFormIndex = this.illusion.active && useIllusion ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = this.illusion.active && !useIllusion ? this.illusion.fusionShiny : this.fusionShiny; + const fusionVariant = this.illusion.active && !useIllusion ? this.illusion.fusionVariant : this.fusionVariant; + this.getFusionSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getFusionGender(false, useIllusion) === Gender.FEMALE, fusionFormIndex, fusionShiny, fusionVariant); this.scene.loadPokemonAtlas(this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride)); } this.scene.load.once(Phaser.Loader.Events.COMPLETE, () => { @@ -456,18 +575,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getSpriteId(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny, this.variant); + const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getBattleSpriteId(back?: boolean, ignoreOverride?: boolean): string { if (back === undefined) { back = this.isPlayer(); } - return this.getSpeciesForm(ignoreOverride).getSpriteId(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny, this.variant, back); + + const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + + return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } getSpriteKey(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getSpriteKey(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny, this.variant); + return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( + this.getGender(ignoreOverride) === Gender.FEMALE, + this.formIndex, + this.illusion.shiny ?? this.shiny, + this.illusion.variant ?? this.variant + ); } getBattleSpriteKey(back?: boolean, ignoreOverride?: boolean): string { @@ -475,14 +603,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getSpriteId(this.getFusionGender(ignoreOverride) === Gender.FEMALE, this.fusionFormIndex, this.fusionShiny, this.fusionVariant); + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } getFusionBattleSpriteId(back?: boolean, ignoreOverride?: boolean): string { if (back === undefined) { back = this.isPlayer(); } - return this.getFusionSpeciesForm(ignoreOverride).getSpriteId(this.getFusionGender(ignoreOverride) === Gender.FEMALE, this.fusionFormIndex, this.fusionShiny, this.fusionVariant, back); + + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + + return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } getFusionBattleSpriteKey(back?: boolean, ignoreOverride?: boolean): string { @@ -494,39 +626,56 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getIconAtlasKey(this.formIndex, this.shiny, this.variant); + const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } getFusionIconAtlasKey(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getIconAtlasKey(this.fusionFormIndex, this.fusionShiny, this.fusionVariant); + return this.getFusionSpeciesForm(ignoreOverride, true).getIconAtlasKey(this.fusionFormIndex, this.fusionShiny, this.fusionVariant); } getIconId(ignoreOverride?: boolean): string { - return this.getSpeciesForm(ignoreOverride).getIconId(this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, this.shiny, this.variant); + const formIndex: integer = this.illusion.active ? this.illusion.formIndex : this.formIndex; + return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - return this.getFusionSpeciesForm(ignoreOverride).getIconId(this.getFusionGender(ignoreOverride) === Gender.FEMALE, this.fusionFormIndex, this.fusionShiny, this.fusionVariant); + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } - getSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm { + /** + * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. + */ + getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { + const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species : this.species; + const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex : this.formIndex; + if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; } - if (!this.species.forms?.length) { - return this.species; + + if (!species.forms?.length || formIndex === undefined) { + return species; } - return this.species.forms[this.formIndex]; + + return species.forms[formIndex]; } - getFusionSpeciesForm(ignoreOverride?: boolean): PokemonSpeciesForm { + /** + * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. + */ + getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { + const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies : this.fusionSpecies; + const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex : this.fusionFormIndex; + if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; } - if (!this.fusionSpecies?.forms?.length || this.fusionFormIndex >= this.fusionSpecies?.forms.length) { - return this.fusionSpecies; + if (!fusionSpecies?.forms?.length || fusionFormIndex >= fusionSpecies?.forms.length) { + return fusionSpecies; } - return this.fusionSpecies?.forms[this.fusionFormIndex]; + return fusionSpecies?.forms[fusionFormIndex]; } getSprite(): Phaser.GameObjects.Sprite { @@ -835,34 +984,77 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - getGender(ignoreOverride?: boolean): Gender { - if (!ignoreOverride && this.summonData?.gender !== undefined) { + /** + * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. + */ + getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + if (useIllusion && this.illusion.active) { + return this.illusion.gender; + } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } return this.gender; } - getFusionGender(ignoreOverride?: boolean): Gender { - if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { + /** + * @param {boolean} useIllusion - Whether we want the fusionGender of the illusion or not. + */ + getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { + if (useIllusion && this.illusion.active) { + return this.illusion.fusionGender; + } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } return this.fusionGender; } - isShiny(): boolean { - return this.shiny || (this.isFusion() && this.fusionShiny); + isShiny(useIllusion: boolean = false): boolean { + if (!useIllusion && this.illusion.active) { + return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny); + } else { + return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); + } } - getVariant(): Variant { - return !this.isFusion() ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; + isDoubleShiny(useIllusion: boolean = false): boolean { + if (!useIllusion && this.illusion.active) { + return this.isFusion(false) && this.illusion.shiny && this.illusion.fusionShiny; + } else { + return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; + } + } + + getVariant(useIllusion: boolean = false): Variant { + if (!useIllusion && this.illusion.active) { + return !this.isFusion(false) ? this.illusion.variant : Math.max(this.variant, this.fusionVariant) as Variant; + } else { + return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; + } + + } + + getBaseVariant(doubleShiny: boolean): Variant { + if (doubleShiny) { + return this.illusion.active ? this.illusion.variant : this.variant; + } else { + return this.getVariant(); + } } getLuck(): integer { return this.luck + (this.isFusion() ? this.fusionLuck : 0); } - isFusion(): boolean { - return !!this.fusionSpecies; + isFusion(useIllusion: boolean = false): boolean { + if (useIllusion && this.illusion.active) { + return !!this.illusion.fusionSpecies; + } else { + return !!this.fusionSpecies; + } + } + + getName(illusion: boolean = false): string { + return (!illusion && this.illusion.active) ? this.illusion.name : this.name; } abstract isBoss(): boolean; @@ -894,30 +1086,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** * Gets the types of a pokemon - * @param includeTeraType boolean to include tera-formed type, default false - * @param forDefend boolean if the pokemon is defending from an attack - * @param ignoreOverride boolean if true, ignore ability changing effects + * @param {boolean} includeTeraType to include tera-formed type, default false + * @param {boolean} forDefend if the pokemon is defending from an attack + * @param {boolean} ignoreOverride if true, ignore ability changing effects + * @param {boolean} useIllusion whether we want he types of the illusion or not * @returns array of {@linkcode Type} */ - getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean): Type[] { - const types = []; - + getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean, useIllusion: boolean | "AUTO" = "AUTO"): Type[] { + const types: Type[] = []; if (includeTeraType) { const teraType = this.getTeraType(); if (teraType !== Type.UNKNOWN) { types.push(teraType); } } - if (!types.length || !includeTeraType) { - if (!ignoreOverride && this.summonData?.types) { + const doIllusion: boolean = useIllusion === "AUTO" ? !forDefend : useIllusion; + if (!ignoreOverride && this.summonData?.types && (!this.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else { - const speciesForm = this.getSpeciesForm(ignoreOverride); + const speciesForm = this.getSpeciesForm(ignoreOverride, doIllusion); types.push(speciesForm.type1); - - const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride); + const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride, doIllusion); if (fusionSpeciesForm) { if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1) { types.push(fusionSpeciesForm.type2); @@ -925,7 +1116,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { types.push(fusionSpeciesForm.type1); } } - if (types.length === 1 && speciesForm.type2 !== null) { types.push(speciesForm.type2); } @@ -951,7 +1141,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { types.splice(index, 1); } } - return types; } @@ -1179,12 +1368,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param source - The attacking Pokémon. * @param pokemonMove - The move being used by the attacking Pokémon. * @param ignoreAbility - Whether to check for abilities that might affect type effectiveness or immunity. + * @param {boolean} useIllusion - Whether we want the attack move effectiveness on the illusion or not * @returns The type damage multiplier, indicating the effectiveness of the move */ - getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove, ignoreAbility: boolean = false): TypeDamageMultiplier { + getAttackMoveEffectiveness(source: Pokemon, pokemonMove: PokemonMove, ignoreAbility: boolean = false, useIllusion: boolean = false): TypeDamageMultiplier { const move = pokemonMove.getMove(); const typeless = move.hasAttr(TypelessAttr); - const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move, source)); + const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move, source, undefined, undefined, useIllusion)); const cancelled = new Utils.BooleanHolder(false); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); if (!typeless && !ignoreAbility) { @@ -1203,9 +1393,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param source the Pokemon using the move * @param ignoreStrongWinds whether or not this ignores strong winds (anticipation, forewarn, stealth rocks) * @param simulated tag to only apply the strong winds effect message when the move is used + * @param {boolean} useIllusion - Whether we want the attack type effectiveness on the illusion or not * @returns a multiplier for the type effectiveness */ - getAttackTypeEffectiveness(moveOrType: Move | Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true): TypeDamageMultiplier { + getAttackTypeEffectiveness(moveOrType: Move | Type, source?: Pokemon, ignoreStrongWinds: boolean = false, simulated: boolean = true, useIllusion: boolean = false): TypeDamageMultiplier { const move = (moveOrType instanceof Move) ? moveOrType : undefined; @@ -1216,7 +1407,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (moveType === Type.STELLAR) { return this.isTerastallized() ? 2 : 1; } - const types = this.getTypes(true, true); + const types = this.getTypes(true, true, undefined, useIllusion); let multiplier = types.map(defType => { if (source) { @@ -1253,12 +1444,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getMatchupScore(pokemon: Pokemon): number { const types = this.getTypes(true); - const enemyTypes = pokemon.getTypes(true, true); + const enemyTypes = pokemon.getTypes(true, true, false, true); const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, pokemon) : this.getStat(Stat.SPD)) <= pokemon.getBattleStat(Stat.SPD, this); - let atkScore = pokemon.getAttackTypeEffectiveness(types[0], this) * (outspeed ? 1.25 : 1); + let atkScore = pokemon.getAttackTypeEffectiveness(types[0], this, false, false, true) * (outspeed ? 1.25 : 1); let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0], pokemon), 0.25); if (types.length > 1) { - atkScore *= pokemon.getAttackTypeEffectiveness(types[1], this); + atkScore *= pokemon.getAttackTypeEffectiveness(types[1], this, false, false, true); } if (enemyTypes.length > 1) { defScore *= (1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], pokemon), 0.25)); @@ -3720,7 +3911,7 @@ export class EnemyPokemon extends Pokemon { if ((move.name.endsWith(" (N)") || !move.applyConditions(this, target, move)) && ![Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id)) { targetScore = -20; } else if (move instanceof AttackMove) { - const effectiveness = target.getAttackMoveEffectiveness(this, pokemonMove); + const effectiveness = target.getAttackMoveEffectiveness(this, pokemonMove, false, true); if (target.isPlayer() !== this.isPlayer()) { targetScore *= effectiveness; if (this.isOfType(move.type)) { @@ -4017,6 +4208,91 @@ export class EnemyPokemon extends Pokemon { } } +/** + * Illusion property + */ +interface Illusion { + /** + * Whether the illusion is active or not. + * @type {boolean} + */ + active: boolean; + /** + * Whether the pokemon can generate an illusion or not. + * @type {boolean} + */ + available: boolean; + /** + * The stored name of the pokemon. + * @type {string} + */ + name?: string; + /** + * The stored nickname of the pokemon. + * @type {string} + */ + nickname?: string; + /** + * The species of the illusion. + * @type {PokemonSpecies} + */ + species?: PokemonSpecies; + /** + * The formIndex of the illusion + * @type {integer} + */ + formIndex?: integer; + /** + * Store whether the base pokemon is shiny or not. + * @type {boolean} + */ + shiny?: boolean; + /** + * The shiny variant of the base pokemon. + * @type {Variant} + */ + variant?: Variant; + /** + * The shinysparkles of the base pokemon. + * @type {Phaser.GameObjects.Sprite} + */ + shinySparkle?: Phaser.GameObjects.Sprite; + /** + * The gender of the illusion + * @type {Gender} + */ + gender?: Gender; + /** + * The pokeball of the illusion. + */ + pokeball?: PokeballType; + /** + * The fusionned species of the illusion if it's a fusion. + * @type {PokemonSpecies} + */ + fusionSpecies?: PokemonSpecies; + /** + * The fusionFormIndex of the illusion + * @type {integer} + */ + fusionFormIndex?: integer; + /** + * Whether the fusionned species of the base pokemon is shiny or not. + * @type {PokemonSpecies} + */ + fusionShiny?: boolean; + /** + * The variant of the fusionned species of the base pokemon. + * @type {Variant} + */ + fusionVariant?: Variant; + /** + * The fusionGender of the illusion if it's a fusion + * @type {Gender} + */ + fusionGender?: Gender; +} + export interface TurnMove { move: Moves; targets?: BattlerIndex[]; diff --git a/src/locales/de/ability-trigger.ts b/src/locales/de/ability-trigger.ts index 88dc9f9f0275..5164b2945704 100644 --- a/src/locales/de/ability-trigger.ts +++ b/src/locales/de/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}} kopiert {{abilityName}} von {{targetName}}!", "windPowerCharged": "Der Treffer durch {{moveName}} läd die Stärke von {{pokemonName}} auf!", "quickDraw": "Durch Schnellschuss kann {{pokemonName}} schneller handeln als sonst!", + "illusionBreak": "Das Trugbild von {{pokemonName}} verschwindet!", "blockItemTheft": "{{abilityName}} von {{pokemonNameWithAffix}} verhindert Item-Diebstahl!", "typeImmunityHeal": "{{abilityName}} von {{pokemonNameWithAffix}} füllte einige KP auf!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} vermeidet Schaden mit {{abilityName}}!", diff --git a/src/locales/en/ability-trigger.ts b/src/locales/en/ability-trigger.ts index ce41a9649222..c5ee9a858fd5 100644 --- a/src/locales/en/ability-trigger.ts +++ b/src/locales/en/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!", + "illusionBreak": "{{pokemonName}}'s illusion wore off!", "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/es/ability-trigger.ts b/src/locales/es/ability-trigger.ts index 6b1f66a11e39..df0fb1b3bf57 100644 --- a/src/locales/es/ability-trigger.ts +++ b/src/locales/es/ability-trigger.ts @@ -11,6 +11,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "windPowerCharged": "¡{{pokemonName}} se ha cargado de electricidad gracias a {{moveName}}!", "quickDraw": "{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", "blockItemTheft": "{{pokemonNameWithAffix}}'s {{abilityName}}\nprevents item theft!", + "illusionBreak": "¡La ilusión de {{pokemonName}} se ha desvanecido!", "typeImmunityHeal": "{{pokemonNameWithAffix}}'s {{abilityName}}\nrestored its HP a little!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} avoided damage\nwith {{abilityName}}!", "postDefendDisguise": "{{pokemonNameWithAffix}}'s disguise was busted!", diff --git a/src/locales/fr/ability-trigger.ts b/src/locales/fr/ability-trigger.ts index f6b9c306cd19..b97fe3877ace 100644 --- a/src/locales/fr/ability-trigger.ts +++ b/src/locales/fr/ability-trigger.ts @@ -9,6 +9,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "poisonHeal": "{{abilityName}} de {{pokemonName}}\nrestaure un peu ses PV !", "trace": "{{pokemonName}} copie le talent {{abilityName}}\nde {{targetName}} !", "windPowerCharged": "{{pokemonName}} a été touché par la capacité {{moveName}} et se charge en électricité !", + "illusionBreak": "L’illusion de {{pokemonName}} se brise !", "quickDraw": "Tir Vif permet à {{pokemonName}}\nd’agir plus vite que d’habitude !", "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nempêche son objet d’être volé !", "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaure un peu ses PV !", diff --git a/src/locales/it/ability-trigger.ts b/src/locales/it/ability-trigger.ts index 37472dbdeab8..fa5f9d3102fc 100644 --- a/src/locales/it/ability-trigger.ts +++ b/src/locales/it/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "L'abilità {{abilityName}} di {{targetName}}\nviene copiata da {{pokemonName}} con Traccia!", "windPowerCharged": "Venire colpito da {{moveName}} ha caricato {{pokemonName}}!", "quickDraw":"{{pokemonName}} agisce più rapidamente del normale grazie a Colpolesto!", + "illusionBreak": "L'illusione di {{pokemonName}} si dissolve!", "blockItemTheft": "{{abilityName}} di {{pokemonNameWithAffix}}\nlo rende immune ai furti!", "typeImmunityHeal": "{{pokemonName}} recupera alcuni PS\ncon {{abilityName}}!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evita il colpo\ncon {{abilityName}}!", diff --git a/src/locales/ko/ability-trigger.ts b/src/locales/ko/ability-trigger.ts index 61be21bc7ec3..0fbf17780fc5 100644 --- a/src/locales/ko/ability-trigger.ts +++ b/src/locales/ko/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}}[[는]] 상대 {{targetName}}의 \n{{abilityName}}[[를]] 트레이스했다!", "windPowerCharged": "{{pokemonName}}[[는]]\n{{moveName}}에 맞아 충전되었다!", "quickDraw": "{{pokemonName}}[[는]]\n퀵드로에 의해 행동이 빨라졌다!", + "illusionBreak": "{{pokemonName}}의\n일루전이 풀렸다!", "blockItemTheft": "{{pokemonNameWithAffix}}의 {{abilityName}}에 의해\n도구를 빼앗기지 않는다!", "typeImmunityHeal": "{{pokemonNameWithAffix}}[[는]]\n{{abilityName}}[[로]] 체력이 회복되었다!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}[[는]] {{abilityName}} 때문에\n데미지를 입지 않는다!", diff --git a/src/locales/pt_BR/ability-trigger.ts b/src/locales/pt_BR/ability-trigger.ts index f5d9511f3f67..61342c222185 100644 --- a/src/locales/pt_BR/ability-trigger.ts +++ b/src/locales/pt_BR/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "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!", + "illusionBreak": "A ilusão de {{pokemonName}} acabou!", "blockItemTheft": "{{abilityName}} de {{pokemonNameWithAffix}}\nprevine o roubo de itens!", "typeImmunityHeal": "{{abilityName}} de {{pokemonNameWithAffix}}\nrestaurou um pouco de PS!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}} evitou dano\ncom {{abilityName}}!", diff --git a/src/locales/pt_BR/config.ts b/src/locales/pt_BR/config.ts index ce4576646c27..3a44105b8a42 100644 --- a/src/locales/pt_BR/config.ts +++ b/src/locales/pt_BR/config.ts @@ -1,5 +1,6 @@ import { ability } from "./ability"; import { abilityTriggers } from "./ability-trigger"; +import { arenaFlyout } from "./arena-flyout"; import { PGFachv, PGMachv } from "./achv"; import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; diff --git a/src/locales/zh_CN/ability-trigger.ts b/src/locales/zh_CN/ability-trigger.ts index 0f2201049d2e..d19a07025159 100644 --- a/src/locales/zh_CN/ability-trigger.ts +++ b/src/locales/zh_CN/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}}复制了{{targetName}}的\n{{abilityName}}!", "windPowerCharged": "受{{moveName}}的影响,{{pokemonName}}提升了能力!", "quickDraw":"因为速击效果发动,\n{{pokemonName}}比平常出招更快了!", + "illusionBreak": "{{pokemonName}}造成的\n幻觉被解除了!", "blockItemTheft": "{{pokemonNameWithAffix}}的{{abilityName}}\n阻止了对方夺取道具!", "typeImmunityHeal": "{{pokemonNameWithAffix}}因{{abilityName}}\n回复了少许HP!", "nonSuperEffectiveImmunity": "{{pokemonNameWithAffix}}因{{abilityName}}\n避免了伤害!", diff --git a/src/locales/zh_TW/ability-trigger.ts b/src/locales/zh_TW/ability-trigger.ts index baa20614a441..2f89c47f1ab5 100644 --- a/src/locales/zh_TW/ability-trigger.ts +++ b/src/locales/zh_TW/ability-trigger.ts @@ -10,6 +10,7 @@ export const abilityTriggers: SimpleTranslationEntries = { "trace": "{{pokemonName}} 複製了 {{targetName}} 的\n{{abilityName}}!", "windPowerCharged": "受 {{moveName}} 的影響, {{pokemonName}} 提升了能力!", "quickDraw":"{{pokemonName}} can act faster than normal, thanks to its Quick Draw!", + "illusionBreak": "{{pokemonName}}造成的\n幻覺解除了!", "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/messages.ts b/src/messages.ts index 2259e78abfc8..ed0880ef36bc 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -18,23 +18,23 @@ export function getPokemonMessage(pokemon: Pokemon, content: string): string { * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage" */ -export function getPokemonNameWithAffix(pokemon: Pokemon): string { +export function getPokemonNameWithAffix(pokemon: Pokemon, useIllusion: boolean = true): string { switch (pokemon.scene.currentBattle.battleSpec) { case BattleSpec.DEFAULT: return !pokemon.isPlayer() ? pokemon.hasTrainer() ? i18next.t("battle:foePokemonWithAffix", { - pokemonName: pokemon.getNameToRender(), + pokemonName: pokemon.getNameToRender(useIllusion), }) : i18next.t("battle:wildPokemonWithAffix", { - pokemonName: pokemon.getNameToRender(), + pokemonName: pokemon.getNameToRender(useIllusion), }) - : pokemon.getNameToRender(); + : pokemon.getNameToRender(useIllusion); case BattleSpec.FINAL_BOSS: return !pokemon.isPlayer() - ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender() }) - : pokemon.getNameToRender(); + ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) }) + : pokemon.getNameToRender(useIllusion); default: - return pokemon.getNameToRender(); + return pokemon.getNameToRender(useIllusion); } } diff --git a/src/phases.ts b/src/phases.ts index c41ad333b9d3..d444a4ab63a0 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -25,7 +25,10 @@ import { Starter } from "./ui/starter-select-ui-handler"; import { Gender } from "./data/gender"; import { Weather, WeatherType, getRandomWeatherType, getTerrainBlockMessage, getWeatherDamageMessage, getWeatherLapseMessage } from "./data/weather"; import { ArenaTagSide, ArenaTrapTag, MistTag, TrickRoomTag } from "./data/arena-tag"; -import { CheckTrappedAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, applyAbAttrs, applyCheckTrappedAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr } from "./data/ability"; +import { + CheckTrappedAbAttr, PreSummonAbAttr, PostAttackAbAttr, PostBattleAbAttr, PostDefendAbAttr, PostSummonAbAttr, PostTurnAbAttr, PostWeatherLapseAbAttr, PreSwitchOutAbAttr, PreWeatherDamageAbAttr, ProtectStatAbAttr, RedirectMoveAbAttr, BlockRedirectAbAttr, RunSuccessAbAttr, StatChangeMultiplierAbAttr, SuppressWeatherEffectAbAttr, SyncEncounterNatureAbAttr, + applyAbAttrs, applyCheckTrappedAbAttrs, applyPreSummonAbAttrs, applyPostAttackAbAttrs, applyPostBattleAbAttrs, applyPostDefendAbAttrs, applyPostSummonAbAttrs, applyPostTurnAbAttrs, applyPostWeatherLapseAbAttrs, applyPreStatChangeAbAttrs, applyPreSwitchOutAbAttrs, applyPreWeatherEffectAbAttrs, IncrementMovePriorityAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr, BlockNonDirectDamageAbAttr as BlockNonDirectDamageAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, PostBiomeChangeAbAttr, applyPostFaintAbAttrs, PostFaintAbAttr, IncreasePpAbAttr, PostStatChangeAbAttr, applyPostStatChangeAbAttrs, AlwaysHitAbAttr, PreventBerryUseAbAttr, StatChangeCopyAbAttr, PokemonTypeChangeAbAttr, applyPreAttackAbAttrs, applyPostMoveUsedAbAttrs, PostMoveUsedAbAttr, MaxMultiHitAbAttr, HealFromBerryUseAbAttr, IgnoreMoveEffectsAbAttr, BlockStatusDamageAbAttr, BypassSpeedChanceAbAttr, AddSecondStrikeAbAttr +} from "./data/ability"; import { Unlockables, getUnlockableName } from "./system/unlockables"; import { getBiomeKey } from "./field/arena"; import { BattleType, BattlerIndex, TurnCommand } from "./battle"; @@ -842,6 +845,7 @@ export class EncounterPhase extends BattlePhase { }); } } + const enemyPokemon = this.scene.getEnemyParty()[e]; if (e < (battle.double ? 2 : 1)) { enemyPokemon.setX(-66 + enemyPokemon.getFieldPositionOffset()[0]); @@ -895,6 +899,8 @@ export class EncounterPhase extends BattlePhase { battle.enemyParty.forEach((enemyPokemon, e) => { if (e < (battle.double ? 2 : 1)) { if (battle.battleType === BattleType.WILD) { + + applyPreSummonAbAttrs(PreSummonAbAttr, enemyPokemon, []); this.scene.field.add(enemyPokemon); battle.seenEnemyPartyMemberIds.add(enemyPokemon.id); const playerPokemon = this.scene.getPlayerPokemon(); @@ -1058,7 +1064,7 @@ export class EncounterPhase extends BattlePhase { const enemyField = this.scene.getEnemyField(); enemyField.forEach((enemyPokemon, e) => { - if (enemyPokemon.isShiny()) { + if (enemyPokemon.isShiny(true)) { this.scene.unshiftPhase(new ShinySparklePhase(this.scene, BattlerIndex.ENEMY + e)); } }); @@ -1378,7 +1384,8 @@ export class SummonPhase extends PartyMemberPokemonPhase { start() { super.start(); - this.preSummon(); + const pokemon = this.getPokemon(); + applyPreSummonAbAttrs(PreSummonAbAttr, pokemon).then(() => this.preSummon()); } /** @@ -1447,8 +1454,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { summon(): void { const pokemon = this.getPokemon(); - - const pokeball = this.scene.addFieldSprite(this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.pokeball)); + const pokeball = this.scene.addFieldSprite(this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.illusion.pokeball ?? pokemon.pokeball)); pokeball.setVisible(false); pokeball.setOrigin(0.5, 0.625); this.scene.field.add(pokeball); @@ -1494,7 +1500,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); } - addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.pokeball); + addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.illusion.pokeball ?? pokemon.pokeball); this.scene.updateModifiers(this.player); this.scene.updateFieldScale(); pokemon.showInfo(); @@ -1502,7 +1508,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.setVisible(true); pokemon.getSprite().setVisible(true); pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.pokeball)); + pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball)); pokemon.untint(250, "Sine.easeIn"); this.scene.updateFieldScale(); this.scene.tweens.add({ @@ -1526,7 +1532,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { onEnd(): void { const pokemon = this.getPokemon(); - if (pokemon.isShiny()) { + if (pokemon.isShiny(true)) { this.scene.unshiftPhase(new ShinySparklePhase(this.scene, pokemon.getBattlerIndex())); } @@ -1587,7 +1593,10 @@ export class SwitchSummonPhase extends SummonPhase { this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty()); } } + const pokemon: Pokemon = this.getPokemon(); + // if doReturn === False OR slotIndex !== -1 (slotIndex is valid) and the pokemon doesn't exist/is false + // then switchAndSummon(), manually pick pokemon to switch into if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) { if (this.player) { return this.switchAndSummon(); @@ -1597,8 +1606,6 @@ export class SwitchSummonPhase extends SummonPhase { } } - const pokemon = this.getPokemon(); - if (!this.batonPass) { (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.removeTagsBySourceId(pokemon.id)); } @@ -1612,7 +1619,7 @@ export class SwitchSummonPhase extends SummonPhase { ); this.scene.playSound("pb_rel"); pokemon.hideInfo(); - pokemon.tint(getPokeballTintColor(pokemon.pokeball), 1, 250, "Sine.easeIn"); + pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); this.scene.tweens.add({ targets: pokemon, duration: 250, @@ -1631,6 +1638,8 @@ export class SwitchSummonPhase extends SummonPhase { const party = this.player ? this.getParty() : this.scene.getEnemyParty(); const switchedPokemon = party[this.slotIndex]; this.lastPokemon = this.getPokemon(); + + applyPreSummonAbAttrs(PreSummonAbAttr, switchedPokemon); applyPreSwitchOutAbAttrs(PreSwitchOutAbAttr, this.lastPokemon); if (this.batonPass && switchedPokemon) { (this.player ? this.scene.getEnemyField() : this.scene.getPlayerField()).forEach(enemyPokemon => enemyPokemon.transferTagsBySourceId(this.lastPokemon.id, switchedPokemon.id)); @@ -4470,6 +4479,7 @@ export class SwitchPhase extends BattlePhase { } this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); }, PartyUiHandler.FilterNonFainted); + } } diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts new file mode 100644 index 000000000000..39683b956ec0 --- /dev/null +++ b/src/test/abilities/illusion.test.ts @@ -0,0 +1,127 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import overrides from "#app/overrides"; +import { Species } from "#enums/species"; +import { Gender } from "../../data/gender"; +import { PokeballType } from "../../data/pokeball"; +import { + TurnEndPhase, +} from "#app/phases"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; + +describe("Abilities - Illusion", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single"); + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ZORUA); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.ILLUSION); + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + vi.spyOn(overrides, "OPP_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "WIDE_LENS", count: 3}]); + + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE]); + vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "WIDE_LENS", count: 3}]); + }); + + it("create illusion at the start", async () => { + await game.startBattle([Species.ZOROARK, Species.AXEW]); + + const zoroark = game.scene.getPlayerPokemon(); + const zorua = game.scene.getEnemyPokemon(); + + expect(zoroark.illusion.active).equals(true); + expect(zorua.illusion.active).equals(true); + expect(zoroark.illusion.available).equals(false); + + }); + + it("disappear after receiving damaging move and changing ability move", async () => { + await game.startBattle([Species.ZOROARK, Species.AXEW]); + game.doAttack(getMovePosition(game.scene, 0, Moves.WORRY_SEED)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zoroark = game.scene.getPlayerPokemon(); + const zorua = game.scene.getEnemyPokemon(); + + expect(zorua.illusion.active).equals(false); + expect(zoroark.illusion.active).equals(false); + }); + + it("disappear if the ability is suppressed", async () => { + vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NEUTRALIZING_GAS); + await game.startBattle([Species.KOFFING]); + + const zorua = game.scene.getEnemyPokemon(); + + expect(zorua.illusion.active).equals(false); + }); + + it("trick the enemy AI", async () => { + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE]); + await game.startBattle([Species.ZOROARK, Species.AXEW]); + + const enemy = game.scene.getEnemyPokemon(); + const zoroark = game.scene.getPlayerPokemon(); + + const flameThwowerEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[0], false, true); + const psychicEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[1], false, true); + + expect(psychicEffectiveness).above(flameThwowerEffectiveness); + }); + + it("do not disappear if the pokemon takes indirect damages", async () => { + vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.GIGALITH); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SAND_STREAM); + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP]); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLARE_BLITZ]); + + await game.startBattle([Species.ZOROARK, Species.AZUMARILL]); + + game.doAttack(getMovePosition(game.scene, 0, Moves.FLARE_BLITZ)); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zoroark = game.scene.getPlayerPokemon(); + + expect(zoroark.illusion.active).equals(true); + }); + + it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE]); + + await game.startBattle([Species.ABRA, Species.ZOROARK, Species.AXEW]); + + const axew = game.scene.getParty().at(2); + axew.shiny = true; + axew.nickname = btoa(unescape(encodeURIComponent("axew nickname"))); + axew.gender = Gender.FEMALE; + axew.pokeball = PokeballType.GREAT_BALL; + + game.doSwitchPokemon(1); + + await game.phaseInterceptor.to(TurnEndPhase); + + const zoroark = game.scene.getPlayerPokemon(); + expect(zoroark.name).equals("Axew"); + expect(zoroark.getNameToRender()).equals("axew nickname"); + expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); + expect(zoroark.isShiny(true)).equals(true); + expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); + }); +}); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index d78b05a569fc..efd589aee9cc 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -298,7 +298,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { }); this.teraIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); - const isFusion = pokemon.isFusion(); + const isFusion = pokemon.isFusion(true); this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 2.5); this.splicedIcon.setVisible(isFusion); @@ -308,7 +308,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny; - const baseVariant = !doubleShiny ? pokemon.getVariant() : pokemon.variant; + const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant; this.shinyIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0) + (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0), 2.5); this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`); @@ -510,6 +510,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender : pokemon.gender; + + this.genderText.setText(getGenderSymbol(gender)); + this.genderText.setColor(getGenderColor(gender)); + const nameUpdated = this.lastName !== pokemon.getNameToRender(); if (nameUpdated) { @@ -527,8 +532,10 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.lastTeraType = teraType; } + const isFusion = pokemon.isFusion(true); + if (nameUpdated || teraTypeUpdated) { - this.splicedIcon.setVisible(!!pokemon.fusionSpecies); + this.splicedIcon.setVisible(isFusion); this.teraIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1, 2); this.splicedIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 1.5); @@ -630,7 +637,17 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.lastBattleStats = battleStatsStr; } - this.shinyIcon.setVisible(pokemon.isShiny()); + this.shinyIcon.setVisible(pokemon.isShiny(true)); + + const doubleShiny = isFusion && pokemon.shiny && pokemon.fusionShiny; + const baseVariant = !doubleShiny ? pokemon.getVariant(true) : pokemon.variant; + this.shinyIcon.setTint(getVariantTint(baseVariant)); + + this.fusionShinyIcon.setVisible(doubleShiny); + if (isFusion) { + this.fusionShinyIcon.setTint(getVariantTint(pokemon.fusionVariant)); + } + this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); resolve(); }); @@ -643,7 +660,8 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((pokemon.gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender : pokemon.gender; + while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`; nameSizeTest.setText(displayName); nameTextWidth = nameSizeTest.displayWidth; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 9bb8162ce2a7..6c3dfda21417 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -117,14 +117,14 @@ export default class PartyUiHandler extends MessageUiHandler { public static FilterNonFainted = (pokemon: PlayerPokemon) => { if (pokemon.isFainted()) { - return i18next.t("partyUiHandler:noEnergy", { pokemonName: getPokemonNameWithAffix(pokemon) }); + return i18next.t("partyUiHandler:noEnergy", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; public static FilterFainted = (pokemon: PlayerPokemon) => { if (!pokemon.isFainted()) { - return i18next.t("partyUiHandler:hasEnergy", { pokemonName: getPokemonNameWithAffix(pokemon) }); + return i18next.t("partyUiHandler:hasEnergy", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -138,7 +138,7 @@ export default class PartyUiHandler extends MessageUiHandler { const challengeAllowed = new Utils.BooleanHolder(true); applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, pokemon, challengeAllowed); if (!challengeAllowed.value) { - return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: getPokemonNameWithAffix(pokemon) }); + return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -148,7 +148,7 @@ export default class PartyUiHandler extends MessageUiHandler { public static FilterItemMaxStacks = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => { const matchingModifier = pokemon.scene.findModifier(m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id && m.matchType(modifier)) as PokemonHeldItemModifier; if (matchingModifier && matchingModifier.stackCount === matchingModifier.getMaxStackCount(pokemon.scene)) { - return i18next.t("partyUiHandler:tooManyItems", { pokemonName: getPokemonNameWithAffix(pokemon) }); + return i18next.t("partyUiHandler:tooManyItems", { pokemonName: getPokemonNameWithAffix(pokemon, false) }); } return null; }; @@ -380,18 +380,18 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); pokemon.pauseEvolutions = false; - this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => this.showText(null, 0), null, true); + this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), null, () => this.showText(null, 0), null, true); } else if (option === PartyOption.UNSPLICE) { this.clearOptions(); ui.playSelect(); - this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies.name, pokemonName: pokemon.name }), null, () => { + this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies.name, pokemonName: pokemon.getName() }), null, () => { ui.setModeWithoutClear(Mode.CONFIRM, () => { - const fusionName = pokemon.name; + const fusionName = pokemon.getName(false); pokemon.unfuse().then(() => { this.clearPartySlots(); this.populatePartySlots(); ui.setMode(Mode.PARTY); - this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.name }), null, () => { + this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.getName() }), null, () => { ui.setMode(Mode.PARTY); this.showText(null, 0); }, null, true); @@ -405,7 +405,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); if (this.cursor >= this.scene.currentBattle.getBattlerCount() || !pokemon.isAllowedInBattle()) { - this.showText(i18next.t("partyUiHandler:releaseConfirmation", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => { + this.showText(i18next.t("partyUiHandler:releaseConfirmation", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), null, () => { ui.setModeWithoutClear(Mode.CONFIRM, () => { ui.setMode(Mode.PARTY); this.doRelease(this.cursor); @@ -950,7 +950,7 @@ export default class PartyUiHandler extends MessageUiHandler { } doRelease(slotIndex: integer): void { - this.showText(this.getReleaseMessage(getPokemonNameWithAffix(this.scene.getParty()[slotIndex])), null, () => { + this.showText(this.getReleaseMessage(getPokemonNameWithAffix(this.scene.getParty()[slotIndex], false)), null, () => { this.clearPartySlots(); this.scene.removePartyMemberModifiers(slotIndex); const releasedPokemon = this.scene.getParty().splice(slotIndex, 1)[0]; @@ -1081,7 +1081,7 @@ class PartySlot extends Phaser.GameObjects.Container { const slotInfoContainer = this.scene.add.container(0, 0); this.add(slotInfoContainer); - let displayName = this.pokemon.getNameToRender(); + let displayName = this.pokemon.getNameToRender(false); let nameTextWidth: number; const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.PARTY); @@ -1148,12 +1148,12 @@ class PartySlot extends Phaser.GameObjects.Container { } if (this.pokemon.isShiny()) { - const doubleShiny = this.pokemon.isFusion() && this.pokemon.shiny && this.pokemon.fusionShiny; + const doubleShiny = this.pokemon.isDoubleShiny(false); const shinyStar = this.scene.add.image(0, 0, `shiny_star_small${doubleShiny ? "_1" : ""}`); shinyStar.setOrigin(0, 0); shinyStar.setPositionRelative(slotName, -9, 3); - shinyStar.setTint(getVariantTint(!doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant)); + shinyStar.setTint(getVariantTint(this.pokemon.getBaseVariant(doubleShiny))); slotInfoContainer.add(shinyStar); @@ -1161,7 +1161,7 @@ class PartySlot extends Phaser.GameObjects.Container { const fusionShinyStar = this.scene.add.image(0, 0, "shiny_star_small_2"); fusionShinyStar.setOrigin(0, 0); fusionShinyStar.setPosition(shinyStar.x, shinyStar.y); - fusionShinyStar.setTint(getVariantTint(this.pokemon.fusionVariant)); + fusionShinyStar.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); slotInfoContainer.add(fusionShinyStar); } diff --git a/src/ui/rename-form-ui-handler.ts b/src/ui/rename-form-ui-handler.ts index 35127564b602..2c229442ed11 100644 --- a/src/ui/rename-form-ui-handler.ts +++ b/src/ui/rename-form-ui-handler.ts @@ -36,7 +36,7 @@ export default class RenameFormUiHandler extends FormModalUiHandler { show(args: any[]): boolean { if (super.show(args)) { const config = args[0] as ModalConfig; - this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender(); + this.inputs[0].text = (args[1] as PlayerPokemon).getNameToRender(false); this.submitAction = (_) => { this.sanitizeInputs(); diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 405429fcd3f4..8c45d767ae00 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -301,8 +301,8 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType())); this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - this.pokemonSprite.setPipelineData("shiny", this.pokemon.shiny); - this.pokemonSprite.setPipelineData("variant", this.pokemon.variant); + this.pokemonSprite.setPipelineData("shiny", this.pokemon.illusion.shiny ?? this.pokemon.shiny); + this.pokemonSprite.setPipelineData("variant", this.pokemon.illusion.variant ?? this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; if (this.pokemon.summonData?.speciesForm) { @@ -312,7 +312,7 @@ export default class SummaryUiHandler extends UiHandler { }); this.pokemon.cry(); - this.nameText.setText(this.pokemon.getNameToRender()); + this.nameText.setText(this.pokemon.getNameToRender(false)); const isFusion = this.pokemon.isFusion(); @@ -346,12 +346,12 @@ export default class SummaryUiHandler extends UiHandler { this.candyShadow.setCrop(0,0,16, candyCropY); - const doubleShiny = isFusion && this.pokemon.shiny && this.pokemon.fusionShiny; - const baseVariant = !doubleShiny ? this.pokemon.getVariant() : this.pokemon.variant; + const doubleShiny = this.pokemon.isDoubleShiny(false); + const baseVariant = this.pokemon.getBaseVariant(doubleShiny); this.shinyIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + (this.splicedIcon.visible ? this.splicedIcon.displayWidth + 1 : 0) + 1, 3); this.shinyIcon.setTexture(`shiny_star${doubleShiny ? "_1" : ""}`); - this.shinyIcon.setVisible(this.pokemon.isShiny()); + this.shinyIcon.setVisible(this.pokemon.isShiny(false)); this.shinyIcon.setTint(getVariantTint(baseVariant)); if (this.shinyIcon.visible) { const shinyDescriptor = doubleShiny || baseVariant ? @@ -364,7 +364,7 @@ export default class SummaryUiHandler extends UiHandler { this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setVisible(doubleShiny); if (isFusion) { - this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.fusionVariant)); + this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); } this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball)); @@ -727,7 +727,7 @@ export default class SummaryUiHandler extends UiHandler { return typeIcon; }; - const types = this.pokemon.getTypes(false, false, true); + const types = this.pokemon.getTypes(false, false, true, false); profileContainer.add(getTypeIcon(0, types[0])); if (types.length > 1) { profileContainer.add(getTypeIcon(1, types[1])); From 34a8e9b7973668c3636f82cce0beeab7575482c8 Mon Sep 17 00:00:00 2001 From: Lylian Date: Wed, 31 Jul 2024 23:04:37 +0200 Subject: [PATCH 02/24] try removing whitespace change on unnecessary files --- package-lock.json | 2 +- public/images/items.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 35089ea0f627..0605b299dab8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6148,4 +6148,4 @@ } } } -} \ No newline at end of file +} diff --git a/public/images/items.json b/public/images/items.json index f6d14d18dbb5..86792e23cc07 100644 --- a/public/images/items.json +++ b/public/images/items.json @@ -8396,4 +8396,4 @@ "version": "3.0", "smartupdate": "$TexturePacker:SmartUpdate:b317e2cd3502364fcdae296cd439ac4d:ae80196191516a8cb098a8467c6faa2f:110e074689c9edd2c54833ce2e4d9270$" } -} \ No newline at end of file +} From 41ca2cd9efcb337440d4413dcf19afe8a89d5f38 Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 5 Aug 2024 01:17:25 +0200 Subject: [PATCH 03/24] nit corrections --- src/data/ability.ts | 22 +++++++---- src/locales/pt_BR/config.ts | 1 - src/test/abilities/illusion.test.ts | 58 ++++++++++++++++------------- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index f3e8270f17b4..3f25c99fd2d2 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1896,11 +1896,14 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { pokemon.scene.queueMessage(this.messageFunc(pokemon)); + return true; + } +} +export class PostSummonRemoveIllusionAbAttr extends PostSummonAbAttr { + applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { pokemon.scene.getField(true).map(pokemon => { - if (pokemon.breakIllusion()) { - pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); - } + pokemon.breakIllusion(); }); return true; } @@ -4049,6 +4052,11 @@ export class IceFaceBlockPhysicalAbAttr extends ReceivedMoveDamageMultiplierAbAt } } +/** + * Base class for defining {@linkcode Ability} attributes before summon + * (should use {@linkcode PostSummonAbAttr} for most ability) + * @see {@linkcode applyPreSummon()} + */ export class PreSummonAbAttr extends AbAttr { applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { return false; @@ -4097,17 +4105,16 @@ export class IllusionBreakAbAttr extends PostDefendAbAttr { */ applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const breakIllusion: HitResult[] = [HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, HitResult.NOT_VERY_EFFECTIVE, HitResult.ONE_HIT_KO]; + const breakIllusion: HitResult[] = [ HitResult.EFFECTIVE, HitResult.SUPER_EFFECTIVE, HitResult.NOT_VERY_EFFECTIVE, HitResult.ONE_HIT_KO ]; if (!breakIllusion.includes(hitResult)) { return false; } pokemon.breakIllusion(); - pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); return true; } } -export class IllusionAfterBattle extends PostBattleAbAttr { +export class IllusionPostBattleAbAttr extends PostBattleAbAttr { /** * Illusion will be available again after a battle and apply the illusion of the pokemon is already on field * @@ -4868,7 +4875,7 @@ export function initAbilities() { //The pokemon loses his illusion when he is damaged by a move .conditionalAttr((pokemon) => pokemon.illusion.active, IllusionBreakAbAttr, true) //Illusion is available again after a battle - .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionAfterBattle, false) + .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) //Illusion is not available after summon .attr(IllusionDisableAbAttr, false) .bypassFaint(), @@ -5246,6 +5253,7 @@ export function initAbilities() { .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonNeutralizingGas", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })) + .attr(PostSummonRemoveIllusionAbAttr) .partial(), new Ability(Abilities.PASTEL_VEIL, 8) .attr(PostSummonUserFieldRemoveStatusEffectAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) diff --git a/src/locales/pt_BR/config.ts b/src/locales/pt_BR/config.ts index 1b0b20d532b5..5f7582dca63c 100644 --- a/src/locales/pt_BR/config.ts +++ b/src/locales/pt_BR/config.ts @@ -1,6 +1,5 @@ import { ability } from "./ability"; import { abilityTriggers } from "./ability-trigger"; -import { arenaFlyout } from "./arena-flyout"; import { PGFachv, PGMachv } from "./achv"; import { arenaFlyout } from "./arena-flyout"; import { arenaTag } from "./arena-tag"; diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 39683b956ec0..1fe2f740e1fc 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -28,17 +28,17 @@ describe("Abilities - Illusion", () => { beforeEach(() => { game = new GameManager(phaserGame); - vi.spyOn(overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue("single"); - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.ZORUA); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.ILLUSION); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - vi.spyOn(overrides, "OPP_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "WIDE_LENS", count: 3}]); - - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE]); - vi.spyOn(overrides, "STARTING_HELD_ITEMS_OVERRIDE", "get").mockReturnValue([{name: "WIDE_LENS", count: 3}]); + game.override.battleType("single"); + game.override.enemySpecies(Species.ZORUA); + game.override.enemyAbility(Abilities.ILLUSION); + game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); + game.override.enemyHeldItems([{name: "WIDE_LENS", count: 3}]); + + game.override.moveset([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE]); + game.override.startingHeldItems([{name: "WIDE_LENS", count: 3}]); }); - it("create illusion at the start", async () => { + it("creates illusion at the start", async () => { await game.startBattle([Species.ZOROARK, Species.AXEW]); const zoroark = game.scene.getPlayerPokemon(); @@ -47,24 +47,30 @@ describe("Abilities - Illusion", () => { expect(zoroark.illusion.active).equals(true); expect(zorua.illusion.active).equals(true); expect(zoroark.illusion.available).equals(false); - }); - it("disappear after receiving damaging move and changing ability move", async () => { - await game.startBattle([Species.ZOROARK, Species.AXEW]); - game.doAttack(getMovePosition(game.scene, 0, Moves.WORRY_SEED)); + it("break after receiving damaging move", async () => { + await game.startBattle([Species.AXEW]); + game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); await game.phaseInterceptor.to(TurnEndPhase); - const zoroark = game.scene.getPlayerPokemon(); const zorua = game.scene.getEnemyPokemon(); expect(zorua.illusion.active).equals(false); - expect(zoroark.illusion.active).equals(false); }); - it("disappear if the ability is suppressed", async () => { - vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.NEUTRALIZING_GAS); + it("break after getting ability changed", async () => { + await game.startBattle([Species.AXEW]); + game.doAttack(getMovePosition(game.scene, 0, Moves.WORRY_SEED)); + + const zorua = game.scene.getEnemyPokemon(); + + expect(zorua.illusion.active).equals(false); + }); + + it("break if the ability is suppressed", async () => { + game.override.enemyAbility(Abilities.NEUTRALIZING_GAS); await game.startBattle([Species.KOFFING]); const zorua = game.scene.getEnemyPokemon(); @@ -72,24 +78,24 @@ describe("Abilities - Illusion", () => { expect(zorua.illusion.active).equals(false); }); - it("trick the enemy AI", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE]); + it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { + game.override.enemyMoveset([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE]); await game.startBattle([Species.ZOROARK, Species.AXEW]); const enemy = game.scene.getEnemyPokemon(); const zoroark = game.scene.getPlayerPokemon(); - const flameThwowerEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[0], false, true); + const flameThrowerEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[0], false, true); const psychicEffectiveness = zoroark.getAttackMoveEffectiveness(enemy, enemy.getMoveset()[1], false, true); - expect(psychicEffectiveness).above(flameThwowerEffectiveness); + expect(psychicEffectiveness).above(flameThrowerEffectiveness); }); - it("do not disappear if the pokemon takes indirect damages", async () => { - vi.spyOn(overrides, "OPP_SPECIES_OVERRIDE", "get").mockReturnValue(Species.GIGALITH); - vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.SAND_STREAM); - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP]); - vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.FLARE_BLITZ]); + it("do not breaks if the pokemon takes indirect damages", async () => { + game.override.enemySpecies(Species.GIGALITH); + game.override.enemyAbility(Abilities.SAND_STREAM); + game.override.enemyMoveset([Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP]); + game.override.moveset([Moves.FLARE_BLITZ]); await game.startBattle([Species.ZOROARK, Species.AZUMARILL]); From 909d166dd708a51ee7c15a0f4f8d87fa6dc66121 Mon Sep 17 00:00:00 2001 From: Lylian BALL <131535108+PyGaVS@users.noreply.github.com> Date: Mon, 5 Aug 2024 01:19:30 +0200 Subject: [PATCH 04/24] nit update src/field/pokemon.ts Co-authored-by: Adrian T. <68144167+torranx@users.noreply.github.com> --- src/field/pokemon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 365d8c545f13..c25d30b8b98c 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -128,7 +128,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const randAbilityIndex = Utils.randSeedInt(2); this.species = species; - this.illusion = {active: false, available: true}; + this.illusion = { active: false, available: true }; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; From 205ee8367b28c2949dfdd05ce3837140fa4e9211 Mon Sep 17 00:00:00 2001 From: Lylian BALL <131535108+PyGaVS@users.noreply.github.com> Date: Mon, 5 Aug 2024 01:20:55 +0200 Subject: [PATCH 05/24] nit update src/phases.ts Co-authored-by: Amani H. <109637146+xsn34kzx@users.noreply.github.com> --- src/phases.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/phases.ts b/src/phases.ts index 6cc1db399c08..f4f4deefa69b 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1592,8 +1592,6 @@ export class SwitchSummonPhase extends SummonPhase { } const pokemon: Pokemon = this.getPokemon(); - // if doReturn === False OR slotIndex !== -1 (slotIndex is valid) and the pokemon doesn't exist/is false - // then switchAndSummon(), manually pick pokemon to switch into if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) { if (this.player) { return this.switchAndSummon(); From 889465be65925ed58f9ec127701f1d32194c2ab8 Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 5 Aug 2024 02:12:39 +0200 Subject: [PATCH 06/24] illusion test correction --- src/test/abilities/illusion.test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 1fe2f740e1fc..5c34e792e71f 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -64,6 +64,8 @@ describe("Abilities - Illusion", () => { await game.startBattle([Species.AXEW]); game.doAttack(getMovePosition(game.scene, 0, Moves.WORRY_SEED)); + await game.phaseInterceptor.to(TurnEndPhase); + const zorua = game.scene.getEnemyPokemon(); expect(zorua.illusion.active).equals(false); From 8b4a75897e87fcdc3dd352d8581e1abbd6bbd786 Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 8 Aug 2024 15:44:59 +0200 Subject: [PATCH 07/24] unexpected error correction --- src/ui/battle-info.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index ab1802d59ea1..99f10a70dd9f 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -540,7 +540,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender : pokemon.gender; + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; this.genderText.setText(getGenderSymbol(gender)); this.genderText.setColor(getGenderColor(gender)); @@ -690,7 +690,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender : pokemon.gender; + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`; nameSizeTest.setText(displayName); From 7214c70f93f9be47923e7020b07b0c5491957987 Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 19 Aug 2024 22:40:15 +0200 Subject: [PATCH 08/24] refactor property pokemon.illusion to pokemon.battleData.illusion --- src/data/ability.ts | 12 ++-- src/data/move.ts | 2 +- src/field/pokemon.ts | 95 +++++++++++++++-------------- src/phases/summon-phase.ts | 6 +- src/phases/switch-summon-phase.ts | 3 +- src/test/abilities/illusion.test.ts | 16 ++--- src/ui/battle-info.ts | 4 +- src/ui/party-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 6 +- 9 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 32de8b989f2d..c4f761b0332e 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2265,7 +2265,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { return false; } - if (target.illusion.active) { + if (target.battleData.illusion.active) { return false; } pokemon.summonData.speciesForm = target.getSpeciesForm(); @@ -4122,7 +4122,7 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } }); - if (pokemon.illusion.available && !suppressed) { + if (pokemon.battleData.illusion.available && !suppressed) { return pokemon.generateIllusion(); } else { return false; @@ -4164,7 +4164,7 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { */ applyPostBattle(pokemon: Pokemon, passive: boolean, args: any[]): boolean { pokemon.breakIllusion(); - pokemon.illusion.available = true; + pokemon.battleData.illusion.available = true; return true; } } @@ -4180,7 +4180,7 @@ export class IllusionDisableAbAttr extends PostSummonAbAttr { * @returns {boolean} */ applyPostSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { - pokemon.illusion.available = false; + pokemon.battleData.illusion.available = false; return true; } } @@ -4950,9 +4950,9 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) //The pokemon generate an illusion if it's available - .conditionalAttr((pokemon) => pokemon.illusion.available, IllusionPreSummonAbAttr, false) + .conditionalAttr((pokemon) => pokemon.battleData.illusion.available, IllusionPreSummonAbAttr, false) //The pokemon loses his illusion when he is damaged by a move - .conditionalAttr((pokemon) => pokemon.illusion.active, IllusionBreakAbAttr, true) + .conditionalAttr((pokemon) => pokemon.battleData.illusion.active, IllusionBreakAbAttr, true) //Illusion is available again after a battle .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) //Illusion is not available after summon diff --git a/src/data/move.ts b/src/data/move.ts index 0a217786b58e..4667336b6b5b 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -5816,7 +5816,7 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { export class TransformAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - if (!super.apply(user, target, move, args) || target.illusion.active || user.illusion.active) { + if (!super.apply(user, target, move, args) || target.battleData.illusion.active || user.battleData.illusion.active) { user.scene.queueMessage(i18next.t("battle:attackFailed")); return resolve(false); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index b72b64d123ab..4cde21420e23 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -71,7 +71,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public name: string; public nickname: string; public species: PokemonSpecies; - public illusion: Illusion; public formIndex: integer; public abilityIndex: integer; public passive: boolean; @@ -137,7 +136,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const randAbilityIndex = Utils.randSeedInt(2); this.species = species; - this.illusion = { active: false, available: true }; this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; @@ -258,8 +256,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name! : this.name; - const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname! : this.nickname; + const name: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.name! : this.name; + const nickname: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.nickname! : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -274,6 +272,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { init(): void { this.fieldPosition = FieldPosition.CENTER; + this.resetBattleData(); this.initBattleInfo(); this.scene.fieldUI.addAt(this.battleInfo, 0); @@ -368,12 +367,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const lastPokemon: Pokemon = party.at(-1) || this; const speciesId = lastPokemon.species.speciesId; - if ( lastPokemon === this || this.illusion.active || + if ( lastPokemon === this || this.battleData.illusion.active || ((speciesId === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized() || this.isTerastallized()))) { return false; } - this.illusion = { + this.battleData.illusion = { active: true, available: true, name: this.name, @@ -424,7 +423,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const randomIllusion: PokemonSpecies = getPokemonSpecies(availables[this.randSeedInt(availables.length)]); - this.illusion = { + this.battleData.illusion = { active: true, available: true, species: randomIllusion, @@ -441,17 +440,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } breakIllusion(): boolean { - if (!this.illusion.active) { + if (!this.battleData.illusion.active) { return false; } - this.name = this.illusion.name!; - this.nickname = this.illusion.nickname!; - this.shiny = this.illusion.shiny!; - this.variant = this.illusion.variant!; - this.fusionVariant = this.illusion.fusionVariant!; - this.fusionShiny = this.illusion.fusionShiny!; - this.illusion = {active: false, available: false}; + this.name = this.battleData.illusion.name!; + this.nickname = this.battleData.illusion.nickname!; + this.shiny = this.battleData.illusion.shiny!; + this.variant = this.battleData.illusion.variant!; + this.fusionVariant = this.battleData.illusion.fusionVariant!; + this.fusionShiny = this.battleData.illusion.fusionShiny!; + this.battleData.illusion = {active: false, available: false}; if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); } @@ -480,15 +479,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); - const formIndex = this.illusion.active && useIllusion ? this.illusion.formIndex : this.formIndex; + const formIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.formIndex : this.formIndex; this.getSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), this.getVariant(useIllusion)); if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { this.scene.loadPokemonAtlas(this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride)); } if (this.getFusionSpeciesForm(false, useIllusion)) { - const fusionFormIndex = this.illusion.active && useIllusion ? this.illusion.fusionFormIndex : this.fusionFormIndex; - const fusionShiny = this.illusion.active && !useIllusion ? this.illusion.fusionShiny : this.fusionShiny; - const fusionVariant = this.illusion.active && !useIllusion ? this.illusion.fusionVariant : this.fusionVariant; + const fusionFormIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionShiny : this.fusionShiny; + const fusionVariant = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionVariant : this.fusionVariant; this.getFusionSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getFusionGender(false, useIllusion) === Gender.FEMALE, fusionFormIndex, fusionShiny, fusionVariant); this.scene.loadPokemonAtlas(this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride)); } @@ -589,7 +588,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } @@ -598,7 +597,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } @@ -607,8 +606,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, - this.illusion.shiny ?? this.shiny, - this.illusion.variant ?? this.variant + this.battleData.illusion.shiny ?? this.shiny, + this.battleData.illusion.variant ?? this.variant ); } @@ -617,7 +616,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -626,7 +625,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } @@ -640,7 +639,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } @@ -649,12 +648,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -662,8 +661,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species! : this.species; - const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; + const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; @@ -680,8 +679,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies! : this.fusionSpecies!; - const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionSpecies: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; @@ -1003,8 +1002,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.illusion.active) { - return this.illusion.gender!; + console.log("ALLLO", this.battleData.illusion.active); + if (useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } @@ -1015,8 +1015,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionGender of the illusion or not. */ getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.illusion.active) { - return this.illusion.fusionGender!; + if (useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.fusionGender!; } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } @@ -1024,24 +1024,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.illusion.active) { - return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny) || false; + if (!useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.shiny || (!!this.battleData.illusion.fusionSpecies && this.battleData.illusion.fusionShiny) || false; } else { return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } } isDoubleShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.illusion.active) { - return this.isFusion(false) && this.illusion.shiny! && this.illusion.fusionShiny!; + if (!useIllusion && this.battleData.illusion.active) { + return this.isFusion(false) && this.battleData.illusion.shiny! && this.battleData.illusion.fusionShiny!; } else { return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } } getVariant(useIllusion: boolean = false): Variant { - if (!useIllusion && this.illusion.active) { - return !this.isFusion(false) ? this.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; + if (!useIllusion && this.battleData.illusion.active) { + return !this.isFusion(false) ? this.battleData.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; } else { return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; } @@ -1050,7 +1050,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { - return this.illusion.active ? this.illusion.variant! : this.variant; + return this.battleData.illusion.active ? this.battleData.illusion.variant! : this.variant; } else { return this.getVariant(); } @@ -1061,15 +1061,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isFusion(useIllusion: boolean = false): boolean { - if (useIllusion && this.illusion.active) { - return !!this.illusion.fusionSpecies; + if (useIllusion && this.battleData.illusion.active) { + return !!this.battleData.illusion.fusionSpecies; } else { return !!this.fusionSpecies; } } getName(illusion: boolean = false): string { - return (!illusion && this.illusion.active && this.illusion.name) ? this.illusion.name : this.name; + return (!illusion && this.battleData.illusion.active && this.battleData.illusion.name) ? this.battleData.illusion.name : this.name; } abstract isBoss(): boolean; @@ -1147,7 +1147,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (!types.length || !includeTeraType) { const doIllusion: boolean = useIllusion === "AUTO" ? !forDefend : useIllusion; - if (!ignoreOverride && this.summonData?.types && this.summonData.types.length !== 0 && (!this.illusion.active || !doIllusion)) { + if (!ignoreOverride && this.summonData?.types && this.summonData.types.length !== 0 && (!this.battleData.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else { const speciesForm = this.getSpeciesForm(ignoreOverride, doIllusion); @@ -4537,6 +4537,7 @@ export class PokemonBattleData { public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; public abilityRevealed: boolean = false; + public illusion: Illusion = { active: false, available: true }; } export class PokemonBattleSummonData { diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index d523aaa46d29..d20c8cf04c48 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -95,7 +95,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { const pokemon = this.getPokemon(); const pokeball = this.scene.addFieldSprite( - this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.illusion.pokeball ?? pokemon.pokeball) + this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball) ); pokeball.setVisible(false); pokeball.setOrigin(0.5, 0.625); @@ -142,7 +142,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); } - addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.illusion.pokeball ?? pokemon.pokeball); + addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.battleData.illusion.pokeball ?? pokemon.pokeball); this.scene.updateModifiers(this.player); this.scene.updateFieldScale(); pokemon.showInfo(); @@ -150,7 +150,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.setVisible(true); pokemon.getSprite().setVisible(true); pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball)); + pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball)); pokemon.untint(250, "Sine.easeIn"); this.scene.updateFieldScale(); this.scene.tweens.add({ diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index b59375b2230f..d488af96df1c 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -76,7 +76,7 @@ export class SwitchSummonPhase extends SummonPhase { ); this.scene.playSound("pb_rel"); pokemon.hideInfo(); - pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); + pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); this.scene.tweens.add({ targets: pokemon, duration: 250, @@ -118,7 +118,6 @@ export class SwitchSummonPhase extends SummonPhase { ); // Ensure improperly persisted summon data (such as tags) is cleared upon switching if (!this.batonPass) { - switchedInPokemon.resetBattleData(); switchedInPokemon.resetSummonData(); } this.summon(); diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 4f0a2f39e49c..a499b9c74778 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -44,9 +44,9 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; const zorua = game.scene.getEnemyPokemon()!; - expect(zoroark.illusion.active).equals(true); - expect(zorua.illusion.active).equals(true); - expect(zoroark.illusion.available).equals(false); + expect(zoroark.battleData.illusion.active).equals(true); + expect(zorua.battleData.illusion.active).equals(true); + expect(zoroark.battleData.illusion.available).equals(false); }); it("break after receiving damaging move", async () => { @@ -57,7 +57,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("break after getting ability changed", async () => { @@ -68,7 +68,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("break if the ability is suppressed", async () => { @@ -77,7 +77,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { @@ -107,7 +107,7 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; - expect(zoroark.illusion.active).equals(true); + expect(zoroark.battleData.illusion.active).equals(true); }); it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { @@ -130,6 +130,6 @@ describe("Abilities - Illusion", () => { expect(zoroark.getNameToRender()).equals("axew nickname"); expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); expect(zoroark.isShiny(true)).equals(true); - expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); + expect(zoroark.battleData.illusion.pokeball).equals(PokeballType.GREAT_BALL); }); }); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 99f10a70dd9f..d33d5a317bff 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -540,7 +540,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; this.genderText.setText(getGenderSymbol(gender)); this.genderText.setColor(getGenderColor(gender)); @@ -690,7 +690,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`; nameSizeTest.setText(displayName); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 49d23659bd23..ac8a7c3a83ba 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1282,7 +1282,7 @@ class PartySlot extends Phaser.GameObjects.Container { const fusionShinyStar = this.scene.add.image(0, 0, "shiny_star_small_2"); fusionShinyStar.setOrigin(0, 0); fusionShinyStar.setPosition(shinyStar.x, shinyStar.y); - fusionShinyStar.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + fusionShinyStar.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); slotInfoContainer.add(fusionShinyStar); } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 042437d8c85e..80977a352a73 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -300,8 +300,8 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType())); this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - this.pokemonSprite.setPipelineData("shiny", this.pokemon.illusion.shiny ?? this.pokemon.shiny); - this.pokemonSprite.setPipelineData("variant", this.pokemon.illusion.variant ?? this.pokemon.variant); + this.pokemonSprite.setPipelineData("shiny", this.pokemon.battleData.illusion.shiny ?? this.pokemon.shiny); + this.pokemonSprite.setPipelineData("variant", this.pokemon.battleData.illusion.variant ?? this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; if (this.pokemon?.summonData?.speciesForm) { @@ -363,7 +363,7 @@ export default class SummaryUiHandler extends UiHandler { this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setVisible(doubleShiny); if (isFusion) { - this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); } this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball)); From 20295da9123a3acb34d4d62d26cd97ddfccfa49e Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 19 Aug 2024 22:43:31 +0200 Subject: [PATCH 09/24] nit --- src/data/ability.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index c4f761b0332e..0ee7b0b68939 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2259,10 +2259,8 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { let target: Pokemon = targets[0]; if (targets.length > 1) { pokemon.scene.executeWithSeedOffset(() => target = Utils.randSeedItem(targets), pokemon.scene.currentBattle.waveIndex); - } else if (targets.length === 1) { - target = targets[0]; } else { - return false; + target = targets[0]; } if (target.battleData.illusion.active) { From 62865efe8e17199655e81b87015a323e67b87261 Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 19 Aug 2024 22:51:07 +0200 Subject: [PATCH 10/24] nit --- src/data/ability.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 0ee7b0b68939..9d0fd65a98a5 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4112,10 +4112,7 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { let suppressed = false; pokemon.scene.getField(true).filter(p => p !== pokemon).map(p => { - if (p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) { - suppressed = true; - } - if (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true)) { + if ((p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) || (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true))) { suppressed = true; } }); From 9c4703136c7dd23c0cfdd249a89668fc0b99a7db Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 26 Aug 2024 18:55:19 +0200 Subject: [PATCH 11/24] update unit test up-to-date --- src/test/abilities/illusion.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index a499b9c74778..92b399ebae95 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -9,7 +9,6 @@ import { TurnEndPhase, } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; describe("Abilities - Illusion", () => { @@ -51,7 +50,7 @@ describe("Abilities - Illusion", () => { it("break after receiving damaging move", async () => { await game.startBattle([Species.AXEW]); - game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); + game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); @@ -62,7 +61,7 @@ describe("Abilities - Illusion", () => { it("break after getting ability changed", async () => { await game.startBattle([Species.AXEW]); - game.doAttack(getMovePosition(game.scene, 0, Moves.WORRY_SEED)); + game.move.select(Moves.WORRY_SEED); await game.phaseInterceptor.to(TurnEndPhase); @@ -101,7 +100,7 @@ describe("Abilities - Illusion", () => { await game.startBattle([Species.ZOROARK, Species.AZUMARILL]); - game.doAttack(getMovePosition(game.scene, 0, Moves.FLARE_BLITZ)); + game.move.select(Moves.FLARE_BLITZ); await game.phaseInterceptor.to(TurnEndPhase); From c24359c6412fd7fd5a924291debdfbbcb051748f Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 26 Aug 2024 19:12:05 +0200 Subject: [PATCH 12/24] add docs --- src/data/ability.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/data/ability.ts b/src/data/ability.ts index fa7c397b7010..efbcd83c4cea 100755 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1950,6 +1950,9 @@ export class PostSummonMessageAbAttr extends PostSummonAbAttr { } } +/** + * Removes illusions when a Pokemon is summoned. + */ export class PostSummonRemoveIllusionAbAttr extends PostSummonAbAttr { applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { pokemon.scene.getField(true).map(pokemon => { From 05ad97f64fd5e9fbda37dd626e46cbeea8958f42 Mon Sep 17 00:00:00 2001 From: Lylian Date: Mon, 16 Sep 2024 13:29:28 +0200 Subject: [PATCH 13/24] merge up to date --- src/phases/summon-phase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 73671e5c82cf..4816b2ac10b6 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -13,7 +13,7 @@ import { PostSummonPhase } from "./post-summon-phase"; import { GameOverPhase } from "./game-over-phase"; import { ShinySparklePhase } from "./shiny-sparkle-phase"; import { MysteryEncounterMode } from "#enums/mystery-encounter-mode"; -import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/ability.js"; +import { applyPreSummonAbAttrs, PreSummonAbAttr } from "#app/data/ability"; export class SummonPhase extends PartyMemberPokemonPhase { private loaded: boolean; From b5d8034e97325f4c7b78696227a67259b0e1a4fa Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 15:22:36 +0200 Subject: [PATCH 14/24] bugfix --- src/field/pokemon.ts | 3 ++- src/overrides.ts | 2 +- src/phases/switch-summon-phase.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index ced1cf290dc4..8c4b5965331b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -451,6 +451,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { active: true, available: true, species: randomIllusion, + formIndex: randomIllusion.formIndex, name: this.name, shiny: this.shiny, variant: this.variant, @@ -686,6 +687,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; + const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { @@ -1194,7 +1196,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - console.log("ALLLO", this.battleData.illusion.active); if (useIllusion && this.battleData.illusion.active) { return this.battleData.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { diff --git a/src/overrides.ts b/src/overrides.ts index 6b550d152c21..c293568e704a 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -106,7 +106,7 @@ class DefaultOverrides { // -------------------------- // OPPONENT / ENEMY OVERRIDES // -------------------------- - readonly OPP_SPECIES_OVERRIDE: Species | number = 0; + readonly OPP_SPECIES_OVERRIDE: Species | number = Species.ZOROARK; 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/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 5a780c214c8d..d8c7b4377d33 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -1,5 +1,5 @@ import BattleScene from "#app/battle-scene"; -import { applyPreSummonAbAttrs, applyPreSwitchOutAbAttrs, PreSummonAbAttr, PreSwitchOutAbAttr } from "#app/data/ability.js"; +import { applyPreSummonAbAttrs, applyPreSwitchOutAbAttrs, PreSummonAbAttr, PreSwitchOutAbAttr } from "#app/data/ability"; import { allMoves, ForceSwitchOutAttr } from "#app/data/move"; import { getPokeballTintColor } from "#app/data/pokeball"; import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms"; From df1c7898e86cdde7e6afe01310b809fbf379e260 Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 15:23:30 +0200 Subject: [PATCH 15/24] bugfix --- src/field/pokemon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 8c4b5965331b..a385b9b4d57b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -687,7 +687,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; - + const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { From 57bbeb1acf16abbae1eb4a3bfa4e682d815d43a7 Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 15:40:47 +0200 Subject: [PATCH 16/24] merge up to date --- src/data/move.ts | 2 +- src/field/pokemon.ts | 4 ++-- src/ui/party-ui-handler.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/move.ts b/src/data/move.ts index e80aba9c5022..7ab41768dc59 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6245,7 +6245,7 @@ export class AbilityChangeAttr extends MoveEffectAttr { if (pokemon.breakIllusion()) { pokemon.scene.queueMessage(i18next.t("abilityTriggers:illusionBreak", { pokemonName: getPokemonNameWithAffix(pokemon) })); } - user.scene.queueMessage(i18next.t("moveTriggers:acquiredAbility", {pokemonName: getPokemonNameWithAffix(pokemon), abilityName: allAbilities[this.ability].name})); + user.scene.queueMessage(i18next.t("moveTriggers:acquiredAbility", { pokemonName: getPokemonNameWithAffix(pokemon), abilityName: allAbilities[this.ability].name })); return true; } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 84a5d78374e8..e412e24b54b7 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -438,7 +438,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } else { let availables: Species[] = []; if (this.isBoss()) { - availables = [Species.ENTEI, Species.RAIKOU, Species.SUICUNE]; + availables = [ Species.ENTEI, Species.RAIKOU, Species.SUICUNE ]; } else { const area: Species[] = [ Species.HOUNDOUR, Species.SABLEYE, Species.PURRLOIN, Species.PAWNIARD, Species.NICKIT, @@ -485,7 +485,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.variant = this.battleData.illusion.variant!; this.fusionVariant = this.battleData.illusion.fusionVariant!; this.fusionShiny = this.battleData.illusion.fusionShiny!; - this.battleData.illusion = {active: false, available: false}; + this.battleData.illusion = { active: false, available: false }; if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); } diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 04bbfd4ef743..4051ac04b216 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -468,7 +468,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); pokemon.pauseEvolutions = !pokemon.pauseEvolutions; - this.showText(i18next.t(pokemon.pauseEvolutions? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), undefined, () => this.showText("", 0), null, true); + this.showText(i18next.t(pokemon.pauseEvolutions ? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon, false) }), undefined, () => this.showText("", 0), null, true); } else if (option === PartyOption.UNSPLICE) { this.clearOptions(); ui.playSelect(); From 1e9a2a749d3650dfa7e02c5ce3f1ec6ffdec5b46 Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 17:49:18 +0200 Subject: [PATCH 17/24] refactor field illusion out of battleData --- src/data/ability.ts | 13 ++-- src/data/move.ts | 2 +- src/field/pokemon.ts | 99 +++++++++++++++-------------- src/phases/summon-phase.ts | 6 +- src/phases/switch-summon-phase.ts | 2 +- src/test/abilities/illusion.test.ts | 17 ++--- src/ui/battle-info.ts | 4 +- src/ui/party-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 6 +- 9 files changed, 79 insertions(+), 72 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index c03f8e6f26b9..23f926c6ad73 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2428,7 +2428,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; } - if (target.battleData.illusion.active) { + if (target.illusion.active) { return false; } pokemon.summonData.speciesForm = target.getSpeciesForm(); @@ -4562,13 +4562,14 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { */ applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { let suppressed = false; + console.log("PRESUMMONILLUSION : ", pokemon.name) pokemon.scene.getField(true).filter(p => p !== pokemon).map(p => { if ((p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) || (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true))) { suppressed = true; } }); - if (pokemon.battleData.illusion.available && !suppressed) { + if (pokemon.illusion.available && !suppressed) { return pokemon.generateIllusion(); } else { return false; @@ -4610,7 +4611,7 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { */ applyPostBattle(pokemon: Pokemon, passive: boolean, simulated:boolean, args: any[]): boolean { pokemon.breakIllusion(); - pokemon.battleData.illusion.available = true; + pokemon.illusion.available = true; return true; } } @@ -4626,7 +4627,7 @@ export class IllusionDisableAbAttr extends PostSummonAbAttr { * @returns {boolean} */ applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - pokemon.battleData.illusion.available = false; + pokemon.illusion.available = false; return true; } } @@ -5412,9 +5413,9 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) //The pokemon generate an illusion if it's available - .conditionalAttr((pokemon) => pokemon.battleData.illusion.available, IllusionPreSummonAbAttr, false) + .conditionalAttr((pokemon) => pokemon.illusion.available, IllusionPreSummonAbAttr, false) //The pokemon loses his illusion when he is damaged by a move - .conditionalAttr((pokemon) => pokemon.battleData.illusion.active, IllusionBreakAbAttr, true) + .conditionalAttr((pokemon) => pokemon.illusion.active, IllusionBreakAbAttr, true) //Illusion is available again after a battle .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) //Illusion is not available after summon diff --git a/src/data/move.ts b/src/data/move.ts index 7ab41768dc59..d22a29067767 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6402,7 +6402,7 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { export class TransformAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - if (!super.apply(user, target, move, args) || target.battleData.illusion.active || user.battleData.illusion.active) { + if (!super.apply(user, target, move, args) || target.illusion.active || user.illusion.active) { user.scene.queueMessage(i18next.t("battle:attackFailed")); return resolve(false); } diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index e412e24b54b7..d1b27a68051f 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -123,6 +123,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public battleSummonData: PokemonBattleSummonData; public turnData: PokemonTurnData; public mysteryEncounterPokemonData: MysteryEncounterPokemonData; + public illusion: Illusion; /** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */ public mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; @@ -153,6 +154,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; this.switchOutStatus = false; + this.illusion = { active: false, available: true }; // Determine the ability index if (abilityIndex !== undefined) { @@ -281,8 +283,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.name! : this.name; - const nickname: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.nickname! : this.nickname; + const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name! : this.name; + const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname! : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -397,16 +399,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ generateIllusion(): boolean { if (this.hasTrainer()) { + console.log("GENERATEILLUSION() : ", this.name) const party: Pokemon[] = (this.isPlayer() ? this.scene.getParty() : this.scene.getEnemyParty()).filter(p => p.isAllowedInBattle()); - const lastPokemon: Pokemon = party.at(-1) || this; + const lastPokemon: Pokemon = party.filter(p => p !== this).at(-1) || this; + console.log(lastPokemon.name) const speciesId = lastPokemon.species.speciesId; - if ( lastPokemon === this || this.battleData.illusion.active || + if ( lastPokemon === this || this.illusion.active || ((speciesId === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized() || this.isTerastallized()))) { return false; } - this.battleData.illusion = { + this.illusion = { active: true, available: true, name: this.name, @@ -457,7 +461,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const randomIllusion: PokemonSpecies = getPokemonSpecies(availables[this.randSeedInt(availables.length)]); - this.battleData.illusion = { + this.illusion = { active: true, available: true, species: randomIllusion, @@ -471,21 +475,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.name = randomIllusion.name; this.loadAssets(false, true).then(() => this.playAnim()); } + console.log("L'ILLUSION : ", this.illusion) return true; } breakIllusion(): boolean { - if (!this.battleData.illusion.active) { + console.log("BREAKILLUSION") + if (!this.illusion.active) { return false; } - this.name = this.battleData.illusion.name!; - this.nickname = this.battleData.illusion.nickname!; - this.shiny = this.battleData.illusion.shiny!; - this.variant = this.battleData.illusion.variant!; - this.fusionVariant = this.battleData.illusion.fusionVariant!; - this.fusionShiny = this.battleData.illusion.fusionShiny!; - this.battleData.illusion = { active: false, available: false }; + this.name = this.illusion.name!; + this.nickname = this.illusion.nickname!; + this.shiny = this.illusion.shiny!; + this.variant = this.illusion.variant!; + this.fusionVariant = this.illusion.fusionVariant!; + this.fusionShiny = this.illusion.fusionShiny!; + this.illusion = { active: false, available: false }; if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); } @@ -514,15 +520,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); - const formIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.formIndex : this.formIndex; + const formIndex = this.illusion.active && useIllusion ? this.illusion.formIndex : this.formIndex; this.getSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), this.getVariant(useIllusion)); if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { this.scene.loadPokemonAtlas(this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride)); } if (this.getFusionSpeciesForm(false, useIllusion)) { - const fusionFormIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.fusionFormIndex : this.fusionFormIndex; - const fusionShiny = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionShiny : this.fusionShiny; - const fusionVariant = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionVariant : this.fusionVariant; + const fusionFormIndex = this.illusion.active && useIllusion ? this.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = this.illusion.active && !useIllusion ? this.illusion.fusionShiny : this.fusionShiny; + const fusionVariant = this.illusion.active && !useIllusion ? this.illusion.fusionVariant : this.fusionVariant; this.getFusionSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getFusionGender(false, useIllusion) === Gender.FEMALE, fusionFormIndex, fusionShiny, fusionVariant); this.scene.loadPokemonAtlas(this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride)); } @@ -623,7 +629,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } @@ -632,7 +638,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } @@ -641,8 +647,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, - this.battleData.illusion.shiny ?? this.shiny, - this.battleData.illusion.variant ?? this.variant + this.illusion.shiny ?? this.shiny, + this.illusion.variant ?? this.variant ); } @@ -651,7 +657,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -660,7 +666,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } @@ -674,7 +680,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } @@ -683,12 +689,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconId(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -696,9 +702,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; + const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species! : this.species; - const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; @@ -714,8 +720,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionSpecies! : this.fusionSpecies!; - const fusionFormIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; @@ -1211,8 +1217,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.gender!; + if (useIllusion && this.illusion.active) { + return this.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } @@ -1223,8 +1229,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionGender of the illusion or not. */ getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.fusionGender!; + if (useIllusion && this.illusion.active) { + return this.illusion.fusionGender!; } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } @@ -1232,24 +1238,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.shiny || (!!this.battleData.illusion.fusionSpecies && this.battleData.illusion.fusionShiny) || false; + if (!useIllusion && this.illusion.active) { + return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny) || false; } else { return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } } isDoubleShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.battleData.illusion.active) { - return this.isFusion(false) && this.battleData.illusion.shiny! && this.battleData.illusion.fusionShiny!; + if (!useIllusion && this.illusion.active) { + return this.isFusion(false) && this.illusion.shiny! && this.illusion.fusionShiny!; } else { return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } } getVariant(useIllusion: boolean = false): Variant { - if (!useIllusion && this.battleData.illusion.active) { - return !this.isFusion(false) ? this.battleData.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; + if (!useIllusion && this.illusion.active) { + return !this.isFusion(false) ? this.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; } else { return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; } @@ -1258,7 +1264,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { - return this.battleData.illusion.active ? this.battleData.illusion.variant! : this.variant; + return this.illusion.active ? this.illusion.variant! : this.variant; } else { return this.getVariant(); } @@ -1269,15 +1275,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isFusion(useIllusion: boolean = false): boolean { - if (useIllusion && this.battleData.illusion.active) { - return !!this.battleData.illusion.fusionSpecies; + if (useIllusion && this.illusion.active) { + return !!this.illusion.fusionSpecies; } else { return !!this.fusionSpecies; } } getName(illusion: boolean = false): string { - return (!illusion && this.battleData.illusion.active && this.battleData.illusion.name) ? this.battleData.illusion.name : this.name; + return (!illusion && this.illusion.active && this.illusion.name) ? this.illusion.name : this.name; } /** @@ -1381,7 +1387,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters this.mysteryEncounterPokemonData.types.forEach(t => types.push(t)); - } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.battleData.illusion.active || !doIllusion)) { + } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters @@ -5300,7 +5306,6 @@ export class PokemonBattleData { public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; public abilityRevealed: boolean = false; - public illusion: Illusion = { active: false, available: true }; } export class PokemonBattleSummonData { diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 35d17a3aa8da..96f7f3796fe5 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -102,7 +102,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { const pokemon = this.getPokemon(); const pokeball = this.scene.addFieldSprite( - this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball) + this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.illusion.pokeball ?? pokemon.pokeball) ); pokeball.setVisible(false); pokeball.setOrigin(0.5, 0.625); @@ -149,7 +149,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); } - addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.battleData.illusion.pokeball ?? pokemon.pokeball); + addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.illusion.pokeball ?? pokemon.pokeball); this.scene.updateModifiers(this.player); this.scene.updateFieldScale(); pokemon.showInfo(); @@ -157,7 +157,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.setVisible(true); pokemon.getSprite().setVisible(true); pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball)); + pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball)); pokemon.untint(250, "Sine.easeIn"); this.scene.updateFieldScale(); this.scene.tweens.add({ diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index 5d10553b2aa9..dda4871862d9 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -88,7 +88,7 @@ export class SwitchSummonPhase extends SummonPhase { ); this.scene.playSound("se/pb_rel"); pokemon.hideInfo(); - pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); + pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); this.scene.tweens.add({ targets: pokemon, duration: 250, diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 37c4be14331b..c4414f3b8715 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -43,9 +43,9 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; const zorua = game.scene.getEnemyPokemon()!; - expect(zoroark.battleData.illusion.active).equals(true); - expect(zorua.battleData.illusion.active).equals(true); - expect(zoroark.battleData.illusion.available).equals(false); + expect(zoroark.illusion.active).equals(true); + expect(zorua.illusion.active).equals(true); + expect(zoroark.illusion.available).equals(false); }); it("break after receiving damaging move", async () => { @@ -56,7 +56,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.battleData.illusion.active).equals(false); + expect(zorua.illusion.active).equals(false); }); it("break after getting ability changed", async () => { @@ -67,7 +67,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.battleData.illusion.active).equals(false); + expect(zorua.illusion.active).equals(false); }); it("break if the ability is suppressed", async () => { @@ -76,7 +76,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.battleData.illusion.active).equals(false); + expect(zorua.illusion.active).equals(false); }); it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { @@ -107,7 +107,7 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; - expect(zoroark.battleData.illusion.active).equals(true); + expect(zoroark.illusion.active).equals(true); }); it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { @@ -126,10 +126,11 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); const zoroark = game.scene.getPlayerPokemon()!; + console.log(zoroark.illusion) expect(zoroark.name).equals("Axew"); expect(zoroark.getNameToRender()).equals("axew nickname"); expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); expect(zoroark.isShiny(true)).equals(true); - expect(zoroark.battleData.illusion.pokeball).equals(PokeballType.GREAT_BALL); + expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); }); }); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 7b2ccf4b7b07..77fd5934b778 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -534,7 +534,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } - const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; this.genderText.setText(getGenderSymbol(gender)); this.genderText.setColor(getGenderColor(gender)); @@ -682,7 +682,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`; nameSizeTest.setText(displayName); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 4051ac04b216..c60f1048dac6 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1294,7 +1294,7 @@ class PartySlot extends Phaser.GameObjects.Container { const fusionShinyStar = this.scene.add.image(0, 0, "shiny_star_small_2"); fusionShinyStar.setOrigin(0, 0); fusionShinyStar.setPosition(shinyStar.x, shinyStar.y); - fusionShinyStar.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + fusionShinyStar.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); slotInfoContainer.add(fusionShinyStar); } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index bd9b299b7b0d..f04c70c529cf 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -324,8 +324,8 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType())); this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - this.pokemonSprite.setPipelineData("shiny", this.pokemon.battleData.illusion.shiny ?? this.pokemon.shiny); - this.pokemonSprite.setPipelineData("variant", this.pokemon.battleData.illusion.variant ?? this.pokemon.variant); + this.pokemonSprite.setPipelineData("shiny", this.pokemon.illusion.shiny ?? this.pokemon.shiny); + this.pokemonSprite.setPipelineData("variant", this.pokemon.illusion.variant ?? this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; if (this.pokemon?.summonData?.speciesForm) { @@ -396,7 +396,7 @@ export default class SummaryUiHandler extends UiHandler { this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setVisible(doubleShiny); if (isFusion) { - this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); } this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball)); From cf8f778dd3a15d320bbfa384a4a8ef9a31f284bc Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 18:00:06 +0200 Subject: [PATCH 18/24] fix nit --- src/data/ability.ts | 2 +- src/field/pokemon.ts | 8 +-- src/test/abilities/illusion.test.ts | 86 ++++++++++++++--------------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 23f926c6ad73..58a3049d29b6 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4562,7 +4562,7 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { */ applyPreSummon(pokemon: Pokemon, passive: boolean, args: any[]): boolean { let suppressed = false; - console.log("PRESUMMONILLUSION : ", pokemon.name) + console.log("PRESUMMONILLUSION : ", pokemon.name); pokemon.scene.getField(true).filter(p => p !== pokemon).map(p => { if ((p.getAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility()) || (p.getPassiveAbility().hasAttr(SuppressFieldAbilitiesAbAttr) && p.canApplyAbility(true))) { suppressed = true; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d1b27a68051f..5badd040688a 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -399,10 +399,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ generateIllusion(): boolean { if (this.hasTrainer()) { - console.log("GENERATEILLUSION() : ", this.name) + console.log("GENERATEILLUSION() : ", this.name); const party: Pokemon[] = (this.isPlayer() ? this.scene.getParty() : this.scene.getEnemyParty()).filter(p => p.isAllowedInBattle()); const lastPokemon: Pokemon = party.filter(p => p !== this).at(-1) || this; - console.log(lastPokemon.name) + console.log(lastPokemon.name); const speciesId = lastPokemon.species.speciesId; if ( lastPokemon === this || this.illusion.active || @@ -475,12 +475,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.name = randomIllusion.name; this.loadAssets(false, true).then(() => this.playAnim()); } - console.log("L'ILLUSION : ", this.illusion) + console.log("L'ILLUSION : ", this.illusion); return true; } breakIllusion(): boolean { - console.log("BREAKILLUSION") + console.log("BREAKILLUSION"); if (!this.illusion.active) { return false; } diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index c4414f3b8715..5afc96c95a41 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -5,40 +5,40 @@ import overrides from "#app/overrides"; import { Species } from "#enums/species"; import { Gender } from "../../data/gender"; import { PokeballType } from "../../data/pokeball"; -import { +import { TurnEndPhase, } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Abilities } from "#enums/abilities"; -describe("Abilities - Illusion", () => { +describe("Abilities - Illusion", () => { let phaserGame: Phaser.Game; let game: GameManager; - beforeAll(() => { - phaserGame = new Phaser.Game({ + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, - }); - }); + } ); + } ); - afterEach(() => { + afterEach(() => { game.phaseInterceptor.restoreOg(); - }); + } ); - beforeEach(() => { + beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("single"); game.override.enemySpecies(Species.ZORUA); game.override.enemyAbility(Abilities.ILLUSION); - game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); - game.override.enemyHeldItems([{name: "WIDE_LENS", count: 3}]); + game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ] ); + game.override.enemyHeldItems([ { name: "WIDE_LENS", count: 3 } ] ); - game.override.moveset([Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE]); - game.override.startingHeldItems([{name: "WIDE_LENS", count: 3}]); - }); + game.override.moveset([ Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE ] ); + game.override.startingHeldItems([ { name: "WIDE_LENS", count: 3 } ] ); + } ); - it("creates illusion at the start", async () => { - await game.startBattle([Species.ZOROARK, Species.AXEW]); + it("creates illusion at the start", async () => { + await game.startBattle([ Species.ZOROARK, Species.AXEW ] ); const zoroark = game.scene.getPlayerPokemon()!; const zorua = game.scene.getEnemyPokemon()!; @@ -46,10 +46,10 @@ describe("Abilities - Illusion", () => { expect(zoroark.illusion.active).equals(true); expect(zorua.illusion.active).equals(true); expect(zoroark.illusion.available).equals(false); - }); + } ); - it("break after receiving damaging move", async () => { - await game.startBattle([Species.AXEW]); + it("break after receiving damaging move", async () => { + await game.startBattle([ Species.AXEW ] ); game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); @@ -57,10 +57,10 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - }); + } ); - it("break after getting ability changed", async () => { - await game.startBattle([Species.AXEW]); + it("break after getting ability changed", async () => { + await game.startBattle([ Species.AXEW ] ); game.move.select(Moves.WORRY_SEED); await game.phaseInterceptor.to(TurnEndPhase); @@ -68,38 +68,38 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - }); + } ); - it("break if the ability is suppressed", async () => { + it("break if the ability is suppressed", async () => { game.override.enemyAbility(Abilities.NEUTRALIZING_GAS); - await game.startBattle([Species.KOFFING]); + await game.startBattle([ Species.KOFFING ] ); const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - }); + } ); - it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { - game.override.enemyMoveset([Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE]); - await game.startBattle([Species.ZOROARK, Species.AXEW]); + it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { + game.override.enemyMoveset([ Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE ] ); + await game.startBattle([ Species.ZOROARK, Species.AXEW ] ); const enemy = game.scene.getEnemyPokemon()!; const zoroark = game.scene.getPlayerPokemon()!; - const flameThrower = enemy.getMoveset()[0]!.getMove(); - const psychic = enemy.getMoveset()[1]!.getMove(); + const flameThrower = enemy.getMoveset()[ 0] !.getMove(); + const psychic = enemy.getMoveset()[ 1] !.getMove(); const flameThrowerEffectiveness = zoroark.getAttackTypeEffectiveness(flameThrower.type, enemy, undefined, undefined, true); const psychicEffectiveness = zoroark.getAttackTypeEffectiveness(psychic.type, enemy, undefined, undefined, true); expect(psychicEffectiveness).above(flameThrowerEffectiveness); - }); + } ); - it("do not breaks if the pokemon takes indirect damages", async () => { + it("do not breaks if the pokemon takes indirect damages", async () => { game.override.enemySpecies(Species.GIGALITH); game.override.enemyAbility(Abilities.SAND_STREAM); - game.override.enemyMoveset([Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP]); - game.override.moveset([Moves.FLARE_BLITZ]); + game.override.enemyMoveset([ Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP ] ); + game.override.moveset([ Moves.FLARE_BLITZ ] ); - await game.startBattle([Species.ZOROARK, Species.AZUMARILL]); + await game.startBattle([ Species.ZOROARK, Species.AZUMARILL ] ); game.move.select(Moves.FLARE_BLITZ); @@ -108,12 +108,12 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; expect(zoroark.illusion.active).equals(true); - }); + } ); - it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE]); + it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE ] ); - await game.startBattle([Species.ABRA, Species.ZOROARK, Species.AXEW]); + await game.startBattle([ Species.ABRA, Species.ZOROARK, Species.AXEW ] ); const axew = game.scene.getParty().at(2)!; axew.shiny = true; @@ -126,11 +126,11 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); const zoroark = game.scene.getPlayerPokemon()!; - console.log(zoroark.illusion) + console.log(zoroark.illusion); expect(zoroark.name).equals("Axew"); expect(zoroark.getNameToRender()).equals("axew nickname"); expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); expect(zoroark.isShiny(true)).equals(true); expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); - }); -}); + } ); +} ); From 53fcc101459e8060a839cf81e578950fb4dd203a Mon Sep 17 00:00:00 2001 From: Lylian Date: Thu, 10 Oct 2024 18:02:31 +0200 Subject: [PATCH 19/24] fix nit --- src/test/abilities/illusion.test.ts | 84 ++++++++++++++--------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 5afc96c95a41..86a654705cec 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -5,40 +5,40 @@ import overrides from "#app/overrides"; import { Species } from "#enums/species"; import { Gender } from "../../data/gender"; import { PokeballType } from "../../data/pokeball"; -import { +import { TurnEndPhase, } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Abilities } from "#enums/abilities"; -describe("Abilities - Illusion", () => { +describe("Abilities - Illusion", () => { let phaserGame: Phaser.Game; let game: GameManager; - beforeAll(() => { - phaserGame = new Phaser.Game({ + beforeAll(() => { + phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, - } ); - } ); + }); + }); - afterEach(() => { + afterEach(() => { game.phaseInterceptor.restoreOg(); - } ); + }); - beforeEach(() => { + beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("single"); game.override.enemySpecies(Species.ZORUA); game.override.enemyAbility(Abilities.ILLUSION); - game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ] ); - game.override.enemyHeldItems([ { name: "WIDE_LENS", count: 3 } ] ); + game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]); + game.override.enemyHeldItems([{ name: "WIDE_LENS", count: 3 }]); - game.override.moveset([ Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE ] ); - game.override.startingHeldItems([ { name: "WIDE_LENS", count: 3 } ] ); - } ); + game.override.moveset([ Moves.WORRY_SEED, Moves.SOAK, Moves.TACKLE, Moves.TACKLE ]); + game.override.startingHeldItems([{ name: "WIDE_LENS", count: 3 }]); + }); - it("creates illusion at the start", async () => { - await game.startBattle([ Species.ZOROARK, Species.AXEW ] ); + it("creates illusion at the start", async () => { + await game.startBattle([ Species.ZOROARK, Species.AXEW ]); const zoroark = game.scene.getPlayerPokemon()!; const zorua = game.scene.getEnemyPokemon()!; @@ -46,10 +46,10 @@ describe("Abilities - Illusion", () => { expect(zoroark.illusion.active).equals(true); expect(zorua.illusion.active).equals(true); expect(zoroark.illusion.available).equals(false); - } ); + }); - it("break after receiving damaging move", async () => { - await game.startBattle([ Species.AXEW ] ); + it("break after receiving damaging move", async () => { + await game.startBattle([ Species.AXEW ]); game.move.select(Moves.TACKLE); await game.phaseInterceptor.to(TurnEndPhase); @@ -57,10 +57,10 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - } ); + }); - it("break after getting ability changed", async () => { - await game.startBattle([ Species.AXEW ] ); + it("break after getting ability changed", async () => { + await game.startBattle([ Species.AXEW ]); game.move.select(Moves.WORRY_SEED); await game.phaseInterceptor.to(TurnEndPhase); @@ -68,38 +68,38 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - } ); + }); - it("break if the ability is suppressed", async () => { + it("break if the ability is suppressed", async () => { game.override.enemyAbility(Abilities.NEUTRALIZING_GAS); - await game.startBattle([ Species.KOFFING ] ); + await game.startBattle([ Species.KOFFING ]); const zorua = game.scene.getEnemyPokemon()!; expect(zorua.illusion.active).equals(false); - } ); + }); - it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { - game.override.enemyMoveset([ Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE ] ); - await game.startBattle([ Species.ZOROARK, Species.AXEW ] ); + it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { + game.override.enemyMoveset([ Moves.FLAMETHROWER, Moves.PSYCHIC, Moves.TACKLE, Moves.TACKLE ]); + await game.startBattle([ Species.ZOROARK, Species.AXEW ]); const enemy = game.scene.getEnemyPokemon()!; const zoroark = game.scene.getPlayerPokemon()!; - const flameThrower = enemy.getMoveset()[ 0] !.getMove(); - const psychic = enemy.getMoveset()[ 1] !.getMove(); + const flameThrower = enemy.getMoveset()[0]!.getMove(); + const psychic = enemy.getMoveset()[1]!.getMove(); const flameThrowerEffectiveness = zoroark.getAttackTypeEffectiveness(flameThrower.type, enemy, undefined, undefined, true); const psychicEffectiveness = zoroark.getAttackTypeEffectiveness(psychic.type, enemy, undefined, undefined, true); expect(psychicEffectiveness).above(flameThrowerEffectiveness); - } ); + }); - it("do not breaks if the pokemon takes indirect damages", async () => { + it("do not breaks if the pokemon takes indirect damages", async () => { game.override.enemySpecies(Species.GIGALITH); game.override.enemyAbility(Abilities.SAND_STREAM); - game.override.enemyMoveset([ Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP ] ); - game.override.moveset([ Moves.FLARE_BLITZ ] ); + game.override.enemyMoveset([ Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP, Moves.WILL_O_WISP ]); + game.override.moveset([ Moves.FLARE_BLITZ ]); - await game.startBattle([ Species.ZOROARK, Species.AZUMARILL ] ); + await game.startBattle([ Species.ZOROARK, Species.AZUMARILL ]); game.move.select(Moves.FLARE_BLITZ); @@ -108,12 +108,12 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; expect(zoroark.illusion.active).equals(true); - } ); + }); - it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { - vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE ] ); + it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([ Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE, Moves.SCARY_FACE ]); - await game.startBattle([ Species.ABRA, Species.ZOROARK, Species.AXEW ] ); + await game.startBattle([ Species.ABRA, Species.ZOROARK, Species.AXEW ]); const axew = game.scene.getParty().at(2)!; axew.shiny = true; @@ -132,5 +132,5 @@ describe("Abilities - Illusion", () => { expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); expect(zoroark.isShiny(true)).equals(true); expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); - } ); -} ); + }); +}); From d7ed17b066938deeafab63329ec3c40fd439b5a9 Mon Sep 17 00:00:00 2001 From: Lylian Date: Fri, 11 Oct 2024 19:58:37 +0200 Subject: [PATCH 20/24] Zoroark change illusion after lastPokemon update --- src/data/ability.ts | 12 ++-- src/field/pokemon.ts | 101 ++++++++++++++-------------- src/phases/summon-phase.ts | 6 +- src/phases/switch-summon-phase.ts | 2 +- src/test/abilities/illusion.test.ts | 18 ++--- src/ui/battle-info.ts | 4 +- src/ui/party-ui-handler.ts | 2 +- src/ui/summary-ui-handler.ts | 6 +- 8 files changed, 74 insertions(+), 77 deletions(-) diff --git a/src/data/ability.ts b/src/data/ability.ts index 0e9dc4566391..c4e473bcb57c 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -2436,7 +2436,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; } - if (target.illusion.active) { + if (target.battleData.illusion.active) { return false; } pokemon.summonData.speciesForm = target.getSpeciesForm(); @@ -4577,7 +4577,7 @@ export class IllusionPreSummonAbAttr extends PreSummonAbAttr { } }); - if (pokemon.illusion.available && !suppressed) { + if (pokemon.battleData.illusion.available && !suppressed) { return pokemon.generateIllusion(); } else { return false; @@ -4619,7 +4619,7 @@ export class IllusionPostBattleAbAttr extends PostBattleAbAttr { */ applyPostBattle(pokemon: Pokemon, passive: boolean, simulated:boolean, args: any[]): boolean { pokemon.breakIllusion(); - pokemon.illusion.available = true; + pokemon.battleData.illusion.available = true; return true; } } @@ -4635,7 +4635,7 @@ export class IllusionDisableAbAttr extends PostSummonAbAttr { * @returns {boolean} */ applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean { - pokemon.illusion.available = false; + pokemon.battleData.illusion.available = false; return true; } } @@ -5421,9 +5421,9 @@ export function initAbilities() { .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) //The pokemon generate an illusion if it's available - .conditionalAttr((pokemon) => pokemon.illusion.available, IllusionPreSummonAbAttr, false) + .conditionalAttr((pokemon) => pokemon.battleData.illusion.available, IllusionPreSummonAbAttr, false) //The pokemon loses his illusion when he is damaged by a move - .conditionalAttr((pokemon) => pokemon.illusion.active, IllusionBreakAbAttr, true) + .conditionalAttr((pokemon) => pokemon.battleData.illusion.active, IllusionBreakAbAttr, true) //Illusion is available again after a battle .conditionalAttr((pokemon) => pokemon.isAllowedInBattle(), IllusionPostBattleAbAttr, false) //Illusion is not available after summon diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 30a99db485b0..8e84cd36d507 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -123,7 +123,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public battleSummonData: PokemonBattleSummonData; public turnData: PokemonTurnData; public mysteryEncounterPokemonData: MysteryEncounterPokemonData; - public illusion: Illusion; /** Used by Mystery Encounters to execute pokemon-specific logic (such as stat boosts) at start of battle */ public mysteryEncounterBattleEffects?: (pokemon: Pokemon) => void; @@ -154,7 +153,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.pokeball = dataSource?.pokeball || PokeballType.POKEBALL; this.level = level; this.switchOutStatus = false; - this.illusion = { active: false, available: true }; // Determine the ability index if (abilityIndex !== undefined) { @@ -283,8 +281,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.illusion.active) ? this.illusion.name! : this.name; - const nickname: string = (!useIllusion && this.illusion.active) ? this.illusion.nickname! : this.nickname; + const name: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.name! : this.name; + const nickname: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.nickname! : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -298,7 +296,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { init(): void { this.fieldPosition = FieldPosition.CENTER; - this.resetBattleData(); this.initBattleInfo(); @@ -399,18 +396,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { */ generateIllusion(): boolean { if (this.hasTrainer()) { - console.log("GENERATEILLUSION() : ", this.name); const party: Pokemon[] = (this.isPlayer() ? this.scene.getParty() : this.scene.getEnemyParty()).filter(p => p.isAllowedInBattle()); const lastPokemon: Pokemon = party.filter(p => p !== this).at(-1) || this; - console.log(lastPokemon.name); const speciesId = lastPokemon.species.speciesId; - if ( lastPokemon === this || this.illusion.active || + if ( lastPokemon === this || this.battleData.illusion.active || ((speciesId === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized() || this.isTerastallized()))) { return false; } - this.illusion = { + this.battleData.illusion = { active: true, available: true, name: this.name, @@ -461,7 +456,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } const randomIllusion: PokemonSpecies = getPokemonSpecies(availables[this.randSeedInt(availables.length)]); - this.illusion = { + this.battleData.illusion = { active: true, available: true, species: randomIllusion, @@ -475,23 +470,21 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.name = randomIllusion.name; this.loadAssets(false, true).then(() => this.playAnim()); } - console.log("L'ILLUSION : ", this.illusion); return true; } breakIllusion(): boolean { - console.log("BREAKILLUSION"); - if (!this.illusion.active) { + if (!this.battleData?.illusion.active) { return false; } - this.name = this.illusion.name!; - this.nickname = this.illusion.nickname!; - this.shiny = this.illusion.shiny!; - this.variant = this.illusion.variant!; - this.fusionVariant = this.illusion.fusionVariant!; - this.fusionShiny = this.illusion.fusionShiny!; - this.illusion = { active: false, available: false }; + this.name = this.battleData.illusion.name!; + this.nickname = this.battleData.illusion.nickname!; + this.shiny = this.battleData.illusion.shiny!; + this.variant = this.battleData.illusion.variant!; + this.fusionVariant = this.battleData.illusion.fusionVariant!; + this.fusionShiny = this.battleData.illusion.fusionShiny!; + this.battleData.illusion = { active: false, available: false }; if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); } @@ -520,15 +513,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); - const formIndex = this.illusion.active && useIllusion ? this.illusion.formIndex : this.formIndex; + const formIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.formIndex : this.formIndex; this.getSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), this.getVariant(useIllusion)); if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { this.scene.loadPokemonAtlas(this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride)); } if (this.getFusionSpeciesForm(false, useIllusion)) { - const fusionFormIndex = this.illusion.active && useIllusion ? this.illusion.fusionFormIndex : this.fusionFormIndex; - const fusionShiny = this.illusion.active && !useIllusion ? this.illusion.fusionShiny : this.fusionShiny; - const fusionVariant = this.illusion.active && !useIllusion ? this.illusion.fusionVariant : this.fusionVariant; + const fusionFormIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionShiny : this.fusionShiny; + const fusionVariant = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionVariant : this.fusionVariant; this.getFusionSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getFusionGender(false, useIllusion) === Gender.FEMALE, fusionFormIndex, fusionShiny, fusionVariant); this.scene.loadPokemonAtlas(this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride)); } @@ -629,7 +622,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } @@ -638,7 +631,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } @@ -647,8 +640,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, - this.illusion.shiny ?? this.shiny, - this.illusion.variant ?? this.variant + this.battleData.illusion.shiny ?? this.shiny, + this.battleData.illusion.variant ?? this.variant ); } @@ -657,7 +650,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -666,7 +659,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } @@ -680,7 +673,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } @@ -689,12 +682,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconId(ignoreOverride?: boolean): string { - const formIndex: integer = this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -702,9 +695,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const species: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.species! : this.species; + const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; - const formIndex: integer = useIllusion && this.illusion.active ? this.illusion.formIndex! : this.formIndex; + const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; @@ -720,8 +713,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.illusion.active ? this.illusion.fusionSpecies! : this.fusionSpecies!; - const fusionFormIndex: integer = useIllusion && this.illusion.active ? this.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionSpecies: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; @@ -1217,8 +1210,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.illusion.active) { - return this.illusion.gender!; + if (useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } @@ -1229,8 +1222,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionGender of the illusion or not. */ getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.illusion.active) { - return this.illusion.fusionGender!; + if (useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.fusionGender!; } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } @@ -1238,24 +1231,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.illusion.active) { - return this.illusion.shiny || (!!this.illusion.fusionSpecies && this.illusion.fusionShiny) || false; + if (!useIllusion && this.battleData.illusion.active) { + return this.battleData.illusion.shiny || (!!this.battleData.illusion.fusionSpecies && this.battleData.illusion.fusionShiny) || false; } else { return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } } isDoubleShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.illusion.active) { - return this.isFusion(false) && this.illusion.shiny! && this.illusion.fusionShiny!; + if (!useIllusion && this.battleData.illusion.active) { + return this.isFusion(false) && this.battleData.illusion.shiny! && this.battleData.illusion.fusionShiny!; } else { return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } } getVariant(useIllusion: boolean = false): Variant { - if (!useIllusion && this.illusion.active) { - return !this.isFusion(false) ? this.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; + if (!useIllusion && this.battleData.illusion.active) { + return !this.isFusion(false) ? this.battleData.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; } else { return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; } @@ -1264,7 +1257,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { - return this.illusion.active ? this.illusion.variant! : this.variant; + return this.battleData.illusion.active ? this.battleData.illusion.variant! : this.variant; } else { return this.getVariant(); } @@ -1275,15 +1268,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isFusion(useIllusion: boolean = false): boolean { - if (useIllusion && this.illusion.active) { - return !!this.illusion.fusionSpecies; + if (useIllusion && this.battleData.illusion.active) { + return !!this.battleData.illusion.fusionSpecies; } else { return !!this.fusionSpecies; } } getName(illusion: boolean = false): string { - return (!illusion && this.illusion.active && this.illusion.name) ? this.illusion.name : this.name; + return (!illusion && this.battleData.illusion.active && this.battleData.illusion.name) ? this.battleData.illusion.name : this.name; } /** @@ -1387,7 +1380,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters this.mysteryEncounterPokemonData.types.forEach(t => types.push(t)); - } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.illusion.active || !doIllusion)) { + } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.battleData.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters @@ -3717,7 +3710,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } resetBattleData(): void { + const illusionActive: boolean = this.battleData?.illusion.active ?? false + this.breakIllusion(); this.battleData = new PokemonBattleData(); + illusionActive ? this.generateIllusion() : null } resetBattleSummonData(): void { @@ -5308,6 +5304,7 @@ export class PokemonBattleData { public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; public abilityRevealed: boolean = false; + public illusion: Illusion = {active: false, available: true} } export class PokemonBattleSummonData { diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 96f7f3796fe5..35d17a3aa8da 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -102,7 +102,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { const pokemon = this.getPokemon(); const pokeball = this.scene.addFieldSprite( - this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.illusion.pokeball ?? pokemon.pokeball) + this.player ? 36 : 248, this.player ? 80 : 44, "pb", getPokeballAtlasKey(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball) ); pokeball.setVisible(false); pokeball.setOrigin(0.5, 0.625); @@ -149,7 +149,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { } this.scene.currentBattle.seenEnemyPartyMemberIds.add(pokemon.id); } - addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.illusion.pokeball ?? pokemon.pokeball); + addPokeballOpenParticles(this.scene, pokemon.x, pokemon.y - 16, pokemon.battleData.illusion.pokeball ?? pokemon.pokeball); this.scene.updateModifiers(this.player); this.scene.updateFieldScale(); pokemon.showInfo(); @@ -157,7 +157,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.setVisible(true); pokemon.getSprite().setVisible(true); pokemon.setScale(0.5); - pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball)); + pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball)); pokemon.untint(250, "Sine.easeIn"); this.scene.updateFieldScale(); this.scene.tweens.add({ diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index dda4871862d9..5d10553b2aa9 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -88,7 +88,7 @@ export class SwitchSummonPhase extends SummonPhase { ); this.scene.playSound("se/pb_rel"); pokemon.hideInfo(); - pokemon.tint(getPokeballTintColor(pokemon.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); + pokemon.tint(getPokeballTintColor(pokemon.battleData.illusion.pokeball ?? pokemon.pokeball), 1, 250, "Sine.easeIn"); this.scene.tweens.add({ targets: pokemon, duration: 250, diff --git a/src/test/abilities/illusion.test.ts b/src/test/abilities/illusion.test.ts index 86a654705cec..81ae9ae0eb41 100644 --- a/src/test/abilities/illusion.test.ts +++ b/src/test/abilities/illusion.test.ts @@ -43,9 +43,9 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; const zorua = game.scene.getEnemyPokemon()!; - expect(zoroark.illusion.active).equals(true); - expect(zorua.illusion.active).equals(true); - expect(zoroark.illusion.available).equals(false); + expect(zoroark.battleData.illusion.active).equals(true); + expect(zorua.battleData.illusion.active).equals(true); + expect(zoroark.battleData.illusion.available).equals(false); }); it("break after receiving damaging move", async () => { @@ -56,7 +56,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("break after getting ability changed", async () => { @@ -67,7 +67,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("break if the ability is suppressed", async () => { @@ -76,7 +76,7 @@ describe("Abilities - Illusion", () => { const zorua = game.scene.getEnemyPokemon()!; - expect(zorua.illusion.active).equals(false); + expect(zorua.battleData.illusion.active).equals(false); }); it("trick the enemy AI for moves effectiveness using ILLUSION type instead of actual type", async () => { @@ -107,7 +107,7 @@ describe("Abilities - Illusion", () => { const zoroark = game.scene.getPlayerPokemon()!; - expect(zoroark.illusion.active).equals(true); + expect(zoroark.battleData.illusion.active).equals(true); }); it("copy the the name, the nickname, the gender, the shininess and the pokeball of the pokemon", async () => { @@ -126,11 +126,11 @@ describe("Abilities - Illusion", () => { await game.phaseInterceptor.to(TurnEndPhase); const zoroark = game.scene.getPlayerPokemon()!; - console.log(zoroark.illusion); + //console.log(zoroark.battleData.illusion); expect(zoroark.name).equals("Axew"); expect(zoroark.getNameToRender()).equals("axew nickname"); expect(zoroark.getGender(false, true)).equals(Gender.FEMALE); expect(zoroark.isShiny(true)).equals(true); - expect(zoroark.illusion.pokeball).equals(PokeballType.GREAT_BALL); + expect(zoroark.battleData.illusion.pokeball).equals(PokeballType.GREAT_BALL); }); }); diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index 77fd5934b778..7b2ccf4b7b07 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -534,7 +534,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { return resolve(); } - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; this.genderText.setText(getGenderSymbol(gender)); this.genderText.setColor(getGenderColor(gender)); @@ -682,7 +682,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const nameSizeTest = addTextObject(this.scene, 0, 0, displayName, TextStyle.BATTLE_INFO); nameTextWidth = nameSizeTest.displayWidth; - const gender: Gender = pokemon.illusion.active ? pokemon.illusion.gender! : pokemon.gender; + const gender: Gender = pokemon.battleData.illusion.active ? pokemon.battleData.illusion.gender! : pokemon.gender; while (nameTextWidth > (this.player || !this.boss ? 60 : 98) - ((gender !== Gender.GENDERLESS ? 6 : 0) + (pokemon.fusionSpecies ? 8 : 0) + (pokemon.isShiny() ? 8 : 0) + (Math.min(pokemon.level.toString().length, 3) - 3) * 8)) { displayName = `${displayName.slice(0, displayName.endsWith(".") ? -2 : -1).trimEnd()}.`; nameSizeTest.setText(displayName); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index c60f1048dac6..4051ac04b216 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -1294,7 +1294,7 @@ class PartySlot extends Phaser.GameObjects.Container { const fusionShinyStar = this.scene.add.image(0, 0, "shiny_star_small_2"); fusionShinyStar.setOrigin(0, 0); fusionShinyStar.setPosition(shinyStar.x, shinyStar.y); - fusionShinyStar.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + fusionShinyStar.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); slotInfoContainer.add(fusionShinyStar); } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index f04c70c529cf..bd9b299b7b0d 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -324,8 +324,8 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("teraColor", getTypeRgb(this.pokemon.getTeraType())); this.pokemonSprite.setPipelineData("ignoreTimeTint", true); this.pokemonSprite.setPipelineData("spriteKey", this.pokemon.getSpriteKey()); - this.pokemonSprite.setPipelineData("shiny", this.pokemon.illusion.shiny ?? this.pokemon.shiny); - this.pokemonSprite.setPipelineData("variant", this.pokemon.illusion.variant ?? this.pokemon.variant); + this.pokemonSprite.setPipelineData("shiny", this.pokemon.battleData.illusion.shiny ?? this.pokemon.shiny); + this.pokemonSprite.setPipelineData("variant", this.pokemon.battleData.illusion.variant ?? this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; if (this.pokemon?.summonData?.speciesForm) { @@ -396,7 +396,7 @@ export default class SummaryUiHandler extends UiHandler { this.fusionShinyIcon.setPosition(this.shinyIcon.x, this.shinyIcon.y); this.fusionShinyIcon.setVisible(doubleShiny); if (isFusion) { - this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.illusion.fusionVariant ?? this.pokemon.fusionVariant)); + this.fusionShinyIcon.setTint(getVariantTint(this.pokemon.battleData.illusion.fusionVariant ?? this.pokemon.fusionVariant)); } this.pokeball.setFrame(getPokeballAtlasKey(this.pokemon.pokeball)); From 6396943cb2f4a86ba2922eddbcb410eda5844dcb Mon Sep 17 00:00:00 2001 From: Lylian Date: Fri, 11 Oct 2024 19:59:08 +0200 Subject: [PATCH 21/24] Zoroark change illusion after lastPokemon update --- src/field/pokemon.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 8e84cd36d507..395bd225d49b 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3710,10 +3710,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } resetBattleData(): void { - const illusionActive: boolean = this.battleData?.illusion.active ?? false + const illusionActive: boolean = this.battleData?.illusion.active ?? false; this.breakIllusion(); this.battleData = new PokemonBattleData(); - illusionActive ? this.generateIllusion() : null + illusionActive ? this.generateIllusion() : null; } resetBattleSummonData(): void { @@ -5304,7 +5304,7 @@ export class PokemonBattleData { public berriesEaten: BerryType[] = []; public abilitiesApplied: Abilities[] = []; public abilityRevealed: boolean = false; - public illusion: Illusion = {active: false, available: true} + public illusion: Illusion = { active: false, available: true }; } export class PokemonBattleSummonData { From c603260364db6d7cf130d4ee868446cc8e4c2187 Mon Sep 17 00:00:00 2001 From: Lylian Date: Fri, 11 Oct 2024 21:09:02 +0200 Subject: [PATCH 22/24] refactor bug fix --- src/data/move.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/move.ts b/src/data/move.ts index 6afe981448c1..50fd0d7bd70e 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -6411,7 +6411,7 @@ export class SuppressAbilitiesIfActedAttr extends MoveEffectAttr { export class TransformAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { return new Promise(resolve => { - if (!super.apply(user, target, move, args) || target.illusion.active || user.illusion.active) { + if (!super.apply(user, target, move, args) || target.battleData.illusion.active || user.battleData.illusion.active) { user.scene.queueMessage(i18next.t("battle:attackFailed")); return resolve(false); } From 0a9337c92df59240029299f41815c7a0b62caf35 Mon Sep 17 00:00:00 2001 From: Lylian Date: Sat, 12 Oct 2024 10:48:39 +0200 Subject: [PATCH 23/24] bugfix --- src/field/pokemon.ts | 82 ++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 5983871654b0..f4ac1801861d 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -281,8 +281,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getNameToRender(useIllusion: boolean = true) { - const name: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.name! : this.name; - const nickname: string = (!useIllusion && this.battleData.illusion.active) ? this.battleData.illusion.nickname! : this.nickname; + const name: string = (!useIllusion && this.battleData?.illusion.active) ? this.battleData?.illusion.name! : this.name; + const nickname: string = (!useIllusion && this.battleData?.illusion.active) ? this.battleData?.illusion.nickname! : this.nickname; try { if (nickname) { return decodeURIComponent(escape(atob(nickname))); @@ -400,7 +400,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const lastPokemon: Pokemon = party.filter(p => p !== this).at(-1) || this; const speciesId = lastPokemon.species.speciesId; - if ( lastPokemon === this || this.battleData.illusion.active || + if ( lastPokemon === this || this.battleData?.illusion.active || ((speciesId === Species.OGERPON || speciesId === Species.TERAPAGOS) && (lastPokemon.isTerastallized() || this.isTerastallized()))) { return false; } @@ -478,12 +478,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - this.name = this.battleData.illusion.name!; - this.nickname = this.battleData.illusion.nickname!; - this.shiny = this.battleData.illusion.shiny!; - this.variant = this.battleData.illusion.variant!; - this.fusionVariant = this.battleData.illusion.fusionVariant!; - this.fusionShiny = this.battleData.illusion.fusionShiny!; + this.name = this.battleData?.illusion.name!; + this.nickname = this.battleData?.illusion.nickname!; + this.shiny = this.battleData?.illusion.shiny!; + this.variant = this.battleData?.illusion.variant!; + this.fusionVariant = this.battleData?.illusion.fusionVariant!; + this.fusionShiny = this.battleData?.illusion.fusionShiny!; this.battleData.illusion = { active: false, available: false }; if (this.isOnField()) { this.scene.playSound("PRSFX- Transform"); @@ -513,15 +513,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); - const formIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.formIndex : this.formIndex; + const formIndex = this.battleData?.illusion.active && useIllusion ? this.battleData?.illusion.formIndex : this.formIndex; this.getSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getGender(useIllusion) === Gender.FEMALE, formIndex, this.isShiny(useIllusion), this.getVariant(useIllusion)); if (this.isPlayer() || this.getFusionSpeciesForm(false, useIllusion)) { this.scene.loadPokemonAtlas(this.getBattleSpriteKey(true, ignoreOverride), this.getBattleSpriteAtlasPath(true, ignoreOverride)); } if (this.getFusionSpeciesForm(false, useIllusion)) { - const fusionFormIndex = this.battleData.illusion.active && useIllusion ? this.battleData.illusion.fusionFormIndex : this.fusionFormIndex; - const fusionShiny = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionShiny : this.fusionShiny; - const fusionVariant = this.battleData.illusion.active && !useIllusion ? this.battleData.illusion.fusionVariant : this.fusionVariant; + const fusionFormIndex = this.battleData?.illusion.active && useIllusion ? this.battleData?.illusion.fusionFormIndex : this.fusionFormIndex; + const fusionShiny = this.battleData?.illusion.active && !useIllusion ? this.battleData?.illusion.fusionShiny : this.fusionShiny; + const fusionVariant = this.battleData?.illusion.active && !useIllusion ? this.battleData?.illusion.fusionVariant : this.fusionVariant; this.getFusionSpeciesForm(false, useIllusion).loadAssets(this.scene, this.getFusionGender(false, useIllusion) === Gender.FEMALE, fusionFormIndex, fusionShiny, fusionVariant); this.scene.loadPokemonAtlas(this.getFusionBattleSpriteKey(true, ignoreOverride), this.getFusionBattleSpriteAtlasPath(true, ignoreOverride)); } @@ -622,7 +622,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getSpriteId(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } @@ -631,7 +631,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getSpriteId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant, back); } @@ -640,8 +640,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getSpeciesForm(ignoreOverride, false).getSpriteKey( this.getGender(ignoreOverride) === Gender.FEMALE, this.formIndex, - this.battleData.illusion.shiny ?? this.shiny, - this.battleData.illusion.variant ?? this.variant + this.battleData?.illusion.shiny ?? this.shiny, + this.battleData?.illusion.variant ?? this.variant ); } @@ -650,7 +650,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getFusionSpriteId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -659,7 +659,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { back = this.isPlayer(); } - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getSpriteId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant, back); } @@ -673,7 +673,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconAtlasKey(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconAtlasKey(formIndex, this.shiny, this.variant); } @@ -682,12 +682,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } getIconId(ignoreOverride?: boolean): string { - const formIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.formIndex! : this.formIndex; return this.getSpeciesForm(ignoreOverride, true).getIconId(this.getGender(ignoreOverride, true) === Gender.FEMALE, formIndex, this.shiny, this.variant); } getFusionIconId(ignoreOverride?: boolean): string { - const fusionFormIndex: integer = this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionFormIndex: integer = this.battleData?.illusion.active ? this.battleData?.illusion.fusionFormIndex! : this.fusionFormIndex; return this.getFusionSpeciesForm(ignoreOverride, true).getIconId(this.getFusionGender(ignoreOverride, true) === Gender.FEMALE, fusionFormIndex, this.fusionShiny, this.fusionVariant); } @@ -695,9 +695,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the speciesForm of the illusion or not. */ getSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const species: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.species! : this.species; + const species: PokemonSpecies = useIllusion && this.battleData?.illusion.active ? this.battleData?.illusion.species! : this.species; - const formIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.formIndex! : this.formIndex; + const formIndex: integer = useIllusion && this.battleData?.illusion.active ? this.battleData?.illusion.formIndex! : this.formIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.speciesForm; @@ -713,8 +713,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionSpeciesForm of the illusion or not. */ getFusionSpeciesForm(ignoreOverride?: boolean, useIllusion: boolean = false): PokemonSpeciesForm { - const fusionSpecies: PokemonSpecies = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionSpecies! : this.fusionSpecies!; - const fusionFormIndex: integer = useIllusion && this.battleData.illusion.active ? this.battleData.illusion.fusionFormIndex! : this.fusionFormIndex; + const fusionSpecies: PokemonSpecies = useIllusion && this.battleData?.illusion.active ? this.battleData?.illusion.fusionSpecies! : this.fusionSpecies!; + const fusionFormIndex: integer = useIllusion && this.battleData?.illusion.active ? this.battleData?.illusion.fusionFormIndex! : this.fusionFormIndex; if (!ignoreOverride && this.summonData?.speciesForm) { return this.summonData.fusionSpeciesForm; @@ -1210,8 +1210,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the gender of the illusion or not. */ getGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.gender!; + if (useIllusion && this.battleData?.illusion.active) { + return this.battleData?.illusion.gender!; } else if (!ignoreOverride && this.summonData?.gender !== undefined) { return this.summonData.gender; } @@ -1222,8 +1222,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param {boolean} useIllusion - Whether we want the fusionGender of the illusion or not. */ getFusionGender(ignoreOverride?: boolean, useIllusion: boolean = false): Gender { - if (useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.fusionGender!; + if (useIllusion && this.battleData?.illusion.active) { + return this.battleData?.illusion.fusionGender!; } else if (!ignoreOverride && this.summonData?.fusionGender !== undefined) { return this.summonData.fusionGender; } @@ -1231,24 +1231,24 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.battleData.illusion.active) { - return this.battleData.illusion.shiny || (!!this.battleData.illusion.fusionSpecies && this.battleData.illusion.fusionShiny) || false; + if (!useIllusion && this.battleData?.illusion.active) { + return this.battleData?.illusion.shiny || (!!this.battleData?.illusion.fusionSpecies && this.battleData?.illusion.fusionShiny) || false; } else { return this.shiny || (this.isFusion(useIllusion) && this.fusionShiny); } } isDoubleShiny(useIllusion: boolean = false): boolean { - if (!useIllusion && this.battleData.illusion.active) { - return this.isFusion(false) && this.battleData.illusion.shiny! && this.battleData.illusion.fusionShiny!; + if (!useIllusion && this.battleData?.illusion.active) { + return this.isFusion(false) && this.battleData?.illusion.shiny! && this.battleData?.illusion.fusionShiny!; } else { return this.isFusion(useIllusion) && this.shiny && this.fusionShiny; } } getVariant(useIllusion: boolean = false): Variant { - if (!useIllusion && this.battleData.illusion.active) { - return !this.isFusion(false) ? this.battleData.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; + if (!useIllusion && this.battleData?.illusion.active) { + return !this.isFusion(false) ? this.battleData?.illusion.variant! : Math.max(this.variant, this.fusionVariant) as Variant; } else { return !this.isFusion(true) ? this.variant : Math.max(this.variant, this.fusionVariant) as Variant; } @@ -1257,7 +1257,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { getBaseVariant(doubleShiny: boolean): Variant { if (doubleShiny) { - return this.battleData.illusion.active ? this.battleData.illusion.variant! : this.variant; + return this.battleData?.illusion.active ? this.battleData?.illusion.variant! : this.variant; } else { return this.getVariant(); } @@ -1268,15 +1268,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } isFusion(useIllusion: boolean = false): boolean { - if (useIllusion && this.battleData.illusion.active) { - return !!this.battleData.illusion.fusionSpecies; + if (useIllusion && this.battleData?.illusion.active) { + return !!this.battleData?.illusion.fusionSpecies; } else { return !!this.fusionSpecies; } } getName(illusion: boolean = false): string { - return (!illusion && this.battleData.illusion.active && this.battleData.illusion.name) ? this.battleData.illusion.name : this.name; + return (!illusion && this.battleData?.illusion.active && this.battleData?.illusion.name) ? this.battleData?.illusion.name : this.name; } /** @@ -1380,7 +1380,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters this.mysteryEncounterPokemonData.types.forEach(t => types.push(t)); - } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.battleData.illusion.active || !doIllusion)) { + } else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0 && (!this.battleData?.illusion.active || !doIllusion)) { this.summonData.types.forEach(t => types.push(t)); } else if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) { // "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters From 467ebaa2d73a8f188fd0a33ba5ce63fed50e0803 Mon Sep 17 00:00:00 2001 From: Lylian Date: Tue, 22 Oct 2024 16:46:49 +0200 Subject: [PATCH 24/24] bug fix on tests --- src/messages.ts | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/messages.ts b/src/messages.ts index 918b2bc0adab..233f5ba3f391 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -13,21 +13,21 @@ export function getPokemonNameWithAffix(pokemon: Pokemon | undefined, useIllusio } switch (pokemon.scene.currentBattle.battleSpec) { - case BattleSpec.DEFAULT: - return !pokemon.isPlayer() - ? pokemon.hasTrainer() - ? i18next.t("battle:foePokemonWithAffix", { - pokemonName: pokemon.getNameToRender(useIllusion), - }) - : i18next.t("battle:wildPokemonWithAffix", { - pokemonName: pokemon.getNameToRender(useIllusion), - }) - : pokemon.getNameToRender(useIllusion); - case BattleSpec.FINAL_BOSS: - return !pokemon.isPlayer() - ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) }) - : pokemon.getNameToRender(useIllusion); - default: - return pokemon.getNameToRender(useIllusion); + case BattleSpec.DEFAULT: + return !pokemon.isPlayer() + ? pokemon.hasTrainer() + ? i18next.t("battle:foePokemonWithAffix", { + pokemonName: pokemon.getNameToRender(useIllusion), + }) + : i18next.t("battle:wildPokemonWithAffix", { + pokemonName: pokemon.getNameToRender(useIllusion), + }) + : pokemon.getNameToRender(useIllusion); + case BattleSpec.FINAL_BOSS: + return !pokemon.isPlayer() + ? i18next.t("battle:foePokemonWithAffix", { pokemonName: pokemon.getNameToRender(useIllusion) }) + : pokemon.getNameToRender(useIllusion); + default: + return pokemon.getNameToRender(useIllusion); } }