pokemon-showdown/data/mods/megasforall/scripts.ts
Guangcong Luo d18c0a4e1f Refactor more things to use pokemon.allies/foes
(This will make multi battles easier to implement.)
2021-03-28 20:06:18 -07:00

386 lines
16 KiB
TypeScript

export const Scripts: ModdedBattleScriptsData = {
init() {
const newMoves = (mon: string, moves: string[]) => {
for (const move of moves) {
this.modData('Learnsets', this.toID(mon)).learnset[this.toID(move)] = ["8M"];
}
};
newMoves("dragonite", ["playrough"]);
newMoves("goodra", ["gigadrain", "drainpunch"]);
newMoves("dragapult", ["icebeam"]);
newMoves("orbeetle", ["focusblast", "teleport"]);
newMoves("thievul", ["focusblast", "aurasphere", "hiddenpower", "moonlight", "spiritbreak"]);
newMoves("gumshoos", ["coil", "bodyslam"]);
newMoves("vikavolt", ["leafblade", "darkpulse", "uturn", "thundercage"]);
newMoves("lycanrocmidnight", ["headsmash"]);
newMoves("lycanroc", ["extremespeed", "spikes"]);
newMoves("raichu", ["highjumpkick"]);
newMoves("clefable", ["hex", "nastyplot", "shadowsneak", "willowisp"]);
newMoves("rillaboom", ["toxic"]);
newMoves("cinderace", ["energyball"]);
newMoves("inteleon", ["taunt", "firstimpression", "encore", "pursuit"]);
newMoves("klinklang", ["overheat", "rapidspin"]);
newMoves("garbodor", ["stealthrock", "knockoff"]);
newMoves("jolteon", ["calmmind"]);
newMoves("flareon", ["burnup", "morningsun"]);
newMoves("butterfree", ["taunt", "earthpower"]);
newMoves("chandelure", ["mindblown"]);
newMoves("gothitelle", ["wish", "teleport", "doomdesire", "flashcannon"]);
newMoves("conkeldurr", ["shoreup"]);
newMoves("gigalith", ["skullbash", "sunnyday", "synthesis"]);
newMoves("reuniclus", ["photongeyser", "psychoboost"]);
newMoves("boltund", ["pursuit"]);
newMoves("archeops", ["fireblast", "dualwingbeat", "bravebird"]);
newMoves("talonflame", ["scorchingsands"]);
newMoves("staraptor", ["roleplay", "superfang"]);
newMoves("bibarel", ["fly"]);
newMoves("kricketune", ["closecombat", "drainpunch", "dualwingbeat", "firstimpression", "powertrip", "tripleaxel", "uturn"]);
newMoves("mismagius", ["sludgebomb", "sludgewave", "toxicspikes", "poisonfang", "partingshot"]);
newMoves("murkrow", ["partingshot"]);
newMoves("honchkrow", ["partingshot", "dualwingbeat"]);
newMoves("spiritomb", ["partingshot"]);
newMoves("ariados", ["spikes"]);
newMoves("gourgeist", ["bodypress", "encore", "flareblitz", "partingshot", "strengthsap"]);
newMoves("mimikyu", ["firstimpression", "strengthsap", "uturn"]);
newMoves("nidoqueen", ["milkdrink"]);
newMoves("walrein", ["darkpulse", "flipturn", "focusblast", "freezedry", "slackoff"]);
newMoves("aurorus", ["rapidspin", "voltswitch"]);
newMoves("trevenant", ["floralhealing", "synthesis", "floralhealing", "synthesis"]);
newMoves("eelektross", ["recover", "scald"]);
newMoves("dragalge", ["acidspray", "gastroacid", "roost", "terrainpulse"]);
newMoves("dhelmise", ["flipturn", "superpower"]);
newMoves("meganium", ["calmmind", "dragondance", "rockslide", "solarblade", "weatherball"]);
newMoves("typhlosion", ["explosion", "headcharge", "honeclaws", "morningsun", "rapidspin"]);
newMoves("feraligatr", ["darkpulse", "firefang", "suckerpunch", "thunderfang"]);
newMoves("regice", ["teleport", "freezedry"]);
newMoves("magcargo", ["firelash", "energyball"]);
newMoves("bastiodon", ["earthpower"]);
newMoves("leavanny", ["appleacid", "lunge", "thunderouskick", "quiverdance"]);
newMoves("parasect", ["junglehealing", "taunt"]);
newMoves("samurott", ["flipturn", "psychocut", "slackoff"]);
newMoves("meowstic", ["brickbreak", "foulplay", "knockoff", "partingshot", "pursuit"]);
newMoves("meowsticf", ["dazzlinggleam", "drainingkiss", "moonblast", "mysticalfire"]);
newMoves("starmie", ["calmmind", "futuresight", "followme", "moonblast", "storedpower"]);
newMoves("delibird", ["celebrate", "healingwish", "roost", "swordsdance", "uturn", "wish"]);
newMoves("sawsbuck", ["moonblast", "petalblizzard", "playrough"]);
newMoves("sawsbucksummer", ["flameburst", "flamethrower", "growth", "leafstorm", "overheat"]);
newMoves("sawsbuckautumn", ["petalblizzard", "poltergeist", "shadowsneak", "strengthsap", "trickortreat"]);
newMoves("sawsbuckwinter", ["blizzard", "freezedry", "icebeam", "iceshard", "iciclecrash"]);
newMoves("flygon", ["extremespeed", "flashcannon", "ironhead"]);
newMoves("drapion", ["shoreup"]);
newMoves("lurantis", ["moonblast", "moonlight", "playrough", "silverwind"]);
newMoves("exploud", ["clangingscales", "dragonpulse", "snarl"]);
newMoves("noivern", ["encore", "psyshock"]);
newMoves("toxtricity", ["frustration", "gearup", "hiddenpower"]);
newMoves("toxtricitylowkey", ["hiddenpower", "return", "slackoff"]);
newMoves("cacturne", ["assurance", "knockoff", "strengthsap"]);
newMoves("hawlucha", ["partingshot", "stormthrow"]);
newMoves("araquanid", ["hypnosis", "lifedew", "painsplit", "purify"]);
newMoves("zoroark", ["focuspunch", "gunkshot", "superpower"]);
newMoves("delphox", ["recover", "speedswap", "teleport"]);
newMoves("wishiwashi", ["lifedew", "wish"]);
newMoves("falinks", ["aurasphere", "flameburst", "flashcannon", "kingsshield", "thunder"]);
newMoves("floatzel", ["coaching", "flipturn"]);
newMoves("simisear", ["calmmind", "dazzlinggleam", "drainingkiss", "mysticalfire", "playrough", "slackoff"]);
newMoves("krookodile", ["partingshot", "topsyturvy"]);
newMoves("torterra", ["bodypress", "gravapple", "meteorbeam"]);
newMoves("empoleon", ["flipturn", "haze", "originpulse", "roost"]);
},
actions: {
canMegaEvo(pokemon) {
const altForme = pokemon.baseSpecies.otherFormes && this.dex.getSpecies(pokemon.baseSpecies.otherFormes[0]);
const item = pokemon.getItem();
if (
altForme?.isMega && altForme?.requiredMove &&
pokemon.baseMoves.includes(this.battle.toID(altForme.requiredMove)) && !item.zMove
) {
return altForme.name;
}
if (item.name === "Lycanite" && pokemon.baseSpecies.name === "Lycanroc-Midnight") {
return "Lycanroc-Midnight-Mega";
}
if (item.name === "Lycanite" && pokemon.baseSpecies.name === "Lycanroc-Dusk") {
return "Lycanroc-Dusk-Mega";
}
if (item.name === "Raichunite" && pokemon.baseSpecies.name === "Raichu-Alola") {
return null;
}
if (item.name === "Slowbronite" && pokemon.baseSpecies.name === "Slowbro-Galar") {
return null;
}
if (item.name === "Slowkinite" && pokemon.baseSpecies.name === "Slowking-Galar") {
return null;
}
if (item.name === "Gourgeite" && pokemon.baseSpecies.name === "Gourgeist-Small") {
return "Gourgeist-Small-Mega";
}
if (item.name === "Gourgeite" && pokemon.baseSpecies.name === "Gourgeist-Large") {
return "Gourgeist-Large-Mega";
}
if (item.name === "Gourgeite" && pokemon.baseSpecies.name === "Gourgeist-Super") {
return "Gourgeist-Super-Mega";
}
if (item.name === "Reginite" && pokemon.baseSpecies.name === "Regice") {
return "Regice-Mega";
}
if (item.name === "Reginite" && pokemon.baseSpecies.name === "Registeel") {
return "Registeel-Mega";
}
if (item.name === "Meowsticite" && pokemon.baseSpecies.name === "Meowstic-F") {
return "Meowstic-F-Mega";
}
if (item.name === "Sawsbuckite" && pokemon.baseSpecies.name === "Delibird") {
return "Delibird-Mega-Festive-Rider";
}
if (item.name === "Sawsbuckite" && pokemon.baseSpecies.name === "Sawsbuck-Summer") {
return "Sawsbuck-Summer-Mega";
}
if (item.name === "Sawsbuckite" && pokemon.baseSpecies.name === "Sawsbuck-Autumn") {
return "Sawsbuck-Autumn-Mega";
}
if (item.name === "Sawsbuckite" && pokemon.baseSpecies.name === "Sawsbuck-Winter") {
return "Sawsbuck-Winter-Mega";
}
if (item.name === "Toxtricitite" && pokemon.baseSpecies.name === "Toxtricity-Low-Key") {
return "Toxtricity-Low-Key-Mega";
}
if (item.name === "Ninetalesite" && pokemon.baseSpecies.name === "Ninetales") {
return null;
}
if (item.name === "Dugtrionite" && pokemon.baseSpecies.name === "Dugtrio-Alola") {
return null;
}
if (item.megaEvolves !== pokemon.baseSpecies.name || item.megaStone === pokemon.species.name) {
return null;
}
return item.megaStone;
},
runMegaEvo(pokemon) {
const speciesid = pokemon.canMegaEvo || pokemon.canUltraBurst;
if (!speciesid) return false;
// Pokémon affected by Sky Drop cannot mega evolve. Enforce it here for now.
for (const foeActive of pokemon.foes()) {
if (foeActive.volatiles['skydrop']?.source === pokemon) {
return false;
}
}
if (pokemon.illusion) {
this.battle.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon);
} // only part that's changed
pokemon.formeChange(speciesid, pokemon.getItem(), true);
// Limit one mega evolution
const wasMega = pokemon.canMegaEvo;
for (const ally of pokemon.alliesAndSelf()) {
if (wasMega) {
ally.canMegaEvo = null;
} else {
ally.canUltraBurst = null;
}
}
this.battle.runEvent('AfterMega', pokemon);
return true;
},
getDamage(
pokemon: Pokemon, target: Pokemon, move: string | number | ActiveMove,
suppressMessages = false
): number | undefined | null | false {
if (typeof move === 'string') move = this.dex.getActiveMove(move);
if (typeof move === 'number') {
const basePower = move;
move = new Dex.Move({
basePower,
type: '???',
category: 'Physical',
willCrit: false,
}) as ActiveMove;
move.hit = 0;
}
if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) {
if (!target.runImmunity(move.type, !suppressMessages)) {
return false;
}
}
if (move.ohko) return target.maxhp;
if (move.damageCallback) return move.damageCallback.call(this.battle, pokemon, target);
if (move.damage === 'level') {
return pokemon.level;
} else if (move.damage) {
return move.damage;
}
const category = this.battle.getCategory(move);
const defensiveCategory = move.defensiveCategory || category;
let basePower: number | false | null = move.basePower;
if (move.basePowerCallback) {
basePower = move.basePowerCallback.call(this.battle, pokemon, target, move);
}
if (!basePower) return basePower === 0 ? undefined : basePower;
basePower = this.battle.clampIntRange(basePower, 1);
let critMult;
let critRatio = this.battle.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0);
if (this.battle.gen <= 5) {
critRatio = this.battle.clampIntRange(critRatio, 0, 5);
critMult = [0, 16, 8, 4, 3, 2];
} else {
critRatio = this.battle.clampIntRange(critRatio, 0, 4);
if (this.battle.gen === 6) {
critMult = [0, 16, 8, 2, 1];
} else {
critMult = [0, 24, 8, 2, 1];
}
}
const moveHit = target.getMoveHitData(move);
moveHit.crit = move.willCrit || false;
if (move.willCrit === undefined && critRatio) {
moveHit.crit = this.battle.randomChance(1, critMult[critRatio]);
}
if (moveHit.crit) {
moveHit.crit = this.battle.runEvent('CriticalHit', target, null, move);
}
// happens after crit calculation
basePower = this.battle.runEvent('BasePower', pokemon, target, move, basePower, true);
if (!basePower) return 0;
basePower = this.battle.clampIntRange(basePower, 1);
const level = pokemon.level;
const attacker = pokemon;
const defender = target;
let attackStat: StatNameExceptHP = category === 'Physical' ? 'atk' : 'spa';
const defenseStat: StatNameExceptHP = defensiveCategory === 'Physical' ? 'def' : 'spd';
if (move.useSourceDefensiveAsOffensive) {
attackStat = defenseStat;
// Body press really wants to use the def stat,
// so it switches stats to compensate for Wonder Room.
// Of course, the game thus miscalculates the boosts...
if ('wonderroom' in this.battle.field.pseudoWeather) {
if (attackStat === 'def') {
attackStat = 'spd';
} else if (attackStat === 'spd') {
attackStat = 'def';
}
if (attacker.boosts['def'] || attacker.boosts['spd']) {
this.battle.hint("Body Press uses Sp. Def boosts when Wonder Room is active.");
}
}
}
if ((move as any).settleBoosted) {
attackStat = 'atk';
}
const statTable = {atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe'};
let attack;
let defense;
let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat];
if ((move as any).bodyofwaterBoosted) {
if (attackStat === 'def') {
atkBoosts = attacker.boosts['atk'];
} else if (attackStat === 'spd') {
atkBoosts = attacker.boosts['spa'];
}
}
let defBoosts = defender.boosts[defenseStat];
let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive;
let ignorePositiveDefensive = !!move.ignorePositiveDefensive;
if (moveHit.crit) {
ignoreNegativeOffensive = true;
ignorePositiveDefensive = true;
}
const ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0));
const ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0));
if (ignoreOffensive) {
this.battle.debug('Negating (sp)atk boost/penalty.');
atkBoosts = 0;
}
if (ignoreDefensive) {
this.battle.debug('Negating (sp)def boost/penalty.');
defBoosts = 0;
}
if (move.useTargetOffensive) {
attack = defender.calculateStat(attackStat, atkBoosts);
} else {
attack = attacker.calculateStat(attackStat, atkBoosts);
}
attackStat = (category === 'Physical' ? 'atk' : 'spa');
defense = defender.calculateStat(defenseStat, defBoosts);
// Apply Stat Modifiers
attack = this.battle.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack);
defense = this.battle.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense);
if (this.battle.gen <= 4 && ['explosion', 'selfdestruct'].includes(move.id) && defenseStat === 'def') {
defense = this.battle.clampIntRange(Math.floor(defense / 2), 1);
}
const tr = this.battle.trunc;
// int(int(int(2 * L / 5 + 2) * A * P / D) / 50);
const baseDamage = tr(tr(tr(tr(2 * level / 5 + 2) * basePower * attack) / defense) / 50);
// Calculate damage modifiers separately (order differs between generations)
return this.modifyDamage(baseDamage, pokemon, target, move, suppressMessages);
},
},
pokemon: {
lostItemForDelibird: null,
setItem(item: string | Item, source?: Pokemon, effect?: Effect) {
if (!this.hp) return false;
if (typeof item === 'string') item = this.battle.dex.getItem(item);
const effectid = this.battle.effect ? this.battle.effect.id : '';
const RESTORATIVE_BERRIES = new Set([
'leppaberry', 'aguavberry', 'enigmaberry', 'figyberry', 'iapapaberry', 'magoberry', 'sitrusberry', 'wikiberry', 'oranberry',
] as ID[]);
if (RESTORATIVE_BERRIES.has('leppaberry' as ID)) {
const inflicted = ['trick', 'switcheroo'].includes(effectid);
const external = inflicted && source && source.side.id !== this.side.id;
this.pendingStaleness = external ? 'external' : 'internal';
} else {
this.pendingStaleness = undefined;
}
this.item = item.id;
this.itemData = {id: item.id, target: this};
if (item.id) {
this.battle.singleEvent('Start', item, this.itemData, this, source, effect);
}
return true;
},
isGrounded(negateImmunity = false) {
if ('gravity' in this.battle.field.pseudoWeather) return true;
if ('ingrain' in this.volatiles && this.battle.gen >= 4) return true;
if ('smackdown' in this.volatiles) return true;
const item = (this.ignoringItem() ? '' : this.item);
if (item === 'ironball') return true;
// If a Fire/Flying type uses Burn Up and Roost, it becomes ???/Flying-type, but it's still grounded.
if (!negateImmunity && this.hasType('Flying') && !('roost' in this.volatiles)) return false;
if (this.hasAbility('levitate') && !this.battle.suppressingAttackEvents()) return null;
if ('magnetrise' in this.volatiles) return false;
if ('telekinesis' in this.volatiles) return false;
if ('poolfloaties' in this.volatiles) return false;
return item !== 'airballoon';
},
},
};