mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-26 08:46:55 +00:00
clean up GTS Encounter and add unit tests
This commit is contained in:
parent
0e958d5d64
commit
7698e0f7be
@ -4,8 +4,8 @@
|
|||||||
"image": "teleporter.png",
|
"image": "teleporter.png",
|
||||||
"format": "RGBA8888",
|
"format": "RGBA8888",
|
||||||
"size": {
|
"size": {
|
||||||
"w": 64,
|
"w": 74,
|
||||||
"h": 78
|
"h": 79
|
||||||
},
|
},
|
||||||
"scale": 1,
|
"scale": 1,
|
||||||
"frames": [
|
"frames": [
|
||||||
@ -14,20 +14,20 @@
|
|||||||
"rotated": false,
|
"rotated": false,
|
||||||
"trimmed": false,
|
"trimmed": false,
|
||||||
"sourceSize": {
|
"sourceSize": {
|
||||||
"w": 64,
|
"w": 74,
|
||||||
"h": 78
|
"h": 79
|
||||||
},
|
},
|
||||||
"spriteSourceSize": {
|
"spriteSourceSize": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"w": 64,
|
"w": 74,
|
||||||
"h": 78
|
"h": 79
|
||||||
},
|
},
|
||||||
"frame": {
|
"frame": {
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0,
|
"y": 0,
|
||||||
"w": 64,
|
"w": 74,
|
||||||
"h": 78
|
"h": 79
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -36,6 +36,6 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"app": "https://www.codeandweb.com/texturepacker",
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
"smartupdate": "$TexturePacker:SmartUpdate:a8e006630c2838130468b0d5c9aeb8a6:684c1813cb6c86e395c18027a593ed28:ce1615396ce7b0a146766d50b319bb81$"
|
"smartupdate": "$TexturePacker:SmartUpdate:937d8502b98f79720118061b6021e108:2b4f9db00d5b0997b42a5466f808509b:ce1615396ce7b0a146766d50b319bb81$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 661 B After Width: | Height: | Size: 1.3 KiB |
@ -1,31 +1,26 @@
|
|||||||
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { TrainerSlot, } from "#app/data/trainer-config";
|
import { TrainerSlot, } from "#app/data/trainer-config";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } 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 MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "../mystery-encounter";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
import PokemonSpecies, { allSpecies, getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import { getTypeRgb, Type } from "#app/data/type";
|
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
|
import { IntegerHolder, isNullOrUndefined, randInt, randSeedInt, randSeedShuffle } from "#app/utils";
|
||||||
import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { BerryModifier, PokemonFormChangeItemModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
|
import { HiddenAbilityRateBoosterModifier, PokemonFormChangeItemModifier, PokemonHeldItemModifier, SpeciesStatBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { BerryType } from "#enums/berry-type";
|
|
||||||
import { EncounterAnim, EncounterBattleAnim } from "#app/data/battle-anims";
|
|
||||||
import { MoveCategory } from "#app/data/move";
|
|
||||||
import { MysteryEncounterPokemonData } from "#app/data/mystery-encounters/mystery-encounter-pokemon-data";
|
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { Gender, getGenderSymbol } from "#app/data/gender";
|
import { Gender, getGenderSymbol } from "#app/data/gender";
|
||||||
import { getNatureName } from "#app/data/nature";
|
import { getNatureName } from "#app/data/nature";
|
||||||
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
import { getPokeballAtlasKey, getPokeballTintColor } from "#app/data/pokeball";
|
||||||
import { showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { trainerNamePools } from "#app/data/trainer-names";
|
import { trainerNamePools } from "#app/data/trainer-names";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
@ -180,6 +175,88 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
// Remove the original party member from party
|
// Remove the original party member from party
|
||||||
scene.removePokemonFromPlayerParty(tradedPokemon, false);
|
scene.removePokemonFromPlayerParty(tradedPokemon, false);
|
||||||
|
|
||||||
|
// Set data properly, then generate the new Pokemon's assets
|
||||||
|
receivedPokemonData.passive = tradedPokemon.passive;
|
||||||
|
receivedPokemonData.pokeball = randSeedInt(5);
|
||||||
|
const dataSource = new PokemonData(receivedPokemonData);
|
||||||
|
const newPlayerPokemon = scene.addPlayerPokemon(receivedPokemonData.species, receivedPokemonData.level, dataSource.abilityIndex, dataSource.formIndex, dataSource.gender, dataSource.shiny, dataSource.variant, dataSource.ivs, dataSource.nature, dataSource);
|
||||||
|
scene.getParty().push(newPlayerPokemon);
|
||||||
|
await newPlayerPokemon.loadAssets();
|
||||||
|
|
||||||
|
for (const mod of modifiers) {
|
||||||
|
mod.pokemonId = newPlayerPokemon.id;
|
||||||
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the trade animation
|
||||||
|
await showTradeBackground(scene);
|
||||||
|
await doPokemonTradeSequence(scene, tradedPokemon, newPlayerPokemon);
|
||||||
|
await showEncounterText(scene, `${namespace}.trade_received`, 0, true, 4000);
|
||||||
|
scene.playBgm("mystery_encounter_gts");
|
||||||
|
await hideTradeBackground(scene);
|
||||||
|
tradedPokemon.destroy();
|
||||||
|
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
})
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.withOption(
|
||||||
|
MysteryEncounterOptionBuilder
|
||||||
|
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
||||||
|
.withDialogue({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`,
|
||||||
|
})
|
||||||
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
|
// Randomly generate a Wonder Trade pokemon
|
||||||
|
const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
|
||||||
|
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||||
|
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
||||||
|
if (!tradePokemon.shiny) {
|
||||||
|
// 512/65536 -> 1/128
|
||||||
|
tradePokemon.trySetShinySeed(512, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra HA roll at base 1/64 odds (boosted by events and charms)
|
||||||
|
if (pokemon.species.abilityHidden) {
|
||||||
|
const hiddenIndex = pokemon.species.ability2 ? 2 : 1;
|
||||||
|
if (pokemon.abilityIndex < hiddenIndex) {
|
||||||
|
const hiddenAbilityChance = new IntegerHolder(64);
|
||||||
|
scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||||
|
|
||||||
|
const hasHiddenAbility = !randSeedInt(hiddenAbilityChance.value);
|
||||||
|
|
||||||
|
if (hasHiddenAbility) {
|
||||||
|
pokemon.abilityIndex = hiddenIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
encounter.setDialogueToken("tradedPokemon", pokemon.getNameToRender());
|
||||||
|
encounter.setDialogueToken("received", tradePokemon.getNameToRender());
|
||||||
|
encounter.misc = {
|
||||||
|
tradedPokemon: pokemon,
|
||||||
|
receivedPokemon: tradePokemon,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
return selectPokemonForOption(scene, onPokemonSelected);
|
||||||
|
})
|
||||||
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
const tradedPokemon: PlayerPokemon = encounter.misc.tradedPokemon;
|
||||||
|
const receivedPokemonData: EnemyPokemon = encounter.misc.receivedPokemon;
|
||||||
|
const modifiers = tradedPokemon.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier) && !(m instanceof SpeciesStatBoosterModifier));
|
||||||
|
|
||||||
|
// Generate a trainer name
|
||||||
|
const traderName = generateRandomTraderName();
|
||||||
|
encounter.setDialogueToken("tradeTrainerName", traderName.trim());
|
||||||
|
|
||||||
|
// Remove the original party member from party
|
||||||
|
scene.removePokemonFromPlayerParty(tradedPokemon, false);
|
||||||
|
|
||||||
// Set data properly, then generate the new Pokemon's assets
|
// Set data properly, then generate the new Pokemon's assets
|
||||||
receivedPokemonData.passive = tradedPokemon.passive;
|
receivedPokemonData.passive = tradedPokemon.passive;
|
||||||
receivedPokemonData.pokeball = randSeedInt(5);
|
receivedPokemonData.pokeball = randSeedInt(5);
|
||||||
@ -199,96 +276,10 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
await showEncounterText(scene, `${namespace}.trade_received`, 0, true, 4000);
|
await showEncounterText(scene, `${namespace}.trade_received`, 0, true, 4000);
|
||||||
scene.playBgm("mystery_encounter_gts");
|
scene.playBgm("mystery_encounter_gts");
|
||||||
await hideTradeBackground(scene);
|
await hideTradeBackground(scene);
|
||||||
|
tradedPokemon.destroy();
|
||||||
|
|
||||||
setEncounterRewards(scene, { fillRemaining: true });
|
|
||||||
leaveEncounterWithoutBattle(scene);
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.withOption(
|
|
||||||
MysteryEncounterOptionBuilder
|
|
||||||
.newOptionWithMode(MysteryEncounterOptionMode.DEFAULT)
|
|
||||||
.withDialogue({
|
|
||||||
buttonLabel: `${namespace}.option.2.label`,
|
|
||||||
buttonTooltip: `${namespace}.option.2.tooltip`,
|
|
||||||
selected: [
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.2.selected`,
|
|
||||||
speaker: `${namespace}.speaker`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.2.selected_2`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.2.selected_3`,
|
|
||||||
speaker: `${namespace}.speaker`
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Swap player's items on pokemon with the most items
|
|
||||||
// Item comparisons look at whichever Pokemon has the greatest number of TRANSFERABLE, non-berry items
|
|
||||||
// So Vitamins, form change items, etc. are not included
|
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
|
||||||
|
|
||||||
const party = scene.getParty();
|
|
||||||
let mostHeldItemsPokemon = party[0];
|
|
||||||
let count = mostHeldItemsPokemon.getHeldItems()
|
|
||||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
|
||||||
.reduce((v, m) => v + m.stackCount, 0);
|
|
||||||
|
|
||||||
party.forEach(pokemon => {
|
|
||||||
const nextCount = pokemon.getHeldItems()
|
|
||||||
.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
|
||||||
.reduce((v, m) => v + m.stackCount, 0);
|
|
||||||
if (nextCount > count) {
|
|
||||||
mostHeldItemsPokemon = pokemon;
|
|
||||||
count = nextCount;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
encounter.setDialogueToken("switchPokemon", mostHeldItemsPokemon.getNameToRender());
|
|
||||||
|
|
||||||
const items = mostHeldItemsPokemon.getHeldItems();
|
|
||||||
|
|
||||||
// Shuffles Berries (if they have any)
|
|
||||||
let numBerries = 0;
|
|
||||||
items.filter(m => m instanceof BerryModifier)
|
|
||||||
.forEach(m => {
|
|
||||||
numBerries += m.stackCount;
|
|
||||||
scene.removeModifier(m);
|
|
||||||
});
|
|
||||||
|
|
||||||
generateItemsOfTier(scene, mostHeldItemsPokemon, numBerries, "Berries");
|
|
||||||
|
|
||||||
// Shuffle Transferable held items in the same tier (only shuffles Ultra and Rogue atm)
|
|
||||||
let numUltra = 0;
|
|
||||||
let numRogue = 0;
|
|
||||||
items.filter(m => m.isTransferrable && !(m instanceof BerryModifier))
|
|
||||||
.forEach(m => {
|
|
||||||
const type = m.type.withTierFromPool();
|
|
||||||
const tier = type.tier ?? ModifierTier.ULTRA;
|
|
||||||
if (type.id === "LUCKY_EGG" || tier === ModifierTier.ULTRA) {
|
|
||||||
numUltra += m.stackCount;
|
|
||||||
scene.removeModifier(m);
|
|
||||||
} else if (type.id === "GOLDEN_EGG" || tier === ModifierTier.ROGUE) {
|
|
||||||
numRogue += m.stackCount;
|
|
||||||
scene.removeModifier(m);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
generateItemsOfTier(scene, mostHeldItemsPokemon, numUltra, ModifierTier.ULTRA);
|
|
||||||
generateItemsOfTier(scene, mostHeldItemsPokemon, numRogue, ModifierTier.ROGUE);
|
|
||||||
})
|
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
})
|
})
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Play animations
|
|
||||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
|
||||||
background.playWithoutTargets(scene, 230, 40, 2);
|
|
||||||
await transitionMysteryEncounterIntroVisuals(scene);
|
|
||||||
})
|
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.withOption(
|
.withOption(
|
||||||
@ -297,74 +288,108 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
.withDialogue({
|
.withDialogue({
|
||||||
buttonLabel: `${namespace}.option.3.label`,
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
buttonTooltip: `${namespace}.option.3.tooltip`,
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
selected: [
|
secondOptionPrompt: `${namespace}.option.3.trade_options_prompt`,
|
||||||
{
|
|
||||||
text: `${namespace}.option.3.selected`,
|
|
||||||
speaker: `${namespace}.speaker`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.3.selected_2`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: `${namespace}.option.3.selected_3`,
|
|
||||||
speaker: `${namespace}.speaker`
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
.withPreOptionPhase(async (scene: BattleScene): Promise<boolean> => {
|
||||||
// Swap player's types on all party pokemon
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
// If a Pokemon had a single type prior, they will still have a single type after
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
for (const pokemon of scene.getParty()) {
|
// Get Pokemon held items and filter for valid ones
|
||||||
const originalTypes = pokemon.getTypes(false, false, true);
|
const validItems = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return it.isTransferrable;
|
||||||
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
|
|
||||||
// Makes the "randomness" of the shuffle slightly less punishing
|
|
||||||
let priorityTypes = pokemon.moveset
|
|
||||||
.filter(move => move && !originalTypes.includes(move.getMove().type) && move.getMove().category !== MoveCategory.STATUS)
|
|
||||||
.map(move => move!.getMove().type);
|
|
||||||
if (priorityTypes?.length > 0) {
|
|
||||||
priorityTypes = [...new Set(priorityTypes)];
|
|
||||||
randSeedShuffle(priorityTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
let newTypes: Type[];
|
|
||||||
if (!originalTypes || originalTypes.length < 1) {
|
|
||||||
newTypes = priorityTypes && priorityTypes.length > 0 ? [priorityTypes.pop()!] : [(randSeedInt(18) as Type)];
|
|
||||||
} else {
|
|
||||||
newTypes = originalTypes.map(m => {
|
|
||||||
if (priorityTypes && priorityTypes.length > 0) {
|
|
||||||
const ret = priorityTypes.pop()!;
|
|
||||||
randSeedShuffle(priorityTypes);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return randSeedInt(18) as Type;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return validItems.map((modifier: PokemonHeldItemModifier) => {
|
||||||
|
const option: OptionSelectItem = {
|
||||||
|
label: modifier.type.name,
|
||||||
|
handler: () => {
|
||||||
|
// Pokemon and item selected
|
||||||
|
encounter.setDialogueToken("chosenItem", modifier.type.name);
|
||||||
|
encounter.misc = {
|
||||||
|
chosenModifier: modifier,
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only Pokemon that can gain benefits are above 1/3rd HP with no status
|
||||||
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
|
// If pokemon has items to trade
|
||||||
|
const meetsReqs = pokemon.getHeldItems().filter((it) => {
|
||||||
|
return it.isTransferrable;
|
||||||
|
}).length > 0;
|
||||||
|
if (!meetsReqs) {
|
||||||
|
return getEncounterText(scene, `${namespace}.option.3.invalid_selection`) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pokemon.mysteryEncounterData) {
|
return null;
|
||||||
pokemon.mysteryEncounterData = new MysteryEncounterPokemonData(undefined, undefined, undefined, newTypes);
|
};
|
||||||
} else {
|
|
||||||
pokemon.mysteryEncounterData.types = newTypes;
|
return selectPokemonForOption(scene, onPokemonSelected, undefined, selectableFilter);
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
})
|
const modifier = encounter.misc.chosenModifier;
|
||||||
.withPostOptionPhase(async (scene: BattleScene) => {
|
|
||||||
// Play animations
|
// Check tier of the traded item, the received item will be one tier up
|
||||||
const background = new EncounterBattleAnim(EncounterAnim.SMOKESCREEN, scene.getPlayerPokemon()!, scene.getPlayerPokemon());
|
const type = modifier.type.withTierFromPool();
|
||||||
background.playWithoutTargets(scene, 230, 40, 2);
|
let tier = type.tier ?? ModifierTier.GREAT;
|
||||||
await transitionMysteryEncounterIntroVisuals(scene);
|
// Eggs and White Herb are not in the pool
|
||||||
|
if (type.id === "WHITE_HERB") {
|
||||||
|
tier = ModifierTier.GREAT;
|
||||||
|
} else if (type.id === "LUCKY_EGG") {
|
||||||
|
tier = ModifierTier.ULTRA;
|
||||||
|
} else if (type.id === "GOLDEN_EGG") {
|
||||||
|
tier = ModifierTier.ROGUE;
|
||||||
|
}
|
||||||
|
// Increment tier by 1
|
||||||
|
if (tier < ModifierTier.MASTER) {
|
||||||
|
tier++;
|
||||||
|
}
|
||||||
|
|
||||||
|
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
||||||
|
let item: ModifierTypeOption | null = null;
|
||||||
|
// TMs excluded from possible rewards
|
||||||
|
while (!item || item.type.id.includes("TM_")) {
|
||||||
|
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [tier], allowLuckUpgrades: false })[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
|
setEncounterRewards(scene, { guaranteedModifierTypeOptions: [item], fillRemaining: false });
|
||||||
|
|
||||||
|
// Remove the chosen modifier if its stacks go to 0
|
||||||
|
modifier.stackCount -= 1;
|
||||||
|
if (modifier.stackCount === 0) {
|
||||||
|
scene.removeModifier(modifier);
|
||||||
|
}
|
||||||
|
scene.updateModifiers(true, true);
|
||||||
|
|
||||||
|
// Generate a trainer name
|
||||||
|
const traderName = generateRandomTraderName();
|
||||||
|
encounter.setDialogueToken("tradeTrainerName", traderName.trim());
|
||||||
|
await showEncounterText(scene, `${namespace}.item_trade_selected`);
|
||||||
|
leaveEncounterWithoutBattle(scene);
|
||||||
})
|
})
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.withOutroDialogue([
|
.withSimpleOption(
|
||||||
{
|
{
|
||||||
text: `${namespace}.outro`,
|
buttonLabel: `${namespace}.option.4.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.4.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.4.selected`,
|
||||||
},
|
},
|
||||||
])
|
],
|
||||||
|
},
|
||||||
|
async (scene: BattleScene) => {
|
||||||
|
// Leave encounter with no rewards or exp
|
||||||
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> {
|
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> {
|
||||||
@ -386,7 +411,7 @@ function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]>
|
|||||||
|
|
||||||
const tradeOptions: PokemonSpecies[] = [];
|
const tradeOptions: PokemonSpecies[] = [];
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
const speciesTradeOption = generateTradeOption(originalBst, alreadyUsedSpecies);
|
const speciesTradeOption = generateTradeOption(alreadyUsedSpecies, originalBst);
|
||||||
alreadyUsedSpecies.push(speciesTradeOption);
|
alreadyUsedSpecies.push(speciesTradeOption);
|
||||||
tradeOptions.push(speciesTradeOption);
|
tradeOptions.push(speciesTradeOption);
|
||||||
}
|
}
|
||||||
@ -401,11 +426,15 @@ function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]>
|
|||||||
return tradeOptionsMap;
|
return tradeOptionsMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateTradeOption(originalBst: number, alreadyUsedSpecies: PokemonSpecies[]): PokemonSpecies {
|
function generateTradeOption(alreadyUsedSpecies: PokemonSpecies[], originalBst?: number): PokemonSpecies {
|
||||||
let newSpecies: PokemonSpecies | undefined;
|
let newSpecies: PokemonSpecies | undefined;
|
||||||
while (isNullOrUndefined(newSpecies)) {
|
while (isNullOrUndefined(newSpecies)) {
|
||||||
let bstCap = originalBst + 100;
|
let bstCap = 9999;
|
||||||
let bstMin = originalBst - 100;
|
let bstMin = 0;
|
||||||
|
if (originalBst) {
|
||||||
|
bstCap = originalBst + 100;
|
||||||
|
bstMin = originalBst - 100;
|
||||||
|
}
|
||||||
|
|
||||||
// Get all non-legendary species that fall within the Bst range requirements
|
// Get all non-legendary species that fall within the Bst range requirements
|
||||||
let validSpecies = allSpecies
|
let validSpecies = allSpecies
|
||||||
@ -433,69 +462,6 @@ function generateTradeOption(originalBst: number, alreadyUsedSpecies: PokemonSpe
|
|||||||
return newSpecies!;
|
return newSpecies!;
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateItemsOfTier(scene: BattleScene, pokemon: PlayerPokemon, numItems: integer, tier: ModifierTier | "Berries") {
|
|
||||||
// These pools have to be defined at runtime so that modifierTypes exist
|
|
||||||
// Pools have instances of the modifier type equal to the max stacks that modifier can be applied to any one pokemon
|
|
||||||
// This is to prevent "over-generating" a random item of a certain type during item swaps
|
|
||||||
const ultraPool = [
|
|
||||||
[modifierTypes.REVIVER_SEED, 1],
|
|
||||||
[modifierTypes.GOLDEN_PUNCH, 5],
|
|
||||||
[modifierTypes.ATTACK_TYPE_BOOSTER, 99],
|
|
||||||
[modifierTypes.QUICK_CLAW, 3],
|
|
||||||
[modifierTypes.WIDE_LENS, 3]
|
|
||||||
];
|
|
||||||
|
|
||||||
const roguePool = [
|
|
||||||
[modifierTypes.LEFTOVERS, 4],
|
|
||||||
[modifierTypes.SHELL_BELL, 4],
|
|
||||||
[modifierTypes.SOUL_DEW, 10],
|
|
||||||
[modifierTypes.SOOTHE_BELL, 3],
|
|
||||||
[modifierTypes.SCOPE_LENS, 1],
|
|
||||||
[modifierTypes.BATON, 1],
|
|
||||||
[modifierTypes.FOCUS_BAND, 5],
|
|
||||||
[modifierTypes.KINGS_ROCK, 3],
|
|
||||||
[modifierTypes.GRIP_CLAW, 5]
|
|
||||||
];
|
|
||||||
|
|
||||||
const berryPool = [
|
|
||||||
[BerryType.APICOT, 3],
|
|
||||||
[BerryType.ENIGMA, 2],
|
|
||||||
[BerryType.GANLON, 3],
|
|
||||||
[BerryType.LANSAT, 3],
|
|
||||||
[BerryType.LEPPA, 2],
|
|
||||||
[BerryType.LIECHI, 3],
|
|
||||||
[BerryType.LUM, 2],
|
|
||||||
[BerryType.PETAYA, 3],
|
|
||||||
[BerryType.SALAC, 2],
|
|
||||||
[BerryType.SITRUS, 2],
|
|
||||||
[BerryType.STARF, 3]
|
|
||||||
];
|
|
||||||
|
|
||||||
let pool: any[];
|
|
||||||
if (tier === "Berries") {
|
|
||||||
pool = berryPool;
|
|
||||||
} else {
|
|
||||||
pool = tier === ModifierTier.ULTRA ? ultraPool : roguePool;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < numItems; i++) {
|
|
||||||
const randIndex = randSeedInt(pool.length);
|
|
||||||
const newItemType = pool[randIndex];
|
|
||||||
let newMod;
|
|
||||||
if (tier === "Berries") {
|
|
||||||
newMod = generateModifierType(scene, modifierTypes.BERRY, [newItemType[0]]) as PokemonHeldItemModifierType;
|
|
||||||
} else {
|
|
||||||
newMod = generateModifierType(scene, newItemType[0]) as PokemonHeldItemModifierType;
|
|
||||||
}
|
|
||||||
applyModifierTypeToPlayerPokemon(scene, pokemon, newMod);
|
|
||||||
// Decrement max stacks and remove from pool if at max
|
|
||||||
newItemType[1]--;
|
|
||||||
if (newItemType[1] <= 0) {
|
|
||||||
pool.splice(randIndex, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showTradeBackground(scene: BattleScene) {
|
function showTradeBackground(scene: BattleScene) {
|
||||||
return new Promise<void>(resolve => {
|
return new Promise<void>(resolve => {
|
||||||
const tradeContainer = scene.add.container(0, -scene.game.canvas.height / 6);
|
const tradeContainer = scene.add.container(0, -scene.game.canvas.height / 6);
|
||||||
@ -846,7 +812,12 @@ function doTradeReceivedSequence(scene: BattleScene, receivedPokemon: PlayerPoke
|
|||||||
|
|
||||||
function generateRandomTraderName() {
|
function generateRandomTraderName() {
|
||||||
const length = Object.keys(trainerNamePools).length;
|
const length = Object.keys(trainerNamePools).length;
|
||||||
const trainerTypePool = trainerNamePools[randInt(length)];
|
// +1 avoids TrainerType.UNKNOWN
|
||||||
|
let trainerTypePool = trainerNamePools[randInt(length) + 1];
|
||||||
|
while (!trainerTypePool) {
|
||||||
|
trainerTypePool = trainerNamePools[randInt(length) + 1];
|
||||||
|
}
|
||||||
|
// Some trainers have 2 gendered pools, some do not
|
||||||
const genderedPool = trainerTypePool[randInt(trainerTypePool.length)];
|
const genderedPool = trainerTypePool[randInt(trainerTypePool.length)];
|
||||||
const trainerNameString = genderedPool instanceof Array ? genderedPool[randInt(genderedPool.length)] : genderedPool;
|
const trainerNameString = genderedPool instanceof Array ? genderedPool[randInt(genderedPool.length)] : genderedPool;
|
||||||
// Some names have an '&' symbol and need to be trimmed to a single name instead of a double name
|
// Some names have an '&' symbol and need to be trimmed to a single name instead of a double name
|
||||||
|
@ -256,7 +256,7 @@ async function summonSafariPokemon(scene: BattleScene) {
|
|||||||
|
|
||||||
// Roll shiny twice
|
// Roll shiny twice
|
||||||
if (!pokemon.shiny) {
|
if (!pokemon.shiny) {
|
||||||
pokemon.trySetShiny();
|
pokemon.trySetShinySeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Roll HA twice
|
// Roll HA twice
|
||||||
|
@ -45,7 +45,9 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
|||||||
spriteKey: "teleporter",
|
spriteKey: "teleporter",
|
||||||
fileRoot: "mystery-encounters",
|
fileRoot: "mystery-encounters",
|
||||||
hasShadow: true,
|
hasShadow: true,
|
||||||
y: 4
|
x: 4,
|
||||||
|
y: 4,
|
||||||
|
yShadow: 1
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
.withIntroDialogue([
|
.withIntroDialogue([
|
||||||
|
@ -5,7 +5,7 @@ import { variantData } from "#app/data/variant";
|
|||||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
||||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
|
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
|
||||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||||
import { Constructor, isNullOrUndefined } from "#app/utils";
|
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
|
||||||
import { getLevelTotalExp } from "../data/exp";
|
import { getLevelTotalExp } from "../data/exp";
|
||||||
@ -201,7 +201,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.fusionGender = dataSource.fusionGender;
|
this.fusionGender = dataSource.fusionGender;
|
||||||
this.fusionLuck = dataSource.fusionLuck;
|
this.fusionLuck = dataSource.fusionLuck;
|
||||||
this.usedTMs = dataSource.usedTMs ?? [];
|
this.usedTMs = dataSource.usedTMs ?? [];
|
||||||
this.mysteryEncounterData = dataSource.mysteryEncounterData;
|
this.mysteryEncounterData = dataSource.mysteryEncounterData ?? new MysteryEncounterPokemonData();
|
||||||
} else {
|
} else {
|
||||||
this.id = Utils.randSeedInt(4294967296);
|
this.id = Utils.randSeedInt(4294967296);
|
||||||
this.ivs = ivs || Utils.getIvsFromId(this.id);
|
this.ivs = ivs || Utils.getIvsFromId(this.id);
|
||||||
@ -577,8 +577,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const formKey = this.getFormKey();
|
const formKey = this.getFormKey();
|
||||||
if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) {
|
if (formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1 || formKey.indexOf(SpeciesFormKey.ETERNAMAX) > -1) {
|
||||||
return 1.5;
|
return 1.5;
|
||||||
} else if (!isNullOrUndefined(this.mysteryEncounterData?.spriteScale)) {
|
} else if (!isNullOrUndefined(this.mysteryEncounterData.spriteScale) && this.mysteryEncounterData.spriteScale !== 0) {
|
||||||
return this.mysteryEncounterData.spriteScale;
|
return this.mysteryEncounterData.spriteScale!;
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1082,7 +1082,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!types.length || !includeTeraType) {
|
if (!types.length || !includeTeraType) {
|
||||||
if (this.mysteryEncounterData?.types && this.mysteryEncounterData.types.length > 0) {
|
if (this.mysteryEncounterData.types && this.mysteryEncounterData.types.length > 0) {
|
||||||
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
// "Permanent" override for a Pokemon's normal types, currently only used by Mystery Encounters
|
||||||
this.mysteryEncounterData.types.forEach(t => types.push(t));
|
this.mysteryEncounterData.types.forEach(t => types.push(t));
|
||||||
} else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
} else if (!ignoreOverride && this.summonData?.types && this.summonData.types.length > 0) {
|
||||||
@ -1716,6 +1716,42 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.shiny;
|
return this.shiny;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function that tries to set a Pokemon shiny based on seed.
|
||||||
|
* For manual use only, usually to roll a Pokemon's shiny chance a second time.
|
||||||
|
*
|
||||||
|
* The base shiny odds are {@linkcode baseShinyChance} / 65536
|
||||||
|
* @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
||||||
|
* @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride}
|
||||||
|
* @returns true if the Pokemon has been set as a shiny, false otherwise
|
||||||
|
*/
|
||||||
|
trySetShinySeed(thresholdOverride?: integer, applyModifiersToOverride?: boolean): boolean {
|
||||||
|
/** `64/65536 -> 1/1024` */
|
||||||
|
const baseShinyChance = 64;
|
||||||
|
const shinyThreshold = new Utils.IntegerHolder(baseShinyChance);
|
||||||
|
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
||||||
|
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||||
|
shinyThreshold.value = thresholdOverride;
|
||||||
|
}
|
||||||
|
if (this.scene.eventManager.isEventActive()) {
|
||||||
|
shinyThreshold.value *= this.scene.eventManager.getShinyMultiplier();
|
||||||
|
}
|
||||||
|
if (!this.hasTrainer()) {
|
||||||
|
this.scene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shinyThreshold.value = thresholdOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shiny = randSeedInt(65536) < shinyThreshold.value;
|
||||||
|
|
||||||
|
if (this.shiny) {
|
||||||
|
this.initShinySparkle();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.shiny;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a variant
|
* Generates a variant
|
||||||
* Has a 10% of returning 2 (epic variant)
|
* Has a 10% of returning 2 (epic variant)
|
||||||
|
@ -15,16 +15,18 @@
|
|||||||
},
|
},
|
||||||
"3": {
|
"3": {
|
||||||
"label": "Trade an Item",
|
"label": "Trade an Item",
|
||||||
|
"trade_options_prompt": "Select an item to send.",
|
||||||
|
"invalid_selection": "This Pokémon doesn't have legal items to trade.",
|
||||||
"tooltip": "(+) Send one of your Items to the GTS and get a random new Item"
|
"tooltip": "(+) Send one of your Items to the GTS and get a random new Item"
|
||||||
},
|
},
|
||||||
"4": {
|
"4": {
|
||||||
"label": "Leave",
|
"label": "Leave",
|
||||||
"tooltip": "(-) No Rewards",
|
"tooltip": "(-) No Rewards",
|
||||||
"selected": "No time to try your luck today!\nYou continue on."
|
"selected": "No time to trade today!\nYou continue on."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"pokemon_trade_selected": "{{tradedPokemon}} will be sent to {{tradeTrainerName}}.",
|
"pokemon_trade_selected": "{{tradedPokemon}} will be sent to {{tradeTrainerName}}.",
|
||||||
"pokemon_trade_goodbye": "Goodbye, {{tradedPokemon}}!",
|
"pokemon_trade_goodbye": "Goodbye, {{tradedPokemon}}!",
|
||||||
"item_trade_selected": "{{tradedPokemon}} will be sent to {{tradeTrainerName}}",
|
"item_trade_selected": "{{chosenItem}} will be sent to {{tradeTrainerName}}.$.@d{64}.@d{64}.@d{64}\n@s{level_up_fanfare}Trade complete!$You received a {{itemName}} from {{tradeTrainerName}}!",
|
||||||
"trade_received": "@s{evolution_fanfare}{{tradeTrainerName}} sent over {{received}}!"
|
"trade_received": "@s{evolution_fanfare}{{tradeTrainerName}} sent over {{received}}!"
|
||||||
}
|
}
|
@ -141,9 +141,9 @@ class DefaultOverrides {
|
|||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
/** 1 to 256, set to null to ignore */
|
/** 1 to 256, set to null to ignore */
|
||||||
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = 256;
|
readonly MYSTERY_ENCOUNTER_RATE_OVERRIDE: number | null = null;
|
||||||
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null;
|
readonly MYSTERY_ENCOUNTER_TIER_OVERRIDE: MysteryEncounterTier | null = null;
|
||||||
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = MysteryEncounterType.GLOBAL_TRADE_SYSTEM;
|
readonly MYSTERY_ENCOUNTER_OVERRIDE: MysteryEncounterType | null = null;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// MODIFIER / ITEM OVERRIDES
|
// MODIFIER / ITEM OVERRIDES
|
||||||
|
@ -103,6 +103,8 @@ export default class PokemonData {
|
|||||||
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
|
this.fusionLuck = source.fusionLuck !== undefined ? source.fusionLuck : (source.fusionShiny ? source.fusionVariant + 1 : 0);
|
||||||
this.usedTMs = source.usedTMs ?? [];
|
this.usedTMs = source.usedTMs ?? [];
|
||||||
|
|
||||||
|
this.mysteryEncounterData = source.mysteryEncounterData ?? new MysteryEncounterPokemonData();
|
||||||
|
|
||||||
if (!forHistory) {
|
if (!forHistory) {
|
||||||
this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss);
|
this.boss = (source instanceof EnemyPokemon && !!source.bossSegments) || (!this.player && !!source.boss);
|
||||||
this.bossSegments = source.bossSegments;
|
this.bossSegments = source.bossSegments;
|
||||||
@ -114,7 +116,6 @@ export default class PokemonData {
|
|||||||
this.status = sourcePokemon.status;
|
this.status = sourcePokemon.status;
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
this.summonData = sourcePokemon.summonData;
|
this.summonData = sourcePokemon.summonData;
|
||||||
this.mysteryEncounterData = sourcePokemon.mysteryEncounterData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -143,14 +144,6 @@ export default class PokemonData {
|
|||||||
this.summonData.tags = [];
|
this.summonData.tags = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mysteryEncounterData = new MysteryEncounterPokemonData();
|
|
||||||
if (!forHistory && source.mysteryEncounterData) {
|
|
||||||
this.mysteryEncounterData.spriteScale = source.mysteryEncounterData.spriteScale;
|
|
||||||
this.mysteryEncounterData.ability = source.mysteryEncounterData.ability;
|
|
||||||
this.mysteryEncounterData.passive = source.mysteryEncounterData.passive;
|
|
||||||
this.mysteryEncounterData.types = source.mysteryEncounterData.types;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +113,10 @@ export async function runSelectMysteryEncounterOption(game: GameManager, optionN
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uiHandler.processInput(Button.ACTION);
|
|
||||||
|
|
||||||
if (!isNullOrUndefined(secondaryOptionSelect?.pokemonNo)) {
|
if (!isNullOrUndefined(secondaryOptionSelect?.pokemonNo)) {
|
||||||
await handleSecondaryOptionSelect(game, secondaryOptionSelect!.pokemonNo, secondaryOptionSelect!.optionNo);
|
await handleSecondaryOptionSelect(game, secondaryOptionSelect!.pokemonNo, secondaryOptionSelect!.optionNo);
|
||||||
|
} else {
|
||||||
|
uiHandler.processInput(Button.ACTION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,6 +124,10 @@ async function handleSecondaryOptionSelect(game: GameManager, pokemonNo: number,
|
|||||||
// Handle secondary option selections
|
// Handle secondary option selections
|
||||||
const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler;
|
const partyUiHandler = game.scene.ui.handlers[Mode.PARTY] as PartyUiHandler;
|
||||||
vi.spyOn(partyUiHandler, "show");
|
vi.spyOn(partyUiHandler, "show");
|
||||||
|
|
||||||
|
const encounterUiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
|
encounterUiHandler.processInput(Button.ACTION);
|
||||||
|
|
||||||
await vi.waitFor(() => expect(partyUiHandler.show).toHaveBeenCalled());
|
await vi.waitFor(() => expect(partyUiHandler.show).toHaveBeenCalled());
|
||||||
|
|
||||||
for (let i = 1; i < pokemonNo; i++) {
|
for (let i = 1; i < pokemonNo; i++) {
|
||||||
|
@ -0,0 +1,270 @@
|
|||||||
|
import { Biome } from "#app/enums/biome";
|
||||||
|
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#app/enums/species";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { runMysteryEncounterToEnd } from "#test/mystery-encounter/encounter-test-utils";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { PokemonNatureWeightModifier } from "#app/modifier/modifier";
|
||||||
|
import { generateModifierType } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
|
import { GlobalTradeSystemEncounter } from "#app/data/mystery-encounters/encounters/global-trade-system-encounter";
|
||||||
|
import { CIVILIZATION_ENCOUNTER_BIOMES } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
|
||||||
|
const namespace = "mysteryEncounter:globalTradeSystem";
|
||||||
|
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
|
const defaultBiome = Biome.CAVE;
|
||||||
|
const defaultWave = 45;
|
||||||
|
|
||||||
|
describe("Global Trade System - Mystery Encounter", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
let scene: BattleScene;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({ type: Phaser.HEADLESS });
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
scene = game.scene;
|
||||||
|
game.override.mysteryEncounterChance(100);
|
||||||
|
game.override.startingWave(defaultWave);
|
||||||
|
game.override.startingBiome(defaultBiome);
|
||||||
|
game.override.disableTrainerWaves();
|
||||||
|
|
||||||
|
const biomeMap = new Map<Biome, MysteryEncounterType[]>([
|
||||||
|
[Biome.VOLCANO, [MysteryEncounterType.MYSTERIOUS_CHALLENGERS]],
|
||||||
|
]);
|
||||||
|
CIVILIZATION_ENCOUNTER_BIOMES.forEach(biome => {
|
||||||
|
biomeMap.set(biome, [MysteryEncounterType.GLOBAL_TRADE_SYSTEM]);
|
||||||
|
});
|
||||||
|
vi.spyOn(MysteryEncounters, "mysteryEncountersByBiome", "get").mockReturnValue(biomeMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have the correct properties", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
expect(GlobalTradeSystemEncounter.encounterType).toBe(MysteryEncounterType.GLOBAL_TRADE_SYSTEM);
|
||||||
|
expect(GlobalTradeSystemEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||||
|
expect(GlobalTradeSystemEncounter.dialogue).toBeDefined();
|
||||||
|
expect(GlobalTradeSystemEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]);
|
||||||
|
expect(GlobalTradeSystemEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
|
||||||
|
expect(GlobalTradeSystemEncounter.dialogue.encounterOptionsDialogue?.description).toBe(`${namespace}.description`);
|
||||||
|
expect(GlobalTradeSystemEncounter.dialogue.encounterOptionsDialogue?.query).toBe(`${namespace}.query`);
|
||||||
|
expect(GlobalTradeSystemEncounter.options.length).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run below wave 10", async () => {
|
||||||
|
game.override.startingWave(9);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.GLOBAL_TRADE_SYSTEM);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not run above wave 179", async () => {
|
||||||
|
game.override.startingWave(181);
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle.mysteryEncounter).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not spawn outside of CIVILIZATION_ENCOUNTER_BIOMES", async () => {
|
||||||
|
game.override.mysteryEncounterTier(MysteryEncounterTier.COMMON);
|
||||||
|
game.override.startingBiome(Biome.VOLCANO);
|
||||||
|
await game.runToMysteryEncounter();
|
||||||
|
|
||||||
|
expect(scene.currentBattle?.mysteryEncounter?.encounterType).not.toBe(MysteryEncounterType.GLOBAL_TRADE_SYSTEM);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 1 - Check Trade Offers", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = GlobalTradeSystemEncounter.options[0];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}.option.1.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.1.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.1.trade_options_prompt`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should trade a Pokemon from the player's party for the first of 3 Pokemon options", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
const speciesBefore = scene.getParty()[0].species.speciesId;
|
||||||
|
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 });
|
||||||
|
|
||||||
|
const speciesAfter = scene.getParty().at(-1)?.species.speciesId;
|
||||||
|
|
||||||
|
expect(speciesAfter).toBeDefined();
|
||||||
|
expect(speciesBefore).not.toBe(speciesAfter);
|
||||||
|
expect(defaultParty.includes(speciesAfter!)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should trade a Pokemon from the player's party for the second of 3 Pokemon options", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
const speciesBefore = scene.getParty()[1].species.speciesId;
|
||||||
|
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 2, optionNo: 2 });
|
||||||
|
|
||||||
|
const speciesAfter = scene.getParty().at(-1)?.species.speciesId;
|
||||||
|
|
||||||
|
expect(speciesAfter).toBeDefined();
|
||||||
|
expect(speciesBefore).not.toBe(speciesAfter);
|
||||||
|
expect(defaultParty.includes(speciesAfter!)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should trade a Pokemon from the player's party for the third of 3 Pokemon options", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
const speciesBefore = scene.getParty()[2].species.speciesId;
|
||||||
|
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 3, optionNo: 3 });
|
||||||
|
|
||||||
|
const speciesAfter = scene.getParty().at(-1)?.species.speciesId;
|
||||||
|
|
||||||
|
expect(speciesAfter).toBeDefined();
|
||||||
|
expect(speciesBefore).not.toBe(speciesAfter);
|
||||||
|
expect(defaultParty.includes(speciesAfter!)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1, optionNo: 1 });
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 2 - Wonder Trade", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = GlobalTradeSystemEncounter.options[1];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}.option.2.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.2.tooltip`
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should trade a Pokemon from the player's party for the a random wonder trade Pokemon", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
const speciesBefore = scene.getParty()[2].species.speciesId;
|
||||||
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 1 });
|
||||||
|
|
||||||
|
const speciesAfter = scene.getParty().at(-1)?.species.speciesId;
|
||||||
|
|
||||||
|
expect(speciesAfter).toBeDefined();
|
||||||
|
expect(speciesBefore).not.toBe(speciesAfter);
|
||||||
|
expect(defaultParty.includes(speciesAfter!)).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 2, { pokemonNo: 2 });
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 3 - Trade an Item", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = GlobalTradeSystemEncounter.options[2];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}.option.3.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.3.tooltip`,
|
||||||
|
secondOptionPrompt: `${namespace}.option.3.trade_options_prompt`,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should decrease item stacks of chosen item and have a tiered up item in rewards", async () => {
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
// Set 2 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierType(scene, modifierTypes.SOUL_DEW)!;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]) as PokemonNatureWeightModifier;
|
||||||
|
modifier.stackCount = 2;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1});
|
||||||
|
expect(scene.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||||
|
|
||||||
|
expect(scene.ui.getMode()).to.equal(Mode.MODIFIER_SELECT);
|
||||||
|
const modifierSelectHandler = scene.ui.handlers.find(h => h instanceof ModifierSelectUiHandler) as ModifierSelectUiHandler;
|
||||||
|
expect(modifierSelectHandler.options.length).toEqual(1);
|
||||||
|
expect(modifierSelectHandler.options[0].modifierTypeOption.type.tier).toBe(ModifierTier.MASTER);
|
||||||
|
const soulDewAfter = scene.findModifier(m => m instanceof PokemonNatureWeightModifier);
|
||||||
|
expect(soulDewAfter?.stackCount).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
|
||||||
|
// Set 1 Soul Dew on party lead
|
||||||
|
scene.modifiers = [];
|
||||||
|
const soulDew = generateModifierType(scene, modifierTypes.SOUL_DEW)!;
|
||||||
|
const modifier = soulDew.newModifier(scene.getParty()[0]) as PokemonNatureWeightModifier;
|
||||||
|
modifier.stackCount = 1;
|
||||||
|
await scene.addModifier(modifier, true, false, false, true);
|
||||||
|
await scene.updateModifiers(true);
|
||||||
|
|
||||||
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1});
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Option 4 - Leave", () => {
|
||||||
|
it("should have the correct properties", () => {
|
||||||
|
const option = GlobalTradeSystemEncounter.options[3];
|
||||||
|
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);
|
||||||
|
expect(option.dialogue).toBeDefined();
|
||||||
|
expect(option.dialogue).toStrictEqual({
|
||||||
|
buttonLabel: `${namespace}.option.4.label`,
|
||||||
|
buttonTooltip: `${namespace}.option.4.tooltip`,
|
||||||
|
selected: [
|
||||||
|
{
|
||||||
|
text: `${namespace}.option.4.selected`,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should leave encounter without battle", async () => {
|
||||||
|
const leaveEncounterWithoutBattleSpy = vi.spyOn(EncounterPhaseUtils, "leaveEncounterWithoutBattle");
|
||||||
|
|
||||||
|
await game.runToMysteryEncounter(MysteryEncounterType.GLOBAL_TRADE_SYSTEM, defaultParty);
|
||||||
|
await runMysteryEncounterToEnd(game, 4);
|
||||||
|
|
||||||
|
expect(leaveEncounterWithoutBattleSpy).toBeCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -66,7 +66,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||||
|
|
||||||
expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
expect(TheStrongStuffEncounter.encounterType).toBe(MysteryEncounterType.THE_STRONG_STUFF);
|
||||||
expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
expect(TheStrongStuffEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT);
|
||||||
expect(TheStrongStuffEncounter.dialogue).toBeDefined();
|
expect(TheStrongStuffEncounter.dialogue).toBeDefined();
|
||||||
expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]);
|
expect(TheStrongStuffEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}.intro` }]);
|
||||||
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
|
expect(TheStrongStuffEncounter.dialogue.encounterOptionsDialogue?.title).toBe(`${namespace}.title`);
|
||||||
@ -121,7 +121,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
species: getPokemonSpecies(Species.SHUCKLE),
|
species: getPokemonSpecies(Species.SHUCKLE),
|
||||||
isBoss: true,
|
isBoss: true,
|
||||||
bossSegments: 5,
|
bossSegments: 5,
|
||||||
mysteryEncounterData: new MysteryEncounterPokemonData(1.5),
|
mysteryEncounterData: new MysteryEncounterPokemonData(1.25),
|
||||||
nature: Nature.BOLD,
|
nature: Nature.BOLD,
|
||||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||||
modifierConfigs: expect.any(Array),
|
modifierConfigs: expect.any(Array),
|
||||||
|
@ -287,7 +287,7 @@ describe("Mystery Encounter Utils", () => {
|
|||||||
const spy = vi.spyOn(game.scene.ui, "showText");
|
const spy = vi.spyOn(game.scene.ui, "showText");
|
||||||
|
|
||||||
await showEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
await showEncounterText(scene, "mysteryEncounter:unit_test_dialogue");
|
||||||
expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, expect.any(Function), 0, true);
|
expect(spy).toHaveBeenCalledWith("valuevalue {{testvalue}} {{test1}} {{test}} {{test\\}} {{test\\}} {test}}", null, expect.any(Function), 0, true, null);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user