updating AI for Slumbering Snorlax ME, and small ME balance changes

This commit is contained in:
ImperialSympathizer 2024-09-17 23:19:13 -04:00
parent 6affced66a
commit 888f971c45
12 changed files with 45 additions and 51 deletions

View File

@ -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;
}
}
/**

View File

@ -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();

View File

@ -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`,

View File

@ -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.

View File

@ -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,

View File

@ -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];
/**

View File

@ -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.

View File

@ -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));

View File

@ -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]);
}
}

View File

@ -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!"
}

View File

@ -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}}!"
}

View File

@ -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 () => {