mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-04-26 02:39:38 -05:00
Mix and Mega: Expand mechanics (#9452)
* Expand Mix and Mega mechanics * more stuff * Make the ability delta the ability name * Final tweaks
This commit is contained in:
parent
57ed1353cc
commit
9a52e0d33b
|
|
@ -817,21 +817,26 @@ export const Formats: FormatList = [
|
|||
const itemTable = new Set<ID>();
|
||||
for (const set of team) {
|
||||
const item = this.dex.items.get(set.item);
|
||||
if (!item.megaStone) continue;
|
||||
if (!item.megaStone && !item.onPrimal &&
|
||||
!item.forcedForme?.endsWith('Origin') && !item.name.startsWith('Rusted')) continue;
|
||||
const natdex = this.ruleTable.has('standardnatdex');
|
||||
if (natdex && item.id !== 'ultranecroziumz') continue;
|
||||
const species = this.dex.species.get(set.species);
|
||||
if (species.isNonstandard && !this.ruleTable.has(`+pokemontag:${this.toID(species.isNonstandard)}`)) {
|
||||
return [`${species.baseSpecies} does not exist in gen 9.`];
|
||||
}
|
||||
if (natdex && species.name.startsWith('Necrozma-') && item.id === 'ultranecroziumz') {
|
||||
if ((item.itemUser?.includes(species.name) && !item.megaStone && !item.onPrimal) ||
|
||||
(natdex && species.name.startsWith('Necrozma-') && item.id === 'ultranecroziumz')) {
|
||||
continue;
|
||||
}
|
||||
if (this.ruleTable.isRestrictedSpecies(species) || this.toID(set.ability) === 'powerconstruct') {
|
||||
return [`${species.name} is not allowed to hold ${item.name}.`];
|
||||
}
|
||||
if (itemTable.has(item.id)) {
|
||||
return [`You are limited to one of each mega stone.`, `(You have more than one ${item.name})`];
|
||||
return [
|
||||
`You are limited to one of each mega stone/orb/rusted item/sinnoh item.`,
|
||||
`(You have more than one ${item.name})`,
|
||||
];
|
||||
}
|
||||
itemTable.add(item.id);
|
||||
}
|
||||
|
|
@ -843,10 +848,10 @@ export const Formats: FormatList = [
|
|||
},
|
||||
onSwitchIn(pokemon) {
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(pokemon.species.originalMega);
|
||||
if (oMegaSpecies.exists && pokemon.m.originalSpecies !== oMegaSpecies.baseSpecies) {
|
||||
const originalFormeSecies = this.dex.species.get(pokemon.species.originalSpecies);
|
||||
if (originalFormeSecies.exists && pokemon.m.originalSpecies !== originalFormeSecies.baseSpecies) {
|
||||
// Place volatiles on the Pokémon to show its mega-evolved condition and details
|
||||
this.add('-start', pokemon, oMegaSpecies.requiredItem || oMegaSpecies.requiredMove, '[silent]');
|
||||
this.add('-start', pokemon, originalFormeSecies.requiredItem || originalFormeSecies.requiredMove, '[silent]');
|
||||
const oSpecies = this.dex.species.get(pokemon.m.originalSpecies);
|
||||
if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
this.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
|
||||
|
|
@ -855,7 +860,7 @@ export const Formats: FormatList = [
|
|||
},
|
||||
onSwitchOut(pokemon) {
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(pokemon.species.originalMega);
|
||||
const oMegaSpecies = this.dex.species.get(pokemon.species.originalSpecies);
|
||||
if (oMegaSpecies.exists && pokemon.m.originalSpecies !== oMegaSpecies.baseSpecies) {
|
||||
this.add('-end', pokemon, oMegaSpecies.requiredItem || oMegaSpecies.requiredMove, '[silent]');
|
||||
}
|
||||
|
|
@ -1649,74 +1654,6 @@ export const Formats: FormatList = [
|
|||
section: "Retro Other Metagames",
|
||||
column: 2,
|
||||
},
|
||||
{
|
||||
name: "[Gen 7] Mix and Mega",
|
||||
desc: `Mega Stones and Primal Orbs can be used on almost any Pokémon with no Mega Evolution limit.`,
|
||||
threads: [
|
||||
`• <a href="https://www.smogon.com/forums/posts/8778656/">USM Mix and Mega</a>`,
|
||||
],
|
||||
|
||||
mod: 'gen7mixandmega',
|
||||
ruleset: ['Standard OMs', 'Mega Rayquaza Clause', 'Sleep Clause Mod'],
|
||||
banlist: ['Shadow Tag', 'Gengarite', 'Baton Pass', 'Electrify'],
|
||||
restricted: [
|
||||
'Arceus', 'Deoxys', 'Dialga', 'Dragonite', 'Giratina', 'Groudon', 'Ho-Oh', 'Kyogre', 'Kyurem', 'Landorus-Therian', 'Lugia',
|
||||
'Lunala', 'Marshadow', 'Mewtwo', 'Naganadel', 'Necrozma', 'Palkia', 'Pheromosa', 'Rayquaza', 'Regigigas', 'Reshiram', 'Shuckle',
|
||||
'Slaking', 'Solgaleo', 'Xerneas', 'Yveltal', 'Zekrom',
|
||||
'Beedrillite', 'Blazikenite', 'Kangaskhanite', 'Mawilite', 'Medichamite', 'Pidgeotite', 'Ultranecrozium Z',
|
||||
],
|
||||
unbanlist: ['Deoxys-Defense', 'Kyurem-Base', 'Necrozma-Base'],
|
||||
onValidateTeam(team) {
|
||||
const itemTable = new Set<ID>();
|
||||
for (const set of team) {
|
||||
const item = this.dex.items.get(set.item);
|
||||
if (!item.exists) continue;
|
||||
if (itemTable.has(item.id) && (item.megaStone || item.onPrimal)) {
|
||||
return [
|
||||
`You are limited to one of each Mega Stone and Primal Orb.`,
|
||||
`(You have more than one ${item.name}.)`,
|
||||
];
|
||||
}
|
||||
itemTable.add(item.id);
|
||||
}
|
||||
},
|
||||
onValidateSet(set) {
|
||||
const species = this.dex.species.get(set.species);
|
||||
const item = this.dex.items.get(set.item);
|
||||
if (!item.megaEvolves && !item.onPrimal && item.id !== 'ultranecroziumz') return;
|
||||
if (species.baseSpecies === item.megaEvolves || (item.onPrimal && item.itemUser?.includes(species.baseSpecies)) ||
|
||||
(species.name.startsWith('Necrozma-') && item.id === 'ultranecroziumz')) {
|
||||
return;
|
||||
}
|
||||
if (this.ruleTable.isRestricted(`item:${item.id}`) || this.ruleTable.isRestrictedSpecies(species) ||
|
||||
set.ability === 'Power Construct') {
|
||||
return [`${set.species} is not allowed to hold ${item.name}.`];
|
||||
}
|
||||
},
|
||||
onBegin() {
|
||||
for (const pokemon of this.getAllPokemon()) {
|
||||
pokemon.m.originalSpecies = pokemon.baseSpecies.name;
|
||||
}
|
||||
},
|
||||
onSwitchIn(pokemon) {
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(pokemon.species.originalMega);
|
||||
if (oMegaSpecies.exists && pokemon.m.originalSpecies !== oMegaSpecies.baseSpecies) {
|
||||
this.add('-start', pokemon, oMegaSpecies.requiredItem || oMegaSpecies.requiredMove, '[silent]');
|
||||
const oSpecies = this.dex.species.get(pokemon.m.originalSpecies);
|
||||
if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
this.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
|
||||
}
|
||||
}
|
||||
},
|
||||
onSwitchOut(pokemon) {
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(pokemon.species.originalMega);
|
||||
if (oMegaSpecies.exists && pokemon.m.originalSpecies !== oMegaSpecies.baseSpecies) {
|
||||
this.add('-start', pokemon, oMegaSpecies.requiredItem || oMegaSpecies.requiredMove, '[silent]');
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[Gen 7] STABmons",
|
||||
desc: `Pokémon can use any move of their typing, in addition to the moves they can normally learn.`,
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
export const Items: {[k: string]: ModdedItemData} = {
|
||||
blueorb: {
|
||||
inherit: true,
|
||||
onSwitchIn(pokemon) {
|
||||
if (pokemon.isActive && !pokemon.species.isPrimal) {
|
||||
this.queue.insertChoice({pokemon, choice: 'runPrimal'});
|
||||
}
|
||||
},
|
||||
onPrimal(pokemon) {
|
||||
// @ts-ignore
|
||||
const species: Species = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Kyogre-Primal');
|
||||
if (pokemon.m.originalSpecies === 'Kyogre') {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
} else {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
pokemon.baseSpecies = species;
|
||||
this.add('-start', pokemon, 'Blue Orb', '[silent]');
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
redorb: {
|
||||
inherit: true,
|
||||
onSwitchIn(pokemon) {
|
||||
if (pokemon.isActive && !pokemon.species.isPrimal) {
|
||||
this.queue.insertChoice({pokemon, choice: 'runPrimal'});
|
||||
}
|
||||
},
|
||||
onPrimal(pokemon) {
|
||||
// @ts-ignore
|
||||
const species: Species = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Groudon-Primal');
|
||||
if (pokemon.m.originalSpecies === 'Groudon') {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
} else {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
pokemon.baseSpecies = species;
|
||||
this.add('-start', pokemon, 'Red Orb', '[silent]');
|
||||
const apparentSpecies = pokemon.illusion ? pokemon.illusion.species.name : pokemon.m.originalSpecies;
|
||||
const oSpecies = this.dex.species.get(apparentSpecies);
|
||||
if (pokemon.illusion) {
|
||||
const types = oSpecies.types;
|
||||
if (types.length > 1 || types[types.length - 1] !== 'Fire') {
|
||||
this.add('-start', pokemon, 'typechange', (types[0] !== 'Fire' ? types[0] + '/' : '') + 'Fire', '[silent]');
|
||||
}
|
||||
} else if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
this.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
|
||||
}
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
};
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
export const Scripts: ModdedBattleScriptsData = {
|
||||
inherit: 'gen7',
|
||||
init() {
|
||||
for (const id in this.data.Items) {
|
||||
if (!this.data.Items[id].megaStone) continue;
|
||||
this.modData('Items', id).onTakeItem = false;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
canMegaEvo(pokemon) {
|
||||
if (pokemon.species.isMega || pokemon.species.isPrimal) return null;
|
||||
|
||||
const item = pokemon.getItem();
|
||||
if (item.megaStone) {
|
||||
if (item.megaStone === pokemon.name) return null;
|
||||
return item.megaStone;
|
||||
} else if (pokemon.baseMoves.includes('dragonascent' as ID)) {
|
||||
return 'Rayquaza-Mega';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
runMegaEvo(pokemon) {
|
||||
if (pokemon.species.isMega || pokemon.species.isPrimal) return false;
|
||||
|
||||
const isUltraBurst = !pokemon.canMegaEvo;
|
||||
// @ts-ignore
|
||||
const species: Species = this.getMixedSpecies(pokemon.m.originalSpecies, pokemon.canMegaEvo || pokemon.canUltraBurst);
|
||||
|
||||
// Do we have a proper sprite for it?
|
||||
// @ts-ignore assert non-null pokemon.canMegaEvo
|
||||
if (isUltraBurst || this.dex.species.get(pokemon.canMegaEvo).baseSpecies === pokemon.m.originalSpecies) {
|
||||
pokemon.formeChange(species, pokemon.getItem(), true);
|
||||
} else {
|
||||
const oSpecies = this.dex.species.get(pokemon.m.originalSpecies);
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(species.originalMega);
|
||||
pokemon.formeChange(species, pokemon.getItem(), true);
|
||||
this.battle.add('-start', pokemon, oMegaSpecies.requiredItem || oMegaSpecies.requiredMove, '[silent]');
|
||||
if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
this.battle.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
|
||||
}
|
||||
}
|
||||
|
||||
pokemon.canMegaEvo = null;
|
||||
if (isUltraBurst) pokemon.canUltraBurst = null;
|
||||
return true;
|
||||
},
|
||||
getMixedSpecies(originalSpecies, megaSpecies) {
|
||||
const oSpecies = this.dex.species.get(originalSpecies);
|
||||
const mSpecies = this.dex.species.get(megaSpecies);
|
||||
if (oSpecies.baseSpecies === mSpecies.baseSpecies) return mSpecies;
|
||||
// @ts-ignore
|
||||
const deltas = this.getMegaDeltas(mSpecies);
|
||||
// @ts-ignore
|
||||
const species = this.doGetMixedSpecies(oSpecies, deltas);
|
||||
return species;
|
||||
},
|
||||
getMegaDeltas(megaSpecies) {
|
||||
const baseSpecies = this.dex.species.get(megaSpecies.baseSpecies);
|
||||
const deltas: {
|
||||
ability: string,
|
||||
baseStats: SparseStatsTable,
|
||||
weighthg: number,
|
||||
originalMega: string,
|
||||
requiredItem: string | undefined,
|
||||
type?: string,
|
||||
isMega?: boolean,
|
||||
isPrimal?: boolean,
|
||||
} = {
|
||||
ability: megaSpecies.abilities['0'],
|
||||
baseStats: {},
|
||||
weighthg: megaSpecies.weighthg - baseSpecies.weighthg,
|
||||
originalMega: megaSpecies.name,
|
||||
requiredItem: megaSpecies.requiredItem,
|
||||
};
|
||||
let stat: StatID;
|
||||
for (stat in megaSpecies.baseStats) {
|
||||
deltas.baseStats[stat] = megaSpecies.baseStats[stat] - baseSpecies.baseStats[stat];
|
||||
}
|
||||
if (megaSpecies.types.length > baseSpecies.types.length) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
} else if (megaSpecies.types.length < baseSpecies.types.length) {
|
||||
deltas.type = baseSpecies.types[0];
|
||||
} else if (megaSpecies.types[1] !== baseSpecies.types[1]) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
}
|
||||
if (megaSpecies.isMega) deltas.isMega = true;
|
||||
if (megaSpecies.isPrimal) deltas.isPrimal = true;
|
||||
return deltas;
|
||||
},
|
||||
doGetMixedSpecies(speciesOrSpeciesName, deltas) {
|
||||
if (!deltas) throw new TypeError("Must specify deltas!");
|
||||
const species = this.dex.deepClone(this.dex.species.get(speciesOrSpeciesName));
|
||||
species.abilities = {'0': deltas.ability};
|
||||
if (species.types[0] === deltas.type) {
|
||||
species.types = [deltas.type];
|
||||
} else if (deltas.type) {
|
||||
species.types = [species.types[0], deltas.type];
|
||||
}
|
||||
const baseStats = species.baseStats;
|
||||
for (const statName in baseStats) {
|
||||
baseStats[statName] = this.battle.clampIntRange(baseStats[statName] + deltas.baseStats[statName], 1, 255);
|
||||
}
|
||||
species.weighthg = Math.max(1, species.weighthg + deltas.weighthg);
|
||||
species.originalMega = deltas.originalMega;
|
||||
species.requiredItem = deltas.requiredItem;
|
||||
if (deltas.isMega) species.isMega = true;
|
||||
if (deltas.isPrimal) species.isPrimal = true;
|
||||
return species;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -7,6 +7,15 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
adamantcrystal: {
|
||||
inherit: true,
|
||||
onBasePower(basePower, user, target, move) {
|
||||
if (move.type === 'Steel' || move.type === 'Dragon') {
|
||||
return this.chainModify([4915, 4096]);
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
aerodactylite: {
|
||||
inherit: true,
|
||||
isNonstandard: null,
|
||||
|
|
@ -47,6 +56,28 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
blueorb: {
|
||||
inherit: true,
|
||||
onSwitchIn(pokemon) {
|
||||
if (pokemon.isActive && !pokemon.species.isPrimal) {
|
||||
this.queue.insertChoice({pokemon, choice: 'runPrimal'});
|
||||
}
|
||||
},
|
||||
onPrimal(pokemon) {
|
||||
// @ts-ignore
|
||||
const species: Species = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Kyogre-Primal', pokemon);
|
||||
if (pokemon.m.originalSpecies === 'Kyogre') {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
} else {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
pokemon.baseSpecies = species;
|
||||
this.add('-start', pokemon, 'Blue Orb', '[silent]');
|
||||
}
|
||||
pokemon.canTerastallize = null;
|
||||
},
|
||||
onTakeItem: false,
|
||||
isNonstandard: null,
|
||||
},
|
||||
cameruptite: {
|
||||
inherit: true,
|
||||
isNonstandard: null,
|
||||
|
|
@ -83,6 +114,15 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
griseouscore: {
|
||||
inherit: true,
|
||||
onBasePower(basePower, user, target, move) {
|
||||
if (move.type === 'Ghost' || move.type === 'Dragon') {
|
||||
return this.chainModify([4915, 4096]);
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
gyaradosite: {
|
||||
inherit: true,
|
||||
isNonstandard: null,
|
||||
|
|
@ -115,6 +155,15 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
lustrousglobe: {
|
||||
inherit: true,
|
||||
onBasePower(basePower, user, target, move) {
|
||||
if (move.type === 'Water' || move.type === 'Dragon') {
|
||||
return this.chainModify([4915, 4096]);
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
manectite: {
|
||||
inherit: true,
|
||||
isNonstandard: null,
|
||||
|
|
@ -147,6 +196,48 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
redorb: {
|
||||
inherit: true,
|
||||
onSwitchIn(pokemon) {
|
||||
if (pokemon.isActive && !pokemon.species.isPrimal) {
|
||||
this.queue.insertChoice({pokemon, choice: 'runPrimal'});
|
||||
}
|
||||
},
|
||||
onPrimal(pokemon) {
|
||||
// @ts-ignore
|
||||
const species: Species = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Groudon-Primal', pokemon);
|
||||
if (pokemon.m.originalSpecies === 'Groudon') {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
} else {
|
||||
pokemon.formeChange(species, this.effect, true);
|
||||
pokemon.baseSpecies = species;
|
||||
this.add('-start', pokemon, 'Red Orb', '[silent]');
|
||||
const apparentSpecies = pokemon.illusion ? pokemon.illusion.species.name : pokemon.m.originalSpecies;
|
||||
const oSpecies = this.dex.species.get(apparentSpecies);
|
||||
if (pokemon.illusion) {
|
||||
const types = oSpecies.types;
|
||||
if (types.length > 1 || types[types.length - 1] !== 'Fire') {
|
||||
this.add('-start', pokemon, 'typechange', (types[0] !== 'Fire' ? types[0] + '/' : '') + 'Fire', '[silent]');
|
||||
}
|
||||
} else if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
this.add('-start', pokemon, 'typechange', pokemon.species.types.join('/'), '[silent]');
|
||||
}
|
||||
}
|
||||
pokemon.canTerastallize = null;
|
||||
},
|
||||
onTakeItem: false,
|
||||
isNonstandard: null,
|
||||
},
|
||||
rustedshield: {
|
||||
inherit: true,
|
||||
onTakeItem: false,
|
||||
isNonstandard: null,
|
||||
},
|
||||
rustedsword: {
|
||||
inherit: true,
|
||||
onTakeItem: false,
|
||||
isNonstandard: null,
|
||||
},
|
||||
sablenite: {
|
||||
inherit: true,
|
||||
isNonstandard: null,
|
||||
|
|
@ -187,4 +278,13 @@ export const Items: {[k: string]: ModdedItemData} = {
|
|||
inherit: true,
|
||||
isNonstandard: null,
|
||||
},
|
||||
vilevial: {
|
||||
inherit: true,
|
||||
onBasePower(basePower, user, target, move) {
|
||||
if (['Poison', 'Flying'].includes(move.type)) {
|
||||
return this.chainModify([4915, 4096]);
|
||||
}
|
||||
},
|
||||
onTakeItem: false,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,6 +8,390 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
this.modData('FormatsData', id).isNonstandard = null;
|
||||
}
|
||||
},
|
||||
start() {
|
||||
// Deserialized games should use restart()
|
||||
if (this.deserialized) return;
|
||||
// need all players to start
|
||||
if (!this.sides.every(side => !!side)) throw new Error(`Missing sides: ${this.sides}`);
|
||||
|
||||
if (this.started) throw new Error(`Battle already started`);
|
||||
|
||||
const format = this.format;
|
||||
this.started = true;
|
||||
if (this.gameType === 'multi') {
|
||||
this.sides[1].foe = this.sides[2]!;
|
||||
this.sides[0].foe = this.sides[3]!;
|
||||
this.sides[2]!.foe = this.sides[1];
|
||||
this.sides[3]!.foe = this.sides[0];
|
||||
this.sides[1].allySide = this.sides[3]!;
|
||||
this.sides[0].allySide = this.sides[2]!;
|
||||
this.sides[2]!.allySide = this.sides[0];
|
||||
this.sides[3]!.allySide = this.sides[1];
|
||||
// sync side conditions
|
||||
this.sides[2]!.sideConditions = this.sides[0].sideConditions;
|
||||
this.sides[3]!.sideConditions = this.sides[1].sideConditions;
|
||||
} else {
|
||||
this.sides[1].foe = this.sides[0];
|
||||
this.sides[0].foe = this.sides[1];
|
||||
if (this.sides.length > 2) { // ffa
|
||||
this.sides[2]!.foe = this.sides[3]!;
|
||||
this.sides[3]!.foe = this.sides[2]!;
|
||||
}
|
||||
}
|
||||
|
||||
for (const side of this.sides) {
|
||||
this.add('teamsize', side.id, side.pokemon.length);
|
||||
}
|
||||
|
||||
this.add('gen', this.gen);
|
||||
|
||||
this.add('tier', format.name);
|
||||
if (this.rated) {
|
||||
if (this.rated === 'Rated battle') this.rated = true;
|
||||
this.add('rated', typeof this.rated === 'string' ? this.rated : '');
|
||||
}
|
||||
|
||||
if (format.onBegin) format.onBegin.call(this);
|
||||
for (const rule of this.ruleTable.keys()) {
|
||||
if ('+*-!'.includes(rule.charAt(0))) continue;
|
||||
const subFormat = this.dex.formats.get(rule);
|
||||
if (subFormat.onBegin) subFormat.onBegin.call(this);
|
||||
}
|
||||
for (const pokemon of this.getAllPokemon()) {
|
||||
const item = pokemon.getItem();
|
||||
if (['adamantcrystal', 'griseouscore', 'lustrousglobe', 'vilevial'].includes(item.id) &&
|
||||
item.forcedForme !== pokemon.species.name) {
|
||||
// @ts-ignore
|
||||
const rawSpecies = this.actions.getMixedSpecies(pokemon.m.originalSpecies, item.forcedForme!, pokemon);
|
||||
const species = pokemon.setSpecies(rawSpecies);
|
||||
if (!species) continue;
|
||||
pokemon.baseSpecies = rawSpecies;
|
||||
pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
|
||||
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
||||
pokemon.setAbility(species.abilities['0'], null, true);
|
||||
pokemon.baseAbility = pokemon.ability;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.sides.some(side => !side.pokemon[0])) {
|
||||
throw new Error('Battle not started: A player has an empty team.');
|
||||
}
|
||||
|
||||
if (this.debugMode) {
|
||||
this.checkEVBalance();
|
||||
}
|
||||
|
||||
if (format.onTeamPreview) format.onTeamPreview.call(this);
|
||||
for (const rule of this.ruleTable.keys()) {
|
||||
if ('+*-!'.includes(rule.charAt(0))) continue;
|
||||
const subFormat = this.dex.formats.get(rule);
|
||||
if (subFormat.onTeamPreview) subFormat.onTeamPreview.call(this);
|
||||
}
|
||||
|
||||
this.queue.addChoice({choice: 'start'});
|
||||
this.midTurn = true;
|
||||
if (!this.requestState) this.go();
|
||||
},
|
||||
runAction(action) {
|
||||
const pokemonOriginalHP = action.pokemon?.hp;
|
||||
let residualPokemon: (readonly [Pokemon, number])[] = [];
|
||||
// returns whether or not we ended in a callback
|
||||
switch (action.choice) {
|
||||
case 'start': {
|
||||
for (const side of this.sides) {
|
||||
if (side.pokemonLeft) side.pokemonLeft = side.pokemon.length;
|
||||
}
|
||||
|
||||
this.add('start');
|
||||
|
||||
// Change Pokemon holding Rusted items into their Crowned formes
|
||||
for (const pokemon of this.getAllPokemon()) {
|
||||
let rawSpecies: Species | null = null;
|
||||
const item = pokemon.getItem();
|
||||
if (item.id === 'rustedsword') {
|
||||
// @ts-ignore
|
||||
rawSpecies = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Zacian-Crowned', pokemon);
|
||||
} else if (item.id === 'rustedshield') {
|
||||
// @ts-ignore
|
||||
rawSpecies = this.actions.getMixedSpecies(pokemon.m.originalSpecies, 'Zamazenta-Crowned', pokemon);
|
||||
}
|
||||
if (!rawSpecies) continue;
|
||||
const species = pokemon.setSpecies(rawSpecies);
|
||||
if (!species) continue;
|
||||
pokemon.baseSpecies = rawSpecies;
|
||||
pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
|
||||
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
||||
pokemon.setAbility(species.abilities['0'], null, true);
|
||||
pokemon.baseAbility = pokemon.ability;
|
||||
|
||||
const behemothMove: {[k: string]: string} = {
|
||||
'Rusted Sword': 'behemothblade', 'Rusted Shield': 'behemothbash',
|
||||
};
|
||||
const ironHead = pokemon.baseMoves.indexOf('ironhead');
|
||||
if (ironHead >= 0) {
|
||||
const move = this.dex.moves.get(behemothMove[pokemon.getItem().name]);
|
||||
pokemon.baseMoveSlots[ironHead] = {
|
||||
move: move.name,
|
||||
id: move.id,
|
||||
pp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
|
||||
maxpp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
|
||||
target: move.target,
|
||||
disabled: false,
|
||||
disabledSource: '',
|
||||
used: false,
|
||||
};
|
||||
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.format.onBattleStart) this.format.onBattleStart.call(this);
|
||||
for (const rule of this.ruleTable.keys()) {
|
||||
if ('+*-!'.includes(rule.charAt(0))) continue;
|
||||
const subFormat = this.dex.formats.get(rule);
|
||||
if (subFormat.onBattleStart) subFormat.onBattleStart.call(this);
|
||||
}
|
||||
|
||||
for (const side of this.sides) {
|
||||
for (let i = 0; i < side.active.length; i++) {
|
||||
if (!side.pokemonLeft) {
|
||||
// forfeited before starting
|
||||
side.active[i] = side.pokemon[i];
|
||||
side.active[i].fainted = true;
|
||||
side.active[i].hp = 0;
|
||||
} else {
|
||||
this.actions.switchIn(side.pokemon[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const pokemon of this.getAllPokemon()) {
|
||||
this.singleEvent('Start', this.dex.conditions.getByID(pokemon.species.id), pokemon.speciesState, pokemon);
|
||||
}
|
||||
this.midTurn = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'move':
|
||||
if (!action.pokemon.isActive) return false;
|
||||
if (action.pokemon.fainted) return false;
|
||||
this.actions.runMove(action.move, action.pokemon, action.targetLoc, action.sourceEffect,
|
||||
action.zmove, undefined, action.maxMove, action.originalTarget);
|
||||
break;
|
||||
case 'megaEvo':
|
||||
this.actions.runMegaEvo(action.pokemon);
|
||||
break;
|
||||
case 'runDynamax':
|
||||
action.pokemon.addVolatile('dynamax');
|
||||
action.pokemon.side.dynamaxUsed = true;
|
||||
if (action.pokemon.side.allySide) action.pokemon.side.allySide.dynamaxUsed = true;
|
||||
break;
|
||||
case 'terastallize':
|
||||
this.actions.terastallize(action.pokemon);
|
||||
break;
|
||||
case 'beforeTurnMove':
|
||||
if (!action.pokemon.isActive) return false;
|
||||
if (action.pokemon.fainted) return false;
|
||||
this.debug('before turn callback: ' + action.move.id);
|
||||
const target = this.getTarget(action.pokemon, action.move, action.targetLoc);
|
||||
if (!target) return false;
|
||||
if (!action.move.beforeTurnCallback) throw new Error(`beforeTurnMove has no beforeTurnCallback`);
|
||||
action.move.beforeTurnCallback.call(this, action.pokemon, target);
|
||||
break;
|
||||
case 'priorityChargeMove':
|
||||
if (!action.pokemon.isActive) return false;
|
||||
if (action.pokemon.fainted) return false;
|
||||
this.debug('priority charge callback: ' + action.move.id);
|
||||
if (!action.move.priorityChargeCallback) throw new Error(`priorityChargeMove has no priorityChargeCallback`);
|
||||
action.move.priorityChargeCallback.call(this, action.pokemon);
|
||||
break;
|
||||
|
||||
case 'event':
|
||||
this.runEvent(action.event!, action.pokemon);
|
||||
break;
|
||||
case 'team':
|
||||
if (action.index === 0) {
|
||||
action.pokemon.side.pokemon = [];
|
||||
}
|
||||
action.pokemon.side.pokemon.push(action.pokemon);
|
||||
action.pokemon.position = action.index;
|
||||
// we return here because the update event would crash since there are no active pokemon yet
|
||||
return;
|
||||
|
||||
case 'pass':
|
||||
return;
|
||||
case 'instaswitch':
|
||||
case 'switch':
|
||||
if (action.choice === 'switch' && action.pokemon.status) {
|
||||
this.singleEvent('CheckShow', this.dex.abilities.getByID('naturalcure' as ID), null, action.pokemon);
|
||||
}
|
||||
if (this.actions.switchIn(action.target, action.pokemon.position, action.sourceEffect) === 'pursuitfaint') {
|
||||
// a pokemon fainted from Pursuit before it could switch
|
||||
if (this.gen <= 4) {
|
||||
// in gen 2-4, the switch still happens
|
||||
this.hint("Previously chosen switches continue in Gen 2-4 after a Pursuit target faints.");
|
||||
action.priority = -101;
|
||||
this.queue.unshift(action);
|
||||
break;
|
||||
} else {
|
||||
// in gen 5+, the switch is cancelled
|
||||
this.hint("A Pokemon can't switch between when it runs out of HP and when it faints");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'revivalblessing':
|
||||
action.pokemon.side.pokemonLeft++;
|
||||
if (action.target.position < action.pokemon.side.active.length) {
|
||||
this.queue.addChoice({
|
||||
choice: 'instaswitch',
|
||||
pokemon: action.target,
|
||||
target: action.target,
|
||||
});
|
||||
}
|
||||
action.target.fainted = false;
|
||||
action.target.faintQueued = false;
|
||||
action.target.subFainted = false;
|
||||
action.target.status = '';
|
||||
action.target.hp = 1; // Needed so hp functions works
|
||||
action.target.sethp(action.target.maxhp / 2);
|
||||
this.add('-heal', action.target, action.target.getHealth, '[from] move: Revival Blessing');
|
||||
action.pokemon.side.removeSlotCondition(action.pokemon, 'revivalblessing');
|
||||
break;
|
||||
case 'runUnnerve':
|
||||
this.singleEvent('PreStart', action.pokemon.getAbility(), action.pokemon.abilityState, action.pokemon);
|
||||
break;
|
||||
case 'runSwitch':
|
||||
this.actions.runSwitch(action.pokemon);
|
||||
break;
|
||||
case 'runPrimal':
|
||||
if (!action.pokemon.transformed) {
|
||||
this.singleEvent('Primal', action.pokemon.getItem(), action.pokemon.itemState, action.pokemon);
|
||||
}
|
||||
break;
|
||||
case 'shift':
|
||||
if (!action.pokemon.isActive) return false;
|
||||
if (action.pokemon.fainted) return false;
|
||||
this.swapPosition(action.pokemon, 1);
|
||||
break;
|
||||
|
||||
case 'beforeTurn':
|
||||
this.eachEvent('BeforeTurn');
|
||||
break;
|
||||
case 'residual':
|
||||
this.add('');
|
||||
this.clearActiveMove(true);
|
||||
this.updateSpeed();
|
||||
residualPokemon = this.getAllActive().map(pokemon => [pokemon, pokemon.getUndynamaxedHP()] as const);
|
||||
this.residualEvent('Residual');
|
||||
this.add('upkeep');
|
||||
break;
|
||||
}
|
||||
|
||||
// phazing (Roar, etc)
|
||||
for (const side of this.sides) {
|
||||
for (const pokemon of side.active) {
|
||||
if (pokemon.forceSwitchFlag) {
|
||||
if (pokemon.hp) this.actions.dragIn(pokemon.side, pokemon.position);
|
||||
pokemon.forceSwitchFlag = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.clearActiveMove();
|
||||
|
||||
// fainting
|
||||
|
||||
this.faintMessages();
|
||||
if (this.ended) return true;
|
||||
|
||||
// switching (fainted pokemon, U-turn, Baton Pass, etc)
|
||||
|
||||
if (!this.queue.peek() || (this.gen <= 3 && ['move', 'residual'].includes(this.queue.peek()!.choice))) {
|
||||
// in gen 3 or earlier, switching in fainted pokemon is done after
|
||||
// every move, rather than only at the end of the turn.
|
||||
this.checkFainted();
|
||||
} else if (action.choice === 'megaEvo' && this.gen === 7) {
|
||||
this.eachEvent('Update');
|
||||
// In Gen 7, the action order is recalculated for a Pokémon that mega evolves.
|
||||
for (const [i, queuedAction] of this.queue.list.entries()) {
|
||||
if (queuedAction.pokemon === action.pokemon && queuedAction.choice === 'move') {
|
||||
this.queue.list.splice(i, 1);
|
||||
queuedAction.mega = 'done';
|
||||
this.queue.insertChoice(queuedAction, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (this.queue.peek()?.choice === 'instaswitch') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.gen >= 5) {
|
||||
this.eachEvent('Update');
|
||||
for (const [pokemon, originalHP] of residualPokemon) {
|
||||
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
||||
if (pokemon.hp && pokemon.getUndynamaxedHP() <= maxhp / 2 && originalHP > maxhp / 2) {
|
||||
this.runEvent('EmergencyExit', pokemon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action.choice === 'runSwitch') {
|
||||
const pokemon = action.pokemon;
|
||||
if (pokemon.hp && pokemon.hp <= pokemon.maxhp / 2 && pokemonOriginalHP! > pokemon.maxhp / 2) {
|
||||
this.runEvent('EmergencyExit', pokemon);
|
||||
}
|
||||
}
|
||||
|
||||
const switches = this.sides.map(
|
||||
side => side.active.some(pokemon => pokemon && !!pokemon.switchFlag)
|
||||
);
|
||||
|
||||
for (let i = 0; i < this.sides.length; i++) {
|
||||
let reviveSwitch = false; // Used to ignore the fake switch for Revival Blessing
|
||||
if (switches[i] && !this.canSwitch(this.sides[i])) {
|
||||
for (const pokemon of this.sides[i].active) {
|
||||
if (this.sides[i].slotConditions[pokemon.position]['revivalblessing']) {
|
||||
reviveSwitch = true;
|
||||
continue;
|
||||
}
|
||||
pokemon.switchFlag = false;
|
||||
}
|
||||
if (!reviveSwitch) switches[i] = false;
|
||||
} else if (switches[i]) {
|
||||
for (const pokemon of this.sides[i].active) {
|
||||
if (pokemon.switchFlag && pokemon.switchFlag !== 'revivalblessing' && !pokemon.skipBeforeSwitchOutEventFlag) {
|
||||
this.runEvent('BeforeSwitchOut', pokemon);
|
||||
pokemon.skipBeforeSwitchOutEventFlag = true;
|
||||
this.faintMessages(); // Pokemon may have fainted in BeforeSwitchOut
|
||||
if (this.ended) return true;
|
||||
if (pokemon.fainted) {
|
||||
switches[i] = this.sides[i].active.some(sidePokemon => sidePokemon && !!sidePokemon.switchFlag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const playerSwitch of switches) {
|
||||
if (playerSwitch) {
|
||||
this.makeRequest('switch');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.gen < 5) this.eachEvent('Update');
|
||||
|
||||
if (this.gen >= 8 && (this.queue.peek()?.choice === 'move' || this.queue.peek()?.choice === 'runDynamax')) {
|
||||
// In gen 8, speed is updated dynamically so update the queue's speed properties and sort it.
|
||||
this.updateSpeed();
|
||||
for (const queueAction of this.queue.list) {
|
||||
if (queueAction.pokemon) this.getActionSpeed(queueAction);
|
||||
}
|
||||
this.queue.sort();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
actions: {
|
||||
canMegaEvo(pokemon) {
|
||||
if (pokemon.species.isMega) return null;
|
||||
|
|
@ -24,7 +408,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
if (pokemon.species.isMega) return false;
|
||||
|
||||
// @ts-ignore
|
||||
const species: Species = this.getMixedSpecies(pokemon.m.originalSpecies, pokemon.canMegaEvo);
|
||||
const species: Species = this.getMixedSpecies(pokemon.m.originalSpecies, pokemon.canMegaEvo, pokemon);
|
||||
|
||||
// Do we have a proper sprite for it?
|
||||
if (this.dex.species.get(pokemon.canMegaEvo!).baseSpecies === pokemon.m.originalSpecies) {
|
||||
|
|
@ -32,7 +416,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
} else {
|
||||
const oSpecies = this.dex.species.get(pokemon.m.originalSpecies);
|
||||
// @ts-ignore
|
||||
const oMegaSpecies = this.dex.species.get(species.originalMega);
|
||||
const oMegaSpecies = this.dex.species.get(species.originalSpecies);
|
||||
pokemon.formeChange(species, pokemon.getItem(), true);
|
||||
this.battle.add('-start', pokemon, oMegaSpecies.requiredItem, '[silent]');
|
||||
if (oSpecies.types.length !== pokemon.species.types.length || oSpecies.types[1] !== pokemon.species.types[1]) {
|
||||
|
|
@ -43,48 +427,56 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
pokemon.canMegaEvo = null;
|
||||
return true;
|
||||
},
|
||||
getMixedSpecies(originalForme, megaForme) {
|
||||
getMixedSpecies(originalForme, megaForme, pokemon) {
|
||||
const originalSpecies = this.dex.species.get(originalForme);
|
||||
const megaSpecies = this.dex.species.get(megaForme);
|
||||
if (originalSpecies.baseSpecies === megaSpecies.baseSpecies) return megaSpecies;
|
||||
// @ts-ignore
|
||||
const deltas = this.getMegaDeltas(megaSpecies);
|
||||
const deltas = this.getFormeChangeDeltas(megaSpecies, pokemon);
|
||||
// @ts-ignore
|
||||
const species = this.doGetMixedSpecies(originalSpecies, deltas);
|
||||
const species = this.mutateOriginalSpecies(originalSpecies, deltas);
|
||||
return species;
|
||||
},
|
||||
getMegaDeltas(megaSpecies) {
|
||||
const baseSpecies = this.dex.species.get(megaSpecies.baseSpecies);
|
||||
getFormeChangeDeltas(formeChangeSpecies, pokemon) {
|
||||
const baseSpecies = this.dex.species.get(formeChangeSpecies.baseSpecies);
|
||||
const deltas: {
|
||||
ability: string,
|
||||
baseStats: SparseStatsTable,
|
||||
weighthg: number,
|
||||
originalMega: string,
|
||||
originalSpecies: string,
|
||||
requiredItem: string | undefined,
|
||||
type?: string,
|
||||
isMega?: boolean,
|
||||
formeType?: string,
|
||||
} = {
|
||||
ability: megaSpecies.abilities['0'],
|
||||
ability: formeChangeSpecies.abilities['0'],
|
||||
baseStats: {},
|
||||
weighthg: megaSpecies.weighthg - baseSpecies.weighthg,
|
||||
originalMega: megaSpecies.name,
|
||||
requiredItem: megaSpecies.requiredItem,
|
||||
weighthg: formeChangeSpecies.weighthg - baseSpecies.weighthg,
|
||||
originalSpecies: formeChangeSpecies.name,
|
||||
requiredItem: formeChangeSpecies.requiredItem,
|
||||
};
|
||||
let statId: StatID;
|
||||
for (statId in megaSpecies.baseStats) {
|
||||
deltas.baseStats[statId] = megaSpecies.baseStats[statId] - baseSpecies.baseStats[statId];
|
||||
for (statId in formeChangeSpecies.baseStats) {
|
||||
deltas.baseStats[statId] = formeChangeSpecies.baseStats[statId] - baseSpecies.baseStats[statId];
|
||||
}
|
||||
if (megaSpecies.types.length > baseSpecies.types.length) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
} else if (megaSpecies.types.length < baseSpecies.types.length) {
|
||||
if (formeChangeSpecies.types.length > baseSpecies.types.length) {
|
||||
deltas.type = formeChangeSpecies.types[1];
|
||||
} else if (formeChangeSpecies.types.length < baseSpecies.types.length) {
|
||||
deltas.type = 'mono';
|
||||
} else if (megaSpecies.types[1] !== baseSpecies.types[1]) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
} else if (formeChangeSpecies.types[1] !== baseSpecies.types[1]) {
|
||||
deltas.type = formeChangeSpecies.types[1];
|
||||
}
|
||||
let formeType: string | null = null;
|
||||
if (formeChangeSpecies.isMega) formeType = 'Mega';
|
||||
if (formeChangeSpecies.isPrimal) formeType = 'Primal';
|
||||
if (formeChangeSpecies.name.endsWith('Crowned')) formeType = 'Crowned';
|
||||
if (formeType) deltas.formeType = formeType;
|
||||
if (!deltas.formeType && formeChangeSpecies.abilities['H'] &&
|
||||
pokemon && pokemon.baseSpecies.abilities['H'] === pokemon.getAbility().name) {
|
||||
deltas.ability = formeChangeSpecies.abilities['H'];
|
||||
}
|
||||
if (megaSpecies.isMega) deltas.isMega = true;
|
||||
return deltas;
|
||||
},
|
||||
doGetMixedSpecies(speciesOrForme, deltas) {
|
||||
mutateOriginalSpecies(speciesOrForme, deltas) {
|
||||
if (!deltas) throw new TypeError("Must specify deltas!");
|
||||
const species = this.dex.deepClone(this.dex.species.get(speciesOrForme));
|
||||
species.abilities = {'0': deltas.ability};
|
||||
|
|
@ -100,9 +492,12 @@ export const Scripts: ModdedBattleScriptsData = {
|
|||
baseStats[statName] = this.battle.clampIntRange(baseStats[statName] + deltas.baseStats[statName], 1, 255);
|
||||
}
|
||||
species.weighthg = Math.max(1, species.weighthg + deltas.weighthg);
|
||||
species.originalMega = deltas.originalMega;
|
||||
species.originalSpecies = deltas.originalSpecies;
|
||||
species.requiredItem = deltas.requiredItem;
|
||||
if (deltas.isMega) species.isMega = true;
|
||||
switch (deltas.formeType) {
|
||||
case 'Mega': species.isMega = true; break;
|
||||
case 'Primal': species.isPrimal = true; break;
|
||||
}
|
||||
return species;
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ interface StoneDeltas {
|
|||
|
||||
type TierShiftTiers = 'UU' | 'RUBL' | 'RU' | 'NUBL' | 'NU' | 'PUBL' | 'PU' | 'NFE' | 'LC';
|
||||
|
||||
function getMegaStone(stone: string, mod = 'gen8'): Item | null {
|
||||
function getMegaStone(stone: string, mod = 'gen9'): Item | null {
|
||||
let dex = Dex;
|
||||
if (mod && toID(mod) in Dex.dexes) dex = Dex.mod(toID(mod));
|
||||
const item = dex.items.get(stone);
|
||||
|
|
@ -40,7 +40,8 @@ function getMegaStone(stone: string, mod = 'gen8'): Item | null {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
if (!item.megaStone && !item.onPrimal) return null;
|
||||
if (!item.megaStone && !item.onPrimal && !item.forcedForme?.endsWith('Epilogue') &&
|
||||
!item.forcedForme?.endsWith('Origin') && !item.name.startsWith('Rusted')) return null;
|
||||
return item;
|
||||
}
|
||||
|
||||
|
|
@ -96,18 +97,39 @@ export const commands: Chat.ChatCommands = {
|
|||
}
|
||||
const stone = getMegaStone(stoneName[0], mod);
|
||||
const species = dex.species.get(sep[0]);
|
||||
if (!stone || (dex.gen >= 8 && ['redorb', 'blueorb'].includes(stone.id))) {
|
||||
throw new Chat.ErrorMessage(`Error: Mega Stone not found.`);
|
||||
if (!stone) {
|
||||
throw new Chat.ErrorMessage(`Error: Mega Stone/Primal Orb/Rusted Item/Origin Item not found.`);
|
||||
}
|
||||
if (!species.exists) throw new Chat.ErrorMessage(`Error: Pok\u00e9mon not found.`);
|
||||
let baseSpecies = dex.species.get(stone.megaEvolves);
|
||||
let megaSpecies = dex.species.get(stone.megaStone);
|
||||
if (stone.id === 'redorb') { // Orbs do not have 'Item.megaStone' or 'Item.megaEvolves' properties.
|
||||
megaSpecies = dex.species.get("Groudon-Primal");
|
||||
baseSpecies = dex.species.get("Groudon");
|
||||
} else if (stone.id === 'blueorb') {
|
||||
let baseSpecies: Species;
|
||||
let megaSpecies: Species;
|
||||
switch (stone.id) {
|
||||
case 'blueorb':
|
||||
megaSpecies = dex.species.get("Kyogre-Primal");
|
||||
baseSpecies = dex.species.get("Kyogre");
|
||||
break;
|
||||
case 'redorb':
|
||||
megaSpecies = dex.species.get("Groudon-Primal");
|
||||
baseSpecies = dex.species.get("Groudon");
|
||||
break;
|
||||
case 'rustedshield':
|
||||
megaSpecies = dex.species.get("Zamazenta-Crowned");
|
||||
baseSpecies = dex.species.get("Zamazenta");
|
||||
break;
|
||||
case 'rustedsword':
|
||||
megaSpecies = dex.species.get("Zacian-Crowned");
|
||||
baseSpecies = dex.species.get("Zacian");
|
||||
break;
|
||||
default:
|
||||
const forcedForme = stone.forcedForme;
|
||||
if (forcedForme && (forcedForme.endsWith('Origin') || forcedForme.endsWith('Epilogue'))) {
|
||||
megaSpecies = dex.species.get(forcedForme);
|
||||
baseSpecies = dex.species.get(forcedForme.split('-')[0]);
|
||||
} else {
|
||||
megaSpecies = dex.species.get(stone.megaStone);
|
||||
baseSpecies = dex.species.get(stone.megaEvolves);
|
||||
}
|
||||
break;
|
||||
}
|
||||
const deltas: StoneDeltas = {
|
||||
baseStats: Object.create(null),
|
||||
|
|
@ -121,7 +143,7 @@ export const commands: Chat.ChatCommands = {
|
|||
if (megaSpecies.types.length > baseSpecies.types.length) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
} else if (megaSpecies.types.length < baseSpecies.types.length) {
|
||||
deltas.type = dex.gen >= 8 ? 'mono' : megaSpecies.types[0];
|
||||
deltas.type = 'mono';
|
||||
} else if (megaSpecies.types[1] !== baseSpecies.types[1]) {
|
||||
deltas.type = megaSpecies.types[1];
|
||||
}
|
||||
|
|
@ -171,7 +193,7 @@ export const commands: Chat.ChatCommands = {
|
|||
)).join(" |  ") + `</font>`);
|
||||
},
|
||||
mixandmegahelp: [
|
||||
`/mnm <pokemon> @ <mega stone>[, generation] - Shows the Mix and Mega evolved Pok\u00e9mon's type and stats.`,
|
||||
`/mnm <pokemon> @ <mega stone or other>[, generation] - Shows the Mix and Mega evolved Pok\u00e9mon's type and stats.`,
|
||||
],
|
||||
|
||||
orb: 'stone',
|
||||
|
|
@ -193,16 +215,13 @@ export const commands: Chat.ChatCommands = {
|
|||
if (!targetid) return this.parse('/help stone');
|
||||
this.runBroadcast();
|
||||
const stone = getMegaStone(targetid, sep[1]);
|
||||
if (stone && dex.gen >= 8 && ['redorb', 'blueorb'].includes(stone.id)) {
|
||||
throw new Chat.ErrorMessage("The Orbs do not exist in Gen 8 and later.");
|
||||
}
|
||||
const stones = [];
|
||||
if (!stone) {
|
||||
const species = dex.species.get(targetid.replace(/(?:mega[xy]?|primal)$/, ''));
|
||||
const species = dex.species.get(targetid.replace(/(?:mega[xy]?|primal|origin|crowned|epilogue)$/, ''));
|
||||
if (!species.exists) throw new Chat.ErrorMessage(`Error: Mega Stone not found.`);
|
||||
if (!species.otherFormes) throw new Chat.ErrorMessage(`Error: Mega Evolution not found.`);
|
||||
for (const poke of species.otherFormes) {
|
||||
if (!/(?:-Primal|-Mega(?:-[XY])?)$/.test(poke)) continue;
|
||||
if (!/(?:-Crowned|-Epilogue|-Origin|-Primal|-Mega(?:-[XY])?)$/.test(poke)) continue;
|
||||
const megaPoke = dex.species.get(poke);
|
||||
const flag = megaPoke.requiredMove === 'Dragon Ascent' ? megaPoke.requiredMove : megaPoke.requiredItem;
|
||||
if (/mega[xy]$/.test(targetid) && toID(megaPoke.name) !== toID(dex.species.get(targetid))) continue;
|
||||
|
|
@ -214,17 +233,35 @@ export const commands: Chat.ChatCommands = {
|
|||
const toDisplay = (stones.length ? stones : [stone]);
|
||||
for (const aStone of toDisplay) {
|
||||
if (!aStone) return;
|
||||
let baseSpecies = dex.species.get(aStone.megaEvolves);
|
||||
let megaSpecies = dex.species.get(aStone.megaStone);
|
||||
if (dex.gen >= 8 && ['redorb', 'blueorb'].includes(aStone.id)) {
|
||||
throw new Chat.ErrorMessage("The Orbs do not exist in Gen 8 and later.");
|
||||
}
|
||||
if (aStone.id === 'redorb') { // Orbs do not have 'Item.megaStone' or 'Item.megaEvolves' properties.
|
||||
megaSpecies = dex.species.get("Groudon-Primal");
|
||||
baseSpecies = dex.species.get("Groudon");
|
||||
} else if (aStone.id === 'blueorb') {
|
||||
let baseSpecies: Species;
|
||||
let megaSpecies: Species;
|
||||
switch (aStone.id) {
|
||||
case 'blueorb':
|
||||
megaSpecies = dex.species.get("Kyogre-Primal");
|
||||
baseSpecies = dex.species.get("Kyogre");
|
||||
break;
|
||||
case 'redorb':
|
||||
megaSpecies = dex.species.get("Groudon-Primal");
|
||||
baseSpecies = dex.species.get("Groudon");
|
||||
break;
|
||||
case 'rustedshield':
|
||||
megaSpecies = dex.species.get("Zamazenta-Crowned");
|
||||
baseSpecies = dex.species.get("Zamazenta");
|
||||
break;
|
||||
case 'rustedsword':
|
||||
megaSpecies = dex.species.get("Zacian-Crowned");
|
||||
baseSpecies = dex.species.get("Zacian");
|
||||
break;
|
||||
default:
|
||||
const forcedForme = aStone.forcedForme;
|
||||
if (forcedForme && (forcedForme.endsWith('Origin') || forcedForme.endsWith('Epilogue'))) {
|
||||
megaSpecies = dex.species.get(forcedForme);
|
||||
baseSpecies = dex.species.get(forcedForme.split('-')[0]);
|
||||
} else {
|
||||
megaSpecies = dex.species.get(aStone.megaStone);
|
||||
baseSpecies = dex.species.get(aStone.megaEvolves);
|
||||
}
|
||||
break;
|
||||
}
|
||||
const deltas: StoneDeltas = {
|
||||
baseStats: Object.create(null),
|
||||
|
|
@ -247,19 +284,23 @@ export const commands: Chat.ChatCommands = {
|
|||
Weight: (deltas.weighthg < 0 ? "" : "+") + deltas.weighthg / 10 + " kg",
|
||||
};
|
||||
let tier;
|
||||
if (['redorb', 'blueorb'].includes(aStone.id)) {
|
||||
if (['redorb', 'blueorb'].includes(aStone.id) || aStone.forcedForme?.endsWith('Origin')) {
|
||||
tier = "Orb";
|
||||
} else if (aStone.name === "Dragon Ascent") {
|
||||
tier = "Move";
|
||||
} else {
|
||||
} else if (aStone.megaStone) {
|
||||
tier = "Stone";
|
||||
} else {
|
||||
tier = "Item";
|
||||
}
|
||||
let buf = `<li class="result">`;
|
||||
buf += `<span class="col numcol">${tier}</span> `;
|
||||
if (aStone.name === "Dragon Ascent") {
|
||||
buf += `<span class="col itemiconcol"></span>`;
|
||||
} else {
|
||||
buf += `<span class="col itemiconcol"><psicon item="${toID(aStone)}"/></span> `;
|
||||
// temp image support until real images are uploaded
|
||||
const itemName = aStone.forcedForme?.endsWith('Origin') ? aStone.name.split(' ')[0] + ' Orb' : aStone.name;
|
||||
buf += `<span class="col itemiconcol"><psicon item="${toID(itemName)}"/></span> `;
|
||||
}
|
||||
if (aStone.name === "Dragon Ascent") {
|
||||
buf += `<span class="col movenamecol" style="white-space:nowrap"><a href="https://${Config.routes.dex}/moves/${targetid}" target="_blank">Dragon Ascent</a></span> `;
|
||||
|
|
@ -273,7 +314,7 @@ export const commands: Chat.ChatCommands = {
|
|||
}
|
||||
buf += `<span style="float:left;min-height:26px">`;
|
||||
buf += `<span class="col abilitycol">${megaSpecies.abilities['0']}</span>`;
|
||||
buf += `<span class="col abilitycol"></span>`;
|
||||
buf += `<span class="col abilitycol">${`<em>${megaSpecies.abilities['H']}</em>` || ''}</span>`;
|
||||
buf += `</span>`;
|
||||
buf += `<span style="float:left;min-height:26px">`;
|
||||
buf += `<span class="col statcol"><em>HP</em><br />0</span> `;
|
||||
|
|
@ -289,7 +330,7 @@ export const commands: Chat.ChatCommands = {
|
|||
this.sendReply(`|raw|<font size="1"><font color="#686868">Gen:</font> ${details["Gen"]} |  <font color="#686868">Weight:</font> ${details["Weight"]}</font>`);
|
||||
}
|
||||
},
|
||||
stonehelp: [`/stone <mega stone>[, generation] - Shows the changes that a mega stone/orb applies to a Pok\u00e9mon.`],
|
||||
stonehelp: [`/stone <mega stone or other>[, generation] - Shows the changes that a mega stone/orb applies to a Pok\u00e9mon.`],
|
||||
|
||||
350: '350cup',
|
||||
'350cup'(target, room, user) {
|
||||
|
|
|
|||
|
|
@ -296,9 +296,9 @@ interface ModdedBattleActions {
|
|||
) => void;
|
||||
|
||||
// oms
|
||||
doGetMixedSpecies?: (this: BattleActions, species: Species, deltas: AnyObject) => Species;
|
||||
getMegaDeltas?: (this: BattleActions, megaSpecies: Species) => AnyObject;
|
||||
getMixedSpecies?: (this: BattleActions, originalName: string, megaName: string) => Species;
|
||||
mutateOriginalSpecies?: (this: BattleActions, species: Species, deltas: AnyObject) => Species;
|
||||
getFormeChangeDeltas?: (this: BattleActions, formeChangeSpecies: Species, pokemon?: Pokemon) => AnyObject;
|
||||
getMixedSpecies?: (this: BattleActions, originalName: string, megaName: string, pokemon?: Pokemon) => Species;
|
||||
}
|
||||
|
||||
interface ModdedBattleSide {
|
||||
|
|
@ -392,6 +392,7 @@ interface ModdedBattleScriptsData extends Partial<BattleScriptsData> {
|
|||
nextTurn?: (this: Battle) => void;
|
||||
runAction?: (this: Battle, action: Action) => void;
|
||||
spreadModify?: (this: Battle, baseStats: StatsTable, set: PokemonSet) => StatsTable;
|
||||
start?: (this: Battle) => void;
|
||||
suppressingWeather?: (this: Battle) => boolean;
|
||||
trunc?: (n: number) => number;
|
||||
win?: (this: Battle, side?: SideID | '' | Side | null) => boolean;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user