From cd231efdfa7b77c6b5bb4c3f649ab6cfd5145ab2 Mon Sep 17 00:00:00 2001 From: Leonard Craft III Date: Sun, 31 Dec 2023 19:56:17 -0600 Subject: [PATCH] Implement notransform flag --- data/abilities.ts | 53 ++++++++++------------------ sim/pokemon.ts | 3 ++ test/sim/abilities/commander.js | 39 +++++++++++++++++--- test/sim/abilities/disguise.js | 13 +++++++ test/sim/abilities/iceface.js | 23 ++++++++++-- test/sim/abilities/protosynthesis.js | 13 +++++++ 6 files changed, 103 insertions(+), 41 deletions(-) diff --git a/data/abilities.ts b/data/abilities.ts index 729f943c6a72a..40ea4eeade6b1 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -592,8 +592,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onUpdate(pokemon) { if (this.gameType !== 'doubles') return; const ally = pokemon.allies()[0]; - if (!ally || pokemon.transformed || - pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') { + if (!ally || pokemon.baseSpecies.baseSpecies !== 'Tatsugiri' || ally.baseSpecies.baseSpecies !== 'Dondozo') { // Handle any edge cases if (pokemon.getVolatile('commanding')) pokemon.removeVolatile('commanding'); return; @@ -955,10 +954,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { disguise: { onDamagePriority: 1, onDamage(damage, target, source, effect) { - if ( - effect && effect.effectType === 'Move' && - ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed - ) { + if (effect && effect.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id)) { this.add('-activate', target, 'ability: Disguise'); this.effectState.busted = true; return 0; @@ -966,7 +962,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onCriticalHit(target, source, move) { if (!target) return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed) { + if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) { return; } const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6); @@ -977,7 +973,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onEffectiveness(typeMod, target, type, move) { if (!target || move.category === 'Status') return; - if (!['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed) { + if (!['mimikyu', 'mimikyutotem'].includes(target.species.id)) { return; } @@ -1155,7 +1151,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, embodyaspectcornerstone: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({def: 1}, pokemon); } @@ -1170,7 +1166,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, embodyaspecthearthflame: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({atk: 1}, pokemon); } @@ -1185,7 +1181,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, embodyaspectteal: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({spe: 1}, pokemon); } @@ -1200,7 +1196,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, embodyaspectwellspring: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !pokemon.transformed && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied) { this.effectState.embodied = true; this.boost({spd: 1}, pokemon); } @@ -1686,7 +1682,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, gulpmissile: { onDamagingHit(damage, target, source, move) { - if (!source.hp || !source.isActive || target.transformed || target.isSemiInvulnerable()) return; + if (!source.hp || !source.isActive || target.isSemiInvulnerable()) return; if (['cramorantgulping', 'cramorantgorging'].includes(target.species.id)) { this.damage(source.baseMaxhp / 4, source, target); if (target.species.id === 'cramorantgulping') { @@ -1699,10 +1695,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, // The Dive part of this mechanic is implemented in Dive's `onTryMove` in moves.ts onSourceTryPrimaryHit(target, source, effect) { - if ( - effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && - source.species.name === 'Cramorant' && !source.transformed - ) { + if (effect && effect.id === 'surf' && source.hasAbility('gulpmissile') && source.species.name === 'Cramorant') { const forme = source.hp <= source.maxhp / 2 ? 'cramorantgorging' : 'cramorantgulping'; source.formeChange(forme, effect); } @@ -1840,7 +1833,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { hungerswitch: { onResidualOrder: 29, onResidual(pokemon) { - if (pokemon.species.baseSpecies !== 'Morpeko' || pokemon.transformed || pokemon.terastallized) return; + if (pokemon.species.baseSpecies !== 'Morpeko' || pokemon.terastallized) return; const targetForme = pokemon.species.name === 'Morpeko' ? 'Morpeko-Hangry' : 'Morpeko'; pokemon.formeChange(targetForme); }, @@ -1912,8 +1905,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, iceface: { onStart(pokemon) { - if (this.field.isWeather(['hail', 'snow']) && - pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') { this.add('-activate', pokemon, 'ability: Ice Face'); this.effectState.busted = false; pokemon.formeChange('Eiscue', this.effect, true); @@ -1921,10 +1913,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onDamagePriority: 1, onDamage(damage, target, source, effect) { - if ( - effect && effect.effectType === 'Move' && effect.category === 'Physical' && - target.species.id === 'eiscue' && !target.transformed - ) { + if (effect && effect.effectType === 'Move' && effect.category === 'Physical' && target.species.id === 'eiscue') { this.add('-activate', target, 'ability: Ice Face'); this.effectState.busted = true; return 0; @@ -1932,14 +1921,14 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, onCriticalHit(target, type, move) { if (!target) return; - if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed) return; + if (move.category !== 'Physical' || target.species.id !== 'eiscue') return; if (target.volatiles['substitute'] && !(move.flags['bypasssub'] || move.infiltrates)) return; if (!target.runImmunity(move.type)) return; return false; }, onEffectiveness(typeMod, target, type, move) { if (!target) return; - if (move.category !== 'Physical' || target.species.id !== 'eiscue' || target.transformed) return; + if (move.category !== 'Physical' || target.species.id !== 'eiscue') return; const hitSub = target.volatiles['substitute'] && !move.flags['bypasssub'] && !(move.infiltrates && this.gen >= 6); if (hitSub) return; @@ -1956,8 +1945,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { // snow/hail resuming because Cloud Nine/Air Lock ended does not trigger Ice Face if ((sourceEffect as Ability)?.suppressWeather) return; if (!pokemon.hp) return; - if (this.field.isWeather(['hail', 'snow']) && - pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + if (this.field.isWeather(['hail', 'snow']) && pokemon.species.id === 'eiscuenoice') { this.add('-activate', pokemon, 'ability: Ice Face'); this.effectState.busted = false; pokemon.formeChange('Eiscue', this.effect, true); @@ -2846,7 +2834,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { neutralizinggas: { // Ability suppression implemented in sim/pokemon.ts:Pokemon#ignoringAbility onPreStart(pokemon) { - if (pokemon.transformed) return; this.add('-ability', pokemon, 'Neutralizing Gas'); pokemon.abilityState.ending = false; const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; @@ -3433,7 +3420,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.singleEvent('WeatherChange', this.effect, this.effectState, pokemon); }, onWeatherChange(pokemon) { - if (pokemon.transformed) return; // Protosynthesis is not affected by Utility Umbrella if (this.field.isWeather('sunnyday')) { pokemon.addVolatile('protosynthesis'); @@ -3570,7 +3556,6 @@ export const Abilities: {[abilityid: string]: AbilityData} = { this.singleEvent('TerrainChange', this.effect, this.effectState, pokemon); }, onTerrainChange(pokemon) { - if (pokemon.transformed) return; if (this.field.isTerrain('electricterrain')) { pokemon.addVolatile('quarkdrive'); } else if (!pokemon.volatiles['quarkdrive']?.fromBooster) { @@ -4883,7 +4868,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, terashift: { onPreStart(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Terapagos' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Terapagos') return; if (pokemon.species.forme !== 'Terastal') { this.add('-activate', pokemon, 'ability: Tera Shift'); pokemon.formeChange('Terapagos-Terastal', this.effect, true); @@ -5557,7 +5542,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { }, zerotohero: { onSwitchOut(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Palafin' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return; if (pokemon.species.forme !== 'Hero') { pokemon.formeChange('Palafin-Hero', this.effect, true); } @@ -5568,7 +5553,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = { onStart(pokemon) { if (!this.effectState.switchingIn) return; this.effectState.switchingIn = false; - if (pokemon.baseSpecies.baseSpecies !== 'Palafin' || pokemon.transformed) return; + if (pokemon.baseSpecies.baseSpecies !== 'Palafin') return; if (!this.effectState.heroMessageDisplayed && pokemon.species.forme === 'Hero') { this.add('-activate', pokemon, 'ability: Zero to Hero'); this.effectState.heroMessageDisplayed = true; diff --git a/sim/pokemon.ts b/sim/pokemon.ts index 466266cad456e..c289e56daed7f 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -820,6 +820,9 @@ export class Pokemon { ignoringAbility() { if (this.battle.gen >= 5 && !this.isActive) return true; + + // Certain Abilities won't activate while Transformed, even if they ordinarily couldn't be suppressed (e.g. Disguise) + if (this.getAbility().flags['notransform'] && this.transformed) return true; if (this.getAbility().flags['cantsuppress']) return false; if (this.volatiles['gastroacid']) return true; diff --git a/test/sim/abilities/commander.js b/test/sim/abilities/commander.js index 71d16f54e9b17..f9285b0754d42 100644 --- a/test/sim/abilities/commander.js +++ b/test/sim/abilities/commander.js @@ -21,7 +21,7 @@ describe('Commander', function () { assert.cantMove(() => battle.p2.choose('move swordsdance', 'move sleeptalk')); }); - it(`should not work if either Pokemon is Transformed into Dondozo/Tatsugiri`, function () { + it(`should not work if another Pokemon is Transformed into Dondozo`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'wynaut', moves: ['sleeptalk']}, {species: 'dondozo', moves: ['sleeptalk']}, @@ -35,10 +35,26 @@ describe('Commander', function () { assert.false(!!mewDondozo.volatiles['commanded']); }); - it(`should not work if Tatsugiri is Transformed, and should work if Dondozo is Transformed`, function () { + it(`should not work if another Pokemon is Transformed into Tatsugiri`, function () { battle = common.createBattle({gameType: 'doubles'}, [[ {species: 'wynaut', moves: ['sleeptalk']}, {species: 'tatsugiri', ability: 'commander', moves: ['sleeptalk']}, + ], [ + {species: 'roggenrola', moves: ['sleeptalk']}, + {species: 'sunkern', ability: 'commander', moves: ['transform']}, + {species: 'dondozo', moves: ['transform']}, + ]]); + + battle.makeChoices('auto', 'move sleeptalk, move transform 2'); + battle.makeChoices('auto', 'switch 3, move sleeptalk'); + const dondozo = battle.p2.active[0]; + assert.false(!!dondozo.volatiles['commanded'], `Transformed Sunkern into another Tatsugiri should not trigger Commander`); + }); + + it(`should work if Tatsugiri is Transformed into another Pokemon with Commander`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', moves: ['sleeptalk']}, + {species: 'sunkern', ability: 'commander', moves: ['sleeptalk']}, ], [ {species: 'roggenrola', moves: ['sleeptalk']}, {species: 'tatsugiri', ability: 'commander', moves: ['transform']}, @@ -48,11 +64,24 @@ describe('Commander', function () { battle.makeChoices('auto', 'move sleeptalk, move transform 2'); battle.makeChoices('auto', 'switch 3, move sleeptalk'); const dondozo = battle.p2.active[0]; - assert.false(!!dondozo.volatiles['commanded'], `Transformed Tatsugiri should not trigger Commander`); + assert(!!dondozo.volatiles['commanded']); + }); + + it(`should work if Dondozo is Transformed`, function () { + battle = common.createBattle({gameType: 'doubles'}, [[ + {species: 'wynaut', moves: ['sleeptalk']}, + {species: 'diglett', moves: ['sleeptalk']}, + ], [ + {species: 'dondozo', moves: ['transform']}, + {species: 'roggenrola', moves: ['sleeptalk']}, + {species: 'tatsugiri', ability: 'commander', moves: ['sleeptalk']}, + + ]]); - battle.makeChoices('auto', 'move transform 1, switch 3'); + battle.makeChoices('auto', 'move transform 2, move sleeptalk'); battle.makeChoices('auto', 'move sleeptalk, switch 3'); - assert(!!dondozo.volatiles['commanded'], `Transformed Dondozo should trigger Commander`); + const dondozo = battle.p2.active[0]; + assert(!!dondozo.volatiles['commanded']); }); it.skip(`should cause Tatsugiri to dodge all moves, including moves which normally bypass semi-invulnerability`, function () { diff --git a/test/sim/abilities/disguise.js b/test/sim/abilities/disguise.js index d4bc8fea47c9e..05f587d7564c2 100644 --- a/test/sim/abilities/disguise.js +++ b/test/sim/abilities/disguise.js @@ -99,4 +99,17 @@ describe('Disguise', function () { battle.makeChoices(); assert(battle.log.every(line => !line.startsWith('|-crit'))); }); + + it(`should not work while Transformed`, function () { + battle = common.createBattle([[ + {species: 'Mimikyu', ability: 'disguise', moves: ['transform']}, + ], [ + {species: 'Mimikyu', ability: 'disguise', moves: ['sleeptalk', 'aerialace']}, + ]]); + battle.makeChoices(); + battle.makeChoices('auto', 'move aerialace'); + const transformedMimikyu = battle.p1.active[0]; + assert.species(transformedMimikyu, 'Mimikyu', `Transformed Mimikyu should not have changed to Mimikyu-busted after taking damage`); + assert.false.fullHP(transformedMimikyu); + }); }); diff --git a/test/sim/abilities/iceface.js b/test/sim/abilities/iceface.js index fc5185aa6ffbf..d0e4fd6bf7c99 100644 --- a/test/sim/abilities/iceface.js +++ b/test/sim/abilities/iceface.js @@ -24,8 +24,27 @@ describe('Ice Face', function () { assert.hurts(eiscue, () => battle.makeChoices()); }); - it(`should not trigger if the Pokemon was KOed`, function () { - // TODO: Make compatible with gen 9 + it(`should not work while Transformed`, function () { + battle = common.createBattle([[ + {species: 'Eiscue', ability: 'iceface', moves: ['transform']}, + {species: 'Wynaut', moves: ['sleeptalk']}, + ], [ + {species: 'Eiscue', ability: 'iceface', moves: ['sleeptalk', 'aerialace', 'hail']}, + ]]); + battle.makeChoices(); + battle.makeChoices('move aerialace', 'move aerialace'); + const transformedEiscue = battle.p1.active[0]; + assert.species(transformedEiscue, 'Eiscue', `Transformed Eiscue should not have changed to Eiscue-Noice after taking physical damage`); + assert.false.fullHP(transformedEiscue); + + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('move transform', 'auto'); + battle.makeChoices('move hail', 'auto'); + assert.species(transformedEiscue, 'Eiscue-Noice', `Transformed Eiscue should not have changed to Eiscue after hail was set`); + }); + + it(`should not trigger if the Pokemon was KOed by Max Hailstorm`, function () { battle = common.gen(8).createBattle([[ {species: 'Eiscue', level: 1, ability: 'iceface', moves: ['sleeptalk']}, ], [ diff --git a/test/sim/abilities/protosynthesis.js b/test/sim/abilities/protosynthesis.js index 37ead4fe55d3f..630f8a5964d8b 100644 --- a/test/sim/abilities/protosynthesis.js +++ b/test/sim/abilities/protosynthesis.js @@ -96,4 +96,17 @@ describe('Protosynthesis', function () { battle.makeChoices('move recover', 'move venoshock'); assert.bounded(tail.maxhp - tail.hp, [84, 102]); }); + + it(`should not activate while the user is Transformed`, function () { + battle = common.createBattle([[ + {species: 'Torkoal', ability: 'drought', moves: ['sleeptalk']}, + {species: 'Ditto', ability: 'imposter', moves: ['transform']}, + ], [ + {species: 'Roaring Moon', ability: 'protosynthesis', moves: ['sleeptalk']}, + ]]); + + battle.makeChoices('switch 2', 'auto'); + const dittoMoon = battle.p1.active[0]; + assert(!dittoMoon.volatiles['protosynthesis']); + }); });