mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
179 lines
7.0 KiB
TypeScript
179 lines
7.0 KiB
TypeScript
import { RandomGen7Teams, ZeroAttackHPIVs } from '../gen7/teams';
|
|
|
|
const POWERFUL_ABILITIES = [
|
|
'Adaptability', 'Aftermath', 'Analytic', 'Arena Trap', 'Beast Boost', 'Berserk', 'Comatose', 'Competitive',
|
|
'Compound Eyes', 'Contrary', 'Cursed Body', 'Defiant', 'Desolate Land', 'Download', 'Drizzle', 'Drought', 'Dry Skin',
|
|
'Effect Spore', 'Filter', 'Flame Body', 'Flash Fire', 'Fluffy', 'Fur Coat', 'Gooey', 'Grassy Surge', 'Heatproof',
|
|
'Huge Power', 'Hustle', 'Illusion', 'Imposter', 'Innards Out', 'Intimidate', 'Iron Barbs', 'Levitate', 'Lightning Rod',
|
|
'Magic Bounce', 'Magic Guard', 'Marvel Scale', 'Mold Breaker', 'Moody', 'Motor Drive', 'Moxie', 'Multiscale',
|
|
'Natural Cure', 'Neuroforce', 'Parental Bond', 'Poison Point', 'Poison Touch', 'Prankster', 'Primordial Sea', 'Prism Armor',
|
|
'Protean', 'Pure Power', 'Regenerator', 'Rough Skin', 'Sand Stream', 'Sap Sipper', 'Serene Grace',
|
|
'Shadow Shield', 'Shadow Tag', 'Shed Skin', 'Sheer Force', 'Shield Dust', 'Simple', 'Snow Warning', 'Solid Rock',
|
|
'Soul-Heart', 'Speed Boost', 'Stakeout', 'Stamina', 'Static', 'Storm Drain', 'Sturdy', 'Tangling Hair', 'Technician',
|
|
'Teravolt', 'Thick Fat', 'Tinted Lens', 'Tough Claws', 'Trace', 'Triage', 'Turboblaze', 'Unaware', 'Volt Absorb',
|
|
'Water Absorb', 'Water Bubble', 'Wonder Guard',
|
|
];
|
|
|
|
export class RandomGen7ApexTeams extends RandomGen7Teams {
|
|
override randomSet(
|
|
species: string | Species,
|
|
teamDetails: RandomTeamsTypes.TeamDetails = {},
|
|
isLead = false
|
|
): RandomTeamsTypes.RandomSet {
|
|
species = this.dex.species.get(species);
|
|
const forme = this.getForme(species);
|
|
const sets = this.randomSets[species.id]["sets"];
|
|
const possibleSets = [];
|
|
// Check if the Pokemon has a Z-Move user set
|
|
let canZMove = false;
|
|
for (const set of sets) {
|
|
if (!teamDetails.zMove && set.role === 'Z-Move user') canZMove = true;
|
|
}
|
|
for (const set of sets) {
|
|
// Prevent multiple Z-Move users
|
|
if (teamDetails.zMove && set.role === 'Z-Move user') continue;
|
|
// Prevent Setup Sweeper and Bulky Setup if Z-Move user is available
|
|
if (canZMove && ['Setup Sweeper', 'Bulky Setup'].includes(set.role)) continue;
|
|
possibleSets.push(set);
|
|
}
|
|
const set = this.sampleIfArray(possibleSets);
|
|
const role = set.role;
|
|
const movePool: string[] = Array.from(set.movepool);
|
|
const preferredTypes = set.preferredTypes;
|
|
const preferredType = this.sampleIfArray(preferredTypes) || '';
|
|
|
|
let ability = '';
|
|
let item = undefined;
|
|
|
|
const evs = { hp: 85, atk: 85, def: 85, spa: 85, spd: 85, spe: 85 };
|
|
const ivs = { hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31 };
|
|
|
|
const types = species.types;
|
|
const baseAbilities = set.abilities!;
|
|
// Use the mega's ability for moveset generation
|
|
const abilities = (species.battleOnly && !species.requiredAbility) ? Object.values(species.abilities) : baseAbilities;
|
|
|
|
// Get moves
|
|
const moves = this.randomMoveset(types, abilities, teamDetails, species, isLead, movePool,
|
|
preferredType, role);
|
|
const counter = this.newQueryMoves(moves, species, preferredType, abilities);
|
|
|
|
// Get ability
|
|
ability = this.getAbility(new Set(types), moves, baseAbilities, counter, movePool, teamDetails, species,
|
|
preferredType, role);
|
|
|
|
// Get items
|
|
item = this.getPriorityItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role);
|
|
if (item === undefined) {
|
|
item = this.getItem(ability, types, moves, counter, teamDetails, species, isLead, preferredType, role);
|
|
}
|
|
|
|
// For Trick / Switcheroo
|
|
if (item === 'Leftovers' && types.includes('Poison')) {
|
|
item = 'Black Sludge';
|
|
}
|
|
|
|
const level = this.getLevel(species);
|
|
|
|
// Minimize confusion damage, including if Foul Play is its only physical attack
|
|
if (
|
|
(!counter.get('Physical') || (counter.get('Physical') <= 1 && (moves.has('foulplay') || moves.has('rapidspin')))) &&
|
|
!moves.has('copycat') && !moves.has('transform')
|
|
) {
|
|
evs.atk = 0;
|
|
ivs.atk = 0;
|
|
}
|
|
|
|
// Override regular ability with a random "powerful" ability
|
|
ability = this.sample(POWERFUL_ABILITIES);
|
|
|
|
if (ability === 'Beast Boost' && !counter.get('Special')) {
|
|
evs.spa = 0;
|
|
ivs.spa = 0;
|
|
}
|
|
|
|
// We use a special variable to track Hidden Power
|
|
// so that we can check for all Hidden Powers at once
|
|
let hasHiddenPower = false;
|
|
for (const move of moves) {
|
|
if (move.startsWith('hiddenpower')) hasHiddenPower = true;
|
|
}
|
|
|
|
// Fix IVs for non-Bottle Cap-able sets
|
|
if (hasHiddenPower && level < 100) {
|
|
let hpType;
|
|
for (const move of moves) {
|
|
if (move.startsWith('hiddenpower')) hpType = move.substr(11);
|
|
}
|
|
if (!hpType) throw new Error(`hasHiddenPower is true, but no Hidden Power move was found.`);
|
|
const HPivs = ivs.atk === 0 ? ZeroAttackHPIVs[hpType] : this.dex.types.get(hpType).HPivs;
|
|
let iv: StatID;
|
|
for (iv in HPivs) {
|
|
ivs[iv] = HPivs[iv]!;
|
|
}
|
|
}
|
|
|
|
// Prepare optimal HP
|
|
const srImmunity = ability === 'Magic Guard';
|
|
const srWeakness = srImmunity ? 0 : this.dex.getEffectiveness('Rock', species);
|
|
while (evs.hp > 1) {
|
|
const hp = Math.floor(Math.floor(2 * species.baseStats.hp + ivs.hp + Math.floor(evs.hp / 4) + 100) * level / 100 + 10);
|
|
if (moves.has('substitute') && !['Black Sludge', 'Leftovers'].includes(item)) {
|
|
if (item === 'Sitrus Berry') {
|
|
// Two Substitutes should activate Sitrus Berry
|
|
if (hp % 4 === 0) break;
|
|
} else {
|
|
// Should be able to use Substitute four times from full HP without fainting
|
|
if (hp % 4 > 0) break;
|
|
}
|
|
} else if (moves.has('bellydrum') && (item === 'Sitrus Berry')) {
|
|
// Belly Drum should activate Sitrus Berry
|
|
if (hp % 2 === 0) break;
|
|
} else if (['highjumpkick', 'jumpkick'].some(m => moves.has(m))) {
|
|
// Crash damage move users want an odd HP to survive two misses
|
|
if (hp % 2 > 0) break;
|
|
} else {
|
|
// Maximize number of Stealth Rock switch-ins
|
|
if (srWeakness <= 0 || ability === 'Regenerator') break;
|
|
if (srWeakness === 1 && ['Black Sludge', 'Leftovers', 'Life Orb'].includes(item)) break;
|
|
if (item !== 'Sitrus Berry' && hp % (4 / srWeakness) > 0) break;
|
|
// Minimise number of Stealth Rock switch-ins to activate Sitrus Berry
|
|
if (item === 'Sitrus Berry' && hp % (4 / srWeakness) === 0) break;
|
|
}
|
|
evs.hp -= 4;
|
|
}
|
|
|
|
if (['gyroball', 'metalburst', 'trickroom'].some(m => moves.has(m))) {
|
|
evs.spe = 0;
|
|
ivs.spe = (hasHiddenPower && level < 100) ? ivs.spe - 30 : 0;
|
|
}
|
|
|
|
// shuffle moves to add more randomness to camomons
|
|
const shuffledMoves = Array.from(moves);
|
|
this.prng.shuffle(shuffledMoves);
|
|
|
|
// Z-Conversion Porygon-Z should have Shadow Ball first if no Recover, otherwise Thunderbolt
|
|
if (species.id === 'porygonz' && role === 'Z-Move user') {
|
|
const firstMove = (moves.has('shadowball') ? 'shadowball' : 'thunderbolt');
|
|
this.fastPop(shuffledMoves, shuffledMoves.indexOf(firstMove));
|
|
shuffledMoves.unshift(firstMove);
|
|
}
|
|
|
|
return {
|
|
name: species.baseSpecies,
|
|
species: forme,
|
|
gender: species.baseSpecies === 'Greninja' ? 'M' : species.gender,
|
|
shiny: this.randomChance(1, 1024),
|
|
level,
|
|
moves: shuffledMoves,
|
|
ability,
|
|
evs,
|
|
ivs,
|
|
item,
|
|
role,
|
|
};
|
|
}
|
|
}
|
|
|
|
export default RandomGen7ApexTeams;
|