diff --git a/CREDITS.md b/CREDITS.md index cded7ea90a2..fd9a3d7bde3 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -372,63 +372,67 @@ In addition to the lists below, please check [the PokéRogue wiki](https://wiki. - Lily - PigeonBar -## Past Contributors -- Fontbane -- sodaMelon -- schmidtc1 -- shayebeadling +## Other Code Contributors +- Admiral-Billy +- allen925 +- arColm +- Arxalc +- AsdarDevelops +- bennybroseph +- Brain Frog +- Corrade +- Dakurei - DustinLin -- lucfd -- madibye +- ElizaAlex - EmberCM -- Mewtwo2387 +- EmoUsedHM01 +- EvasiveAce +- Fontbane +- francktrouillez +- FredeX +- geeilhan +- Greenlamp +- happinyz - hayuna -- sirzento -- ReneGV -- mattrossdev -- zacharied -- NxKarim -- td76099 -- Xiaphear +- InfernoVulpix - j-diefenbach - jaimefd -- EvasiveAce -- EmoUsedHM01 -- francktrouillez - JakubHanko -- FredeX -- PigeonBar -- prime-dialga -- rnicar245 -- rationality6 -- Neverblade -- Corrade -- Admiral-Billy -- okimin -- Arxalc -- PrabbyDD - JonStudders - karl-police -- prateau -- meepen -- arColm -- allen925 -- InfernoVulpix -- snoozbuster -- zaccie -- happinyz -- PyGaVS +- lucfd +- Lugiadrien +- madibye +- mattrossdev - mcmontag -- ElizaAlex -- AsdarDevelops -- Vassiat -- RedstonewolfX -- Sam/Flashfyre (initial developer, started PokéRogue) -- Greenlamp -- bennybroseph +- meepen +- Mewtwo2387 +- muscode +- Neverblade +- NxKarim +- okimin - OrangeRed -- Dakurei -- Brain Frog +- PigeonBar +- PrabbyDD +- prateau +- prime-dialga +- PyGaVS +- rationality6 +- RedstonewolfX +- ReneGV +- rnicar245 +- Sam aka Flashfyre (initial developer, started PokéRogue) +- schmidtc1 +- shayebeadling +- sirzento +- snoozbuster +- sodaMelon +- td76099 +- Vassiat +- Xiaphear +- zaccie +- zacharied +- Zé Ricardo # 🌎 Translation diff --git a/package-lock.json b/package-lock.json index a4568b3f5ac..0ef89a8831f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pokemon-rogue-battle", - "version": "1.3.0", + "version": "1.4.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pokemon-rogue-battle", - "version": "1.3.0", + "version": "1.4.1", "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", diff --git a/package.json b/package.json index a8641bb0b98..980bcd2034e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.3.0", + "version": "1.4.1", "type": "module", "scripts": { "start": "vite", diff --git a/public/images/events/winter_holidays2024-event-de.png b/public/images/events/winter_holidays2024-event-de.png new file mode 100644 index 00000000000..1c2e10086f2 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-de.png differ diff --git a/public/images/events/winter_holidays2024-event-en.png b/public/images/events/winter_holidays2024-event-en.png new file mode 100644 index 00000000000..3a361e99bee Binary files /dev/null and b/public/images/events/winter_holidays2024-event-en.png differ diff --git a/public/images/events/winter_holidays2024-event-es-ES.png b/public/images/events/winter_holidays2024-event-es-ES.png new file mode 100644 index 00000000000..f7e64268ad5 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-es-ES.png differ diff --git a/public/images/events/winter_holidays2024-event-fr.png b/public/images/events/winter_holidays2024-event-fr.png new file mode 100644 index 00000000000..278f5f2afd4 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-fr.png differ diff --git a/public/images/events/winter_holidays2024-event-it.png b/public/images/events/winter_holidays2024-event-it.png new file mode 100644 index 00000000000..f3062f40d51 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-it.png differ diff --git a/public/images/events/winter_holidays2024-event-ja.png b/public/images/events/winter_holidays2024-event-ja.png new file mode 100644 index 00000000000..0a217c81d03 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-ja.png differ diff --git a/public/images/events/winter_holidays2024-event-ko.png b/public/images/events/winter_holidays2024-event-ko.png new file mode 100644 index 00000000000..83c9a8525f0 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-ko.png differ diff --git a/public/images/events/winter_holidays2024-event-pt-BR.png b/public/images/events/winter_holidays2024-event-pt-BR.png new file mode 100644 index 00000000000..1f003be5986 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-pt-BR.png differ diff --git a/public/images/events/winter_holidays2024-event-zh-CN.png b/public/images/events/winter_holidays2024-event-zh-CN.png new file mode 100644 index 00000000000..03a9f57c6f2 Binary files /dev/null and b/public/images/events/winter_holidays2024-event-zh-CN.png differ diff --git a/public/locales b/public/locales index 7ad20e64caa..6c6f0af398a 160000 --- a/public/locales +++ b/public/locales @@ -1 +1 @@ -Subproject commit 7ad20e64caa9367b444712f10036fa9bbe4837a5 +Subproject commit 6c6f0af398ae11f8d96c6ac064f171d927812c85 diff --git a/src/data/balance/starters.ts b/src/data/balance/starters.ts index ec66401675b..abbe3897908 100644 --- a/src/data/balance/starters.ts +++ b/src/data/balance/starters.ts @@ -4,7 +4,7 @@ export const POKERUS_STARTER_COUNT = 5; // #region Friendship constants export const CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER = 3; -export const FRIENDSHIP_GAIN_FROM_BATTLE = 3; +export const FRIENDSHIP_GAIN_FROM_BATTLE = 4; export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 6; export const FRIENDSHIP_LOSS_FROM_FAINT = 5; diff --git a/src/data/balance/tms.ts b/src/data/balance/tms.ts index 4882cf4f652..da900768987 100644 --- a/src/data/balance/tms.ts +++ b/src/data/balance/tms.ts @@ -67148,6 +67148,7 @@ export const tmSpecies: TmSpecies = { Species.VELUZA, Species.DONDOZO, Species.TATSUGIRI, + Species.ANNIHILAPE, Species.CLODSIRE, Species.FARIGIRAF, Species.DUDUNSPARCE, diff --git a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts index 786ca3e8fc0..eca358e51f3 100644 --- a/src/data/mystery-encounters/encounters/berries-abound-encounter.ts +++ b/src/data/mystery-encounters/encounters/berries-abound-encounter.ts @@ -13,7 +13,7 @@ import { ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds, } from "#app/modifier/modifier-type"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt, randSeedItem } from "#app/utils"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; import BattleScene from "#app/battle-scene"; @@ -31,6 +31,7 @@ import { BerryType } from "#enums/berry-type"; import { PERMANENT_STATS, Stat } from "#enums/stat"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/berriesAbound"; @@ -58,7 +59,14 @@ export const BerriesAboundEncounter: MysteryEncounter = // Calculate boss mon const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); - const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + let bossSpecies: PokemonSpecies; + if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { + const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); + const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); + bossSpecies = getPokemonSpecies( levelSpecies ); + } else { + bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + } const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon)); const config: EnemyPartyConfig = { diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index a3a97a01238..1df87a3d17f 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -13,6 +13,7 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; import i18next from "#app/plugins/i18n"; import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler"; +import { randSeedItem } from "#app/utils"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterType } from "#enums/mystery-encounter-type"; @@ -33,7 +34,24 @@ const OPTION_3_DISALLOWED_MODIFIERS = [ "PokemonBaseStatTotalModifier" ]; -const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; +const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5; + +const doEventReward = (scene: BattleScene) => { + const event_buff = scene.eventManager.activeEvent()?.delibirdyBuff ?? []; + if (event_buff.length > 0) { + const candidates = event_buff.filter((c => { + const mtype = generateModifierType(scene, modifierTypes[c]); + const existingCharm = scene.findModifier(m => m.type.id === mtype?.id); + return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount(scene)); + })); + if (candidates.length > 0) { + scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes[randSeedItem(candidates)])); + } else { + // At max stacks, give a Voucher instead + scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER)); + } + } +}; /** * Delibird-y encounter. @@ -42,7 +60,8 @@ const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2; */ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY) - .withEncounterTier(MysteryEncounterTier.GREAT) + .withMaxAllowedEncounters(4) + .withEncounterTier(MysteryEncounterTier.COMMON) //Change back after event! .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) .withSceneRequirement(new MoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER)) // Must have enough money for it to spawn at the very least .withPrimaryPokemonRequirement( @@ -136,8 +155,10 @@ export const DelibirdyEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); scene.playSound("item_fanfare"); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); + doEventReward(scene); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN)); + doEventReward(scene); } leaveEncounterWithoutBattle(scene, true); @@ -211,8 +232,10 @@ export const DelibirdyEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); scene.playSound("item_fanfare"); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); + doEventReward(scene); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR)); + doEventReward(scene); } } else { // Check if the player has max stacks of that Berry Pouch already @@ -224,8 +247,10 @@ export const DelibirdyEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell); scene.playSound("item_fanfare"); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); + doEventReward(scene); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH)); + doEventReward(scene); } } @@ -300,8 +325,10 @@ export const DelibirdyEncounter: MysteryEncounter = await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell); scene.playSound("item_fanfare"); await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true); + doEventReward(scene); } else { scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM)); + doEventReward(scene); } chosenPokemon.loseHeldItem(modifier, false); diff --git a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts index 3533e10df29..e238fd51e66 100644 --- a/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts +++ b/src/data/mystery-encounters/encounters/fight-or-flight-encounter.ts @@ -26,9 +26,10 @@ import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENC import PokemonData from "#app/system/pokemon-data"; import { BattlerTagType } from "#enums/battler-tag-type"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import { randSeedInt } from "#app/utils"; +import { randSeedInt, randSeedItem } from "#app/utils"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/fightOrFlight"; @@ -56,7 +57,14 @@ export const FightOrFlightEncounter: MysteryEncounter = // Calculate boss mon const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER); - const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + let bossSpecies: PokemonSpecies; + if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { + const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); + const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); + bossSpecies = getPokemonSpecies( levelSpecies ); + } else { + bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + } const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true); encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender()); const config: EnemyPartyConfig = { diff --git a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts index d3679825ac8..ebea34253d1 100644 --- a/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts +++ b/src/data/mystery-encounters/encounters/uncommon-breed-encounter.ts @@ -12,7 +12,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode import { TrainerSlot } from "#app/data/trainer-config"; import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils"; import PokemonData from "#app/system/pokemon-data"; -import { isNullOrUndefined, randSeedInt } from "#app/utils"; +import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; import { SelfStatusMove } from "#app/data/move"; @@ -23,6 +23,7 @@ import { BerryModifier } from "#app/modifier/modifier"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { Stat } from "#enums/stat"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; +import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounters/uncommonBreed"; @@ -51,7 +52,14 @@ export const UncommonBreedEncounter: MysteryEncounter = // Calculate boss mon // Level equal to 2 below highest party member const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2; - const species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + let species: PokemonSpecies; + if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) { + const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!); + const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, eventEncounter.allowEvolution ?? false, true, scene.gameMode); + species = getPokemonSpecies( levelSpecies ); + } else { + species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true); + } const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true); // Pokemon will always have one of its egg moves in its moveset diff --git a/src/data/mystery-encounters/mystery-encounters.ts b/src/data/mystery-encounters/mystery-encounters.ts index 8c1c3bf6de4..8a747cd4cd4 100644 --- a/src/data/mystery-encounters/mystery-encounters.ts +++ b/src/data/mystery-encounters/mystery-encounters.ts @@ -177,7 +177,7 @@ export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter } const extremeBiomeEncounters: MysteryEncounterType[] = []; const nonExtremeBiomeEncounters: MysteryEncounterType[] = [ - MysteryEncounterType.FIELD_TRIP, + // MysteryEncounterType.FIELD_TRIP, Disabled for holiday event MysteryEncounterType.DANCING_LESSONS, // Is also in BADLANDS, DESERT, VOLCANO, WASTELAND, ABYSS ]; @@ -185,14 +185,14 @@ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [ MysteryEncounterType.MYSTERIOUS_CHALLENGERS, MysteryEncounterType.SHADY_VITAMIN_DEALER, MysteryEncounterType.THE_POKEMON_SALESMAN, - MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, + // MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, Disabled for holiday event MysteryEncounterType.THE_WINSTRATE_CHALLENGE, MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER ]; const civilizationBiomeEncounters: MysteryEncounterType[] = [ - MysteryEncounterType.DEPARTMENT_STORE_SALE, - MysteryEncounterType.PART_TIMER, + // MysteryEncounterType.DEPARTMENT_STORE_SALE, Disabled for holiday event + // MysteryEncounterType.PART_TIMER, Disabled for holiday event MysteryEncounterType.FUN_AND_GAMES, MysteryEncounterType.GLOBAL_TRADE_SYSTEM ]; diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index d99ca601bdf..707f6c5fdb9 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -1170,6 +1170,9 @@ function getGymLeaderPartyTemplate(scene: BattleScene) { export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) { return (scene: BattleScene, level: number, strength: PartyMemberStrength) => { let species = Utils.randSeedItem(speciesPool); + if (scene.gameMode.isClassic && scene.currentBattle.waveIndex === 20) { + ignoreEvolution = true; + } if (!ignoreEvolution) { species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex); } @@ -1229,7 +1232,7 @@ export const signatureSpecies: SignatureSpecies = { GIOVANNI: [ Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F ], FALKNER: [ Species.PIDGEY, Species.HOOTHOOT, Species.DODUO ], BUGSY: [ Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR ], - WHITNEY: [ Species.GIRAFARIG, Species.MILTANK ], + WHITNEY: [ Species.JIGGLYPUFF, Species.MILTANK, Species.AIPOM, Species.GIRAFARIG ], MORTY: [ Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE ], CHUCK: [ Species.POLIWRATH, Species.MANKEY ], JASMINE: [ Species.MAGNEMITE, Species.STEELIX ], @@ -1239,7 +1242,7 @@ export const signatureSpecies: SignatureSpecies = { BRAWLY: [ Species.MACHOP, Species.MAKUHITA ], WATTSON: [ Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE ], FLANNERY: [ Species.SLUGMA, Species.TORKOAL, Species.NUMEL ], - NORMAN: [ Species.SLAKOTH, Species.SPINDA, Species.CHANSEY, Species.KANGASKHAN ], + NORMAN: [ Species.SLAKOTH, Species.SPINDA, Species.ZIGZAGOON, Species.KECLEON ], WINONA: [ Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY ], TATE: [ Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE ], LIZA: [ Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR ], @@ -1247,16 +1250,16 @@ export const signatureSpecies: SignatureSpecies = { ROARK: [ Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE ], GARDENIA: [ Species.ROSELIA, Species.TANGELA, Species.TURTWIG ], MAYLENE: [ Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR ], - CRASHER_WAKE: [ Species.BUIZEL, Species.MAGIKARP, Species.PIPLUP ], + CRASHER_WAKE: [ Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP ], FANTINA: [ Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB ], BYRON: [ Species.SHIELDON, Species.BRONZOR, Species.AGGRON ], CANDICE: [ Species.SNEASEL, Species.SNOVER, Species.SNORUNT ], VOLKNER: [ Species.SHINX, Species.CHINCHOU, Species.ROTOM ], - CILAN: [ Species.PANSAGE, Species.COTTONEE, Species.PETILIL ], - CHILI: [ Species.PANSEAR, Species.DARUMAKA, Species.HEATMOR ], - CRESS: [ Species.PANPOUR, Species.BASCULIN, Species.TYMPOLE ], - CHEREN: [ Species.LILLIPUP, Species.MINCCINO, Species.PATRAT ], - LENORA: [ Species.KANGASKHAN, Species.DEERLING, Species.AUDINO ], + CILAN: [ Species.PANSAGE, Species.FOONGUS, Species.PETILIL ], + CHILI: [ Species.PANSEAR, Species.DARUMAKA, Species.NUMEL ], + CRESS: [ Species.PANPOUR, Species.TYMPOLE, Species.SLOWPOKE ], + CHEREN: [ Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE ], + LENORA: [ Species.PATRAT, Species.DEERLING, Species.AUDINO ], ROXIE: [ Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI ], BURGH: [ Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST ], ELESA: [ Species.EMOLGA, Species.BLITZLE, Species.JOLTIK ], @@ -1289,7 +1292,7 @@ export const signatureSpecies: SignatureSpecies = { BRASSIUS: [ Species.SMOLIV, Species.SHROOMISH, Species.ODDISH ], IONO: [ Species.TADBULB, Species.WATTREL, Species.VOLTORB ], KOFU: [ Species.VELUZA, Species.WIGLETT, Species.WINGULL ], - LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.KOMALA ], + LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA ], RYME: [ Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU ], TULIP: [ Species.GIRAFARIG, Species.FLITTLE, Species.RALTS ], GRUSHA: [ Species.CETODDLE, Species.ALOLA_VULPIX, Species.CUBCHOO ], @@ -1852,7 +1855,7 @@ export const trainerConfigs: TrainerConfigs = { [TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL) .setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE) - .setEventModifierRewardFuncs(() => modifierTypes.SHINY_CHARM, () => modifierTypes.ABILITY_CHARM) + .setEventModifierRewardFuncs(() => modifierTypes.SHINY_CHARM, () => modifierTypes.ABILITY_CHARM, () => modifierTypes.CATCHING_CHARM) .setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.CHIKORITA, Species.CYNDAQUIL, Species.TOTODILE, Species.TREECKO, Species.TORCHIC, Species.MUDKIP, Species.TURTWIG, Species.CHIMCHAR, Species.PIPLUP, Species.SNIVY, Species.TEPIG, Species.OSHAWOTT, Species.CHESPIN, Species.FENNEKIN, Species.FROAKIE, Species.ROWLET, Species.LITTEN, Species.POPPLIO, Species.GROOKEY, Species.SCORBUNNY, Species.SOBBLE, Species.SPRIGATITO, Species.FUECOCO, Species.QUAXLY ], TrainerSlot.TRAINER, true, (p => p.abilityIndex = 0))) .setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEY, Species.HOOTHOOT, Species.TAILLOW, Species.STARLY, Species.PIDOVE, Species.FLETCHLING, Species.PIKIPEK, Species.ROOKIDEE, Species.WATTREL ], TrainerSlot.TRAINER, true)), diff --git a/src/data/weather.ts b/src/data/weather.ts index 0a76a015402..24434206bcd 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -242,7 +242,7 @@ export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainTyp return i18next.t("terrain:defaultBlockMessage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), terrainName: getTerrainName(terrainType) }); } -interface WeatherPoolEntry { +export interface WeatherPoolEntry { weatherType: WeatherType; weight: integer; } @@ -373,6 +373,10 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a break; } + if (arena.biomeType === Biome.TOWN && arena.scene.eventManager.isEventActive() && arena.scene.eventManager.activeEvent()?.weather?.length > 0) { + arena.scene.eventManager.activeEvent().weather.map(w => weatherPool.push(w)); + } + if (weatherPool.length > 1) { let totalWeight = 0; weatherPool.forEach(w => totalWeight += w.weight); diff --git a/src/loading-scene.ts b/src/loading-scene.ts index c49b8d5aaa9..2e9484a847d 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -246,9 +246,9 @@ export class LoadingScene extends SceneBase { } const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ]; if (lang && availableLangs.includes(lang)) { - this.loadImage("halloween2024-event-" + lang, "events"); + this.loadImage("winter_holidays2024-event-" + lang, "events"); } else { - this.loadImage("halloween2024-event-en", "events"); + this.loadImage("winter_holidays2024-event-en", "events"); } this.loadAtlas("statuses", ""); diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 57b3ced1813..540af8a0b41 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -1093,7 +1093,10 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator { if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Moves)) { return new TmModifierType(pregenArgs[0] as Moves); } - const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm))); + const partyMemberCompatibleTms = party.map(p => { + const previousLevelMoves = p.getLearnableLevelMoves(); + return (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm) && !previousLevelMoves.find(lm=>lm === tm)); + }); const tierUniqueCompatibleTms = partyMemberCompatibleTms.flat().filter(tm => tmPoolTiers[tm] === tier).filter(tm => !allMoves[tm].name.endsWith(" (N)")).filter((tm, i, array) => array.indexOf(tm) === i); if (!tierUniqueCompatibleTms.length) { return null; diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index d797e4360ac..456d548a9ba 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -39,7 +39,7 @@ export class TrainerVictoryPhase extends BattlePhase { // Validate Voucher for boss trainers if (vouchers.hasOwnProperty(TrainerType[trainerType])) { if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { - this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [ modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM ][vouchers[TrainerType[trainerType]].voucherType])); + this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [ modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM ][vouchers[TrainerType[trainerType]].voucherType])); } } // Breeders in Space achievement diff --git a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts index c226d60a9b4..71fb695111a 100644 --- a/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/delibirdy-encounter.test.ts @@ -56,7 +56,7 @@ describe("Delibird-y - Mystery Encounter", () => { await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty); expect(DelibirdyEncounter.encounterType).toBe(MysteryEncounterType.DELIBIRDY); - expect(DelibirdyEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT); + expect(DelibirdyEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON); expect(DelibirdyEncounter.dialogue).toBeDefined(); expect(DelibirdyEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]); expect(DelibirdyEncounter.dialogue.outro).toStrictEqual([{ text: `${namespace}:outro` }]); diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 9515be7b49e..926da91b352 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -2,6 +2,9 @@ import BattleScene from "#app/battle-scene"; import { TextStyle, addTextObject } from "#app/ui/text"; import { nil } from "#app/utils"; import i18next from "i18next"; +import { Species } from "#enums/species"; +import { WeatherPoolEntry } from "#app/data/weather"; +import { WeatherType } from "#enums/weather-type"; export enum EventType { SHINY, @@ -16,6 +19,11 @@ interface EventBanner { availableLangs?: string[]; } +interface EventEncounter { + species: Species; + allowEvolution?: boolean; +} + interface TimedEvent extends EventBanner { name: string; eventType: EventType; @@ -23,19 +31,46 @@ interface TimedEvent extends EventBanner { friendshipMultiplier?: number; startDate: Date; endDate: Date; + uncommonBreedEncounters?: EventEncounter[]; + delibirdyBuff?: string[]; + weather?: WeatherPoolEntry[]; } const timedEvents: TimedEvent[] = [ { - name: "Halloween Update", + name: "Winter Holiday Update", eventType: EventType.SHINY, shinyMultiplier: 2, - friendshipMultiplier: 2, - startDate: new Date(Date.UTC(2024, 9, 27, 0)), - endDate: new Date(Date.UTC(2024, 10, 4, 0)), - bannerKey: "halloween2024-event-", + friendshipMultiplier: 1, + startDate: new Date(Date.UTC(2024, 11, 21, 0)), + endDate: new Date(Date.UTC(2025, 0, 4, 0)), + bannerKey: "winter_holidays2024-event-", scale: 0.21, - availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ] + availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ], + uncommonBreedEncounters: [ + { species: Species.GIMMIGHOUL }, + { species: Species.DELIBIRD }, + { species: Species.STANTLER, allowEvolution: true }, + { species: Species.CYNDAQUIL, allowEvolution: true }, + { species: Species.PIPLUP, allowEvolution: true }, + { species: Species.CHESPIN, allowEvolution: true }, + { species: Species.BALTOY, allowEvolution: true }, + { species: Species.SNOVER, allowEvolution: true }, + { species: Species.CHINGLING, allowEvolution: true }, + { species: Species.LITWICK, allowEvolution: true }, + { species: Species.CUBCHOO, allowEvolution: true }, + { species: Species.SWIRLIX, allowEvolution: true }, + { species: Species.AMAURA, allowEvolution: true }, + { species: Species.MUDBRAY, allowEvolution: true }, + { species: Species.ROLYCOLY, allowEvolution: true }, + { species: Species.MILCERY, allowEvolution: true }, + { species: Species.SMOLIV, allowEvolution: true }, + { species: Species.ALOLA_VULPIX, allowEvolution: true }, + { species: Species.GALAR_DARUMAKA, allowEvolution: true }, + { species: Species.IRON_BUNDLE } + ], + delibirdyBuff: [ "CATCHING_CHARM", "SHINY_CHARM", "ABILITY_CHARM", "EXP_CHARM", "SUPER_EXP_CHARM", "HEALING_CHARM" ], + weather: [{ weatherType: WeatherType.SNOW, weight: 1 }] } ]; diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index bd3561dd0b4..0d20753f069 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -362,12 +362,12 @@ export default class PartyUiHandler extends MessageUiHandler { if (p !== this.transferCursor) { // this skips adding the able/not able labels on the pokemon doing the transfer if (matchingModifier) { // if matchingModifier exists then the item exists on the new pokemon if (matchingModifier.getMaxStackCount(this.scene) === matchingModifier.stackCount) { // checks to see if the stack of items is at max stack; if so, set the description label to "Not able" - ableToTransfer = "Not able"; + ableToTransfer = i18next.t("partyUiHandler:notAble"); } else { // if the pokemon isn't at max stack, make the label "Able" - ableToTransfer = "Able"; + ableToTransfer = i18next.t("partyUiHandler:able"); } } else { // if matchingModifier doesn't exist, that means the pokemon doesn't have any of the item, and we need to show "Able" - ableToTransfer = "Able"; + ableToTransfer = i18next.t("partyUiHandler:able"); } } else { // this else relates to the transfer pokemon. We set the text to be blank so there's no "Able"/"Not able" text ableToTransfer = "";