From 5724ed4a5cdb367bbffe16694fec6c810651f5e4 Mon Sep 17 00:00:00 2001 From: Leo Kim <47556641+KimJeongSun@users.noreply.github.com> Date: Thu, 12 Sep 2024 23:33:36 +0900 Subject: [PATCH 1/4] [Enhancement] Add option for egg skip to settings (#4193) * add option for egg skip * change eggSkip variable name more descriptive to eggSkipPreference * update requested changes from opaquer and tinylad and walker * update requested change from tinylad * update comment --- src/battle-scene.ts | 7 +++++++ src/locales/en/settings.json | 4 ++++ src/phases/egg-lapse-phase.ts | 10 ++++++---- src/system/settings/settings.ts | 24 ++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 936d0b83253..c9c8d6b788a 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -161,6 +161,13 @@ export default class BattleScene extends SceneBase { public moveAnimations: boolean = true; public expGainsSpeed: integer = 0; public skipSeenDialogues: boolean = false; + /** + * Determines if the egg hatching animation should be skipped + * - 0 = Never (never skip animation) + * - 1 = Ask (ask to skip animation when hatching 2 or more eggs) + * - 2 = Always (automatically skip animation when hatching 2 or more eggs) + */ + public eggSkipPreference: number = 0; /** * Defines the experience gain display mode. diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 301ebea9b2b..ee8a43d7510 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -11,6 +11,10 @@ "expGainsSpeed": "EXP Gains Speed", "expPartyDisplay": "Show EXP Party", "skipSeenDialogues": "Skip Seen Dialogues", + "eggSkip": "Egg Skip", + "never": "Never", + "always": "Always", + "ask": "Ask", "battleStyle": "Battle Style", "enableRetries": "Enable Retries", "hideIvs": "Hide IV scanner", diff --git a/src/phases/egg-lapse-phase.ts b/src/phases/egg-lapse-phase.ts index 1adb1568166..65426846bb3 100644 --- a/src/phases/egg-lapse-phase.ts +++ b/src/phases/egg-lapse-phase.ts @@ -17,14 +17,13 @@ import { EggHatchData } from "#app/data/egg-hatch-data"; export class EggLapsePhase extends Phase { private eggHatchData: EggHatchData[] = []; - private readonly minEggsToPromptSkip: number = 5; + private readonly minEggsToSkip: number = 2; constructor(scene: BattleScene) { super(scene); } start() { super.start(); - const eggsToHatch: Egg[] = this.scene.gameData.eggs.filter((egg: Egg) => { return Overrides.EGG_IMMEDIATE_HATCH_OVERRIDE ? true : --egg.hatchWaves < 1; }); @@ -32,8 +31,7 @@ export class EggLapsePhase extends Phase { this.eggHatchData= []; if (eggsToHatchCount > 0) { - - if (eggsToHatchCount >= this.minEggsToPromptSkip) { + if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 1) { this.scene.ui.showText(i18next.t("battle:eggHatching"), 0, () => { // show prompt for skip this.scene.ui.showText(i18next.t("battle:eggSkipPrompt"), 0); @@ -46,6 +44,10 @@ export class EggLapsePhase extends Phase { } ); }, 100, true); + } else if (eggsToHatchCount >= this.minEggsToSkip && this.scene.eggSkipPreference === 2) { + this.scene.queueMessage(i18next.t("battle:eggHatching")); + this.hatchEggsSkipped(eggsToHatch); + this.showSummary(); } else { // regular hatches, no summary this.scene.queueMessage(i18next.t("battle:eggHatching")); diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index bc88c21e1e1..66021845c29 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -126,6 +126,7 @@ export const SettingKeys = { EXP_Gains_Speed: "EXP_GAINS_SPEED", EXP_Party_Display: "EXP_PARTY_DISPLAY", Skip_Seen_Dialogues: "SKIP_SEEN_DIALOGUES", + Egg_Skip: "EGG_SKIP", Battle_Style: "BATTLE_STYLE", Enable_Retries: "ENABLE_RETRIES", Hide_IVs: "HIDE_IVS", @@ -281,6 +282,26 @@ export const Setting: Array = [ default: 0, type: SettingType.GENERAL }, + { + key: SettingKeys.Egg_Skip, + label: i18next.t("settings:eggSkip"), + options: [ + { + value: "Never", + label: i18next.t("settings:never") + }, + { + value: "Ask", + label: i18next.t("settings:ask") + }, + { + value: "Always", + label: i18next.t("settings:always") + } + ], + default: 1, + type: SettingType.GENERAL + }, { key: SettingKeys.Battle_Style, label: i18next.t("settings:battleStyle"), @@ -727,6 +748,9 @@ export function setSetting(scene: BattleScene, setting: string, value: integer): case SettingKeys.Skip_Seen_Dialogues: scene.skipSeenDialogues = Setting[index].options[value].value === "On"; break; + case SettingKeys.Egg_Skip: + scene.eggSkipPreference = value; + break; case SettingKeys.Battle_Style: scene.battleStyle = value; break; From bfc4f2d1cd954f019cf26f31c4784783725e67f6 Mon Sep 17 00:00:00 2001 From: AJ Fontaine <36677462+Fontbane@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:21:43 -0400 Subject: [PATCH 2/4] [Balance] Tweak trainer evolution delay so trainers are more likely to use evolved Pokemon in later waves (#4190) * Tweak trainer evolution delay * Document getStrengthLevelDiff --- src/data/pokemon-species.ts | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 09448b332e4..3645cb03c60 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -657,6 +657,24 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali return this.getSpeciesForLevel(level, allowEvolving, true, strength, currentWave); } + /** + * @see {@linkcode getSpeciesForLevel} uses an ease in and ease out sine function: + * @see {@link https://easings.net/#easeInSine} + * @see {@link https://easings.net/#easeOutSine} + * Ease in is similar to an exponential function with slower growth, as in, x is directly related to y, and increase in y is higher for higher x. + * Ease out looks more similar to a logarithmic function shifted to the left. It's still a direct relation but it plateaus instead of increasing in growth. + * + * This function is used to calculate the x given to these functions, which is used for evolution chance. + * + * First is maxLevelDiff, which is a denominator for evolution chance for mons without wild evolution delay. + * This means a lower value of x will lead to a higher evolution chance. + * + * It's also used for preferredMinLevel, which is used when an evolution delay exists. + * The calculation with evolution delay is a weighted average of the easeIn and easeOut functions where preferredMinLevel is the denominator. + * This also means a lower value of x will lead to a higher evolution chance. + * @param strength {@linkcode PartyMemberStrength} The strength of the party member in question + * @returns {@linkcode integer} The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance. + */ private getStrengthLevelDiff(strength: PartyMemberStrength): integer { switch (Math.min(strength, PartyMemberStrength.STRONGER)) { case PartyMemberStrength.WEAKEST: @@ -666,9 +684,9 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali case PartyMemberStrength.WEAK: return 20; case PartyMemberStrength.AVERAGE: - return 10; + return 8; case PartyMemberStrength.STRONG: - return 5; + return 4; default: return 0; } @@ -716,7 +734,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali if (strength === PartyMemberStrength.STRONGER) { evolutionChance = 1; } else { - const maxLevelDiff = this.getStrengthLevelDiff(strength); + const maxLevelDiff = this.getStrengthLevelDiff(strength); //The maximum distance from the evolution level tolerated for the mon to not evolve const minChance: number = 0.875 - 0.125 * strength; evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1); @@ -735,11 +753,6 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali evolutionChance = Math.min(0.65 * easeInFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel) / preferredMinLevel) + 0.35 * easeOutFunc(Math.min(Math.max(level - evolutionLevel, 0), preferredMinLevel * 2.5) / (preferredMinLevel * 2.5)), 1); } } - /* (Most) Trainers shouldn't be using unevolved Pokemon by the third gym leader / wave 80. Exceptions to this include Breeders, whose large teams are balanced by the use of weaker pokemon */ - if (currentWave >= 80 && forTrainer && strength > PartyMemberStrength.WEAKER) { - evolutionChance = 1; - noEvolutionChance = 0; - } if (evolutionChance > 0) { if (isRegionalEvolution) { From ef4d2ec91eb45b1dc10753b97338ec4673f48d7d Mon Sep 17 00:00:00 2001 From: Blitzy <118096277+Blitz425@users.noreply.github.com> Date: Thu, 12 Sep 2024 11:01:32 -0500 Subject: [PATCH 3/4] [Balance] More Evil Team Adjustments (#4056) * Update trainer-config.ts * Update trainer-config.ts * Devolve 98% of the list * Update trainer-config.ts * Update trainer-config.ts * Update Pools / Minor Giovanni Changes * Update trainer-config.ts * Make Ghetsis2 Basculegion/Jellicent male --------- Co-authored-by: Tempoanon <163687446+Tempo-anon@users.noreply.github.com> --- src/data/trainer-config.ts | 120 ++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index a6cf4247f27..07722a5a206 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -556,64 +556,64 @@ export class TrainerConfig { switch (team) { case "rocket": { return { - [TrainerPoolTier.COMMON]: [Species.RATTATA, Species.KOFFING, Species.EKANS, Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], - [TrainerPoolTier.UNCOMMON]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE], + [TrainerPoolTier.COMMON]: [Species.RATTATA, Species.KOFFING, Species.EKANS, Species.ZUBAT, Species.MAGIKARP, Species.HOUNDOUR, Species.ONIX, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.DROWZEE, Species.VILEPLUME], + [TrainerPoolTier.UNCOMMON]: [Species.PORYGON, Species.MANKEY, Species.MAGNEMITE, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO, Species.MAGBY, Species.ELEKID], [TrainerPoolTier.RARE]: [Species.DRATINI, Species.LARVITAR] }; } case "magma": { return { - [TrainerPoolTier.COMMON]: [Species.NUMEL, Species.POOCHYENA, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR], - [TrainerPoolTier.UNCOMMON]: [Species.TRAPINCH, Species.HEATMOR], + [TrainerPoolTier.COMMON]: [Species.GROWLITHE, Species.SLUGMA, Species.SOLROCK, Species.HIPPOPOTAS, Species.BALTOY, Species.ROLYCOLY, Species.GLIGAR, Species.TORKOAL, Species.HOUNDOUR, Species.MAGBY], + [TrainerPoolTier.UNCOMMON]: [Species.TRAPINCH, Species.SILICOBRA, Species.RHYHORN, Species.ANORITH, Species.LILEEP, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON, Species.BARBOACH], [TrainerPoolTier.RARE]: [Species.CAPSAKID, Species.CHARCADET] }; } case "aqua": { return { - [TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.CORPHISH, Species.ZIGZAGOON, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH], - [TrainerPoolTier.UNCOMMON]: [Species.MANTINE, Species.BASCULEGION, Species.REMORAID, Species.ARROKUDA], - [TrainerPoolTier.RARE]: [Species.DONDOZO] + [TrainerPoolTier.COMMON]: [Species.CORPHISH, Species.SPHEAL, Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.LOTAD, Species.WAILMER, Species.REMORAID], + [TrainerPoolTier.UNCOMMON]: [Species.MANTYKE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.DHELMISE, Species.CLOBBOPUS, Species.FEEBAS, Species.PALDEA_WOOPER, Species.HORSEA, Species.SKRELP], + [TrainerPoolTier.RARE]: [Species.DONDOZO, Species.BASCULEGION] }; } case "galactic": { return { - [TrainerPoolTier.COMMON]: [Species.GLAMEOW, Species.STUNKY, Species.BRONZOR, Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL], - [TrainerPoolTier.UNCOMMON]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.HISUI_SNEASEL], - [TrainerPoolTier.RARE]: [Species.HISUI_ZORUA, Species.HISUI_SLIGGOO] + [TrainerPoolTier.COMMON]: [Species.BRONZOR, Species.SWINUB, Species.YANMA, Species.LICKITUNG, Species.TANGELA, Species.MAGBY, Species.ELEKID, Species.SKORUPI, Species.ZUBAT, Species.MURKROW, Species.MAGIKARP, Species.VOLTORB], + [TrainerPoolTier.UNCOMMON]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.DUSKULL, Species.ROTOM, Species.HISUI_VOLTORB, Species.GLIGAR, Species.ABRA], + [TrainerPoolTier.RARE]: [Species.URSALUNA, Species.HISUI_LILLIGANT, Species.SPIRITOMB, Species.HISUI_SNEASEL] }; } case "plasma": { return { - [TrainerPoolTier.COMMON]: [Species.SCRAFTY, Species.LILLIPUP, Species.PURRLOIN, Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], - [TrainerPoolTier.UNCOMMON]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK], - [TrainerPoolTier.RARE]: [Species.DRUDDIGON, Species.BOUFFALANT, Species.AXEW, Species.DEINO, Species.DURANT] + [TrainerPoolTier.COMMON]: [Species.YAMASK, Species.ROGGENROLA, Species.JOLTIK, Species.TYMPOLE, Species.FRILLISH, Species.FERROSEED, Species.SANDILE, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.CUBCHOO, Species.VANILLITE], + [TrainerPoolTier.UNCOMMON]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK, Species.TYNAMO, Species.GALAR_DARUMAKA, Species.GOLETT, Species.MIENFOO, Species.DURANT, Species.SIGILYPH], + [TrainerPoolTier.RARE]: [Species.HISUI_ZORUA, Species.AXEW, Species.DEINO, Species.HISUI_BRAVIARY] }; } case "flare": { return { - [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.GULPIN, Species.PURRLOIN, Species.POOCHYENA, Species.SCATTERBUG], - [TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PANCHAM, Species.PAWNIARD], - [TrainerPoolTier.RARE]: [Species.NOIVERN, Species.DRUDDIGON] + [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.INKAY, Species.HELIOPTILE, Species.ELECTRIKE, Species.SKORUPI, Species.PURRLOIN, Species.CLAWITZER, Species.PANCHAM, Species.ESPURR, Species.BUNNELBY], + [TrainerPoolTier.UNCOMMON]: [Species.LITWICK, Species.SNEASEL, Species.PUMPKABOO, Species.PHANTUMP, Species.HONEDGE, Species.BINACLE, Species.BERGMITE, Species.HOUNDOUR, Species.SKRELP, Species.SLIGGOO], + [TrainerPoolTier.RARE]: [Species.NOIVERN, Species.HISUI_AVALUGG, Species.HISUI_SLIGGOO] }; } case "aether": { return { - [TrainerPoolTier.COMMON]: [ Species.BRUXISH, Species.SLOWPOKE, Species.BALTOY, Species.EXEGGCUTE, Species.ABRA, Species.ALOLA_RAICHU, Species.ELGYEM, Species.NATU], - [TrainerPoolTier.UNCOMMON]: [Species.GALAR_SLOWKING, Species.MEDITITE, Species.BELDUM, Species.ORANGURU, Species.HATTERENE, Species.INKAY, Species.RALTS], - [TrainerPoolTier.RARE]: [Species.ARMAROUGE, Species.GIRAFARIG, Species.PORYGON] + [TrainerPoolTier.COMMON]: [ Species.BRUXISH, Species.SLOWPOKE, Species.BALTOY, Species.EXEGGCUTE, Species.ABRA, Species.ALOLA_RAICHU, Species.ELGYEM, Species.NATU, Species.BLIPBUG, Species.GIRAFARIG, Species.ORANGURU], + [TrainerPoolTier.UNCOMMON]: [Species.GALAR_SLOWPOKE, Species.MEDITITE, Species.BELDUM, Species.HATENNA, Species.INKAY, Species.RALTS, Species.GALAR_MR_MIME], + [TrainerPoolTier.RARE]: [Species.ARMAROUGE, Species.HISUI_BRAVIARY, Species.PORYGON] }; } case "skull": { return { - [TrainerPoolTier.COMMON]: [ Species.MAREANIE, Species.ALOLA_GRIMER, Species.GASTLY, Species.ZUBAT, Species.LURANTIS, Species.VENIPEDE, Species.BUDEW, Species.KOFFING], - [TrainerPoolTier.UNCOMMON]: [Species.GALAR_SLOWBRO, Species.SKORUPI, Species.PALDEA_WOOPER, Species.NIDORAN_F, Species.CROAGUNK, Species.MANDIBUZZ], - [TrainerPoolTier.RARE]: [Species.DRAGALGE, Species.HISUI_SNEASEL] + [TrainerPoolTier.COMMON]: [ Species.MAREANIE, Species.ALOLA_GRIMER, Species.GASTLY, Species.ZUBAT, Species.FOMANTIS, Species.VENIPEDE, Species.BUDEW, Species.KOFFING, Species.STUNKY, Species.CROAGUNK, Species.NIDORAN_F], + [TrainerPoolTier.UNCOMMON]: [Species.GALAR_SLOWPOKE, Species.SKORUPI, Species.PALDEA_WOOPER, Species.VULLABY, Species.HISUI_QWILFISH, Species.GLIMMET], + [TrainerPoolTier.RARE]: [Species.SKRELP, Species.HISUI_SNEASEL] }; } case "macro": { return { - [TrainerPoolTier.COMMON]: [ Species.HATTERENE, Species.MILOTIC, Species.TSAREENA, Species.SALANDIT, Species.GALAR_PONYTA, Species.GOTHITA, Species.FROSLASS], - [TrainerPoolTier.UNCOMMON]: [Species.MANDIBUZZ, Species.MAREANIE, Species.ALOLA_VULPIX, Species.TOGEPI, Species.GALAR_CORSOLA, Species.SINISTEA, Species.APPLIN], + [TrainerPoolTier.COMMON]: [ Species.HATENNA, Species.FEEBAS, Species.BOUNSWEET, Species.SALANDIT, Species.GALAR_PONYTA, Species.GOTHITA, Species.FROSLASS, Species.VULPIX, Species.FRILLISH, Species.ODDISH, Species.SINISTEA], + [TrainerPoolTier.UNCOMMON]: [Species.VULLABY, Species.MAREANIE, Species.ALOLA_VULPIX, Species.TOGEPI, Species.GALAR_CORSOLA, Species.APPLIN], [TrainerPoolTier.RARE]: [Species.TINKATINK, Species.HISUI_LILLIGANT] }; } @@ -1328,9 +1328,9 @@ export const trainerConfigs: TrainerConfigs = { ), [TrainerType.ROCKET_GRUNT]: new TrainerConfig(++t).setHasGenders("Rocket Grunt Female").setHasDouble("Rocket Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH], - [TrainerPoolTier.UNCOMMON]: [Species.GYARADOS, Species.TAUROS, Species.SCYTHER, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB], - [TrainerPoolTier.RARE]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE], + [TrainerPoolTier.COMMON]: [Species.WEEDLE, Species.RATTATA, Species.EKANS, Species.SANDSHREW, Species.ZUBAT, Species.GEODUDE, Species.KOFFING, Species.GRIMER, Species.ODDISH, Species.SLOWPOKE], + [TrainerPoolTier.UNCOMMON]: [Species.GYARADOS, Species.LICKITUNG, Species.TAUROS, Species.MANKEY, Species.SCYTHER, Species.ELEKID, Species.MAGBY, Species.CUBONE, Species.GROWLITHE, Species.MURKROW, Species.GASTLY, Species.EXEGGCUTE, Species.VOLTORB, Species.MAGNEMITE], + [TrainerPoolTier.RARE]: [Species.PORYGON, Species.ALOLA_RATTATA, Species.ALOLA_SANDSHREW, Species.ALOLA_MEOWTH, Species.ALOLA_GRIMER, Species.ALOLA_GEODUDE, Species.PALDEA_TAUROS, Species.OMANYTE, Species.KABUTO], [TrainerPoolTier.SUPER_RARE]: [Species.DRATINI, Species.LARVITAR] }), [TrainerType.ARCHER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [Species.HOUNDOOM]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), @@ -1339,63 +1339,63 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.PETREL]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("rocket_admin", "rocket", [Species.WEEZING]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_rocket_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.MAGMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Magma Grunt Female").setHasDouble("Magma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.BALTOY, Species.BARBOACH], - [TrainerPoolTier.UNCOMMON]: [Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR], - [TrainerPoolTier.RARE]: [Species.TRAPINCH, Species.HEATMOR], + [TrainerPoolTier.COMMON]: [Species.SLUGMA, Species.POOCHYENA, Species.NUMEL, Species.ZIGZAGOON, Species.DIGLETT, Species.MAGBY, Species.TORKOAL, Species.GROWLITHE, Species.BALTOY], + [TrainerPoolTier.UNCOMMON]: [Species.SOLROCK, Species.HIPPOPOTAS, Species.SANDACONDA, Species.PHANPY, Species.ROLYCOLY, Species.GLIGAR, Species.RHYHORN, Species.HEATMOR], + [TrainerPoolTier.RARE]: [Species.TRAPINCH, Species.LILEEP, Species.ANORITH, Species.HISUI_GROWLITHE, Species.TURTONATOR, Species.ARON], [TrainerPoolTier.SUPER_RARE]: [Species.CAPSAKID, Species.CHARCADET] }), [TrainerType.TABITHA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin", "magma", [Species.CAMERUPT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.COURTNEY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("magma_admin_female", "magma", [Species.CAMERUPT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.AQUA_GRUNT]: new TrainerConfig(++t).setHasGenders("Aqua Grunt Female").setHasDouble("Aqua Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL], - [TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.QWILFISH], - [TrainerPoolTier.RARE]: [Species.MANTINE, Species.BASCULEGION, Species.REMORAID, Species.ARROKUDA], - [TrainerPoolTier.SUPER_RARE]: [Species.DONDOZO] + [TrainerPoolTier.COMMON]: [Species.CARVANHA, Species.WAILMER, Species.ZIGZAGOON, Species.LOTAD, Species.CORPHISH, Species.SPHEAL, Species.REMORAID, Species.QWILFISH, Species.BARBOACH], + [TrainerPoolTier.UNCOMMON]: [Species.CLAMPERL, Species.CHINCHOU, Species.WOOPER, Species.WINGULL, Species.TENTACOOL, Species.AZURILL, Species.CLOBBOPUS, Species.HORSEA], + [TrainerPoolTier.RARE]: [Species.MANTINE, Species.DHELMISE, Species.HISUI_QWILFISH, Species.ARROKUDA, Species.PALDEA_WOOPER, Species.SKRELP], + [TrainerPoolTier.SUPER_RARE]: [Species.DONDOZO, Species.BASCULEGION] }), [TrainerType.MATT]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.SHELLY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aqua_admin_female", "aqua", [Species.SHARPEDO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aqua_magma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.GALACTIC_GRUNT]: new TrainerConfig(++t).setHasGenders("Galactic Grunt Female").setHasDouble("Galactic Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY], - [TrainerPoolTier.UNCOMMON]: [Species.CARNIVINE, Species.GROWLITHE, Species.QWILFISH, Species.SNEASEL], - [TrainerPoolTier.RARE]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.HISUI_SNEASEL], - [TrainerPoolTier.SUPER_RARE]: [Species.HISUI_ZORUA, Species.HISUI_SLIGGOO] + [TrainerPoolTier.COMMON]: [Species.GLAMEOW, Species.STUNKY, Species.CROAGUNK, Species.SHINX, Species.WURMPLE, Species.BRONZOR, Species.DRIFLOON, Species.BURMY, Species.CARNIVINE], + [TrainerPoolTier.UNCOMMON]: [Species.LICKITUNG, Species.RHYHORN, Species.TANGELA, Species.ZUBAT, Species.YANMA, Species.SKORUPI, Species.GLIGAR, Species.SWINUB], + [TrainerPoolTier.RARE]: [Species.HISUI_GROWLITHE, Species.HISUI_QWILFISH, Species.SNEASEL, Species.ELEKID, Species.MAGBY, Species.DUSKULL], + [TrainerPoolTier.SUPER_RARE]: [Species.ROTOM, Species.SPIRITOMB, Species.HISUI_SNEASEL] }), [TrainerType.JUPITER]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [Species.SKUNTANK]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.MARS]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander_female", "galactic", [Species.PURUGLY]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.SATURN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("galactic_commander", "galactic", [Species.TOXICROAK]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_galactic_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.PLASMA_GRUNT]: new TrainerConfig(++t).setHasGenders("Plasma Grunt Female").setHasDouble("Plasma Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH], - [TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.AMOONGUSS], - [TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK], - [TrainerPoolTier.SUPER_RARE]: [Species.DRUDDIGON, Species.BOUFFALANT, Species.AXEW, Species.DEINO, Species.DURANT] + [TrainerPoolTier.COMMON]: [Species.PATRAT, Species.LILLIPUP, Species.PURRLOIN, Species.SCRAFTY, Species.WOOBAT, Species.VANILLITE, Species.SANDILE, Species.TRUBBISH, Species.TYMPOLE], + [TrainerPoolTier.UNCOMMON]: [Species.FRILLISH, Species.VENIPEDE, Species.GOLETT, Species.TIMBURR, Species.DARUMAKA, Species.FOONGUS, Species.JOLTIK], + [TrainerPoolTier.RARE]: [Species.PAWNIARD, Species.RUFFLET, Species.VULLABY, Species.ZORUA, Species.DRILBUR, Species.KLINK, Species.CUBCHOO, Species.MIENFOO, Species.DURANT, Species.BOUFFALANT], + [TrainerPoolTier.SUPER_RARE]: [Species.DRUDDIGON, Species.HISUI_ZORUA, Species.AXEW, Species.DEINO] }), [TrainerType.ZINZOLIN]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.CRYOGONAL]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.ROOD]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("plasma_sage", "plasma", [Species.SWOOBAT]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_plasma_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.FLARE_GRUNT]: new TrainerConfig(++t).setHasGenders("Flare Grunt Female").setHasDouble("Flare Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK], - [TrainerPoolTier.UNCOMMON]: [Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.GULPIN, Species.PURRLOIN, Species.POOCHYENA, Species.SCATTERBUG], - [TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PANCHAM, Species.PAWNIARD], - [TrainerPoolTier.SUPER_RARE]: [Species.NOIVERN, Species.DRUDDIGON] + [TrainerPoolTier.COMMON]: [Species.FLETCHLING, Species.LITLEO, Species.PONYTA, Species.INKAY, Species.HOUNDOUR, Species.SKORUPI, Species.SCRAFTY, Species.CROAGUNK, Species.SCATTERBUG, Species.ESPURR], + [TrainerPoolTier.UNCOMMON]: [Species.HELIOPTILE, Species.ELECTRIKE, Species.SKRELP, Species.PANCHAM, Species.PURRLOIN, Species.POOCHYENA, Species.BINACLE, Species.CLAUNCHER, Species.PUMPKABOO, Species.PHANTUMP], + [TrainerPoolTier.RARE]: [Species.LITWICK, Species.SNEASEL, Species.PAWNIARD, Species.BERGMITE, Species.SLIGGOO], + [TrainerPoolTier.SUPER_RARE]: [Species.NOIVERN, Species.HISUI_SLIGGOO, Species.HISUI_AVALUGG] }), [TrainerType.BRYONY]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin_female", "flare", [Species.LIEPARD]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.XEROSIC]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("flare_admin", "flare", [Species.MALAMAR]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_flare_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.AETHER_GRUNT]: new TrainerConfig(++t).setHasGenders("Aether Grunt Female").setHasDouble("Aether Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.BOUNSWEET, Species.LILLIPUP, Species.ALOLA_MAROWAK], - [TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ALOLA_EXEGGUTOR, Species.CRABRAWLER, Species.CUTIEFLY, Species.ALOLA_RAICHU, Species.ORICORIO, Species.MUDBRAY], - [TrainerPoolTier.RARE]: [ Species.ORANGURU, Species.PASSIMIAN, Species.GALAR_CORSOLA, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.TURTONATOR, Species.DRAMPA], + [TrainerPoolTier.COMMON]: [ Species.PIKIPEK, Species.ROCKRUFF, Species.ALOLA_DIGLETT, Species.ALOLA_EXEGGUTOR, Species.YUNGOOS, Species.CORSOLA, Species.ALOLA_GEODUDE, Species.ALOLA_RAICHU, Species.BOUNSWEET, Species.LILLIPUP, Species.KOMALA, Species.MORELULL, Species.COMFEY, Species.TOGEDEMARU], + [TrainerPoolTier.UNCOMMON]: [ Species.POLIWAG, Species.STUFFUL, Species.ORANGURU, Species.PASSIMIAN, Species.BRUXISH, Species.MINIOR, Species.WISHIWASHI, Species.CRABRAWLER, Species.CUTIEFLY, Species.ORICORIO, Species.MUDBRAY, Species.PYUKUMUKU, Species.ALOLA_MAROWAK], + [TrainerPoolTier.RARE]: [ Species.GALAR_CORSOLA, Species.ALOLA_SANDSHREW, Species.ALOLA_VULPIX, Species.TURTONATOR, Species.DRAMPA], [TrainerPoolTier.SUPER_RARE]: [Species.JANGMO_O, Species.PORYGON] }), [TrainerType.FABA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("aether_admin", "aether", [Species.HYPNO]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_aether_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), [TrainerType.SKULL_GRUNT]: new TrainerConfig(++t).setHasGenders("Skull Grunt Female").setHasDouble("Skull Grunts").setMoneyMultiplier(1.0).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_grunt").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)) .setSpeciesPools({ - [TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH], - [TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.ALOLA_MAROWAK, Species.PANCHAM, Species.DROWZEE, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY], - [TrainerPoolTier.RARE]: [Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.GASTLY, Species.WISHIWASHI], + [TrainerPoolTier.COMMON]: [ Species.SALANDIT, Species.ALOLA_RATTATA, Species.EKANS, Species.ALOLA_MEOWTH, Species.SCRAGGY, Species.KOFFING, Species.ALOLA_GRIMER, Species.MAREANIE, Species.SPINARAK, Species.TRUBBISH], + [TrainerPoolTier.UNCOMMON]: [ Species.FOMANTIS, Species.SABLEYE, Species.SANDILE, Species.HOUNDOUR, Species.ALOLA_MAROWAK, Species.GASTLY, Species.PANCHAM, Species.DROWZEE, Species.ZUBAT, Species.VENIPEDE, Species.VULLABY], + [TrainerPoolTier.RARE]: [Species.SANDYGAST, Species.PAWNIARD, Species.MIMIKYU, Species.DHELMISE, Species.WISHIWASHI, Species.NYMBLE], [TrainerPoolTier.SUPER_RARE]: [Species.GRUBBIN, Species.DEWPIDER] }), [TrainerType.PLUMERIA]: new TrainerConfig(++t).setMoneyMultiplier(1.5).initForEvilTeamAdmin("skull_admin", "skull", [Species.SALAZZLE]).setEncounterBgm(TrainerType.PLASMA_GRUNT).setBattleBgm("battle_plasma_grunt").setMixedBattleBgm("battle_skull_admin").setVictoryBgm("victory_team_plasma").setPartyTemplateFunc(scene => getEvilGruntPartyTemplate(scene)), @@ -1698,10 +1698,10 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.PERSIAN, Species.ALOLA_PERSIAN])) - .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.NIDOKING, Species.NIDOQUEEN])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.RHYPERIOR])) - .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.DUGTRIO, Species.ALOLA_DUGTRIO])) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.MAROWAK, Species.ALOLA_MAROWAK])) + .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.DUGTRIO, Species.ALOLA_DUGTRIO])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.HONCHKROW])) + .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.NIDOKING, Species.NIDOQUEEN])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.RHYPERIOR])) .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.KANGASKHAN], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); @@ -1716,7 +1716,7 @@ export const trainerConfigs: TrainerConfigs = { p.pokeball = PokeballType.ULTRA_BALL; })) .setPartyMemberFunc(1, getRandomPartyMemberFunc([Species.HIPPOWDON])) - .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.EXCADRILL])) + .setPartyMemberFunc(2, getRandomPartyMemberFunc([Species.EXCADRILL, Species.GARCHOMP])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([Species.KANGASKHAN], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); @@ -1724,7 +1724,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON])) + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.GASTRODON, Species.SEISMITOAD])) .setPartyMemberFunc(5, getRandomPartyMemberFunc([Species.MEWTWO], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); @@ -1842,7 +1842,7 @@ export const trainerConfigs: TrainerConfigs = { p.formIndex = 1; p.generateName(); })) - .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.WEAVILE], TrainerSlot.TRAINER, true, p => { + .setPartyMemberFunc(4, getRandomPartyMemberFunc([Species.WEAVILE, Species.SNEASLER], TrainerSlot.TRAINER, true, p => { p.setBoss(true, 2); p.generateAndPopulateMoveset(); p.pokeball = PokeballType.ULTRA_BALL; @@ -1873,7 +1873,7 @@ export const trainerConfigs: TrainerConfigs = { .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.BASCULEGION, Species.JELLICENT ], TrainerSlot.TRAINER, true, p => { p.generateAndPopulateMoveset(); p.gender = Gender.MALE; - p.formIndex = 1; + p.formIndex = 0; })) .setPartyMemberFunc(2, getRandomPartyMemberFunc([ Species.KINGAMBIT ])) .setPartyMemberFunc(3, getRandomPartyMemberFunc([ Species.VOLCARONA, Species.SLITHER_WING ])) From f80c073def0e7aaf0b76a59b2a4fe65cd4213265 Mon Sep 17 00:00:00 2001 From: PrabbyDD <147005742+PrabbyDD@users.noreply.github.com> Date: Thu, 12 Sep 2024 12:27:47 -0700 Subject: [PATCH 4/4] [Bug] Fix for Roost Grounds the User Rather than Removing Flying Typing#3984 (#4164) * double checking tests on a new made branch for bug 3984 * roost test update * added roost test file * Roost test update * removed random stackdump * cleaned up message for roost * fixed test file for linter * cleaning up code and fixing some desync test issues * Cleaned up more code and added case for double shock * fixing some messages and putting burn up and double shock in same class --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/battler-tags.ts | 83 +++++++++++- src/data/move.ts | 2 + src/enums/battler-tag-type.ts | 2 + src/test/moves/roost.test.ts | 237 +++++++++++++++++++++++++++++++--- 4 files changed, 302 insertions(+), 22 deletions(-) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 66b6676a4f5..4685a4fc7e1 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -1885,11 +1885,18 @@ export class CursedTag extends BattlerTag { return ret; } } +/** + * Battler tag for attacks that remove a type post use. + */ +export class RemovedTypeTag extends BattlerTag { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: Moves) { + super(tagType, lapseType, 1, sourceMove); + } +} /** - * Battler tag for effects that ground the source, allowing Ground-type moves to hit them. Encompasses two tag types: - * @item `IGNORE_FLYING`: Persistent grounding effects (i.e. from Smack Down and Thousand Waves) - * @item `ROOSTED`: One-turn grounding effects (i.e. from Roost) + * Battler tag for effects that ground the source, allowing Ground-type moves to hit them. + * @description `IGNORE_FLYING`: Persistent grounding effects (i.e. from Smack Down and Thousand Waves) */ export class GroundedTag extends BattlerTag { constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType, sourceMove: Moves) { @@ -1897,6 +1904,70 @@ export class GroundedTag extends BattlerTag { } } +/** + * @description `ROOSTED`: Tag for temporary grounding if only source of ungrounding is flying and pokemon uses Roost. + * Roost removes flying type from a pokemon for a single turn. + */ + +export class RoostedTag extends BattlerTag { + private isBaseFlying : boolean; + private isBasePureFlying : boolean; + + constructor() { + super(BattlerTagType.ROOSTED, BattlerTagLapseType.TURN_END, 1, Moves.ROOST); + } + + onRemove(pokemon: Pokemon): void { + const currentTypes = pokemon.getTypes(); + const baseTypes = pokemon.getTypes(false, false, true); + + const forestsCurseApplied: boolean = currentTypes.includes(Type.GRASS) && !baseTypes.includes(Type.GRASS); + const trickOrTreatApplied: boolean = currentTypes.includes(Type.GHOST) && !baseTypes.includes(Type.GHOST); + + if (this.isBaseFlying) { + let modifiedTypes: Type[] = []; + if (this.isBasePureFlying) { + if (forestsCurseApplied || trickOrTreatApplied) { + modifiedTypes = currentTypes.filter(type => type !== Type.NORMAL); + modifiedTypes.push(Type.FLYING); + } else { + modifiedTypes = [Type.FLYING]; + } + } else { + modifiedTypes = [...currentTypes]; + modifiedTypes.push(Type.FLYING); + } + pokemon.summonData.types = modifiedTypes; + pokemon.updateInfo(); + } + } + + onAdd(pokemon: Pokemon): void { + const currentTypes = pokemon.getTypes(); + const baseTypes = pokemon.getTypes(false, false, true); + + const isOriginallyDualType = baseTypes.length === 2; + const isCurrentlyDualType = currentTypes.length === 2; + this.isBaseFlying = baseTypes.includes(Type.FLYING); + this.isBasePureFlying = baseTypes[0] === Type.FLYING && baseTypes.length === 1; + + if (this.isBaseFlying) { + let modifiedTypes: Type[]; + if (this.isBasePureFlying && !isCurrentlyDualType) { + modifiedTypes = [Type.NORMAL]; + } else { + if (!!pokemon.getTag(RemovedTypeTag) && isOriginallyDualType && !isCurrentlyDualType) { + modifiedTypes = [Type.UNKNOWN]; + } else { + modifiedTypes = currentTypes.filter(type => type !== Type.FLYING); + } + } + pokemon.summonData.types = modifiedTypes; + pokemon.updateInfo(); + } + } +} + /** Common attributes of form change abilities that block damage */ export class FormBlockDamageTag extends BattlerTag { constructor(tagType: BattlerTagType) { @@ -2259,7 +2330,11 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source case BattlerTagType.IGNORE_FLYING: return new GroundedTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove); case BattlerTagType.ROOSTED: - return new GroundedTag(tagType, BattlerTagLapseType.TURN_END, sourceMove); + return new RoostedTag(); + case BattlerTagType.BURNED_UP: + return new RemovedTypeTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove); + case BattlerTagType.DOUBLE_SHOCKED: + return new RemovedTypeTag(tagType, BattlerTagLapseType.CUSTOM, sourceMove); case BattlerTagType.SALT_CURED: return new SaltCuredTag(sourceId); case BattlerTagType.CURSED: diff --git a/src/data/move.ts b/src/data/move.ts index 672a1154a0e..f09f8db7487 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -8532,6 +8532,7 @@ export function initMoves() { return userTypes.includes(Type.FIRE); }) .attr(HealStatusEffectAttr, true, StatusEffect.FREEZE) + .attr(AddBattlerTagAttr, BattlerTagType.BURNED_UP, true, false) .attr(RemoveTypeAttr, Type.FIRE, (user) => { user.scene.queueMessage(i18next.t("moveTriggers:burnedItselfOut", {pokemonName: getPokemonNameWithAffix(user)})); }), @@ -9315,6 +9316,7 @@ export function initMoves() { const userTypes = user.getTypes(true); return userTypes.includes(Type.ELECTRIC); }) + .attr(AddBattlerTagAttr, BattlerTagType.DOUBLE_SHOCKED, true, false) .attr(RemoveTypeAttr, Type.ELECTRIC, (user) => { user.scene.queueMessage(i18next.t("moveTriggers:usedUpAllElectricity", {pokemonName: getPokemonNameWithAffix(user)})); }), diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index cb83ebf4882..105f359df76 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -76,4 +76,6 @@ export enum BattlerTagType { GORILLA_TACTICS = "GORILLA_TACTICS", THROAT_CHOPPED = "THROAT_CHOPPED", TAR_SHOT = "TAR_SHOT", + BURNED_UP = "BURNED_UP", + DOUBLE_SHOCKED = "DOUBLE_SHOCKED", } diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index cf07a3485e7..df7fc7096b0 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -1,4 +1,4 @@ -import { Abilities } from "#app/enums/abilities"; +import { Type } from "#app/data/type"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; @@ -27,33 +27,234 @@ describe("Moves - Roost", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override.battleType("single"); - game.override.enemySpecies(Species.STARAPTOR); - game.override.enemyAbility(Abilities.INSOMNIA); + game.override.enemySpecies(Species.RELICANTH); game.override.startingLevel(100); - game.override.enemyLevel(100); - game.override.moveset([Moves.STOMPING_TANTRUM]); - game.override.enemyMoveset([Moves.ROOST, Moves.ROOST, Moves.ROOST, Moves.ROOST]); + game.override.enemyLevel(60); + game.override.enemyMoveset(Moves.EARTHQUAKE); + game.override.moveset([Moves.ROOST, Moves.BURN_UP, Moves.DOUBLE_SHOCK]); + game.override.starterForms({ [Species.ROTOM]: 4 }); }); + /** + * Roost's behavior should be defined as: + * The pokemon loses its flying type for a turn. If the pokemon was ungroundd solely due to being a flying type, it will be grounded until end of turn. + * 1. Pure Flying type pokemon -> become normal type until end of turn + * 2. Dual Flying/X type pokemon -> become type X until end of turn + * 3. Pokemon that use burn up into roost (ex. Moltres) -> become flying due to burn up, then typeless until end of turn after using roost + * 4. If a pokemon is afflicted with Forest's Curse or Trick or treat, dual type pokemon will become 3 type pokemon after the flying type is regained + * Pure flying types become (Grass or Ghost) and then back to flying/ (Grass or Ghost), + * and pokemon post Burn up become () + * 5. If a pokemon is also ungrounded due to other reasons (such as levitate), it will stay ungrounded post roost, despite not being flying type. + * 6. Non flying types using roost (such as dunsparce) are already grounded, so this move will only heal and have no other effects. + */ + test( - "move should ground the user until the end of turn", + "Non flying type uses roost -> no type change, took damage", async () => { - await game.startBattle([Species.MAGIKARP]); - - const enemyPokemon = game.scene.getEnemyPokemon()!; - - const enemyStartingHp = enemyPokemon.hp; - - game.move.select(Moves.STOMPING_TANTRUM); - + await game.classicMode.startBattle([Species.DUNSPARCE]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.ROOST); await game.phaseInterceptor.to(MoveEffectPhase); - expect(enemyPokemon.getTag(BattlerTagType.ROOSTED)).toBeDefined(); + // Should only be normal type, and NOT flying type + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.NORMAL).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); await game.phaseInterceptor.to(TurnEndPhase); - expect(enemyPokemon.hp).toBeLessThan(enemyStartingHp); - expect(enemyPokemon.getTag(BattlerTagType.ROOSTED)).toBeUndefined(); + // Lose HP, still normal type + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBeLessThan(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.NORMAL).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); }, TIMEOUT ); + + test( + "Pure flying type -> becomes normal after roost and takes damage from ground moves -> regains flying", + async () => { + await game.classicMode.startBattle([Species.TORNADUS]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be normal type, and NOT flying type + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.NORMAL).toBeTruthy(); + expect(playerPokemonTypes[0] === Type.FLYING).toBeFalsy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should have lost HP and is now back to being pure flying + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBeLessThan(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.NORMAL).toBeFalsy(); + expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + + test( + "Dual X/flying type -> becomes type X after roost and takes damage from ground moves -> regains flying", + async () => { + await game.classicMode.startBattle([Species.HAWLUCHA]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be pure fighting type and grounded + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.FIGHTING).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should have lost HP and is now back to being fighting/flying + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBeLessThan(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.FIGHTING).toBeTruthy(); + expect(playerPokemonTypes[1] === Type.FLYING).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + + test( + "Pokemon with levitate after using roost should lose flying type but still be unaffected by ground moves", + async () => { + await game.classicMode.startBattle([Species.ROTOM]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be pure fighting type and grounded + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.ELECTRIC).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should have lost HP and is now back to being fighting/flying + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBe(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.ELECTRIC).toBeTruthy(); + expect(playerPokemonTypes[1] === Type.FLYING).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + + test( + "A fire/flying type that uses burn up, then roost should be typeless until end of turn", + async () => { + await game.classicMode.startBattle([Species.MOLTRES]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.BURN_UP); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be pure flying type after burn up + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be typeless type after roost and is grounded + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.getTag(BattlerTagType.ROOSTED)).toBeDefined(); + expect(playerPokemonTypes[0] === Type.UNKNOWN).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should go back to being pure flying and have taken damage from earthquake, and is ungrounded again + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBeLessThan(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + + test( + "An electric/flying type that uses double shock, then roost should be typeless until end of turn", + async () => { + game.override.enemySpecies(Species.ZEKROM); + await game.classicMode.startBattle([Species.ZAPDOS]); + const playerPokemon = game.scene.getPlayerPokemon()!; + const playerPokemonStartingHP = playerPokemon.hp; + game.move.select(Moves.DOUBLE_SHOCK); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be pure flying type after burn up + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + // Should only be typeless type after roost and is grounded + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.getTag(BattlerTagType.ROOSTED)).toBeDefined(); + expect(playerPokemonTypes[0] === Type.UNKNOWN).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should go back to being pure flying and have taken damage from earthquake, and is ungrounded again + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemon.hp).toBeLessThan(playerPokemonStartingHP); + expect(playerPokemonTypes[0] === Type.FLYING).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + + test( + "Dual Type Pokemon afflicted with Forests Curse/Trick or Treat and post roost will become dual type and then become 3 type at end of turn", + async () => { + game.override.enemyMoveset([Moves.TRICK_OR_TREAT, Moves.TRICK_OR_TREAT, Moves.TRICK_OR_TREAT, Moves.TRICK_OR_TREAT]); + await game.classicMode.startBattle([Species.MOLTRES]); + const playerPokemon = game.scene.getPlayerPokemon()!; + game.move.select(Moves.ROOST); + await game.phaseInterceptor.to(MoveEffectPhase); + + let playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes[0] === Type.FIRE).toBeTruthy(); + expect(playerPokemonTypes.length === 1).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeTruthy(); + + await game.phaseInterceptor.to(TurnEndPhase); + + // Should be fire/flying/ghost + playerPokemonTypes = playerPokemon.getTypes(); + expect(playerPokemonTypes.filter(type => type === Type.FLYING)).toHaveLength(1); + expect(playerPokemonTypes.filter(type => type === Type.FIRE)).toHaveLength(1); + expect(playerPokemonTypes.filter(type => type === Type.GHOST)).toHaveLength(1); + expect(playerPokemonTypes.length === 3).toBeTruthy(); + expect(playerPokemon.isGrounded()).toBeFalsy(); + + }, TIMEOUT + ); + });