mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-29 10:49:29 +02:00
ME balance changes and bug fixes
This commit is contained in:
parent
64aee2ffd7
commit
3e751a477c
@ -1,10 +1,10 @@
|
|||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import UI from "./ui/ui";
|
import UI from "./ui/ui";
|
||||||
import Pokemon, { PlayerPokemon, EnemyPokemon } from "./field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "./field/pokemon";
|
||||||
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies } from "./data/pokemon-species";
|
import PokemonSpecies, { allSpecies, getPokemonSpecies, PokemonSpeciesFilter } from "./data/pokemon-species";
|
||||||
import { Constructor, isNullOrUndefined } from "#app/utils";
|
import { Constructor, isNullOrUndefined } from "#app/utils";
|
||||||
import * as Utils from "./utils";
|
import * as Utils from "./utils";
|
||||||
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonHpRestoreModifier, TurnHeldItemTransferModifier, HealingBoosterModifier, PersistentModifier, PokemonHeldItemModifier, ModifierPredicate, DoubleBattleChanceBoosterModifier, FusePokemonModifier, PokemonFormChangeItemModifier, TerastallizeModifier, overrideModifiers, overrideHeldItems, PokemonIncrementingStatModifier, ExpShareModifier, ExpBalanceModifier, MultipleParticipantExpBonusModifier, PokemonExpBoosterModifier } from "./modifier/modifier";
|
import { ConsumableModifier, ConsumablePokemonModifier, DoubleBattleChanceBoosterModifier, ExpBalanceModifier, ExpShareModifier, FusePokemonModifier, HealingBoosterModifier, Modifier, ModifierBar, ModifierPredicate, MultipleParticipantExpBonusModifier, overrideHeldItems, overrideModifiers, PersistentModifier, PokemonExpBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, PokemonHpRestoreModifier, PokemonIncrementingStatModifier, TerastallizeModifier, TurnHeldItemTransferModifier } from "./modifier/modifier";
|
||||||
import { PokeballType } from "./data/pokeball";
|
import { PokeballType } from "./data/pokeball";
|
||||||
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
|
import { initCommonAnims, initMoveAnim, loadCommonAnimAssets, loadMoveAnimAssets, populateAnims } from "./data/battle-anims";
|
||||||
import { Phase } from "./phase";
|
import { Phase } from "./phase";
|
||||||
@ -13,20 +13,9 @@ import { Arena, ArenaBase } from "./field/arena";
|
|||||||
import { GameData } from "./system/game-data";
|
import { GameData } from "./system/game-data";
|
||||||
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
|
import { addTextObject, getTextColor, TextStyle } from "./ui/text";
|
||||||
import { allMoves } from "./data/move";
|
import { allMoves } from "./data/move";
|
||||||
import {
|
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "./modifier/modifier-type";
|
||||||
ModifierPoolType,
|
|
||||||
getDefaultModifierTypeForTier,
|
|
||||||
getEnemyModifierTypesForWave,
|
|
||||||
getLuckString,
|
|
||||||
getLuckTextTint,
|
|
||||||
getModifierPoolForType,
|
|
||||||
getModifierType,
|
|
||||||
getPartyLuckValue,
|
|
||||||
modifierTypes, PokemonHeldItemModifierType
|
|
||||||
} from "./modifier/modifier-type";
|
|
||||||
import AbilityBar from "./ui/ability-bar";
|
import AbilityBar from "./ui/ability-bar";
|
||||||
import { BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, ChangeMovePriorityAbAttr, PostBattleInitAbAttr, applyAbAttrs, applyPostBattleInitAbAttrs } from "./data/ability";
|
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, ChangeMovePriorityAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "./data/ability";
|
||||||
import { allAbilities } from "./data/ability";
|
|
||||||
import Battle, { BattleType, FixedBattleConfig } from "./battle";
|
import Battle, { BattleType, FixedBattleConfig } from "./battle";
|
||||||
import { GameMode, GameModes, getGameMode } from "./game-mode";
|
import { GameMode, GameModes, getGameMode } from "./game-mode";
|
||||||
import FieldSpritePipeline from "./pipelines/field-sprite";
|
import FieldSpritePipeline from "./pipelines/field-sprite";
|
||||||
@ -46,7 +35,7 @@ import UIPlugin from "phaser3-rex-plugins/templates/ui/ui-plugin";
|
|||||||
import { addUiThemeOverrides } from "./ui/ui-theme";
|
import { addUiThemeOverrides } from "./ui/ui-theme";
|
||||||
import PokemonData from "./system/pokemon-data";
|
import PokemonData from "./system/pokemon-data";
|
||||||
import { Nature } from "./data/nature";
|
import { Nature } from "./data/nature";
|
||||||
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger, pokemonFormChanges, FormChangeItem, SpeciesFormChange } from "./data/pokemon-forms";
|
import { FormChangeItem, pokemonFormChanges, SpeciesFormChange, SpeciesFormChangeManualTrigger, SpeciesFormChangeTimeOfDayTrigger, SpeciesFormChangeTrigger } from "./data/pokemon-forms";
|
||||||
import { FormChangePhase } from "./phases/form-change-phase";
|
import { FormChangePhase } from "./phases/form-change-phase";
|
||||||
import { getTypeRgb } from "./data/type";
|
import { getTypeRgb } from "./data/type";
|
||||||
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
|
import PokemonSpriteSparkleHandler from "./field/pokemon-sprite-sparkle-handler";
|
||||||
@ -1081,6 +1070,11 @@ export default class BattleScene extends SceneBase {
|
|||||||
p.destroy();
|
p.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a ME, clear any residual visual sprites before reloading
|
||||||
|
if (this.currentBattle?.mysteryEncounter?.introVisuals) {
|
||||||
|
this.field.remove(this.currentBattle.mysteryEncounter?.introVisuals, true);
|
||||||
|
}
|
||||||
|
|
||||||
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
|
//@ts-ignore - allowing `null` for currentBattle causes a lot of trouble
|
||||||
this.currentBattle = null; // TODO: resolve ts-ignore
|
this.currentBattle = null; // TODO: resolve ts-ignore
|
||||||
|
|
||||||
@ -1111,6 +1105,8 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.trainer.setPosition(406, 186);
|
this.trainer.setPosition(406, 186);
|
||||||
this.trainer.setVisible(true);
|
this.trainer.setVisible(true);
|
||||||
|
|
||||||
|
this.mysteryEncounterSaveData = new MysteryEncounterSaveData();
|
||||||
|
|
||||||
this.updateGameInfo();
|
this.updateGameInfo();
|
||||||
|
|
||||||
if (reloadI18n) {
|
if (reloadI18n) {
|
||||||
|
@ -743,16 +743,21 @@ export abstract class BattleAnim {
|
|||||||
public target: Pokemon | null;
|
public target: Pokemon | null;
|
||||||
public sprites: Phaser.GameObjects.Sprite[];
|
public sprites: Phaser.GameObjects.Sprite[];
|
||||||
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
|
public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle;
|
||||||
public playOnEmptyField: boolean;
|
/**
|
||||||
|
* Will attempt to play as much of an animation as possible, even if not all targets are on the field.
|
||||||
|
* Will also play the animation, even if the user has selected "Move Animations" OFF in Settings.
|
||||||
|
* Exclusively used by MEs atm, for visual animations at the start of an encounter.
|
||||||
|
*/
|
||||||
|
public playRegardlessOfIssues: boolean;
|
||||||
|
|
||||||
private srcLine: number[];
|
private srcLine: number[];
|
||||||
private dstLine: number[];
|
private dstLine: number[];
|
||||||
|
|
||||||
constructor(user?: Pokemon, target?: Pokemon, playOnEmptyField: boolean = false) {
|
constructor(user?: Pokemon, target?: Pokemon, playRegardlessOfIssues: boolean = false) {
|
||||||
this.user = user ?? null;
|
this.user = user ?? null;
|
||||||
this.target = target ?? null;
|
this.target = target ?? null;
|
||||||
this.sprites = [];
|
this.sprites = [];
|
||||||
this.playOnEmptyField = playOnEmptyField;
|
this.playRegardlessOfIssues = playRegardlessOfIssues;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getAnim(): AnimConfig | null;
|
abstract getAnim(): AnimConfig | null;
|
||||||
@ -829,7 +834,7 @@ export abstract class BattleAnim {
|
|||||||
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
|
const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct?
|
||||||
const target = !isOppAnim ? this.target! : this.user!;
|
const target = !isOppAnim ? this.target! : this.user!;
|
||||||
|
|
||||||
if (!target?.isOnField() && !this.playOnEmptyField) {
|
if (!target?.isOnField() && !this.playRegardlessOfIssues) {
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
@ -896,7 +901,7 @@ export abstract class BattleAnim {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!scene.moveAnimations) {
|
if (!scene.moveAnimations && !this.playRegardlessOfIssues) {
|
||||||
return cleanUpAndComplete();
|
return cleanUpAndComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -932,7 +937,7 @@ export abstract class BattleAnim {
|
|||||||
const isUser = frame.target === AnimFrameTarget.USER;
|
const isUser = frame.target === AnimFrameTarget.USER;
|
||||||
if (isUser && target === user) {
|
if (isUser && target === user) {
|
||||||
continue;
|
continue;
|
||||||
} else if (this.playOnEmptyField && frame.target === AnimFrameTarget.TARGET && !target.isOnField()) {
|
} else if (this.playRegardlessOfIssues && frame.target === AnimFrameTarget.TARGET && !target.isOnField()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
|
const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET];
|
||||||
@ -1145,7 +1150,7 @@ export abstract class BattleAnim {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!scene.moveAnimations) {
|
if (!scene.moveAnimations && !this.playRegardlessOfIssues) {
|
||||||
return cleanUpAndComplete();
|
return cleanUpAndComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,9 +376,10 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
|||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Get Pokemon held items and filter for valid ones
|
// Get Pokemon held items and filter for valid ones
|
||||||
const validItems = pokemon.getHeldItems().filter(item => {
|
const validItems = pokemon.getHeldItems().filter(item => {
|
||||||
return item instanceof BypassSpeedChanceModifier ||
|
return (item instanceof BypassSpeedChanceModifier ||
|
||||||
item instanceof ContactHeldItemTransferChanceModifier ||
|
item instanceof ContactHeldItemTransferChanceModifier ||
|
||||||
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG);
|
(item instanceof AttackTypeBoosterModifier && (item.type as AttackTypeBoosterModifierType).moveType === Type.BUG)) &&
|
||||||
|
item.isTransferable;
|
||||||
});
|
});
|
||||||
|
|
||||||
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
@ -349,10 +349,18 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
newTypes.push(secondType);
|
newTypes.push(secondType);
|
||||||
|
|
||||||
|
// Apply the type changes (to both base and fusion, if pokemon is fused)
|
||||||
if (!pokemon.mysteryEncounterPokemonData) {
|
if (!pokemon.mysteryEncounterPokemonData) {
|
||||||
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
||||||
}
|
}
|
||||||
pokemon.mysteryEncounterPokemonData.types = newTypes;
|
pokemon.mysteryEncounterPokemonData.types = newTypes;
|
||||||
|
if (pokemon.isFusion()) {
|
||||||
|
if (!pokemon.fusionMysteryEncounterPokemonData) {
|
||||||
|
pokemon.fusionMysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
||||||
|
}
|
||||||
|
pokemon.fusionMysteryEncounterPokemonData.types = newTypes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
@ -415,10 +423,17 @@ function onYesAbilitySwap(scene: BattleScene, resolve) {
|
|||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Do ability swap
|
// Do ability swap
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
if (!pokemon.mysteryEncounterPokemonData) {
|
if (pokemon.isFusion()) {
|
||||||
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
if (!pokemon.fusionMysteryEncounterPokemonData) {
|
||||||
|
pokemon.fusionMysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
||||||
|
}
|
||||||
|
pokemon.fusionMysteryEncounterPokemonData.ability = encounter.misc.ability;
|
||||||
|
} else {
|
||||||
|
if (!pokemon.mysteryEncounterPokemonData) {
|
||||||
|
pokemon.mysteryEncounterPokemonData = new MysteryEncounterPokemonData();
|
||||||
|
}
|
||||||
|
pokemon.mysteryEncounterPokemonData.ability = encounter.misc.ability;
|
||||||
}
|
}
|
||||||
pokemon.mysteryEncounterPokemonData.ability = encounter.misc.ability;
|
|
||||||
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
encounter.setDialogueToken("chosenPokemon", pokemon.getNameToRender());
|
||||||
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
|
scene.ui.setMode(Mode.MESSAGE).then(() => resolve(true));
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,7 @@ import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
|||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:dancingLessons";
|
const namespace = "mysteryEncounter:dancingLessons";
|
||||||
@ -271,6 +272,9 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||||||
// Only Pokemon that have a Dancing move can be selected
|
// Only Pokemon that have a Dancing move can be selected
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
|
if (!pokemon.isAllowed()) {
|
||||||
|
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||||
|
}
|
||||||
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
@ -159,7 +159,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Get Pokemon held items and filter for valid ones
|
// Get Pokemon held items and filter for valid ones
|
||||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
return OPTION_2_ALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
return OPTION_2_ALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem) && it.isTransferable;
|
||||||
});
|
});
|
||||||
|
|
||||||
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
@ -179,9 +179,8 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon has valid item, it can be selected
|
||||||
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
const meetsReqs = encounter.options[1].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
@ -254,7 +253,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Get Pokemon held items and filter for valid ones
|
// Get Pokemon held items and filter for valid ones
|
||||||
const validItems = pokemon.getHeldItems().filter((it) => {
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
return !OPTION_3_DISALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem);
|
return !OPTION_3_DISALLOWED_MODIFIERS.some(heldItem => it.constructor.name === heldItem) && it.isTransferable;
|
||||||
});
|
});
|
||||||
|
|
||||||
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
@ -274,9 +273,8 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon has valid item, it can be selected
|
||||||
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
const meetsReqs = encounter.options[2].pokemonMeetsPrimaryRequirements(scene, pokemon);
|
||||||
if (!meetsReqs) {
|
if (!meetsReqs) {
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
|
@ -189,7 +189,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Burn random member
|
// Burn random member
|
||||||
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.BURN);
|
const burnable = nonFireTypes.filter(p => isNullOrUndefined(p.status) || isNullOrUndefined(p.status!.effect) || p.status?.effect === StatusEffect.NONE);
|
||||||
if (burnable?.length > 0) {
|
if (burnable?.length > 0) {
|
||||||
const roll = randSeedInt(burnable.length);
|
const roll = randSeedInt(burnable.length);
|
||||||
const chosenPokemon = burnable[roll];
|
const chosenPokemon = burnable[roll];
|
||||||
|
@ -68,6 +68,7 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
|||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
|
queueEncounterMessage(pokemon.scene, `${namespace}.option.1.stat_boost`);
|
||||||
// Randomly boost 1 stat 2 stages
|
// Randomly boost 1 stat 2 stages
|
||||||
|
// Cannot boost Spd, Acc, or Evasion
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [randSeedInt(4, 1)], 2));
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
|
@ -7,7 +7,7 @@ import { TrainerSlot } from "#app/data/trainer-config";
|
|||||||
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
@ -22,6 +22,7 @@ import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
|||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:funAndGames";
|
const namespace = "mysteryEncounter:funAndGames";
|
||||||
@ -110,12 +111,7 @@ export const FunAndGamesEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon that are not KOed/legal can be selected
|
// Only Pokemon that are not KOed/legal can be selected
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
const meetsReqs = pokemon.isAllowedInBattle();
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
if (!meetsReqs) {
|
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
@ -317,7 +317,6 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon has items to trade
|
// If pokemon has items to trade
|
||||||
const meetsReqs = pokemon.getHeldItems().filter((it) => {
|
const meetsReqs = pokemon.getHeldItems().filter((it) => {
|
||||||
|
@ -163,11 +163,12 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
|||||||
leaveEncounterWithoutBattle(scene);
|
leaveEncounterWithoutBattle(scene);
|
||||||
} else {
|
} else {
|
||||||
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
|
// Your highest level unfainted Pokemon gets OHKO. Start battle against a Gimmighoul (35%)
|
||||||
const highestLevelPokemon = getHighestLevelPlayerPokemon(
|
const highestLevelPokemon = getHighestLevelPlayerPokemon(scene, true);
|
||||||
scene,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
koPlayerPokemon(scene, highestLevelPokemon);
|
koPlayerPokemon(scene, highestLevelPokemon);
|
||||||
|
|
||||||
|
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
||||||
|
await showEncounterText(scene, `${namespace}.option.1.bad`);
|
||||||
|
|
||||||
// Handle game over edge case
|
// Handle game over edge case
|
||||||
const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle());
|
const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle());
|
||||||
if (allowedPokemon.length === 0) {
|
if (allowedPokemon.length === 0) {
|
||||||
@ -176,8 +177,6 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
|||||||
scene.unshiftPhase(new GameOverPhase(scene));
|
scene.unshiftPhase(new GameOverPhase(scene));
|
||||||
} else {
|
} else {
|
||||||
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
// Show which Pokemon was KOed, then start battle against Gimmighoul
|
||||||
encounter.setDialogueToken("pokeName", highestLevelPokemon.getNameToRender());
|
|
||||||
await showEncounterText(scene, `${namespace}.option.1.bad`);
|
|
||||||
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
transitionMysteryEncounterIntroVisuals(scene, true, true, 500);
|
||||||
setEncounterRewards(scene, { fillRemaining: true });
|
setEncounterRewards(scene, { fillRemaining: true });
|
||||||
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
await initBattleWithEnemyConfig(scene, encounter.enemyPartyConfigs[0]);
|
||||||
|
@ -8,10 +8,11 @@ import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { CHARMING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { getEncounterText, showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { showEncounterDialogue, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:partTimer";
|
const namespace = "mysteryEncounter:partTimer";
|
||||||
@ -117,11 +118,7 @@ export const PartTimerEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon non-KOd pokemon can be selected
|
// Only Pokemon non-KOd pokemon can be selected
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
if (!pokemon.isAllowedInBattle()) {
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
@ -198,11 +195,7 @@ export const PartTimerEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon non-KOd pokemon can be selected
|
// Only Pokemon non-KOd pokemon can be selected
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
if (!pokemon.isAllowedInBattle()) {
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
@ -254,7 +254,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
|||||||
let enemySpecies;
|
let enemySpecies;
|
||||||
let pokemon;
|
let pokemon;
|
||||||
scene.executeWithSeedOffset(() => {
|
scene.executeWithSeedOffset(() => {
|
||||||
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
enemySpecies = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
|
||||||
const level = scene.currentBattle.getLevelForWave();
|
const level = scene.currentBattle.getLevelForWave();
|
||||||
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode));
|
enemySpecies = getPokemonSpecies(enemySpecies.getWildSpeciesForLevel(level, true, false, scene.gameMode));
|
||||||
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
|
pokemon = scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, false);
|
||||||
@ -282,7 +282,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
|||||||
pokemon.calculateStats();
|
pokemon.calculateStats();
|
||||||
|
|
||||||
scene.currentBattle.enemyParty.unshift(pokemon);
|
scene.currentBattle.enemyParty.unshift(pokemon);
|
||||||
}, scene.currentBattle.waveIndex * 1000 + encounter.misc.safariPokemonRemaining);
|
}, scene.currentBattle.waveIndex * 1000 * encounter.misc.safariPokemonRemaining);
|
||||||
|
|
||||||
scene.gameData.setPokemonSeen(pokemon, true);
|
scene.gameData.setPokemonSeen(pokemon, true);
|
||||||
await pokemon.loadAssets();
|
await pokemon.loadAssets();
|
||||||
|
@ -9,12 +9,13 @@ import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-enc
|
|||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyDamageToPokemon, applyModifierTypeToPlayerPokemon, isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { getNatureName } from "#app/data/nature";
|
import { getNatureName } from "#app/data/nature";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounter:shadyVitaminDealer";
|
const namespace = "mysteryEncounter:shadyVitaminDealer";
|
||||||
@ -32,7 +33,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
|||||||
.withEncounterTier(MysteryEncounterTier.COMMON)
|
.withEncounterTier(MysteryEncounterTier.COMMON)
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)) // Must have the money for at least the cheap deal
|
.withSceneRequirement(new MoneyRequirement(0, VITAMIN_DEALER_CHEAP_PRICE_MULTIPLIER)) // Must have the money for at least the cheap deal
|
||||||
.withPrimaryPokemonHealthRatioRequirement([0.5, 1]) // At least 1 Pokemon must have above half HP
|
.withPrimaryPokemonHealthRatioRequirement([0.51, 1]) // At least 1 Pokemon must have above half HP
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
{
|
{
|
||||||
spriteKey: Species.KROOKODILE.toString(),
|
spriteKey: Species.KROOKODILE.toString(),
|
||||||
@ -98,8 +99,10 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
|||||||
// Only Pokemon that can gain benefits are above half HP with no status
|
// Only Pokemon that can gain benefits are above half HP with no status
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
const meetsReqs = encounter.pokemonMeetsPrimaryRequirements(scene, pokemon);
|
if (!pokemon.isAllowed()) {
|
||||||
if (!meetsReqs) {
|
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||||
|
}
|
||||||
|
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,13 +178,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon that can gain benefits are unfainted
|
// Only Pokemon that can gain benefits are unfainted
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon is unfainted it can be selected
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
const meetsReqs = !pokemon.isFainted(true);
|
|
||||||
if (!meetsReqs) {
|
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
@ -171,6 +171,12 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
|||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getParty()), true);
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
encounter.setDialogueToken("enemyPokemon", getPokemonNameWithAffix(bossPokemon));
|
||||||
|
|
||||||
|
// Defense/Spd buffs below wave 50, Atk/Def/Spd buffs otherwise
|
||||||
|
const statChangesForBattle: (Stat.ATK | Stat.DEF | Stat.SPATK | Stat.SPDEF | Stat.SPD | Stat.ACC | Stat.EVA)[] = scene.currentBattle.waveIndex < 50 ?
|
||||||
|
[Stat.DEF, Stat.SPDEF, Stat.SPD] :
|
||||||
|
[Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD];
|
||||||
|
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
pokemonConfigs: [{
|
pokemonConfigs: [{
|
||||||
level: level,
|
level: level,
|
||||||
@ -180,7 +186,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
|||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(pokemon.scene, `${namespace}.boss_enraged`);
|
queueEncounterMessage(pokemon.scene, `${namespace}.boss_enraged`);
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.SPD], 1));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, statChangesForBattle, 1));
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { EnemyPartyConfig, generateModifierType, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { EnemyPartyConfig, handleMysteryEncounterBattleFailed, initBattleWithEnemyConfig, setEncounterRewards, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { trainerConfigs } from "#app/data/trainer-config";
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { randSeedShuffle } from "#app/utils";
|
import { randSeedShuffle } from "#app/utils";
|
||||||
@ -14,8 +13,6 @@ import { Species } from "#enums/species";
|
|||||||
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
import { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Type } from "#app/data/type";
|
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { IEggOptions } from "#app/data/egg";
|
import { IEggOptions } from "#app/data/egg";
|
||||||
@ -30,9 +27,9 @@ const namespace = "mysteryEncounter:expertPokemonBreeder";
|
|||||||
|
|
||||||
const trainerNameKey = "trainerNames:expert_pokemon_breeder";
|
const trainerNameKey = "trainerNames:expert_pokemon_breeder";
|
||||||
|
|
||||||
const FIRST_STAGE_EVOLUTION_WAVE = 30;
|
const FIRST_STAGE_EVOLUTION_WAVE = 45;
|
||||||
const SECOND_STAGE_EVOLUTION_WAVE = 45;
|
const SECOND_STAGE_EVOLUTION_WAVE = 60;
|
||||||
const FINAL_STAGE_EVOLUTION_WAVE = 60;
|
const FINAL_STAGE_EVOLUTION_WAVE = 75;
|
||||||
|
|
||||||
const FRIENDSHIP_ADDED = 20;
|
const FRIENDSHIP_ADDED = 20;
|
||||||
|
|
||||||
@ -193,6 +190,12 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
|||||||
pokemon3RareEggs
|
pokemon3RareEggs
|
||||||
};
|
};
|
||||||
|
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.withTitle(`${namespace}.title`)
|
.withTitle(`${namespace}.title`)
|
||||||
@ -241,14 +244,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encounter.onGameOver = onGameOver;
|
||||||
initBattleWithEnemyConfig(scene, config);
|
initBattleWithEnemyConfig(scene, config);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
// Give achievement if in Space biome
|
await doPostEncounterCleanup(scene);
|
||||||
checkAchievement(scene);
|
|
||||||
// Give 20 friendship to the chosen pokemon
|
|
||||||
scene.currentBattle.mysteryEncounter!.misc.pokemon1.addFriendship(FRIENDSHIP_ADDED);
|
|
||||||
await restorePartyAndHeldItems(scene);
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -295,14 +295,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encounter.onGameOver = onGameOver;
|
||||||
initBattleWithEnemyConfig(scene, config);
|
initBattleWithEnemyConfig(scene, config);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
// Give achievement if in Space biome
|
await doPostEncounterCleanup(scene);
|
||||||
checkAchievement(scene);
|
|
||||||
// Give 20 friendship to the chosen pokemon
|
|
||||||
scene.currentBattle.mysteryEncounter!.misc.pokemon2.addFriendship(FRIENDSHIP_ADDED);
|
|
||||||
await restorePartyAndHeldItems(scene);
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -349,14 +346,11 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encounter.onGameOver = onGameOver;
|
||||||
initBattleWithEnemyConfig(scene, config);
|
initBattleWithEnemyConfig(scene, config);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
.withPostOptionPhase(async (scene: BattleScene) => {
|
||||||
// Give achievement if in Space biome
|
await doPostEncounterCleanup(scene);
|
||||||
checkAchievement(scene);
|
|
||||||
// Give 20 friendship to the chosen pokemon
|
|
||||||
scene.currentBattle.mysteryEncounter!.misc.pokemon3.addFriendship(FRIENDSHIP_ADDED);
|
|
||||||
await restorePartyAndHeldItems(scene);
|
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
@ -387,19 +381,6 @@ function getPartyConfig(scene: BattleScene): EnemyPartyConfig {
|
|||||||
nature: Nature.ADAMANT,
|
nature: Nature.ADAMANT,
|
||||||
moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH],
|
moveSet: [Moves.METEOR_MASH, Moves.FIRE_PUNCH, Moves.ICE_PUNCH, Moves.THUNDER_PUNCH],
|
||||||
ivs: [31, 31, 31, 31, 31, 31],
|
ivs: [31, 31, 31, 31, 31, 31],
|
||||||
modifierConfigs: [
|
|
||||||
{
|
|
||||||
modifier: generateModifierType(scene, modifierTypes.TERA_SHARD, [Type.STEEL]) as PokemonHeldItemModifierType,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.ATK]) as PokemonHeldItemModifierType,
|
|
||||||
stackCount: 1 + Math.floor(waveIndex / 20), // +1 Protein every 20 waves
|
|
||||||
},
|
|
||||||
{
|
|
||||||
modifier: generateModifierType(scene, modifierTypes.BASE_STAT_BOOSTER, [Stat.SPD]) as PokemonHeldItemModifierType,
|
|
||||||
stackCount: 1 + Math.floor(waveIndex / 40), // +1 Carbos every 40 waves
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
@ -547,3 +528,39 @@ async function restorePartyAndHeldItems(scene: BattleScene) {
|
|||||||
});
|
});
|
||||||
await scene.updateModifiers(true);
|
await scene.updateModifiers(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onGameOver(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
|
encounter.dialogue.outro = [
|
||||||
|
{
|
||||||
|
text: `${namespace}.outro_failed`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Restore original party, player loses all friendship with chosen mon (it remains fainted)
|
||||||
|
restorePartyAndHeldItems(scene);
|
||||||
|
const chosenPokemon = encounter.misc.chosenPokemon;
|
||||||
|
chosenPokemon.friendship = 0;
|
||||||
|
|
||||||
|
// Clear all rewards that would have been earned
|
||||||
|
encounter.doEncounterRewards = undefined;
|
||||||
|
|
||||||
|
// Set flag that encounter was failed
|
||||||
|
encounter.misc.encounterFailed = true;
|
||||||
|
|
||||||
|
handleMysteryEncounterBattleFailed(scene, true);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function doPostEncounterCleanup(scene: BattleScene) {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
if (!encounter.misc.encounterFailed) {
|
||||||
|
// Give achievement if in Space biome
|
||||||
|
checkAchievement(scene);
|
||||||
|
// Give 20 friendship to the chosen pokemon
|
||||||
|
encounter.misc.chosenPokemon.addFriendship(FRIENDSHIP_ADDED);
|
||||||
|
await restorePartyAndHeldItems(scene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -58,12 +58,12 @@ export const ThePokemonSalesmanEncounter: MysteryEncounter =
|
|||||||
.withOnInit((scene: BattleScene) => {
|
.withOnInit((scene: BattleScene) => {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
let species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
|
||||||
const tries = 0;
|
const tries = 0;
|
||||||
|
|
||||||
// Reroll any species that don't have HAs
|
// Reroll any species that don't have HAs
|
||||||
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
|
while ((isNullOrUndefined(species.abilityHidden) || species.abilityHidden === Abilities.NONE) && tries < 5) {
|
||||||
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5]));
|
species = getPokemonSpecies(getRandomSpeciesByStarterTier([0, 5], undefined, undefined, false, false, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
let pokemon: PlayerPokemon;
|
let pokemon: PlayerPokemon;
|
||||||
|
@ -13,13 +13,14 @@ import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
import HeldModifierConfig from "#app/interfaces/held-modifier-config";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { getStatKey } from "#enums/stat";
|
import { getStatKey } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
|
|
||||||
/** The i18n namespace for the encounter */
|
/** The i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounter:trainingSession";
|
const namespace = "mysteryEncounter:trainingSession";
|
||||||
@ -77,12 +78,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon that are not KOed/legal can be trained
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
const meetsReqs = pokemon.isAllowedInBattle();
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
if (!meetsReqs) {
|
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
@ -211,12 +207,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon that are not KOed/legal can be trained
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
const meetsReqs = pokemon.isAllowedInBattle();
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
if (!meetsReqs) {
|
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
@ -307,12 +298,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Only Pokemon that are not KOed/legal can be trained
|
// Only Pokemon that are not KOed/legal can be trained
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
const meetsReqs = pokemon.isAllowedInBattle();
|
return isPokemonValidForEncounterOptionSelection(pokemon, scene, `${namespace}.invalid_selection`);
|
||||||
if (!meetsReqs) {
|
|
||||||
return getEncounterText(scene, `${namespace}.invalid_selection`) ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
|
@ -82,6 +82,9 @@ const SUPER_LEGENDARY_BST_THRESHOLD = 600;
|
|||||||
const NON_LEGENDARY_BST_THRESHOLD = 570;
|
const NON_LEGENDARY_BST_THRESHOLD = 570;
|
||||||
const GAIN_OLD_GATEAU_ITEM_BST_THRESHOLD = 450;
|
const GAIN_OLD_GATEAU_ITEM_BST_THRESHOLD = 450;
|
||||||
|
|
||||||
|
/** 0-100 */
|
||||||
|
const PERCENT_LEVEL_LOSS_ON_REFUSE = 12.5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value ranges of the resulting species BST transformations after adding values to original species
|
* Value ranges of the resulting species BST transformations after adding values to original species
|
||||||
* 2 Pokemon in the party use this range
|
* 2 Pokemon in the party use this range
|
||||||
@ -207,7 +210,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
|||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Reduce party levels by 20%
|
// Reduce party levels by 20%
|
||||||
for (const pokemon of scene.getParty()) {
|
for (const pokemon of scene.getParty()) {
|
||||||
pokemon.level = Math.max(Math.ceil(0.8 * pokemon.level), 1);
|
pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1);
|
||||||
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
||||||
pokemon.levelExp = 0;
|
pokemon.levelExp = 0;
|
||||||
|
|
||||||
@ -339,6 +342,9 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the previous pokemon had pokerus, transfer to new pokemon
|
||||||
|
newPokemon.pokerus = previousPokemon.pokerus;
|
||||||
|
|
||||||
// If the previous pokemon had higher IVs, override to those (after updating dex IVs > prevents perfect 31s on a new unlock)
|
// If the previous pokemon had higher IVs, override to those (after updating dex IVs > prevents perfect 31s on a new unlock)
|
||||||
newPokemon.ivs = newPokemon.ivs.map((iv, index) => {
|
newPokemon.ivs = newPokemon.ivs.map((iv, index) => {
|
||||||
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
|
return previousPokemon.ivs[index] > iv ? previousPokemon.ivs[index] : iv;
|
||||||
@ -349,22 +355,46 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
scene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
|
scene.gameData.addStarterCandy(getPokemonSpecies(speciesRootForm), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move of the new species
|
// Set the moveset of the new pokemon to be the same as previous, but with 1 egg move and 1 (attempted) STAB move of the new species
|
||||||
|
newPokemon.generateAndPopulateMoveset();
|
||||||
|
|
||||||
|
// Try to find a favored STAB move
|
||||||
|
let favoredMove;
|
||||||
|
for (const move of newPokemon.moveset) {
|
||||||
|
// Needs to match first type, second type will be replaced
|
||||||
|
if (move?.getMove().type === newPokemon.getTypes()[0]) {
|
||||||
|
favoredMove = move;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If was unable to find a move, uses first move in moveset (typically a high power STAB move)
|
||||||
|
favoredMove = favoredMove ?? newPokemon.moveset[0];
|
||||||
|
|
||||||
newPokemon.moveset = previousPokemon.moveset;
|
newPokemon.moveset = previousPokemon.moveset;
|
||||||
|
let eggMoveIndex: null | number = null;
|
||||||
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
|
if (speciesEggMoves.hasOwnProperty(speciesRootForm)) {
|
||||||
const eggMoves = speciesEggMoves[speciesRootForm];
|
const eggMoves = speciesEggMoves[speciesRootForm];
|
||||||
const eggMoveIndex = randSeedInt(4);
|
const randomEggMoveIndex = randSeedInt(4);
|
||||||
const randomEggMove = eggMoves[eggMoveIndex];
|
const randomEggMove = eggMoves[randomEggMoveIndex];
|
||||||
if (newPokemon.moveset.length < 4) {
|
if (newPokemon.moveset.length < 4) {
|
||||||
newPokemon.moveset.push(new PokemonMove(randomEggMove));
|
newPokemon.moveset.push(new PokemonMove(randomEggMove));
|
||||||
} else {
|
} else {
|
||||||
newPokemon.moveset[randSeedInt(4)] = new PokemonMove(randomEggMove);
|
eggMoveIndex = randSeedInt(4);
|
||||||
|
newPokemon.moveset[eggMoveIndex] = new PokemonMove(randomEggMove);
|
||||||
}
|
}
|
||||||
// For pokemon that the player owns (including ones just caught), unlock the egg move
|
// For pokemon that the player owns (including ones just caught), unlock the egg move
|
||||||
if (!!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
if (!!scene.gameData.dexData[speciesRootForm].caughtAttr) {
|
||||||
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), eggMoveIndex, true);
|
await scene.gameData.setEggMoveUnlocked(getPokemonSpecies(speciesRootForm), randomEggMoveIndex, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (favoredMove) {
|
||||||
|
let favoredMoveIndex = randSeedInt(4);
|
||||||
|
while (favoredMoveIndex === eggMoveIndex) {
|
||||||
|
favoredMoveIndex = randSeedInt(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
newPokemon.moveset[favoredMoveIndex] = favoredMove;
|
||||||
|
}
|
||||||
|
|
||||||
// Randomize the second type of the pokemon
|
// Randomize the second type of the pokemon
|
||||||
// If the pokemon does not normally have a second type, it will gain 1
|
// If the pokemon does not normally have a second type, it will gain 1
|
||||||
|
@ -259,23 +259,23 @@ export class WeatherRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
export class PartySizeRequirement extends EncounterSceneRequirement {
|
export class PartySizeRequirement extends EncounterSceneRequirement {
|
||||||
partySizeRange: [number, number];
|
partySizeRange: [number, number];
|
||||||
excludeFainted: boolean;
|
excludeDisallowedPokemon: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for specifying a party size requirement
|
* Used for specifying a party size requirement
|
||||||
* If min and max are equivalent, will check for exact size
|
* If min and max are equivalent, will check for exact size
|
||||||
* @param partySizeRange
|
* @param partySizeRange
|
||||||
* @param excludeFainted
|
* @param excludeDisallowedPokemon
|
||||||
*/
|
*/
|
||||||
constructor(partySizeRange: [number, number], excludeFainted: boolean) {
|
constructor(partySizeRange: [number, number], excludeDisallowedPokemon: boolean) {
|
||||||
super();
|
super();
|
||||||
this.partySizeRange = partySizeRange;
|
this.partySizeRange = partySizeRange;
|
||||||
this.excludeFainted = excludeFainted;
|
this.excludeDisallowedPokemon = excludeDisallowedPokemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange?.[0] <= this.partySizeRange?.[1]) {
|
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange?.[0] <= this.partySizeRange?.[1]) {
|
||||||
const partySize = this.excludeFainted ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length;
|
const partySize = this.excludeDisallowedPokemon ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length;
|
||||||
if (partySize >= 0 && (this.partySizeRange?.[0] >= 0 && this.partySizeRange?.[0] > partySize) || (this.partySizeRange?.[1] >= 0 && this.partySizeRange?.[1] < partySize)) {
|
if (partySize >= 0 && (this.partySizeRange?.[0] >= 0 && this.partySizeRange?.[0] > partySize) || (this.partySizeRange?.[1] >= 0 && this.partySizeRange?.[1] < partySize)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -767,12 +767,14 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
requiredHeldItemModifiers: string[];
|
requiredHeldItemModifiers: string[];
|
||||||
minNumberOfPokemon: number;
|
minNumberOfPokemon: number;
|
||||||
invertQuery: boolean;
|
invertQuery: boolean;
|
||||||
|
requireTransferable: boolean;
|
||||||
|
|
||||||
constructor(heldItem: string | string[], minNumberOfPokemon: number = 1, invertQuery: boolean = false) {
|
constructor(heldItem: string | string[], minNumberOfPokemon: number = 1, invertQuery: boolean = false, requireTransferable: boolean = true) {
|
||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
this.requiredHeldItemModifiers = Array.isArray(heldItem) ? heldItem : [heldItem];
|
||||||
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -787,21 +789,23 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
if (!this.invertQuery) {
|
if (!this.invertQuery) {
|
||||||
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifiers.some((heldItem) => {
|
return partyPokemon.filter((pokemon) => this.requiredHeldItemModifiers.some((heldItem) => {
|
||||||
return pokemon.getHeldItems().some((it) => {
|
return pokemon.getHeldItems().some((it) => {
|
||||||
return it.constructor.name === heldItem;
|
return it.constructor.name === heldItem && (!this.requireTransferable || it.isTransferable);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
// for an inverted query, we only want to get the pokemon that have any held items that are NOT in requiredHeldItemModifiers
|
// for an inverted query, we only want to get the pokemon that have any held items that are NOT in requiredHeldItemModifiers
|
||||||
// E.g. functions as a blacklist
|
// E.g. functions as a blacklist
|
||||||
return partyPokemon.filter((pokemon) => pokemon.getHeldItems().filter((it) => {
|
return partyPokemon.filter((pokemon) => pokemon.getHeldItems().filter((it) => {
|
||||||
return !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem);
|
return !this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
|
||||||
|
&& (!this.requireTransferable || it.isTransferable);
|
||||||
}).length > 0);
|
}).length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
||||||
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem);
|
return this.requiredHeldItemModifiers.some(heldItem => it.constructor.name === heldItem)
|
||||||
|
&& (!this.requireTransferable || it.isTransferable);
|
||||||
});
|
});
|
||||||
if (requiredItems && requiredItems.length > 0) {
|
if (requiredItems && requiredItems.length > 0) {
|
||||||
return ["heldItem", requiredItems[0].type.name];
|
return ["heldItem", requiredItems[0].type.name];
|
||||||
@ -814,12 +818,14 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
|||||||
requiredHeldItemTypes: Type[];
|
requiredHeldItemTypes: Type[];
|
||||||
minNumberOfPokemon: number;
|
minNumberOfPokemon: number;
|
||||||
invertQuery: boolean;
|
invertQuery: boolean;
|
||||||
|
requireTransferable: boolean;
|
||||||
|
|
||||||
constructor(heldItemTypes: Type | Type[], minNumberOfPokemon: number = 1, invertQuery: boolean = false) {
|
constructor(heldItemTypes: Type | Type[], minNumberOfPokemon: number = 1, invertQuery: boolean = false, requireTransferable: boolean = true) {
|
||||||
super();
|
super();
|
||||||
this.minNumberOfPokemon = minNumberOfPokemon;
|
this.minNumberOfPokemon = minNumberOfPokemon;
|
||||||
this.invertQuery = invertQuery;
|
this.invertQuery = invertQuery;
|
||||||
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
|
this.requiredHeldItemTypes = Array.isArray(heldItemTypes) ? heldItemTypes : [heldItemTypes];
|
||||||
|
this.requireTransferable = requireTransferable;
|
||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
@ -834,21 +840,29 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
|||||||
if (!this.invertQuery) {
|
if (!this.invertQuery) {
|
||||||
return partyPokemon.filter((pokemon) => this.requiredHeldItemTypes.some((heldItemType) => {
|
return partyPokemon.filter((pokemon) => this.requiredHeldItemTypes.some((heldItemType) => {
|
||||||
return pokemon.getHeldItems().some((it) => {
|
return pokemon.getHeldItems().some((it) => {
|
||||||
return it instanceof AttackTypeBoosterModifier && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType;
|
return it instanceof AttackTypeBoosterModifier
|
||||||
|
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
|
||||||
|
&& (!this.requireTransferable || it.isTransferable);
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
// for an inverted query, we only want to get the pokemon that have any held items that are NOT in requiredHeldItemModifiers
|
// for an inverted query, we only want to get the pokemon that have any held items that are NOT in requiredHeldItemModifiers
|
||||||
// E.g. functions as a blacklist
|
// E.g. functions as a blacklist
|
||||||
return partyPokemon.filter((pokemon) => pokemon.getHeldItems().filter((it) => {
|
return partyPokemon.filter((pokemon) => pokemon.getHeldItems().filter((it) => {
|
||||||
return !this.requiredHeldItemTypes.some(heldItemType => it instanceof AttackTypeBoosterModifier && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType);
|
return !this.requiredHeldItemTypes.some(heldItemType =>
|
||||||
|
it instanceof AttackTypeBoosterModifier
|
||||||
|
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType
|
||||||
|
&& (!this.requireTransferable || it.isTransferable));
|
||||||
}).length > 0);
|
}).length > 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
const requiredItems = pokemon?.getHeldItems().filter((it) => {
|
||||||
return this.requiredHeldItemTypes.some(heldItemType => it instanceof AttackTypeBoosterModifier && (it.type as AttackTypeBoosterModifierType).moveType === heldItemType);
|
return this.requiredHeldItemTypes.some(heldItemType =>
|
||||||
|
it instanceof AttackTypeBoosterModifier
|
||||||
|
&& (it.type as AttackTypeBoosterModifierType).moveType === heldItemType)
|
||||||
|
&& (!this.requireTransferable || it.isTransferable);
|
||||||
});
|
});
|
||||||
if (requiredItems && requiredItems.length > 0) {
|
if (requiredItems && requiredItems.length > 0) {
|
||||||
return ["heldItem", requiredItems[0].type.name];
|
return ["heldItem", requiredItems[0].type.name];
|
||||||
|
@ -161,6 +161,11 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
|||||||
doEncounterRewards?: (scene: BattleScene) => boolean;
|
doEncounterRewards?: (scene: BattleScene) => boolean;
|
||||||
/** Will execute callback during VictoryPhase of a continuousEncounter */
|
/** Will execute callback during VictoryPhase of a continuousEncounter */
|
||||||
doContinueEncounter?: (scene: BattleScene) => Promise<void>;
|
doContinueEncounter?: (scene: BattleScene) => Promise<void>;
|
||||||
|
/**
|
||||||
|
* Can perform special logic when a ME battle is lost, before GameOver/battle retry prompt.
|
||||||
|
* Should return `true` if it is treated as "real" Game Over, `false` if not.
|
||||||
|
*/
|
||||||
|
onGameOver?: (scene: BattleScene) => boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requirements
|
* Requirements
|
||||||
@ -742,11 +747,11 @@ export class MysteryEncounterBuilder implements Partial<IMysteryEncounter> {
|
|||||||
*
|
*
|
||||||
* @param min min wave (or exact size if only min is given)
|
* @param min min wave (or exact size if only min is given)
|
||||||
* @param max optional max size. If not given, defaults to min => exact wave
|
* @param max optional max size. If not given, defaults to min => exact wave
|
||||||
* @param excludeFainted if true, only counts unfainted mons
|
* @param excludeDisallowedPokemon if true, only counts allowed (legal in Challenge/unfainted) mons
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
withScenePartySizeRequirement(min: number, max?: number, excludeFainted: boolean = false): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
withScenePartySizeRequirement(min: number, max?: number, excludeDisallowedPokemon: boolean = false): this & Required<Pick<IMysteryEncounter, "requirements">> {
|
||||||
return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min], excludeFainted));
|
return this.withSceneRequirement(new PartySizeRequirement([min, max ?? min], excludeDisallowedPokemon));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -744,6 +744,37 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to {@linkcode handleMysteryEncounterVictory}, but for cases where the player lost a battle or failed a challenge
|
||||||
|
* @param scene
|
||||||
|
* @param addHealPhase
|
||||||
|
*/
|
||||||
|
export function handleMysteryEncounterBattleFailed(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) {
|
||||||
|
const allowedPkm = scene.getParty().filter((pkm) => pkm.isAllowedInBattle());
|
||||||
|
|
||||||
|
if (allowedPkm.length === 0) {
|
||||||
|
scene.clearPhaseQueue();
|
||||||
|
scene.unshiftPhase(new GameOverPhase(scene));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If in repeated encounter variant, do nothing
|
||||||
|
// Variant must eventually be swapped in order to handle "true" end of the encounter
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
if (encounter.continuousEncounter || doNotContinue) {
|
||||||
|
return;
|
||||||
|
} else if (encounter.encounterMode !== MysteryEncounterMode.NO_BATTLE) {
|
||||||
|
scene.pushPhase(new BattleEndPhase(scene));
|
||||||
|
}
|
||||||
|
|
||||||
|
scene.pushPhase(new MysteryEncounterRewardsPhase(scene, addHealPhase));
|
||||||
|
|
||||||
|
if (!encounter.doContinueEncounter) {
|
||||||
|
// Only lapse eggs once for multi-battle encounters
|
||||||
|
scene.pushPhase(new EggLapsePhase(scene));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param scene
|
* @param scene
|
||||||
|
@ -13,7 +13,7 @@ import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
|||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies, speciesStarters } from "#app/data/pokemon-species";
|
||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
@ -170,15 +170,24 @@ export function getHighestStatTotalPlayerPokemon(scene: BattleScene, unfainted:
|
|||||||
* @param starterTiers
|
* @param starterTiers
|
||||||
* @param excludedSpecies
|
* @param excludedSpecies
|
||||||
* @param types
|
* @param types
|
||||||
|
* @param allowSubLegendary
|
||||||
|
* @param allowLegendary
|
||||||
|
* @param allowMythical
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[]): Species {
|
export function getRandomSpeciesByStarterTier(starterTiers: number | [number, number], excludedSpecies?: Species[], types?: Type[], allowSubLegendary: boolean = true, allowLegendary: boolean = true, allowMythical: boolean = true): Species {
|
||||||
let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers;
|
let min = Array.isArray(starterTiers) ? starterTiers[0] : starterTiers;
|
||||||
let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers;
|
let max = Array.isArray(starterTiers) ? starterTiers[1] : starterTiers;
|
||||||
|
|
||||||
let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters)
|
let filteredSpecies: [PokemonSpecies, number][] = Object.keys(speciesStarters)
|
||||||
.map(s => [parseInt(s) as Species, speciesStarters[s] as number])
|
.map(s => [parseInt(s) as Species, speciesStarters[s] as number])
|
||||||
.filter(s => getPokemonSpecies(s[0]) && (!excludedSpecies || !excludedSpecies.includes(s[0])))
|
.filter(s => {
|
||||||
|
const pokemonSpecies = getPokemonSpecies(s[0]);
|
||||||
|
return pokemonSpecies && (!excludedSpecies || !excludedSpecies.includes(s[0])
|
||||||
|
&& (allowSubLegendary || !pokemonSpecies.subLegendary)
|
||||||
|
&& (allowLegendary || !pokemonSpecies.legendary)
|
||||||
|
&& (allowMythical || !pokemonSpecies.mythical));
|
||||||
|
})
|
||||||
.map(s => [getPokemonSpecies(s[0]), s[1]]);
|
.map(s => [getPokemonSpecies(s[0]), s[1]]);
|
||||||
|
|
||||||
if (types && types.length > 0) {
|
if (types && types.length > 0) {
|
||||||
@ -773,3 +782,23 @@ export async function addPokemonDataToDexAndValidateAchievements(scene: BattleSc
|
|||||||
scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
|
scene.gameData.updateSpeciesDexIvs(pokemon.species.getRootSpeciesId(true), pokemon.ivs);
|
||||||
return scene.gameData.setPokemonCaught(pokemon, true, false, false);
|
return scene.gameData.setPokemonCaught(pokemon, true, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a Pokemon is allowed under a challenge, and allowed in battle.
|
||||||
|
* If both are true, returns `null`.
|
||||||
|
* If one of them is not true, returns message content that the Pokemon is invalid.
|
||||||
|
* Typically used for cheecking whether a Pokemon can be selected for a {@linkcode MysteryEncounterOption}
|
||||||
|
* @param pokemon
|
||||||
|
* @param scene
|
||||||
|
* @param invalidSelectionKey
|
||||||
|
*/
|
||||||
|
export function isPokemonValidForEncounterOptionSelection(pokemon: Pokemon, scene: BattleScene, invalidSelectionKey: string): string | null {
|
||||||
|
if (!pokemon.isAllowed()) {
|
||||||
|
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||||
|
}
|
||||||
|
if (!pokemon.isAllowedInBattle()) {
|
||||||
|
return getEncounterText(scene, invalidSelectionKey) ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@ -109,6 +109,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
public fusionVariant: Variant;
|
public fusionVariant: Variant;
|
||||||
public fusionGender: Gender;
|
public fusionGender: Gender;
|
||||||
public fusionLuck: integer;
|
public fusionLuck: integer;
|
||||||
|
public fusionMysteryEncounterPokemonData: MysteryEncounterPokemonData | null;
|
||||||
|
|
||||||
private summonDataPrimer: PokemonSummonData | null;
|
private summonDataPrimer: PokemonSummonData | null;
|
||||||
|
|
||||||
@ -206,6 +207,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.fusionVariant = dataSource.fusionVariant || 0;
|
this.fusionVariant = dataSource.fusionVariant || 0;
|
||||||
this.fusionGender = dataSource.fusionGender;
|
this.fusionGender = dataSource.fusionGender;
|
||||||
this.fusionLuck = dataSource.fusionLuck;
|
this.fusionLuck = dataSource.fusionLuck;
|
||||||
|
this.fusionMysteryEncounterPokemonData = dataSource.fusionMysteryEncounterPokemonData;
|
||||||
this.usedTMs = dataSource.usedTMs ?? [];
|
this.usedTMs = dataSource.usedTMs ?? [];
|
||||||
this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(dataSource.mysteryEncounterPokemonData);
|
this.mysteryEncounterPokemonData = new MysteryEncounterPokemonData(dataSource.mysteryEncounterPokemonData);
|
||||||
} else {
|
} else {
|
||||||
@ -1164,11 +1166,31 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!types.length || !includeTeraType) {
|
if (!types.length || !includeTeraType) {
|
||||||
if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) {
|
if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
||||||
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
|
||||||
this.mysteryEncounterPokemonData.types.forEach(t => types.push(t));
|
|
||||||
} else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
|
||||||
this.summonData.types.forEach(t => types.push(t));
|
this.summonData.types.forEach(t => types.push(t));
|
||||||
|
} else if (this.mysteryEncounterPokemonData.types && this.mysteryEncounterPokemonData.types.length > 0) {
|
||||||
|
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
||||||
|
types.push(this.mysteryEncounterPokemonData.types[0]);
|
||||||
|
|
||||||
|
// Fusing a Pokemon onto something with "permanently changed" types will still apply the fusion's types as normal
|
||||||
|
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
||||||
|
if (fusionSpeciesForm) {
|
||||||
|
// Check if the fusion Pokemon also had "permanently changed" types
|
||||||
|
const fusionMETypes = this.fusionMysteryEncounterPokemonData?.types;
|
||||||
|
if (fusionMETypes && fusionMETypes.length >= 2 && fusionMETypes[1] !== types[0]) {
|
||||||
|
types.push(fusionMETypes[1]);
|
||||||
|
} else if (fusionMETypes && fusionMETypes.length === 1 && fusionMETypes[0] !== types[0]) {
|
||||||
|
types.push(fusionMETypes[0]);
|
||||||
|
} else if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== types[0]) {
|
||||||
|
types.push(fusionSpeciesForm.type2);
|
||||||
|
} else if (fusionSpeciesForm.type1 !== types[0]) {
|
||||||
|
types.push(fusionSpeciesForm.type1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (types.length === 1 && this.mysteryEncounterPokemonData.types.length >= 2) {
|
||||||
|
types.push(this.mysteryEncounterPokemonData.types[1]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const speciesForm = this.getSpeciesForm(ignoreOverride);
|
const speciesForm = this.getSpeciesForm(ignoreOverride);
|
||||||
|
|
||||||
@ -1176,7 +1198,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
const fusionSpeciesForm = this.getFusionSpeciesForm(ignoreOverride);
|
||||||
if (fusionSpeciesForm) {
|
if (fusionSpeciesForm) {
|
||||||
if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1) {
|
// Check if the fusion Pokemon also had "permanently changed" types
|
||||||
|
// Otherwise, use standard fusion type logic
|
||||||
|
const fusionMETypes = this.fusionMysteryEncounterPokemonData?.types;
|
||||||
|
if (fusionMETypes && fusionMETypes.length >= 2 && fusionMETypes[1] !== types[0]) {
|
||||||
|
types.push(fusionMETypes[1]);
|
||||||
|
} else if (fusionMETypes && fusionMETypes.length === 1 && fusionMETypes[0] !== types[0]) {
|
||||||
|
types.push(fusionMETypes[0]);
|
||||||
|
} else if (fusionSpeciesForm.type2 !== null && fusionSpeciesForm.type2 !== speciesForm.type1) {
|
||||||
types.push(fusionSpeciesForm.type2);
|
types.push(fusionSpeciesForm.type2);
|
||||||
} else if (fusionSpeciesForm.type1 !== speciesForm.type1) {
|
} else if (fusionSpeciesForm.type1 !== speciesForm.type1) {
|
||||||
types.push(fusionSpeciesForm.type1);
|
types.push(fusionSpeciesForm.type1);
|
||||||
@ -1228,12 +1257,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
|
if (Overrides.OPP_ABILITY_OVERRIDE && !this.isPlayer()) {
|
||||||
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
|
return allAbilities[Overrides.OPP_ABILITY_OVERRIDE];
|
||||||
}
|
}
|
||||||
|
if (this.isFusion()) {
|
||||||
|
if (!isNullOrUndefined(this.fusionMysteryEncounterPokemonData?.ability) && this.fusionMysteryEncounterPokemonData!.ability !== -1) {
|
||||||
|
return allAbilities[this.fusionMysteryEncounterPokemonData!.ability];
|
||||||
|
} else {
|
||||||
|
return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)];
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!isNullOrUndefined(this.mysteryEncounterPokemonData.ability) && this.mysteryEncounterPokemonData.ability !== -1) {
|
if (!isNullOrUndefined(this.mysteryEncounterPokemonData.ability) && this.mysteryEncounterPokemonData.ability !== -1) {
|
||||||
return allAbilities[this.mysteryEncounterPokemonData.ability];
|
return allAbilities[this.mysteryEncounterPokemonData.ability];
|
||||||
}
|
}
|
||||||
if (this.isFusion()) {
|
|
||||||
return allAbilities[this.getFusionSpeciesForm(ignoreOverride).getAbility(this.fusionAbilityIndex)];
|
|
||||||
}
|
|
||||||
let abilityId = this.getSpeciesForm(ignoreOverride).getAbility(this.abilityIndex);
|
let abilityId = this.getSpeciesForm(ignoreOverride).getAbility(this.abilityIndex);
|
||||||
if (abilityId === Abilities.NONE) {
|
if (abilityId === Abilities.NONE) {
|
||||||
abilityId = this.species.ability1;
|
abilityId = this.species.ability1;
|
||||||
@ -1927,6 +1960,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.fusionVariant = 0;
|
this.fusionVariant = 0;
|
||||||
this.fusionGender = 0;
|
this.fusionGender = 0;
|
||||||
this.fusionLuck = 0;
|
this.fusionLuck = 0;
|
||||||
|
this.fusionMysteryEncounterPokemonData = null;
|
||||||
|
|
||||||
this.generateName();
|
this.generateName();
|
||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
@ -4207,6 +4241,7 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
this.fusionVariant = pokemon.variant;
|
this.fusionVariant = pokemon.variant;
|
||||||
this.fusionGender = pokemon.gender;
|
this.fusionGender = pokemon.gender;
|
||||||
this.fusionLuck = pokemon.luck;
|
this.fusionLuck = pokemon.luck;
|
||||||
|
this.fusionMysteryEncounterPokemonData = pokemon.mysteryEncounterPokemonData;
|
||||||
if ((pokemon.pauseEvolutions) || (this.pauseEvolutions)) {
|
if ((pokemon.pauseEvolutions) || (this.pauseEvolutions)) {
|
||||||
this.pauseEvolutions = true;
|
this.pauseEvolutions = true;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"selected": "Let's do this!"
|
"selected": "Let's do this!"
|
||||||
},
|
},
|
||||||
"outro": "Look how happy your {{chosenPokemon}} is now!$Here, you can have these as well.",
|
"outro": "Look how happy your {{chosenPokemon}} is now!$Here, you can have these as well.",
|
||||||
|
"outro_dialogue": "How disappointing...$It looks like you still have a long way\nto go to earn your Pokémon's trust!",
|
||||||
"gained_eggs": "@s{item_fanfare}You received {{numEggs}}!",
|
"gained_eggs": "@s{item_fanfare}You received {{numEggs}}!",
|
||||||
"eggs_tooltip": "\n(+) Earn {{eggs}}",
|
"eggs_tooltip": "\n(+) Earn {{eggs}}",
|
||||||
"numEggs_one": "{{count}} {{rarity}} Egg",
|
"numEggs_one": "{{count}} {{rarity}} Egg",
|
||||||
|
@ -888,7 +888,7 @@ export class PokemonBaseStatTotalModifier extends PokemonHeldItemModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override matchType(modifier: Modifier): boolean {
|
override matchType(modifier: Modifier): boolean {
|
||||||
return modifier instanceof PokemonBaseStatTotalModifier;
|
return modifier instanceof PokemonBaseStatTotalModifier && this.statModifier === modifier.statModifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
override clone(): PersistentModifier {
|
override clone(): PersistentModifier {
|
||||||
@ -939,7 +939,7 @@ export class PokemonBaseStatFlatModifier extends PokemonHeldItemModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override matchType(modifier: Modifier): boolean {
|
override matchType(modifier: Modifier): boolean {
|
||||||
return modifier instanceof PokemonBaseStatFlatModifier;
|
return modifier instanceof PokemonBaseStatFlatModifier && modifier.statModifier === this.statModifier && this.stats.every(s => modifier.stats.some(stat => s === stat));
|
||||||
}
|
}
|
||||||
|
|
||||||
override clone(): PersistentModifier {
|
override clone(): PersistentModifier {
|
||||||
|
@ -48,6 +48,14 @@ export class GameOverPhase extends BattlePhase {
|
|||||||
this.victory = true;
|
this.victory = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Mystery Encounter special Game Over cases
|
||||||
|
// Situations such as when player lost a battle, but it isn't treated as full Game Over
|
||||||
|
if (!this.victory && this.scene.currentBattle.mysteryEncounter?.onGameOver && !this.scene.currentBattle.mysteryEncounter.onGameOver(this.scene)) {
|
||||||
|
// Do not end the game
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
|
// Otherwise, continue standard Game Over logic
|
||||||
|
|
||||||
if (this.victory && this.scene.gameMode.isEndless) {
|
if (this.victory && this.scene.gameMode.isEndless) {
|
||||||
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET;
|
const genderIndex = this.scene.gameData.gender ?? PlayerGender.UNSET;
|
||||||
const genderStr = PlayerGender[genderIndex].toLowerCase();
|
const genderStr = PlayerGender[genderIndex].toLowerCase();
|
||||||
@ -60,11 +68,6 @@ export class GameOverPhase extends BattlePhase {
|
|||||||
this.scene.ui.fadeOut(1250).then(() => {
|
this.scene.ui.fadeOut(1250).then(() => {
|
||||||
this.scene.reset();
|
this.scene.reset();
|
||||||
this.scene.clearPhaseQueue();
|
this.scene.clearPhaseQueue();
|
||||||
// If this is a ME, clear any residual visual sprites before reloading
|
|
||||||
const encounter = this.scene.currentBattle.mysteryEncounter;
|
|
||||||
if (encounter?.introVisuals) {
|
|
||||||
this.scene.field.remove(encounter.introVisuals, true);
|
|
||||||
}
|
|
||||||
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
||||||
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ export default class PokemonData {
|
|||||||
public fusionVariant: Variant;
|
public fusionVariant: Variant;
|
||||||
public fusionGender: Gender;
|
public fusionGender: Gender;
|
||||||
public fusionLuck: integer;
|
public fusionLuck: integer;
|
||||||
|
public fusionMysteryEncounterPokemonData: MysteryEncounterPokemonData;
|
||||||
|
|
||||||
public boss: boolean;
|
public boss: boolean;
|
||||||
public bossSegments?: integer;
|
public bossSegments?: integer;
|
||||||
|
@ -175,7 +175,16 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
|||||||
expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
|
expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should start a battle against an enraged boss", { retry: 5 }, async () => {
|
it("should start a battle against an enraged boss below wave 50", { retry: 5 }, async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
|
const enemyField = scene.getEnemyField();
|
||||||
|
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 1, 0, 0]);
|
||||||
|
expect(enemyField[0].isBoss()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start a battle against an extra enraged boss above wave 50", { retry: 5 }, async () => {
|
||||||
|
game.override.startingWave(56);
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
const enemyField = scene.getEnemyField();
|
const enemyField = scene.getEnemyField();
|
||||||
@ -238,10 +247,19 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
|||||||
expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
|
expect(TRANSPORT_BIOMES).toContain(scene.arena.biomeType);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should start a battle against an enraged boss", async () => {
|
it("should start a battle against an enraged boss below wave 50", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [Species.PIKACHU]);
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [Species.PIKACHU]);
|
||||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
const enemyField = scene.getEnemyField();
|
const enemyField = scene.getEnemyField();
|
||||||
|
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 1, 0, 0]);
|
||||||
|
expect(enemyField[0].isBoss()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should start a battle against an extra enraged boss above wave 50", { retry: 5 }, async () => {
|
||||||
|
game.override.startingWave(56);
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
|
const enemyField = scene.getEnemyField();
|
||||||
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 1, 0, 0]);
|
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 1, 0, 0]);
|
||||||
expect(enemyField[0].isBoss()).toBe(true);
|
expect(enemyField[0].isBoss()).toBe(true);
|
||||||
});
|
});
|
||||||
|
@ -169,12 +169,14 @@ export class UiInputs {
|
|||||||
}
|
}
|
||||||
switch (this.scene.ui?.getMode()) {
|
switch (this.scene.ui?.getMode()) {
|
||||||
case Mode.MESSAGE:
|
case Mode.MESSAGE:
|
||||||
if (!(this.scene.ui.getHandler() as MessageUiHandler).pendingPrompt) {
|
const messageHandler = this.scene.ui.getHandler<MessageUiHandler>();
|
||||||
|
if (!messageHandler.pendingPrompt || messageHandler.isTextAnimationInProgress()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case Mode.TITLE:
|
case Mode.TITLE:
|
||||||
case Mode.COMMAND:
|
case Mode.COMMAND:
|
||||||
case Mode.MODIFIER_SELECT:
|
case Mode.MODIFIER_SELECT:
|
||||||
|
case Mode.MYSTERY_ENCOUNTER:
|
||||||
this.scene.ui.setOverlayMode(Mode.MENU);
|
this.scene.ui.setOverlayMode(Mode.MENU);
|
||||||
break;
|
break;
|
||||||
case Mode.STARTER_SELECT:
|
case Mode.STARTER_SELECT:
|
||||||
|
@ -223,6 +223,14 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isTextAnimationInProgress() {
|
||||||
|
if (this.textTimer) {
|
||||||
|
return this.textTimer.repeatCount < this.textTimer.repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
clearText() {
|
clearText() {
|
||||||
this.message.setText("");
|
this.message.setText("");
|
||||||
this.pendingPrompt = false;
|
this.pendingPrompt = false;
|
||||||
|
@ -701,6 +701,7 @@ export default class SummaryUiHandler extends UiHandler {
|
|||||||
const profileContainer = this.scene.add.container(0, -pageBg.height);
|
const profileContainer = this.scene.add.container(0, -pageBg.height);
|
||||||
pageContainer.add(profileContainer);
|
pageContainer.add(profileContainer);
|
||||||
|
|
||||||
|
// TODO: should add field for original trainer name to Pokemon object, to support gift/traded Pokemon from MEs
|
||||||
const trainerText = addBBCodeTextObject(this.scene, 7, 12, `${i18next.t("pokemonSummary:ot")}/${getBBCodeFrag(loggedInUser?.username || i18next.t("pokemonSummary:unknown"), this.scene.gameData.gender === PlayerGender.FEMALE ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY_BLUE)}`, TextStyle.SUMMARY_ALT);
|
const trainerText = addBBCodeTextObject(this.scene, 7, 12, `${i18next.t("pokemonSummary:ot")}/${getBBCodeFrag(loggedInUser?.username || i18next.t("pokemonSummary:unknown"), this.scene.gameData.gender === PlayerGender.FEMALE ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY_BLUE)}`, TextStyle.SUMMARY_ALT);
|
||||||
trainerText.setOrigin(0, 0);
|
trainerText.setOrigin(0, 0);
|
||||||
profileContainer.add(trainerText);
|
profileContainer.add(trainerText);
|
||||||
|
Loading…
Reference in New Issue
Block a user