small ME balance changes

This commit is contained in:
ImperialSympathizer 2024-08-28 17:48:09 -04:00
parent 162a3b1d5b
commit ed8d502cf4
13 changed files with 87 additions and 73 deletions

View File

@ -22,7 +22,7 @@ import {
getModifierPoolForType,
getModifierType,
getPartyLuckValue,
modifierTypes
modifierTypes, PokemonHeldItemModifierType
} from "./modifier/modifier-type";
import AbilityBar from "./ui/ability-bar";
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, ChangeMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
@ -2521,13 +2521,19 @@ export default class BattleScene extends SceneBase {
party.forEach((enemyPokemon: EnemyPokemon, i: integer) => {
if (heldModifiersConfigs && i < heldModifiersConfigs.length && heldModifiersConfigs[i] && heldModifiersConfigs[i].length > 0) {
heldModifiersConfigs[i].forEach(mt => {
const stackCount = mt.stackCount ?? 1;
// const isTransferable = mt.isTransferable ?? true;
const modifier = mt.modifierType.newModifier(enemyPokemon);
modifier.stackCount = stackCount;
// TODO: set isTransferable
// modifier.setIsTransferable(isTransferable);
this.addEnemyModifier(modifier, true);
if (mt.modifier instanceof PokemonHeldItemModifierType) {
const stackCount = mt.stackCount ?? 1;
// const isTransferable = mt.isTransferable ?? true;
const modifier = mt.modifier.newModifier(enemyPokemon);
modifier.stackCount = stackCount;
// TODO: set isTransferable
// modifier.setIsTransferable(isTransferable);
this.addEnemyModifier(modifier, true);
} else {
const modifier = mt.modifier as PokemonHeldItemModifier;
modifier.pokemonId = enemyPokemon.id;
this.addEnemyModifier(modifier, true);
}
});
} else {
const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && !!this.currentBattle.trainer?.config.isBoss);

View File

@ -13,6 +13,8 @@ import { IEggOptions } from "#app/data/egg";
import { EggSourceType } from "#enums/egg-source-types";
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";
/** the i18n namespace for the encounter */
const namespace = "mysteryEncounter:aTrainersTest";
@ -136,7 +138,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
},
async (scene: BattleScene) => {
const encounter = scene.currentBattle.mysteryEncounter!;
// Spawn standard trainer battle with memory mushroom reward
// Battle the stat trainer for an Egg and great rewards
const config: EnemyPartyConfig = encounter.enemyPartyConfigs[0];
await transitionMysteryEncounterIntroVisuals(scene);
@ -149,7 +151,7 @@ export const ATrainersTestEncounter: MysteryEncounter =
tier: EggTier.ULTRA
};
encounter.setDialogueToken("eggType", i18next.t(`${namespace}.eggTypes.epic`));
setEncounterRewards(scene, { fillRemaining: true }, [eggOptions]);
setEncounterRewards(scene, { guaranteedModifierTypeFuncs: [modifierTypes.SACRED_ASH], guaranteedModifierTiers: [ModifierTier.ROGUE, ModifierTier.ULTRA], fillRemaining: true }, [eggOptions]);
return initBattleWithEnemyConfig(scene, config);
}

View File

@ -141,11 +141,11 @@ export const DancingLessonsEncounter: MysteryEncounter =
species: species,
dataSource: oricorioData,
isBoss: true,
// Gets +1 to all stats on battle start
// Gets +1 to all stats except SPD on battle start
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.boss_enraged`);
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD], 1));
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF], 1));
}
}],
};

View File

@ -12,6 +12,7 @@ import { getRandomPlayerPokemon, getRandomSpeciesByStarterTier } from "#app/data
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
/** i18n namespace for encounter */
const namespace = "mysteryEncounter:darkDeal";
@ -125,25 +126,27 @@ export const DarkDealEncounter: MysteryEncounter =
// Removes random pokemon (including fainted) from party and adds name to dialogue data tokens
// Will never return last battle able mon and instead pick fainted/unable to battle
const removedPokemon = getRandomPlayerPokemon(scene, false, true);
// Get all the pokemon's held items
const modifiers = removedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
scene.removePokemonFromPlayerParty(removedPokemon);
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("pokeName", removedPokemon.getNameToRender());
// Store removed pokemon types
encounter.misc = [
removedPokemon.species.type1,
];
if (removedPokemon.species.type2) {
encounter.misc.push(removedPokemon.species.type2);
}
encounter.misc = {
removedTypes: removedPokemon.getTypes(),
modifiers
};
})
.withOptionPhase(async (scene: BattleScene) => {
// Give the player 5 Rogue Balls
const encounter = scene.currentBattle.mysteryEncounter!;
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.ROGUE_BALL));
// Start encounter with random legendary (7-10 starter strength) that has level additive
const bossTypes = scene.currentBattle.mysteryEncounter!.misc as Type[];
const bossTypes: Type[] = encounter.misc.removedTypes;
const bossModifiers: PokemonHeldItemModifier[] = encounter.misc.modifiers;
// Starter egg tier, 35/50/10/5 %odds for tiers 6/7/8/9+
const roll = randSeedInt(100);
const starterTier: number | [number, number] =
@ -152,6 +155,11 @@ export const DarkDealEncounter: MysteryEncounter =
const pokemonConfig: EnemyPokemonConfig = {
species: bossSpecies,
isBoss: true,
modifierConfigs: bossModifiers.map(m => {
return {
modifier: m
};
})
};
if (!isNullOrUndefined(bossSpecies.forms) && bossSpecies.forms.length > 0) {
pokemonConfig.formIndex = 0;

View File

@ -6,7 +6,7 @@ import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter"
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Species } from "#enums/species";
import { Nature } from "#app/data/nature";
import Pokemon, { PlayerPokemon, PokemonMove } from "#app/field/pokemon";
import Pokemon, { PokemonMove } from "#app/field/pokemon";
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
import { modifyPlayerPokemonBST } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
import { Moves } from "#enums/moves";
@ -30,6 +30,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_STRONG_STUFF)
.withEncounterTier(MysteryEncounterTier.COMMON)
.withSceneWaveRangeRequirement(10, 180) // waves 10 to 180
.withScenePartySizeRequirement(3, 6) // Must have at least 3 pokemon in party
.withHideWildIntroMessage(true)
.withAutoHideIntroVisuals(false)
.withIntroSpriteConfigs([
@ -124,35 +125,21 @@ export const TheStrongStuffEncounter: MysteryEncounter =
transitionMysteryEncounterIntroVisuals(scene, true, true, 50);
});
// -20 to all base stats of highest BST, +10 to all base stats of rest of party
// Get highest BST mon
const party = scene.getParty();
let highestBst: PlayerPokemon | null = null;
let statTotal = 0;
for (const pokemon of party) {
if (!highestBst) {
highestBst = pokemon;
statTotal = pokemon.getSpeciesForm().getBaseStatTotal();
continue;
}
const total = pokemon.getSpeciesForm().getBaseStatTotal();
if (total > statTotal) {
highestBst = pokemon;
statTotal = total;
}
}
if (!highestBst) {
highestBst = party[0];
}
modifyPlayerPokemonBST(highestBst, -20);
for (const pokemon of party) {
if (highestBst.id === pokemon.id) {
continue;
}
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
// Sort party by bst (inverted to pop 2 highest off end)
const sortedParty = scene.getParty().slice(0)
.sort((pokemon1, pokemon2) => {
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
return pokemon1Bst - pokemon2Bst;
});
const highestBst = sortedParty.pop()!;
const highestBst2 = sortedParty.pop()!;
modifyPlayerPokemonBST(highestBst, -15);
modifyPlayerPokemonBST(highestBst2, -15);
// +10 for the rest
for (const pokemon of sortedParty) {
modifyPlayerPokemonBST(pokemon, 10);
}

View File

@ -32,7 +32,7 @@ const namespace = "mysteryEncounter:theWinstrateChallenge";
export const TheWinstrateChallengeEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.THE_WINSTRATE_CHALLENGE)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withSceneWaveRangeRequirement(80, 180)
.withSceneWaveRangeRequirement(100, 180)
.withIntroSpriteConfigs([
{
spriteKey: "vito",

View File

@ -4,7 +4,7 @@ import { getNatureName, Nature } from "#app/data/nature";
import { speciesStarters } from "#app/data/pokemon-species";
import { getStatName } from "#app/data/pokemon-stat";
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
import { PokemonFormChangeItemModifier, PokemonHeldItemModifier } from "#app/modifier/modifier";
import { AbilityAttr } from "#app/system/game-data";
import PokemonData from "#app/system/pokemon-data";
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
@ -247,9 +247,10 @@ export const TrainingSessionEncounter: MysteryEncounter =
playerPokemon.setNature(encounter.misc.chosenNature);
scene.gameData.setPokemonCaught(playerPokemon, false);
// Add pokemon and mods back
// Add pokemon and modifiers back
scene.getParty().push(playerPokemon);
for (const mod of modifiers.value) {
mod.pokemonId = playerPokemon.id;
scene.addModifier(mod, true, false, false, true);
}
scene.updateModifiers(true);
@ -385,10 +386,10 @@ function getEnemyConfig(scene: BattleScene, playerPokemon: PlayerPokemon, segmen
playerPokemon.resetSummonData();
// Passes modifiers by reference
modifiers.value = scene.findModifiers((m) => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === playerPokemon.id) as PokemonHeldItemModifier[];
modifiers.value = playerPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier));
const modifierConfigs = modifiers.value.map((mod) => {
return {
modifierType: mod.type
modifier: mod
};
}) as HeldModifierConfig[];

View File

@ -31,7 +31,7 @@ const SOUND_EFFECT_WAIT_TIME = 700;
export const TrashToTreasureEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.TRASH_TO_TREASURE)
.withEncounterTier(MysteryEncounterTier.ULTRA)
.withSceneWaveRangeRequirement(10, 180)
.withSceneWaveRangeRequirement(60, 180)
.withMaxAllowedEncounters(1)
.withIntroSpriteConfigs([
{

View File

@ -94,6 +94,7 @@ const STANDARD_BST_TRANSFORM_BASE_VALUES = [40, 50];
export const WeirdDreamEncounter: MysteryEncounter =
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.WEIRD_DREAM)
.withEncounterTier(MysteryEncounterTier.ROGUE)
.withSceneWaveRangeRequirement(10, 180)
.withIntroSpriteConfigs([
{
spriteKey: "girawitch",
@ -111,7 +112,6 @@ export const WeirdDreamEncounter: MysteryEncounter =
text: `${namespace}.intro_dialogue`,
},
])
.withSceneWaveRangeRequirement(10, 180)
.withTitle(`${namespace}.title`)
.withDescription(`${namespace}.description`)
.withQuery(`${namespace}.query`)

View File

@ -10,7 +10,7 @@ import * as Utils from "../utils";
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
import { getLevelTotalExp } from "../data/exp";
import { Stat } from "../data/pokemon-stat";
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier } from "../modifier/modifier";
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, PokemonBaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempBattleStatBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, PokemonBaseStatFlatModifier } from "../modifier/modifier";
import { PokeballType } from "../data/pokeball";
import { Gender } from "../data/gender";
import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
@ -778,19 +778,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!this.stats) {
this.stats = [ 0, 0, 0, 0, 0, 0 ];
}
const baseStats = this.getSpeciesForm().baseStats.slice(0);
this.scene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats);
if (this.fusionSpecies) {
const fusionBaseStats = this.getFusionSpeciesForm().baseStats;
for (let s = 0; s < this.stats.length; s++) {
baseStats[s] = Math.ceil((baseStats[s] + fusionBaseStats[s]) / 2);
}
} else if (this.scene.gameMode.isSplicedOnly) {
for (let s = 0; s < this.stats.length; s++) {
baseStats[s] = Math.ceil(baseStats[s] / 2);
}
}
this.scene.applyModifiers(PokemonBaseStatModifier, this.isPlayer(), this, baseStats);
const baseStats = this.calculateBaseStats();
const stats = Utils.getEnumValues(Stat);
for (const s of stats) {
const isHp = s === Stat.HP;
@ -822,6 +810,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.scene.applyModifier(PokemonIncrementingStatModifier, this.isPlayer(), this, this.stats);
}
calculateBaseStats(): number[] {
const baseStats = this.getSpeciesForm().baseStats.slice(0);
// Shuckle Juice
this.scene.applyModifiers(PokemonBaseStatTotalModifier, this.isPlayer(), this, baseStats);
// Old Gateau
this.scene.applyModifiers(PokemonBaseStatFlatModifier, this.isPlayer(), this, baseStats);
if (this.fusionSpecies) {
const fusionBaseStats = this.getFusionSpeciesForm().baseStats;
for (let s = 0; s < this.stats.length; s++) {
baseStats[s] = Math.ceil((baseStats[s] + fusionBaseStats[s]) / 2);
}
} else if (this.scene.gameMode.isSplicedOnly) {
for (let s = 0; s < this.stats.length; s++) {
baseStats[s] = Math.ceil(baseStats[s] / 2);
}
}
// Vitamins
this.scene.applyModifiers(PokemonBaseStatModifier, this.isPlayer(), this, baseStats);
return baseStats;
}
getNature(): Nature {
return this.natureOverride !== -1 ? this.natureOverride : this.nature;
}

View File

@ -1,7 +1,8 @@
import { PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
export default interface HeldModifierConfig {
modifierType: PokemonHeldItemModifierType;
modifier: PokemonHeldItemModifierType | PokemonHeldItemModifier;
stackCount?: number;
isTransferable?: boolean;
}

View File

@ -132,9 +132,9 @@ class DefaultOverrides {
// -------------------------
/** 1 to 256, set to null to ignore */
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = null;
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = 256;
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null;
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = null;
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = MysteryEncounterType.DARK_DEAL;
// -------------------------
// MODIFIER / ITEM OVERRIDES

View File

@ -119,7 +119,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(Species.ORICORIO);
expect(enemyField[0].summonData.battleStats).toEqual([1, 1, 1, 1, 1, 0, 0]);
expect(enemyField[0].summonData.battleStats).toEqual([1, 1, 1, 1, 0, 0, 0]);
const moveset = enemyField[0].moveset.map(m => m?.moveId);
expect(moveset.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy();