Implement Zacian and Zamazenta transformations as species conditions (#11748)
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run

* Implement Zacian and Zamazenta transformations as species conditions

* Remove custom transformation from gen8linked
This commit is contained in:
André Bastos Dias 2026-02-18 01:34:52 +00:00 committed by GitHub
parent 42f591a330
commit f4889cac74
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 125 additions and 148 deletions

View File

@ -879,6 +879,65 @@ export const Conditions: import('../sim/dex-conditions').ConditionDataTable = {
return [type];
},
},
zacian: {
name: 'Zacian',
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedsword') return;
const rawSpecies = this.dex.species.get('Zacian-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothblade');
pokemon.baseMoveSlots[ironHeadIndex] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
zamazenta: {
name: 'Zamazenta',
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedshield') return;
const rawSpecies = this.dex.species.get('Zamazenta-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothbash');
pokemon.baseMoveSlots[ironHeadIndex] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
rolloutstorage: {
name: 'rolloutstorage',
duration: 2,

View File

@ -70,40 +70,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.add('start');
// Change Zacian/Zamazenta into their Crowned formes
for (const pokemon of this.getAllPokemon()) {
let rawSpecies: Species | null = null;
if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
rawSpecies = this.dex.species.get('Zacian-Crowned');
} else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
rawSpecies = this.dex.species.get('Zamazenta-Crowned');
}
if (!rawSpecies) continue;
const species = pokemon.setSpecies(rawSpecies);
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
};
const ironHead = pokemon.baseMoves.indexOf('ironhead');
if (ironHead >= 0) {
const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
pokemon.baseMoveSlots[ironHead] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
this.singleEvent('BattleStart', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.format.onBattleStart?.call(this);
@ -125,9 +93,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
}
for (const pokemon of this.getAllPokemon()) {
this.singleEvent('Start', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.midTurn = true;
break;
}

View File

@ -3,6 +3,64 @@ import { changeSet, getName, enemyStaff } from './scripts';
import type { ModdedConditionData } from "../../../sim/dex-conditions";
export const Conditions: { [id: IDEntry]: ModdedConditionData & { innateName?: string } } = {
zacian: {
inherit: true,
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedsword') return;
const rawSpecies = this.dex.species.get('Zacian-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothblade');
pokemon.baseMoveSlots[ironHeadIndex] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
zamazenta: {
inherit: true,
onBattleStart(pokemon) {
if (pokemon.item !== 'rustedshield') return;
const rawSpecies = this.dex.species.get('Zamazenta-Crowned');
const species = pokemon.setSpecies(rawSpecies);
if (!species) return;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get('behemothbash');
pokemon.baseMoveSlots[ironHeadIndex] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
},
},
/*
// Example:
userid: {

View File

@ -374,40 +374,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.add('start');
// Change Zacian/Zamazenta into their Crowned formes
for (const pokemon of this.getAllPokemon()) {
let rawSpecies: Species | null = null;
if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
rawSpecies = this.dex.species.get('Zacian-Crowned');
} else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
rawSpecies = this.dex.species.get('Zamazenta-Crowned');
}
if (!rawSpecies) continue;
const species = pokemon.setSpecies(rawSpecies);
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
// pokemon.setAbility(species.abilities['0'], null, null, true);
// pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
};
const ironHead = pokemon.baseMoves.indexOf('ironhead');
if (ironHead >= 0) {
const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
pokemon.baseMoveSlots[ironHead] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
this.singleEvent('BattleStart', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.format.onBattleStart?.call(this);
@ -429,9 +397,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
}
for (const pokemon of this.getAllPokemon()) {
this.singleEvent('Start', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.midTurn = true;
break;
}

View File

@ -193,40 +193,8 @@ export const Scripts: ModdedBattleScriptsData = {
this.add('start');
// Change Zacian/Zamazenta into their Crowned formes
for (const pokemon of this.getAllPokemon()) {
let rawSpecies: Species | null = null;
if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
rawSpecies = this.dex.species.get('Zacian-Crowned');
} else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
rawSpecies = this.dex.species.get('Zamazenta-Crowned');
}
if (!rawSpecies) continue;
const species = pokemon.setSpecies(rawSpecies);
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
};
const ironHead = pokemon.baseMoves.indexOf('ironhead');
if (ironHead >= 0) {
const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
pokemon.baseMoveSlots[ironHead] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
this.singleEvent('BattleStart', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.format.onBattleStart?.call(this);
@ -248,9 +216,6 @@ export const Scripts: ModdedBattleScriptsData = {
}
}
}
for (const pokemon of this.getAllPokemon()) {
this.singleEvent('Start', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.midTurn = true;
break;
}

View File

@ -2672,40 +2672,8 @@ export class Battle {
this.add('start');
// Change Zacian/Zamazenta into their Crowned formes
for (const pokemon of this.getAllPokemon()) {
let rawSpecies: Species | null = null;
if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
rawSpecies = this.dex.species.get('Zacian-Crowned');
} else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
rawSpecies = this.dex.species.get('Zamazenta-Crowned');
}
if (!rawSpecies) continue;
const species = pokemon.setSpecies(rawSpecies);
if (!species) continue;
pokemon.baseSpecies = rawSpecies;
pokemon.details = pokemon.getUpdatedDetails();
pokemon.setAbility(species.abilities['0'], null, null, true);
pokemon.baseAbility = pokemon.ability;
const behemothMove: { [k: string]: string } = {
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
};
const ironHeadIndex = pokemon.baseMoves.indexOf('ironhead');
if (ironHeadIndex >= 0) {
const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
pokemon.baseMoveSlots[ironHeadIndex] = {
move: move.name,
id: move.id,
pp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
maxpp: move.noPPBoosts ? move.pp : move.pp * 8 / 5,
target: move.target,
disabled: false,
disabledSource: '',
used: false,
};
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
}
this.singleEvent('BattleStart', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.format.onBattleStart?.call(this);
@ -2727,9 +2695,6 @@ export class Battle {
}
}
}
for (const pokemon of this.getAllPokemon()) {
this.singleEvent('Start', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
}
this.midTurn = true;
break;
}

View File

@ -639,6 +639,7 @@ export class Condition extends BasicEffect implements
declare readonly onStart?: (
this: Battle, target: Pokemon, source: Pokemon, sourceEffect: Effect
) => boolean | null | void;
declare readonly onBattleStart?: (this: Battle, pokemon: Pokemon) => void;
constructor(data: AnyObject) {
super(data);

View File

@ -100,17 +100,16 @@ describe('Team Validator', () => {
assert.legalTeam(team, 'gen7anythinggoes');
});
// Zamazenta is unreleased currently
it.skip('should tier Zacian and Zamazenta formes separately', () => {
it('should tier Zacian and Zamazenta formes separately', () => {
team = [
{ species: 'zamazenta-crowned', ability: 'dauntlessshield', item: 'rustedshield', moves: ['howl'], evs: { hp: 1 } },
];
assert.legalTeam(team, 'gen9almostanyability');
assert.false.legalTeam(team, 'gen9ou');
team = [
{ species: 'zamazenta', ability: 'dauntlessshield', item: 'lifeorb', moves: ['howl'], evs: { hp: 1 } },
];
assert.false.legalTeam(team, 'gen9almostanyability');
assert.legalTeam(team, 'gen9ou');
});
it('should validate Unown formes in Gen 2 based on DVs', () => {