From 1c9f87853b639cc6af2cb525af9e47755408e046 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Fri, 10 Jan 2025 14:09:22 +0000 Subject: [PATCH 01/13] Terapagos with other tera types --- data/abilities.ts | 7 +++++++ sim/battle-actions.ts | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/data/abilities.ts b/data/abilities.ts index 2482241efa85..18e00a7e03c4 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -4856,6 +4856,13 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { if (pokemon.baseSpecies.name !== 'Terapagos-Stellar') return; if (this.field.weather || this.field.terrain) { this.add('-ability', pokemon, 'Teraform Zero'); + if (pokemon.terastallized !== 'Stellar') { + if (this.field.weather || this.field.terrain) { + // display fail message https://www.youtube.com/watch?v=g-eUP0IrxI0 + this.hint("Teraform Zero fails to remove the weather and the terrain if Terapagos-Stellar is not Tera-Stellar."); + } + return; + } this.field.clearWeather(); this.field.clearTerrain(); } diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 22c2873ed52c..ce2efb0d7af8 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1949,7 +1949,7 @@ export class BattleActions { const tera = pokemon.species.id === 'ogerpon' ? 'tealtera' : 'tera'; pokemon.formeChange(pokemon.species.id + tera, null, true); } - if (pokemon.species.name === 'Terapagos-Terastal' && type === 'Stellar') { + if (pokemon.species.name === 'Terapagos-Terastal') { pokemon.formeChange('Terapagos-Stellar', null, true); pokemon.baseMaxhp = Math.floor(Math.floor( 2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 From ca3b30108c5325aa58c18dbc7d537efa1d36918e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Fri, 10 Jan 2025 14:48:54 +0000 Subject: [PATCH 02/13] Add test --- sim/team-validator.ts | 2 +- test/sim/misc/terapagos.js | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 9618a9b43d5a..facdb4d1f6f1 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -682,7 +682,7 @@ export class TeamValidator { set.hpType = type.name; } } - if (species.forceTeraType) { + if (species.forceTeraType && ruleTable.has('obtainablemisc')) { set.teraType = species.forceTeraType; } if (set.teraType) { diff --git a/test/sim/misc/terapagos.js b/test/sim/misc/terapagos.js index a1276f21a290..4b5e75e23465 100644 --- a/test/sim/misc/terapagos.js +++ b/test/sim/misc/terapagos.js @@ -39,4 +39,18 @@ describe(`Terapagos`, function () { const terapagos = battle.p1.active[0]; assert.species(terapagos, 'Terapagos-Terastal'); }); + + it(`[Hackmons] can Terastallize into other types, but Teraform Zero fails`, function () { + battle = common.createBattle([[ + {species: 'terapagos', ability: 'terashift', moves: ['sleeptalk'], teraType: 'Fire'}, + ], [ + {species: 'kyogre', ability: 'drizzle', moves: ['sleeptalk']}, + ]]); + const terapagos = battle.p1.active[0]; + battle.makeChoices('move sleeptalk terastallize', 'auto'); + assert.species(terapagos, 'Terapagos-Stellar'); + assert.equal(terapagos.terastallized, 'Fire'); + assert(battle.log.includes('|-ability|p1a: Terapagos|Teraform Zero'), 'Teraform Zero should activate'); + assert.equal(battle.field.weather, 'raindance', 'Teraform Zero should fail'); + }); }); From 87ed9d7deec6a78343bcae474a95c577fba39dca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Fri, 10 Jan 2025 18:51:15 +0000 Subject: [PATCH 03/13] Ogerpon with other tera types --- data/abilities.ts | 16 ++++++--- sim/battle-actions.ts | 9 +++-- sim/pokemon.ts | 14 +++++++- test/sim/misc/ogerpon.js | 76 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/data/abilities.ts b/data/abilities.ts index 18e00a7e03c4..d89b785ff8ed 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -1167,7 +1167,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, embodyaspectcornerstone: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && !this.effectState.embodied && + pokemon.terastallized + ) { this.effectState.embodied = true; this.boost({def: 1}, pokemon); } @@ -1182,7 +1184,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, embodyaspecthearthflame: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && !this.effectState.embodied && + pokemon.terastallized + ) { this.effectState.embodied = true; this.boost({atk: 1}, pokemon); } @@ -1197,7 +1201,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, embodyaspectteal: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && !this.effectState.embodied && + pokemon.terastallized + ) { this.effectState.embodied = true; this.boost({spe: 1}, pokemon); } @@ -1212,7 +1218,9 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { }, embodyaspectwellspring: { onStart(pokemon) { - if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied) { + if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && !this.effectState.embodied && + pokemon.terastallized + ) { this.effectState.embodied = true; this.boost({spd: 1}, pokemon); } diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index ce2efb0d7af8..0d0b0065711f 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1928,6 +1928,10 @@ export class BattleActions { if (pokemon.getItem().zMove || pokemon.canMegaEvo || this.dex.gen !== 9) { return null; } + if (pokemon.species.baseSpecies === 'Ogerpon' && !['Fire', 'Grass', 'Rock', 'Water'].includes(pokemon.teraType)) { + this.battle.hint("Ogerpon can only terastallize into Fire, Grass, Rock or Water types, or the game gets softlocked."); + return null; + } return pokemon.teraType; } @@ -1946,8 +1950,9 @@ export class BattleActions { pokemon.knownType = true; pokemon.apparentType = type; if (pokemon.species.baseSpecies === 'Ogerpon') { - const tera = pokemon.species.id === 'ogerpon' ? 'tealtera' : 'tera'; - pokemon.formeChange(pokemon.species.id + tera, null, true); + let ogerponSpecies = toID(pokemon.species.battleOnly || pokemon.species.id); + ogerponSpecies += ogerponSpecies === 'ogerpon' ? 'tealtera' : 'tera'; + pokemon.formeChange(ogerponSpecies, null, true); } if (pokemon.species.name === 'Terapagos-Terastal') { pokemon.formeChange('Terapagos-Stellar', null, true); diff --git a/sim/pokemon.ts b/sim/pokemon.ts index bb954c86b7d5..39dddb39e46f 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -1387,11 +1387,23 @@ export class Pokemon { this.illusion ? this.illusion.species.name : species.baseSpecies; if (isPermanent) { this.baseSpecies = rawSpecies; - this.details = species.name + (this.level === 100 ? '' : ', L' + this.level) + + let displayedSpeciesName = species.name; + if (species.baseSpecies === 'Ogerpon' && this.terastallized && this.teraType !== species.forceTeraType) { + switch (this.teraType) { + case 'Grass': displayedSpeciesName = 'Ogerpon-Teal-Tera'; break; + case 'Water': displayedSpeciesName = 'Ogerpon-Wellspring-Tera'; break; + case 'Fire': displayedSpeciesName = 'Ogerpon-Hearthflame-Tera'; break; + case 'Rock': displayedSpeciesName = 'Ogerpon-Cornerstone-Tera'; break; + } + } + this.details = displayedSpeciesName + (this.level === 100 ? '' : ', L' + this.level) + (this.gender === '' ? '' : ', ' + this.gender) + (this.set.shiny ? ', shiny' : ''); let details = (this.illusion || this).details; if (this.terastallized) details += `, tera:${this.terastallized}`; this.battle.add('detailschange', this, details); + if (species.baseSpecies === 'Ogerpon' && this.terastallized && this.teraType !== species.forceTeraType) { + this.battle.hint(`Ogerpon terastallized into ${species.name}, but it has taken the appearance of ${displayedSpeciesName} due to its tera type being ${this.teraType}.`); + } if (!source) { // Tera forme // Ogerpon/Terapagos text goes here diff --git a/test/sim/misc/ogerpon.js b/test/sim/misc/ogerpon.js index 21b7deeaeb79..7e865acda519 100644 --- a/test/sim/misc/ogerpon.js +++ b/test/sim/misc/ogerpon.js @@ -20,4 +20,80 @@ describe(`[Hackmons] Ogerpon`, function () { battle.makeChoices('switch 2', 'auto'); assert.equal(ogerpon.ability, 'embodyaspectteal', `Ogerpon's ability should be Embody Aspect after switching out`); }); + + it(`can't Terastallize into a type other than Fire, Grass, Rock or Water`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['sleeptalk'], teraType: 'Electric'}, + ], [ + {species: 'silicobra', moves: ['stealthrock']}, + ]]); + assert.throws(() => battle.makeChoices('move sleeptalk terastallize', 'auto'), "/Can't move: Ogerpon can't Terastallize\./"); + }); + + // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 + it(`can Terastallize to a type other than its mask type`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspring', ability: 'waterabsorb', moves: ['ivycudgel'], teraType: 'Rock'}, + ], [ + {species: 'seismitoad', ability: 'waterabsorb', moves: ['stealthrock']}, + ]]); + const ogerpon = battle.p1.active[0]; + battle.makeChoices('move ivycudgel terastallize', 'auto'); + assert.species(ogerpon, 'Ogerpon-Wellspring-Tera'); + assert.equal(ogerpon.ability, 'embodyaspectwellspring'); + assert.statStage(ogerpon, 'spd', 1); + assert.equal(ogerpon.getTypes().join(''), 'Rock'); + assert.fullHP(battle.p2.active[0]); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock') >= 0); + }); + + // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 + it(`Tera form can Terastallize`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['ivycudgel'], teraType: 'Water'}, + ], [ + {species: 'seismitoad', ability: 'waterabsorb', moves: ['stealthrock']}, + ]]); + const ogerpon = battle.p1.active[0]; + battle.makeChoices('move ivycudgel terastallize', 'auto'); + assert.species(ogerpon, 'Ogerpon-Wellspring-Tera'); + assert.equal(ogerpon.ability, 'embodyaspectwellspring'); + assert.statStage(ogerpon, 'spd', 1); + assert.equal(ogerpon.getTypes().join(''), 'Water'); + assert.fullHP(battle.p2.active[0]); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Wellspring-Tera, F, tera:Water') >= 0); + }); + + // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 + it(`Tera form can Terastallize to a type other than its mask type`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['ivycudgel'], teraType: 'Rock'}, + ], [ + {species: 'seismitoad', ability: 'waterabsorb', moves: ['stealthrock']}, + ]]); + const ogerpon = battle.p1.active[0]; + battle.makeChoices('move ivycudgel terastallize', 'auto'); + assert.species(ogerpon, 'Ogerpon-Wellspring-Tera'); + assert.equal(ogerpon.ability, 'embodyaspectwellspring'); + assert.statStage(ogerpon, 'spd', 1); + assert.equal(ogerpon.getTypes().join(''), 'Rock'); + assert.fullHP(battle.p2.active[0]); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock') >= 0); + }); + + // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 + it(`Embody Aspect should not activate unless the user is Terastallized`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['sleeptalk'], teraType: 'Water'}, + ], [ + {species: 'silicobra', moves: ['stealthrock']}, + ]]); + const ogerpon = battle.p1.active[0]; + battle.makeChoices(); + assert.species(ogerpon, 'Ogerpon-Wellspring-Tera'); + assert.statStage(ogerpon, 'spd', 0); + battle.makeChoices('move sleeptalk terastallize', 'auto'); + assert.species(ogerpon, 'Ogerpon-Wellspring-Tera'); + assert.statStage(ogerpon, 'spd', 1); + }); }); From fdce056c1cdf5eccfc8f12fccad7a7414b6999c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Fri, 10 Jan 2025 22:45:51 +0000 Subject: [PATCH 04/13] Small fixes --- sim/pokemon.ts | 6 +++--- test/sim/misc/ogerpon.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sim/pokemon.ts b/sim/pokemon.ts index 39dddb39e46f..d16be09130f0 100644 --- a/sim/pokemon.ts +++ b/sim/pokemon.ts @@ -1401,12 +1401,12 @@ export class Pokemon { let details = (this.illusion || this).details; if (this.terastallized) details += `, tera:${this.terastallized}`; this.battle.add('detailschange', this, details); - if (species.baseSpecies === 'Ogerpon' && this.terastallized && this.teraType !== species.forceTeraType) { - this.battle.hint(`Ogerpon terastallized into ${species.name}, but it has taken the appearance of ${displayedSpeciesName} due to its tera type being ${this.teraType}.`); - } if (!source) { // Tera forme // Ogerpon/Terapagos text goes here + if (species.baseSpecies === 'Ogerpon' && this.terastallized && this.teraType !== species.forceTeraType) { + this.battle.hint(`Ogerpon terastallized into ${species.name}, but it has taken the appearance of ${displayedSpeciesName} due to its tera type being ${this.teraType}.`); + } } else if (source.effectType === 'Item') { this.canTerastallize = null; // National Dex behavior if (source.zMove) { diff --git a/test/sim/misc/ogerpon.js b/test/sim/misc/ogerpon.js index 7e865acda519..cb3596f9b1bf 100644 --- a/test/sim/misc/ogerpon.js +++ b/test/sim/misc/ogerpon.js @@ -27,7 +27,7 @@ describe(`[Hackmons] Ogerpon`, function () { ], [ {species: 'silicobra', moves: ['stealthrock']}, ]]); - assert.throws(() => battle.makeChoices('move sleeptalk terastallize', 'auto'), "/Can't move: Ogerpon can't Terastallize\./"); + assert.throws(() => battle.makeChoices('move sleeptalk terastallize', 'auto'), "/Can't move: Ogerpon can't Terastallize./"); }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 From 2f5a0999a48d1734b8e720b58e070feb522ad74e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Sun, 12 Jan 2025 12:25:02 +0000 Subject: [PATCH 05/13] Fix Teraform Zero "Due to how overworld weather works in Generation IX, Teraform Zero will fail to protect from its effects." --- data/abilities.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/data/abilities.ts b/data/abilities.ts index 76ba9043e262..743e957b1597 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -4846,13 +4846,6 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { if (pokemon.baseSpecies.name !== 'Terapagos-Stellar') return; if (this.field.weather || this.field.terrain) { this.add('-ability', pokemon, 'Teraform Zero'); - if (pokemon.terastallized !== 'Stellar') { - if (this.field.weather || this.field.terrain) { - // display fail message https://www.youtube.com/watch?v=g-eUP0IrxI0 - this.hint("Teraform Zero fails to remove the weather and the terrain if Terapagos-Stellar is not Tera-Stellar."); - } - return; - } this.field.clearWeather(); this.field.clearTerrain(); } From 6631c967624e2efa6089f39ae09130e8bde80014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= <80102738+andrebastosdias@users.noreply.github.com> Date: Sun, 12 Jan 2025 12:27:03 +0000 Subject: [PATCH 06/13] Teraform Zero works fine --- test/sim/misc/terapagos.js | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/sim/misc/terapagos.js b/test/sim/misc/terapagos.js index 4b5e75e23465..a1276f21a290 100644 --- a/test/sim/misc/terapagos.js +++ b/test/sim/misc/terapagos.js @@ -39,18 +39,4 @@ describe(`Terapagos`, function () { const terapagos = battle.p1.active[0]; assert.species(terapagos, 'Terapagos-Terastal'); }); - - it(`[Hackmons] can Terastallize into other types, but Teraform Zero fails`, function () { - battle = common.createBattle([[ - {species: 'terapagos', ability: 'terashift', moves: ['sleeptalk'], teraType: 'Fire'}, - ], [ - {species: 'kyogre', ability: 'drizzle', moves: ['sleeptalk']}, - ]]); - const terapagos = battle.p1.active[0]; - battle.makeChoices('move sleeptalk terastallize', 'auto'); - assert.species(terapagos, 'Terapagos-Stellar'); - assert.equal(terapagos.terastallized, 'Fire'); - assert(battle.log.includes('|-ability|p1a: Terapagos|Teraform Zero'), 'Teraform Zero should activate'); - assert.equal(battle.field.weather, 'raindance', 'Teraform Zero should fail'); - }); }); From 139338d0af5c05b3af745af9304ad64a09586f81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 13 Jan 2025 07:23:28 +0000 Subject: [PATCH 07/13] Rename similar to requiredAbility/Item/Move --- data/cg-teams.ts | 6 +++--- data/mods/mixandmega/scripts.ts | 2 +- data/pokedex.ts | 22 +++++++++++----------- sim/dex-species.ts | 2 +- sim/pokemon.ts | 4 ++-- sim/team-validator.ts | 4 ++-- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/data/cg-teams.ts b/data/cg-teams.ts index 430d3dec2a09..a1747f24dffa 100644 --- a/data/cg-teams.ts +++ b/data/cg-teams.ts @@ -346,8 +346,8 @@ export default class TeamGenerator { const hasTeraBlast = moves.some(m => m.id === 'terablast'); const hasRevelationDance = moves.some(m => m.id === 'revelationdance'); let teraType; - if (species.forceTeraType) { - teraType = species.forceTeraType; + if (species.requiredTeraType) { + teraType = species.requiredTeraType; } else if (item === 'blacksludge' && this.prng.randomChance(2, 3)) { teraType = 'Poison'; } else if (hasTeraBlast && ability === 'Contrary' && this.prng.randomChance(2, 3)) { @@ -728,7 +728,7 @@ export default class TeamGenerator { // Oricorio should rarely get Tera Blast, as Revelation Dance is strictly better // Tera Blast is also bad on species with forced Tera types, a.k.a. Ogerpon and Terapagos - if (move.id === 'terablast' && (species.baseSpecies === 'Oricorio' || species.forceTeraType)) weight *= 0.5; + if (move.id === 'terablast' && (species.baseSpecies === 'Oricorio' || species.requiredTeraType)) weight *= 0.5; return weight; } diff --git a/data/mods/mixandmega/scripts.ts b/data/mods/mixandmega/scripts.ts index a687f445ba51..a5d8ddc790ca 100644 --- a/data/mods/mixandmega/scripts.ts +++ b/data/mods/mixandmega/scripts.ts @@ -436,7 +436,7 @@ export const Scripts: ModdedBattleScriptsData = { let type = pokemon.teraType; if (pokemon.species.baseSpecies !== 'Ogerpon' && pokemon.getItem().name.endsWith('Mask')) { - type = this.dex.species.get(pokemon.getItem().forcedForme).forceTeraType!; + type = this.dex.species.get(pokemon.getItem().forcedForme).requiredTeraType!; } this.battle.add('-terastallize', pokemon, type); pokemon.terastallized = type; diff --git a/data/pokedex.ts b/data/pokedex.ts index f80acd870965..286f099d5f42 100644 --- a/data/pokedex.ts +++ b/data/pokedex.ts @@ -18195,7 +18195,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], otherFormes: ["Ogerpon-Wellspring", "Ogerpon-Hearthflame", "Ogerpon-Cornerstone", "Ogerpon-Teal-Tera", "Ogerpon-Wellspring-Tera", "Ogerpon-Hearthflame-Tera", "Ogerpon-Cornerstone-Tera"], formeOrder: ["Ogerpon", "Ogerpon-Wellspring", "Ogerpon-Hearthflame", "Ogerpon-Cornerstone", "Ogerpon-Teal-Tera", "Ogerpon-Wellspring-Tera", "Ogerpon-Hearthflame-Tera", "Ogerpon-Cornerstone-Tera"], - forceTeraType: "Grass", + requiredTeraType: "Grass", }, ogerponwellspring: { num: 1017, @@ -18212,7 +18212,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Wellspring Mask", changesFrom: "Ogerpon", - forceTeraType: "Water", + requiredTeraType: "Water", }, ogerponhearthflame: { num: 1017, @@ -18229,7 +18229,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Hearthflame Mask", changesFrom: "Ogerpon", - forceTeraType: "Fire", + requiredTeraType: "Fire", }, ogerponcornerstone: { num: 1017, @@ -18246,7 +18246,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Cornerstone Mask", changesFrom: "Ogerpon", - forceTeraType: "Rock", + requiredTeraType: "Rock", }, ogerpontealtera: { num: 1017, @@ -18262,7 +18262,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { color: "Green", eggGroups: ["Undiscovered"], battleOnly: "Ogerpon", - forceTeraType: "Grass", + requiredTeraType: "Grass", }, ogerponwellspringtera: { num: 1017, @@ -18279,7 +18279,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Wellspring Mask", battleOnly: "Ogerpon-Wellspring", - forceTeraType: "Water", + requiredTeraType: "Water", }, ogerponhearthflametera: { num: 1017, @@ -18296,7 +18296,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Hearthflame Mask", battleOnly: "Ogerpon-Hearthflame", - forceTeraType: "Fire", + requiredTeraType: "Fire", }, ogerponcornerstonetera: { num: 1017, @@ -18313,7 +18313,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], requiredItem: "Cornerstone Mask", battleOnly: "Ogerpon-Cornerstone", - forceTeraType: "Rock", + requiredTeraType: "Rock", }, archaludon: { num: 1018, @@ -18404,7 +18404,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { eggGroups: ["Undiscovered"], otherFormes: ["Terapagos-Terastal", "Terapagos-Stellar"], formeOrder: ["Terapagos", "Terapagos-Terastal", "Terapagos-Stellar"], - forceTeraType: "Stellar", + requiredTeraType: "Stellar", }, terapagosterastal: { num: 1024, @@ -18419,7 +18419,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { color: "Blue", eggGroups: ["Undiscovered"], battleOnly: "Terapagos", - forceTeraType: "Stellar", + requiredTeraType: "Stellar", }, terapagosstellar: { num: 1024, @@ -18434,7 +18434,7 @@ export const Pokedex: import('../sim/dex-species').SpeciesDataTable = { color: "Blue", eggGroups: ["Undiscovered"], battleOnly: "Terapagos", - forceTeraType: "Stellar", + requiredTeraType: "Stellar", }, pecharunt: { num: 1025, diff --git a/sim/dex-species.ts b/sim/dex-species.ts index 660529d66d9e..623a00f22be3 100644 --- a/sim/dex-species.ts +++ b/sim/dex-species.ts @@ -226,7 +226,7 @@ export class Species extends BasicEffect implements Readonly Date: Mon, 13 Jan 2025 07:44:44 +0000 Subject: [PATCH 08/13] Push problems instead of forcing it --- sim/team-validator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 4538ca3f74e4..6c02c21dd179 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -683,7 +683,7 @@ export class TeamValidator { } } if (species.requiredTeraType && ruleTable.has('obtainablemisc')) { - set.teraType = species.requiredTeraType; + problems.push(`${species.name}'s Terastal type needs to be ${species.requiredTeraType}, please fix it.`); } if (set.teraType) { const type = dex.types.get(set.teraType); From 981859bc2c882130a4846f73a9b6ab3405b9223a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 13 Jan 2025 08:00:40 +0000 Subject: [PATCH 09/13] Refactor Tera type validation --- sim/team-validator.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sim/team-validator.ts b/sim/team-validator.ts index 6c02c21dd179..64c265bf2294 100644 --- a/sim/team-validator.ts +++ b/sim/team-validator.ts @@ -582,10 +582,6 @@ export class TeamValidator { name = `${set.name} (${set.species})`; } - if (!set.teraType && this.gen === 9) { - set.teraType = species.types[0]; - } - if (!set.level) set.level = ruleTable.defaultLevel; let adjustLevel = ruleTable.adjustLevel; @@ -682,13 +678,12 @@ export class TeamValidator { set.hpType = type.name; } } - if (species.requiredTeraType && ruleTable.has('obtainablemisc')) { - problems.push(`${species.name}'s Terastal type needs to be ${species.requiredTeraType}, please fix it.`); - } - if (set.teraType) { - const type = dex.types.get(set.teraType); + if (set.teraType || this.gen === 9) { + const type = dex.types.get(set.teraType || species.requiredTeraType || species.types[0]); if (!type.exists || type.isNonstandard) { problems.push(`${name}'s Terastal type (${set.teraType}) is invalid.`); + } else if (species.requiredTeraType && species.requiredTeraType !== type.name && ruleTable.has('obtainablemisc')) { + problems.push(`${species.name}'s Terastal type needs to be ${species.requiredTeraType}, please fix it.`); } else { set.teraType = type.name; } From c95835847b418789978ea6b3ed72ee0e854b89c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 13 Jan 2025 08:22:54 +0000 Subject: [PATCH 10/13] Remove hint at the beginning of the battle --- sim/battle-actions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index 0d0b0065711f..d3d9de01a224 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1929,7 +1929,6 @@ export class BattleActions { return null; } if (pokemon.species.baseSpecies === 'Ogerpon' && !['Fire', 'Grass', 'Rock', 'Water'].includes(pokemon.teraType)) { - this.battle.hint("Ogerpon can only terastallize into Fire, Grass, Rock or Water types, or the game gets softlocked."); return null; } return pokemon.teraType; From 7e7dbeb09000e4424b8e82e2bf03f6314088737a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 13 Jan 2025 08:34:06 +0000 Subject: [PATCH 11/13] Showdown-like code --- data/abilities.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/data/abilities.ts b/data/abilities.ts index 743e957b1597..6302fbc245e2 100644 --- a/data/abilities.ts +++ b/data/abilities.ts @@ -1168,8 +1168,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { embodyaspectcornerstone: { onStart(pokemon) { if (pokemon.baseSpecies.name === 'Ogerpon-Cornerstone-Tera' && pokemon.terastallized && - this.effectState.embodied !== pokemon.previouslySwitchedIn - ) { + this.effectState.embodied !== pokemon.previouslySwitchedIn) { this.effectState.embodied = pokemon.previouslySwitchedIn; this.boost({def: 1}, pokemon); } @@ -1182,8 +1181,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { embodyaspecthearthflame: { onStart(pokemon) { if (pokemon.baseSpecies.name === 'Ogerpon-Hearthflame-Tera' && pokemon.terastallized && - this.effectState.embodied !== pokemon.previouslySwitchedIn - ) { + this.effectState.embodied !== pokemon.previouslySwitchedIn) { this.effectState.embodied = pokemon.previouslySwitchedIn; this.boost({atk: 1}, pokemon); } @@ -1196,8 +1194,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { embodyaspectteal: { onStart(pokemon) { if (pokemon.baseSpecies.name === 'Ogerpon-Teal-Tera' && pokemon.terastallized && - this.effectState.embodied !== pokemon.previouslySwitchedIn - ) { + this.effectState.embodied !== pokemon.previouslySwitchedIn) { this.effectState.embodied = pokemon.previouslySwitchedIn; this.boost({spe: 1}, pokemon); } @@ -1210,8 +1207,7 @@ export const Abilities: import('../sim/dex-abilities').AbilityDataTable = { embodyaspectwellspring: { onStart(pokemon) { if (pokemon.baseSpecies.name === 'Ogerpon-Wellspring-Tera' && pokemon.terastallized && - this.effectState.embodied !== pokemon.previouslySwitchedIn - ) { + this.effectState.embodied !== pokemon.previouslySwitchedIn) { this.effectState.embodied = pokemon.previouslySwitchedIn; this.boost({spd: 1}, pokemon); } From 317d3a7c9638b76c9a1978bb0b836b4e95591ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Mon, 13 Jan 2025 08:41:37 +0000 Subject: [PATCH 12/13] Allow but fail Terastallization Similar do Desync Clause Mod --- sim/battle-actions.ts | 8 +++++--- test/sim/misc/ogerpon.js | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/sim/battle-actions.ts b/sim/battle-actions.ts index d3d9de01a224..2f6acb9635d9 100644 --- a/sim/battle-actions.ts +++ b/sim/battle-actions.ts @@ -1928,13 +1928,15 @@ export class BattleActions { if (pokemon.getItem().zMove || pokemon.canMegaEvo || this.dex.gen !== 9) { return null; } - if (pokemon.species.baseSpecies === 'Ogerpon' && !['Fire', 'Grass', 'Rock', 'Water'].includes(pokemon.teraType)) { - return null; - } return pokemon.teraType; } terastallize(pokemon: Pokemon) { + if (pokemon.species.baseSpecies === 'Ogerpon' && !['Fire', 'Grass', 'Rock', 'Water'].includes(pokemon.teraType)) { + this.battle.hint("If Ogerpon Terastallizes into a type other than Fire, Grass, Rock, or Water, the game softlocks."); + return; + } + if (pokemon.illusion && ['Ogerpon', 'Terapagos'].includes(pokemon.illusion.species.baseSpecies)) { this.battle.singleEvent('End', this.dex.abilities.get('Illusion'), pokemon.abilityState, pokemon); } diff --git a/test/sim/misc/ogerpon.js b/test/sim/misc/ogerpon.js index cb3596f9b1bf..88c4191cd4ab 100644 --- a/test/sim/misc/ogerpon.js +++ b/test/sim/misc/ogerpon.js @@ -21,17 +21,19 @@ describe(`[Hackmons] Ogerpon`, function () { assert.equal(ogerpon.ability, 'embodyaspectteal', `Ogerpon's ability should be Embody Aspect after switching out`); }); - it(`can't Terastallize into a type other than Fire, Grass, Rock or Water`, function () { + it(`won't Terastallize into a type other than Fire, Grass, Rock or Water`, function () { battle = common.gen(9).createBattle([[ {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['sleeptalk'], teraType: 'Electric'}, ], [ {species: 'silicobra', moves: ['stealthrock']}, ]]); - assert.throws(() => battle.makeChoices('move sleeptalk terastallize', 'auto'), "/Can't move: Ogerpon can't Terastallize./"); + const ogerpon = battle.p1.active[0]; + battle.makeChoices('move sleeptalk terastallize', 'auto'); + assert.false(!!ogerpon.terastallized); }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 - it(`can Terastallize to a type other than its mask type`, function () { + it(`can Terastallize into the type of another mask`, function () { battle = common.gen(9).createBattle([[ {species: 'ogerponwellspring', ability: 'waterabsorb', moves: ['ivycudgel'], teraType: 'Rock'}, ], [ @@ -65,7 +67,7 @@ describe(`[Hackmons] Ogerpon`, function () { }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 - it(`Tera form can Terastallize to a type other than its mask type`, function () { + it(`Tera form can Terastallize into the type of another mask`, function () { battle = common.gen(9).createBattle([[ {species: 'ogerponwellspringtera', ability: 'embodyaspectwellspring', moves: ['ivycudgel'], teraType: 'Rock'}, ], [ From 96926b6b175d6f016510c5afd3c8c86721b6a5ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bastos=20Dias?= Date: Tue, 14 Jan 2025 04:27:41 +0000 Subject: [PATCH 13/13] Transformed Ogerpon test --- test/sim/misc/ogerpon.js | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/test/sim/misc/ogerpon.js b/test/sim/misc/ogerpon.js index 88c4191cd4ab..e0bd5d2fab43 100644 --- a/test/sim/misc/ogerpon.js +++ b/test/sim/misc/ogerpon.js @@ -46,7 +46,7 @@ describe(`[Hackmons] Ogerpon`, function () { assert.statStage(ogerpon, 'spd', 1); assert.equal(ogerpon.getTypes().join(''), 'Rock'); assert.fullHP(battle.p2.active[0]); - assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock') >= 0); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock')); }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 @@ -63,7 +63,7 @@ describe(`[Hackmons] Ogerpon`, function () { assert.statStage(ogerpon, 'spd', 1); assert.equal(ogerpon.getTypes().join(''), 'Water'); assert.fullHP(battle.p2.active[0]); - assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Wellspring-Tera, F, tera:Water') >= 0); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Wellspring-Tera, F, tera:Water')); }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811 @@ -80,7 +80,33 @@ describe(`[Hackmons] Ogerpon`, function () { assert.statStage(ogerpon, 'spd', 1); assert.equal(ogerpon.getTypes().join(''), 'Rock'); assert.fullHP(battle.p2.active[0]); - assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock') >= 0); + assert(battle.log.includes('|detailschange|p1a: Ogerpon|Ogerpon-Cornerstone-Tera, F, tera:Rock')); + }); + + // https://www.smogon.com/forums/threads/scarlet-violet-battle-mechanics-research.3709545/post-10404934 + it(`can Terastallize into any type if transformed, but it won't change form`, function () { + battle = common.gen(9).createBattle([[ + {species: 'ogerponwellspring', ability: 'waterabsorb', moves: ['transform', 'ivycudgel'], teraType: 'Fairy'}, + {species: 'silicobra', moves: ['stealthrock']}, + ], [ + {species: 'seismitoad', ability: 'waterabsorb', moves: ['sleeptalk']}, + ]]); + const ogerpon = battle.p1.active[0]; + battle.makeChoices('move transform', 'auto'); + battle.makeChoices('move sleeptalk terastallize', 'auto'); + assert.equal(ogerpon.baseSpecies.name, 'Ogerpon-Wellspring'); + assert.species(ogerpon, 'Seismitoad'); + assert.equal(ogerpon.getTypes().join(''), 'Fairy'); + + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('switch 2', 'auto'); + battle.makeChoices('move ivycudgel', 'auto'); + assert.species(ogerpon, 'Ogerpon-Wellspring'); + assert.equal(ogerpon.ability, 'waterabsorb'); + assert.statStage(ogerpon, 'spd', 0); + assert.equal(ogerpon.getTypes().join(''), 'Fairy'); + assert.fullHP(battle.p2.active[0]); + assert.false(battle.log.includes('|detailschange|')); }); // https://www.smogon.com/forums/threads/ogerpon-teal-tera-tera-can-exist.3742851/post-10132811