mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Add March 2026 Randomized Format Spotlight (#11792)
* Add March 2026 Randomized Format Spotlight * Make message clearer ( I think) * lint * Move to gen9/teams.ts * Move to gen9/teams.ts pt 2 * done * lint * Prevent mons from being harmed by Godly Gift stat passing
This commit is contained in:
parent
d57d100065
commit
802c1c1d99
|
|
@ -488,11 +488,13 @@ export const Formats: import('../sim/dex-formats').FormatList = [
|
|||
column: 2,
|
||||
},
|
||||
{
|
||||
name: "[Gen 9] BSS Factory (Bo3)",
|
||||
desc: `Randomized 3v3 Singles featuring Pokémon and movesets popular in Battle Stadium Singles.`,
|
||||
mod: 'gen9',
|
||||
team: 'randomBSSFactory',
|
||||
ruleset: ['Flat Rules', 'VGC Timer', 'Best of = 3'],
|
||||
name: "[Gen 9] Godly Gift Random Battle",
|
||||
desc: `Each Pokémon receives one base stat from the God in the first slot depending on its position in the team.`,
|
||||
team: 'randomGodlyGift',
|
||||
ruleset: ['[Gen 9] Random Battle', 'Godly Gift Mod', 'Team Preview'],
|
||||
onBegin() {
|
||||
this.add(`raw|<div class="broadcast-blue"><b>In this format, the "God" in the first slot has "gifted" (shared) its base attack to the Pokémon in the second slot, defense to the one in the third slot, etc."`);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "[Gen 9] Linked",
|
||||
|
|
|
|||
|
|
@ -1920,6 +1920,216 @@ export class RandomTeams {
|
|||
return pokemon;
|
||||
}
|
||||
|
||||
randomGodlyGiftTeam() {
|
||||
this.enforceNoDirectCustomBanlistChanges();
|
||||
|
||||
const seed = this.prng.getSeed();
|
||||
const ruleTable = this.dex.formats.getRuleTable(this.format);
|
||||
const pokemon: RandomTeamsTypes.RandomSet[] = [];
|
||||
|
||||
// For Monotype
|
||||
const isMonotype = !!this.forceMonotype || ruleTable.has('sametypeclause');
|
||||
const isDoubles = this.format.gameType !== 'singles';
|
||||
const typePool = this.dex.types.names().filter(name => name !== "Stellar");
|
||||
const type = this.forceMonotype || this.sample(typePool);
|
||||
|
||||
// PotD stuff
|
||||
const usePotD = global.Config && Config.potd && ruleTable.has('potd');
|
||||
const potd = usePotD ? this.dex.species.get(Config.potd) : null;
|
||||
|
||||
const baseFormes: { [k: string]: number } = {};
|
||||
|
||||
const typeCount: { [k: string]: number } = {};
|
||||
const typeComboCount: { [k: string]: number } = {};
|
||||
const typeWeaknesses: { [k: string]: number } = {};
|
||||
const typeDoubleWeaknesses: { [k: string]: number } = {};
|
||||
const teamDetails: RandomTeamsTypes.TeamDetails = {};
|
||||
let numMaxLevelPokemon = 0;
|
||||
|
||||
const pokemonList = isDoubles ? Object.keys(this.randomDoublesSets) : Object.keys(this.randomSets);
|
||||
// God Pokemon are those with at least 510 BST, not including HP
|
||||
const godPokemonList = pokemonList.filter(poke => {
|
||||
const baseStats = this.dex.species.get(poke).baseStats;
|
||||
return baseStats.atk + baseStats.def + baseStats.spa + baseStats.spd + baseStats.spe >= 510;
|
||||
});
|
||||
const [pokemonPool, baseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, pokemonList);
|
||||
const [godPokemonPool, godBaseSpeciesPool] = this.getPokemonPool(type, pokemon, isMonotype, godPokemonList);
|
||||
|
||||
while (baseSpeciesPool.length && pokemon.length < this.maxTeamSize) {
|
||||
let baseSpecies, species;
|
||||
// Generate a God Pokemon in the lead slot
|
||||
if (pokemon.length === 0) {
|
||||
baseSpecies = this.sampleNoReplace(godBaseSpeciesPool);
|
||||
species = this.dex.species.get(this.sample(godPokemonPool[baseSpecies]));
|
||||
} else {
|
||||
baseSpecies = this.sampleNoReplace(baseSpeciesPool);
|
||||
species = this.dex.species.get(this.sample(pokemonPool[baseSpecies]));
|
||||
|
||||
// Ensure that the species isn't harmed by Godly Gift stat passing
|
||||
if (pokemon.length < 6) {
|
||||
const s: StatID[] = ["hp", "atk", "def", "spa", "spd", "spe"];
|
||||
const passedStatName = s[pokemon.length];
|
||||
const passedStat = (this.dex.species.get(pokemon[0].speciesId).baseStats[passedStatName]);
|
||||
// If Deoxys-Attack is the god, just make sure the def/spd stat is <= 50
|
||||
if (Math.max(passedStat, 50) < species.baseStats[passedStatName]) continue;
|
||||
}
|
||||
}
|
||||
if (!species.exists) continue;
|
||||
|
||||
// Limit to one of each species (Species Clause)
|
||||
if (baseFormes[species.baseSpecies]) continue;
|
||||
|
||||
// Treat Ogerpon formes and Terapagos like the Tera Blast user role; reject if team has one already
|
||||
if (['ogerpon', 'ogerponhearthflame', 'terapagos'].includes(species.id) && teamDetails.teraBlast) continue;
|
||||
|
||||
// Illusion shouldn't be on the last slot
|
||||
if (species.baseSpecies === 'Zoroark' && pokemon.length >= (this.maxTeamSize - 1)) continue;
|
||||
|
||||
const types = species.types;
|
||||
const typeCombo = types.slice().sort().join();
|
||||
const weakToFreezeDry = (
|
||||
this.dex.getEffectiveness('Ice', species) > 0 ||
|
||||
(this.dex.getEffectiveness('Ice', species) > -2 && types.includes('Water'))
|
||||
);
|
||||
// Dynamically scale limits for different team sizes. The default and minimum value is 1.
|
||||
const limitFactor = Math.round(this.maxTeamSize / 6) || 1;
|
||||
|
||||
if (!isMonotype && !this.forceMonotype) {
|
||||
let skip = false;
|
||||
|
||||
// Limit two of any type
|
||||
for (const typeName of types) {
|
||||
if (typeCount[typeName] >= 2 * limitFactor) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (skip) continue;
|
||||
|
||||
// Limit three weak to any type, and one double weak to any type
|
||||
for (const typeName of this.dex.types.names()) {
|
||||
// it's weak to the type
|
||||
if (this.dex.getEffectiveness(typeName, species) > 0) {
|
||||
if (!typeWeaknesses[typeName]) typeWeaknesses[typeName] = 0;
|
||||
if (typeWeaknesses[typeName] >= 3 * limitFactor) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (this.dex.getEffectiveness(typeName, species) > 1) {
|
||||
if (!typeDoubleWeaknesses[typeName]) typeDoubleWeaknesses[typeName] = 0;
|
||||
if (typeDoubleWeaknesses[typeName] >= limitFactor) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (skip) continue;
|
||||
|
||||
// Count Dry Skin/Fluffy as Fire weaknesses
|
||||
if (
|
||||
this.dex.getEffectiveness('Fire', species) === 0 &&
|
||||
Object.values(species.abilities).filter(a => ['Dry Skin', 'Fluffy'].includes(a)).length
|
||||
) {
|
||||
if (!typeWeaknesses['Fire']) typeWeaknesses['Fire'] = 0;
|
||||
if (typeWeaknesses['Fire'] >= 3 * limitFactor) continue;
|
||||
}
|
||||
|
||||
// Limit four weak to Freeze-Dry
|
||||
if (weakToFreezeDry) {
|
||||
if (!typeWeaknesses['Freeze-Dry']) typeWeaknesses['Freeze-Dry'] = 0;
|
||||
if (typeWeaknesses['Freeze-Dry'] >= 4 * limitFactor) continue;
|
||||
}
|
||||
|
||||
// Limit one level 100 Pokemon
|
||||
if (!this.adjustLevel && (this.getLevel(species, isDoubles) === 100) && numMaxLevelPokemon >= limitFactor) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check compatibility with team
|
||||
if (!this.getPokemonCompatibility(species, pokemon, isDoubles)) continue;
|
||||
}
|
||||
|
||||
// Limit three of any type combination in Monotype
|
||||
if (!this.forceMonotype && isMonotype && (typeComboCount[typeCombo] >= 3 * limitFactor)) continue;
|
||||
|
||||
// The Pokemon of the Day
|
||||
if (potd?.exists && (pokemon.length === 1 || this.maxTeamSize === 1)) species = potd;
|
||||
|
||||
const set = this.randomSet(species, teamDetails, false, isDoubles);
|
||||
pokemon.push(set);
|
||||
|
||||
// Don't bother tracking details for the last Pokemon
|
||||
if (pokemon.length === this.maxTeamSize) break;
|
||||
|
||||
// Now that our Pokemon has passed all checks, we can increment our counters
|
||||
baseFormes[species.baseSpecies] = 1;
|
||||
|
||||
// Increment type counters
|
||||
for (const typeName of types) {
|
||||
if (typeName in typeCount) {
|
||||
typeCount[typeName]++;
|
||||
} else {
|
||||
typeCount[typeName] = 1;
|
||||
}
|
||||
}
|
||||
if (typeCombo in typeComboCount) {
|
||||
typeComboCount[typeCombo]++;
|
||||
} else {
|
||||
typeComboCount[typeCombo] = 1;
|
||||
}
|
||||
|
||||
// Increment weakness counter
|
||||
for (const typeName of this.dex.types.names()) {
|
||||
// it's weak to the type
|
||||
if (this.dex.getEffectiveness(typeName, species) > 0) {
|
||||
typeWeaknesses[typeName]++;
|
||||
}
|
||||
if (this.dex.getEffectiveness(typeName, species) > 1) {
|
||||
typeDoubleWeaknesses[typeName]++;
|
||||
}
|
||||
}
|
||||
// Count Dry Skin/Fluffy as Fire weaknesses
|
||||
if (['Dry Skin', 'Fluffy'].includes(set.ability) && this.dex.getEffectiveness('Fire', species) === 0) {
|
||||
typeWeaknesses['Fire']++;
|
||||
}
|
||||
if (weakToFreezeDry) typeWeaknesses['Freeze-Dry']++;
|
||||
|
||||
// Increment level 100 counter
|
||||
if (set.level === 100) numMaxLevelPokemon++;
|
||||
|
||||
// Track what the team has
|
||||
if (set.ability === 'Drizzle' || set.moves.includes('raindance')) teamDetails.rain = 1;
|
||||
if (set.ability === 'Drought' || set.ability === 'Orichalcum Pulse' || set.moves.includes('sunnyday')) {
|
||||
teamDetails.sun = 1;
|
||||
}
|
||||
if (set.ability === 'Sand Stream') teamDetails.sand = 1;
|
||||
if (set.ability === 'Snow Warning' || set.moves.includes('snowscape') || set.moves.includes('chillyreception')) {
|
||||
teamDetails.snow = 1;
|
||||
}
|
||||
if (set.moves.includes('healbell')) teamDetails.statusCure = 1;
|
||||
if (set.moves.includes('spikes') || set.moves.includes('ceaselessedge')) {
|
||||
teamDetails.spikes = (teamDetails.spikes || 0) + 1;
|
||||
}
|
||||
if (set.moves.includes('toxicspikes') || set.ability === 'Toxic Debris') teamDetails.toxicSpikes = 1;
|
||||
if (set.moves.includes('stealthrock') || set.moves.includes('stoneaxe')) teamDetails.stealthRock = 1;
|
||||
if (set.moves.includes('stickyweb')) teamDetails.stickyWeb = 1;
|
||||
if (set.moves.includes('defog')) teamDetails.defog = 1;
|
||||
if (set.moves.includes('rapidspin') || set.moves.includes('mortalspin')) teamDetails.rapidSpin = 1;
|
||||
if (set.moves.includes('auroraveil') || (set.moves.includes('reflect') && set.moves.includes('lightscreen'))) {
|
||||
teamDetails.screens = 1;
|
||||
}
|
||||
if (set.role === 'Tera Blast user' || ['ogerpon', 'ogerponhearthflame', 'terapagos'].includes(species.id)) {
|
||||
teamDetails.teraBlast = 1;
|
||||
}
|
||||
}
|
||||
if (pokemon.length < this.maxTeamSize && pokemon.length < 12) { // large teams sometimes cannot be built
|
||||
throw new Error(`Could not build a random team for ${this.format} (seed=${seed})`);
|
||||
}
|
||||
|
||||
return pokemon;
|
||||
}
|
||||
|
||||
randomCCTeam(): RandomTeamsTypes.RandomSet[] {
|
||||
this.enforceNoDirectCustomBanlistChanges();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user