mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-06-02 22:08:36 -05:00
Add new OMs of the Month (#3309)
This commit is contained in:
parent
68df2a78ae
commit
6633d606b5
|
|
@ -266,148 +266,48 @@ exports.Formats = [
|
|||
column: 2,
|
||||
},
|
||||
{
|
||||
name: "[Gen 7] Inheritance",
|
||||
name: "[Gen 7] Full Potential",
|
||||
desc: [
|
||||
"Pokémon may use the ability and moves of another, as long as they forfeit their own learnset.",
|
||||
"• <a href=\"https://www.smogon.com/forums/threads/3592844/\">Inheritance</a>",
|
||||
"A Pokémon's highest stat is used when calculating the damage their attacks inflict.",
|
||||
"• <a href=\"https://www.smogon.com/forums/threads/3596777/\">Full Potential</a>",
|
||||
],
|
||||
|
||||
mod: 'gen7',
|
||||
ruleset: ['Pokemon', 'Standard', 'Team Preview'],
|
||||
banlist: ['Uber', 'Kyurem-Black', 'Pheromosa', 'Regigigas', 'Shedinja', 'Slaking', 'Gengarite', 'Kangaskhanite', 'Lucarionite', 'Salamencite', 'Power Construct', 'Shadow Tag', 'Baton Pass'],
|
||||
bannedDonors: ['Araquanid', 'Azumarill', 'Azurill', 'Blaziken', 'Bunnelby', 'Carvanha', 'Chatot', 'Combusken', 'Dewpider', 'Diggersby', 'Diglett', 'Ditto', 'Dugtrio', 'Golett', 'Golurk', 'Liepard', 'Machamp', 'Machoke', 'Machop', 'Marill', 'Medicham', 'Meditite', 'Meowstic', 'Purrloin', 'Scolipede', 'Sharpedo', 'Smeargle', 'Torchic', 'Trapinch', 'Venipede', 'Whirlipede'],
|
||||
noChangeForme: true,
|
||||
noChangeAbility: true,
|
||||
getEvoFamily: function (species) {
|
||||
let template = Tools.getTemplate(species);
|
||||
while (template.prevo) {
|
||||
template = Tools.getTemplate(template.prevo);
|
||||
}
|
||||
return template.speciesid;
|
||||
},
|
||||
validateSet: function (set, teamHas) {
|
||||
if (!this.format.abilityMap) {
|
||||
let abilityMap = Object.create(null);
|
||||
for (let speciesid in this.tools.data.Pokedex) {
|
||||
let pokemon = this.tools.data.Pokedex[speciesid];
|
||||
if (pokemon.num < 1 || pokemon.species in this.format.banlistTable || this.format.bannedDonors.includes(pokemon.species)) continue;
|
||||
if (this.tools.data.FormatsData[speciesid].requiredItem || this.tools.data.FormatsData[speciesid].requiredMove) continue;
|
||||
for (let key in pokemon.abilities) {
|
||||
let abilityId = toId(pokemon.abilities[key]);
|
||||
if (abilityMap[abilityId]) {
|
||||
abilityMap[abilityId][pokemon.evos ? 'push' : 'unshift'](speciesid);
|
||||
} else {
|
||||
abilityMap[abilityId] = [speciesid];
|
||||
}
|
||||
}
|
||||
}
|
||||
this.format.abilityMap = abilityMap;
|
||||
}
|
||||
|
||||
this.format.noChangeForme = false;
|
||||
let problems = this.tools.getFormat('Pokemon').onChangeSet.call(this.tools, set, this.format) || [];
|
||||
this.format.noChangeForme = true;
|
||||
|
||||
if (problems.length) return problems;
|
||||
|
||||
let species = toId(set.species);
|
||||
let template = this.tools.getTemplate(species);
|
||||
if (!template.exists) return [`The Pokemon "${set.species}" does not exist.`];
|
||||
if (template.isUnreleased) return [`${template.species} is unreleased.`];
|
||||
if (template.tier === 'Uber' || template.species in this.format.banlistTable) return [`${template.species} is banned.`];
|
||||
|
||||
let name = set.name;
|
||||
|
||||
let abilityId = toId(set.ability);
|
||||
if (!abilityId || !(abilityId in this.tools.data.Abilities)) return [`${name} needs to have a valid ability.`];
|
||||
let pokemonWithAbility = this.format.abilityMap[abilityId];
|
||||
if (!pokemonWithAbility) return [`"${set.ability}" is not available on a legal Pokemon.`];
|
||||
|
||||
let canonicalSource = ''; // Specific for the basic implementation of Donor Clause (see onValidateTeam).
|
||||
let validSources = set.abilitySources = []; // evolutionary families
|
||||
for (let i = 0; i < pokemonWithAbility.length; i++) {
|
||||
let donorTemplate = this.tools.getTemplate(pokemonWithAbility[i]);
|
||||
let evoFamily = this.format.getEvoFamily(donorTemplate);
|
||||
|
||||
if (validSources.indexOf(evoFamily) >= 0) continue;
|
||||
|
||||
if (set.name === set.species) delete set.name;
|
||||
set.species = donorTemplate.species;
|
||||
problems = this.validateSet(set, teamHas) || [];
|
||||
if (!problems.length) {
|
||||
canonicalSource = donorTemplate.species;
|
||||
validSources.push(evoFamily);
|
||||
}
|
||||
if (validSources.length > 1) {
|
||||
// Specific for the basic implementation of Donor Clause (see onValidateTeam).
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set.species = template.species;
|
||||
if (!validSources.length && pokemonWithAbility.length > 1) {
|
||||
return [`${template.species}'s set is illegal.`];
|
||||
}
|
||||
if (!validSources.length) {
|
||||
problems.unshift(`${template.species} has an illegal set with an ability from ${this.tools.getTemplate(pokemonWithAbility[0]).name}.`);
|
||||
return problems;
|
||||
}
|
||||
|
||||
// Protocol: Include the data of the donor species in the `ability` data slot.
|
||||
// Afterwards, we are going to reset the name to what the user intended. :]
|
||||
set.ability = `${set.ability}0${canonicalSource}`;
|
||||
},
|
||||
onValidateTeam: function (team, format) {
|
||||
// Donor Clause
|
||||
let evoFamilyLists = [];
|
||||
for (let i = 0; i < team.length; i++) {
|
||||
let set = team[i];
|
||||
if (!set.abilitySources) continue;
|
||||
evoFamilyLists.push(set.abilitySources.map(format.getEvoFamily));
|
||||
}
|
||||
|
||||
// Checking actual full incompatibility would require expensive algebra.
|
||||
// Instead, we only check the trivial case of multiple Pokémon only legal for exactly one family. FIXME?
|
||||
// This clause has only gotten more complex over time, so this is probably a won't fix.
|
||||
let requiredFamilies = Object.create(null);
|
||||
for (let i = 0; i < evoFamilyLists.length; i++) {
|
||||
let evoFamilies = evoFamilyLists[i];
|
||||
if (evoFamilies.length !== 1) continue;
|
||||
let [familyId] = evoFamilies;
|
||||
if (!(familyId in requiredFamilies)) requiredFamilies[familyId] = 1;
|
||||
requiredFamilies[familyId]++;
|
||||
if (requiredFamilies[familyId] > 2) return [`You are limited to up to two inheritances from each evolution family by the Donor Clause.`, `(You inherit more than twice from ${this.getTemplate(familyId).species}).`];
|
||||
}
|
||||
},
|
||||
onBegin: function () {
|
||||
for (let pokemon of this.p1.pokemon.concat(this.p2.pokemon)) {
|
||||
if (pokemon.baseAbility.includes('0')) {
|
||||
let donor = pokemon.baseAbility.split('0')[1];
|
||||
pokemon.donor = toId(donor);
|
||||
pokemon.baseAbility = pokemon.baseAbility.split('0')[0];
|
||||
pokemon.ability = pokemon.baseAbility;
|
||||
}
|
||||
}
|
||||
},
|
||||
onSwitchIn: function (pokemon) {
|
||||
if (!pokemon.donor) return;
|
||||
let donorTemplate = this.getTemplate(pokemon.donor);
|
||||
if (!donorTemplate.exists) return;
|
||||
// Place volatiles on the Pokémon to show the donor details.
|
||||
this.add('-start', pokemon, donorTemplate.species, '[silent]');
|
||||
},
|
||||
mod: 'fullpotential',
|
||||
ruleset: ['[Gen 7] OU'],
|
||||
banlist: ['Pheromosa', 'Shuckle', 'Speed Boost'],
|
||||
},
|
||||
{
|
||||
name: "[Gen 7] Mergemons",
|
||||
name: "[Gen 7] Automagic",
|
||||
desc: [
|
||||
"Pokémon gain the movepool of the previous and the next fully evolved Pokémon, according to the Pokédex.",
|
||||
"• <a href=\"https://www.smogon.com/forums/threads/3591780/\">Mergemons</a>",
|
||||
"Whenever an attack's secondary effect is triggered, any setup moves in that Pokémon's movepool are run.",
|
||||
"• <a href=\"https://www.smogon.com/forums/threads/3594333/\">Automagic</a>",
|
||||
],
|
||||
|
||||
mod: 'mergemons',
|
||||
mod: 'automagic',
|
||||
searchShow: false,
|
||||
ruleset: ['[Gen 7] OU'],
|
||||
banlist: [],
|
||||
onAfterSecondaryEffect: function (target, source, move) {
|
||||
let moreSetup = ['bellydrum'];
|
||||
if (!source.types.includes("Ghost")) moreSetup.push("curse");
|
||||
source.baseMoveset.forEach(curmove => {
|
||||
let move = this.getMove(curmove.id);
|
||||
if (moreSetup.includes(move.id) || (move.category === "Status" && move.boosts && move.target === "self")) {
|
||||
this.useMove(move, source);
|
||||
curmove.pp = target.hasAbility("pressure") ? (curmove.pp - 2) : (curmove.pp - 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
onAfterMove: function (source, target, move) {
|
||||
if (move.id !== "genesissupernova") return;
|
||||
source.baseMoveset.forEach(curmove => {
|
||||
let move = this.getMove(curmove.id);
|
||||
if ((move.id === 'bellydrum' || (move.category === "Status" && move.boosts && move.target === "self")) && this.terrain === "psychicterrain") {// This is so that its confirmed that it successfully set PTerrain
|
||||
this.useMove(move, source);
|
||||
curmove.pp = target.hasAbility("pressure") ? (curmove.pp - 2) : (curmove.pp - 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
section: "Other Metagames",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ exports.BattleAliases = {
|
|||
"bss": "[Gen 7] Battle Spot Singles",
|
||||
"bsdoubles": "[Gen 7] Battle Spot Doubles",
|
||||
"bstriples": "Battle Spot Triples",
|
||||
"pokebilities": "[Gen 7] Pokébilities",
|
||||
"balancedhackmons": "[Gen 7] Balanced Hackmons",
|
||||
"bh": "[Gen 7] Balanced Hackmons",
|
||||
"1v1": "[Gen 7] 1v1",
|
||||
|
|
@ -37,6 +36,8 @@ exports.BattleAliases = {
|
|||
"monorandom": "[Gen 7] Monotype Random Battle",
|
||||
"bf": "Battle Factory",
|
||||
"inverse": "Inverse Battle",
|
||||
"fullpotential": "[Gen 7] Full Potential",
|
||||
"automagic": "[Gen 7] Automagic",
|
||||
|
||||
// mega evos
|
||||
"maero": "Aerodactyl-Mega",
|
||||
|
|
|
|||
257
mods/automagic/scripts.js
Normal file
257
mods/automagic/scripts.js
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
'use strict';
|
||||
|
||||
exports.BattleScripts = {
|
||||
moveHit: function (target, pokemon, move, moveData, isSecondary, isSelf) {
|
||||
let damage;
|
||||
move = this.getMoveCopy(move);
|
||||
|
||||
if (!moveData) moveData = move;
|
||||
if (!moveData.flags) moveData.flags = {};
|
||||
let hitResult = true;
|
||||
|
||||
// TryHit events:
|
||||
// STEP 1: we see if the move will succeed at all:
|
||||
// - TryHit, TryHitSide, or TryHitField are run on the move,
|
||||
// depending on move target (these events happen in useMove
|
||||
// or tryMoveHit, not below)
|
||||
// == primary hit line ==
|
||||
// Everything after this only happens on the primary hit (not on
|
||||
// secondary or self-hits)
|
||||
// STEP 2: we see if anything blocks the move from hitting:
|
||||
// - TryFieldHit is run on the target
|
||||
// STEP 3: we see if anything blocks the move from hitting the target:
|
||||
// - If the move's target is a pokemon, TryHit is run on that pokemon
|
||||
|
||||
// Note:
|
||||
// If the move target is `foeSide`:
|
||||
// event target = pokemon 0 on the target side
|
||||
// If the move target is `allySide` or `all`:
|
||||
// event target = the move user
|
||||
//
|
||||
// This is because events can't accept actual sides or fields as
|
||||
// targets. Choosing these event targets ensures that the correct
|
||||
// side or field is hit.
|
||||
//
|
||||
// It is the `TryHitField` event handler's responsibility to never
|
||||
// use `target`.
|
||||
// It is the `TryFieldHit` event handler's responsibility to read
|
||||
// move.target and react accordingly.
|
||||
// An exception is `TryHitSide` as a single event (but not as a normal
|
||||
// event), which is passed the target side.
|
||||
|
||||
if (move.target === 'all' && !isSelf) {
|
||||
hitResult = this.singleEvent('TryHitField', moveData, {}, target, pokemon, move);
|
||||
} else if ((move.target === 'foeSide' || move.target === 'allySide') && !isSelf) {
|
||||
hitResult = this.singleEvent('TryHitSide', moveData, {}, target.side, pokemon, move);
|
||||
} else if (target) {
|
||||
hitResult = this.singleEvent('TryHit', moveData, {}, target, pokemon, move);
|
||||
}
|
||||
if (!hitResult) {
|
||||
if (hitResult === false) this.add('-fail', target);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target && !isSecondary && !isSelf) {
|
||||
if (move.target !== 'all' && move.target !== 'allySide' && move.target !== 'foeSide') {
|
||||
hitResult = this.runEvent('TryPrimaryHit', target, pokemon, moveData);
|
||||
if (hitResult === 0) {
|
||||
// special Substitute flag
|
||||
hitResult = true;
|
||||
target = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target && isSecondary && !moveData.self) {
|
||||
hitResult = true;
|
||||
}
|
||||
if (!hitResult) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target) {
|
||||
let didSomething = false;
|
||||
|
||||
damage = this.getDamage(pokemon, target, moveData);
|
||||
|
||||
// getDamage has several possible return values:
|
||||
//
|
||||
// a number:
|
||||
// means that much damage is dealt (0 damage still counts as dealing
|
||||
// damage for the purposes of things like Static)
|
||||
// false:
|
||||
// gives error message: "But it failed!" and move ends
|
||||
// null:
|
||||
// the move ends, with no message (usually, a custom fail message
|
||||
// was already output by an event handler)
|
||||
// undefined:
|
||||
// means no damage is dealt and the move continues
|
||||
//
|
||||
// basically, these values have the same meanings as they do for event
|
||||
// handlers.
|
||||
|
||||
if ((damage || damage === 0) && !target.fainted) {
|
||||
if (move.noFaint && damage >= target.hp) {
|
||||
damage = target.hp - 1;
|
||||
}
|
||||
damage = this.damage(damage, target, pokemon, move);
|
||||
if (!(damage || damage === 0)) {
|
||||
this.debug('damage interrupted');
|
||||
return false;
|
||||
}
|
||||
didSomething = true;
|
||||
}
|
||||
if (damage === false || damage === null) {
|
||||
if (damage === false && !isSecondary && !isSelf) {
|
||||
this.add('-fail', target);
|
||||
}
|
||||
this.debug('damage calculation interrupted');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (moveData.boosts && !target.fainted) {
|
||||
hitResult = this.boost(moveData.boosts, target, pokemon, move, isSecondary, isSelf);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.heal && !target.fainted) {
|
||||
let d = target.heal((this.gen < 5 ? Math.floor : Math.round)(target.maxhp * moveData.heal[0] / moveData.heal[1]));
|
||||
if (!d && d !== 0) {
|
||||
this.add('-fail', target);
|
||||
this.debug('heal interrupted');
|
||||
return false;
|
||||
}
|
||||
this.add('-heal', target, target.getHealth);
|
||||
didSomething = true;
|
||||
}
|
||||
if (moveData.status) {
|
||||
hitResult = target.trySetStatus(moveData.status, pokemon, moveData.ability ? moveData.ability : move);
|
||||
if (!hitResult && move.status) return hitResult;
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.forceStatus) {
|
||||
hitResult = target.setStatus(moveData.forceStatus, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.volatileStatus) {
|
||||
hitResult = target.addVolatile(moveData.volatileStatus, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.sideCondition) {
|
||||
hitResult = target.side.addSideCondition(moveData.sideCondition, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.weather) {
|
||||
hitResult = this.setWeather(moveData.weather, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.terrain) {
|
||||
hitResult = this.setTerrain(moveData.terrain, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.pseudoWeather) {
|
||||
hitResult = this.addPseudoWeather(moveData.pseudoWeather, pokemon, move);
|
||||
didSomething = didSomething || hitResult;
|
||||
}
|
||||
if (moveData.forceSwitch) {
|
||||
if (this.canSwitch(target.side)) didSomething = true; // at least defer the fail message to later
|
||||
}
|
||||
if (moveData.selfSwitch) {
|
||||
if (this.canSwitch(pokemon.side)) didSomething = true; // at least defer the fail message to later
|
||||
}
|
||||
// Hit events
|
||||
// These are like the TryHit events, except we don't need a FieldHit event.
|
||||
// Scroll up for the TryHit event documentation, and just ignore the "Try" part. ;)
|
||||
hitResult = null;
|
||||
if (move.target === 'all' && !isSelf) {
|
||||
if (moveData.onHitField) hitResult = this.singleEvent('HitField', moveData, {}, target, pokemon, move);
|
||||
} else if ((move.target === 'foeSide' || move.target === 'allySide') && !isSelf) {
|
||||
if (moveData.onHitSide) hitResult = this.singleEvent('HitSide', moveData, {}, target.side, pokemon, move);
|
||||
} else {
|
||||
if (moveData.onHit) hitResult = this.singleEvent('Hit', moveData, {}, target, pokemon, move);
|
||||
if (!isSelf && !isSecondary) {
|
||||
this.runEvent('Hit', target, pokemon, move);
|
||||
}
|
||||
if (moveData.onAfterHit) hitResult = this.singleEvent('AfterHit', moveData, {}, target, pokemon, move);
|
||||
}
|
||||
|
||||
if (!hitResult && !didSomething && !moveData.self && !moveData.selfdestruct) {
|
||||
if (!isSelf && !isSecondary) {
|
||||
if (hitResult === false || didSomething === false) this.add('-fail', target);
|
||||
}
|
||||
this.debug('move failed because it did nothing');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (moveData.self) {
|
||||
let selfRoll;
|
||||
if (!isSecondary && moveData.self.boosts) selfRoll = this.random(100);
|
||||
// This is done solely to mimic in-game RNG behaviour. All self drops have a 100% chance of happening but still grab a random number.
|
||||
if (typeof moveData.self.chance === 'undefined' || selfRoll < moveData.self.chance) {
|
||||
this.moveHit(pokemon, pokemon, move, moveData.self, isSecondary, true);
|
||||
}
|
||||
}
|
||||
if (moveData.secondaries) {
|
||||
let secondaryRoll;
|
||||
let secondaries = this.runEvent('ModifySecondaries', target, pokemon, moveData, moveData.secondaries.slice());
|
||||
for (let i = 0; i < secondaries.length; i++) {
|
||||
secondaryRoll = this.random(100);
|
||||
if (typeof secondaries[i].chance === 'undefined' || secondaryRoll < secondaries[i].chance) {
|
||||
// mod for automagic start
|
||||
let flag = true;
|
||||
if (moveData.secondary.status) flag = moveData.secondary.status !== target.status;
|
||||
if (moveData.secondary.volatileStatus) flag = !(moveData.secondary.volatileStatus in target.volatiles);
|
||||
if (moveData.secondary.volatileStatus === 'flinch') flag = flag && target.activeTurns && !target.moveThisTurn;
|
||||
this.moveHit(target, pokemon, move, secondaries[i], true, isSelf);
|
||||
if (moveData.secondary.self && moveData.secondary.self.boosts) {
|
||||
Object.keys(moveData.secondary.self.boosts).forEach(boost => {
|
||||
if (pokemon.boosts[boost] === 6) flag = false;
|
||||
});
|
||||
} else {
|
||||
flag = flag && !(target.hp === undefined || target.hp <= 0);
|
||||
}
|
||||
if (moveData.target === 'Normal' && moveData.secondary.boosts) {
|
||||
let cantLower = {
|
||||
'atk': ['clearbody', 'fullmetalbody', 'hypercutter', 'whitesmoke'],
|
||||
'def': ['bigpecks', 'clearbody', 'fullmetalbody', 'whitesmoke'],
|
||||
'spa': ['clearbody', 'fullmetalbody', 'whitesmoke'],
|
||||
'spd': ['clearbody', 'fullmetalbody', 'whitesmoke'],
|
||||
'spe': ['clearbody', 'fullmetalbody', 'whitesmoke'],
|
||||
'accuracy': ['clearbody', 'fullmetalbody', 'keeneye', 'whitesmoke'],
|
||||
};
|
||||
for (let k in moveData.secondary.boosts) {
|
||||
if (target.boosts[k] === -6) {
|
||||
flag = false;
|
||||
continue;
|
||||
}
|
||||
if (moveData.secondary.boosts[k] < 0) {
|
||||
for (let j = 0; j < cantLower[k].length; j++) {
|
||||
if (target.hasAbility(cantLower[k][j])) {
|
||||
flag = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pokemon.hasAbility('sheerforce')) flag = false;
|
||||
if (target.hasAbility('shielddust') && !move.ignoreAbility) {
|
||||
flag = false;
|
||||
}
|
||||
if (flag) this.runEvent('AfterSecondaryEffect', target, pokemon, moveData);
|
||||
// mod for automagic end
|
||||
}
|
||||
}
|
||||
}
|
||||
if (target && target.hp > 0 && pokemon.hp > 0 && moveData.forceSwitch && this.canSwitch(target.side)) {
|
||||
hitResult = this.runEvent('DragOut', target, pokemon, move);
|
||||
if (hitResult) {
|
||||
target.forceSwitchFlag = true;
|
||||
} else if (hitResult === false && move.category === 'Status') {
|
||||
this.add('-fail', target);
|
||||
}
|
||||
}
|
||||
if (move.selfSwitch && pokemon.hp) {
|
||||
pokemon.switchFlag = move.selfSwitch;
|
||||
}
|
||||
return damage;
|
||||
},
|
||||
};
|
||||
146
mods/fullpotential/scripts.js
Normal file
146
mods/fullpotential/scripts.js
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
'use strict';
|
||||
|
||||
exports.BattleScripts = {
|
||||
getDamage : function (pokemon, target, move, suppressMessages) {
|
||||
if (typeof move === 'string') move = this.getMove(move);
|
||||
|
||||
if (typeof move === 'number') {
|
||||
move = {
|
||||
basePower: move,
|
||||
type: '???',
|
||||
category: 'Physical',
|
||||
willCrit: false,
|
||||
flags: {},
|
||||
};
|
||||
}
|
||||
|
||||
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, pokemon, target);
|
||||
if (move.damage === 'level') return pokemon.level;
|
||||
if (move.damage) return move.damage;
|
||||
|
||||
if (!move) move = {};
|
||||
if (!move.type) move.type = '???';
|
||||
let type = move.type;
|
||||
let category = this.getCategory(move);
|
||||
let defensiveCategory = move.defensiveCategory || category;
|
||||
|
||||
let basePower = move.basePower;
|
||||
if (move.basePowerCallback) basePower = move.basePowerCallback.call(this, pokemon, target, move);
|
||||
if (!basePower) {
|
||||
if (basePower === 0) return;
|
||||
return basePower;
|
||||
}
|
||||
basePower = this.clampIntRange(basePower, 1);
|
||||
|
||||
let critMult;
|
||||
let critRatio = this.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0);
|
||||
critRatio = this.clampIntRange(critRatio, 0, 4);
|
||||
critMult = [0, 16, 8, 2, 1];
|
||||
|
||||
move.crit = move.willCrit || false;
|
||||
if (move.willCrit === undefined && critRatio) move.crit = (this.random(critMult[critRatio]) === 0);
|
||||
|
||||
if (move.crit) move.crit = this.runEvent('CriticalHit', target, null, move);
|
||||
|
||||
basePower = this.runEvent('BasePower', pokemon, target, move, basePower, true);
|
||||
|
||||
if (!basePower) return 0;
|
||||
basePower = this.clampIntRange(basePower, 1);
|
||||
|
||||
let level = pokemon.level;
|
||||
|
||||
let attacker = pokemon;
|
||||
let defender = target;
|
||||
let statTable = {atk:'Atk', def:'Def', spa:'SpA', spd:'SpD', spe:'Spe'};
|
||||
let attackStat, highestStat = 0;
|
||||
let defenseStat = defensiveCategory === 'Physical' ? 'def' : 'spd';
|
||||
for (let i in statTable) {
|
||||
let stat = attacker.calculateStat(i, attacker.boosts[i]);
|
||||
stat = this.runEvent('Modify' + statTable[i], attacker, defender, move, stat);
|
||||
if (stat > highestStat) {
|
||||
attackStat = i;
|
||||
highestStat = stat;
|
||||
}
|
||||
}
|
||||
let attack;
|
||||
let defense;
|
||||
|
||||
let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat];
|
||||
let defBoosts = move.useSourceDefensive ? attacker.boosts[defenseStat] : defender.boosts[defenseStat];
|
||||
|
||||
let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive;
|
||||
let ignorePositiveDefensive = !!move.ignorePositiveDefensive;
|
||||
|
||||
if (move.crit) {
|
||||
ignoreNegativeOffensive = true;
|
||||
ignorePositiveDefensive = true;
|
||||
}
|
||||
let ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0));
|
||||
let ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0));
|
||||
|
||||
if (ignoreOffensive) atkBoosts = 0;
|
||||
if (ignoreDefensive) defBoosts = 0;
|
||||
|
||||
if (move.useTargetOffensive) {
|
||||
attack = defender.calculateStat(attackStat, atkBoosts);
|
||||
} else {
|
||||
attack = attacker.calculateStat(attackStat, atkBoosts);
|
||||
}
|
||||
|
||||
if (move.useSourceDefensive) {
|
||||
defense = attacker.calculateStat(defenseStat, defBoosts);
|
||||
} else {
|
||||
defense = defender.calculateStat(defenseStat, defBoosts);
|
||||
}
|
||||
|
||||
attack = this.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack);
|
||||
defense = this.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense);
|
||||
|
||||
let baseDamage = Math.floor(Math.floor(Math.floor(2 * level / 5 + 2) * basePower * attack / defense) / 50) + 2;
|
||||
|
||||
baseDamage = this.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
|
||||
|
||||
if (move.crit) baseDamage = this.modify(baseDamage, move.critModifier || (this.gen >= 6 ? 1.5 : 2));
|
||||
|
||||
baseDamage = this.randomizer(baseDamage);
|
||||
|
||||
if (move.hasSTAB || pokemon.hasType(type)) baseDamage = this.modify(baseDamage, move.stab || 1.5);
|
||||
move.typeMod = target.runEffectiveness(move);
|
||||
|
||||
move.typeMod = this.clampIntRange(move.typeMod, -6, 6);
|
||||
if (move.typeMod > 0) {
|
||||
if (!suppressMessages) this.add('-supereffective', target);
|
||||
|
||||
for (let i = 0; i < move.typeMod; i++) {
|
||||
baseDamage *= 2;
|
||||
}
|
||||
}
|
||||
if (move.typeMod < 0) {
|
||||
if (!suppressMessages) this.add('-resisted', target);
|
||||
|
||||
for (let i = 0; i > move.typeMod; i--) {
|
||||
baseDamage = Math.floor(baseDamage / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (move.crit && !suppressMessages) this.add('-crit', target);
|
||||
|
||||
if (pokemon.status === 'brn' && basePower && move.category === 'Physical' && move.id !== 'facade' && !pokemon.hasAbility('guts')) {
|
||||
baseDamage = this.modify(baseDamage, 0.5);
|
||||
}
|
||||
|
||||
baseDamage = this.runEvent('ModifyDamage', pokemon, target, move, baseDamage);
|
||||
|
||||
if (basePower && !Math.floor(baseDamage)) return 1;
|
||||
|
||||
return Math.floor(baseDamage);
|
||||
},
|
||||
};
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
exports.BattleScripts = {
|
||||
init: function () {
|
||||
let learnsets = Object.assign({}, this.data.Learnsets);
|
||||
let dex = [];
|
||||
for (let i in this.data.Pokedex) {
|
||||
if (this.data.Pokedex[i].num <= 0) continue;
|
||||
if (this.data.Pokedex[i].evos) continue;
|
||||
if (!learnsets[i]) continue;
|
||||
if (this.data.FormatsData[i].isUnreleased) continue;
|
||||
if (this.data.FormatsData[i].tier && this.data.FormatsData[i].tier === 'Illegal') continue;
|
||||
dex.push(i);
|
||||
}
|
||||
for (let i = 0; i < dex.length; i++) {
|
||||
let pokemon = dex[i];
|
||||
while (this.data.Pokedex[pokemon].prevo) {
|
||||
pokemon = this.data.Pokedex[pokemon].prevo;
|
||||
}
|
||||
let target = dex[(i === 0 ? dex.length - 1 : i - 1)];
|
||||
while (this.data.Pokedex[pokemon].num === this.data.Pokedex[target].num) {
|
||||
target = dex[dex.indexOf(target) - 1];
|
||||
}
|
||||
let merge = false;
|
||||
do {
|
||||
let learnset = learnsets[target].learnset;
|
||||
for (let move in learnset) {
|
||||
let source = learnset[move][0].charAt(0) + 'L0';
|
||||
if (!(move in learnsets[pokemon].learnset)) this.modData('Learnsets', pokemon).learnset[move] = [source];
|
||||
}
|
||||
if (this.data.Pokedex[target].prevo) {
|
||||
target = this.data.Pokedex[target].prevo;
|
||||
} else if (!merge) {
|
||||
merge = true;
|
||||
target = dex[(i === dex.length - 1 ? 0 : i + 1)];
|
||||
while (this.data.Pokedex[pokemon].num === this.data.Pokedex[target].num) {
|
||||
target = dex[dex.indexOf(target) + 1];
|
||||
}
|
||||
} else {
|
||||
target = null;
|
||||
}
|
||||
} while (target && learnsets[target]);
|
||||
}
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user