Compare commits
69 Commits
943f01881d
...
d61f0728bd
Author | SHA1 | Date |
---|---|---|
schmidtc1 | d61f0728bd | |
AJ Fontaine | 6392ee857c | |
NightKev | e75fa0d16d | |
AJ Fontaine | 82dad5568c | |
damocleas | e9d97db11b | |
AJ Fontaine | bbb6b46801 | |
AJ Fontaine | 1953e8dbe9 | |
Christopher Schmidt | 9d5e84dd90 | |
Christopher Schmidt | 068b0189db | |
Christopher Schmidt | c558c7f5fc | |
Christopher Schmidt | c2293716b5 | |
Christopher Schmidt | 77c9fb0daa | |
Christopher Schmidt | 7d1553320d | |
Christopher Schmidt | 468275ba23 | |
schmidtc1 | 83bafbe153 | |
Christopher Schmidt | 365e77d328 | |
Christopher Schmidt | 29c76294fc | |
Christopher Schmidt | df04e31886 | |
NightKev | 33809f4add | |
Christopher Schmidt | ce717b2db8 | |
Christopher Schmidt | 25e4037e45 | |
Christopher Schmidt | c9a4e8894e | |
Christopher Schmidt | fb29682b0b | |
Christopher Schmidt | b1fb1bd5d9 | |
Christopher Schmidt | a50984217f | |
Christopher Schmidt | 463f86fb83 | |
Christopher Schmidt | 931025d3d2 | |
Christopher Schmidt | 0ac69d1ee6 | |
Christopher Schmidt | dfdbe133d6 | |
Christopher Schmidt | 3c209f32b1 | |
Christopher Schmidt | 3206fa8e65 | |
Christopher Schmidt | e37805d53c | |
Christopher Schmidt | 6b5436fe44 | |
schmidtc1 | 259f06c324 | |
Christopher Schmidt | 9c60d1d6fd | |
Christopher Schmidt | 56ea4be3cc | |
schmidtc1 | c42b9da403 | |
Christopher Schmidt | f86b348afc | |
Christopher Schmidt | 76dba4c300 | |
Christopher Schmidt | 68ecc3dc19 | |
Christopher Schmidt | 6c656d81de | |
Christopher Schmidt | 0008881eca | |
Christopher Schmidt | ae0504b451 | |
Christopher Schmidt | bfef09ecb3 | |
Christopher Schmidt | 364e4cdb1b | |
schmidtc1 | 85c6a49e8d | |
Christopher Schmidt | 714211d381 | |
Christopher Schmidt | d874f98bf6 | |
NightKev | 4c9bcdc0e6 | |
Christopher Schmidt | fbb2a44c92 | |
Christopher Schmidt | 70dcc75a7c | |
Christopher Schmidt | 1e64ef0c90 | |
Christopher Schmidt | b854a37e05 | |
Christopher Schmidt | 15ff2c7f7e | |
schmidtc1 | e588c14a63 | |
Christopher Schmidt | df5da77dd7 | |
Christopher Schmidt | eb1d0714f1 | |
Christopher Schmidt | 040a8778ee | |
Christopher Schmidt | 42ec6297c5 | |
schmidtc1 | e66b75aa6d | |
Christopher Schmidt | c1d018ef29 | |
Tempoanon | 86b9f88335 | |
Christopher Schmidt | e10ed4320f | |
Christopher Schmidt | 67a04e171c | |
Christopher Schmidt | d712c3fbc0 | |
Christopher Schmidt | 8352f1a5c8 | |
Christopher Schmidt | f9bd2df312 | |
Christopher Schmidt | ad26883309 | |
Christopher Schmidt | bd60de6624 |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 33 KiB |
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 38 KiB |
|
@ -7,7 +7,7 @@ import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/mod
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { trainerConfigs } from "#app/data/trainer-config";
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
@ -44,12 +44,12 @@ export enum BattlerIndex {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TurnCommand {
|
export interface TurnCommand {
|
||||||
command: Command;
|
command: Command;
|
||||||
cursor?: number;
|
cursor?: number;
|
||||||
move?: QueuedMove;
|
move?: TurnMove;
|
||||||
targets?: BattlerIndex[];
|
targets?: BattlerIndex[];
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
args?: any[];
|
args?: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FaintLogEntry {
|
export interface FaintLogEntry {
|
||||||
|
@ -91,6 +91,7 @@ export default class Battle {
|
||||||
public playerFaintsHistory: FaintLogEntry[] = [];
|
public playerFaintsHistory: FaintLogEntry[] = [];
|
||||||
public enemyFaintsHistory: FaintLogEntry[] = [];
|
public enemyFaintsHistory: FaintLogEntry[] = [];
|
||||||
|
|
||||||
|
|
||||||
public mysteryEncounterType?: MysteryEncounterType;
|
public mysteryEncounterType?: MysteryEncounterType;
|
||||||
/** If the current battle is a Mystery Encounter, this will always be defined */
|
/** If the current battle is a Mystery Encounter, this will always be defined */
|
||||||
public mysteryEncounter?: MysteryEncounter;
|
public mysteryEncounter?: MysteryEncounter;
|
||||||
|
|
|
@ -4,7 +4,7 @@ export const POKERUS_STARTER_COUNT = 5;
|
||||||
|
|
||||||
// #region Friendship constants
|
// #region Friendship constants
|
||||||
export const CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER = 3;
|
export const CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER = 3;
|
||||||
export const FRIENDSHIP_GAIN_FROM_BATTLE = 3;
|
export const FRIENDSHIP_GAIN_FROM_BATTLE = 4;
|
||||||
export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 6;
|
export const FRIENDSHIP_GAIN_FROM_RARE_CANDY = 6;
|
||||||
export const FRIENDSHIP_LOSS_FROM_FAINT = 5;
|
export const FRIENDSHIP_LOSS_FROM_FAINT = 5;
|
||||||
|
|
||||||
|
|
|
@ -67148,6 +67148,7 @@ export const tmSpecies: TmSpecies = {
|
||||||
Species.VELUZA,
|
Species.VELUZA,
|
||||||
Species.DONDOZO,
|
Species.DONDOZO,
|
||||||
Species.TATSUGIRI,
|
Species.TATSUGIRI,
|
||||||
|
Species.ANNIHILAPE,
|
||||||
Species.CLODSIRE,
|
Species.CLODSIRE,
|
||||||
Species.FARIGIRAF,
|
Species.FARIGIRAF,
|
||||||
Species.DUDUNSPARCE,
|
Species.DUDUNSPARCE,
|
||||||
|
|
|
@ -610,7 +610,7 @@ export class InterruptedTag extends BattlerTag {
|
||||||
super.onAdd(pokemon);
|
super.onAdd(pokemon);
|
||||||
|
|
||||||
pokemon.getMoveQueue().shift();
|
pokemon.getMoveQueue().shift();
|
||||||
pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.OTHER });
|
pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.OTHER, targets: []});
|
||||||
}
|
}
|
||||||
|
|
||||||
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
|
||||||
|
|
830
src/data/move.ts
|
@ -13,7 +13,7 @@ import {
|
||||||
ModifierTypeOption, modifierTypes,
|
ModifierTypeOption, modifierTypes,
|
||||||
regenerateModifierPoolThresholds,
|
regenerateModifierPoolThresholds,
|
||||||
} from "#app/modifier/modifier-type";
|
} from "#app/modifier/modifier-type";
|
||||||
import { randSeedInt } from "#app/utils";
|
import { randSeedInt, randSeedItem } from "#app/utils";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-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";
|
||||||
|
@ -31,6 +31,7 @@ import { BerryType } from "#enums/berry-type";
|
||||||
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
import { PERMANENT_STATS, Stat } from "#enums/stat";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import PokemonSpecies, { allSpecies } from "#app/data/pokemon-species";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/berriesAbound";
|
const namespace = "mysteryEncounters/berriesAbound";
|
||||||
|
@ -58,7 +59,14 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
let bossSpecies: PokemonSpecies;
|
||||||
|
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||||
|
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||||
|
bossSpecies = allSpecies[eventEncounter.species];
|
||||||
|
bossSpecies.speciesId = bossSpecies.getSpeciesForLevel(level, eventEncounter.allowEvolution);
|
||||||
|
} else {
|
||||||
|
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), 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));
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifi
|
||||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { randSeedItem } from "#app/utils";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
@ -33,7 +34,24 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
|
||||||
"PokemonBaseStatTotalModifier"
|
"PokemonBaseStatTotalModifier"
|
||||||
];
|
];
|
||||||
|
|
||||||
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
|
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 1.5;
|
||||||
|
|
||||||
|
const doEventReward = (scene: BattleScene) => {
|
||||||
|
const event_buff = scene.eventManager.activeEvent()?.delibirdyBuff ?? [];
|
||||||
|
if (event_buff.length > 0) {
|
||||||
|
const candidates = event_buff.filter((c => {
|
||||||
|
const mtype = generateModifierType(scene, modifierTypes[c]);
|
||||||
|
const existingCharm = scene.findModifier(m => m.type.id === mtype?.id);
|
||||||
|
return !(existingCharm && existingCharm.getStackCount() >= existingCharm.getMaxStackCount(scene));
|
||||||
|
}));
|
||||||
|
if (candidates.length > 0) {
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes[randSeedItem(candidates)]));
|
||||||
|
} else {
|
||||||
|
// At max stacks, give a Voucher instead
|
||||||
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.VOUCHER));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delibird-y encounter.
|
* Delibird-y encounter.
|
||||||
|
@ -42,7 +60,8 @@ const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
|
||||||
*/
|
*/
|
||||||
export const DelibirdyEncounter: MysteryEncounter =
|
export const DelibirdyEncounter: MysteryEncounter =
|
||||||
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
MysteryEncounterBuilder.withEncounterType(MysteryEncounterType.DELIBIRDY)
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withMaxAllowedEncounters(4)
|
||||||
|
.withEncounterTier(MysteryEncounterTier.COMMON) //Change back after event!
|
||||||
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
.withSceneWaveRangeRequirement(...CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES)
|
||||||
.withSceneRequirement(new MoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER)) // Must have enough money for it to spawn at the very least
|
.withSceneRequirement(new MoneyRequirement(0, DELIBIRDY_MONEY_PRICE_MULTIPLIER)) // Must have enough money for it to spawn at the very least
|
||||||
.withPrimaryPokemonRequirement(
|
.withPrimaryPokemonRequirement(
|
||||||
|
@ -136,8 +155,10 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||||
|
doEventReward(scene);
|
||||||
} else {
|
} else {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN));
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.AMULET_COIN));
|
||||||
|
doEventReward(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveEncounterWithoutBattle(scene, true);
|
leaveEncounterWithoutBattle(scene, true);
|
||||||
|
@ -211,8 +232,10 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||||
|
doEventReward(scene);
|
||||||
} else {
|
} else {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.CANDY_JAR));
|
||||||
|
doEventReward(scene);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Check if the player has max stacks of that Berry Pouch already
|
// Check if the player has max stacks of that Berry Pouch already
|
||||||
|
@ -224,8 +247,10 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerPokemon()!, shellBell);
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||||
|
doEventReward(scene);
|
||||||
} else {
|
} else {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.BERRY_POUCH));
|
||||||
|
doEventReward(scene);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,8 +325,10 @@ export const DelibirdyEncounter: MysteryEncounter =
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell);
|
await applyModifierTypeToPlayerPokemon(scene, scene.getPlayerParty()[0], shellBell);
|
||||||
scene.playSound("item_fanfare");
|
scene.playSound("item_fanfare");
|
||||||
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
await showEncounterText(scene, i18next.t("battle:rewardGain", { modifierName: shellBell.name }), null, undefined, true);
|
||||||
|
doEventReward(scene);
|
||||||
} else {
|
} else {
|
||||||
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
scene.unshiftPhase(new ModifierRewardPhase(scene, modifierTypes.HEALING_CHARM));
|
||||||
|
doEventReward(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
chosenPokemon.loseHeldItem(modifier, false);
|
chosenPokemon.loseHeldItem(modifier, false);
|
||||||
|
|
|
@ -26,9 +26,10 @@ import { getEncounterPokemonLevelForWave, getSpriteKeysFromPokemon, STANDARD_ENC
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { randSeedInt } from "#app/utils";
|
import { randSeedInt, randSeedItem } from "#app/utils";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import PokemonSpecies, { allSpecies } from "#app/data/pokemon-species";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/fightOrFlight";
|
const namespace = "mysteryEncounters/fightOrFlight";
|
||||||
|
@ -56,7 +57,14 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
||||||
|
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
const level = getEncounterPokemonLevelForWave(scene, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER);
|
||||||
const bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
let bossSpecies: PokemonSpecies;
|
||||||
|
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||||
|
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||||
|
bossSpecies = allSpecies[eventEncounter.species];
|
||||||
|
bossSpecies.speciesId = bossSpecies.getSpeciesForLevel(level, eventEncounter.allowEvolution);
|
||||||
|
} else {
|
||||||
|
bossSpecies = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||||
|
}
|
||||||
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
const bossPokemon = new EnemyPokemon(scene, bossSpecies, level, TrainerSlot.NONE, true);
|
||||||
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
|
encounter.setDialogueToken("enemyPokemon", bossPokemon.getNameToRender());
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { catchPokemon, getHighestLevelPlayerPokemon, getSpriteKeysFromPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { isNullOrUndefined, randSeedInt } from "#app/utils";
|
import { isNullOrUndefined, randSeedInt, randSeedItem } from "#app/utils";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { SelfStatusMove } from "#app/data/move";
|
import { SelfStatusMove } from "#app/data/move";
|
||||||
|
@ -23,6 +23,7 @@ import { BerryModifier } from "#app/modifier/modifier";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import PokemonSpecies, { allSpecies } from "#app/data/pokemon-species";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/uncommonBreed";
|
const namespace = "mysteryEncounters/uncommonBreed";
|
||||||
|
@ -51,7 +52,14 @@ export const UncommonBreedEncounter: MysteryEncounter =
|
||||||
// Calculate boss mon
|
// Calculate boss mon
|
||||||
// Level equal to 2 below highest party member
|
// Level equal to 2 below highest party member
|
||||||
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
|
const level = getHighestLevelPlayerPokemon(scene, false, true).level - 2;
|
||||||
const species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
let species: PokemonSpecies;
|
||||||
|
if (scene.eventManager.isEventActive() && scene.eventManager.activeEvent()?.uncommonBreedEncounters && randSeedInt(2) === 1) {
|
||||||
|
const eventEncounter = randSeedItem(scene.eventManager.activeEvent()!.uncommonBreedEncounters!);
|
||||||
|
species = allSpecies[eventEncounter.species];
|
||||||
|
species.speciesId = species.getSpeciesForLevel(level, eventEncounter.allowEvolution);
|
||||||
|
} else {
|
||||||
|
species = scene.arena.randomSpecies(scene.currentBattle.waveIndex, level, 0, getPartyLuckValue(scene.getPlayerParty()), true);
|
||||||
|
}
|
||||||
const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true);
|
const pokemon = new EnemyPokemon(scene, species, level, TrainerSlot.NONE, true);
|
||||||
|
|
||||||
// Pokemon will always have one of its egg moves in its moveset
|
// Pokemon will always have one of its egg moves in its moveset
|
||||||
|
|
|
@ -177,7 +177,7 @@ export const allMysteryEncounters: { [encounterType: number]: MysteryEncounter }
|
||||||
const extremeBiomeEncounters: MysteryEncounterType[] = [];
|
const extremeBiomeEncounters: MysteryEncounterType[] = [];
|
||||||
|
|
||||||
const nonExtremeBiomeEncounters: MysteryEncounterType[] = [
|
const nonExtremeBiomeEncounters: MysteryEncounterType[] = [
|
||||||
MysteryEncounterType.FIELD_TRIP,
|
// MysteryEncounterType.FIELD_TRIP, Disabled for holiday event
|
||||||
MysteryEncounterType.DANCING_LESSONS, // Is also in BADLANDS, DESERT, VOLCANO, WASTELAND, ABYSS
|
MysteryEncounterType.DANCING_LESSONS, // Is also in BADLANDS, DESERT, VOLCANO, WASTELAND, ABYSS
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -185,14 +185,14 @@ const humanTransitableBiomeEncounters: MysteryEncounterType[] = [
|
||||||
MysteryEncounterType.MYSTERIOUS_CHALLENGERS,
|
MysteryEncounterType.MYSTERIOUS_CHALLENGERS,
|
||||||
MysteryEncounterType.SHADY_VITAMIN_DEALER,
|
MysteryEncounterType.SHADY_VITAMIN_DEALER,
|
||||||
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
||||||
MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE,
|
// MysteryEncounterType.AN_OFFER_YOU_CANT_REFUSE, Disabled for holiday event
|
||||||
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
||||||
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
MysteryEncounterType.THE_EXPERT_POKEMON_BREEDER
|
||||||
];
|
];
|
||||||
|
|
||||||
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
const civilizationBiomeEncounters: MysteryEncounterType[] = [
|
||||||
MysteryEncounterType.DEPARTMENT_STORE_SALE,
|
// MysteryEncounterType.DEPARTMENT_STORE_SALE, Disabled for holiday event
|
||||||
MysteryEncounterType.PART_TIMER,
|
// MysteryEncounterType.PART_TIMER, Disabled for holiday event
|
||||||
MysteryEncounterType.FUN_AND_GAMES,
|
MysteryEncounterType.FUN_AND_GAMES,
|
||||||
MysteryEncounterType.GLOBAL_TRADE_SYSTEM
|
MysteryEncounterType.GLOBAL_TRADE_SYSTEM
|
||||||
];
|
];
|
||||||
|
|
|
@ -1170,6 +1170,9 @@ function getGymLeaderPartyTemplate(scene: BattleScene) {
|
||||||
export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
export function getRandomPartyMemberFunc(speciesPool: Species[], trainerSlot: TrainerSlot = TrainerSlot.TRAINER, ignoreEvolution: boolean = false, postProcess?: (enemyPokemon: EnemyPokemon) => void) {
|
||||||
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
return (scene: BattleScene, level: number, strength: PartyMemberStrength) => {
|
||||||
let species = Utils.randSeedItem(speciesPool);
|
let species = Utils.randSeedItem(speciesPool);
|
||||||
|
if (scene.gameMode.isClassic && scene.currentBattle.waveIndex === 20) {
|
||||||
|
ignoreEvolution = true;
|
||||||
|
}
|
||||||
if (!ignoreEvolution) {
|
if (!ignoreEvolution) {
|
||||||
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
species = getPokemonSpecies(species).getTrainerSpeciesForLevel(level, true, strength, scene.currentBattle.waveIndex);
|
||||||
}
|
}
|
||||||
|
@ -1229,7 +1232,7 @@ export const signatureSpecies: SignatureSpecies = {
|
||||||
GIOVANNI: [ Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F ],
|
GIOVANNI: [ Species.SANDILE, Species.MURKROW, Species.NIDORAN_M, Species.NIDORAN_F ],
|
||||||
FALKNER: [ Species.PIDGEY, Species.HOOTHOOT, Species.DODUO ],
|
FALKNER: [ Species.PIDGEY, Species.HOOTHOOT, Species.DODUO ],
|
||||||
BUGSY: [ Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR ],
|
BUGSY: [ Species.SCYTHER, Species.HERACROSS, Species.SHUCKLE, Species.PINSIR ],
|
||||||
WHITNEY: [ Species.GIRAFARIG, Species.MILTANK ],
|
WHITNEY: [ Species.JIGGLYPUFF, Species.MILTANK, Species.AIPOM, Species.GIRAFARIG ],
|
||||||
MORTY: [ Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE ],
|
MORTY: [ Species.GASTLY, Species.MISDREAVUS, Species.SABLEYE ],
|
||||||
CHUCK: [ Species.POLIWRATH, Species.MANKEY ],
|
CHUCK: [ Species.POLIWRATH, Species.MANKEY ],
|
||||||
JASMINE: [ Species.MAGNEMITE, Species.STEELIX ],
|
JASMINE: [ Species.MAGNEMITE, Species.STEELIX ],
|
||||||
|
@ -1239,7 +1242,7 @@ export const signatureSpecies: SignatureSpecies = {
|
||||||
BRAWLY: [ Species.MACHOP, Species.MAKUHITA ],
|
BRAWLY: [ Species.MACHOP, Species.MAKUHITA ],
|
||||||
WATTSON: [ Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE ],
|
WATTSON: [ Species.MAGNEMITE, Species.VOLTORB, Species.ELECTRIKE ],
|
||||||
FLANNERY: [ Species.SLUGMA, Species.TORKOAL, Species.NUMEL ],
|
FLANNERY: [ Species.SLUGMA, Species.TORKOAL, Species.NUMEL ],
|
||||||
NORMAN: [ Species.SLAKOTH, Species.SPINDA, Species.CHANSEY, Species.KANGASKHAN ],
|
NORMAN: [ Species.SLAKOTH, Species.SPINDA, Species.ZIGZAGOON, Species.KECLEON ],
|
||||||
WINONA: [ Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY ],
|
WINONA: [ Species.SWABLU, Species.WINGULL, Species.TROPIUS, Species.SKARMORY ],
|
||||||
TATE: [ Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE ],
|
TATE: [ Species.SOLROCK, Species.NATU, Species.CHIMECHO, Species.GALLADE ],
|
||||||
LIZA: [ Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR ],
|
LIZA: [ Species.LUNATONE, Species.SPOINK, Species.BALTOY, Species.GARDEVOIR ],
|
||||||
|
@ -1247,16 +1250,16 @@ export const signatureSpecies: SignatureSpecies = {
|
||||||
ROARK: [ Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE ],
|
ROARK: [ Species.CRANIDOS, Species.LARVITAR, Species.GEODUDE ],
|
||||||
GARDENIA: [ Species.ROSELIA, Species.TANGELA, Species.TURTWIG ],
|
GARDENIA: [ Species.ROSELIA, Species.TANGELA, Species.TURTWIG ],
|
||||||
MAYLENE: [ Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR ],
|
MAYLENE: [ Species.LUCARIO, Species.MEDITITE, Species.CHIMCHAR ],
|
||||||
CRASHER_WAKE: [ Species.BUIZEL, Species.MAGIKARP, Species.PIPLUP ],
|
CRASHER_WAKE: [ Species.BUIZEL, Species.WOOPER, Species.PIPLUP, Species.MAGIKARP ],
|
||||||
FANTINA: [ Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB ],
|
FANTINA: [ Species.MISDREAVUS, Species.DRIFLOON, Species.SPIRITOMB ],
|
||||||
BYRON: [ Species.SHIELDON, Species.BRONZOR, Species.AGGRON ],
|
BYRON: [ Species.SHIELDON, Species.BRONZOR, Species.AGGRON ],
|
||||||
CANDICE: [ Species.SNEASEL, Species.SNOVER, Species.SNORUNT ],
|
CANDICE: [ Species.SNEASEL, Species.SNOVER, Species.SNORUNT ],
|
||||||
VOLKNER: [ Species.SHINX, Species.CHINCHOU, Species.ROTOM ],
|
VOLKNER: [ Species.SHINX, Species.CHINCHOU, Species.ROTOM ],
|
||||||
CILAN: [ Species.PANSAGE, Species.COTTONEE, Species.PETILIL ],
|
CILAN: [ Species.PANSAGE, Species.FOONGUS, Species.PETILIL ],
|
||||||
CHILI: [ Species.PANSEAR, Species.DARUMAKA, Species.HEATMOR ],
|
CHILI: [ Species.PANSEAR, Species.DARUMAKA, Species.NUMEL ],
|
||||||
CRESS: [ Species.PANPOUR, Species.BASCULIN, Species.TYMPOLE ],
|
CRESS: [ Species.PANPOUR, Species.TYMPOLE, Species.SLOWPOKE ],
|
||||||
CHEREN: [ Species.LILLIPUP, Species.MINCCINO, Species.PATRAT ],
|
CHEREN: [ Species.LILLIPUP, Species.MINCCINO, Species.PIDOVE ],
|
||||||
LENORA: [ Species.KANGASKHAN, Species.DEERLING, Species.AUDINO ],
|
LENORA: [ Species.PATRAT, Species.DEERLING, Species.AUDINO ],
|
||||||
ROXIE: [ Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI ],
|
ROXIE: [ Species.VENIPEDE, Species.TRUBBISH, Species.SKORUPI ],
|
||||||
BURGH: [ Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST ],
|
BURGH: [ Species.SEWADDLE, Species.SHELMET, Species.KARRABLAST ],
|
||||||
ELESA: [ Species.EMOLGA, Species.BLITZLE, Species.JOLTIK ],
|
ELESA: [ Species.EMOLGA, Species.BLITZLE, Species.JOLTIK ],
|
||||||
|
@ -1289,7 +1292,7 @@ export const signatureSpecies: SignatureSpecies = {
|
||||||
BRASSIUS: [ Species.SMOLIV, Species.SHROOMISH, Species.ODDISH ],
|
BRASSIUS: [ Species.SMOLIV, Species.SHROOMISH, Species.ODDISH ],
|
||||||
IONO: [ Species.TADBULB, Species.WATTREL, Species.VOLTORB ],
|
IONO: [ Species.TADBULB, Species.WATTREL, Species.VOLTORB ],
|
||||||
KOFU: [ Species.VELUZA, Species.WIGLETT, Species.WINGULL ],
|
KOFU: [ Species.VELUZA, Species.WIGLETT, Species.WINGULL ],
|
||||||
LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.KOMALA ],
|
LARRY: [ Species.STARLY, Species.DUNSPARCE, Species.LECHONK, Species.KOMALA ],
|
||||||
RYME: [ Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU ],
|
RYME: [ Species.GREAVARD, Species.SHUPPET, Species.MIMIKYU ],
|
||||||
TULIP: [ Species.GIRAFARIG, Species.FLITTLE, Species.RALTS ],
|
TULIP: [ Species.GIRAFARIG, Species.FLITTLE, Species.RALTS ],
|
||||||
GRUSHA: [ Species.CETODDLE, Species.ALOLA_VULPIX, Species.CUBCHOO ],
|
GRUSHA: [ Species.CETODDLE, Species.ALOLA_VULPIX, Species.CUBCHOO ],
|
||||||
|
@ -1852,7 +1855,7 @@ export const trainerConfigs: TrainerConfigs = {
|
||||||
|
|
||||||
[TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL)
|
[TrainerType.RIVAL]: new TrainerConfig((t = TrainerType.RIVAL)).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setStaticParty().setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival").setMixedBattleBgm("battle_rival").setPartyTemplates(trainerPartyTemplates.RIVAL)
|
||||||
.setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE)
|
.setModifierRewardFuncs(() => modifierTypes.SUPER_EXP_CHARM, () => modifierTypes.EXP_SHARE)
|
||||||
.setEventModifierRewardFuncs(() => modifierTypes.SHINY_CHARM, () => modifierTypes.ABILITY_CHARM)
|
.setEventModifierRewardFuncs(() => modifierTypes.SHINY_CHARM, () => modifierTypes.ABILITY_CHARM, () => modifierTypes.CATCHING_CHARM)
|
||||||
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.CHIKORITA, Species.CYNDAQUIL, Species.TOTODILE, Species.TREECKO, Species.TORCHIC, Species.MUDKIP, Species.TURTWIG, Species.CHIMCHAR, Species.PIPLUP, Species.SNIVY, Species.TEPIG, Species.OSHAWOTT, Species.CHESPIN, Species.FENNEKIN, Species.FROAKIE, Species.ROWLET, Species.LITTEN, Species.POPPLIO, Species.GROOKEY, Species.SCORBUNNY, Species.SOBBLE, Species.SPRIGATITO, Species.FUECOCO, Species.QUAXLY ], TrainerSlot.TRAINER, true,
|
.setPartyMemberFunc(0, getRandomPartyMemberFunc([ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.CHIKORITA, Species.CYNDAQUIL, Species.TOTODILE, Species.TREECKO, Species.TORCHIC, Species.MUDKIP, Species.TURTWIG, Species.CHIMCHAR, Species.PIPLUP, Species.SNIVY, Species.TEPIG, Species.OSHAWOTT, Species.CHESPIN, Species.FENNEKIN, Species.FROAKIE, Species.ROWLET, Species.LITTEN, Species.POPPLIO, Species.GROOKEY, Species.SCORBUNNY, Species.SOBBLE, Species.SPRIGATITO, Species.FUECOCO, Species.QUAXLY ], TrainerSlot.TRAINER, true,
|
||||||
(p => p.abilityIndex = 0)))
|
(p => p.abilityIndex = 0)))
|
||||||
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEY, Species.HOOTHOOT, Species.TAILLOW, Species.STARLY, Species.PIDOVE, Species.FLETCHLING, Species.PIKIPEK, Species.ROOKIDEE, Species.WATTREL ], TrainerSlot.TRAINER, true)),
|
.setPartyMemberFunc(1, getRandomPartyMemberFunc([ Species.PIDGEY, Species.HOOTHOOT, Species.TAILLOW, Species.STARLY, Species.PIDOVE, Species.FLETCHLING, Species.PIKIPEK, Species.ROOKIDEE, Species.WATTREL ], TrainerSlot.TRAINER, true)),
|
||||||
|
|
|
@ -242,7 +242,7 @@ export function getTerrainBlockMessage(pokemon: Pokemon, terrainType: TerrainTyp
|
||||||
return i18next.t("terrain:defaultBlockMessage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), terrainName: getTerrainName(terrainType) });
|
return i18next.t("terrain:defaultBlockMessage", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), terrainName: getTerrainName(terrainType) });
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WeatherPoolEntry {
|
export interface WeatherPoolEntry {
|
||||||
weatherType: WeatherType;
|
weatherType: WeatherType;
|
||||||
weight: integer;
|
weight: integer;
|
||||||
}
|
}
|
||||||
|
@ -373,6 +373,10 @@ export function getRandomWeatherType(arena: any /* Importing from arena causes a
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arena.biomeType === Biome.TOWN && arena.scene.eventManager.isEventActive() && arena.scene.eventManager.activeEvent()?.weather?.length > 0) {
|
||||||
|
arena.scene.eventManager.activeEvent().weather.map(w => weatherPool.push(w));
|
||||||
|
}
|
||||||
|
|
||||||
if (weatherPool.length > 1) {
|
if (weatherPool.length > 1) {
|
||||||
let totalWeight = 0;
|
let totalWeight = 0;
|
||||||
weatherPool.forEach(w => totalWeight += w.weight);
|
weatherPool.forEach(w => totalWeight += w.weight);
|
||||||
|
|
|
@ -3286,7 +3286,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getMoveQueue(): QueuedMove[] {
|
getMoveQueue(): TurnMove[] {
|
||||||
return this.summonData.moveQueue;
|
return this.summonData.moveQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4801,17 +4801,19 @@ export class EnemyPokemon extends Pokemon {
|
||||||
* the Pokemon the move will target.
|
* the Pokemon the move will target.
|
||||||
* @returns this Pokemon's next move in the format {move, moveTargets}
|
* @returns this Pokemon's next move in the format {move, moveTargets}
|
||||||
*/
|
*/
|
||||||
getNextMove(): QueuedMove {
|
getNextMove(): TurnMove {
|
||||||
// If this Pokemon has a move already queued, return it.
|
// If this Pokemon has a move already queued, return it.
|
||||||
const queuedMove = this.getMoveQueue().length
|
const moveQueue = this.getMoveQueue();
|
||||||
? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move)
|
if (moveQueue.length !== 0) {
|
||||||
: null;
|
const queuedMove = moveQueue[0];
|
||||||
if (queuedMove) {
|
if (queuedMove) {
|
||||||
if (queuedMove.isUsable(this, this.getMoveQueue()[0].ignorePP)) {
|
const moveIndex = this.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move);
|
||||||
return { move: queuedMove.moveId, targets: this.getMoveQueue()[0].targets, ignorePP: this.getMoveQueue()[0].ignorePP };
|
if ((moveIndex > -1 && this.getMoveset()[moveIndex]!.isUsable(this, queuedMove.ignorePP)) || queuedMove.virtual) {
|
||||||
} else {
|
return queuedMove;
|
||||||
this.getMoveQueue().shift();
|
} else {
|
||||||
return this.getNextMove();
|
this.getMoveQueue().shift();
|
||||||
|
return this.getNextMove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5233,15 +5235,10 @@ export class EnemyPokemon extends Pokemon {
|
||||||
|
|
||||||
export interface TurnMove {
|
export interface TurnMove {
|
||||||
move: Moves;
|
move: Moves;
|
||||||
targets?: BattlerIndex[];
|
targets: BattlerIndex[];
|
||||||
result: MoveResult;
|
result?: MoveResult;
|
||||||
virtual?: boolean;
|
virtual?: boolean;
|
||||||
turn?: number;
|
turn?: number;
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueuedMove {
|
|
||||||
move: Moves;
|
|
||||||
targets: BattlerIndex[];
|
|
||||||
ignorePP?: boolean;
|
ignorePP?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5257,7 +5254,7 @@ export interface AttackMoveResult {
|
||||||
export class PokemonSummonData {
|
export class PokemonSummonData {
|
||||||
/** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */
|
/** [Atk, Def, SpAtk, SpDef, Spd, Acc, Eva] */
|
||||||
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
public statStages: number[] = [ 0, 0, 0, 0, 0, 0, 0 ];
|
||||||
public moveQueue: QueuedMove[] = [];
|
public moveQueue: TurnMove[] = [];
|
||||||
public tags: BattlerTag[] = [];
|
public tags: BattlerTag[] = [];
|
||||||
public abilitySuppressed: boolean = false;
|
public abilitySuppressed: boolean = false;
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
|
|
|
@ -246,9 +246,9 @@ export class LoadingScene extends SceneBase {
|
||||||
}
|
}
|
||||||
const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ];
|
const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ];
|
||||||
if (lang && availableLangs.includes(lang)) {
|
if (lang && availableLangs.includes(lang)) {
|
||||||
this.loadImage("halloween2024-event-" + lang, "events");
|
this.loadImage("winter_holidays2024-event-" + lang, "events");
|
||||||
} else {
|
} else {
|
||||||
this.loadImage("halloween2024-event-en", "events");
|
this.loadImage("winter_holidays2024-event-en", "events");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadAtlas("statuses", "");
|
this.loadAtlas("statuses", "");
|
||||||
|
|
|
@ -1093,7 +1093,10 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
|
||||||
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Moves)) {
|
if (pregenArgs && (pregenArgs.length === 1) && (pregenArgs[0] in Moves)) {
|
||||||
return new TmModifierType(pregenArgs[0] as Moves);
|
return new TmModifierType(pregenArgs[0] as Moves);
|
||||||
}
|
}
|
||||||
const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm)));
|
const partyMemberCompatibleTms = party.map(p => {
|
||||||
|
const previousLevelMoves = p.getLearnableLevelMoves();
|
||||||
|
return (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm) && !previousLevelMoves.find(lm=>lm === tm));
|
||||||
|
});
|
||||||
const tierUniqueCompatibleTms = partyMemberCompatibleTms.flat().filter(tm => tmPoolTiers[tm] === tier).filter(tm => !allMoves[tm].name.endsWith(" (N)")).filter((tm, i, array) => array.indexOf(tm) === i);
|
const tierUniqueCompatibleTms = partyMemberCompatibleTms.flat().filter(tm => tmPoolTiers[tm] === tier).filter(tm => !allMoves[tm].name.endsWith(" (N)")).filter((tm, i, array) => array.indexOf(tm) === i);
|
||||||
if (!tierUniqueCompatibleTms.length) {
|
if (!tierUniqueCompatibleTms.length) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -8,7 +8,7 @@ import { BattlerTagType } from "#app/enums/battler-tag-type";
|
||||||
import { Biome } from "#app/enums/biome";
|
import { Biome } from "#app/enums/biome";
|
||||||
import { Moves } from "#app/enums/moves";
|
import { Moves } from "#app/enums/moves";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { FieldPosition, PlayerPokemon } from "#app/field/pokemon";
|
import { FieldPosition, PlayerPokemon, TurnMove } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { Command } from "#app/ui/command-ui-handler";
|
import { Command } from "#app/ui/command-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
|
@ -76,19 +76,19 @@ export class CommandPhase extends FieldPhase {
|
||||||
const moveQueue = playerPokemon.getMoveQueue();
|
const moveQueue = playerPokemon.getMoveQueue();
|
||||||
|
|
||||||
while (moveQueue.length && moveQueue[0]
|
while (moveQueue.length && moveQueue[0]
|
||||||
&& moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
&& moveQueue[0].move && !moveQueue[0].virtual && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move)
|
||||||
|| !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
|
|| !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct?
|
||||||
moveQueue.shift();
|
moveQueue.shift();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (moveQueue.length) {
|
if (moveQueue.length !== 0) {
|
||||||
const queuedMove = moveQueue[0];
|
const queuedMove = moveQueue[0];
|
||||||
if (!queuedMove.move) {
|
if (!queuedMove.move) {
|
||||||
this.handleCommand(Command.FIGHT, -1, false);
|
this.handleCommand(Command.FIGHT, -1, undefined);
|
||||||
} else {
|
} else {
|
||||||
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move);
|
||||||
if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct?
|
if ((moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) || queuedMove.virtual) { // TODO: is the bang correct?
|
||||||
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 });
|
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, queuedMove);
|
||||||
} else {
|
} else {
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}
|
}
|
||||||
|
@ -110,12 +110,24 @@ export class CommandPhase extends FieldPhase {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case Command.FIGHT:
|
case Command.FIGHT:
|
||||||
let useStruggle = false;
|
let useStruggle = false;
|
||||||
|
const turnMove: TurnMove | undefined = (args.length === 2 ? (args[1] as TurnMove) : undefined);
|
||||||
if (cursor === -1 ||
|
if (cursor === -1 ||
|
||||||
playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
|
playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
|
||||||
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) {
|
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) {
|
||||||
const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct?
|
|
||||||
|
let moveId: Moves;
|
||||||
|
if (useStruggle) {
|
||||||
|
moveId = Moves.STRUGGLE;
|
||||||
|
} else if (turnMove !== undefined) {
|
||||||
|
moveId = turnMove.move;
|
||||||
|
} else if (cursor > -1) {
|
||||||
|
moveId = playerPokemon.getMoveset()[cursor]!.moveId;
|
||||||
|
} else {
|
||||||
|
moveId = Moves.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args };
|
||||||
const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2];
|
const moveTargets: MoveTargetSet = turnMove === undefined ? getMoveTargets(playerPokemon, moveId) : { targets: turnMove.targets, multiple: turnMove.targets.length > 1 };
|
||||||
if (!moveId) {
|
if (!moveId) {
|
||||||
turnCommand.targets = [ this.fieldIndex ];
|
turnCommand.targets = [ this.fieldIndex ];
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,11 +294,6 @@ export class MovePhase extends BattlePhase {
|
||||||
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
|
||||||
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
|
||||||
this.scene.currentBattle.lastMove = this.move.moveId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the move is successful (meaning that its damage/effects can be attempted)
|
* Determine if the move is successful (meaning that its damage/effects can be attempted)
|
||||||
* by checking that all of the following are true:
|
* by checking that all of the following are true:
|
||||||
|
@ -322,6 +317,14 @@ export class MovePhase extends BattlePhase {
|
||||||
|
|
||||||
const success = passesConditions && !failedDueToWeather && !failedDueToTerrain;
|
const success = passesConditions && !failedDueToWeather && !failedDueToTerrain;
|
||||||
|
|
||||||
|
// Update the battle's "last move" pointer, unless we're currently mimicking a move.
|
||||||
|
if (!allMoves[this.move.moveId].hasAttr(CopyMoveAttr)) {
|
||||||
|
// The last move used is unaffected by moves that fail
|
||||||
|
if (success) {
|
||||||
|
this.scene.currentBattle.lastMove = this.move.moveId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the move has not failed, trigger ability-based user type changes and then execute it.
|
* If the move has not failed, trigger ability-based user type changes and then execute it.
|
||||||
*
|
*
|
||||||
|
@ -516,7 +519,7 @@ export class MovePhase extends BattlePhase {
|
||||||
frenzyMissFunc(this.pokemon, this.move.getMove());
|
frenzyMissFunc(this.pokemon, this.move.getMove());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL });
|
this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL, targets: this.targets });
|
||||||
|
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT);
|
||||||
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
this.pokemon.lapseTags(BattlerTagLapseType.AFTER_MOVE);
|
||||||
|
|
|
@ -39,7 +39,7 @@ export class TrainerVictoryPhase extends BattlePhase {
|
||||||
// Validate Voucher for boss trainers
|
// Validate Voucher for boss trainers
|
||||||
if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
|
if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
|
||||||
if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) {
|
if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) {
|
||||||
this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [ modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM ][vouchers[TrainerType[trainerType]].voucherType]));
|
this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [ modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM ][vouchers[TrainerType[trainerType]].voucherType]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Breeders in Space achievement
|
// Breeders in Space achievement
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Assist", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.ASSIST, Moves.SKETCH, Moves.FLY, Moves.DRAGON_TAIL ]) // These are all moves Assist cannot call; Sketch will be used to test that it can call other moves properly
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to use an ally's moves", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyMoveset(Moves.SWORDS_DANCE);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.SHUCKLE ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
game.move.select(Moves.SKETCH, 1);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER ]);
|
||||||
|
// Player_2 uses Sketch, copies Swords Dance, Player_1 uses Assist, uses Player_2's Sketched Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(2); // Stat raised from Assist -> Swords Dance
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if there are no usable moves", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
// Moves above are already unusable by Assist
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.moveset([ Moves.ASSIST, Moves.WOOD_HAMMER, Moves.WOOD_HAMMER, Moves.WOOD_HAMMER ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.ASSIST, 0);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // should receive recoil damage from Wood Hammer
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,88 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Copycat", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.COPYCAT, Moves.SPIKY_SHIELD, Moves.SWORDS_DANCE, Moves.SPLASH ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.starterSpecies(Species.FEEBAS)
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy the last move successfully executed", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.SUCKER_PUNCH);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SWORDS_DANCE);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT); // Last successful move should be Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK)).toBe(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when the last move used is not a valid Copycat move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.PROTECT); // Protect is not a valid move for Copycat to copy
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SPIKY_SHIELD); // Spiky Shield is not a valid move for Copycat to copy
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should copy the called move when the last move successfully calls another", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SPLASH, Moves.METRONOME ])
|
||||||
|
.enemyMoveset(Moves.COPYCAT);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
vi.spyOn(game.scene.getPlayerPokemon()!, "randSeedInt").mockReturnValue(Moves.SWORDS_DANCE);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); // Player moves first, so enemy can copy Swords Dance
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.ACID_SPRAY); // Secondary effect lowers SpDef by 2 stages
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.COPYCAT);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { RechargingTag, SemiInvulnerableTag } from "#app/data/battler-tags";
|
||||||
|
import { Abilities } from "#app/enums/abilities";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Metronome", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.METRONOME, Moves.SPLASH ])
|
||||||
|
.battleType("single")
|
||||||
|
.startingLevel(100)
|
||||||
|
.starterSpecies(Species.REGIELEKI)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemySpecies(Species.SHUCKLE)
|
||||||
|
.enemyMoveset(Moves.SPLASH)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should have one semi-invulnerable turn and deal damage on the second turn when a semi-invulnerable move is called", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(player, "randSeedInt").mockReturnValue(Moves.DIVE);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.getTag(SemiInvulnerableTag)).toBeTruthy();
|
||||||
|
await game.move.forceHit(); // Force hit on Dive, required due to randSeedInt mock making hitCheck return false every time.
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(player.getTag(SemiInvulnerableTag)).toBeFalsy();
|
||||||
|
expect(enemy.isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(player, "randSeedInt").mockReturnValue(Moves.WOOD_HAMMER);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase"); // Metronome has its own MoveEffectPhase, followed by Wood Hammer's MoveEffectPhase
|
||||||
|
await game.move.forceHit(); // Calls forceHit on Wood Hammer's MoveEffectPhase, required due to randSeedInt mock making hitCheck return false every time.
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should recharge after using recharge move", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(player, "randSeedInt").mockReturnValue(Moves.HYPER_BEAM);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(player.getTag(RechargingTag)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should only target ally for Aromatic Mist", async () => {
|
||||||
|
game.override.battleType("double");
|
||||||
|
await game.classicMode.startBattle([ Species.REGIELEKI, Species.RATTATA ]);
|
||||||
|
const [ leftPlayer, rightPlayer ] = game.scene.getPlayerField();
|
||||||
|
const [ leftOpp, rightOpp ] = game.scene.getEnemyField();
|
||||||
|
vi.spyOn(leftPlayer, "randSeedInt").mockReturnValue(Moves.AROMATIC_MIST);
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(rightPlayer.getStatStage(Stat.SPDEF)).toBe(1);
|
||||||
|
expect(leftPlayer.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
expect(leftOpp.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
expect(rightOpp.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should cause opponent to flee, and not crash for Roar", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
vi.spyOn(player, "randSeedInt").mockReturnValue(Moves.ROAR);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.METRONOME);
|
||||||
|
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||||
|
await game.move.forceHit();
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
const isVisible = enemyPokemon.visible;
|
||||||
|
const hasFled = enemyPokemon.switchOutStatus;
|
||||||
|
expect(!isVisible && hasFled).toBe(true);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BattleEndPhase");
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Mirror Move", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.MIRROR_MOVE, Moves.SPLASH ])
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should use the last move that the target", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyMoveset([ Moves.TACKLE, Moves.GROWL ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE, 0, BattlerIndex.ENEMY); // target's last move is Tackle, enemy should receive damage from Mirror Move copying Tackle
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
await game.forceEnemyMove(Moves.TACKLE, BattlerIndex.PLAYER_2);
|
||||||
|
await game.forceEnemyMove(Moves.GROWL, BattlerIndex.PLAYER_2);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyField()[0].isFullHp()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.ACID_SPRAY);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to copy status moves", async () => {
|
||||||
|
game.override.enemyMoveset(Moves.GROWL);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getEnemyPokemon()!.getStatStage(Stat.ATK)).toBe(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if the target has not used any moves", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.MIRROR_MOVE);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { Stat } from "#app/enums/stat";
|
||||||
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
|
import { MoveResult } from "#app/field/pokemon";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
describe("Moves - Sleep Talk", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SPLASH, Moves.SLEEP_TALK ])
|
||||||
|
.statusEffect(StatusEffect.SLEEP)
|
||||||
|
.ability(Abilities.BALL_FETCH)
|
||||||
|
.battleType("single")
|
||||||
|
.disableCrits()
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.BALL_FETCH)
|
||||||
|
.enemyMoveset(Moves.SPLASH)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail when the user is not asleep", async () => {
|
||||||
|
game.override.statusEffect(StatusEffect.NONE);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail if the user has no valid moves", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.METRONOME, Moves.SOLAR_BEAM, Moves.SLEEP_TALK ]);
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should call a random valid move if the user is asleep", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.FLY, Moves.SWORDS_DANCE ]); // Dig and Fly are invalid moves, Swords Dance should always be called
|
||||||
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getStatStage(Stat.ATK));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should apply secondary effects of a move", async () => {
|
||||||
|
game.override.moveset([ Moves.SLEEP_TALK, Moves.DIG, Moves.FLY, Moves.WOOD_HAMMER ]); // Dig and Fly are invalid moves, Wood Hammer should always be called
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
game.move.select(Moves.SLEEP_TALK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.isFullHp()).toBeFalsy(); // Wood Hammer recoil effect should be applied
|
||||||
|
});
|
||||||
|
});
|
|
@ -124,7 +124,7 @@ describe("Moves - Spit Up", () => {
|
||||||
game.move.select(Moves.SPIT_UP);
|
game.move.select(Moves.SPIT_UP);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.FAIL });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.FAIL, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).not.toHaveBeenCalled();
|
expect(spitUp.calculateBattlePower).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
@ -147,7 +147,7 @@ describe("Moves - Spit Up", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ describe("Moves - Spit Up", () => {
|
||||||
game.move.select(Moves.SPIT_UP);
|
game.move.select(Moves.SPIT_UP);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SPIT_UP, result: MoveResult.SUCCESS, targets: [ game.scene.getEnemyPokemon()!.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
expect(spitUp.calculateBattlePower).toHaveBeenCalledOnce();
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ describe("Moves - Stockpile", () => {
|
||||||
expect(user.getStatStage(Stat.SPDEF)).toBe(3);
|
expect(user.getStatStage(Stat.SPDEF)).toBe(3);
|
||||||
expect(stockpilingTag).toBeDefined();
|
expect(stockpilingTag).toBeDefined();
|
||||||
expect(stockpilingTag.stockpiledCount).toBe(3);
|
expect(stockpilingTag.stockpiledCount).toBe(3);
|
||||||
expect(user.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ result: MoveResult.FAIL, move: Moves.STOCKPILE });
|
expect(user.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ result: MoveResult.FAIL, move: Moves.STOCKPILE, targets: [ user.getBattlerIndex() ]});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -134,7 +134,7 @@ describe("Moves - Swallow", () => {
|
||||||
game.move.select(Moves.SWALLOW);
|
game.move.select(Moves.SWALLOW);
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.FAIL });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.FAIL, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("restores stat stage boosts granted by stacks", () => {
|
describe("restores stat stage boosts granted by stacks", () => {
|
||||||
|
@ -155,7 +155,7 @@ describe("Moves - Swallow", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
expect(pokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0);
|
||||||
|
@ -182,7 +182,7 @@ describe("Moves - Swallow", () => {
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
|
||||||
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS });
|
expect(pokemon.getMoveHistory().at(-1)).toMatchObject<TurnMove>({ move: Moves.SWALLOW, result: MoveResult.SUCCESS, targets: [ pokemon.getBattlerIndex() ]});
|
||||||
|
|
||||||
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
expect(pokemon.getStatStage(Stat.DEF)).toBe(1);
|
||||||
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2);
|
||||||
|
|
|
@ -56,7 +56,7 @@ describe("Delibird-y - Mystery Encounter", () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.DELIBIRDY, defaultParty);
|
||||||
|
|
||||||
expect(DelibirdyEncounter.encounterType).toBe(MysteryEncounterType.DELIBIRDY);
|
expect(DelibirdyEncounter.encounterType).toBe(MysteryEncounterType.DELIBIRDY);
|
||||||
expect(DelibirdyEncounter.encounterTier).toBe(MysteryEncounterTier.GREAT);
|
expect(DelibirdyEncounter.encounterTier).toBe(MysteryEncounterTier.COMMON);
|
||||||
expect(DelibirdyEncounter.dialogue).toBeDefined();
|
expect(DelibirdyEncounter.dialogue).toBeDefined();
|
||||||
expect(DelibirdyEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
expect(DelibirdyEncounter.dialogue.intro).toStrictEqual([{ text: `${namespace}:intro` }]);
|
||||||
expect(DelibirdyEncounter.dialogue.outro).toStrictEqual([{ text: `${namespace}:outro` }]);
|
expect(DelibirdyEncounter.dialogue.outro).toStrictEqual([{ text: `${namespace}:outro` }]);
|
||||||
|
|
|
@ -2,6 +2,9 @@ import BattleScene from "#app/battle-scene";
|
||||||
import { TextStyle, addTextObject } from "#app/ui/text";
|
import { TextStyle, addTextObject } from "#app/ui/text";
|
||||||
import { nil } from "#app/utils";
|
import { nil } from "#app/utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { WeatherPoolEntry } from "#app/data/weather";
|
||||||
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
SHINY,
|
SHINY,
|
||||||
|
@ -16,6 +19,11 @@ interface EventBanner {
|
||||||
availableLangs?: string[];
|
availableLangs?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EventEncounter {
|
||||||
|
species: Species;
|
||||||
|
allowEvolution?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
interface TimedEvent extends EventBanner {
|
interface TimedEvent extends EventBanner {
|
||||||
name: string;
|
name: string;
|
||||||
eventType: EventType;
|
eventType: EventType;
|
||||||
|
@ -23,19 +31,46 @@ interface TimedEvent extends EventBanner {
|
||||||
friendshipMultiplier?: number;
|
friendshipMultiplier?: number;
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
|
uncommonBreedEncounters?: EventEncounter[];
|
||||||
|
delibirdyBuff?: string[];
|
||||||
|
weather?: WeatherPoolEntry[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const timedEvents: TimedEvent[] = [
|
const timedEvents: TimedEvent[] = [
|
||||||
{
|
{
|
||||||
name: "Halloween Update",
|
name: "Winter Holiday Update",
|
||||||
eventType: EventType.SHINY,
|
eventType: EventType.SHINY,
|
||||||
shinyMultiplier: 2,
|
shinyMultiplier: 2,
|
||||||
friendshipMultiplier: 2,
|
friendshipMultiplier: 1,
|
||||||
startDate: new Date(Date.UTC(2024, 9, 27, 0)),
|
startDate: new Date(Date.UTC(2024, 11, 21, 0)),
|
||||||
endDate: new Date(Date.UTC(2024, 10, 4, 0)),
|
endDate: new Date(Date.UTC(2025, 0, 4, 0)),
|
||||||
bannerKey: "halloween2024-event-",
|
bannerKey: "winter_holidays2024-event-",
|
||||||
scale: 0.21,
|
scale: 0.21,
|
||||||
availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ]
|
availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ],
|
||||||
|
uncommonBreedEncounters: [
|
||||||
|
{ species: Species.GIMMIGHOUL },
|
||||||
|
{ species: Species.DELIBIRD },
|
||||||
|
{ species: Species.STANTLER, allowEvolution: true },
|
||||||
|
{ species: Species.CYNDAQUIL, allowEvolution: true },
|
||||||
|
{ species: Species.PIPLUP, allowEvolution: true },
|
||||||
|
{ species: Species.CHESPIN, allowEvolution: true },
|
||||||
|
{ species: Species.BALTOY, allowEvolution: true },
|
||||||
|
{ species: Species.SNOVER, allowEvolution: true },
|
||||||
|
{ species: Species.CHINGLING, allowEvolution: true },
|
||||||
|
{ species: Species.LITWICK, allowEvolution: true },
|
||||||
|
{ species: Species.CUBCHOO, allowEvolution: true },
|
||||||
|
{ species: Species.SWIRLIX, allowEvolution: true },
|
||||||
|
{ species: Species.AMAURA, allowEvolution: true },
|
||||||
|
{ species: Species.MUDBRAY, allowEvolution: true },
|
||||||
|
{ species: Species.ROLYCOLY, allowEvolution: true },
|
||||||
|
{ species: Species.MILCERY, allowEvolution: true },
|
||||||
|
{ species: Species.SMOLIV, allowEvolution: true },
|
||||||
|
{ species: Species.ALOLA_VULPIX, allowEvolution: true },
|
||||||
|
{ species: Species.GALAR_DARUMAKA, allowEvolution: true },
|
||||||
|
{ species: Species.IRON_BUNDLE }
|
||||||
|
],
|
||||||
|
delibirdyBuff: [ "CATCHING_CHARM", "SHINY_CHARM", "ABILITY_CHARM", "EXP_CHARM", "SUPER_EXP_CHARM", "HEALING_CHARM" ],
|
||||||
|
weather: [{ weatherType: WeatherType.SNOW, weight: 1 }]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|