diff --git a/src/data/move.ts b/src/data/move.ts index e23a40c9379..ed1b8046694 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1,16 +1,15 @@ -import { ChargeAnim, MoveChargeAnim, initMoveAnim, loadMoveAnimAssets } from "./battle-anims"; -import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, TrappedTag, SubstituteTag, TypeBoostTag } from "./battler-tags"; +import { ChargeAnim, initMoveAnim, loadMoveAnimAssets, MoveChargeAnim } from "./battle-anims"; +import { EncoreTag, GulpMissileTag, HelpingHandTag, SemiInvulnerableTag, ShellTrapTag, StockpilingTag, SubstituteTag, TrappedTag, TypeBoostTag } from "./battler-tags"; import { getPokemonNameWithAffix } from "../messages"; import Pokemon, { AttackMoveResult, EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove, TurnMove } from "../field/pokemon"; -import { StatusEffect, getStatusEffectHealText, isNonVolatileStatusEffect, getNonVolatileStatusEffects } from "./status-effect"; +import { getNonVolatileStatusEffects, getStatusEffectHealText, isNonVolatileStatusEffect, StatusEffect } from "./status-effect"; import { getTypeDamageMultiplier, Type } from "./type"; -import { Constructor } from "#app/utils"; +import { Constructor, NumberHolder } from "#app/utils"; import * as Utils from "../utils"; import { WeatherType } from "./weather"; import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag"; -import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, BlockItemTheftAbAttr, applyPostAttackAbAttrs, ConfusionOnStatusEffectAbAttr, HealFromBerryUseAbAttr, IgnoreProtectOnContactAbAttr, IgnoreMoveEffectsAbAttr, applyPreDefendAbAttrs, MoveEffectChanceMultiplierAbAttr, WonderSkinAbAttr, applyPreAttackAbAttrs, MoveTypeChangeAbAttr, UserFieldMoveTypePowerBoostAbAttr, FieldMoveTypePowerBoostAbAttr, AllyMoveCategoryPowerBoostAbAttr, VariableMovePowerAbAttr } from "./ability"; -import { allAbilities } from "./ability"; -import { PokemonHeldItemModifier, BerryModifier, PreserveBerryModifier, PokemonMoveAccuracyBoosterModifier, AttackTypeBoosterModifier, PokemonMultiHitModifier } from "../modifier/modifier"; +import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability"; +import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier"; import { BattlerIndex, BattleType } from "../battle"; import { TerrainType } from "./terrain"; import { ModifierPoolType } from "#app/modifier/modifier-type"; @@ -25,7 +24,7 @@ import { Biome } from "#enums/biome"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import { MoveUsedEvent } from "#app/events/battle-scene"; -import { Stat, type BattleStat, type EffectiveStat, BATTLE_STATS, EFFECTIVE_STATS, getStatKey } from "#app/enums/stat"; +import { BATTLE_STATS, type BattleStat, EFFECTIVE_STATS, type EffectiveStat, getStatKey, Stat } from "#app/enums/stat"; import { PartyStatusCurePhase } from "#app/phases/party-status-cure-phase"; import { BattleEndPhase } from "#app/phases/battle-end-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase"; @@ -36,7 +35,6 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { SwitchPhase } from "#app/phases/switch-phase"; import { SwitchSummonPhase } from "#app/phases/switch-summon-phase"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "./pokemon-forms"; -import { NumberHolder } from "#app/utils"; import { GameMode } from "#app/game-mode"; import { applyChallenges, ChallengeType } from "./challenge"; @@ -2417,6 +2415,10 @@ export class BypassSleepAttr extends MoveAttr { return false; } + + getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + return user.status && user.status.effect === StatusEffect.SLEEP ? 100 : -10; + } } /** diff --git a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts index b94b58b700d..f7666fa1b37 100644 --- a/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts +++ b/src/data/mystery-encounters/encounters/a-trainers-test-encounter.ts @@ -15,8 +15,6 @@ import { EggTier } from "#enums/egg-type"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { ModifierTier } from "#app/modifier/modifier-tier"; import { modifierTypes } from "#app/modifier/modifier-type"; -import { MessagePhase } from "#app/phases/message-phase"; -import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase"; /** the i18n namespace for the encounter */ const namespace = "mysteryEncounter:aTrainersTest"; @@ -153,19 +151,7 @@ export const ATrainersTestEncounter: MysteryEncounter = tier: EggTier.ULTRA }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`)); - if (scene.gameData.eggs.length >= 99) { - // Eggs already full, give 2 10x Voucher instead - encounter.dialogue.outro = []; - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, undefined, () => { - scene.unshiftPhase(new MessagePhase(scene, i18next.t(`${namespace}.egg_list_full_dialogue`), undefined, true, undefined, i18next.t(`trainerNames:${encounter.misc.trainerNameKey}`))); - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER_PREMIUM)); - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER_PREMIUM)); - }); - } else { - encounter.dialogue.outro = [{ text: `${namespace}.outro` }]; - setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]); - } - + setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]); return initBattleWithEnemyConfig(scene, config); } ) @@ -187,17 +173,13 @@ export const ATrainersTestEncounter: MysteryEncounter = tier: EggTier.GREAT }; encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.rare`)); - if (scene.gameData.eggs.length >= 99) { - // Eggs already full, give 2 10x Voucher instead - encounter.dialogue.outro = []; - scene.unshiftPhase(new MessagePhase(scene, i18next.t(`${namespace}.egg_list_full_dialogue`), undefined, true, undefined, i18next.t(`trainerNames:${encounter.misc.trainerNameKey}`))); - scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER_PLUS)); - setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }); - } else { - encounter.dialogue.outro = [{ text: `${namespace}.outro` }]; - setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [eggOptions]); - } + setEncounterRewards(scene, { fillRemaining: false, rerollMultiplier: -1 }, [eggOptions]); leaveEncounterWithoutBattle(scene); } ) + .withOutroDialogue([ + { + text: `${namespace}.outro` + } + ]) .build(); diff --git a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts index ca2fb59d198..346d8599546 100644 --- a/src/data/mystery-encounters/encounters/delibirdy-encounter.ts +++ b/src/data/mystery-encounters/encounters/delibirdy-encounter.ts @@ -42,7 +42,7 @@ export const DelibirdyEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY) .withEncounterTier(MysteryEncounterTier.GREAT) .withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES) - .withSceneRequirement(new MoneyRequirement(0, 2)) // Must have enough money for it to spawn at the very least + .withSceneRequirement(new MoneyRequirement(0, 1.5)) // Must have enough money for it to spawn at the very least .withPrimaryPokemonRequirement(new CombinationPokemonRequirement( // Must also have either option 2 or 3 available to spawn new HeldItemRequirement(OPTION_2_ALLOWED_MODIFIERS), new HeldItemRequirement(OPTION_3_DISALLOWED_MODIFIERS, 1, true) @@ -109,7 +109,7 @@ export const DelibirdyEncounter: MysteryEncounter = .withOption( MysteryEncounterOptionBuilder .newOptionWithMode(MysteryEncounterOptionMode.DISABLED_OR_DEFAULT) - .withSceneMoneyRequirement(0, 2) // Must have money to spawn + .withSceneMoneyRequirement(0, 1.5) // Must have money to spawn .withDialogue({ buttonLabel: `${namespace}.option.1.label`, buttonTooltip: `${namespace}.option.1.tooltip`, diff --git a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts index 849f87a86a6..2690460757f 100644 --- a/src/data/mystery-encounters/encounters/safari-zone-encounter.ts +++ b/src/data/mystery-encounters/encounters/safari-zone-encounter.ts @@ -25,7 +25,7 @@ const namespace = "mysteryEncounter:safariZone"; const TRAINER_THROW_ANIMATION_TIMES = [512, 184, 768]; -const SAFARI_MONEY_MULTIPLIER = 2.75; +const SAFARI_MONEY_MULTIPLIER = 2; /** * Safari Zone encounter. diff --git a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts index 2627a0f819b..bfccc46ee0f 100644 --- a/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts +++ b/src/data/mystery-encounters/encounters/slumbering-snorlax-encounter.ts @@ -7,17 +7,18 @@ import { StatusEffect } from "#app/data/status-effect"; import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter"; import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option"; import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements"; -import { EnemyPartyConfig, EnemyPokemonConfig, initBattleWithEnemyConfig, loadCustomMovesForEncounter, leaveEncounterWithoutBattle, setEncounterExp, setEncounterRewards, generateModifierType, } from "../utils/encounter-phase-utils"; +import { EnemyPartyConfig, EnemyPokemonConfig, generateModifierType, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, loadCustomMovesForEncounter, setEncounterExp, setEncounterRewards, } from "../utils/encounter-phase-utils"; import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; import { Moves } from "#enums/moves"; import { BattlerIndex } from "#app/battle"; -import { PokemonMove } from "#app/field/pokemon"; +import { AiType, PokemonMove } from "#app/field/pokemon"; import { getPokemonSpecies } from "#app/data/pokemon-species"; import { MysteryEncounterTier } from "#enums/mystery-encounter-tier"; import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode"; import { PartyHealPhase } from "#app/phases/party-heal-phase"; import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode"; import { BerryType } from "#enums/berry-type"; +import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data"; /** i18n namespace for the encounter */ const namespace = "mysteryEncounter:slumberingSnorlax"; @@ -39,7 +40,7 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = fileRoot: "pokemon", hasShadow: true, tint: 0.25, - scale: 1.5, + scale: 1.25, repeat: true, y: 5, }, @@ -70,6 +71,8 @@ export const SlumberingSnorlaxEncounter: MysteryEncounter = stackCount: 2 }, ], + mysteryEncounterPokemonData: new MysteryEncounterPokemonData({ spriteScale: 1.25 }), + aiType: AiType.SMART // Required to ensure Snorlax uses Sleep Talk while it is asleep }; const config: EnemyPartyConfig = { levelAdditiveModifier: 0.5, diff --git a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts index e992f189272..4db9222b19d 100644 --- a/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts +++ b/src/data/mystery-encounters/encounters/teleporting-hijinks-encounter.ts @@ -25,8 +25,8 @@ import { getEncounterPokemonLevelForWave } from "#app/data/mystery-encounters/ut /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:teleportingHijinks"; -const MONEY_COST_MULTIPLIER = 2.5; -const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND]; +const MONEY_COST_MULTIPLIER = 1.75; +const BIOME_CANDIDATES = [Biome.SPACE, Biome.FAIRY_CAVE, Biome.LABORATORY, Biome.ISLAND, Biome.WASTELAND, Biome.DOJO]; const MACHINE_INTERFACING_TYPES = [Type.ELECTRIC, Type.STEEL]; /** diff --git a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts index 951367ab2dc..62f4eb2e830 100644 --- a/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-pokemon-salesman-encounter.ts @@ -20,7 +20,7 @@ import { Abilities } from "#enums/abilities"; /** the i18n namespace for this encounter */ const namespace = "mysteryEncounter:pokemonSalesman"; -const MAX_POKEMON_PRICE_MULTIPLIER = 6; +const MAX_POKEMON_PRICE_MULTIPLIER = 4; /** * Pokemon Salesman encounter. diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index 36ecef0435c..be2b7b1a0f1 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -3,7 +3,7 @@ import { biomeLinks, BiomePoolTier } from "#app/data/biomes"; import MysteryEncounterOption from "#app/data/mystery-encounters/mystery-encounter-option"; import { AVERAGE_ENCOUNTERS_PER_RUN_TARGET, WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters"; import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils"; -import Pokemon, { FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; +import Pokemon, { AiType, FieldPosition, PlayerPokemon, PokemonMove, PokemonSummonData } from "#app/field/pokemon"; import { CustomModifierSettings, ModifierPoolType, ModifierType, ModifierTypeGenerator, ModifierTypeOption, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type"; import { MysteryEncounterBattlePhase, MysteryEncounterBattleStartCleanupPhase, MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#app/phases/mystery-encounter-phases"; import PokemonData from "#app/system/pokemon-data"; @@ -85,6 +85,7 @@ export interface EnemyPokemonConfig { modifierConfigs?: HeldModifierConfig[]; tags?: BattlerTagType[]; dataSource?: PokemonData; + aiType?: AiType; } export interface EnemyPartyConfig { @@ -288,6 +289,11 @@ export async function initBattleWithEnemyConfig(scene: BattleScene, partyConfig: enemyPokemon.summonData.gender = config.gender!; } + // Set AI type + if (!isNullOrUndefined(config.aiType)) { + enemyPokemon.aiType = config.aiType; + } + // Set moves if (config?.moveSet && config.moveSet.length > 0) { const moves = config.moveSet.map(m => new PokemonMove(m)); diff --git a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts index 393ba5de26b..1bc9def466b 100644 --- a/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-pokemon-utils.ts @@ -723,10 +723,10 @@ export function getGoldenBugNetSpecies(): PokemonSpecies { const roll = randSeedInt(totalWeight); let w = 0; - for (const species of GOLDEN_BUG_NET_SPECIES_POOL) { - w += species[1]; + for (const speciesWeightPair of GOLDEN_BUG_NET_SPECIES_POOL) { + w += speciesWeightPair[1]; if (roll < w) { - return getPokemonSpecies(species); + return getPokemonSpecies(speciesWeightPair[0]); } } diff --git a/src/locales/en/bgm-name.json b/src/locales/en/bgm-name.json index 57ea2ee2491..7a8ec738c98 100644 --- a/src/locales/en/bgm-name.json +++ b/src/locales/en/bgm-name.json @@ -152,5 +152,5 @@ "mystery_encounter_fun_and_games": "PMD EoS Guildmaster Wigglytuff", "mystery_encounter_gen_5_gts": "BW GTS", "mystery_encounter_gen_6_gts": "XY GTS", - "mystery_encounter_delibirdy": "Firel - Delibir-dy!" + "mystery_encounter_delibirdy": "Firel - DeliDelivery!" } diff --git a/src/locales/en/mystery-encounters/a-trainers-test-dialogue.json b/src/locales/en/mystery-encounters/a-trainers-test-dialogue.json index 9d1cd7f6d14..c96c0d5f327 100644 --- a/src/locales/en/mystery-encounters/a-trainers-test-dialogue.json +++ b/src/locales/en/mystery-encounters/a-trainers-test-dialogue.json @@ -43,6 +43,5 @@ "epic": "an Epic Egg", "legendary": "a Legendary Egg" }, - "egg_list_full_dialogue": "Oh, it looks like you don't have room to carry another egg.$Here, take this instead!", "outro": "{{statTrainerName}} gave you {{eggType}}!" } \ No newline at end of file diff --git a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts index 13860e83baa..2505f28aa43 100644 --- a/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts +++ b/src/test/mystery-encounter/encounters/teleporting-hijinks-encounter.test.ts @@ -21,6 +21,8 @@ const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA]; const defaultBiome = Biome.CAVE; const defaultWave = 45; +const TRANSPORT_BIOMES = [Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE, Biome.WASTELAND, Biome.DOJO]; + describe("Teleporting Hijinks - Mystery Encounter", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -183,7 +185,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 1, undefined, true); expect(previousBiome).not.toBe(scene.arena.biomeType); - expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); + expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType); }); it("should start a battle against an enraged boss", { retry: 5 }, async () => { @@ -246,7 +248,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => { await runMysteryEncounterToEnd(game, 2, undefined, true); expect(previousBiome).not.toBe(scene.arena.biomeType); - expect([Biome.SPACE, Biome.ISLAND, Biome.LABORATORY, Biome.FAIRY_CAVE]).toContain(scene.arena.biomeType); + expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType); }); it("should start a battle against an enraged boss", async () => {