mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2024-11-29 10:16:14 +00:00
Merge branch 'pagefaultgames:beta' into linger-arena-trap-fix
This commit is contained in:
commit
95873417bd
@ -1 +1 @@
|
|||||||
Subproject commit 3cf6d553541d79ba165387bc73fb06544d00f1f9
|
Subproject commit fc4a1effd5170def3c8314208a52cd0d8e6913ef
|
@ -13,9 +13,10 @@ import { Arena, ArenaBase } from "#app/field/arena";
|
|||||||
import { GameData } from "#app/system/game-data";
|
import { GameData } from "#app/system/game-data";
|
||||||
import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
|
import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
|
import { MusicPreference } from "#app/system/settings/settings";
|
||||||
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import AbilityBar from "#app/ui/ability-bar";
|
import AbilityBar from "#app/ui/ability-bar";
|
||||||
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr } from "#app/data/ability";
|
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability";
|
||||||
import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
|
import Battle, { BattleType, FixedBattleConfig } from "#app/battle";
|
||||||
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
||||||
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
import FieldSpritePipeline from "#app/pipelines/field-sprite";
|
||||||
@ -169,7 +170,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
public uiTheme: UiTheme = UiTheme.DEFAULT;
|
public uiTheme: UiTheme = UiTheme.DEFAULT;
|
||||||
public windowType: integer = 0;
|
public windowType: integer = 0;
|
||||||
public experimentalSprites: boolean = false;
|
public experimentalSprites: boolean = false;
|
||||||
public musicPreference: integer = 0;
|
public musicPreference: number = MusicPreference.MIXED;
|
||||||
public moveAnimations: boolean = true;
|
public moveAnimations: boolean = true;
|
||||||
public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT;
|
public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT;
|
||||||
public skipSeenDialogues: boolean = false;
|
public skipSeenDialogues: boolean = false;
|
||||||
@ -764,57 +765,65 @@ export default class BattleScene extends SceneBase {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getParty(): PlayerPokemon[] {
|
public getPlayerParty(): PlayerPokemon[] {
|
||||||
return this.party;
|
return this.party;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPlayerPokemon(): PlayerPokemon | undefined {
|
|
||||||
return this.getPlayerField().find(p => p.isActive());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the first {@linkcode Pokemon.isActive() | active PlayerPokemon} that isn't also currently switching out
|
* @returns An array of {@linkcode PlayerPokemon} filtered from the player's party
|
||||||
* @returns Either the first {@linkcode PlayerPokemon} satisfying, or undefined if no player pokemon on the field satisfy
|
* that are {@linkcode PlayerPokemon.isAllowedInBattle | allowed in battle}.
|
||||||
*/
|
*/
|
||||||
getNonSwitchedPlayerPokemon(): PlayerPokemon | undefined {
|
public getPokemonAllowedInBattle(): PlayerPokemon[] {
|
||||||
return this.getPlayerField().find(p => p.isActive() && p.switchOutStatus === false);
|
return this.getPlayerParty().filter(p => p.isAllowedInBattle());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of PlayerPokemon of length 1 or 2 depending on if double battles or not
|
* @returns The first {@linkcode PlayerPokemon} that is {@linkcode getPlayerField on the field}
|
||||||
|
* and {@linkcode PlayerPokemon.isActive is active}
|
||||||
|
* (aka {@linkcode PlayerPokemon.isAllowedInBattle is allowed in battle}),
|
||||||
|
* or `undefined` if there are no valid pokemon
|
||||||
|
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
|
||||||
|
*/
|
||||||
|
public getPlayerPokemon(includeSwitching: boolean = true): PlayerPokemon | undefined {
|
||||||
|
return this.getPlayerField().find(p => p.isActive() && (includeSwitching || p.switchOutStatus === false));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of PlayerPokemon of length 1 or 2 depending on if in a double battle or not.
|
||||||
|
* Does not actually check if the pokemon are on the field or not.
|
||||||
* @returns array of {@linkcode PlayerPokemon}
|
* @returns array of {@linkcode PlayerPokemon}
|
||||||
*/
|
*/
|
||||||
getPlayerField(): PlayerPokemon[] {
|
public getPlayerField(): PlayerPokemon[] {
|
||||||
const party = this.getParty();
|
const party = this.getPlayerParty();
|
||||||
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
|
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
getEnemyParty(): EnemyPokemon[] {
|
public getEnemyParty(): EnemyPokemon[] {
|
||||||
return this.currentBattle?.enemyParty ?? [];
|
return this.currentBattle?.enemyParty ?? [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getEnemyPokemon(): EnemyPokemon | undefined {
|
|
||||||
return this.getEnemyField().find(p => p.isActive());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the first {@linkcode Pokemon.isActive() | active EnemyPokemon} pokemon from the enemy that isn't also currently switching out
|
* @returns The first {@linkcode EnemyPokemon} that is {@linkcode getEnemyField on the field}
|
||||||
* @returns Either the first {@linkcode EnemyPokemon} satisfying, or undefined if no player pokemon on the field satisfy
|
* and {@linkcode EnemyPokemon.isActive is active}
|
||||||
|
* (aka {@linkcode EnemyPokemon.isAllowedInBattle is allowed in battle}),
|
||||||
|
* or `undefined` if there are no valid pokemon
|
||||||
|
* @param includeSwitching Whether a pokemon that is currently switching out is valid, default `true`
|
||||||
*/
|
*/
|
||||||
getNonSwitchedEnemyPokemon(): EnemyPokemon | undefined {
|
public getEnemyPokemon(includeSwitching: boolean = true): EnemyPokemon | undefined {
|
||||||
return this.getEnemyField().find(p => p.isActive() && p.switchOutStatus === false);
|
return this.getEnemyField().find(p => p.isActive() && (includeSwitching || p.switchOutStatus === false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array of EnemyPokemon of length 1 or 2 depending on if double battles or not
|
* Returns an array of EnemyPokemon of length 1 or 2 depending on if in a double battle or not.
|
||||||
|
* Does not actually check if the pokemon are on the field or not.
|
||||||
* @returns array of {@linkcode EnemyPokemon}
|
* @returns array of {@linkcode EnemyPokemon}
|
||||||
*/
|
*/
|
||||||
getEnemyField(): EnemyPokemon[] {
|
public getEnemyField(): EnemyPokemon[] {
|
||||||
const party = this.getEnemyParty();
|
const party = this.getEnemyParty();
|
||||||
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
|
return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
getField(activeOnly: boolean = false): Pokemon[] {
|
public getField(activeOnly: boolean = false): Pokemon[] {
|
||||||
const ret = new Array(4).fill(null);
|
const ret = new Array(4).fill(null);
|
||||||
const playerField = this.getPlayerField();
|
const playerField = this.getPlayerField();
|
||||||
const enemyField = this.getEnemyField();
|
const enemyField = this.getEnemyField();
|
||||||
@ -867,7 +876,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
getPokemonById(pokemonId: integer): Pokemon | null {
|
getPokemonById(pokemonId: integer): Pokemon | null {
|
||||||
const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId);
|
const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId);
|
||||||
return (findInParty(this.getParty()) || findInParty(this.getEnemyParty())) ?? null;
|
return (findInParty(this.getPlayerParty()) || findInParty(this.getEnemyParty())) ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
|
addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon {
|
||||||
@ -1062,7 +1071,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.modifierBar.removeAll(true);
|
this.modifierBar.removeAll(true);
|
||||||
this.enemyModifierBar.removeAll(true);
|
this.enemyModifierBar.removeAll(true);
|
||||||
|
|
||||||
for (const p of this.getParty()) {
|
for (const p of this.getPlayerParty()) {
|
||||||
p.destroy();
|
p.destroy();
|
||||||
}
|
}
|
||||||
this.party = [];
|
this.party = [];
|
||||||
@ -1275,7 +1284,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const pokemon of this.getParty()) {
|
for (const pokemon of this.getPlayerParty()) {
|
||||||
pokemon.resetBattleData();
|
pokemon.resetBattleData();
|
||||||
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
|
||||||
}
|
}
|
||||||
@ -1285,7 +1294,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.getParty()) {
|
for (const pokemon of this.getPlayerParty()) {
|
||||||
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
|
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeTimeOfDayTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1480,7 +1489,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trySpreadPokerus(): void {
|
trySpreadPokerus(): void {
|
||||||
const party = this.getParty();
|
const party = this.getPlayerParty();
|
||||||
const infectedIndexes: integer[] = [];
|
const infectedIndexes: integer[] = [];
|
||||||
const spread = (index: number, spreadTo: number) => {
|
const spread = (index: number, spreadTo: number) => {
|
||||||
const partyMember = party[index + spreadTo];
|
const partyMember = party[index + spreadTo];
|
||||||
@ -1677,7 +1686,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
updateAndShowText(duration: number): void {
|
updateAndShowText(duration: number): void {
|
||||||
const labels = [ this.luckLabelText, this.luckText ];
|
const labels = [ this.luckLabelText, this.luckText ];
|
||||||
labels.forEach(t => t.setAlpha(0));
|
labels.forEach(t => t.setAlpha(0));
|
||||||
const luckValue = getPartyLuckValue(this.getParty());
|
const luckValue = getPartyLuckValue(this.getPlayerParty());
|
||||||
this.luckText.setText(getLuckString(luckValue));
|
this.luckText.setText(getLuckString(luckValue));
|
||||||
if (luckValue < 14) {
|
if (luckValue < 14) {
|
||||||
this.luckText.setTint(getLuckTextTint(luckValue));
|
this.luckText.setTint(getLuckTextTint(luckValue));
|
||||||
@ -2593,9 +2602,19 @@ export default class BattleScene extends SceneBase {
|
|||||||
const addModifier = () => {
|
const addModifier = () => {
|
||||||
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
|
if (!matchingModifier || this.removeModifier(matchingModifier, !target.isPlayer())) {
|
||||||
if (target.isPlayer()) {
|
if (target.isPlayer()) {
|
||||||
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant).then(() => resolve(true));
|
this.addModifier(newItemModifier, ignoreUpdate, playSound, false, instant).then(() => {
|
||||||
|
if (source) {
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
||||||
|
}
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant).then(() => resolve(true));
|
this.addEnemyModifier(newItemModifier, ignoreUpdate, instant).then(() => {
|
||||||
|
if (source) {
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, source, false);
|
||||||
|
}
|
||||||
|
resolve(true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
@ -2615,7 +2634,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
|
|
||||||
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
|
removePartyMemberModifiers(partyMemberIndex: integer): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const pokemonId = this.getParty()[partyMemberIndex].id;
|
const pokemonId = this.getPlayerParty()[partyMemberIndex].id;
|
||||||
const modifiersToRemove = this.modifiers.filter(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
|
const modifiersToRemove = this.modifiers.filter(m => m instanceof PokemonHeldItemModifier && (m as PokemonHeldItemModifier).pokemonId === pokemonId);
|
||||||
for (const m of modifiersToRemove) {
|
for (const m of modifiersToRemove) {
|
||||||
this.modifiers.splice(this.modifiers.indexOf(m), 1);
|
this.modifiers.splice(this.modifiers.indexOf(m), 1);
|
||||||
@ -2742,7 +2761,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updatePartyForModifiers(player ? this.getParty() : this.getEnemyParty(), instant).then(() => {
|
this.updatePartyForModifiers(player ? this.getPlayerParty() : this.getEnemyParty(), instant).then(() => {
|
||||||
(player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers);
|
(player ? this.modifierBar : this.enemyModifierBar).updateModifiers(modifiers);
|
||||||
if (!player) {
|
if (!player) {
|
||||||
this.updateUIPositions();
|
this.updateUIPositions();
|
||||||
@ -2980,22 +2999,16 @@ export default class BattleScene extends SceneBase {
|
|||||||
*/
|
*/
|
||||||
getActiveKeys(): string[] {
|
getActiveKeys(): string[] {
|
||||||
const keys: string[] = [];
|
const keys: string[] = [];
|
||||||
const playerParty = this.getParty();
|
let activePokemon: (PlayerPokemon | EnemyPokemon)[] = this.getPlayerParty();
|
||||||
playerParty.forEach(p => {
|
activePokemon = activePokemon.concat(this.getEnemyParty());
|
||||||
|
activePokemon.forEach((p) => {
|
||||||
keys.push(p.getSpriteKey(true));
|
keys.push(p.getSpriteKey(true));
|
||||||
|
if (p instanceof PlayerPokemon) {
|
||||||
keys.push(p.getBattleSpriteKey(true, true));
|
keys.push(p.getBattleSpriteKey(true, true));
|
||||||
keys.push("cry/" + p.species.getCryKey(p.formIndex));
|
|
||||||
if (p.fusionSpecies) {
|
|
||||||
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
|
||||||
}
|
}
|
||||||
});
|
keys.push(p.species.getCryKey(p.formIndex));
|
||||||
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
|
|
||||||
const enemyParty = this.getEnemyParty();
|
|
||||||
enemyParty.forEach(p => {
|
|
||||||
keys.push(p.getSpriteKey(true));
|
|
||||||
keys.push("cry/" + p.species.getCryKey(p.formIndex));
|
|
||||||
if (p.fusionSpecies) {
|
if (p.fusionSpecies) {
|
||||||
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
keys.push(p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return keys;
|
return keys;
|
||||||
@ -3016,7 +3029,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
this.setFieldScale(0.75);
|
this.setFieldScale(0.75);
|
||||||
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
|
this.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
|
||||||
this.currentBattle.double = true;
|
this.currentBattle.double = true;
|
||||||
const availablePartyMembers = this.getParty().filter((p) => p.isAllowedInBattle());
|
const availablePartyMembers = this.getPlayerParty().filter((p) => p.isAllowedInBattle());
|
||||||
if (availablePartyMembers.length > 1) {
|
if (availablePartyMembers.length > 1) {
|
||||||
this.pushPhase(new ToggleDoublePositionPhase(this, true));
|
this.pushPhase(new ToggleDoublePositionPhase(this, true));
|
||||||
if (!availablePartyMembers[1].isOnField()) {
|
if (!availablePartyMembers[1].isOnField()) {
|
||||||
@ -3041,7 +3054,7 @@ export default class BattleScene extends SceneBase {
|
|||||||
*/
|
*/
|
||||||
applyPartyExp(expValue: number, pokemonDefeated: boolean, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>): void {
|
applyPartyExp(expValue: number, pokemonDefeated: boolean, useWaveIndexMultiplier?: boolean, pokemonParticipantIds?: Set<number>): void {
|
||||||
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
|
const participantIds = pokemonParticipantIds ?? this.currentBattle.playerParticipantIds;
|
||||||
const party = this.getParty();
|
const party = this.getPlayerParty();
|
||||||
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
const expShareModifier = this.findModifier(m => m instanceof ExpShareModifier) as ExpShareModifier;
|
||||||
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
const expBalanceModifier = this.findModifier(m => m instanceof ExpBalanceModifier) as ExpBalanceModifier;
|
||||||
const multipleParticipantExpBonusModifier = this.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
|
const multipleParticipantExpBonusModifier = this.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier;
|
||||||
|
202
src/battle.ts
202
src/battle.ts
@ -6,11 +6,13 @@ import { GameMode } from "./game-mode";
|
|||||||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||||
import { PokeballType } from "./data/pokeball";
|
import { PokeballType } from "./data/pokeball";
|
||||||
import { trainerConfigs } from "#app/data/trainer-config";
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
|
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } 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";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
|
import { MusicPreference } from "#app/system/settings/settings";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
import i18next from "#app/plugins/i18n";
|
import i18next from "#app/plugins/i18n";
|
||||||
@ -212,7 +214,6 @@ export default class Battle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getBgmOverride(scene: BattleScene): string | null {
|
getBgmOverride(scene: BattleScene): string | null {
|
||||||
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
|
||||||
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
|
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
|
||||||
// Music is overridden for MEs during ME onInit()
|
// Music is overridden for MEs during ME onInit()
|
||||||
// Should not use any BGM overrides before swapping from DEFAULT mode
|
// Should not use any BGM overrides before swapping from DEFAULT mode
|
||||||
@ -221,7 +222,7 @@ export default class Battle {
|
|||||||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
||||||
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
||||||
}
|
}
|
||||||
if (scene.musicPreference === 0) {
|
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||||
return this.trainer?.getBattleBgm() ?? null;
|
return this.trainer?.getBattleBgm() ?? null;
|
||||||
} else {
|
} else {
|
||||||
return this.trainer?.getMixedBattleBgm() ?? null;
|
return this.trainer?.getMixedBattleBgm() ?? null;
|
||||||
@ -229,143 +230,163 @@ export default class Battle {
|
|||||||
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
|
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
|
||||||
return "end_summit";
|
return "end_summit";
|
||||||
}
|
}
|
||||||
for (const pokemon of battlers) {
|
const wildOpponents = scene.getEnemyParty();
|
||||||
|
for (const pokemon of wildOpponents) {
|
||||||
if (this.battleSpec === BattleSpec.FINAL_BOSS) {
|
if (this.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||||
if (pokemon.formIndex) {
|
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) {
|
||||||
return "battle_final";
|
return "battle_final";
|
||||||
}
|
}
|
||||||
return "battle_final_encounter";
|
return "battle_final_encounter";
|
||||||
}
|
}
|
||||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||||
if (scene.musicPreference === 0) {
|
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
switch (pokemon.species.speciesId) {
|
||||||
|
case Species.REGIROCK:
|
||||||
|
case Species.REGICE:
|
||||||
|
case Species.REGISTEEL:
|
||||||
|
case Species.REGIGIGAS:
|
||||||
|
case Species.REGIDRAGO:
|
||||||
|
case Species.REGIELEKI:
|
||||||
return "battle_legendary_regis_g5";
|
return "battle_legendary_regis_g5";
|
||||||
}
|
case Species.KYUREM:
|
||||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
|
||||||
return "battle_legendary_unova";
|
|
||||||
}
|
|
||||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
|
||||||
return "battle_legendary_kyurem";
|
return "battle_legendary_kyurem";
|
||||||
}
|
default:
|
||||||
if (pokemon.species.legendary) {
|
if (pokemon.species.legendary) {
|
||||||
return "battle_legendary_res_zek";
|
return "battle_legendary_res_zek";
|
||||||
}
|
}
|
||||||
return "battle_legendary_unova";
|
return "battle_legendary_unova";
|
||||||
} else {
|
}
|
||||||
if (pokemon.species.speciesId === Species.ARTICUNO || pokemon.species.speciesId === Species.ZAPDOS || pokemon.species.speciesId === Species.MOLTRES || pokemon.species.speciesId === Species.MEWTWO || pokemon.species.speciesId === Species.MEW) {
|
} else if (scene.musicPreference === MusicPreference.MIXED) {
|
||||||
|
switch (pokemon.species.speciesId) {
|
||||||
|
case Species.ARTICUNO:
|
||||||
|
case Species.ZAPDOS:
|
||||||
|
case Species.MOLTRES:
|
||||||
|
case Species.MEWTWO:
|
||||||
|
case Species.MEW:
|
||||||
return "battle_legendary_kanto";
|
return "battle_legendary_kanto";
|
||||||
}
|
case Species.RAIKOU:
|
||||||
if (pokemon.species.speciesId === Species.RAIKOU) {
|
|
||||||
return "battle_legendary_raikou";
|
return "battle_legendary_raikou";
|
||||||
}
|
case Species.ENTEI:
|
||||||
if (pokemon.species.speciesId === Species.ENTEI) {
|
|
||||||
return "battle_legendary_entei";
|
return "battle_legendary_entei";
|
||||||
}
|
case Species.SUICUNE:
|
||||||
if (pokemon.species.speciesId === Species.SUICUNE) {
|
|
||||||
return "battle_legendary_suicune";
|
return "battle_legendary_suicune";
|
||||||
}
|
case Species.LUGIA:
|
||||||
if (pokemon.species.speciesId === Species.LUGIA) {
|
|
||||||
return "battle_legendary_lugia";
|
return "battle_legendary_lugia";
|
||||||
}
|
case Species.HO_OH:
|
||||||
if (pokemon.species.speciesId === Species.HO_OH) {
|
|
||||||
return "battle_legendary_ho_oh";
|
return "battle_legendary_ho_oh";
|
||||||
}
|
case Species.REGIROCK:
|
||||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
case Species.REGICE:
|
||||||
|
case Species.REGISTEEL:
|
||||||
|
case Species.REGIGIGAS:
|
||||||
|
case Species.REGIDRAGO:
|
||||||
|
case Species.REGIELEKI:
|
||||||
return "battle_legendary_regis_g6";
|
return "battle_legendary_regis_g6";
|
||||||
}
|
case Species.GROUDON:
|
||||||
if (pokemon.species.speciesId === Species.GROUDON || pokemon.species.speciesId === Species.KYOGRE) {
|
case Species.KYOGRE:
|
||||||
return "battle_legendary_gro_kyo";
|
return "battle_legendary_gro_kyo";
|
||||||
}
|
case Species.RAYQUAZA:
|
||||||
if (pokemon.species.speciesId === Species.RAYQUAZA) {
|
|
||||||
return "battle_legendary_rayquaza";
|
return "battle_legendary_rayquaza";
|
||||||
}
|
case Species.DEOXYS:
|
||||||
if (pokemon.species.speciesId === Species.DEOXYS) {
|
|
||||||
return "battle_legendary_deoxys";
|
return "battle_legendary_deoxys";
|
||||||
}
|
case Species.UXIE:
|
||||||
if (pokemon.species.speciesId === Species.UXIE || pokemon.species.speciesId === Species.MESPRIT || pokemon.species.speciesId === Species.AZELF) {
|
case Species.MESPRIT:
|
||||||
|
case Species.AZELF:
|
||||||
return "battle_legendary_lake_trio";
|
return "battle_legendary_lake_trio";
|
||||||
}
|
case Species.HEATRAN:
|
||||||
if (pokemon.species.speciesId === Species.HEATRAN || pokemon.species.speciesId === Species.CRESSELIA || pokemon.species.speciesId === Species.DARKRAI || pokemon.species.speciesId === Species.SHAYMIN) {
|
case Species.CRESSELIA:
|
||||||
|
case Species.DARKRAI:
|
||||||
|
case Species.SHAYMIN:
|
||||||
return "battle_legendary_sinnoh";
|
return "battle_legendary_sinnoh";
|
||||||
}
|
case Species.DIALGA:
|
||||||
if (pokemon.species.speciesId === Species.DIALGA || pokemon.species.speciesId === Species.PALKIA) {
|
case Species.PALKIA:
|
||||||
if (pokemon.getFormKey() === "") {
|
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ORIGIN) {
|
||||||
return "battle_legendary_dia_pal";
|
|
||||||
}
|
|
||||||
if (pokemon.getFormKey() === "origin") {
|
|
||||||
return "battle_legendary_origin_forme";
|
return "battle_legendary_origin_forme";
|
||||||
}
|
}
|
||||||
}
|
return "battle_legendary_dia_pal";
|
||||||
if (pokemon.species.speciesId === Species.GIRATINA) {
|
case Species.GIRATINA:
|
||||||
return "battle_legendary_giratina";
|
return "battle_legendary_giratina";
|
||||||
}
|
case Species.ARCEUS:
|
||||||
if (pokemon.species.speciesId === Species.ARCEUS) {
|
|
||||||
return "battle_legendary_arceus";
|
return "battle_legendary_arceus";
|
||||||
}
|
case Species.COBALION:
|
||||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
case Species.TERRAKION:
|
||||||
|
case Species.VIRIZION:
|
||||||
|
case Species.KELDEO:
|
||||||
|
case Species.TORNADUS:
|
||||||
|
case Species.LANDORUS:
|
||||||
|
case Species.THUNDURUS:
|
||||||
|
case Species.MELOETTA:
|
||||||
|
case Species.GENESECT:
|
||||||
return "battle_legendary_unova";
|
return "battle_legendary_unova";
|
||||||
}
|
case Species.KYUREM:
|
||||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
|
||||||
return "battle_legendary_kyurem";
|
return "battle_legendary_kyurem";
|
||||||
}
|
case Species.XERNEAS:
|
||||||
if (pokemon.species.speciesId === Species.XERNEAS || pokemon.species.speciesId === Species.YVELTAL || pokemon.species.speciesId === Species.ZYGARDE) {
|
case Species.YVELTAL:
|
||||||
|
case Species.ZYGARDE:
|
||||||
return "battle_legendary_xern_yvel";
|
return "battle_legendary_xern_yvel";
|
||||||
}
|
case Species.TAPU_KOKO:
|
||||||
if (pokemon.species.speciesId === Species.TAPU_KOKO || pokemon.species.speciesId === Species.TAPU_LELE || pokemon.species.speciesId === Species.TAPU_BULU || pokemon.species.speciesId === Species.TAPU_FINI) {
|
case Species.TAPU_LELE:
|
||||||
|
case Species.TAPU_BULU:
|
||||||
|
case Species.TAPU_FINI:
|
||||||
return "battle_legendary_tapu";
|
return "battle_legendary_tapu";
|
||||||
}
|
case Species.SOLGALEO:
|
||||||
if ([ Species.COSMOG, Species.COSMOEM, Species.SOLGALEO, Species.LUNALA ].includes(pokemon.species.speciesId)) {
|
case Species.LUNALA:
|
||||||
return "battle_legendary_sol_lun";
|
return "battle_legendary_sol_lun";
|
||||||
}
|
case Species.NECROZMA:
|
||||||
if (pokemon.species.speciesId === Species.NECROZMA) {
|
switch (pokemon.getFormKey()) {
|
||||||
if (pokemon.getFormKey() === "") {
|
case "dusk-mane":
|
||||||
return "battle_legendary_sol_lun";
|
case "dawn-wings":
|
||||||
}
|
|
||||||
if (pokemon.getFormKey() === "dusk-mane" || pokemon.getFormKey() === "dawn-wings") {
|
|
||||||
return "battle_legendary_dusk_dawn";
|
return "battle_legendary_dusk_dawn";
|
||||||
}
|
case "ultra":
|
||||||
if (pokemon.getFormKey() === "ultra") {
|
|
||||||
return "battle_legendary_ultra_nec";
|
return "battle_legendary_ultra_nec";
|
||||||
|
default:
|
||||||
|
return "battle_legendary_sol_lun";
|
||||||
}
|
}
|
||||||
}
|
case Species.NIHILEGO:
|
||||||
if ([ Species.NIHILEGO, Species.BUZZWOLE, Species.PHEROMOSA, Species.XURKITREE, Species.CELESTEELA, Species.KARTANA, Species.GUZZLORD, Species.POIPOLE, Species.NAGANADEL, Species.STAKATAKA, Species.BLACEPHALON ].includes(pokemon.species.speciesId)) {
|
case Species.PHEROMOSA:
|
||||||
|
case Species.BUZZWOLE:
|
||||||
|
case Species.XURKITREE:
|
||||||
|
case Species.CELESTEELA:
|
||||||
|
case Species.KARTANA:
|
||||||
|
case Species.GUZZLORD:
|
||||||
|
case Species.POIPOLE:
|
||||||
|
case Species.NAGANADEL:
|
||||||
|
case Species.STAKATAKA:
|
||||||
|
case Species.BLACEPHALON:
|
||||||
return "battle_legendary_ub";
|
return "battle_legendary_ub";
|
||||||
}
|
case Species.ZACIAN:
|
||||||
if (pokemon.species.speciesId === Species.ZACIAN || pokemon.species.speciesId === Species.ZAMAZENTA) {
|
case Species.ZAMAZENTA:
|
||||||
return "battle_legendary_zac_zam";
|
return "battle_legendary_zac_zam";
|
||||||
}
|
case Species.GLASTRIER:
|
||||||
if (pokemon.species.speciesId === Species.GLASTRIER || pokemon.species.speciesId === Species.SPECTRIER) {
|
case Species.SPECTRIER:
|
||||||
return "battle_legendary_glas_spec";
|
return "battle_legendary_glas_spec";
|
||||||
}
|
case Species.CALYREX:
|
||||||
if (pokemon.species.speciesId === Species.CALYREX) {
|
|
||||||
if (pokemon.getFormKey() === "") {
|
|
||||||
return "battle_legendary_calyrex";
|
|
||||||
}
|
|
||||||
if (pokemon.getFormKey() === "ice" || pokemon.getFormKey() === "shadow") {
|
if (pokemon.getFormKey() === "ice" || pokemon.getFormKey() === "shadow") {
|
||||||
return "battle_legendary_riders";
|
return "battle_legendary_riders";
|
||||||
}
|
}
|
||||||
}
|
return "battle_legendary_calyrex";
|
||||||
if (pokemon.species.speciesId === Species.GALAR_ARTICUNO || pokemon.species.speciesId === Species.GALAR_ZAPDOS || pokemon.species.speciesId === Species.GALAR_MOLTRES) {
|
case Species.GALAR_ARTICUNO:
|
||||||
|
case Species.GALAR_ZAPDOS:
|
||||||
|
case Species.GALAR_MOLTRES:
|
||||||
return "battle_legendary_birds_galar";
|
return "battle_legendary_birds_galar";
|
||||||
}
|
case Species.WO_CHIEN:
|
||||||
if (pokemon.species.speciesId === Species.WO_CHIEN || pokemon.species.speciesId === Species.CHIEN_PAO || pokemon.species.speciesId === Species.TING_LU || pokemon.species.speciesId === Species.CHI_YU) {
|
case Species.CHIEN_PAO:
|
||||||
|
case Species.TING_LU:
|
||||||
|
case Species.CHI_YU:
|
||||||
return "battle_legendary_ruinous";
|
return "battle_legendary_ruinous";
|
||||||
}
|
case Species.KORAIDON:
|
||||||
if (pokemon.species.speciesId === Species.KORAIDON || pokemon.species.speciesId === Species.MIRAIDON) {
|
case Species.MIRAIDON:
|
||||||
return "battle_legendary_kor_mir";
|
return "battle_legendary_kor_mir";
|
||||||
}
|
case Species.OKIDOGI:
|
||||||
if (pokemon.species.speciesId === Species.OKIDOGI || pokemon.species.speciesId === Species.MUNKIDORI || pokemon.species.speciesId === Species.FEZANDIPITI) {
|
case Species.MUNKIDORI:
|
||||||
|
case Species.FEZANDIPITI:
|
||||||
return "battle_legendary_loyal_three";
|
return "battle_legendary_loyal_three";
|
||||||
}
|
case Species.OGERPON:
|
||||||
if (pokemon.species.speciesId === Species.OGERPON) {
|
|
||||||
return "battle_legendary_ogerpon";
|
return "battle_legendary_ogerpon";
|
||||||
}
|
case Species.TERAPAGOS:
|
||||||
if (pokemon.species.speciesId === Species.TERAPAGOS) {
|
|
||||||
return "battle_legendary_terapagos";
|
return "battle_legendary_terapagos";
|
||||||
}
|
case Species.PECHARUNT:
|
||||||
if (pokemon.species.speciesId === Species.PECHARUNT) {
|
|
||||||
return "battle_legendary_pecharunt";
|
return "battle_legendary_pecharunt";
|
||||||
}
|
default:
|
||||||
if (pokemon.species.legendary) {
|
if (pokemon.species.legendary) {
|
||||||
return "battle_legendary_res_zek";
|
return "battle_legendary_res_zek";
|
||||||
}
|
}
|
||||||
@ -373,6 +394,7 @@ export default class Battle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (scene.gameMode.isClassic && this.waveIndex <= 4) {
|
if (scene.gameMode.isClassic && this.waveIndex <= 4) {
|
||||||
return "battle_wild";
|
return "battle_wild";
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import Pokemon, { HitResult, PlayerPokemon, PokemonMove } from "../field/pokemon";
|
import Pokemon, { EnemyPokemon, HitResult, MoveResult, PlayerPokemon, PokemonMove } from "../field/pokemon";
|
||||||
import { Type } from "./type";
|
import { Type } from "./type";
|
||||||
import { Constructor } from "#app/utils";
|
import { Constructor } from "#app/utils";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
@ -9,7 +9,7 @@ import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, g
|
|||||||
import { Gender } from "./gender";
|
import { Gender } from "./gender";
|
||||||
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
|
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, SacrificialAttr, SacrificialAttrOnHit, NeutralDamageAgainstFlyingTypeMultiplierAttr, FixedDamageAttr } from "./move";
|
||||||
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
|
||||||
import { BerryModifier, PokemonHeldItemModifier } from "../modifier/modifier";
|
import { BerryModifier, HitHealModifier, PokemonHeldItemModifier } from "../modifier/modifier";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "./pokemon-forms";
|
import { SpeciesFormChangeManualTrigger, SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "./pokemon-forms";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
@ -17,7 +17,7 @@ import { Localizable } from "#app/interfaces/locales";
|
|||||||
import { Command } from "../ui/command-ui-handler";
|
import { Command } from "../ui/command-ui-handler";
|
||||||
import { BerryModifierType } from "#app/modifier/modifier-type";
|
import { BerryModifierType } from "#app/modifier/modifier-type";
|
||||||
import { getPokeballName } from "./pokeball";
|
import { getPokeballName } from "./pokeball";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex, BattleType } from "#app/battle";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
@ -29,6 +29,12 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
|||||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { SwitchType } from "#app/enums/switch-type";
|
||||||
|
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||||
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
|
import { BattleEndPhase } from "#app/phases/battle-end-phase";
|
||||||
|
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||||
|
import { MoveEndPhase } from "#app/phases/move-end-phase";
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
export class Ability implements Localizable {
|
||||||
public id: Abilities;
|
public id: Abilities;
|
||||||
@ -1950,6 +1956,10 @@ export class CopyFaintedAllyAbilityAbAttr extends PostKnockOutAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability attribute for ignoring the opponent's stat changes
|
||||||
|
* @param stats the stats that should be ignored
|
||||||
|
*/
|
||||||
export class IgnoreOpponentStatStagesAbAttr extends AbAttr {
|
export class IgnoreOpponentStatStagesAbAttr extends AbAttr {
|
||||||
private stats: readonly BattleStat[];
|
private stats: readonly BattleStat[];
|
||||||
|
|
||||||
@ -1959,6 +1969,15 @@ export class IgnoreOpponentStatStagesAbAttr extends AbAttr {
|
|||||||
this.stats = stats ?? BATTLE_STATS;
|
this.stats = stats ?? BATTLE_STATS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies a BooleanHolder and returns the result to see if a stat is ignored or not
|
||||||
|
* @param _pokemon n/a
|
||||||
|
* @param _passive n/a
|
||||||
|
* @param simulated n/a
|
||||||
|
* @param _cancelled n/a
|
||||||
|
* @param args A BooleanHolder that represents whether or not to ignore a stat's stat changes
|
||||||
|
* @returns true if the stat is ignored, false otherwise
|
||||||
|
*/
|
||||||
apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]) {
|
apply(_pokemon: Pokemon, _passive: boolean, simulated: boolean, _cancelled: Utils.BooleanHolder, args: any[]) {
|
||||||
if (this.stats.includes(args[0])) {
|
if (this.stats.includes(args[0])) {
|
||||||
(args[1] as Utils.BooleanHolder).value = true;
|
(args[1] as Utils.BooleanHolder).value = true;
|
||||||
@ -3092,7 +3111,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr {
|
|||||||
/**
|
/**
|
||||||
* Condition function to applied to abilities related to Sheer Force.
|
* Condition function to applied to abilities related to Sheer Force.
|
||||||
* Checks if last move used against target was affected by a Sheer Force user and:
|
* Checks if last move used against target was affected by a Sheer Force user and:
|
||||||
* Disables: Color Change, Pickpocket, Wimp Out, Emergency Exit, Berserk, Anger Shell
|
* Disables: Color Change, Pickpocket, Berserk, Anger Shell
|
||||||
* @returns {AbAttrCondition} If false disables the ability which the condition is applied to.
|
* @returns {AbAttrCondition} If false disables the ability which the condition is applied to.
|
||||||
*/
|
*/
|
||||||
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
|
function getSheerForceHitDisableAbCondition(): AbAttrCondition {
|
||||||
@ -3838,6 +3857,41 @@ export class PostDancingMoveAbAttr extends PostMoveUsedAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers after the Pokemon loses or consumes an item
|
||||||
|
* @extends AbAttr
|
||||||
|
*/
|
||||||
|
export class PostItemLostAbAttr extends AbAttr {
|
||||||
|
applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a Battler Tag to the Pokemon after it loses or consumes item
|
||||||
|
* @extends PostItemLostAbAttr
|
||||||
|
*/
|
||||||
|
export class PostItemLostApplyBattlerTagAbAttr extends PostItemLostAbAttr {
|
||||||
|
private tagType: BattlerTagType;
|
||||||
|
constructor(tagType: BattlerTagType) {
|
||||||
|
super(true);
|
||||||
|
this.tagType = tagType;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Adds the last used Pokeball back into the player's inventory
|
||||||
|
* @param pokemon {@linkcode Pokemon} with this ability
|
||||||
|
* @param args N/A
|
||||||
|
* @returns true if BattlerTag was applied
|
||||||
|
*/
|
||||||
|
applyPostItemLost(pokemon: Pokemon, simulated: boolean, args: any[]): boolean | Promise<boolean> {
|
||||||
|
if (!pokemon.getTag(this.tagType) && !simulated) {
|
||||||
|
pokemon.addTag(this.tagType);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class StatStageChangeMultiplierAbAttr extends AbAttr {
|
export class StatStageChangeMultiplierAbAttr extends AbAttr {
|
||||||
private multiplier: integer;
|
private multiplier: integer;
|
||||||
|
|
||||||
@ -4833,6 +4887,239 @@ async function applyAbAttrsInternal<TAttr extends AbAttr>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ForceSwitchOutHelper {
|
||||||
|
constructor(private switchType: SwitchType) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the logic for switching out a Pokémon based on battle conditions, HP, and the switch type.
|
||||||
|
*
|
||||||
|
* @param pokemon The {@linkcode Pokemon} attempting to switch out.
|
||||||
|
* @returns `true` if the switch is successful
|
||||||
|
*/
|
||||||
|
public switchOutLogic(pokemon: Pokemon): boolean {
|
||||||
|
const switchOutTarget = pokemon;
|
||||||
|
/**
|
||||||
|
* If the switch-out target is a player-controlled Pokémon, the function checks:
|
||||||
|
* - Whether there are available party members to switch in.
|
||||||
|
* - If the Pokémon is still alive (hp > 0), and if so, it leaves the field and a new SwitchPhase is initiated.
|
||||||
|
*/
|
||||||
|
if (switchOutTarget instanceof PlayerPokemon) {
|
||||||
|
if (switchOutTarget.scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switchOutTarget.hp > 0) {
|
||||||
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
|
pokemon.scene.prependToPhase(new SwitchPhase(pokemon.scene, this.switchType, switchOutTarget.getFieldIndex(), true, true), MoveEndPhase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* For non-wild battles, it checks if the opposing party has any available Pokémon to switch in.
|
||||||
|
* If yes, the Pokémon leaves the field and a new SwitchSummonPhase is initiated.
|
||||||
|
*/
|
||||||
|
} else if (pokemon.scene.currentBattle.battleType !== BattleType.WILD) {
|
||||||
|
if (switchOutTarget.scene.getEnemyParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (switchOutTarget.hp > 0) {
|
||||||
|
switchOutTarget.leaveField(this.switchType === SwitchType.SWITCH);
|
||||||
|
pokemon.scene.prependToPhase(new SwitchSummonPhase(pokemon.scene, this.switchType, switchOutTarget.getFieldIndex(),
|
||||||
|
(pokemon.scene.currentBattle.trainer ? pokemon.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0),
|
||||||
|
false, false), MoveEndPhase);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* For wild Pokémon battles, the Pokémon will flee if the conditions are met (waveIndex and double battles).
|
||||||
|
*/
|
||||||
|
} else {
|
||||||
|
if (!pokemon.scene.currentBattle.waveIndex && pokemon.scene.currentBattle.waveIndex % 10 === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (switchOutTarget.hp > 0) {
|
||||||
|
switchOutTarget.leaveField(false);
|
||||||
|
pokemon.scene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
|
||||||
|
|
||||||
|
if (switchOutTarget.scene.currentBattle.double) {
|
||||||
|
const allyPokemon = switchOutTarget.getAlly();
|
||||||
|
switchOutTarget.scene.redirectPokemonMoves(switchOutTarget, allyPokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!switchOutTarget.getAlly()?.isActive(true)) {
|
||||||
|
pokemon.scene.clearEnemyHeldItemModifiers();
|
||||||
|
|
||||||
|
if (switchOutTarget.hp) {
|
||||||
|
pokemon.scene.pushPhase(new BattleEndPhase(pokemon.scene));
|
||||||
|
pokemon.scene.pushPhase(new NewBattlePhase(pokemon.scene));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a Pokémon can switch out based on its status, the opponent's status, and battle conditions.
|
||||||
|
*
|
||||||
|
* @param pokemon The Pokémon attempting to switch out.
|
||||||
|
* @param opponent The opponent Pokémon.
|
||||||
|
* @returns `true` if the switch-out condition is met
|
||||||
|
*/
|
||||||
|
public getSwitchOutCondition(pokemon: Pokemon, opponent: Pokemon): boolean {
|
||||||
|
const switchOutTarget = pokemon;
|
||||||
|
const player = switchOutTarget instanceof PlayerPokemon;
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
const blockedByAbility = new Utils.BooleanHolder(false);
|
||||||
|
applyAbAttrs(ForceSwitchOutImmunityAbAttr, opponent, blockedByAbility);
|
||||||
|
return !blockedByAbility.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player && pokemon.scene.currentBattle.battleType === BattleType.WILD) {
|
||||||
|
if (!pokemon.scene.currentBattle.waveIndex && pokemon.scene.currentBattle.waveIndex % 10 === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!player && pokemon.scene.currentBattle.isBattleMysteryEncounter() && !pokemon.scene.currentBattle.mysteryEncounter?.fleeAllowed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const party = player ? pokemon.scene.getPlayerParty() : pokemon.scene.getEnemyParty();
|
||||||
|
return (!player && pokemon.scene.currentBattle.battleType === BattleType.WILD)
|
||||||
|
|| party.filter(p => p.isAllowedInBattle()
|
||||||
|
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > pokemon.scene.currentBattle.getBattlerCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a message if the switch-out attempt fails due to ability effects.
|
||||||
|
*
|
||||||
|
* @param target The target Pokémon.
|
||||||
|
* @returns The failure message, or `null` if no failure.
|
||||||
|
*/
|
||||||
|
public getFailedText(target: Pokemon): string | null {
|
||||||
|
const blockedByAbility = new Utils.BooleanHolder(false);
|
||||||
|
applyAbAttrs(ForceSwitchOutImmunityAbAttr, target, blockedByAbility);
|
||||||
|
return blockedByAbility.value ? i18next.t("moveTriggers:cannotBeSwitchedOut", { pokemonName: getPokemonNameWithAffix(target) }) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the amount of recovery from the Shell Bell item.
|
||||||
|
*
|
||||||
|
* If the Pokémon is holding a Shell Bell, this function computes the amount of health
|
||||||
|
* recovered based on the damage dealt in the current turn. The recovery is multiplied by the
|
||||||
|
* Shell Bell's modifier (if any).
|
||||||
|
*
|
||||||
|
* @param pokemon - The Pokémon whose Shell Bell recovery is being calculated.
|
||||||
|
* @returns The amount of health recovered by Shell Bell.
|
||||||
|
*/
|
||||||
|
function calculateShellBellRecovery(pokemon: Pokemon): number {
|
||||||
|
const shellBellModifier = pokemon.getHeldItems().find(m => m instanceof HitHealModifier);
|
||||||
|
if (shellBellModifier) {
|
||||||
|
return Utils.toDmgValue(pokemon.turnData.totalDamageDealt / 8) * shellBellModifier.stackCount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers after the Pokemon takes any damage
|
||||||
|
* @extends AbAttr
|
||||||
|
*/
|
||||||
|
export class PostDamageAbAttr extends AbAttr {
|
||||||
|
public applyPostDamage(pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], source?: Pokemon): boolean | Promise<boolean> {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ability attribute for forcing a Pokémon to switch out after its health drops below half.
|
||||||
|
* This attribute checks various conditions related to the damage received, the moves used by the Pokémon
|
||||||
|
* and its opponents, and determines whether a forced switch-out should occur.
|
||||||
|
*
|
||||||
|
* Used by Wimp Out and Emergency Exit
|
||||||
|
*
|
||||||
|
* @extends PostDamageAbAttr
|
||||||
|
* @see {@linkcode applyPostDamage}
|
||||||
|
*/
|
||||||
|
export class PostDamageForceSwitchAbAttr extends PostDamageAbAttr {
|
||||||
|
private helper: ForceSwitchOutHelper = new ForceSwitchOutHelper(SwitchType.SWITCH);
|
||||||
|
private hpRatio: number;
|
||||||
|
|
||||||
|
constructor(hpRatio: number = 0.5) {
|
||||||
|
super();
|
||||||
|
this.hpRatio = hpRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the switch-out logic after the Pokémon takes damage.
|
||||||
|
* Checks various conditions based on the moves used by the Pokémon, the opponents' moves, and
|
||||||
|
* the Pokémon's health after damage to determine whether the switch-out should occur.
|
||||||
|
*
|
||||||
|
* @param pokemon The Pokémon that took damage.
|
||||||
|
* @param damage The amount of damage taken by the Pokémon.
|
||||||
|
* @param passive N/A
|
||||||
|
* @param simulated Whether the ability is being simulated.
|
||||||
|
* @param args N/A
|
||||||
|
* @param source The Pokemon that dealt damage
|
||||||
|
* @returns `true` if the switch-out logic was successfully applied
|
||||||
|
*/
|
||||||
|
public override applyPostDamage(pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean, args: any[], source?: Pokemon): boolean | Promise<boolean> {
|
||||||
|
const moveHistory = pokemon.getMoveHistory();
|
||||||
|
// Will not activate when the Pokémon's HP is lowered by cutting its own HP
|
||||||
|
const fordbiddenAttackingMoves = [ Moves.BELLY_DRUM, Moves.SUBSTITUTE, Moves.CURSE, Moves.PAIN_SPLIT ];
|
||||||
|
if (moveHistory.length > 0) {
|
||||||
|
const lastMoveUsed = moveHistory[moveHistory.length - 1];
|
||||||
|
if (fordbiddenAttackingMoves.includes(lastMoveUsed.move)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.
|
||||||
|
const fordbiddenDefendingMoves = [ Moves.DRAGON_TAIL, Moves.CIRCLE_THROW ];
|
||||||
|
if (source) {
|
||||||
|
const enemyMoveHistory = source.getMoveHistory();
|
||||||
|
if (enemyMoveHistory.length > 0) {
|
||||||
|
const enemyLastMoveUsed = enemyMoveHistory[enemyMoveHistory.length - 1];
|
||||||
|
// Will not activate if the Pokémon's HP falls below half while it is in the air during Sky Drop.
|
||||||
|
if (fordbiddenDefendingMoves.includes(enemyLastMoveUsed.move) || enemyLastMoveUsed.move === Moves.SKY_DROP && enemyLastMoveUsed.result === MoveResult.OTHER) {
|
||||||
|
return false;
|
||||||
|
// Will not activate if the Pokémon's HP falls below half by a move affected by Sheer Force.
|
||||||
|
} else if (allMoves[enemyLastMoveUsed.move].chance >= 0 && source.hasAbility(Abilities.SHEER_FORCE)) {
|
||||||
|
return false;
|
||||||
|
// Activate only after the last hit of multistrike moves
|
||||||
|
} else if (source.turnData.hitsLeft > 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (source.turnData.hitCount > 1) {
|
||||||
|
damage = pokemon.turnData.damageTaken;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pokemon.hp + damage >= pokemon.getMaxHp() * this.hpRatio) {
|
||||||
|
// Activates if it falls below half and recovers back above half from a Shell Bell
|
||||||
|
const shellBellHeal = calculateShellBellRecovery(pokemon);
|
||||||
|
if (pokemon.hp - shellBellHeal < pokemon.getMaxHp() * this.hpRatio) {
|
||||||
|
for (const opponent of pokemon.getOpponents()) {
|
||||||
|
if (!this.helper.getSwitchOutCondition(pokemon, opponent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.helper.switchOutLogic(pokemon);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public getFailedText(user: Pokemon, target: Pokemon, move: Move, cancelled: Utils.BooleanHolder): string | null {
|
||||||
|
return this.helper.getFailedText(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise<void> {
|
export function applyAbAttrs(attrType: Constructor<AbAttr>, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||||
return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), args, false, simulated);
|
return applyAbAttrsInternal<AbAttr>(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, simulated, cancelled, args), args, false, simulated);
|
||||||
}
|
}
|
||||||
@ -4866,6 +5153,11 @@ export function applyPostSetStatusAbAttrs(attrType: Constructor<PostSetStatusAbA
|
|||||||
return applyAbAttrsInternal<PostSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), args, false, simulated);
|
return applyAbAttrsInternal<PostSetStatusAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostSetStatus(pokemon, sourcePokemon, passive, effect, simulated, args), args, false, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPostDamageAbAttrs(attrType: Constructor<PostDamageAbAttr>,
|
||||||
|
pokemon: Pokemon, damage: number, passive: boolean, simulated: boolean = false, args: any[], source?: Pokemon): Promise<void> {
|
||||||
|
return applyAbAttrsInternal<PostDamageAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostDamage(pokemon, damage, passive, simulated, args, source), args);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies a field Stat multiplier attribute
|
* Applies a field Stat multiplier attribute
|
||||||
* @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
|
* @param attrType {@linkcode FieldMultiplyStatAbAttr} should always be FieldMultiplyBattleStatAbAttr for the time being
|
||||||
@ -4971,6 +5263,11 @@ export function applyPostFaintAbAttrs(attrType: Constructor<PostFaintAbAttr>,
|
|||||||
return applyAbAttrsInternal<PostFaintAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated);
|
return applyAbAttrsInternal<PostFaintAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostFaint(pokemon, passive, simulated, attacker, move, hitResult, args), args, false, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function applyPostItemLostAbAttrs(attrType: Constructor<PostItemLostAbAttr>,
|
||||||
|
pokemon: Pokemon, simulated: boolean = false, ...args: any[]): Promise<void> {
|
||||||
|
return applyAbAttrsInternal<PostItemLostAbAttr>(attrType, pokemon, (attr, passive) => attr.applyPostItemLost(pokemon, simulated, args), args);
|
||||||
|
}
|
||||||
|
|
||||||
function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
|
function queueShowAbility(pokemon: Pokemon, passive: boolean): void {
|
||||||
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, passive));
|
pokemon.scene.unshiftPhase(new ShowAbilityPhase(pokemon.scene, pokemon.id, passive));
|
||||||
pokemon.scene.clearPhaseQueueSplice();
|
pokemon.scene.clearPhaseQueueSplice();
|
||||||
@ -5062,7 +5359,8 @@ export function initAbilities() {
|
|||||||
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1)
|
.attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.SHIELD_DUST, 3)
|
new Ability(Abilities.SHIELD_DUST, 3)
|
||||||
.attr(IgnoreMoveEffectsAbAttr),
|
.attr(IgnoreMoveEffectsAbAttr)
|
||||||
|
.ignorable(),
|
||||||
new Ability(Abilities.OWN_TEMPO, 3)
|
new Ability(Abilities.OWN_TEMPO, 3)
|
||||||
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
|
.attr(BattlerTagImmunityAbAttr, BattlerTagType.CONFUSED)
|
||||||
.attr(IntimidateImmunityAbAttr)
|
.attr(IntimidateImmunityAbAttr)
|
||||||
@ -5116,6 +5414,7 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.ILLUMINATE, 3)
|
new Ability(Abilities.ILLUMINATE, 3)
|
||||||
.attr(ProtectStatAbAttr, Stat.ACC)
|
.attr(ProtectStatAbAttr, Stat.ACC)
|
||||||
.attr(DoubleBattleChanceAbAttr)
|
.attr(DoubleBattleChanceAbAttr)
|
||||||
|
.attr(IgnoreOpponentStatStagesAbAttr, [ Stat.EVA ])
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.TRACE, 3)
|
new Ability(Abilities.TRACE, 3)
|
||||||
.attr(PostSummonCopyAbilityAbAttr)
|
.attr(PostSummonCopyAbilityAbAttr)
|
||||||
@ -5180,11 +5479,9 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.CUTE_CHARM, 3)
|
new Ability(Abilities.CUTE_CHARM, 3)
|
||||||
.attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED),
|
.attr(PostDefendContactApplyTagChanceAbAttr, 30, BattlerTagType.INFATUATED),
|
||||||
new Ability(Abilities.PLUS, 3)
|
new Ability(Abilities.PLUS, 3)
|
||||||
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
|
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
||||||
.ignorable(),
|
|
||||||
new Ability(Abilities.MINUS, 3)
|
new Ability(Abilities.MINUS, 3)
|
||||||
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5)
|
.conditionalAttr(p => p.scene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
||||||
.ignorable(),
|
|
||||||
new Ability(Abilities.FORECAST, 3)
|
new Ability(Abilities.FORECAST, 3)
|
||||||
.attr(UncopiableAbilityAbAttr)
|
.attr(UncopiableAbilityAbAttr)
|
||||||
.attr(NoFusionAbilityAbAttr)
|
.attr(NoFusionAbilityAbAttr)
|
||||||
@ -5264,7 +5561,7 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.ANGER_POINT, 4)
|
new Ability(Abilities.ANGER_POINT, 4)
|
||||||
.attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6),
|
.attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6),
|
||||||
new Ability(Abilities.UNBURDEN, 4)
|
new Ability(Abilities.UNBURDEN, 4)
|
||||||
.unimplemented(),
|
.attr(PostItemLostApplyBattlerTagAbAttr, BattlerTagType.UNBURDEN),
|
||||||
new Ability(Abilities.HEATPROOF, 4)
|
new Ability(Abilities.HEATPROOF, 4)
|
||||||
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
|
.attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5)
|
||||||
.attr(ReduceBurnDamageAbAttr, 0.5)
|
.attr(ReduceBurnDamageAbAttr, 0.5)
|
||||||
@ -5337,7 +5634,7 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.FOREWARN, 4)
|
new Ability(Abilities.FOREWARN, 4)
|
||||||
.attr(ForewarnAbAttr),
|
.attr(ForewarnAbAttr),
|
||||||
new Ability(Abilities.UNAWARE, 4)
|
new Ability(Abilities.UNAWARE, 4)
|
||||||
.attr(IgnoreOpponentStatStagesAbAttr)
|
.attr(IgnoreOpponentStatStagesAbAttr, [ Stat.ATK, Stat.DEF, Stat.SPATK, Stat.SPDEF, Stat.ACC, Stat.EVA ])
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.TINTED_LENS, 4)
|
new Ability(Abilities.TINTED_LENS, 4)
|
||||||
.attr(DamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5),
|
.attr(DamageBoostAbAttr, 2, (user, target, move) => (target?.getMoveEffectiveness(user!, move) ?? 1) <= 0.5),
|
||||||
@ -5522,7 +5819,8 @@ export function initAbilities() {
|
|||||||
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTeravolt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => i18next.t("abilityTriggers:postSummonTeravolt", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }))
|
||||||
.attr(MoveAbilityBypassAbAttr),
|
.attr(MoveAbilityBypassAbAttr),
|
||||||
new Ability(Abilities.AROMA_VEIL, 6)
|
new Ability(Abilities.AROMA_VEIL, 6)
|
||||||
.attr(UserFieldBattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT, BattlerTagType.DISABLED, BattlerTagType.TORMENT, BattlerTagType.HEAL_BLOCK ]),
|
.attr(UserFieldBattlerTagImmunityAbAttr, [ BattlerTagType.INFATUATED, BattlerTagType.TAUNT, BattlerTagType.DISABLED, BattlerTagType.TORMENT, BattlerTagType.HEAL_BLOCK ])
|
||||||
|
.ignorable(),
|
||||||
new Ability(Abilities.FLOWER_VEIL, 6)
|
new Ability(Abilities.FLOWER_VEIL, 6)
|
||||||
.ignorable()
|
.ignorable()
|
||||||
.unimplemented(),
|
.unimplemented(),
|
||||||
@ -5607,11 +5905,11 @@ export function initAbilities() {
|
|||||||
new Ability(Abilities.STAMINA, 7)
|
new Ability(Abilities.STAMINA, 7)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => move.category !== MoveCategory.STATUS, Stat.DEF, 1),
|
||||||
new Ability(Abilities.WIMP_OUT, 7)
|
new Ability(Abilities.WIMP_OUT, 7)
|
||||||
.condition(getSheerForceHitDisableAbCondition())
|
.attr(PostDamageForceSwitchAbAttr)
|
||||||
.unimplemented(),
|
.edgeCase(), // Should not trigger when hurting itself in confusion
|
||||||
new Ability(Abilities.EMERGENCY_EXIT, 7)
|
new Ability(Abilities.EMERGENCY_EXIT, 7)
|
||||||
.condition(getSheerForceHitDisableAbCondition())
|
.attr(PostDamageForceSwitchAbAttr)
|
||||||
.unimplemented(),
|
.edgeCase(), // Should not trigger when hurting itself in confusion
|
||||||
new Ability(Abilities.WATER_COMPACTION, 7)
|
new Ability(Abilities.WATER_COMPACTION, 7)
|
||||||
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
.attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.WATER && move.category !== MoveCategory.STATUS, Stat.DEF, 2),
|
||||||
new Ability(Abilities.MERCILESS, 7)
|
new Ability(Abilities.MERCILESS, 7)
|
||||||
@ -5973,16 +6271,14 @@ export function initAbilities() {
|
|||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.SWORD_OF_RUIN, 9)
|
new Ability(Abilities.SWORD_OF_RUIN, 9)
|
||||||
.attr(FieldMultiplyStatAbAttr, Stat.DEF, 0.75)
|
.attr(FieldMultiplyStatAbAttr, Stat.DEF, 0.75)
|
||||||
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonSwordOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.DEF)) }))
|
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonSwordOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.DEF)) })),
|
||||||
.ignorable(),
|
|
||||||
new Ability(Abilities.TABLETS_OF_RUIN, 9)
|
new Ability(Abilities.TABLETS_OF_RUIN, 9)
|
||||||
.attr(FieldMultiplyStatAbAttr, Stat.ATK, 0.75)
|
.attr(FieldMultiplyStatAbAttr, Stat.ATK, 0.75)
|
||||||
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonTabletsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) }))
|
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonTabletsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.ATK)) }))
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
new Ability(Abilities.BEADS_OF_RUIN, 9)
|
new Ability(Abilities.BEADS_OF_RUIN, 9)
|
||||||
.attr(FieldMultiplyStatAbAttr, Stat.SPDEF, 0.75)
|
.attr(FieldMultiplyStatAbAttr, Stat.SPDEF, 0.75)
|
||||||
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonBeadsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPDEF)) }))
|
.attr(PostSummonMessageAbAttr, (user) => i18next.t("abilityTriggers:postSummonBeadsOfRuin", { pokemonNameWithAffix: getPokemonNameWithAffix(user), statName: i18next.t(getStatKey(Stat.SPDEF)) })),
|
||||||
.ignorable(),
|
|
||||||
new Ability(Abilities.ORICHALCUM_PULSE, 9)
|
new Ability(Abilities.ORICHALCUM_PULSE, 9)
|
||||||
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY)
|
.attr(PostSummonWeatherChangeAbAttr, WeatherType.SUNNY)
|
||||||
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY)
|
.attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.SUNNY)
|
||||||
|
@ -313,8 +313,8 @@ export class ConditionalProtectTag extends ArenaTag {
|
|||||||
* protection effect.
|
* protection effect.
|
||||||
* @param arena {@linkcode Arena} The arena containing the protection effect
|
* @param arena {@linkcode Arena} The arena containing the protection effect
|
||||||
* @param moveId {@linkcode Moves} The move to check against this condition
|
* @param moveId {@linkcode Moves} The move to check against this condition
|
||||||
* @returns `true` if the incoming move's priority is greater than 0. This includes
|
* @returns `true` if the incoming move's priority is greater than 0.
|
||||||
* moves with modified priorities from abilities (e.g. Prankster)
|
* This includes moves with modified priorities from abilities (e.g. Prankster)
|
||||||
*/
|
*/
|
||||||
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
||||||
const move = allMoves[moveId];
|
const move = allMoves[moveId];
|
||||||
@ -322,10 +322,12 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
|||||||
const effectPhase = arena.scene.getCurrentPhase();
|
const effectPhase = arena.scene.getCurrentPhase();
|
||||||
|
|
||||||
if (effectPhase instanceof MoveEffectPhase) {
|
if (effectPhase instanceof MoveEffectPhase) {
|
||||||
const attacker = effectPhase.getUserPokemon()!;
|
const attacker = effectPhase.getUserPokemon();
|
||||||
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
|
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
|
||||||
|
if (attacker) {
|
||||||
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
|
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return priority.value > 0;
|
return priority.value > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1203,6 +1205,24 @@ class GrassWaterPledgeTag extends ArenaTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arena Tag class for {@link https://bulbapedia.bulbagarden.net/wiki/Fairy_Lock_(move) Fairy Lock}.
|
||||||
|
* Fairy Lock prevents all Pokémon (except Ghost types) on the field from switching out or
|
||||||
|
* fleeing during their next turn.
|
||||||
|
* If a Pokémon that's on the field when Fairy Lock is used goes on to faint later in the same turn,
|
||||||
|
* the Pokémon that replaces it will still be unable to switch out in the following turn.
|
||||||
|
*/
|
||||||
|
export class FairyLockTag extends ArenaTag {
|
||||||
|
constructor(turnCount: number, sourceId: number) {
|
||||||
|
super(ArenaTagType.FAIRY_LOCK, turnCount, Moves.FAIRY_LOCK, sourceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
onAdd(arena: Arena): void {
|
||||||
|
arena.scene.queueMessage(i18next.t("arenaTag:fairyLockOnAdd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
|
// TODO: swap `sourceMove` and `sourceId` and make `sourceMove` an optional parameter
|
||||||
export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove: Moves | undefined, sourceId: number, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
||||||
switch (tagType) {
|
switch (tagType) {
|
||||||
@ -1261,6 +1281,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: number, sourceMove
|
|||||||
return new WaterFirePledgeTag(sourceId, side);
|
return new WaterFirePledgeTag(sourceId, side);
|
||||||
case ArenaTagType.GRASS_WATER_PLEDGE:
|
case ArenaTagType.GRASS_WATER_PLEDGE:
|
||||||
return new GrassWaterPledgeTag(sourceId, side);
|
return new GrassWaterPledgeTag(sourceId, side);
|
||||||
|
case ArenaTagType.FAIRY_LOCK:
|
||||||
|
return new FairyLockTag(turnCount, sourceId);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -478,7 +478,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
|||||||
],
|
],
|
||||||
[Species.NINCADA]: [
|
[Species.NINCADA]: [
|
||||||
new SpeciesEvolution(Species.NINJASK, 20, null, null),
|
new SpeciesEvolution(Species.NINJASK, 20, null, null),
|
||||||
new SpeciesEvolution(Species.SHEDINJA, 20, null, new SpeciesEvolutionCondition(p => p.scene.getParty().length < 6 && p.scene.pokeballCounts[PokeballType.POKEBALL] > 0))
|
new SpeciesEvolution(Species.SHEDINJA, 20, null, new SpeciesEvolutionCondition(p => p.scene.getPlayerParty().length < 6 && p.scene.pokeballCounts[PokeballType.POKEBALL] > 0))
|
||||||
],
|
],
|
||||||
[Species.WHISMUR]: [
|
[Species.WHISMUR]: [
|
||||||
new SpeciesEvolution(Species.LOUDRED, 20, null, null)
|
new SpeciesEvolution(Species.LOUDRED, 20, null, null)
|
||||||
@ -890,7 +890,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
|
|||||||
new SpeciesEvolution(Species.GOGOAT, 32, null, null)
|
new SpeciesEvolution(Species.GOGOAT, 32, null, null)
|
||||||
],
|
],
|
||||||
[Species.PANCHAM]: [
|
[Species.PANCHAM]: [
|
||||||
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
|
new SpeciesEvolution(Species.PANGORO, 32, null, new SpeciesEvolutionCondition(p => !!p.scene.getPlayerParty().find(p => p.getTypes(false, false, true).indexOf(Type.DARK) > -1)), SpeciesWildEvolutionDelay.MEDIUM)
|
||||||
],
|
],
|
||||||
[Species.ESPURR]: [
|
[Species.ESPURR]: [
|
||||||
new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)),
|
new SpeciesFormEvolution(Species.MEOWSTIC, "", "female", 25, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE)),
|
||||||
|
@ -428,7 +428,7 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent {
|
|||||||
moveAnim.bgSprite.setScale(1.25);
|
moveAnim.bgSprite.setScale(1.25);
|
||||||
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
moveAnim.bgSprite.setAlpha(this.opacity / 255);
|
||||||
scene.field.add(moveAnim.bgSprite);
|
scene.field.add(moveAnim.bgSprite);
|
||||||
const fieldPokemon = scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon();
|
const fieldPokemon = scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false);
|
||||||
if (!isNullOrUndefined(priority)) {
|
if (!isNullOrUndefined(priority)) {
|
||||||
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
|
scene.field.moveTo(moveAnim.bgSprite as Phaser.GameObjects.GameObject, priority);
|
||||||
} else if (fieldPokemon?.isOnField()) {
|
} else if (fieldPokemon?.isOnField()) {
|
||||||
@ -999,7 +999,7 @@ export abstract class BattleAnim {
|
|||||||
const setSpritePriority = (priority: integer) => {
|
const setSpritePriority = (priority: integer) => {
|
||||||
switch (priority) {
|
switch (priority) {
|
||||||
case 0:
|
case 0:
|
||||||
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getNonSwitchedEnemyPokemon() || scene.getNonSwitchedPlayerPokemon()!); // This bang assumes that if (the EnemyPokemon is undefined, then the PlayerPokemon function must return an object), correct assumption?
|
scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon(false) ?? scene.getPlayerPokemon(false)!); // TODO: is this bang correct?
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
|
scene.field.moveTo(moveSprite, scene.field.getAll().length - 1);
|
||||||
|
@ -1573,6 +1573,22 @@ export class AbilityBattlerTag extends BattlerTag {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tag used by Unburden to double speed
|
||||||
|
* @extends AbilityBattlerTag
|
||||||
|
*/
|
||||||
|
export class UnburdenTag extends AbilityBattlerTag {
|
||||||
|
constructor() {
|
||||||
|
super(BattlerTagType.UNBURDEN, Abilities.UNBURDEN, BattlerTagLapseType.CUSTOM, 1);
|
||||||
|
}
|
||||||
|
onAdd(pokemon: Pokemon): void {
|
||||||
|
super.onAdd(pokemon);
|
||||||
|
}
|
||||||
|
onRemove(pokemon: Pokemon): void {
|
||||||
|
super.onRemove(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class TruantTag extends AbilityBattlerTag {
|
export class TruantTag extends AbilityBattlerTag {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(BattlerTagType.TRUANT, Abilities.TRUANT, BattlerTagLapseType.MOVE, 1);
|
super(BattlerTagType.TRUANT, Abilities.TRUANT, BattlerTagLapseType.MOVE, 1);
|
||||||
@ -2480,7 +2496,10 @@ export class SubstituteTag extends BattlerTag {
|
|||||||
onHit(pokemon: Pokemon): void {
|
onHit(pokemon: Pokemon): void {
|
||||||
const moveEffectPhase = pokemon.scene.getCurrentPhase();
|
const moveEffectPhase = pokemon.scene.getCurrentPhase();
|
||||||
if (moveEffectPhase instanceof MoveEffectPhase) {
|
if (moveEffectPhase instanceof MoveEffectPhase) {
|
||||||
const attacker = moveEffectPhase.getUserPokemon()!;
|
const attacker = moveEffectPhase.getUserPokemon();
|
||||||
|
if (!attacker) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const move = moveEffectPhase.move.getMove();
|
const move = moveEffectPhase.move.getMove();
|
||||||
const firstHit = (attacker.turnData.hitCount === attacker.turnData.hitsLeft);
|
const firstHit = (attacker.turnData.hitCount === attacker.turnData.hitsLeft);
|
||||||
|
|
||||||
@ -2934,6 +2953,8 @@ export function getBattlerTag(tagType: BattlerTagType, turnCount: number, source
|
|||||||
return new ThroatChoppedTag();
|
return new ThroatChoppedTag();
|
||||||
case BattlerTagType.GORILLA_TACTICS:
|
case BattlerTagType.GORILLA_TACTICS:
|
||||||
return new GorillaTacticsTag();
|
return new GorillaTacticsTag();
|
||||||
|
case BattlerTagType.UNBURDEN:
|
||||||
|
return new UnburdenTag();
|
||||||
case BattlerTagType.SUBSTITUTE:
|
case BattlerTagType.SUBSTITUTE:
|
||||||
return new SubstituteTag(sourceMove, sourceId);
|
return new SubstituteTag(sourceMove, sourceId);
|
||||||
case BattlerTagType.AUTOTOMIZED:
|
case BattlerTagType.AUTOTOMIZED:
|
||||||
|
@ -2,7 +2,7 @@ import { getPokemonNameWithAffix } from "../messages";
|
|||||||
import Pokemon, { HitResult } from "../field/pokemon";
|
import Pokemon, { HitResult } from "../field/pokemon";
|
||||||
import { getStatusEffectHealText } from "./status-effect";
|
import { getStatusEffectHealText } from "./status-effect";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { DoubleBerryEffectAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs } from "./ability";
|
import { DoubleBerryEffectAbAttr, PostItemLostAbAttr, ReduceBerryUseThresholdAbAttr, applyAbAttrs, applyPostItemLostAbAttrs } from "./ability";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
@ -75,6 +75,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, hpHealed);
|
||||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
||||||
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
|
hpHealed.value, i18next.t("battle:hpHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), berryName: getBerryName(berryType) }), true));
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
};
|
};
|
||||||
case BerryType.LUM:
|
case BerryType.LUM:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
@ -86,6 +87,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
}
|
}
|
||||||
pokemon.resetStatus(true, true);
|
pokemon.resetStatus(true, true);
|
||||||
pokemon.updateInfo();
|
pokemon.updateInfo();
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
};
|
};
|
||||||
case BerryType.LIECHI:
|
case BerryType.LIECHI:
|
||||||
case BerryType.GANLON:
|
case BerryType.GANLON:
|
||||||
@ -101,6 +103,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
const statStages = new Utils.NumberHolder(1);
|
const statStages = new Utils.NumberHolder(1);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, statStages);
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ stat ], statStages.value));
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
};
|
};
|
||||||
case BerryType.LANSAT:
|
case BerryType.LANSAT:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
@ -108,6 +111,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
pokemon.battleData.berriesEaten.push(berryType);
|
pokemon.battleData.berriesEaten.push(berryType);
|
||||||
}
|
}
|
||||||
pokemon.addTag(BattlerTagType.CRIT_BOOST);
|
pokemon.addTag(BattlerTagType.CRIT_BOOST);
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
};
|
};
|
||||||
case BerryType.STARF:
|
case BerryType.STARF:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
@ -118,6 +122,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
const stages = new Utils.NumberHolder(2);
|
const stages = new Utils.NumberHolder(2);
|
||||||
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
|
applyAbAttrs(DoubleBerryEffectAbAttr, pokemon, null, false, stages);
|
||||||
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value));
|
pokemon.scene.unshiftPhase(new StatStageChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ randStat ], stages.value));
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
};
|
};
|
||||||
case BerryType.LEPPA:
|
case BerryType.LEPPA:
|
||||||
return (pokemon: Pokemon) => {
|
return (pokemon: Pokemon) => {
|
||||||
@ -128,6 +133,7 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
|
|||||||
if (ppRestoreMove !== undefined) {
|
if (ppRestoreMove !== undefined) {
|
||||||
ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
|
ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
|
||||||
pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) }));
|
pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) }));
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, pokemon, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
140
src/data/move.ts
140
src/data/move.ts
@ -8,7 +8,7 @@ import { Constructor, NumberHolder } from "#app/utils";
|
|||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { WeatherType } from "./weather";
|
import { WeatherType } from "./weather";
|
||||||
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
import { ArenaTagSide, ArenaTrapTag, WeakenMoveTypeTag } from "./arena-tag";
|
||||||
import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, InfiltratorAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability";
|
import { allAbilities, AllyMoveCategoryPowerBoostAbAttr, applyAbAttrs, applyPostAttackAbAttrs, applyPostItemLostAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, BlockItemTheftAbAttr, BlockNonDirectDamageAbAttr, BlockOneHitKOAbAttr, BlockRecoilDamageAttr, ConfusionOnStatusEffectAbAttr, FieldMoveTypePowerBoostAbAttr, FieldPreventExplosiveMovesAbAttr, ForceSwitchOutImmunityAbAttr, HealFromBerryUseAbAttr, IgnoreContactAbAttr, IgnoreMoveEffectsAbAttr, IgnoreProtectOnContactAbAttr, InfiltratorAbAttr, MaxMultiHitAbAttr, MoveAbilityBypassAbAttr, MoveEffectChanceMultiplierAbAttr, MoveTypeChangeAbAttr, PostDamageForceSwitchAbAttr, PostItemLostAbAttr, ReverseDrainAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, UnswappableAbilityAbAttr, UserFieldMoveTypePowerBoostAbAttr, VariableMovePowerAbAttr, WonderSkinAbAttr } from "./ability";
|
||||||
import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier";
|
import { AttackTypeBoosterModifier, BerryModifier, PokemonHeldItemModifier, PokemonMoveAccuracyBoosterModifier, PokemonMultiHitModifier, PreserveBerryModifier } from "../modifier/modifier";
|
||||||
import { BattlerIndex, BattleType } from "../battle";
|
import { BattlerIndex, BattleType } from "../battle";
|
||||||
import { TerrainType } from "./terrain";
|
import { TerrainType } from "./terrain";
|
||||||
@ -1474,8 +1474,8 @@ export class RecoilAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const damageValue = (!this.useHp ? user.turnData.damageDealt : user.getMaxHp()) * this.damageRatio;
|
const damageValue = (!this.useHp ? user.turnData.totalDamageDealt : user.getMaxHp()) * this.damageRatio;
|
||||||
const minValue = user.turnData.damageDealt ? 1 : 0;
|
const minValue = user.turnData.totalDamageDealt ? 1 : 0;
|
||||||
const recoilDamage = Utils.toDmgValue(damageValue, minValue);
|
const recoilDamage = Utils.toDmgValue(damageValue, minValue);
|
||||||
if (!recoilDamage) {
|
if (!recoilDamage) {
|
||||||
return false;
|
return false;
|
||||||
@ -1743,7 +1743,7 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
|
|||||||
if (!this.canApply(user, target, move, args)) {
|
if (!this.canApply(user, target, move, args)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const partyPokemon = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const partyPokemon = user.isPlayer() ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
partyPokemon.forEach(p => this.cureStatus(p, user.id));
|
partyPokemon.forEach(p => this.cureStatus(p, user.id));
|
||||||
|
|
||||||
if (this.message) {
|
if (this.message) {
|
||||||
@ -1815,7 +1815,7 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We don't know which party member will be chosen, so pick the highest max HP in the party
|
// We don't know which party member will be chosen, so pick the highest max HP in the party
|
||||||
const maxPartyMemberHp = user.scene.getParty().map(p => p.getMaxHp()).reduce((maxHp: integer, hp: integer) => Math.max(hp, maxHp), 0);
|
const maxPartyMemberHp = user.scene.getPlayerParty().map(p => p.getMaxHp()).reduce((maxHp: integer, hp: integer) => Math.max(hp, maxHp), 0);
|
||||||
|
|
||||||
user.scene.pushPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
|
user.scene.pushPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
|
||||||
maxPartyMemberHp, i18next.t("moveTriggers:sacrificialFullRestore", { pokemonName: getPokemonNameWithAffix(user) }), true, false, false, true), true);
|
maxPartyMemberHp, i18next.t("moveTriggers:sacrificialFullRestore", { pokemonName: getPokemonNameWithAffix(user) }), true, false, false, true), true);
|
||||||
@ -1828,7 +1828,7 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user, target, move) => user.scene.getParty().filter(p => p.isActive()).length > user.scene.currentBattle.getBattlerCount();
|
return (user, _target, _move) => user.scene.getPlayerParty().filter(p => p.isActive()).length > user.scene.currentBattle.getBattlerCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2006,7 +2006,7 @@ export class HitHealAttr extends MoveEffectAttr {
|
|||||||
message = i18next.t("battle:drainMessage", { pokemonName: getPokemonNameWithAffix(target) });
|
message = i18next.t("battle:drainMessage", { pokemonName: getPokemonNameWithAffix(target) });
|
||||||
} else {
|
} else {
|
||||||
// Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc.
|
// Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc.
|
||||||
healAmount = Utils.toDmgValue(user.turnData.currDamageDealt * this.healRatio);
|
healAmount = Utils.toDmgValue(user.turnData.singleHitDamageDealt * this.healRatio);
|
||||||
message = i18next.t("battle:regainHealth", { pokemonName: getPokemonNameWithAffix(user) });
|
message = i18next.t("battle:regainHealth", { pokemonName: getPokemonNameWithAffix(user) });
|
||||||
}
|
}
|
||||||
if (reverseDrain) {
|
if (reverseDrain) {
|
||||||
@ -2158,7 +2158,7 @@ export class MultiHitAttr extends MoveAttr {
|
|||||||
case MultiHitType._10:
|
case MultiHitType._10:
|
||||||
return 10;
|
return 10;
|
||||||
case MultiHitType.BEAT_UP:
|
case MultiHitType.BEAT_UP:
|
||||||
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party = user.isPlayer() ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
// No status means the ally pokemon can contribute to Beat Up
|
// No status means the ally pokemon can contribute to Beat Up
|
||||||
return party.reduce((total, pokemon) => {
|
return party.reduce((total, pokemon) => {
|
||||||
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
|
return total + (pokemon.id === user.id ? 1 : pokemon?.status && pokemon.status.effect !== StatusEffect.NONE ? 0 : 1);
|
||||||
@ -2402,6 +2402,8 @@ export class RemoveHeldItemAttr extends MoveEffectAttr {
|
|||||||
// Decrease item amount and update icon
|
// Decrease item amount and update icon
|
||||||
!--removedItem.stackCount;
|
!--removedItem.stackCount;
|
||||||
target.scene.updateModifiers(target.isPlayer());
|
target.scene.updateModifiers(target.isPlayer());
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false);
|
||||||
|
|
||||||
|
|
||||||
if (this.berriesOnly) {
|
if (this.berriesOnly) {
|
||||||
user.scene.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name }));
|
user.scene.queueMessage(i18next.t("moveTriggers:incineratedItem", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), itemName: removedItem.type.name }));
|
||||||
@ -2481,6 +2483,7 @@ export class EatBerryAttr extends MoveEffectAttr {
|
|||||||
eatBerry(consumer: Pokemon) {
|
eatBerry(consumer: Pokemon) {
|
||||||
getBerryEffectFunc(this.chosenBerry!.berryType)(consumer); // consumer eats the berry
|
getBerryEffectFunc(this.chosenBerry!.berryType)(consumer); // consumer eats the berry
|
||||||
applyAbAttrs(HealFromBerryUseAbAttr, consumer, new Utils.BooleanHolder(false));
|
applyAbAttrs(HealFromBerryUseAbAttr, consumer, new Utils.BooleanHolder(false));
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, consumer, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2516,6 +2519,7 @@ export class StealEatBerryAttr extends EatBerryAttr {
|
|||||||
}
|
}
|
||||||
// if the target has berries, pick a random berry and steal it
|
// if the target has berries, pick a random berry and steal it
|
||||||
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)];
|
this.chosenBerry = heldBerries[user.randSeedInt(heldBerries.length)];
|
||||||
|
applyPostItemLostAbAttrs(PostItemLostAbAttr, target, false);
|
||||||
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
|
const message = i18next.t("battle:stealEatBerry", { pokemonName: user.name, targetName: target.name, berryName: this.chosenBerry.type.name });
|
||||||
user.scene.queueMessage(message);
|
user.scene.queueMessage(message);
|
||||||
this.reduceBerryModifier(target);
|
this.reduceBerryModifier(target);
|
||||||
@ -3467,7 +3471,7 @@ export class MovePowerMultiplierAttr extends VariablePowerAttr {
|
|||||||
* @returns The base power of the Beat Up hit.
|
* @returns The base power of the Beat Up hit.
|
||||||
*/
|
*/
|
||||||
const beatUpFunc = (user: Pokemon, allyIndex: number): number => {
|
const beatUpFunc = (user: Pokemon, allyIndex: number): number => {
|
||||||
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party = user.isPlayer() ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
|
|
||||||
for (let i = allyIndex; i < party.length; i++) {
|
for (let i = allyIndex; i < party.length; i++) {
|
||||||
const pokemon = party[i];
|
const pokemon = party[i];
|
||||||
@ -3495,7 +3499,7 @@ export class BeatUpAttr extends VariablePowerAttr {
|
|||||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
const power = args[0] as Utils.NumberHolder;
|
const power = args[0] as Utils.NumberHolder;
|
||||||
|
|
||||||
const party = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party = user.isPlayer() ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
const allyCount = party.filter(pokemon => {
|
const allyCount = party.filter(pokemon => {
|
||||||
return pokemon.id === user.id || !pokemon.status?.effect;
|
return pokemon.id === user.id || !pokemon.status?.effect;
|
||||||
}).length;
|
}).length;
|
||||||
@ -4157,6 +4161,60 @@ export class CombinedPledgeStabBoostAttr extends MoveAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable Power attribute for {@link https://bulbapedia.bulbagarden.net/wiki/Round_(move) | Round}.
|
||||||
|
* Doubles power if another Pokemon has previously selected Round this turn.
|
||||||
|
* @extends VariablePowerAttr
|
||||||
|
*/
|
||||||
|
export class RoundPowerAttr extends VariablePowerAttr {
|
||||||
|
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||||
|
const power = args[0];
|
||||||
|
if (!(power instanceof Utils.NumberHolder)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.turnData?.joinedRound) {
|
||||||
|
power.value *= 2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute for the "combo" effect of {@link https://bulbapedia.bulbagarden.net/wiki/Round_(move) | Round}.
|
||||||
|
* Preempts the next move in the turn order with the first instance of any Pokemon
|
||||||
|
* using Round. Also marks the Pokemon using the cued Round to double the move's power.
|
||||||
|
* @extends MoveEffectAttr
|
||||||
|
* @see {@linkcode RoundPowerAttr}
|
||||||
|
*/
|
||||||
|
export class CueNextRoundAttr extends MoveEffectAttr {
|
||||||
|
constructor() {
|
||||||
|
super(true, { lastHitOnly: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
override apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean {
|
||||||
|
const nextRoundPhase = user.scene.findPhase<MovePhase>(phase =>
|
||||||
|
phase instanceof MovePhase && phase.move.moveId === Moves.ROUND
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!nextRoundPhase) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the phase queue so that the next Pokemon using Round moves next
|
||||||
|
const nextRoundIndex = user.scene.phaseQueue.indexOf(nextRoundPhase);
|
||||||
|
const nextMoveIndex = user.scene.phaseQueue.findIndex(phase => phase instanceof MovePhase);
|
||||||
|
if (nextRoundIndex !== nextMoveIndex) {
|
||||||
|
user.scene.prependToPhase(user.scene.phaseQueue.splice(nextRoundIndex, 1)[0], MovePhase);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark the corresponding Pokemon as having "joined the Round" (for doubling power later)
|
||||||
|
nextRoundPhase.pokemon.turnData.joinedRound = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class VariableAtkAttr extends MoveAttr {
|
export class VariableAtkAttr extends MoveAttr {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@ -4465,7 +4523,7 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(Species.ARCEUS) || [ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(Species.SILVALLY)) {
|
if ([ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(Species.ARCEUS) || [ user.species.speciesId, user.fusionSpecies?.speciesId ].includes(Species.SILVALLY)) {
|
||||||
const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!; // TODO: is this bang correct?
|
const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!;
|
||||||
|
|
||||||
moveType.value = Type[Type[form]];
|
moveType.value = Type[Type[form]];
|
||||||
return true;
|
return true;
|
||||||
@ -5720,7 +5778,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
// If user is player, checks if the user has fainted pokemon
|
// If user is player, checks if the user has fainted pokemon
|
||||||
if (user instanceof PlayerPokemon
|
if (user instanceof PlayerPokemon
|
||||||
&& user.scene.getParty().findIndex(p => p.isFainted()) > -1) {
|
&& user.scene.getPlayerParty().findIndex(p => p.isFainted()) > -1) {
|
||||||
(user as PlayerPokemon).revivalBlessing().then(() => {
|
(user as PlayerPokemon).revivalBlessing().then(() => {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
@ -5761,6 +5819,7 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class ForceSwitchOutAttr extends MoveEffectAttr {
|
export class ForceSwitchOutAttr extends MoveEffectAttr {
|
||||||
constructor(
|
constructor(
|
||||||
private selfSwitch: boolean = false,
|
private selfSwitch: boolean = false,
|
||||||
@ -5779,14 +5838,21 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Move the switch out logic inside the conditional block
|
|
||||||
* This ensures that the switch out only happens when the conditions are met
|
|
||||||
*/
|
|
||||||
const switchOutTarget = this.selfSwitch ? user : target;
|
const switchOutTarget = this.selfSwitch ? user : target;
|
||||||
if (switchOutTarget instanceof PlayerPokemon) {
|
if (switchOutTarget instanceof PlayerPokemon) {
|
||||||
|
/**
|
||||||
|
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||||
|
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||||
|
*/
|
||||||
|
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) &&
|
||||||
|
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH || move.id === Moves.FLIP_TURN)
|
||||||
|
) {
|
||||||
|
if (this.hpDroppedBelowHalf(target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Switch out logic for the player's Pokemon
|
// Switch out logic for the player's Pokemon
|
||||||
if (switchOutTarget.scene.getParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
if (switchOutTarget.scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.isOnField()).length < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5810,6 +5876,17 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
false, false), MoveEndPhase);
|
false, false), MoveEndPhase);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
/**
|
||||||
|
* Check if Wimp Out/Emergency Exit activates due to being hit by U-turn or Volt Switch
|
||||||
|
* If it did, the user of U-turn or Volt Switch will not be switched out.
|
||||||
|
*/
|
||||||
|
if (target.getAbility().hasAttr(PostDamageForceSwitchAbAttr) &&
|
||||||
|
(move.id === Moves.U_TURN || move.id === Moves.VOLT_SWITCH) || move.id === Moves.FLIP_TURN) {
|
||||||
|
if (this.hpDroppedBelowHalf(target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Switch out logic for everything else (eg: WILD battles)
|
// Switch out logic for everything else (eg: WILD battles)
|
||||||
if (user.scene.currentBattle.waveIndex % 10 === 0) {
|
if (user.scene.currentBattle.waveIndex % 10 === 0) {
|
||||||
return false;
|
return false;
|
||||||
@ -5884,7 +5961,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const party = player ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party = player ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
return (!player && !user.scene.currentBattle.battleType)
|
return (!player && !user.scene.currentBattle.battleType)
|
||||||
|| party.filter(p => p.isAllowedInBattle()
|
|| party.filter(p => p.isAllowedInBattle()
|
||||||
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
|
&& (player || (p as EnemyPokemon).trainerSlot === (switchOutTarget as EnemyPokemon).trainerSlot)).length > user.scene.currentBattle.getBattlerCount();
|
||||||
@ -5902,6 +5979,21 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to check if the Pokémon's health is below half after taking damage.
|
||||||
|
* Used for an edge case interaction with Wimp Out/Emergency Exit.
|
||||||
|
* If the Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out.
|
||||||
|
*/
|
||||||
|
hpDroppedBelowHalf(target: Pokemon): boolean {
|
||||||
|
const pokemonHealth = target.hp;
|
||||||
|
const maxPokemonHealth = target.getMaxHp();
|
||||||
|
const damageTaken = target.turnData.damageTaken;
|
||||||
|
const initialHealth = pokemonHealth + damageTaken;
|
||||||
|
|
||||||
|
// Check if the Pokémon's health has dropped below half after the damage
|
||||||
|
return initialHealth >= maxPokemonHealth / 2 && pokemonHealth < maxPokemonHealth / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ChillyReceptionAttr extends ForceSwitchOutAttr {
|
export class ChillyReceptionAttr extends ForceSwitchOutAttr {
|
||||||
@ -7220,7 +7312,7 @@ const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target
|
|||||||
const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => user.scene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined;
|
const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => user.scene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined;
|
||||||
|
|
||||||
const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => {
|
||||||
const party: Pokemon[] = user.isPlayer() ? user.scene.getParty() : user.scene.getEnemyParty();
|
const party: Pokemon[] = user.isPlayer() ? user.scene.getPlayerParty() : user.scene.getEnemyParty();
|
||||||
return party.some(pokemon => pokemon.isActive() && !pokemon.isOnField());
|
return party.some(pokemon => pokemon.isActive() && !pokemon.isOnField());
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -8922,8 +9014,9 @@ export function initMoves() {
|
|||||||
.condition((user, target, move) => !target.turnData.acted)
|
.condition((user, target, move) => !target.turnData.acted)
|
||||||
.attr(AfterYouAttr),
|
.attr(AfterYouAttr),
|
||||||
new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
|
new AttackMove(Moves.ROUND, Type.NORMAL, MoveCategory.SPECIAL, 60, 100, 15, -1, 0, 5)
|
||||||
.soundBased()
|
.attr(CueNextRoundAttr)
|
||||||
.partial(), // No effect implemented
|
.attr(RoundPowerAttr)
|
||||||
|
.soundBased(),
|
||||||
new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5)
|
new AttackMove(Moves.ECHOED_VOICE, Type.NORMAL, MoveCategory.SPECIAL, 40, 100, 15, -1, 0, 5)
|
||||||
.attr(ConsecutiveUseMultiBasePowerAttr, 5, false)
|
.attr(ConsecutiveUseMultiBasePowerAttr, 5, false)
|
||||||
.soundBased(),
|
.soundBased(),
|
||||||
@ -9150,7 +9243,7 @@ export function initMoves() {
|
|||||||
.target(MoveTarget.ALL)
|
.target(MoveTarget.ALL)
|
||||||
.condition((user, target, move) => {
|
.condition((user, target, move) => {
|
||||||
// If any fielded pokémon is grass-type and grounded.
|
// If any fielded pokémon is grass-type and grounded.
|
||||||
return [ ...user.scene.getEnemyParty(), ...user.scene.getParty() ].some((poke) => poke.isOfType(Type.GRASS) && poke.isGrounded());
|
return [ ...user.scene.getEnemyParty(), ...user.scene.getPlayerParty() ].some((poke) => poke.isOfType(Type.GRASS) && poke.isGrounded());
|
||||||
})
|
})
|
||||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded() }),
|
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => target.isOfType(Type.GRASS) && target.isGrounded() }),
|
||||||
new StatusMove(Moves.STICKY_WEB, Type.BUG, -1, 20, -1, 0, 6)
|
new StatusMove(Moves.STICKY_WEB, Type.BUG, -1, 20, -1, 0, 6)
|
||||||
@ -9224,8 +9317,9 @@ export function initMoves() {
|
|||||||
.target(MoveTarget.ALL_NEAR_OTHERS),
|
.target(MoveTarget.ALL_NEAR_OTHERS),
|
||||||
new StatusMove(Moves.FAIRY_LOCK, Type.FAIRY, -1, 10, -1, 0, 6)
|
new StatusMove(Moves.FAIRY_LOCK, Type.FAIRY, -1, 10, -1, 0, 6)
|
||||||
.ignoresSubstitute()
|
.ignoresSubstitute()
|
||||||
|
.ignoresProtect()
|
||||||
.target(MoveTarget.BOTH_SIDES)
|
.target(MoveTarget.BOTH_SIDES)
|
||||||
.unimplemented(),
|
.attr(AddArenaTagAttr, ArenaTagType.FAIRY_LOCK, 2, true),
|
||||||
new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6)
|
new SelfStatusMove(Moves.KINGS_SHIELD, Type.STEEL, -1, 10, -1, 4, 6)
|
||||||
.attr(ProtectAttr, BattlerTagType.KINGS_SHIELD)
|
.attr(ProtectAttr, BattlerTagType.KINGS_SHIELD)
|
||||||
.condition(failIfLastCondition),
|
.condition(failIfLastCondition),
|
||||||
|
@ -181,7 +181,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Sort berries by party member ID to more easily re-add later if necessary
|
// Sort berries by party member ID to more easily re-add later if necessary
|
||||||
const berryItemsMap = new Map<number, BerryModifier[]>();
|
const berryItemsMap = new Map<number, BerryModifier[]>();
|
||||||
scene.getParty().forEach(pokemon => {
|
scene.getPlayerParty().forEach(pokemon => {
|
||||||
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
|
const pokemonBerries = berryItems.filter(b => b.pokemonId === pokemon.id);
|
||||||
if (pokemonBerries?.length > 0) {
|
if (pokemonBerries?.length > 0) {
|
||||||
berryItemsMap.set(pokemon.id, pokemonBerries);
|
berryItemsMap.set(pokemon.id, pokemonBerries);
|
||||||
@ -267,7 +267,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
|||||||
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
|
const revSeed = generateModifierType(scene, modifierTypes.REVIVER_SEED);
|
||||||
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
|
encounter.setDialogueToken("foodReward", revSeed?.name ?? i18next.t("modifierType:ModifierType.REVIVER_SEED.name"));
|
||||||
const givePartyPokemonReviverSeeds = () => {
|
const givePartyPokemonReviverSeeds = () => {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
party.forEach(p => {
|
party.forEach(p => {
|
||||||
const heldItems = p.getHeldItems();
|
const heldItems = p.getHeldItems();
|
||||||
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
|
if (revSeed && !heldItems.some(item => item instanceof PokemonInstantReviveModifier)) {
|
||||||
@ -308,7 +308,7 @@ export const AbsoluteAvariceEncounter: MysteryEncounter =
|
|||||||
const berryMap = encounter.misc.berryItemsMap;
|
const berryMap = encounter.misc.berryItemsMap;
|
||||||
|
|
||||||
// Returns 2/5 of the berries stolen to each Pokemon
|
// Returns 2/5 of the berries stolen to each Pokemon
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
party.forEach(pokemon => {
|
party.forEach(pokemon => {
|
||||||
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
const stolenBerries: BerryModifier[] = berryMap.get(pokemon.id);
|
||||||
const berryTypesAsArray: BerryType[] = [];
|
const berryTypesAsArray: BerryType[] = [];
|
||||||
|
@ -58,7 +58,7 @@ 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.getParty()), true);
|
const 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 = {
|
||||||
@ -77,7 +77,7 @@ export const BerriesAboundEncounter: MysteryEncounter =
|
|||||||
scene.currentBattle.waveIndex > 160 ? 7
|
scene.currentBattle.waveIndex > 160 ? 7
|
||||||
: scene.currentBattle.waveIndex > 120 ? 5
|
: scene.currentBattle.waveIndex > 120 ? 5
|
||||||
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
|
: scene.currentBattle.waveIndex > 40 ? 4 : 2;
|
||||||
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||||
encounter.misc = { numBerries };
|
encounter.misc = { numBerries };
|
||||||
|
|
||||||
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
const { spriteKey, fileRoot } = getSpriteKeysFromPokemon(bossPokemon);
|
||||||
@ -253,7 +253,7 @@ function tryGiveBerry(scene: BattleScene, prioritizedPokemon?: PlayerPokemon) {
|
|||||||
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
|
const berryType = randSeedInt(Object.keys(BerryType).filter(s => !isNaN(Number(s))).length) as BerryType;
|
||||||
const berry = generateModifierType(scene, modifierTypes.BERRY, [ berryType ]) as BerryModifierType;
|
const berry = generateModifierType(scene, modifierTypes.BERRY, [ berryType ]) as BerryModifierType;
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
|
|
||||||
// Will try to apply to prioritized pokemon first, then do normal application method if it fails
|
// Will try to apply to prioritized pokemon first, then do normal application method if it fails
|
||||||
if (prioritizedPokemon) {
|
if (prioritizedPokemon) {
|
||||||
|
@ -331,7 +331,7 @@ export const BugTypeSuperfanEncounter: MysteryEncounter =
|
|||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
// Player gets different rewards depending on the number of bug types they have
|
// Player gets different rewards depending on the number of bug types they have
|
||||||
const numBugTypes = scene.getParty().filter(p => p.isOfType(Type.BUG, true)).length;
|
const numBugTypes = scene.getPlayerParty().filter(p => p.isOfType(Type.BUG, true)).length;
|
||||||
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
|
const numBugTypesText = i18next.t(`${namespace}:numBugTypes`, { count: numBugTypes });
|
||||||
encounter.setDialogueToken("numBugTypes", numBugTypesText);
|
encounter.setDialogueToken("numBugTypes", numBugTypesText);
|
||||||
|
|
||||||
|
@ -245,7 +245,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
// So Vitamins, form change items, etc. are not included
|
// So Vitamins, form change items, etc. are not included
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let mostHeldItemsPokemon = party[0];
|
let mostHeldItemsPokemon = party[0];
|
||||||
let count = mostHeldItemsPokemon.getHeldItems()
|
let count = mostHeldItemsPokemon.getHeldItems()
|
||||||
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
.filter(m => m.isTransferable && !(m instanceof BerryModifier))
|
||||||
@ -328,7 +328,7 @@ export const ClowningAroundEncounter: MysteryEncounter =
|
|||||||
.withPreOptionPhase(async (scene: BattleScene) => {
|
.withPreOptionPhase(async (scene: BattleScene) => {
|
||||||
// Randomize the second type of all player's pokemon
|
// Randomize the second type of all player's pokemon
|
||||||
// If the pokemon does not normally have a second type, it will gain 1
|
// If the pokemon does not normally have a second type, it will gain 1
|
||||||
for (const pokemon of scene.getParty()) {
|
for (const pokemon of scene.getPlayerParty()) {
|
||||||
const originalTypes = pokemon.getTypes(false, false, true);
|
const originalTypes = pokemon.getTypes(false, false, true);
|
||||||
|
|
||||||
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
|
// If the Pokemon has non-status moves that don't match the Pokemon's type, prioritizes those as the new type
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
|
||||||
import { Biome } from "#enums/biome";
|
|
||||||
import { EncounterBattleAnim } from "#app/data/battle-anims";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
||||||
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { MoveRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
import { DANCING_MOVES } from "#app/data/mystery-encounters/requirements/requirement-groups";
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import { getEncounterText, queueEncounterMessage } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { catchPokemon, getEncounterPokemonLevelForWave, STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
|
import Pokemon, { EnemyPokemon, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
import { modifierTypes } from "#app/modifier/modifier-type";
|
import { modifierTypes } from "#app/modifier/modifier-type";
|
||||||
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
import { LearnMovePhase } from "#app/phases/learn-move-phase";
|
||||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||||
import { Stat } from "#enums/stat";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Biome } from "#enums/biome";
|
||||||
import { EncounterAnim } from "#enums/encounter-anims";
|
import { EncounterAnim } from "#enums/encounter-anims";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { Moves } from "#enums/moves";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { PokeballType } from "#enums/pokeball";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
@ -92,7 +92,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||||||
.withCatchAllowed(true)
|
.withCatchAllowed(true)
|
||||||
.withFleeAllowed(false)
|
.withFleeAllowed(false)
|
||||||
.withOnVisualsStart((scene: BattleScene) => {
|
.withOnVisualsStart((scene: BattleScene) => {
|
||||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getParty()[0]);
|
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon()!);
|
||||||
danceAnim.play(scene);
|
danceAnim.play(scene);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -217,7 +217,7 @@ export const DancingLessonsEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
encounter.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
scene.unshiftPhase(new LearnMovePhase(scene, scene.getParty().indexOf(pokemon), Moves.REVELATION_DANCE));
|
scene.unshiftPhase(new LearnMovePhase(scene, scene.getPlayerParty().indexOf(pokemon), Moves.REVELATION_DANCE));
|
||||||
|
|
||||||
// Play animation again to "learn" the dance
|
// Play animation again to "learn" the dance
|
||||||
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
|
const danceAnim = new EncounterBattleAnim(EncounterAnim.DANCE, scene.getEnemyPokemon()!, scene.getPlayerPokemon());
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
import { CombinationPokemonRequirement, HeldItemRequirement, MoneyRequirement } from "#app/data/mystery-encounters/mystery-encounter-requirements";
|
||||||
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
import { getEncounterText, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { generateModifierType, leaveEncounterWithoutBattle, selectPokemonForOption, updatePlayerMoney, } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
|
||||||
import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
|
||||||
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
|
||||||
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { applyModifierTypeToPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import i18next from "#app/plugins/i18n";
|
|
||||||
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { BerryModifier, HealingBoosterModifier, LevelIncrementBoosterModifier, MoneyMultiplierModifier, PokemonHeldItemModifier, PreserveBerryModifier } from "#app/modifier/modifier";
|
||||||
|
import { modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { ModifierRewardPhase } from "#app/phases/modifier-reward-phase";
|
||||||
|
import i18next from "#app/plugins/i18n";
|
||||||
|
import { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/delibirdy";
|
const namespace = "mysteryEncounters/delibirdy";
|
||||||
@ -133,7 +133,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
// At max stacks, give the first party pokemon a Shell Bell instead
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], 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);
|
||||||
} else {
|
} else {
|
||||||
@ -207,7 +207,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
// At max stacks, give the first party pokemon a Shell Bell instead
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], 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);
|
||||||
} else {
|
} else {
|
||||||
@ -220,7 +220,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
// At max stacks, give the first party pokemon a Shell Bell instead
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[0], 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);
|
||||||
} else {
|
} else {
|
||||||
@ -299,7 +299,7 @@ export const DelibirdyEncounter: MysteryEncounter =
|
|||||||
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
if (existing && existing.getStackCount() >= existing.getMaxStackCount(scene)) {
|
||||||
// At max stacks, give the first party pokemon a Shell Bell instead
|
// At max stacks, give the first party pokemon a Shell Bell instead
|
||||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
await applyModifierTypeToPlayerPokemon(scene, scene.getParty()[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);
|
||||||
} else {
|
} else {
|
||||||
|
@ -214,7 +214,7 @@ function pokemonAndMoveChosen(scene: BattleScene, pokemon: PlayerPokemon, move:
|
|||||||
text: `${namespace}:incorrect_exp`,
|
text: `${namespace}:incorrect_exp`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
setEncounterExp(scene, scene.getParty().map((p) => p.id), 50);
|
setEncounterExp(scene, scene.getPlayerParty().map((p) => p.id), 50);
|
||||||
} else {
|
} else {
|
||||||
encounter.selectedOption!.dialogue!.selected = [
|
encounter.selectedOption!.dialogue!.selected = [
|
||||||
{
|
{
|
||||||
|
@ -184,7 +184,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
|||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Damage non-fire types and burn 1 random non-fire type member + give it Heatproof
|
// Damage non-fire types and burn 1 random non-fire type member + give it Heatproof
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
const nonFireTypes = scene.getParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
|
const nonFireTypes = scene.getPlayerParty().filter((p) => p.isAllowedInBattle() && !p.getTypes().includes(Type.FIRE));
|
||||||
|
|
||||||
for (const pkm of nonFireTypes) {
|
for (const pkm of nonFireTypes) {
|
||||||
const percentage = DAMAGE_PERCENTAGE / 100;
|
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||||
@ -257,7 +257,7 @@ export const FieryFalloutEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
function giveLeadPokemonAttackTypeBoostItem(scene: BattleScene) {
|
function giveLeadPokemonAttackTypeBoostItem(scene: BattleScene) {
|
||||||
// Give first party pokemon attack type boost item for free at end of battle
|
// Give first party pokemon attack type boost item for free at end of battle
|
||||||
const leadPokemon = scene.getParty()?.[0];
|
const leadPokemon = scene.getPlayerParty()?.[0];
|
||||||
if (leadPokemon) {
|
if (leadPokemon) {
|
||||||
// Generate type booster held item, default to Charcoal if item fails to generate
|
// Generate type booster held item, default to Charcoal if item fails to generate
|
||||||
let boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType;
|
let boosterModifierType = generateModifierType(scene, modifierTypes.ATTACK_TYPE_BOOSTER) as AttackTypeBoosterModifierType;
|
||||||
|
@ -56,7 +56,7 @@ 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.getParty()), true);
|
const 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 = {
|
||||||
@ -86,11 +86,11 @@ export const FightOrFlightEncounter: MysteryEncounter =
|
|||||||
: scene.currentBattle.waveIndex > 40
|
: scene.currentBattle.waveIndex > 40
|
||||||
? ModifierTier.ULTRA
|
? ModifierTier.ULTRA
|
||||||
: ModifierTier.GREAT;
|
: ModifierTier.GREAT;
|
||||||
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||||
let item: ModifierTypeOption | null = null;
|
let item: ModifierTypeOption | null = null;
|
||||||
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
|
// TMs and Candy Jar excluded from possible rewards as they're too swingy in value for a singular item reward
|
||||||
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
|
while (!item || item.type.id.includes("TM_") || item.type.id === "CANDY_JAR") {
|
||||||
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
|
item = getPlayerModifierTypeOptions(1, scene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
|
||||||
}
|
}
|
||||||
encounter.setDialogueToken("itemName", item.type.name);
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
encounter.misc = item;
|
encounter.misc = item;
|
||||||
|
@ -165,7 +165,7 @@ async function summonPlayerPokemon(scene: BattleScene) {
|
|||||||
|
|
||||||
const playerPokemon = encounter.misc.playerPokemon;
|
const playerPokemon = encounter.misc.playerPokemon;
|
||||||
// Swaps the chosen Pokemon and the first player's lead Pokemon in the party
|
// Swaps the chosen Pokemon and the first player's lead Pokemon in the party
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
const chosenIndex = party.indexOf(playerPokemon);
|
const chosenIndex = party.indexOf(playerPokemon);
|
||||||
if (chosenIndex !== 0) {
|
if (chosenIndex !== 0) {
|
||||||
const leadPokemon = party[0];
|
const leadPokemon = party[0];
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } 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 { MusicPreference } from "#app/system/settings/settings";
|
||||||
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } 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";
|
||||||
@ -105,7 +106,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Load bgm
|
// Load bgm
|
||||||
let bgmKey: string;
|
let bgmKey: string;
|
||||||
if (scene.musicPreference === 0) {
|
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||||
bgmKey = "mystery_encounter_gen_5_gts";
|
bgmKey = "mystery_encounter_gen_5_gts";
|
||||||
scene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
scene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||||
} else {
|
} else {
|
||||||
@ -191,7 +192,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
||||||
const dataSource = new PokemonData(receivedPokemonData);
|
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);
|
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);
|
scene.getPlayerParty().push(newPlayerPokemon);
|
||||||
await newPlayerPokemon.loadAssets();
|
await newPlayerPokemon.loadAssets();
|
||||||
|
|
||||||
for (const mod of modifiers) {
|
for (const mod of modifiers) {
|
||||||
@ -224,7 +225,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Randomly generate a Wonder Trade pokemon
|
// Randomly generate a Wonder Trade pokemon
|
||||||
const randomTradeOption = generateTradeOption(scene.getParty().map(p => p.species));
|
const randomTradeOption = generateTradeOption(scene.getPlayerParty().map(p => p.species));
|
||||||
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
const tradePokemon = new EnemyPokemon(scene, randomTradeOption, pokemon.level, TrainerSlot.NONE, false);
|
||||||
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
// Extra shiny roll at 1/128 odds (boosted by events and charms)
|
||||||
if (!tradePokemon.shiny) {
|
if (!tradePokemon.shiny) {
|
||||||
@ -299,7 +300,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
receivedPokemonData.pokeball = randInt(4) as PokeballType;
|
||||||
const dataSource = new PokemonData(receivedPokemonData);
|
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);
|
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);
|
scene.getPlayerParty().push(newPlayerPokemon);
|
||||||
await newPlayerPokemon.loadAssets();
|
await newPlayerPokemon.loadAssets();
|
||||||
|
|
||||||
for (const mod of modifiers) {
|
for (const mod of modifiers) {
|
||||||
@ -384,11 +385,11 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
tier++;
|
tier++;
|
||||||
}
|
}
|
||||||
|
|
||||||
regenerateModifierPoolThresholds(scene.getParty(), ModifierPoolType.PLAYER, 0);
|
regenerateModifierPoolThresholds(scene.getPlayerParty(), ModifierPoolType.PLAYER, 0);
|
||||||
let item: ModifierTypeOption | null = null;
|
let item: ModifierTypeOption | null = null;
|
||||||
// TMs excluded from possible rewards
|
// TMs excluded from possible rewards
|
||||||
while (!item || item.type.id.includes("TM_")) {
|
while (!item || item.type.id.includes("TM_")) {
|
||||||
item = getPlayerModifierTypeOptions(1, scene.getParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
|
item = getPlayerModifierTypeOptions(1, scene.getPlayerParty(), [], { guaranteedModifierTiers: [ tier ], allowLuckUpgrades: false })[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
encounter.setDialogueToken("itemName", item.type.name);
|
encounter.setDialogueToken("itemName", item.type.name);
|
||||||
@ -430,9 +431,9 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||||||
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> {
|
function getPokemonTradeOptions(scene: BattleScene): Map<number, EnemyPokemon[]> {
|
||||||
const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>();
|
const tradeOptionsMap: Map<number, EnemyPokemon[]> = new Map<number, EnemyPokemon[]>();
|
||||||
// Starts by filtering out any current party members as valid resulting species
|
// Starts by filtering out any current party members as valid resulting species
|
||||||
const alreadyUsedSpecies: PokemonSpecies[] = scene.getParty().map(p => p.species);
|
const alreadyUsedSpecies: PokemonSpecies[] = scene.getPlayerParty().map(p => p.species);
|
||||||
|
|
||||||
scene.getParty().forEach(pokemon => {
|
scene.getPlayerParty().forEach(pokemon => {
|
||||||
// If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools
|
// If the party member is legendary/mythical, the only trade options available are always pulled from generation-specific legendary trade pools
|
||||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||||
const generation = pokemon.species.generation;
|
const generation = pokemon.species.generation;
|
||||||
|
@ -104,7 +104,7 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
const allowedPokemon = scene.getParty().filter((p) => p.isAllowedInBattle());
|
const allowedPokemon = scene.getPlayerParty().filter((p) => p.isAllowedInBattle());
|
||||||
|
|
||||||
for (const pkm of allowedPokemon) {
|
for (const pkm of allowedPokemon) {
|
||||||
const percentage = DAMAGE_PERCENTAGE / 100;
|
const percentage = DAMAGE_PERCENTAGE / 100;
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
||||||
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
import MysteryEncounter, { MysteryEncounterBuilder } from "#app/data/mystery-encounters/mystery-encounter";
|
||||||
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
import { MysteryEncounterOptionBuilder } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { queueEncounterMessage, showEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
import { EnemyPartyConfig, initBattleWithEnemyConfig, leaveEncounterWithoutBattle, setEncounterRewards, transitionMysteryEncounterIntroVisuals } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { getHighestLevelPlayerPokemon, koPlayerPokemon } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
|
||||||
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
|
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||||
|
import { randSeedInt } from "#app/utils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
|
||||||
/** i18n namespace for encounter */
|
/** i18n namespace for encounter */
|
||||||
const namespace = "mysteryEncounters/mysteriousChest";
|
const namespace = "mysteryEncounters/mysteriousChest";
|
||||||
@ -177,7 +177,7 @@ export const MysteriousChestEncounter: MysteryEncounter =
|
|||||||
await showEncounterText(scene, `${namespace}:option.1.bad`);
|
await showEncounterText(scene, `${namespace}:option.1.bad`);
|
||||||
|
|
||||||
// Handle game over edge case
|
// Handle game over edge case
|
||||||
const allowedPokemon = scene.getParty().filter(p => p.isAllowedInBattle());
|
const allowedPokemon = scene.getPokemonAllowedInBattle();
|
||||||
if (allowedPokemon.length === 0) {
|
if (allowedPokemon.length === 0) {
|
||||||
// If there are no longer any legal pokemon in the party, game over.
|
// If there are no longer any legal pokemon in the party, game over.
|
||||||
scene.clearPhaseQueue();
|
scene.clearPhaseQueue();
|
||||||
|
@ -100,7 +100,7 @@ export const ShadyVitaminDealerEncounter: MysteryEncounter =
|
|||||||
// Only Pokemon that can gain benefits are above half HP with no status
|
// Only Pokemon that can gain benefits are above half HP with no status
|
||||||
const selectableFilter = (pokemon: Pokemon) => {
|
const selectableFilter = (pokemon: Pokemon) => {
|
||||||
// If pokemon meets primary pokemon reqs, it can be selected
|
// If pokemon meets primary pokemon reqs, it can be selected
|
||||||
if (!pokemon.isAllowed()) {
|
if (!pokemon.isAllowedInChallenge()) {
|
||||||
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||||
}
|
}
|
||||||
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
|
if (!encounter.pokemonMeetsPrimaryRequirements(scene, pokemon)) {
|
||||||
|
@ -134,7 +134,7 @@ export const TeleportingHijinksEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// Init enemy
|
// Init enemy
|
||||||
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.getParty()), true);
|
const 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 = {
|
||||||
@ -170,7 +170,7 @@ async function doBiomeTransitionDialogueAndBattleInit(scene: BattleScene) {
|
|||||||
|
|
||||||
// Init enemy
|
// Init enemy
|
||||||
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.getParty()), true);
|
const 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));
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ export const TheExpertPokemonBreederEncounter: MysteryEncounter =
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Determine the 3 pokemon the player can battle with
|
// Determine the 3 pokemon the player can battle with
|
||||||
let partyCopy = scene.getParty().slice(0);
|
let partyCopy = scene.getPlayerParty().slice(0);
|
||||||
partyCopy = partyCopy
|
partyCopy = partyCopy
|
||||||
.filter(p => p.isAllowedInBattle())
|
.filter(p => p.isAllowedInBattle())
|
||||||
.sort((a, b) => a.friendship - b.friendship);
|
.sort((a, b) => a.friendship - b.friendship);
|
||||||
@ -508,11 +508,11 @@ function getEggOptions(scene: BattleScene, commonEggs: number, rareEggs: number)
|
|||||||
}
|
}
|
||||||
|
|
||||||
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
function removePokemonFromPartyAndStoreHeldItems(scene: BattleScene, encounter: MysteryEncounter, chosenPokemon: PlayerPokemon) {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
const chosenIndex = party.indexOf(chosenPokemon);
|
const chosenIndex = party.indexOf(chosenPokemon);
|
||||||
party[chosenIndex] = party[0];
|
party[chosenIndex] = party[0];
|
||||||
party[0] = chosenPokemon;
|
party[0] = chosenPokemon;
|
||||||
encounter.misc.originalParty = scene.getParty().slice(1);
|
encounter.misc.originalParty = scene.getPlayerParty().slice(1);
|
||||||
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
|
encounter.misc.originalPartyHeldItems = encounter.misc.originalParty
|
||||||
.map(p => p.getHeldItems());
|
.map(p => p.getHeldItems());
|
||||||
scene["party"] = [
|
scene["party"] = [
|
||||||
@ -529,7 +529,7 @@ function checkAchievement(scene: BattleScene) {
|
|||||||
function restorePartyAndHeldItems(scene: BattleScene) {
|
function restorePartyAndHeldItems(scene: BattleScene) {
|
||||||
const encounter = scene.currentBattle.mysteryEncounter!;
|
const encounter = scene.currentBattle.mysteryEncounter!;
|
||||||
// Restore original party
|
// Restore original party
|
||||||
scene.getParty().push(...encounter.misc.originalParty);
|
scene.getPlayerParty().push(...encounter.misc.originalParty);
|
||||||
|
|
||||||
// Restore held items
|
// Restore held items
|
||||||
const originalHeldItems = encounter.misc.originalPartyHeldItems;
|
const originalHeldItems = encounter.misc.originalPartyHeldItems;
|
||||||
|
@ -140,7 +140,7 @@ export const TheStrongStuffEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
|
// -15 to all base stats of highest BST (halved for HP), +10 to all base stats of rest of party (halved for HP)
|
||||||
// Sort party by bst
|
// Sort party by bst
|
||||||
const sortedParty = scene.getParty().slice(0)
|
const sortedParty = scene.getPlayerParty().slice(0)
|
||||||
.sort((pokemon1, pokemon2) => {
|
.sort((pokemon1, pokemon2) => {
|
||||||
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
|
const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||||
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
|
const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0);
|
||||||
|
@ -189,7 +189,7 @@ function endTrainerBattleAndShowDialogue(scene: BattleScene): Promise<void> {
|
|||||||
const playerField = scene.getPlayerField();
|
const playerField = scene.getPlayerField();
|
||||||
playerField.forEach((_, p) => scene.unshiftPhase(new ReturnPhase(scene, p)));
|
playerField.forEach((_, p) => scene.unshiftPhase(new ReturnPhase(scene, p)));
|
||||||
|
|
||||||
for (const pokemon of scene.getParty()) {
|
for (const pokemon of scene.getPlayerParty()) {
|
||||||
// Only trigger form change when Eiscue is in Noice form
|
// Only trigger form change when Eiscue is in Noice form
|
||||||
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
// Hardcoded Eiscue for now in case it is fused with another pokemon
|
||||||
if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) {
|
if (pokemon.species.speciesId === Species.EISCUE && pokemon.hasAbility(Abilities.ICE_FACE) && pokemon.formIndex === 1) {
|
||||||
|
@ -152,7 +152,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add pokemon and mods back
|
// Add pokemon and mods back
|
||||||
scene.getParty().push(playerPokemon);
|
scene.getPlayerParty().push(playerPokemon);
|
||||||
for (const mod of modifiers.value) {
|
for (const mod of modifiers.value) {
|
||||||
mod.pokemonId = playerPokemon.id;
|
mod.pokemonId = playerPokemon.id;
|
||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
@ -229,7 +229,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
// Add pokemon and modifiers back
|
// Add pokemon and modifiers back
|
||||||
scene.getParty().push(playerPokemon);
|
scene.getPlayerParty().push(playerPokemon);
|
||||||
for (const mod of modifiers.value) {
|
for (const mod of modifiers.value) {
|
||||||
mod.pokemonId = playerPokemon.id;
|
mod.pokemonId = playerPokemon.id;
|
||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
@ -342,7 +342,7 @@ export const TrainingSessionEncounter: MysteryEncounter =
|
|||||||
scene.gameData.setPokemonCaught(playerPokemon, false);
|
scene.gameData.setPokemonCaught(playerPokemon, false);
|
||||||
|
|
||||||
// Add pokemon and mods back
|
// Add pokemon and mods back
|
||||||
scene.getParty().push(playerPokemon);
|
scene.getPlayerParty().push(playerPokemon);
|
||||||
for (const mod of modifiers.value) {
|
for (const mod of modifiers.value) {
|
||||||
mod.pokemonId = playerPokemon.id;
|
mod.pokemonId = playerPokemon.id;
|
||||||
scene.addModifier(mod, true, false, false, true);
|
scene.addModifier(mod, true, false, false, true);
|
||||||
|
@ -164,7 +164,7 @@ async function tryApplyDigRewardItems(scene: BattleScene) {
|
|||||||
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
const shellBell = generateModifierType(scene, modifierTypes.SHELL_BELL) as PokemonHeldItemModifierType;
|
||||||
const leftovers = generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
|
const leftovers = generateModifierType(scene, modifierTypes.LEFTOVERS) as PokemonHeldItemModifierType;
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
|
|
||||||
// Iterate over the party until an item was successfully given
|
// Iterate over the party until an item was successfully given
|
||||||
// First leftovers
|
// First leftovers
|
||||||
|
@ -51,7 +51,7 @@ 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.getParty()), true);
|
const 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
|
||||||
|
@ -176,7 +176,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
|||||||
|
|
||||||
for (const transformation of scene.currentBattle.mysteryEncounter!.misc.teamTransformations) {
|
for (const transformation of scene.currentBattle.mysteryEncounter!.misc.teamTransformations) {
|
||||||
scene.removePokemonFromPlayerParty(transformation.previousPokemon, false);
|
scene.removePokemonFromPlayerParty(transformation.previousPokemon, false);
|
||||||
scene.getParty().push(transformation.newPokemon);
|
scene.getPlayerParty().push(transformation.newPokemon);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.withOptionPhase(async (scene: BattleScene) => {
|
.withOptionPhase(async (scene: BattleScene) => {
|
||||||
@ -280,7 +280,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
|||||||
const onBeforeRewards = () => {
|
const onBeforeRewards = () => {
|
||||||
// Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently)
|
// Before battle rewards, unlock the passive on a pokemon in the player's team for the rest of the run (not permanently)
|
||||||
// One random pokemon will get its passive unlocked
|
// One random pokemon will get its passive unlocked
|
||||||
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
|
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive);
|
||||||
if (passiveDisabledPokemon?.length > 0) {
|
if (passiveDisabledPokemon?.length > 0) {
|
||||||
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
||||||
enablePassiveMon.passive = true;
|
enablePassiveMon.passive = true;
|
||||||
@ -306,7 +306,7 @@ export const WeirdDreamEncounter: MysteryEncounter =
|
|||||||
},
|
},
|
||||||
async (scene: BattleScene) => {
|
async (scene: BattleScene) => {
|
||||||
// Leave, reduce party levels by 10%
|
// Leave, reduce party levels by 10%
|
||||||
for (const pokemon of scene.getParty()) {
|
for (const pokemon of scene.getPlayerParty()) {
|
||||||
pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1);
|
pokemon.level = Math.max(Math.ceil((100 - PERCENT_LEVEL_LOSS_ON_REFUSE) / 100 * pokemon.level), 1);
|
||||||
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
pokemon.exp = getLevelTotalExp(pokemon.level, pokemon.species.growthRate);
|
||||||
pokemon.levelExp = 0;
|
pokemon.levelExp = 0;
|
||||||
@ -329,7 +329,7 @@ interface PokemonTransformation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
|
function getTeamTransformations(scene: BattleScene): PokemonTransformation[] {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
// Removes all pokemon from the party
|
// Removes all pokemon from the party
|
||||||
const alreadyUsedSpecies: PokemonSpecies[] = party.map(p => p.species);
|
const alreadyUsedSpecies: PokemonSpecies[] = party.map(p => p.species);
|
||||||
const pokemonTransformations: PokemonTransformation[] = party.map(p => {
|
const pokemonTransformations: PokemonTransformation[] = party.map(p => {
|
||||||
@ -404,7 +404,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
if (shouldGetOldGateau(newPokemon)) {
|
if (shouldGetOldGateau(newPokemon)) {
|
||||||
const stats = getOldGateauBoostedStats(newPokemon);
|
const stats = getOldGateauBoostedStats(newPokemon);
|
||||||
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
|
const modType = modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU()
|
||||||
.generateType(scene.getParty(), [ OLD_GATEAU_STATS_UP, stats ])
|
.generateType(scene.getPlayerParty(), [ OLD_GATEAU_STATS_UP, stats ])
|
||||||
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_OLD_GATEAU);
|
||||||
const modifier = modType?.newModifier(newPokemon);
|
const modifier = modType?.newModifier(newPokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
@ -417,7 +417,7 @@ async function doNewTeamPostProcess(scene: BattleScene, transformations: Pokemon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// One random pokemon will get its passive unlocked
|
// One random pokemon will get its passive unlocked
|
||||||
const passiveDisabledPokemon = scene.getParty().filter(p => !p.passive);
|
const passiveDisabledPokemon = scene.getPlayerParty().filter(p => !p.passive);
|
||||||
if (passiveDisabledPokemon?.length > 0) {
|
if (passiveDisabledPokemon?.length > 0) {
|
||||||
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
const enablePassiveMon = passiveDisabledPokemon[randSeedInt(passiveDisabledPokemon.length)];
|
||||||
enablePassiveMon.passive = true;
|
enablePassiveMon.passive = true;
|
||||||
|
@ -88,7 +88,7 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
|||||||
* @param pokemon
|
* @param pokemon
|
||||||
*/
|
*/
|
||||||
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
||||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,10 +102,10 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
|||||||
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let qualified: PlayerPokemon[] = scene.getParty();
|
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||||
for (const req of this.primaryPokemonRequirements) {
|
for (const req of this.primaryPokemonRequirements) {
|
||||||
if (req.meetsRequirement(scene)) {
|
if (req.meetsRequirement(scene)) {
|
||||||
const queryParty = req.queryParty(scene.getParty());
|
const queryParty = req.queryParty(scene.getPlayerParty());
|
||||||
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||||
} else {
|
} else {
|
||||||
this.primaryPokemon = undefined;
|
this.primaryPokemon = undefined;
|
||||||
@ -162,10 +162,10 @@ export default class MysteryEncounterOption implements IMysteryEncounterOption {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let qualified: PlayerPokemon[] = scene.getParty();
|
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||||
for (const req of this.secondaryPokemonRequirements) {
|
for (const req of this.secondaryPokemonRequirements) {
|
||||||
if (req.meetsRequirement(scene)) {
|
if (req.meetsRequirement(scene)) {
|
||||||
const queryParty = req.queryParty(scene.getParty());
|
const queryParty = req.queryParty(scene.getPlayerParty());
|
||||||
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
qualified = qualified.filter(pkmn => queryParty.includes(pkmn));
|
||||||
} else {
|
} else {
|
||||||
this.secondaryPokemon = [];
|
this.secondaryPokemon = [];
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { allAbilities } from "#app/data/ability";
|
||||||
import { Abilities } from "#enums/abilities";
|
|
||||||
import { Moves } from "#enums/moves";
|
|
||||||
import { Species } from "#enums/species";
|
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
|
||||||
import { Nature } from "#app/data/nature";
|
|
||||||
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
import { EvolutionItem, pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
|
import { Nature } from "#app/data/nature";
|
||||||
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
import { FormChangeItem, pokemonFormChanges, SpeciesFormChangeItemTrigger } from "#app/data/pokemon-forms";
|
||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
import { WeatherType } from "#app/data/weather";
|
import { WeatherType } from "#app/data/weather";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
import { AttackTypeBoosterModifier } from "#app/modifier/modifier";
|
||||||
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
import { AttackTypeBoosterModifierType } from "#app/modifier/modifier-type";
|
||||||
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||||
import { allAbilities } from "#app/data/ability";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
|
|
||||||
export interface EncounterRequirement {
|
export interface EncounterRequirement {
|
||||||
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met
|
meetsRequirement(scene: BattleScene): boolean; // Boolean to see if a requirement is met
|
||||||
@ -333,7 +333,7 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
|
|||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) {
|
if (!isNullOrUndefined(this.partySizeRange) && this.partySizeRange[0] <= this.partySizeRange[1]) {
|
||||||
const partySize = this.excludeDisallowedPokemon ? scene.getParty().filter(p => p.isAllowedInBattle()).length : scene.getParty().length;
|
const partySize = this.excludeDisallowedPokemon ? scene.getPokemonAllowedInBattle().length : scene.getPlayerParty().length;
|
||||||
if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) {
|
if (partySize >= 0 && (this.partySizeRange[0] >= 0 && this.partySizeRange[0] > partySize) || (this.partySizeRange[1] >= 0 && this.partySizeRange[1] < partySize)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -343,7 +343,7 @@ export class PartySizeRequirement extends EncounterSceneRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
override getDialogueToken(scene: BattleScene, pokemon?: PlayerPokemon): [string, string] {
|
||||||
return [ "partySize", scene.getParty().length.toString() ];
|
return [ "partySize", scene.getPlayerParty().length.toString() ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,7 +358,7 @@ export class PersistentModifierRequirement extends EncounterSceneRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredHeldItemModifiers?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -421,7 +421,7 @@ export class SpeciesRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredSpecies?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -459,7 +459,7 @@ export class NatureRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredNature?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -498,7 +498,7 @@ export class TypeRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
let partyPokemon = scene.getParty();
|
let partyPokemon = scene.getPlayerParty();
|
||||||
|
|
||||||
if (isNullOrUndefined(partyPokemon)) {
|
if (isNullOrUndefined(partyPokemon)) {
|
||||||
return false;
|
return false;
|
||||||
@ -545,7 +545,7 @@ export class MoveRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -594,7 +594,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -635,7 +635,7 @@ export class AbilityRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredAbilities?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -677,7 +677,7 @@ export class StatusEffectRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredStatusEffect?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -746,7 +746,7 @@ export class CanFormChangeWithItemRequirement extends EncounterPokemonRequiremen
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredFormChangeItem?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -798,7 +798,7 @@ export class CanEvolveWithItemRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredEvolutionItem?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -849,7 +849,7 @@ export class HeldItemRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon)) {
|
if (isNullOrUndefined(partyPokemon)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -900,7 +900,7 @@ export class AttackTypeBoosterHeldItemTypeRequirement extends EncounterPokemonRe
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
if (isNullOrUndefined(partyPokemon)) {
|
if (isNullOrUndefined(partyPokemon)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -957,7 +957,7 @@ export class LevelRequirement extends EncounterPokemonRequirement {
|
|||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
// Party Pokemon inside required level range
|
// Party Pokemon inside required level range
|
||||||
if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) {
|
if (!isNullOrUndefined(this.requiredLevelRange) && this.requiredLevelRange[0] <= this.requiredLevelRange[1]) {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
const pokemonInRange = this.queryParty(partyPokemon);
|
const pokemonInRange = this.queryParty(partyPokemon);
|
||||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||||
return false;
|
return false;
|
||||||
@ -995,7 +995,7 @@ export class FriendshipRequirement extends EncounterPokemonRequirement {
|
|||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
// Party Pokemon inside required friendship range
|
// Party Pokemon inside required friendship range
|
||||||
if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) {
|
if (!isNullOrUndefined(this.requiredFriendshipRange) && this.requiredFriendshipRange[0] <= this.requiredFriendshipRange[1]) {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
const pokemonInRange = this.queryParty(partyPokemon);
|
const pokemonInRange = this.queryParty(partyPokemon);
|
||||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||||
return false;
|
return false;
|
||||||
@ -1038,7 +1038,7 @@ export class HealthRatioRequirement extends EncounterPokemonRequirement {
|
|||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
// Party Pokemon's health inside required health range
|
// Party Pokemon's health inside required health range
|
||||||
if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) {
|
if (!isNullOrUndefined(this.requiredHealthRange) && this.requiredHealthRange[0] <= this.requiredHealthRange[1]) {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
const pokemonInRange = this.queryParty(partyPokemon);
|
const pokemonInRange = this.queryParty(partyPokemon);
|
||||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||||
return false;
|
return false;
|
||||||
@ -1082,7 +1082,7 @@ export class WeightRequirement extends EncounterPokemonRequirement {
|
|||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
// Party Pokemon's weight inside required weight range
|
// Party Pokemon's weight inside required weight range
|
||||||
if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) {
|
if (!isNullOrUndefined(this.requiredWeightRange) && this.requiredWeightRange[0] <= this.requiredWeightRange[1]) {
|
||||||
const partyPokemon = scene.getParty();
|
const partyPokemon = scene.getPlayerParty();
|
||||||
const pokemonInRange = this.queryParty(partyPokemon);
|
const pokemonInRange = this.queryParty(partyPokemon);
|
||||||
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
if (pokemonInRange.length < this.minNumberOfPokemon) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -314,7 +314,7 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
|||||||
* @param pokemon
|
* @param pokemon
|
||||||
*/
|
*/
|
||||||
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
pokemonMeetsPrimaryRequirements(scene: BattleScene, pokemon: Pokemon): boolean {
|
||||||
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getParty()).map(p => p.id).includes(pokemon.id));
|
return !this.primaryPokemonRequirements.some(req => !req.queryParty(scene.getPlayerParty()).map(p => p.id).includes(pokemon.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -326,18 +326,18 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
|||||||
*/
|
*/
|
||||||
private meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
private meetsPrimaryRequirementAndPrimaryPokemonSelected(scene: BattleScene): boolean {
|
||||||
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
if (!this.primaryPokemonRequirements || this.primaryPokemonRequirements.length === 0) {
|
||||||
const activeMon = scene.getParty().filter(p => p.isActive(true));
|
const activeMon = scene.getPlayerParty().filter(p => p.isActive(true));
|
||||||
if (activeMon.length > 0) {
|
if (activeMon.length > 0) {
|
||||||
this.primaryPokemon = activeMon[0];
|
this.primaryPokemon = activeMon[0];
|
||||||
} else {
|
} else {
|
||||||
this.primaryPokemon = scene.getParty().filter(p => p.isAllowedInBattle())[0];
|
this.primaryPokemon = scene.getPlayerParty().filter(p => p.isAllowedInBattle())[0];
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let qualified: PlayerPokemon[] = scene.getParty();
|
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||||
for (const req of this.primaryPokemonRequirements) {
|
for (const req of this.primaryPokemonRequirements) {
|
||||||
if (req.meetsRequirement(scene)) {
|
if (req.meetsRequirement(scene)) {
|
||||||
qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn));
|
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn));
|
||||||
} else {
|
} else {
|
||||||
this.primaryPokemon = undefined;
|
this.primaryPokemon = undefined;
|
||||||
return false;
|
return false;
|
||||||
@ -394,10 +394,10 @@ export default class MysteryEncounter implements IMysteryEncounter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let qualified: PlayerPokemon[] = scene.getParty();
|
let qualified: PlayerPokemon[] = scene.getPlayerParty();
|
||||||
for (const req of this.secondaryPokemonRequirements) {
|
for (const req of this.secondaryPokemonRequirements) {
|
||||||
if (req.meetsRequirement(scene)) {
|
if (req.meetsRequirement(scene)) {
|
||||||
qualified = qualified.filter(pkmn => req.queryParty(scene.getParty()).includes(pkmn));
|
qualified = qualified.filter(pkmn => req.queryParty(scene.getPlayerParty()).includes(pkmn));
|
||||||
} else {
|
} else {
|
||||||
this.secondaryPokemon = [];
|
this.secondaryPokemon = [];
|
||||||
return false;
|
return false;
|
||||||
|
@ -39,7 +39,7 @@ export class CanLearnMoveRequirement extends EncounterPokemonRequirement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override meetsRequirement(scene: BattleScene): boolean {
|
override meetsRequirement(scene: BattleScene): boolean {
|
||||||
const partyPokemon = scene.getParty().filter((pkm) => (this.includeFainted ? pkm.isAllowed() : pkm.isAllowedInBattle()));
|
const partyPokemon = scene.getPlayerParty().filter((pkm) => (this.includeFainted ? pkm.isAllowedInChallenge() : pkm.isAllowedInBattle()));
|
||||||
|
|
||||||
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
if (isNullOrUndefined(partyPokemon) || this.requiredMoves?.length < 0) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -418,9 +418,9 @@ export function generateModifierType(scene: BattleScene, modifier: () => Modifie
|
|||||||
// Populates item id and tier (order matters)
|
// Populates item id and tier (order matters)
|
||||||
result = result
|
result = result
|
||||||
.withIdFromFunc(modifierTypes[modifierId])
|
.withIdFromFunc(modifierTypes[modifierId])
|
||||||
.withTierFromPool(ModifierPoolType.PLAYER, scene.getParty());
|
.withTierFromPool(ModifierPoolType.PLAYER, scene.getPlayerParty());
|
||||||
|
|
||||||
return result instanceof ModifierTypeGenerator ? result.generateType(scene.getParty(), pregenArgs) : result;
|
return result instanceof ModifierTypeGenerator ? result.generateType(scene.getPlayerParty(), pregenArgs) : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -451,9 +451,9 @@ export function selectPokemonForOption(scene: BattleScene, onPokemonSelected: (p
|
|||||||
|
|
||||||
// Open party screen to choose pokemon
|
// Open party screen to choose pokemon
|
||||||
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
|
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
|
||||||
if (slotIndex < scene.getParty().length) {
|
if (slotIndex < scene.getPlayerParty().length) {
|
||||||
scene.ui.setMode(modeToSetOnExit).then(() => {
|
scene.ui.setMode(modeToSetOnExit).then(() => {
|
||||||
const pokemon = scene.getParty()[slotIndex];
|
const pokemon = scene.getPlayerParty()[slotIndex];
|
||||||
const secondaryOptions = onPokemonSelected(pokemon);
|
const secondaryOptions = onPokemonSelected(pokemon);
|
||||||
if (!secondaryOptions) {
|
if (!secondaryOptions) {
|
||||||
scene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
scene.currentBattle.mysteryEncounter!.setDialogueToken("selectedPokemon", pokemon.getNameToRender());
|
||||||
@ -563,7 +563,7 @@ export function selectOptionThenPokemon(scene: BattleScene, options: OptionSelec
|
|||||||
const selectPokemonAfterOption = (selectedOptionIndex: number) => {
|
const selectPokemonAfterOption = (selectedOptionIndex: number) => {
|
||||||
// Open party screen to choose a Pokemon
|
// Open party screen to choose a Pokemon
|
||||||
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
|
scene.ui.setMode(Mode.PARTY, PartyUiMode.SELECT, -1, (slotIndex: number, option: PartyOption) => {
|
||||||
if (slotIndex < scene.getParty().length) {
|
if (slotIndex < scene.getPlayerParty().length) {
|
||||||
// Pokemon and option selected
|
// Pokemon and option selected
|
||||||
scene.ui.setMode(modeToSetOnExit).then(() => {
|
scene.ui.setMode(modeToSetOnExit).then(() => {
|
||||||
const result: PokemonAndOptionSelected = { selectedPokemonIndex: slotIndex, selectedOptionIndex: selectedOptionIndex };
|
const result: PokemonAndOptionSelected = { selectedPokemonIndex: slotIndex, selectedOptionIndex: selectedOptionIndex };
|
||||||
@ -713,7 +713,7 @@ export function leaveEncounterWithoutBattle(scene: BattleScene, addHealPhase: bo
|
|||||||
* @param doNotContinue - default `false`. If set to true, will not end the battle and continue to next wave
|
* @param doNotContinue - default `false`. If set to true, will not end the battle and continue to next wave
|
||||||
*/
|
*/
|
||||||
export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) {
|
export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) {
|
||||||
const allowedPkm = scene.getParty().filter((pkm) => pkm.isAllowedInBattle());
|
const allowedPkm = scene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle());
|
||||||
|
|
||||||
if (allowedPkm.length === 0) {
|
if (allowedPkm.length === 0) {
|
||||||
scene.clearPhaseQueue();
|
scene.clearPhaseQueue();
|
||||||
@ -750,7 +750,7 @@ export function handleMysteryEncounterVictory(scene: BattleScene, addHealPhase:
|
|||||||
* @param addHealPhase
|
* @param addHealPhase
|
||||||
*/
|
*/
|
||||||
export function handleMysteryEncounterBattleFailed(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) {
|
export function handleMysteryEncounterBattleFailed(scene: BattleScene, addHealPhase: boolean = false, doNotContinue: boolean = false) {
|
||||||
const allowedPkm = scene.getParty().filter((pkm) => pkm.isAllowedInBattle());
|
const allowedPkm = scene.getPlayerParty().filter((pkm) => pkm.isAllowedInBattle());
|
||||||
|
|
||||||
if (allowedPkm.length === 0) {
|
if (allowedPkm.length === 0) {
|
||||||
scene.clearPhaseQueue();
|
scene.clearPhaseQueue();
|
||||||
|
@ -53,24 +53,24 @@ export function getSpriteKeysFromPokemon(pokemon: Pokemon): { spriteKey: string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will never remove the player's last non-fainted Pokemon (if they only have 1)
|
* Will never remove the player's last non-fainted Pokemon (if they only have 1).
|
||||||
* Otherwise, picks a Pokemon completely at random and removes from the party
|
* Otherwise, picks a Pokemon completely at random and removes from the party
|
||||||
* @param scene
|
* @param scene
|
||||||
* @param isAllowed Default false. If true, only picks from legal mons. If no legal mons are found (or there is 1, with `doNotReturnLastAllowedMon = true), will return a mon that is not allowed.
|
* @param isAllowed Default `false`. If `true`, only picks from legal mons. If no legal mons are found (or there is 1, with `doNotReturnLastAllowedMon = true`), will return a mon that is not allowed.
|
||||||
* @param isFainted Default false. If true, includes fainted mons.
|
* @param isFainted Default `false`. If `true`, includes fainted mons.
|
||||||
* @param doNotReturnLastAllowedMon Default false. If true, will never return the last unfainted pokemon in the party. Useful when this function is being used to determine what Pokemon to remove from the party (Don't want to remove last unfainted)
|
* @param doNotReturnLastAllowedMon Default `false`. If `true`, will never return the last unfainted pokemon in the party. Useful when this function is being used to determine what Pokemon to remove from the party (Don't want to remove last unfainted)
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getRandomPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false, doNotReturnLastAllowedMon: boolean = false): PlayerPokemon {
|
export function getRandomPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false, doNotReturnLastAllowedMon: boolean = false): PlayerPokemon {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let chosenIndex: number;
|
let chosenIndex: number;
|
||||||
let chosenPokemon: PlayerPokemon | null = null;
|
let chosenPokemon: PlayerPokemon | null = null;
|
||||||
const fullyLegalMons = party.filter(p => (!isAllowed || p.isAllowed()) && (isFainted || !p.isFainted()));
|
const fullyLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && (isFainted || !p.isFainted()));
|
||||||
const allowedOnlyMons = party.filter(p => p.isAllowed());
|
const allowedOnlyMons = party.filter(p => p.isAllowedInChallenge());
|
||||||
|
|
||||||
if (doNotReturnLastAllowedMon && fullyLegalMons.length === 1) {
|
if (doNotReturnLastAllowedMon && fullyLegalMons.length === 1) {
|
||||||
// If there is only 1 legal/unfainted mon left, select from fainted legal mons
|
// If there is only 1 legal/unfainted mon left, select from fainted legal mons
|
||||||
const faintedLegalMons = party.filter(p => (!isAllowed || p.isAllowed()) && p.isFainted());
|
const faintedLegalMons = party.filter(p => (!isAllowed || p.isAllowedInChallenge()) && p.isFainted());
|
||||||
if (faintedLegalMons.length > 0) {
|
if (faintedLegalMons.length > 0) {
|
||||||
chosenIndex = randSeedInt(faintedLegalMons.length);
|
chosenIndex = randSeedInt(faintedLegalMons.length);
|
||||||
chosenPokemon = faintedLegalMons[chosenIndex];
|
chosenPokemon = faintedLegalMons[chosenIndex];
|
||||||
@ -101,11 +101,11 @@ export function getRandomPlayerPokemon(scene: BattleScene, isAllowed: boolean =
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getHighestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
export function getHighestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let pokemon: PlayerPokemon | null = null;
|
let pokemon: PlayerPokemon | null = null;
|
||||||
|
|
||||||
for (const p of party) {
|
for (const p of party) {
|
||||||
if (isAllowed && !p.isAllowed()) {
|
if (isAllowed && !p.isAllowedInChallenge()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isFainted && p.isFainted()) {
|
if (!isFainted && p.isFainted()) {
|
||||||
@ -127,11 +127,11 @@ export function getHighestLevelPlayerPokemon(scene: BattleScene, isAllowed: bool
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getHighestStatPlayerPokemon(scene: BattleScene, stat: PermanentStat, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
export function getHighestStatPlayerPokemon(scene: BattleScene, stat: PermanentStat, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let pokemon: PlayerPokemon | null = null;
|
let pokemon: PlayerPokemon | null = null;
|
||||||
|
|
||||||
for (const p of party) {
|
for (const p of party) {
|
||||||
if (isAllowed && !p.isAllowed()) {
|
if (isAllowed && !p.isAllowedInChallenge()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isFainted && p.isFainted()) {
|
if (!isFainted && p.isFainted()) {
|
||||||
@ -152,11 +152,11 @@ export function getHighestStatPlayerPokemon(scene: BattleScene, stat: PermanentS
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getLowestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
export function getLowestLevelPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let pokemon: PlayerPokemon | null = null;
|
let pokemon: PlayerPokemon | null = null;
|
||||||
|
|
||||||
for (const p of party) {
|
for (const p of party) {
|
||||||
if (isAllowed && !p.isAllowed()) {
|
if (isAllowed && !p.isAllowedInChallenge()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isFainted && p.isFainted()) {
|
if (!isFainted && p.isFainted()) {
|
||||||
@ -177,11 +177,11 @@ export function getLowestLevelPlayerPokemon(scene: BattleScene, isAllowed: boole
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getHighestStatTotalPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
export function getHighestStatTotalPlayerPokemon(scene: BattleScene, isAllowed: boolean = false, isFainted: boolean = false): PlayerPokemon {
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
let pokemon: PlayerPokemon | null = null;
|
let pokemon: PlayerPokemon | null = null;
|
||||||
|
|
||||||
for (const p of party) {
|
for (const p of party) {
|
||||||
if (isAllowed && !p.isAllowed()) {
|
if (isAllowed && !p.isAllowedInChallenge()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!isFainted && p.isFainted()) {
|
if (!isFainted && p.isFainted()) {
|
||||||
@ -315,7 +315,7 @@ export function applyHealToPokemon(scene: BattleScene, pokemon: PlayerPokemon, h
|
|||||||
*/
|
*/
|
||||||
export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
|
export async function modifyPlayerPokemonBST(pokemon: PlayerPokemon, value: number) {
|
||||||
const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE()
|
const modType = modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE()
|
||||||
.generateType(pokemon.scene.getParty(), [ value ])
|
.generateType(pokemon.scene.getPlayerParty(), [ value ])
|
||||||
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE);
|
?.withIdFromFunc(modifierTypes.MYSTERY_ENCOUNTER_SHUCKLE_JUICE);
|
||||||
const modifier = modType?.newModifier(pokemon);
|
const modifier = modType?.newModifier(pokemon);
|
||||||
if (modifier) {
|
if (modifier) {
|
||||||
@ -591,7 +591,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
|||||||
const addToParty = (slotIndex?: number) => {
|
const addToParty = (slotIndex?: number) => {
|
||||||
const newPokemon = pokemon.addToParty(pokeballType, slotIndex);
|
const newPokemon = pokemon.addToParty(pokeballType, slotIndex);
|
||||||
const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
const modifiers = scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
||||||
if (scene.getParty().filter(p => p.isShiny()).length === 6) {
|
if (scene.getPlayerParty().filter(p => p.isShiny()).length === 6) {
|
||||||
scene.validateAchv(achvs.SHINY_PARTY);
|
scene.validateAchv(achvs.SHINY_PARTY);
|
||||||
}
|
}
|
||||||
Promise.all(modifiers.map(m => scene.addModifier(m, true))).then(() => {
|
Promise.all(modifiers.map(m => scene.addModifier(m, true))).then(() => {
|
||||||
@ -605,7 +605,7 @@ export async function catchPokemon(scene: BattleScene, pokemon: EnemyPokemon, po
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Promise.all([ pokemon.hideInfo(), scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
|
Promise.all([ pokemon.hideInfo(), scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
|
||||||
if (scene.getParty().length === 6) {
|
if (scene.getPlayerParty().length === 6) {
|
||||||
const promptRelease = () => {
|
const promptRelease = () => {
|
||||||
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
||||||
scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
|
scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
|
||||||
@ -826,7 +826,7 @@ export async function addPokemonDataToDexAndValidateAchievements(scene: BattleSc
|
|||||||
* @param invalidSelectionKey
|
* @param invalidSelectionKey
|
||||||
*/
|
*/
|
||||||
export function isPokemonValidForEncounterOptionSelection(pokemon: Pokemon, scene: BattleScene, invalidSelectionKey: string): string | null {
|
export function isPokemonValidForEncounterOptionSelection(pokemon: Pokemon, scene: BattleScene, invalidSelectionKey: string): string | null {
|
||||||
if (!pokemon.isAllowed()) {
|
if (!pokemon.isAllowedInChallenge()) {
|
||||||
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
return i18next.t("partyUiHandler:cantBeUsed", { pokemonName: pokemon.getNameToRender() }) ?? null;
|
||||||
}
|
}
|
||||||
if (!pokemon.isAllowedInBattle()) {
|
if (!pokemon.isAllowedInBattle()) {
|
||||||
|
@ -47,7 +47,7 @@ export function getPokemonSpecies(species: Species | Species[] | undefined): Pok
|
|||||||
return allSpecies[species - 1];
|
return allSpecies[species - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPokemonSpeciesForm(species: Species, formIndex: integer): PokemonSpeciesForm {
|
export function getPokemonSpeciesForm(species: Species, formIndex: number): PokemonSpeciesForm {
|
||||||
const retSpecies: PokemonSpecies = species >= 2000
|
const retSpecies: PokemonSpecies = species >= 2000
|
||||||
? allSpecies.find(s => s.speciesId === species)! // TODO: is the bang correct?
|
? allSpecies.find(s => s.speciesId === species)! // TODO: is the bang correct?
|
||||||
: allSpecies[species - 1];
|
: allSpecies[species - 1];
|
||||||
@ -129,26 +129,27 @@ export type PokemonSpeciesFilter = (species: PokemonSpecies) => boolean;
|
|||||||
|
|
||||||
export abstract class PokemonSpeciesForm {
|
export abstract class PokemonSpeciesForm {
|
||||||
public speciesId: Species;
|
public speciesId: Species;
|
||||||
public formIndex: integer;
|
protected _formIndex: number;
|
||||||
public generation: integer;
|
protected _generation: number;
|
||||||
public type1: Type;
|
readonly type1: Type;
|
||||||
public type2: Type | null;
|
readonly type2: Type | null;
|
||||||
public height: number;
|
readonly height: number;
|
||||||
public weight: number;
|
readonly weight: number;
|
||||||
public ability1: Abilities;
|
readonly ability1: Abilities;
|
||||||
public ability2: Abilities;
|
readonly ability2: Abilities;
|
||||||
public abilityHidden: Abilities;
|
readonly abilityHidden: Abilities;
|
||||||
public baseTotal: integer;
|
readonly baseTotal: number;
|
||||||
public baseStats: integer[];
|
readonly baseStats: number[];
|
||||||
public catchRate: integer;
|
readonly catchRate: number;
|
||||||
public baseFriendship: integer;
|
readonly baseFriendship: number;
|
||||||
public baseExp: integer;
|
readonly baseExp: number;
|
||||||
public genderDiffs: boolean;
|
readonly genderDiffs: boolean;
|
||||||
public isStarterSelectable: boolean;
|
readonly isStarterSelectable: boolean;
|
||||||
|
|
||||||
constructor(type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
constructor(type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
||||||
baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer,
|
baseTotal: number, baseHp: number, baseAtk: number, baseDef: number, baseSpatk: number, baseSpdef: number, baseSpd: number,
|
||||||
catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs: boolean, isStarterSelectable: boolean) {
|
catchRate: number, baseFriendship: number, baseExp: number, genderDiffs: boolean, isStarterSelectable: boolean
|
||||||
|
) {
|
||||||
this.type1 = type1;
|
this.type1 = type1;
|
||||||
this.type2 = type2;
|
this.type2 = type2;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
@ -180,7 +181,23 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
isOfType(type: integer): boolean {
|
get generation(): number {
|
||||||
|
return this._generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
set generation(generation: number) {
|
||||||
|
this._generation = generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
get formIndex(): number {
|
||||||
|
return this._formIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
set formIndex(formIndex: number) {
|
||||||
|
this._formIndex = formIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
isOfType(type: number): boolean {
|
||||||
return this.type1 === type || (this.type2 !== null && this.type2 === type);
|
return this.type1 === type || (this.type2 !== null && this.type2 === type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +205,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
* Method to get the total number of abilities a Pokemon species has.
|
* Method to get the total number of abilities a Pokemon species has.
|
||||||
* @returns Number of abilities
|
* @returns Number of abilities
|
||||||
*/
|
*/
|
||||||
getAbilityCount(): integer {
|
getAbilityCount(): number {
|
||||||
return this.abilityHidden !== Abilities.NONE ? 3 : 2;
|
return this.abilityHidden !== Abilities.NONE ? 3 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +214,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
* @param abilityIndex Which ability to get (should only be 0-2)
|
* @param abilityIndex Which ability to get (should only be 0-2)
|
||||||
* @returns The id of the Ability
|
* @returns The id of the Ability
|
||||||
*/
|
*/
|
||||||
getAbility(abilityIndex: integer): Abilities {
|
getAbility(abilityIndex: number): Abilities {
|
||||||
let ret: Abilities;
|
let ret: Abilities;
|
||||||
if (abilityIndex === 0) {
|
if (abilityIndex === 0) {
|
||||||
ret = this.ability1;
|
ret = this.ability1;
|
||||||
@ -277,12 +294,12 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpriteAtlasPath(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): string {
|
getSpriteAtlasPath(female: boolean, formIndex?: number, shiny?: boolean, variant?: number): string {
|
||||||
const spriteId = this.getSpriteId(female, formIndex, shiny, variant).replace(/\_{2}/g, "/");
|
const spriteId = this.getSpriteId(female, formIndex, shiny, variant).replace(/\_{2}/g, "/");
|
||||||
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
|
return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpriteId(female: boolean, formIndex?: integer, shiny?: boolean, variant: integer = 0, back?: boolean): string {
|
getSpriteId(female: boolean, formIndex?: number, shiny?: boolean, variant: number = 0, back?: boolean): string {
|
||||||
if (formIndex === undefined || this instanceof PokemonForm) {
|
if (formIndex === undefined || this instanceof PokemonForm) {
|
||||||
formIndex = this.formIndex;
|
formIndex = this.formIndex;
|
||||||
}
|
}
|
||||||
@ -299,11 +316,11 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant] === 2 ? `_${variant + 1}` : ""}`;
|
return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant] === 2 ? `_${variant + 1}` : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpriteKey(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): string {
|
getSpriteKey(female: boolean, formIndex?: number, shiny?: boolean, variant?: number): string {
|
||||||
return `pkmn__${this.getSpriteId(female, formIndex, shiny, variant)}`;
|
return `pkmn__${this.getSpriteId(female, formIndex, shiny, variant)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract getFormSpriteKey(formIndex?: integer): string;
|
abstract getFormSpriteKey(formIndex?: number): string;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -311,9 +328,9 @@ export abstract class PokemonSpeciesForm {
|
|||||||
* @param formIndex optional form index for pokemon with different forms
|
* @param formIndex optional form index for pokemon with different forms
|
||||||
* @returns species id if no additional forms, index with formkey if a pokemon with a form
|
* @returns species id if no additional forms, index with formkey if a pokemon with a form
|
||||||
*/
|
*/
|
||||||
getVariantDataIndex(formIndex?: integer) {
|
getVariantDataIndex(formIndex?: number) {
|
||||||
let formkey: string | null = null;
|
let formkey: string | null = null;
|
||||||
let variantDataIndex: integer | string = this.speciesId;
|
let variantDataIndex: number | string = this.speciesId;
|
||||||
const species = getPokemonSpecies(this.speciesId);
|
const species = getPokemonSpecies(this.speciesId);
|
||||||
if (species.forms.length > 0 && formIndex !== undefined) {
|
if (species.forms.length > 0 && formIndex !== undefined) {
|
||||||
formkey = species.forms[formIndex]?.getFormSpriteKey(formIndex);
|
formkey = species.forms[formIndex]?.getFormSpriteKey(formIndex);
|
||||||
@ -324,13 +341,13 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return variantDataIndex;
|
return variantDataIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIconAtlasKey(formIndex?: integer, shiny?: boolean, variant?: integer): string {
|
getIconAtlasKey(formIndex?: number, shiny?: boolean, variant?: number): string {
|
||||||
const variantDataIndex = this.getVariantDataIndex(formIndex);
|
const variantDataIndex = this.getVariantDataIndex(formIndex);
|
||||||
const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]);
|
const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]);
|
||||||
return `pokemon_icons_${this.generation}${isVariant ? "v" : ""}`;
|
return `pokemon_icons_${this.generation}${isVariant ? "v" : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIconId(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): string {
|
getIconId(female: boolean, formIndex?: number, shiny?: boolean, variant?: number): string {
|
||||||
if (formIndex === undefined) {
|
if (formIndex === undefined) {
|
||||||
formIndex = this.formIndex;
|
formIndex = this.formIndex;
|
||||||
}
|
}
|
||||||
@ -379,7 +396,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCryKey(formIndex?: integer): string {
|
getCryKey(formIndex?: number): string {
|
||||||
let speciesId = this.speciesId;
|
let speciesId = this.speciesId;
|
||||||
if (this.speciesId > 2000) {
|
if (this.speciesId > 2000) {
|
||||||
switch (this.speciesId) {
|
switch (this.speciesId) {
|
||||||
@ -443,10 +460,10 @@ export abstract class PokemonSpeciesForm {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
return `cry/${ret}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
validateStarterMoveset(moveset: StarterMoveset, eggMoves: integer): boolean {
|
validateStarterMoveset(moveset: StarterMoveset, eggMoves: number): boolean {
|
||||||
const rootSpeciesId = this.getRootSpeciesId();
|
const rootSpeciesId = this.getRootSpeciesId();
|
||||||
for (const moveId of moveset) {
|
for (const moveId of moveset) {
|
||||||
if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) {
|
if (speciesEggMoves.hasOwnProperty(rootSpeciesId)) {
|
||||||
@ -467,11 +484,11 @@ export abstract class PokemonSpeciesForm {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAssets(scene: BattleScene, female: boolean, formIndex?: integer, shiny?: boolean, variant?: Variant, startLoad?: boolean): Promise<void> {
|
loadAssets(scene: BattleScene, female: boolean, formIndex?: number, shiny?: boolean, variant?: Variant, startLoad?: boolean): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant);
|
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant);
|
||||||
scene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant));
|
scene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant));
|
||||||
scene.load.audio(`cry/${this.getCryKey(formIndex)}`, `audio/cry/${this.getCryKey(formIndex)}.m4a`);
|
scene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`);
|
||||||
scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
|
scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
|
||||||
const originalWarn = console.warn;
|
const originalWarn = console.warn;
|
||||||
// Ignore warnings for missing frames, because there will be a lot
|
// Ignore warnings for missing frames, because there will be a lot
|
||||||
@ -529,14 +546,14 @@ export abstract class PokemonSpeciesForm {
|
|||||||
if (cry?.pendingRemove) {
|
if (cry?.pendingRemove) {
|
||||||
cry = null;
|
cry = null;
|
||||||
}
|
}
|
||||||
cry = scene.playSound(`cry/${(cry ?? cryKey)}`, soundConfig);
|
cry = scene.playSound(cry ?? cryKey, soundConfig);
|
||||||
if (ignorePlay) {
|
if (ignorePlay) {
|
||||||
cry.stop();
|
cry.stop();
|
||||||
}
|
}
|
||||||
return cry;
|
return cry;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateCandyColors(scene: BattleScene): integer[][] {
|
generateCandyColors(scene: BattleScene): number[][] {
|
||||||
const sourceTexture = scene.textures.get(this.getSpriteKey(false));
|
const sourceTexture = scene.textures.get(this.getSpriteKey(false));
|
||||||
|
|
||||||
const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame];
|
const sourceFrame = sourceTexture.frames[sourceTexture.firstFrame];
|
||||||
@ -544,7 +561,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
|
|
||||||
const spriteColors: integer[][] = [];
|
const spriteColors: number[][] = [];
|
||||||
|
|
||||||
const context = canvas.getContext("2d");
|
const context = canvas.getContext("2d");
|
||||||
const frame = sourceFrame;
|
const frame = sourceFrame;
|
||||||
@ -567,7 +584,7 @@ export abstract class PokemonSpeciesForm {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < pixelData.length; i += 4) {
|
for (let i = 0; i < pixelData.length; i += 4) {
|
||||||
const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0);
|
const total = pixelData.slice(i, i + 3).reduce((total: number, value: number) => total + value, 0);
|
||||||
if (!total) {
|
if (!total) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -586,27 +603,28 @@ export abstract class PokemonSpeciesForm {
|
|||||||
|
|
||||||
Math.random = originalRandom;
|
Math.random = originalRandom;
|
||||||
|
|
||||||
return Array.from(paletteColors.keys()).map(c => Object.values(rgbaFromArgb(c)) as integer[]);
|
return Array.from(paletteColors.keys()).map(c => Object.values(rgbaFromArgb(c)) as number[]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
export default class PokemonSpecies extends PokemonSpeciesForm implements Localizable {
|
||||||
public name: string;
|
public name: string;
|
||||||
public subLegendary: boolean;
|
readonly subLegendary: boolean;
|
||||||
public legendary: boolean;
|
readonly legendary: boolean;
|
||||||
public mythical: boolean;
|
readonly mythical: boolean;
|
||||||
public species: string;
|
readonly species: string;
|
||||||
public growthRate: GrowthRate;
|
readonly growthRate: GrowthRate;
|
||||||
public malePercent: number | null;
|
readonly malePercent: number | null;
|
||||||
public genderDiffs: boolean;
|
readonly genderDiffs: boolean;
|
||||||
public canChangeForm: boolean;
|
readonly canChangeForm: boolean;
|
||||||
public forms: PokemonForm[];
|
readonly forms: PokemonForm[];
|
||||||
|
|
||||||
constructor(id: Species, generation: integer, subLegendary: boolean, legendary: boolean, mythical: boolean, species: string,
|
constructor(id: Species, generation: number, subLegendary: boolean, legendary: boolean, mythical: boolean, species: string,
|
||||||
type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
||||||
baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer,
|
baseTotal: number, baseHp: number, baseAtk: number, baseDef: number, baseSpatk: number, baseSpdef: number, baseSpd: number,
|
||||||
catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number | null,
|
catchRate: number, baseFriendship: number, baseExp: number, growthRate: GrowthRate, malePercent: number | null,
|
||||||
genderDiffs: boolean, canChangeForm?: boolean, ...forms: PokemonForm[]) {
|
genderDiffs: boolean, canChangeForm?: boolean, ...forms: PokemonForm[]
|
||||||
|
) {
|
||||||
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
|
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
|
||||||
catchRate, baseFriendship, baseExp, genderDiffs, false);
|
catchRate, baseFriendship, baseExp, genderDiffs, false);
|
||||||
this.speciesId = id;
|
this.speciesId = id;
|
||||||
@ -631,7 +649,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getName(formIndex?: integer): string {
|
getName(formIndex?: number): string {
|
||||||
if (formIndex !== undefined && this.forms.length) {
|
if (formIndex !== undefined && this.forms.length) {
|
||||||
const form = this.forms[formIndex];
|
const form = this.forms[formIndex];
|
||||||
let key: string | null;
|
let key: string | null;
|
||||||
@ -662,11 +680,11 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
this.name = i18next.t(`pokemon:${Species[this.speciesId].toLowerCase()}`);
|
this.name = i18next.t(`pokemon:${Species[this.speciesId].toLowerCase()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
getWildSpeciesForLevel(level: integer, allowEvolving: boolean, isBoss: boolean, gameMode: GameMode): Species {
|
getWildSpeciesForLevel(level: number, allowEvolving: boolean, isBoss: boolean, gameMode: GameMode): Species {
|
||||||
return this.getSpeciesForLevel(level, allowEvolving, false, (isBoss ? PartyMemberStrength.WEAKER : PartyMemberStrength.AVERAGE) + (gameMode?.isEndless ? 1 : 0));
|
return this.getSpeciesForLevel(level, allowEvolving, false, (isBoss ? PartyMemberStrength.WEAKER : PartyMemberStrength.AVERAGE) + (gameMode?.isEndless ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
getTrainerSpeciesForLevel(level: integer, allowEvolving: boolean = false, strength: PartyMemberStrength, currentWave: number = 0): Species {
|
getTrainerSpeciesForLevel(level: number, allowEvolving: boolean = false, strength: PartyMemberStrength, currentWave: number = 0): Species {
|
||||||
return this.getSpeciesForLevel(level, allowEvolving, true, strength, currentWave);
|
return this.getSpeciesForLevel(level, allowEvolving, true, strength, currentWave);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,7 +706,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
* @param strength {@linkcode PartyMemberStrength} The strength of the party member in question
|
* @param strength {@linkcode PartyMemberStrength} The strength of the party member in question
|
||||||
* @returns {@linkcode integer} The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance.
|
* @returns {@linkcode integer} The level difference from expected evolution level tolerated for a mon to be unevolved. Lower value = higher evolution chance.
|
||||||
*/
|
*/
|
||||||
private getStrengthLevelDiff(strength: PartyMemberStrength): integer {
|
private getStrengthLevelDiff(strength: PartyMemberStrength): number {
|
||||||
switch (Math.min(strength, PartyMemberStrength.STRONGER)) {
|
switch (Math.min(strength, PartyMemberStrength.STRONGER)) {
|
||||||
case PartyMemberStrength.WEAKEST:
|
case PartyMemberStrength.WEAKEST:
|
||||||
return 60;
|
return 60;
|
||||||
@ -705,7 +723,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpeciesForLevel(level: integer, allowEvolving: boolean = false, forTrainer: boolean = false, strength: PartyMemberStrength = PartyMemberStrength.WEAKER, currentWave: number = 0): Species {
|
getSpeciesForLevel(level: number, allowEvolving: boolean = false, forTrainer: boolean = false, strength: PartyMemberStrength = PartyMemberStrength.WEAKER, currentWave: number = 0): Species {
|
||||||
const prevolutionLevels = this.getPrevolutionLevels();
|
const prevolutionLevels = this.getPrevolutionLevels();
|
||||||
|
|
||||||
if (prevolutionLevels.length) {
|
if (prevolutionLevels.length) {
|
||||||
@ -847,7 +865,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This could definitely be written better and more accurate to the getSpeciesForLevel logic, but it is only for generating movesets for evolved Pokemon
|
// This could definitely be written better and more accurate to the getSpeciesForLevel logic, but it is only for generating movesets for evolved Pokemon
|
||||||
getSimulatedEvolutionChain(currentLevel: integer, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): EvolutionLevel[] {
|
getSimulatedEvolutionChain(currentLevel: number, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): EvolutionLevel[] {
|
||||||
const ret: EvolutionLevel[] = [];
|
const ret: EvolutionLevel[] = [];
|
||||||
if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
|
if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) {
|
||||||
const prevolutionLevels = this.getPrevolutionLevels().reverse();
|
const prevolutionLevels = this.getPrevolutionLevels().reverse();
|
||||||
@ -899,7 +917,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali
|
|||||||
return variantData.hasOwnProperty(variantDataIndex) || variantData.hasOwnProperty(this.speciesId);
|
return variantData.hasOwnProperty(variantDataIndex) || variantData.hasOwnProperty(this.speciesId);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormSpriteKey(formIndex?: integer) {
|
getFormSpriteKey(formIndex?: number) {
|
||||||
if (this.forms.length && (formIndex !== undefined && formIndex >= this.forms.length)) {
|
if (this.forms.length && (formIndex !== undefined && formIndex >= this.forms.length)) {
|
||||||
console.warn(`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`);
|
console.warn(`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`);
|
||||||
formIndex = Math.min(formIndex, this.forms.length - 1);
|
formIndex = Math.min(formIndex, this.forms.length - 1);
|
||||||
@ -919,16 +937,17 @@ export class PokemonForm extends PokemonSpeciesForm {
|
|||||||
private starterSelectableKeys: string[] = [ "10", "50", "10-pc", "50-pc", "red", "orange", "yellow", "green", "blue", "indigo", "violet" ];
|
private starterSelectableKeys: string[] = [ "10", "50", "10-pc", "50-pc", "red", "orange", "yellow", "green", "blue", "indigo", "violet" ];
|
||||||
|
|
||||||
constructor(formName: string, formKey: string, type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
constructor(formName: string, formKey: string, type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities,
|
||||||
baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer,
|
baseTotal: number, baseHp: number, baseAtk: number, baseDef: number, baseSpatk: number, baseSpdef: number, baseSpd: number,
|
||||||
catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs?: boolean, formSpriteKey?: string | null, isStarterSelectable?: boolean, ) {
|
catchRate: number, baseFriendship: number, baseExp: number, genderDiffs: boolean = false, formSpriteKey: string | null = null, isStarterSelectable: boolean = false
|
||||||
|
) {
|
||||||
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
|
super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd,
|
||||||
catchRate, baseFriendship, baseExp, !!genderDiffs, (!!isStarterSelectable || !formKey));
|
catchRate, baseFriendship, baseExp, genderDiffs, (isStarterSelectable || !formKey));
|
||||||
this.formName = formName;
|
this.formName = formName;
|
||||||
this.formKey = formKey;
|
this.formKey = formKey;
|
||||||
this.formSpriteKey = formSpriteKey !== undefined ? formSpriteKey : null;
|
this.formSpriteKey = formSpriteKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFormSpriteKey(_formIndex?: integer) {
|
getFormSpriteKey(_formIndex?: number) {
|
||||||
return this.formSpriteKey !== null ? this.formSpriteKey : this.formKey;
|
return this.formSpriteKey !== null ? this.formSpriteKey : this.formKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,4 +28,5 @@ export enum ArenaTagType {
|
|||||||
FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE",
|
FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE",
|
||||||
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
|
WATER_FIRE_PLEDGE = "WATER_FIRE_PLEDGE",
|
||||||
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
|
GRASS_WATER_PLEDGE = "GRASS_WATER_PLEDGE",
|
||||||
|
FAIRY_LOCK = "FAIRY_LOCK",
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ export enum BattlerTagType {
|
|||||||
DRAGON_CHEER = "DRAGON_CHEER",
|
DRAGON_CHEER = "DRAGON_CHEER",
|
||||||
NO_RETREAT = "NO_RETREAT",
|
NO_RETREAT = "NO_RETREAT",
|
||||||
GORILLA_TACTICS = "GORILLA_TACTICS",
|
GORILLA_TACTICS = "GORILLA_TACTICS",
|
||||||
|
UNBURDEN = "UNBURDEN",
|
||||||
THROAT_CHOPPED = "THROAT_CHOPPED",
|
THROAT_CHOPPED = "THROAT_CHOPPED",
|
||||||
TAR_SHOT = "TAR_SHOT",
|
TAR_SHOT = "TAR_SHOT",
|
||||||
BURNED_UP = "BURNED_UP",
|
BURNED_UP = "BURNED_UP",
|
||||||
|
@ -12,7 +12,7 @@ import * as Utils from "#app/utils";
|
|||||||
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
|
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "#app/data/type";
|
||||||
import { getLevelTotalExp } from "#app/data/exp";
|
import { getLevelTotalExp } from "#app/data/exp";
|
||||||
import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
|
import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
|
||||||
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier } from "#app/modifier/modifier";
|
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier, PokemonMultiHitModifier } from "#app/modifier/modifier";
|
||||||
import { PokeballType } from "#app/data/pokeball";
|
import { PokeballType } from "#app/data/pokeball";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
import { initMoveAnim, loadMoveAnimAssets } from "#app/data/battle-anims";
|
||||||
@ -22,7 +22,7 @@ import { reverseCompatibleTms, tmSpecies, tmPoolTiers } from "#app/data/balance/
|
|||||||
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
|
import { BattlerTag, BattlerTagLapseType, EncoreTag, GroundedTag, HighestStatBoostTag, SubstituteTag, TypeImmuneTag, getBattlerTag, SemiInvulnerableTag, TypeBoostTag, MoveRestrictionBattlerTag, ExposedTag, DragonCheerTag, CritBoostTag, TrappedTag, TarShotTag, AutotomizedTag, PowerTrickTag } from "../data/battler-tags";
|
||||||
import { WeatherType } from "#app/data/weather";
|
import { WeatherType } from "#app/data/weather";
|
||||||
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
import { ArenaTagSide, NoCritTag, WeakenMoveScreenTag } from "#app/data/arena-tag";
|
||||||
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr } from "#app/data/ability";
|
import { Ability, AbAttr, StatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, IgnoreOpponentStatStagesAbAttr, MoveImmunityAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyStatMultiplierAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, ConditionalCritAbAttr, applyFieldStatMultiplierAbAttrs, FieldMultiplyStatAbAttr, AddSecondStrikeAbAttr, UserFieldStatusEffectImmunityAbAttr, UserFieldBattlerTagImmunityAbAttr, BattlerTagImmunityAbAttr, MoveTypeChangeAbAttr, FullHpResistTypeAbAttr, applyCheckTrappedAbAttrs, CheckTrappedAbAttr, PostSetStatusAbAttr, applyPostSetStatusAbAttrs, InfiltratorAbAttr, AlliedFieldDamageReductionAbAttr, PostDamageAbAttr, applyPostDamageAbAttrs, PostDamageForceSwitchAbAttr } from "#app/data/ability";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
@ -230,7 +230,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.variant === undefined) {
|
if (this.variant === undefined) {
|
||||||
this.variant = this.shiny ? this.generateVariant() : 0;
|
this.variant = this.shiny ? this.generateShinyVariant() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customPokemonData = new CustomPokemonData();
|
this.customPokemonData = new CustomPokemonData();
|
||||||
@ -328,35 +328,45 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.scene.field.getIndex(this) > -1;
|
return this.scene.field.getIndex(this) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
isFainted(checkStatus?: boolean): boolean {
|
/**
|
||||||
return !this.hp && (!checkStatus || this.status?.effect === StatusEffect.FAINT);
|
* Checks if a pokemon is fainted (ie: its `hp <= 0`).
|
||||||
|
* It's usually better to call {@linkcode isAllowedInBattle()}
|
||||||
|
* @param checkStatus `true` to also check that the pokemon's status is {@linkcode StatusEffect.FAINT}
|
||||||
|
* @returns `true` if the pokemon is fainted
|
||||||
|
*/
|
||||||
|
public isFainted(checkStatus: boolean = false): boolean {
|
||||||
|
return this.hp <= 0 && (!checkStatus || this.status?.effect === StatusEffect.FAINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this pokemon is both not fainted (or a fled wild pokemon) and allowed to be in battle.
|
* Check if this pokemon is both not fainted and allowed to be in battle based on currently active challenges.
|
||||||
* This is frequently a better alternative to {@link isFainted}
|
* @returns {boolean} `true` if pokemon is allowed in battle
|
||||||
* @returns {boolean} True if pokemon is allowed in battle
|
|
||||||
*/
|
*/
|
||||||
isAllowedInBattle(): boolean {
|
public isAllowedInBattle(): boolean {
|
||||||
return !this.isFainted() && this.isAllowed();
|
return !this.isFainted() && this.isAllowedInChallenge();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this pokemon is allowed (no challenge exclusion)
|
* Check if this pokemon is allowed based on any active challenges.
|
||||||
* This is frequently a better alternative to {@link isFainted}
|
* It's usually better to call {@linkcode isAllowedInBattle()}
|
||||||
* @returns {boolean} True if pokemon is allowed in battle
|
* @returns {boolean} `true` if pokemon is allowed in battle
|
||||||
*/
|
*/
|
||||||
isAllowed(): boolean {
|
public isAllowedInChallenge(): boolean {
|
||||||
const challengeAllowed = new Utils.BooleanHolder(true);
|
const challengeAllowed = new Utils.BooleanHolder(true);
|
||||||
applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed);
|
applyChallenges(this.scene.gameMode, ChallengeType.POKEMON_IN_BATTLE, this, challengeAllowed);
|
||||||
return challengeAllowed.value;
|
return challengeAllowed.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
isActive(onField?: boolean): boolean {
|
/**
|
||||||
|
* Checks if the pokemon is allowed in battle (ie: not fainted, and allowed under any active challenges).
|
||||||
|
* @param onField `true` to also check if the pokemon is currently on the field, defaults to `false`
|
||||||
|
* @returns `true` if the pokemon is "active". Returns `false` if there is no active {@linkcode BattleScene}
|
||||||
|
*/
|
||||||
|
public isActive(onField: boolean = false): boolean {
|
||||||
if (!this.scene) {
|
if (!this.scene) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return this.isAllowedInBattle() && !!this.scene && (!onField || this.isOnField());
|
return this.isAllowedInBattle() && (!onField || this.isOnField());
|
||||||
}
|
}
|
||||||
|
|
||||||
getDexAttr(): bigint {
|
getDexAttr(): bigint {
|
||||||
@ -431,38 +441,26 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
if (this.shiny) {
|
if (this.shiny) {
|
||||||
const populateVariantColors = (key: string, back: boolean = false): Promise<void> => {
|
const populateVariantColors = (isBackSprite: boolean = false): Promise<void> => {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const battleSpritePath = this.getBattleSpriteAtlasPath(back, ignoreOverride).replace("variant/", "").replace(/_[1-3]$/, "");
|
const battleSpritePath = this.getBattleSpriteAtlasPath(isBackSprite, ignoreOverride).replace("variant/", "").replace(/_[1-3]$/, "");
|
||||||
let config = variantData;
|
let config = variantData;
|
||||||
const useExpSprite = this.scene.experimentalSprites && this.scene.hasExpSprite(this.getBattleSpriteKey(back, ignoreOverride));
|
const useExpSprite = this.scene.experimentalSprites && this.scene.hasExpSprite(this.getBattleSpriteKey(isBackSprite, ignoreOverride));
|
||||||
battleSpritePath.split("/").map(p => config ? config = config[p] : null);
|
battleSpritePath.split("/").map(p => config ? config = config[p] : null);
|
||||||
const variantSet: VariantSet = config as VariantSet;
|
const variantSet: VariantSet = config as VariantSet;
|
||||||
if (variantSet && variantSet[this.variant] === 1) {
|
if (variantSet && variantSet[this.variant] === 1) {
|
||||||
if (variantColorCache.hasOwnProperty(key)) {
|
const cacheKey = this.getBattleSpriteKey(isBackSprite);
|
||||||
return resolve();
|
if (!variantColorCache.hasOwnProperty(cacheKey)) {
|
||||||
|
this.populateVariantColorCache(cacheKey, useExpSprite, battleSpritePath);
|
||||||
}
|
}
|
||||||
this.scene.cachedFetch(`./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`).
|
|
||||||
then(res => {
|
|
||||||
// Prevent the JSON from processing if it failed to load
|
|
||||||
if (!res.ok) {
|
|
||||||
console.error(`Could not load ${res.url}!`);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
return res.json();
|
|
||||||
}).then(c => {
|
|
||||||
variantColorCache[key] = c;
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
if (this.isPlayer()) {
|
if (this.isPlayer()) {
|
||||||
Promise.all([ populateVariantColors(this.getBattleSpriteKey(false)), populateVariantColors(this.getBattleSpriteKey(true), true) ]).then(() => updateFusionPaletteAndResolve());
|
Promise.all([ populateVariantColors(false), populateVariantColors(true) ]).then(() => updateFusionPaletteAndResolve());
|
||||||
} else {
|
} else {
|
||||||
populateVariantColors(this.getBattleSpriteKey(false)).then(() => updateFusionPaletteAndResolve());
|
populateVariantColors(false).then(() => updateFusionPaletteAndResolve());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
updateFusionPaletteAndResolve();
|
updateFusionPaletteAndResolve();
|
||||||
@ -475,6 +473,45 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gracefully handle errors loading a variant sprite. Log if it fails and attempt to fall back on
|
||||||
|
* non-experimental sprites before giving up.
|
||||||
|
*
|
||||||
|
* @param cacheKey the cache key for the variant color sprite
|
||||||
|
* @param attemptedSpritePath the sprite path that failed to load
|
||||||
|
* @param useExpSprite was the attempted sprite experimental
|
||||||
|
* @param battleSpritePath the filename of the sprite
|
||||||
|
* @param optionalParams any additional params to log
|
||||||
|
*/
|
||||||
|
fallbackVariantColor(cacheKey: string, attemptedSpritePath: string, useExpSprite: boolean, battleSpritePath: string, ...optionalParams: any[]) {
|
||||||
|
console.warn(`Could not load ${attemptedSpritePath}!`, ...optionalParams);
|
||||||
|
if (useExpSprite) {
|
||||||
|
this.populateVariantColorCache(cacheKey, false, battleSpritePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to process variant sprite.
|
||||||
|
*
|
||||||
|
* @param cacheKey the cache key for the variant color sprite
|
||||||
|
* @param useExpSprite should the experimental sprite be used
|
||||||
|
* @param battleSpritePath the filename of the sprite
|
||||||
|
*/
|
||||||
|
populateVariantColorCache(cacheKey: string, useExpSprite: boolean, battleSpritePath: string) {
|
||||||
|
const spritePath = `./images/pokemon/variant/${useExpSprite ? "exp/" : ""}${battleSpritePath}.json`;
|
||||||
|
this.scene.cachedFetch(spritePath).then(res => {
|
||||||
|
// Prevent the JSON from processing if it failed to load
|
||||||
|
if (!res.ok) {
|
||||||
|
return this.fallbackVariantColor(cacheKey, res.url, useExpSprite, battleSpritePath, res.status, res.statusText);
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
}).catch(error => {
|
||||||
|
this.fallbackVariantColor(cacheKey, spritePath, useExpSprite, battleSpritePath, error);
|
||||||
|
}).then(c => {
|
||||||
|
variantColorCache[cacheKey] = c;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getFormKey(): string {
|
getFormKey(): string {
|
||||||
if (!this.species.forms.length || this.species.forms.length <= this.formIndex) {
|
if (!this.species.forms.length || this.species.forms.length <= this.formIndex) {
|
||||||
return "";
|
return "";
|
||||||
@ -948,6 +985,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (this.status && this.status.effect === StatusEffect.PARALYSIS) {
|
if (this.status && this.status.effect === StatusEffect.PARALYSIS) {
|
||||||
ret >>= 1;
|
ret >>= 1;
|
||||||
}
|
}
|
||||||
|
if (this.getTag(BattlerTagType.UNBURDEN) && !this.scene.getField(true).some(pokemon => pokemon !== this && pokemon.hasAbilityWithAttr(SuppressFieldAbilitiesAbAttr))) {
|
||||||
|
ret *= 2;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1162,7 +1202,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @returns an array of {@linkcode Moves}, the length of which is determined
|
* @returns an array of {@linkcode Moves}, the length of which is determined
|
||||||
* by how many learnable moves there are for the {@linkcode Pokemon}.
|
* by how many learnable moves there are for the {@linkcode Pokemon}.
|
||||||
*/
|
*/
|
||||||
getLearnableLevelMoves(): Moves[] {
|
public getLearnableLevelMoves(): Moves[] {
|
||||||
let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]);
|
let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]);
|
||||||
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
|
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
|
||||||
levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
|
levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
|
||||||
@ -1176,13 +1216,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the types of a pokemon
|
* Gets the types of a pokemon
|
||||||
* @param includeTeraType boolean to include tera-formed type, default false
|
* @param includeTeraType - `true` to include tera-formed type; Default: `false`
|
||||||
* @param forDefend boolean if the pokemon is defending from an attack
|
* @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
|
||||||
* @param ignoreOverride boolean if true, ignore ability changing effects
|
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||||
* @returns array of {@linkcode Type}
|
* @returns array of {@linkcode Type}
|
||||||
*/
|
*/
|
||||||
getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean): Type[] {
|
public getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride: boolean = false): Type[] {
|
||||||
const types : Type[] = [];
|
const types: Type[] = [];
|
||||||
|
|
||||||
if (includeTeraType) {
|
if (includeTeraType) {
|
||||||
const teraType = this.getTeraType();
|
const teraType = this.getTeraType();
|
||||||
@ -1247,14 +1287,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this.scene potentially can be undefined for a fainted pokemon in doubles
|
// become UNKNOWN if no types are present
|
||||||
// use optional chaining to avoid runtime errors
|
if (!types.length) {
|
||||||
|
|
||||||
if (!types.length) { // become UNKNOWN if no types are present
|
|
||||||
types.push(Type.UNKNOWN);
|
types.push(Type.UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (types.length > 1 && types.includes(Type.UNKNOWN)) { // remove UNKNOWN if other types are present
|
// remove UNKNOWN if other types are present
|
||||||
|
if (types.length > 1 && types.includes(Type.UNKNOWN)) {
|
||||||
const index = types.indexOf(Type.UNKNOWN);
|
const index = types.indexOf(Type.UNKNOWN);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
types.splice(index, 1);
|
types.splice(index, 1);
|
||||||
@ -1274,19 +1313,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
|
|
||||||
isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride?: boolean): boolean {
|
/**
|
||||||
return !!this.getTypes(includeTeraType, forDefend, ignoreOverride).some(t => t === type);
|
* Checks if the pokemon's typing includes the specified type
|
||||||
|
* @param type - {@linkcode Type} to check
|
||||||
|
* @param includeTeraType - `true` to include tera-formed type; Default: `true`
|
||||||
|
* @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
|
||||||
|
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||||
|
* @returns `true` if the Pokemon's type matches
|
||||||
|
*/
|
||||||
|
public isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride: boolean = false): boolean {
|
||||||
|
return this.getTypes(includeTeraType, forDefend, ignoreOverride).some((t) => t === type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects.
|
* Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects.
|
||||||
* This should rarely be called, most of the time {@link hasAbility} or {@link hasAbilityWithAttr} are better used as
|
* This should rarely be called, most of the time {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as
|
||||||
* those check both the passive and non-passive abilities and account for ability suppression.
|
* those check both the passive and non-passive abilities and account for ability suppression.
|
||||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||||
* @param {boolean} ignoreOverride If true, ignore ability changing effects
|
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||||
* @returns {Ability} The non-passive ability of the pokemon
|
* @returns The non-passive {@linkcode Ability} of the pokemon
|
||||||
*/
|
*/
|
||||||
getAbility(ignoreOverride?: boolean): Ability {
|
public getAbility(ignoreOverride: boolean = false): Ability {
|
||||||
if (!ignoreOverride && this.summonData?.ability) {
|
if (!ignoreOverride && this.summonData?.ability) {
|
||||||
return allAbilities[this.summonData.ability];
|
return allAbilities[this.summonData.ability];
|
||||||
}
|
}
|
||||||
@ -1315,12 +1362,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the passive ability of the pokemon. This should rarely be called, most of the time
|
* Gets the passive ability of the pokemon. This should rarely be called, most of the time
|
||||||
* {@link hasAbility} or {@link hasAbilityWithAttr} are better used as those check both the passive and
|
* {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as those check both the passive and
|
||||||
* non-passive abilities and account for ability suppression.
|
* non-passive abilities and account for ability suppression.
|
||||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||||
* @returns {Ability} The passive ability of the pokemon
|
* @returns The passive {@linkcode Ability} of the pokemon
|
||||||
*/
|
*/
|
||||||
getPassiveAbility(): Ability {
|
public getPassiveAbility(): Ability {
|
||||||
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
|
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
|
||||||
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
|
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
|
||||||
}
|
}
|
||||||
@ -1342,12 +1389,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* Gets a list of all instances of a given ability attribute among abilities this pokemon has.
|
* Gets a list of all instances of a given ability attribute among abilities this pokemon has.
|
||||||
* Accounts for all the various effects which can affect whether an ability will be present or
|
* Accounts for all the various effects which can affect whether an ability will be present or
|
||||||
* in effect, and both passive and non-passive.
|
* in effect, and both passive and non-passive.
|
||||||
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
|
* @param attrType - {@linkcode AbAttr} The ability attribute to check for.
|
||||||
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
|
* @param canApply - If `false`, it doesn't check whether the ability is currently active; Default `true`
|
||||||
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
|
* @param ignoreOverride - If `true`, it ignores ability changing effects; Default `false`
|
||||||
* @returns A list of all the ability attributes on this ability.
|
* @returns An array of all the ability attributes on this ability.
|
||||||
*/
|
*/
|
||||||
getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] {
|
public getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride: boolean = false): T[] {
|
||||||
const abilityAttrs: T[] = [];
|
const abilityAttrs: T[] = [];
|
||||||
|
|
||||||
if (!canApply || this.canApplyAbility()) {
|
if (!canApply || this.canApplyAbility()) {
|
||||||
@ -1366,12 +1413,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* - bought with starter candy
|
* - bought with starter candy
|
||||||
* - set by override
|
* - set by override
|
||||||
* - is a boss pokemon
|
* - is a boss pokemon
|
||||||
* @returns whether or not a pokemon should have a passive
|
* @returns `true` if the Pokemon has a passive
|
||||||
*/
|
*/
|
||||||
hasPassive(): boolean {
|
public hasPassive(): boolean {
|
||||||
// returns override if valid for current case
|
// returns override if valid for current case
|
||||||
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer()) ||
|
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer())
|
||||||
(Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
|
|| (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1390,12 +1437,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether an ability of a pokemon can be currently applied. This should rarely be
|
* Checks whether an ability of a pokemon can be currently applied. This should rarely be
|
||||||
* directly called, as {@link hasAbility} and {@link hasAbilityWithAttr} already call this.
|
* directly called, as {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} already call this.
|
||||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||||
* @param {boolean} passive If true, check if passive can be applied instead of non-passive
|
* @param passive If true, check if passive can be applied instead of non-passive
|
||||||
* @returns {Ability} The passive ability of the pokemon
|
* @returns `true` if the ability can be applied
|
||||||
*/
|
*/
|
||||||
canApplyAbility(passive: boolean = false): boolean {
|
public canApplyAbility(passive: boolean = false): boolean {
|
||||||
if (passive && !this.hasPassive()) {
|
if (passive && !this.hasPassive()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1424,7 +1471,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (!!this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
return (this.hp > 0 || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1436,7 +1483,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
||||||
* @returns {boolean} Whether the ability is present and active
|
* @returns {boolean} Whether the ability is present and active
|
||||||
*/
|
*/
|
||||||
hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
public hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||||
if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) {
|
if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1456,7 +1503,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
||||||
* @returns {boolean} Whether an ability with that attribute is present and active
|
* @returns {boolean} Whether an ability with that attribute is present and active
|
||||||
*/
|
*/
|
||||||
hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
public hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||||
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
|
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1471,7 +1518,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
|
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
|
||||||
* @returns the kg of the Pokemon (minimum of 0.1)
|
* @returns the kg of the Pokemon (minimum of 0.1)
|
||||||
*/
|
*/
|
||||||
getWeight(): number {
|
public getWeight(): number {
|
||||||
const autotomizedTag = this.getTag(AutotomizedTag);
|
const autotomizedTag = this.getTag(AutotomizedTag);
|
||||||
let weightRemoved = 0;
|
let weightRemoved = 0;
|
||||||
if (!Utils.isNullOrUndefined(autotomizedTag)) {
|
if (!Utils.isNullOrUndefined(autotomizedTag)) {
|
||||||
@ -1486,10 +1533,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the tera-formed type of the pokemon, or UNKNOWN if not present
|
* @returns The tera-formed type of the pokemon, or {@linkcode Type.UNKNOWN} if not present
|
||||||
* @returns the {@linkcode Type}
|
|
||||||
*/
|
*/
|
||||||
getTeraType(): Type {
|
public getTeraType(): Type {
|
||||||
// this.scene can be undefined for a fainted mon in doubles
|
// this.scene can be undefined for a fainted mon in doubles
|
||||||
if (this.scene !== undefined) {
|
if (this.scene !== undefined) {
|
||||||
const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier
|
const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier
|
||||||
@ -1503,23 +1549,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return Type.UNKNOWN;
|
return Type.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
isTerastallized(): boolean {
|
public isTerastallized(): boolean {
|
||||||
return this.getTeraType() !== Type.UNKNOWN;
|
return this.getTeraType() !== Type.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
isGrounded(): boolean {
|
public isGrounded(): boolean {
|
||||||
return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag));
|
return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether this Pokemon is prevented from running or switching due
|
* Determines whether this Pokemon is prevented from running or switching due
|
||||||
* to effects from moves and/or abilities.
|
* to effects from moves and/or abilities.
|
||||||
* @param trappedAbMessages `string[]` If defined, ability trigger messages
|
* @param trappedAbMessages - If defined, ability trigger messages
|
||||||
* (e.g. from Shadow Tag) are forwarded through this array.
|
* (e.g. from Shadow Tag) are forwarded through this array.
|
||||||
* @param simulated `boolean` if `true`, applies abilities via simulated calls.
|
* @param simulated - If `true`, applies abilities via simulated calls.
|
||||||
* @returns
|
* @returns `true` if the pokemon is trapped
|
||||||
*/
|
*/
|
||||||
isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean {
|
public isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean {
|
||||||
if (this.isOfType(Type.GHOST)) {
|
if (this.isOfType(Type.GHOST)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1532,21 +1578,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const opposingFieldUnfiltered = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
|
const opposingFieldUnfiltered = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
|
||||||
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
const opposingField = opposingFieldUnfiltered.filter(enemyPkm => enemyPkm.switchOutStatus === false);
|
||||||
|
|
||||||
opposingField.forEach(opponent =>
|
opposingField.forEach((opponent) =>
|
||||||
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated)
|
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated)
|
||||||
);
|
);
|
||||||
|
|
||||||
return (trappedByAbility.value || !!this.getTag(TrappedTag));
|
const side = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
|
return (trappedByAbility.value || !!this.getTag(TrappedTag) || !!this.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, side));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the type of a move when used by this Pokemon after
|
* Calculates the type of a move when used by this Pokemon after
|
||||||
* type-changing move and ability attributes have applied.
|
* type-changing move and ability attributes have applied.
|
||||||
* @param move {@linkcode Move} The move being used.
|
* @param move - {@linkcode Move} The move being used.
|
||||||
* @param simulated If `true`, prevents showing abilities applied in this calculation.
|
* @param simulated - If `true`, prevents showing abilities applied in this calculation.
|
||||||
* @returns the {@linkcode Type} of the move after attributes are applied
|
* @returns The {@linkcode Type} of the move after attributes are applied
|
||||||
*/
|
*/
|
||||||
getMoveType(move: Move, simulated: boolean = true): Type {
|
public getMoveType(move: Move, simulated: boolean = true): Type {
|
||||||
const moveTypeHolder = new Utils.NumberHolder(move.type);
|
const moveTypeHolder = new Utils.NumberHolder(move.type);
|
||||||
|
|
||||||
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
||||||
@ -1911,13 +1958,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* Function that tries to set a Pokemon shiny based on seed.
|
* 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.
|
* For manual use only, usually to roll a Pokemon's shiny chance a second time.
|
||||||
*
|
*
|
||||||
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / 65536
|
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `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 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}
|
* @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
|
* @returns `true` if the Pokemon has been set as a shiny, `false` otherwise
|
||||||
*/
|
*/
|
||||||
trySetShinySeed(thresholdOverride?: integer, applyModifiersToOverride?: boolean): boolean {
|
public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean {
|
||||||
const shinyThreshold = new Utils.IntegerHolder(BASE_SHINY_CHANCE);
|
const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE);
|
||||||
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
||||||
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||||
shinyThreshold.value = thresholdOverride;
|
shinyThreshold.value = thresholdOverride;
|
||||||
@ -1942,13 +1989,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a variant
|
* Generates a shiny variant
|
||||||
* Has a 10% of returning 2 (epic variant)
|
* @returns `0-2`, with the following probabilities:
|
||||||
* And a 30% of returning 1 (rare variant)
|
* - Has a 10% chance of returning `2` (epic variant)
|
||||||
* Returns 0 (basic shiny) if there is no variant or 60% of the time otherwise
|
* - Has a 30% chance of returning `1` (rare variant)
|
||||||
* @returns the shiny variant
|
* - Has a 60% chance of returning `0` (basic shiny)
|
||||||
*/
|
*/
|
||||||
generateVariant(): Variant {
|
protected generateShinyVariant(): Variant {
|
||||||
const formIndex: number = this.formIndex;
|
const formIndex: number = this.formIndex;
|
||||||
let variantDataIndex: string | number = this.species.speciesId;
|
let variantDataIndex: string | number = this.species.speciesId;
|
||||||
if (this.species.forms.length > 0) {
|
if (this.species.forms.length > 0) {
|
||||||
@ -1974,8 +2021,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
generateFusionSpecies(forStarter?: boolean): void {
|
public generateFusionSpecies(forStarter?: boolean): void {
|
||||||
const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE);
|
const hiddenAbilityChance = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
|
||||||
if (!this.hasTrainer()) {
|
if (!this.hasTrainer()) {
|
||||||
this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||||
}
|
}
|
||||||
@ -2024,7 +2071,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.generateName();
|
this.generateName();
|
||||||
}
|
}
|
||||||
|
|
||||||
clearFusionSpecies(): void {
|
public clearFusionSpecies(): void {
|
||||||
this.fusionSpecies = null;
|
this.fusionSpecies = null;
|
||||||
this.fusionFormIndex = 0;
|
this.fusionFormIndex = 0;
|
||||||
this.fusionAbilityIndex = 0;
|
this.fusionAbilityIndex = 0;
|
||||||
@ -2038,12 +2085,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.calculateStats();
|
this.calculateStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAndPopulateMoveset(): void {
|
/** Generates a semi-random moveset for a Pokemon */
|
||||||
|
public generateAndPopulateMoveset(): void {
|
||||||
this.moveset = [];
|
this.moveset = [];
|
||||||
let movePool: [Moves, number][] = [];
|
let movePool: [Moves, number][] = [];
|
||||||
const allLevelMoves = this.getLevelMoves(1, true, true);
|
const allLevelMoves = this.getLevelMoves(1, true, true);
|
||||||
if (!allLevelMoves) {
|
if (!allLevelMoves) {
|
||||||
console.log(this.species.speciesId, "ERROR");
|
console.warn("Error encountered trying to generate moveset for:", this.species.name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2053,16 +2101,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let weight = levelMove[0];
|
let weight = levelMove[0];
|
||||||
if (weight === 0) { // Evo Moves
|
// Evolution Moves
|
||||||
|
if (weight === 0) {
|
||||||
weight = 50;
|
weight = 50;
|
||||||
}
|
}
|
||||||
if (weight === 1 && allMoves[levelMove[1]].power >= 80) { // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
|
// Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
|
||||||
|
if (weight === 1 && allMoves[levelMove[1]].power >= 80) {
|
||||||
weight = 40;
|
weight = 40;
|
||||||
}
|
}
|
||||||
if (allMoves[levelMove[1]].name.endsWith(" (N)")) {
|
if (!movePool.some(m => m[0] === levelMove[1]) && !allMoves[levelMove[1]].name.endsWith(" (N)")) {
|
||||||
weight /= 100;
|
|
||||||
} // Unimplemented level up moves are possible to generate, but 1% of their normal chance.
|
|
||||||
if (!movePool.some(m => m[0] === levelMove[1])) {
|
|
||||||
movePool.push([ levelMove[1], weight ]);
|
movePool.push([ levelMove[1], weight ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2094,7 +2141,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.level >= 60) { // No egg moves below level 60
|
// No egg moves below level 60
|
||||||
|
if (this.level >= 60) {
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
|
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
|
||||||
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
|
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
|
||||||
@ -2102,7 +2150,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
|
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
|
||||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) { // No rare egg moves before e4
|
// No rare egg moves before e4
|
||||||
|
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
|
||||||
movePool.push([ moveId, 30 ]);
|
movePool.push([ moveId, 30 ]);
|
||||||
}
|
}
|
||||||
if (this.fusionSpecies) {
|
if (this.fusionSpecies) {
|
||||||
@ -2113,14 +2162,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
|
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
|
||||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {// No rare egg moves before e4
|
// No rare egg moves before e4
|
||||||
|
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
|
||||||
movePool.push([ moveId, 30 ]);
|
movePool.push([ moveId, 30 ]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isBoss()) { // Bosses never get self ko moves
|
// Bosses never get self ko moves
|
||||||
|
if (this.isBoss()) {
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr));
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr));
|
||||||
}
|
}
|
||||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
||||||
@ -2149,7 +2200,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
|
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
|
||||||
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]);
|
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]);
|
||||||
|
|
||||||
let weightMultiplier = 0.9; // The higher this is the more the game weights towards higher level moves. At 0 all moves are equal weight.
|
/** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */
|
||||||
|
let weightMultiplier = 0.9;
|
||||||
if (this.hasTrainer()) {
|
if (this.hasTrainer()) {
|
||||||
weightMultiplier += 0.7;
|
weightMultiplier += 0.7;
|
||||||
}
|
}
|
||||||
@ -2158,7 +2210,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]);
|
const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]);
|
||||||
|
|
||||||
if (this.hasTrainer() || this.isBoss()) { // Trainers and bosses always force a stab move
|
// Trainers and bosses always force a stab move
|
||||||
|
if (this.hasTrainer() || this.isBoss()) {
|
||||||
const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type));
|
const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type));
|
||||||
|
|
||||||
if (stabMovePool.length) {
|
if (stabMovePool.length) {
|
||||||
@ -2188,8 +2241,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
// Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier.
|
// Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier.
|
||||||
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB.
|
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB.
|
||||||
// Status moves remain unchanged on weight, this encourages 1-2
|
// Status moves remain unchanged on weight, this encourages 1-2
|
||||||
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [ m[0], this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power!) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1] ]); // TODO: is this bang correct?
|
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map((m) => {
|
||||||
} else { // Non-trainer pokemon just use normal weights
|
let ret: number;
|
||||||
|
if (this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type)) {
|
||||||
|
ret = Math.ceil(Math.sqrt(m[1]));
|
||||||
|
} else if (allMoves[m[0]].category !== MoveCategory.STATUS) {
|
||||||
|
ret = Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power ?? 0) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1));
|
||||||
|
} else {
|
||||||
|
ret = m[1];
|
||||||
|
}
|
||||||
|
return [ m[0], ret ];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Non-trainer pokemon just use normal weights
|
||||||
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId));
|
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId));
|
||||||
}
|
}
|
||||||
const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
|
const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
|
||||||
@ -2207,11 +2271,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
public trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||||
const move = this.getMoveset().length > moveIndex
|
const move = this.getMoveset().length > moveIndex
|
||||||
? this.getMoveset()[moveIndex]
|
? this.getMoveset()[moveIndex]
|
||||||
: null;
|
: null;
|
||||||
return move?.isUsable(this, ignorePp)!; // TODO: is this bang correct?
|
return move?.isUsable(this, ignorePp) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
showInfo(): void {
|
showInfo(): void {
|
||||||
@ -2799,7 +2863,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* We explicitly require to ignore the faint phase here, as we want to show the messages
|
* We explicitly require to ignore the faint phase here, as we want to show the messages
|
||||||
* about the critical hit and the super effective/not very effective messages before the faint phase.
|
* about the critical hit and the super effective/not very effective messages before the faint phase.
|
||||||
*/
|
*/
|
||||||
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
|
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true, source);
|
||||||
|
|
||||||
if (damage > 0) {
|
if (damage > 0) {
|
||||||
if (source.isPlayer()) {
|
if (source.isPlayer()) {
|
||||||
@ -2808,10 +2872,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.scene.gameData.gameStats.highestDamage = damage;
|
this.scene.gameData.gameStats.highestDamage = damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source.turnData.damageDealt += damage;
|
source.turnData.totalDamageDealt += damage;
|
||||||
source.turnData.currDamageDealt = damage;
|
source.turnData.singleHitDamageDealt = damage;
|
||||||
this.turnData.damageTaken += damage;
|
this.turnData.damageTaken += damage;
|
||||||
this.battleData.hitCount++;
|
this.battleData.hitCount++;
|
||||||
|
|
||||||
|
// Multi-Lens and Parental Bond check for Wimp Out/Emergency Exit
|
||||||
|
if (this.hasAbilityWithAttr(PostDamageForceSwitchAbAttr)) {
|
||||||
|
const multiHitModifier = source.getHeldItems().find(m => m instanceof PokemonMultiHitModifier);
|
||||||
|
if (multiHitModifier || source.hasAbilityWithAttr(AddSecondStrikeAbAttr)) {
|
||||||
|
applyPostDamageAbAttrs(PostDamageAbAttr, this, damage, this.hasPassive(), false, [], source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
|
const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
|
||||||
this.turnData.attacksReceived.unshift(attackResult);
|
this.turnData.attacksReceived.unshift(attackResult);
|
||||||
if (source.isPlayer() && !this.isPlayer()) {
|
if (source.isPlayer() && !this.isPlayer()) {
|
||||||
@ -2899,7 +2972,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
this.destroySubstitute();
|
this.destroySubstitute();
|
||||||
this.resetSummonData();
|
this.resetSummonData();
|
||||||
}
|
}
|
||||||
|
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2913,12 +2985,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
* @param ignoreFaintPhase boolean to ignore adding a FaintPhase, passsed to damage()
|
||||||
* @returns integer of damage done
|
* @returns integer of damage done
|
||||||
*/
|
*/
|
||||||
damageAndUpdate(damage: integer, result?: DamageResult, critical: boolean = false, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false): integer {
|
damageAndUpdate(damage: integer, result?: DamageResult, critical: boolean = false, ignoreSegments: boolean = false, preventEndure: boolean = false, ignoreFaintPhase: boolean = false, source?: Pokemon): integer {
|
||||||
const damagePhase = new DamagePhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
const damagePhase = new DamagePhase(this.scene, this.getBattlerIndex(), damage, result as DamageResult, critical);
|
||||||
this.scene.unshiftPhase(damagePhase);
|
this.scene.unshiftPhase(damagePhase);
|
||||||
damage = this.damage(damage, ignoreSegments, preventEndure, ignoreFaintPhase);
|
damage = this.damage(damage, ignoreSegments, preventEndure, ignoreFaintPhase);
|
||||||
// Damage amount may have changed, but needed to be queued before calling damage function
|
// Damage amount may have changed, but needed to be queued before calling damage function
|
||||||
damagePhase.updateAmount(damage);
|
damagePhase.updateAmount(damage);
|
||||||
|
applyPostDamageAbAttrs(PostDamageAbAttr, this, damage, this.hasPassive(), false, [], source);
|
||||||
return damage;
|
return damage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3201,7 +3274,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
try {
|
try {
|
||||||
SoundFade.fadeOut(scene, cry, Utils.fixedInt(Math.ceil(duration * 0.2)));
|
SoundFade.fadeOut(scene, cry, Utils.fixedInt(Math.ceil(duration * 0.2)));
|
||||||
fusionCry = this.getFusionSpeciesForm().cry(scene, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig));
|
fusionCry = this.getFusionSpeciesForm().cry(scene, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig));
|
||||||
SoundFade.fadeIn(scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.seVolume, 0);
|
SoundFade.fadeIn(scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.fieldVolume, 0);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
@ -3216,11 +3289,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
return this.fusionFaintCry(callback);
|
return this.fusionFaintCry(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = `cry/${this.species.getCryKey(this.formIndex)}`;
|
const key = this.species.getCryKey(this.formIndex);
|
||||||
//eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
let i = 0;
|
|
||||||
let rate = 0.85;
|
let rate = 0.85;
|
||||||
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
||||||
|
if (!cry || this.scene.fieldVolume === 0) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
const sprite = this.getSprite();
|
const sprite = this.getSprite();
|
||||||
const tintSprite = this.getTintSprite();
|
const tintSprite = this.getTintSprite();
|
||||||
const delay = Math.max(this.scene.sound.get(key).totalDuration * 50, 25);
|
const delay = Math.max(this.scene.sound.get(key).totalDuration * 50, 25);
|
||||||
@ -3235,7 +3309,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
delay: Utils.fixedInt(delay),
|
delay: Utils.fixedInt(delay),
|
||||||
repeat: -1,
|
repeat: -1,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
++i;
|
|
||||||
frameThreshold = sprite.anims.msPerFrame / rate;
|
frameThreshold = sprite.anims.msPerFrame / rate;
|
||||||
frameProgress += delay;
|
frameProgress += delay;
|
||||||
while (frameProgress > frameThreshold) {
|
while (frameProgress > frameThreshold) {
|
||||||
@ -3274,7 +3347,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fusionFaintCry(callback: Function): void {
|
private fusionFaintCry(callback: Function): void {
|
||||||
const key = `cry/${this.species.getCryKey(this.formIndex)}`;
|
const key = this.species.getCryKey(this.formIndex);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let rate = 0.85;
|
let rate = 0.85;
|
||||||
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
||||||
@ -3282,12 +3355,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
const tintSprite = this.getTintSprite();
|
const tintSprite = this.getTintSprite();
|
||||||
let duration = cry.totalDuration * 1000;
|
let duration = cry.totalDuration * 1000;
|
||||||
|
|
||||||
const fusionCryKey = `cry/${this.fusionSpecies?.getCryKey(this.fusionFormIndex)}`;
|
const fusionCryKey = this.fusionSpecies!.getCryKey(this.fusionFormIndex);
|
||||||
let fusionCry = this.scene.playSound(fusionCryKey, { rate: rate }) as AnySound;
|
let fusionCry = this.scene.playSound(fusionCryKey, { rate: rate }) as AnySound;
|
||||||
|
if (!cry || !fusionCry || this.scene.fieldVolume === 0) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
fusionCry.stop();
|
fusionCry.stop();
|
||||||
duration = Math.min(duration, fusionCry.totalDuration * 1000);
|
duration = Math.min(duration, fusionCry.totalDuration * 1000);
|
||||||
fusionCry.destroy();
|
fusionCry.destroy();
|
||||||
|
|
||||||
const delay = Math.max(duration * 0.05, 25);
|
const delay = Math.max(duration * 0.05, 25);
|
||||||
|
|
||||||
let transitionIndex = 0;
|
let transitionIndex = 0;
|
||||||
@ -3325,10 +3400,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
frameProgress -= frameThreshold;
|
frameProgress -= frameThreshold;
|
||||||
}
|
}
|
||||||
if (i === transitionIndex) {
|
if (i === transitionIndex && fusionCryKey) {
|
||||||
SoundFade.fadeOut(this.scene, cry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)));
|
SoundFade.fadeOut(this.scene, cry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)));
|
||||||
fusionCry = this.scene.playSound(fusionCryKey, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0), rate: rate }));
|
fusionCry = this.scene.playSound(fusionCryKey, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0), rate: rate }));
|
||||||
SoundFade.fadeIn(this.scene, fusionCry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), this.scene.masterVolume * this.scene.seVolume, 0);
|
SoundFade.fadeIn(this.scene, fusionCry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), this.scene.masterVolume * this.scene.fieldVolume, 0);
|
||||||
}
|
}
|
||||||
rate *= 0.99;
|
rate *= 0.99;
|
||||||
if (cry && !cry.pendingRemove) {
|
if (cry && !cry.pendingRemove) {
|
||||||
@ -4170,7 +4245,7 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.getFieldIndex(), (slotIndex:integer, option: PartyOption) => {
|
this.scene.ui.setMode(Mode.PARTY, PartyUiMode.REVIVAL_BLESSING, this.getFieldIndex(), (slotIndex:integer, option: PartyOption) => {
|
||||||
if (slotIndex >= 0 && slotIndex < 6) {
|
if (slotIndex >= 0 && slotIndex < 6) {
|
||||||
const pokemon = this.scene.getParty()[slotIndex];
|
const pokemon = this.scene.getPlayerParty()[slotIndex];
|
||||||
if (!pokemon || !pokemon.isFainted()) {
|
if (!pokemon || !pokemon.isFainted()) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
@ -4180,7 +4255,7 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||||
this.scene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name }), 0, true);
|
this.scene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: pokemon.name }), 0, true);
|
||||||
|
|
||||||
if (this.scene.currentBattle.double && this.scene.getParty().length > 1) {
|
if (this.scene.currentBattle.double && this.scene.getPlayerParty().length > 1) {
|
||||||
const allyPokemon = this.getAlly();
|
const allyPokemon = this.getAlly();
|
||||||
if (slotIndex <= 1) {
|
if (slotIndex <= 1) {
|
||||||
// Revived ally pokemon
|
// Revived ally pokemon
|
||||||
@ -4322,7 +4397,7 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
newPokemon.fusionLuck = this.fusionLuck;
|
newPokemon.fusionLuck = this.fusionLuck;
|
||||||
newPokemon.usedTMs = this.usedTMs;
|
newPokemon.usedTMs = this.usedTMs;
|
||||||
|
|
||||||
this.scene.getParty().push(newPokemon);
|
this.scene.getPlayerParty().push(newPokemon);
|
||||||
newPokemon.evolve((!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution)), evoSpecies);
|
newPokemon.evolve((!isFusion ? newEvolution : new FusionSpeciesFormEvolution(this.id, newEvolution)), evoSpecies);
|
||||||
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier
|
||||||
&& m.pokemonId === this.id, true) as PokemonHeldItemModifier[];
|
&& m.pokemonId === this.id, true) as PokemonHeldItemModifier[];
|
||||||
@ -4438,8 +4513,8 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
|
|
||||||
this.generateCompatibleTms();
|
this.generateCompatibleTms();
|
||||||
this.updateInfo(true);
|
this.updateInfo(true);
|
||||||
const fusedPartyMemberIndex = this.scene.getParty().indexOf(pokemon);
|
const fusedPartyMemberIndex = this.scene.getPlayerParty().indexOf(pokemon);
|
||||||
let partyMemberIndex = this.scene.getParty().indexOf(this);
|
let partyMemberIndex = this.scene.getPlayerParty().indexOf(this);
|
||||||
if (partyMemberIndex > fusedPartyMemberIndex) {
|
if (partyMemberIndex > fusedPartyMemberIndex) {
|
||||||
partyMemberIndex--;
|
partyMemberIndex--;
|
||||||
}
|
}
|
||||||
@ -4452,8 +4527,8 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
Promise.allSettled(transferModifiers).then(() => {
|
Promise.allSettled(transferModifiers).then(() => {
|
||||||
this.scene.updateModifiers(true, true).then(() => {
|
this.scene.updateModifiers(true, true).then(() => {
|
||||||
this.scene.removePartyMemberModifiers(fusedPartyMemberIndex);
|
this.scene.removePartyMemberModifiers(fusedPartyMemberIndex);
|
||||||
this.scene.getParty().splice(fusedPartyMemberIndex, 1)[0];
|
this.scene.getPlayerParty().splice(fusedPartyMemberIndex, 1)[0];
|
||||||
const newPartyMemberIndex = this.scene.getParty().indexOf(this);
|
const newPartyMemberIndex = this.scene.getPlayerParty().indexOf(this);
|
||||||
pokemon.getMoveset(true).map((m: PokemonMove) => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m.getMove().id)));
|
pokemon.getMoveset(true).map((m: PokemonMove) => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m.getMove().id)));
|
||||||
pokemon.destroy();
|
pokemon.destroy();
|
||||||
this.updateFusionPalette();
|
this.updateFusionPalette();
|
||||||
@ -4534,7 +4609,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.shiny) {
|
if (this.shiny) {
|
||||||
this.variant = this.generateVariant();
|
this.variant = this.generateShinyVariant();
|
||||||
if (Overrides.OPP_VARIANT_OVERRIDE !== null) {
|
if (Overrides.OPP_VARIANT_OVERRIDE !== null) {
|
||||||
this.variant = Overrides.OPP_VARIANT_OVERRIDE;
|
this.variant = Overrides.OPP_VARIANT_OVERRIDE;
|
||||||
}
|
}
|
||||||
@ -5044,7 +5119,7 @@ export class EnemyPokemon extends Pokemon {
|
|||||||
* @returns the pokemon that was added or null if the pokemon could not be added
|
* @returns the pokemon that was added or null if the pokemon could not be added
|
||||||
*/
|
*/
|
||||||
addToParty(pokeballType: PokeballType, slotIndex: number = -1) {
|
addToParty(pokeballType: PokeballType, slotIndex: number = -1) {
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getPlayerParty();
|
||||||
let ret: PlayerPokemon | null = null;
|
let ret: PlayerPokemon | null = null;
|
||||||
|
|
||||||
if (party.length < PLAYER_PARTY_MAX_SIZE) {
|
if (party.length < PLAYER_PARTY_MAX_SIZE) {
|
||||||
@ -5102,7 +5177,6 @@ export class PokemonSummonData {
|
|||||||
public tags: BattlerTag[] = [];
|
public tags: BattlerTag[] = [];
|
||||||
public abilitySuppressed: boolean = false;
|
public abilitySuppressed: boolean = false;
|
||||||
public abilitiesApplied: Abilities[] = [];
|
public abilitiesApplied: Abilities[] = [];
|
||||||
|
|
||||||
public speciesForm: PokemonSpeciesForm | null;
|
public speciesForm: PokemonSpeciesForm | null;
|
||||||
public fusionSpeciesForm: PokemonSpeciesForm;
|
public fusionSpeciesForm: PokemonSpeciesForm;
|
||||||
public ability: Abilities = Abilities.NONE;
|
public ability: Abilities = Abilities.NONE;
|
||||||
@ -5142,8 +5216,8 @@ export class PokemonTurnData {
|
|||||||
* - `0` = Move is finished
|
* - `0` = Move is finished
|
||||||
*/
|
*/
|
||||||
public hitsLeft: number = -1;
|
public hitsLeft: number = -1;
|
||||||
public damageDealt: number = 0;
|
public totalDamageDealt: number = 0;
|
||||||
public currDamageDealt: number = 0;
|
public singleHitDamageDealt: number = 0;
|
||||||
public damageTaken: number = 0;
|
public damageTaken: number = 0;
|
||||||
public attacksReceived: AttackMoveResult[] = [];
|
public attacksReceived: AttackMoveResult[] = [];
|
||||||
public order: number;
|
public order: number;
|
||||||
@ -5153,6 +5227,7 @@ export class PokemonTurnData {
|
|||||||
public combiningPledge?: Moves;
|
public combiningPledge?: Moves;
|
||||||
public switchedInThisTurn: boolean = false;
|
public switchedInThisTurn: boolean = false;
|
||||||
public failedRunAway: boolean = false;
|
public failedRunAway: boolean = false;
|
||||||
|
public joinedRound: boolean = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AiType {
|
export enum AiType {
|
||||||
|
@ -232,7 +232,7 @@ export class LoadingScene extends SceneBase {
|
|||||||
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_<lang>
|
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_<lang>
|
||||||
const lang = i18next.resolvedLanguage;
|
const lang = i18next.resolvedLanguage;
|
||||||
if (lang !== "en") {
|
if (lang !== "en") {
|
||||||
if (Utils.verifyLang(lang)) {
|
if (Utils.hasAllLocalizedSprites(lang)) {
|
||||||
this.loadAtlas(`statuses_${lang}`, "");
|
this.loadAtlas(`statuses_${lang}`, "");
|
||||||
this.loadAtlas(`types_${lang}`, "");
|
this.loadAtlas(`types_${lang}`, "");
|
||||||
} else {
|
} else {
|
||||||
|
@ -746,7 +746,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (pokemon.isPlayer() && forThreshold) {
|
if (pokemon.isPlayer() && forThreshold) {
|
||||||
return scene.getParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: number, maxStackCount: number) => Math.max(stackCount, maxStackCount), 0);
|
return scene.getPlayerParty().map(p => this.getMaxHeldItemCount(p)).reduce((stackCount: number, maxStackCount: number) => Math.max(stackCount, maxStackCount), 0);
|
||||||
}
|
}
|
||||||
return this.getMaxHeldItemCount(pokemon);
|
return this.getMaxHeldItemCount(pokemon);
|
||||||
}
|
}
|
||||||
@ -1767,10 +1767,10 @@ export class HitHealModifier extends PokemonHeldItemModifier {
|
|||||||
* @returns `true` if the {@linkcode Pokemon} was healed
|
* @returns `true` if the {@linkcode Pokemon} was healed
|
||||||
*/
|
*/
|
||||||
override apply(pokemon: Pokemon): boolean {
|
override apply(pokemon: Pokemon): boolean {
|
||||||
if (pokemon.turnData.damageDealt && !pokemon.isFullHp()) {
|
if (pokemon.turnData.totalDamageDealt && !pokemon.isFullHp()) {
|
||||||
const scene = pokemon.scene;
|
const scene = pokemon.scene;
|
||||||
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
scene.unshiftPhase(new PokemonHealPhase(scene, pokemon.getBattlerIndex(),
|
||||||
toDmgValue(pokemon.turnData.damageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
|
toDmgValue(pokemon.turnData.totalDamageDealt / 8) * this.stackCount, i18next.t("modifier:hitHealApply", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), typeName: this.type.name }), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -2022,7 +2022,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier {
|
|||||||
abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean | Promise<boolean>;
|
abstract override apply(playerPokemon: PlayerPokemon, ...args: unknown[]): boolean | Promise<boolean>;
|
||||||
|
|
||||||
getPokemon(scene: BattleScene) {
|
getPokemon(scene: BattleScene) {
|
||||||
return scene.getParty().find(p => p.id === this.pokemonId);
|
return scene.getPlayerParty().find(p => p.id === this.pokemonId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2224,7 +2224,7 @@ export class PokemonLevelIncrementModifier extends ConsumablePokemonModifier {
|
|||||||
|
|
||||||
playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY);
|
playerPokemon.addFriendship(FRIENDSHIP_GAIN_FROM_RARE_CANDY);
|
||||||
|
|
||||||
playerPokemon.scene.unshiftPhase(new LevelUpPhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), playerPokemon.level - levelCount.value, playerPokemon.level));
|
playerPokemon.scene.unshiftPhase(new LevelUpPhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), playerPokemon.level - levelCount.value, playerPokemon.level));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2244,7 +2244,7 @@ export class TmModifier extends ConsumablePokemonModifier {
|
|||||||
*/
|
*/
|
||||||
override apply(playerPokemon: PlayerPokemon): boolean {
|
override apply(playerPokemon: PlayerPokemon): boolean {
|
||||||
|
|
||||||
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM));
|
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), this.type.moveId, LearnMoveType.TM));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2266,7 +2266,7 @@ export class RememberMoveModifier extends ConsumablePokemonModifier {
|
|||||||
*/
|
*/
|
||||||
override apply(playerPokemon: PlayerPokemon, cost?: number): boolean {
|
override apply(playerPokemon: PlayerPokemon, cost?: number): boolean {
|
||||||
|
|
||||||
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], LearnMoveType.MEMORY, cost));
|
playerPokemon.scene.unshiftPhase(new LearnMovePhase(playerPokemon.scene, playerPokemon.scene.getPlayerParty().indexOf(playerPokemon), playerPokemon.getLearnableLevelMoves()[this.levelMoveIndex], LearnMoveType.MEMORY, cost));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2783,7 +2783,7 @@ export class MoneyRewardModifier extends ConsumableModifier {
|
|||||||
|
|
||||||
battleScene.addMoney(moneyAmount.value);
|
battleScene.addMoney(moneyAmount.value);
|
||||||
|
|
||||||
battleScene.getParty().map(p => {
|
battleScene.getPlayerParty().map(p => {
|
||||||
if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) {
|
if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) {
|
||||||
p.evoCounter ? p.evoCounter++ : p.evoCounter = 1;
|
p.evoCounter ? p.evoCounter++ : p.evoCounter = 1;
|
||||||
const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier;
|
const modifier = getModifierType(modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL).newModifier(p) as EvoTrackerModifier;
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
|
import { type PokeballCounts } from "#app/battle-scene";
|
||||||
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { Variant } from "#app/data/variant";
|
||||||
|
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
||||||
|
import { Unlockables } from "#app/system/unlockables";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { EggTier } from "#enums/egg-type";
|
import { EggTier } from "#enums/egg-type";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { PokeballType } from "#enums/pokeball";
|
import { PokeballType } from "#enums/pokeball";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { TimeOfDay } from "#enums/time-of-day";
|
import { TimeOfDay } from "#enums/time-of-day";
|
||||||
import { VariantTier } from "#enums/variant-tier";
|
import { VariantTier } from "#enums/variant-tier";
|
||||||
import { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { type PokeballCounts } from "./battle-scene";
|
|
||||||
import { Gender } from "./data/gender";
|
|
||||||
import { Variant } from "./data/variant";
|
|
||||||
import { type ModifierOverride } from "./modifier/modifier-type";
|
|
||||||
import { Unlockables } from "./system/unlockables";
|
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides that are using when testing different in game situations
|
* Overrides that are using when testing different in game situations
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { getPokeballCatchMultiplier, getPokeballAtlasKey, getPokeballTintColor, doPokeballBounceAnim } from "#app/data/pokeball";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
|
import { SubstituteTag } from "#app/data/battler-tags";
|
||||||
|
import { doPokeballBounceAnim, getPokeballAtlasKey, getPokeballCatchMultiplier, getPokeballTintColor } from "#app/data/pokeball";
|
||||||
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
|
import { getStatusEffectCatchRateMultiplier } from "#app/data/status-effect";
|
||||||
import { PokeballType } from "#app/enums/pokeball";
|
import { addPokeballCaptureStars, addPokeballOpenParticles } from "#app/field/anims";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
|
||||||
import { addPokeballOpenParticles, addPokeballCaptureStars } from "#app/field/anims";
|
|
||||||
import { EnemyPokemon } from "#app/field/pokemon";
|
import { EnemyPokemon } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
|
import { VictoryPhase } from "#app/phases/victory-phase";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler";
|
import { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
import { SummaryUiMode } from "#app/ui/summary-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import { PokeballType } from "#enums/pokeball";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
|
||||||
import { VictoryPhase } from "./victory-phase";
|
|
||||||
import { SubstituteTag } from "#app/data/battler-tags";
|
|
||||||
|
|
||||||
export class AttemptCapturePhase extends PokemonPhase {
|
export class AttemptCapturePhase extends PokemonPhase {
|
||||||
private pokeballType: PokeballType;
|
private pokeballType: PokeballType;
|
||||||
@ -235,7 +236,7 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
const addToParty = (slotIndex?: number) => {
|
const addToParty = (slotIndex?: number) => {
|
||||||
const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex);
|
const newPokemon = pokemon.addToParty(this.pokeballType, slotIndex);
|
||||||
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
const modifiers = this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier, false);
|
||||||
if (this.scene.getParty().filter(p => p.isShiny()).length === 6) {
|
if (this.scene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) {
|
||||||
this.scene.validateAchv(achvs.SHINY_PARTY);
|
this.scene.validateAchv(achvs.SHINY_PARTY);
|
||||||
}
|
}
|
||||||
Promise.all(modifiers.map(m => this.scene.addModifier(m, true))).then(() => {
|
Promise.all(modifiers.map(m => this.scene.addModifier(m, true))).then(() => {
|
||||||
@ -249,7 +250,7 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Promise.all([ pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
|
Promise.all([ pokemon.hideInfo(), this.scene.gameData.setPokemonCaught(pokemon) ]).then(() => {
|
||||||
if (this.scene.getParty().length === 6) {
|
if (this.scene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) {
|
||||||
const promptRelease = () => {
|
const promptRelease = () => {
|
||||||
this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
this.scene.ui.showText(i18next.t("battle:partyFull", { pokemonName: pokemon.getNameToRender() }), null, () => {
|
||||||
this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
|
this.scene.pokemonInfoContainer.makeRoomForConfirmUi(1, true);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability";
|
import { applyPostBattleAbAttrs, PostBattleAbAttr } from "#app/data/ability";
|
||||||
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
import { GameOverPhase } from "./game-over-phase";
|
||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
|
|
||||||
export class BattleEndPhase extends BattlePhase {
|
export class BattleEndPhase extends BattlePhase {
|
||||||
/** If true, will increment battles won */
|
/** If true, will increment battles won */
|
||||||
@ -41,7 +41,7 @@ export class BattleEndPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty().filter(p => p.isAllowedInBattle())) {
|
for (const pokemon of this.scene.getPokemonAllowedInBattle()) {
|
||||||
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon);
|
applyPostBattleAbAttrs(PostBattleAbAttr, pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ export class CheckSwitchPhase extends BattlePhase {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.scene.getParty().slice(1).filter(p => p.isActive()).length) {
|
if (!this.scene.getPlayerParty().slice(1).filter(p => p.isActive()).length) {
|
||||||
super.end();
|
super.end();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ import { FieldPhase } from "./field-phase";
|
|||||||
import { SelectTargetPhase } from "./select-target-phase";
|
import { SelectTargetPhase } from "./select-target-phase";
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
|
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||||
|
|
||||||
export class CommandPhase extends FieldPhase {
|
export class CommandPhase extends FieldPhase {
|
||||||
protected fieldIndex: integer;
|
protected fieldIndex: integer;
|
||||||
@ -228,23 +230,24 @@ export class CommandPhase extends FieldPhase {
|
|||||||
}, null, true);
|
}, null, true);
|
||||||
} else {
|
} else {
|
||||||
const trapTag = playerPokemon.getTag(TrappedTag);
|
const trapTag = playerPokemon.getTag(TrappedTag);
|
||||||
|
const fairyLockTag = playerPokemon.scene.arena.getTagOnSide(ArenaTagType.FAIRY_LOCK, ArenaTagSide.PLAYER);
|
||||||
|
|
||||||
// trapTag should be defined at this point, but just in case...
|
// trapTag should be defined at this point, but just in case...
|
||||||
if (!trapTag) {
|
if (!trapTag && !fairyLockTag) {
|
||||||
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
currentBattle.turnCommands[this.fieldIndex] = isSwitch
|
||||||
? { command: Command.POKEMON, cursor: cursor, args: args }
|
? { command: Command.POKEMON, cursor: cursor, args: args }
|
||||||
: { command: Command.RUN };
|
: { command: Command.RUN };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSwitch) {
|
if (!isSwitch) {
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
this.scene.ui.setMode(Mode.MESSAGE);
|
this.scene.ui.setMode(Mode.MESSAGE);
|
||||||
}
|
}
|
||||||
|
const showNoEscapeText = (tag: any) => {
|
||||||
this.scene.ui.showText(
|
this.scene.ui.showText(
|
||||||
i18next.t("battle:noEscapePokemon", {
|
i18next.t("battle:noEscapePokemon", {
|
||||||
pokemonName: trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)!) : "",
|
pokemonName: tag.sourceId && this.scene.getPokemonById(tag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(tag.sourceId)!) : "",
|
||||||
moveName: trapTag.getMoveName(),
|
moveName: tag.getMoveName(),
|
||||||
escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee")
|
escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee")
|
||||||
}),
|
}),
|
||||||
null,
|
null,
|
||||||
@ -253,7 +256,17 @@ export class CommandPhase extends FieldPhase {
|
|||||||
if (!isSwitch) {
|
if (!isSwitch) {
|
||||||
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex);
|
||||||
}
|
}
|
||||||
}, null, true);
|
},
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (trapTag) {
|
||||||
|
showNoEscapeText(trapTag);
|
||||||
|
} else if (fairyLockTag) {
|
||||||
|
showNoEscapeText(fairyLockTag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex, BattleType } from "#app/battle";
|
import { BattlerIndex, BattleType } from "#app/battle";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { PLAYER_PARTY_MAX_SIZE } from "#app/constants";
|
||||||
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
|
import { applyAbAttrs, SyncEncounterNatureAbAttr } from "#app/data/ability";
|
||||||
|
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
||||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||||
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import { TrainerSlot } from "#app/data/trainer-config";
|
import { TrainerSlot } from "#app/data/trainer-config";
|
||||||
import { getRandomWeatherType } from "#app/data/weather";
|
import { getRandomWeatherType } from "#app/data/weather";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
|
||||||
import { PlayerGender } from "#app/enums/player-gender";
|
|
||||||
import { Species } from "#app/enums/species";
|
|
||||||
import { EncounterPhaseEvent } from "#app/events/battle-scene";
|
import { EncounterPhaseEvent } from "#app/events/battle-scene";
|
||||||
import Pokemon, { FieldPosition } from "#app/field/pokemon";
|
import Pokemon, { FieldPosition } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
|
||||||
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
import { BoostBugSpawnModifier, IvScannerModifier, TurnHeldItemTransferModifier } from "#app/modifier/modifier";
|
||||||
|
import { ModifierPoolType, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
|
import Overrides from "#app/overrides";
|
||||||
|
import { BattlePhase } from "#app/phases/battle-phase";
|
||||||
|
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||||
|
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||||
|
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
||||||
|
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
||||||
|
import { ReturnPhase } from "#app/phases/return-phase";
|
||||||
|
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||||
|
import { ShinySparklePhase } from "#app/phases/shiny-sparkle-phase";
|
||||||
|
import { SummonPhase } from "#app/phases/summon-phase";
|
||||||
|
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import i18next from "i18next";
|
import { randSeedInt, randSeedItem } from "#app/utils";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { randSeedInt } from "#app/utils";
|
|
||||||
import { CheckSwitchPhase } from "./check-switch-phase";
|
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
|
||||||
import { PostSummonPhase } from "./post-summon-phase";
|
|
||||||
import { ReturnPhase } from "./return-phase";
|
|
||||||
import { ScanIvsPhase } from "./scan-ivs-phase";
|
|
||||||
import { ShinySparklePhase } from "./shiny-sparkle-phase";
|
|
||||||
import { SummonPhase } from "./summon-phase";
|
|
||||||
import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
|
||||||
import Overrides from "#app/overrides";
|
|
||||||
import { initEncounterAnims, loadEncounterAnimAssets } from "#app/data/battle-anims";
|
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
|
||||||
import { doTrainerExclamation } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
||||||
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
|
|
||||||
import { getGoldenBugNetSpecies } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import i18next from "i18next";
|
||||||
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
import { WEIGHT_INCREMENT_ON_SPAWN_MISS } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
|
||||||
export class EncounterPhase extends BattlePhase {
|
export class EncounterPhase extends BattlePhase {
|
||||||
@ -116,7 +116,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
|
if (this.scene.currentBattle.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||||
battle.enemyParty[e].ivs = new Array(6).fill(31);
|
battle.enemyParty[e].ivs = new Array(6).fill(31);
|
||||||
}
|
}
|
||||||
this.scene.getParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
|
this.scene.getPlayerParty().slice(0, !battle.double ? 1 : 2).reverse().forEach(playerPokemon => {
|
||||||
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
|
applyAbAttrs(SyncEncounterNatureAbAttr, playerPokemon, null, false, battle.enemyParty[e]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -156,7 +156,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.scene.getParty().filter(p => p.isShiny()).length === 6) {
|
if (this.scene.getPlayerParty().filter(p => p.isShiny()).length === PLAYER_PARTY_MAX_SIZE) {
|
||||||
this.scene.validateAchv(achvs.SHINY_PARTY);
|
this.scene.validateAchv(achvs.SHINY_PARTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
|
|
||||||
/*if (startingWave > 10) {
|
/*if (startingWave > 10) {
|
||||||
for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++)
|
for (let m = 0; m < Math.min(Math.floor(startingWave / 10), 99); m++)
|
||||||
this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getParty())[0].type.newModifier(), true);
|
this.scene.addModifier(getPlayerModifierTypeOptionsForWave((m + 1) * 10, 1, this.scene.getPlayerParty())[0].type.newModifier(), true);
|
||||||
this.scene.updateModifiers(true);
|
this.scene.updateModifiers(true);
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
@ -259,7 +259,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
this.scene.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS;
|
this.scene.mysteryEncounterSaveData.encounterSpawnChance += WEIGHT_INCREMENT_ON_SPAWN_MISS;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty()) {
|
for (const pokemon of this.scene.getPlayerParty()) {
|
||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
pokemon.resetBattleData();
|
pokemon.resetBattleData();
|
||||||
}
|
}
|
||||||
@ -338,7 +338,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
const doSummon = () => {
|
const doSummon = () => {
|
||||||
this.scene.currentBattle.started = true;
|
this.scene.currentBattle.started = true;
|
||||||
this.scene.playBgm(undefined);
|
this.scene.playBgm(undefined);
|
||||||
this.scene.pbTray.showPbTray(this.scene.getParty());
|
this.scene.pbTray.showPbTray(this.scene.getPlayerParty());
|
||||||
this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty());
|
this.scene.pbTrayEnemy.showPbTray(this.scene.getEnemyParty());
|
||||||
const doTrainerSummon = () => {
|
const doTrainerSummon = () => {
|
||||||
this.hideEnemyTrainer();
|
this.hideEnemyTrainer();
|
||||||
@ -362,7 +362,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
doSummon();
|
doSummon();
|
||||||
} else {
|
} else {
|
||||||
let message: string;
|
let message: string;
|
||||||
this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
|
this.scene.executeWithSeedOffset(() => message = randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex);
|
||||||
message = message!; // tell TS compiler it's defined now
|
message = message!; // tell TS compiler it's defined now
|
||||||
const showDialogueAndSummon = () => {
|
const showDialogueAndSummon = () => {
|
||||||
this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => {
|
this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => {
|
||||||
@ -447,13 +447,13 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) {
|
if (![ BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER ].includes(this.scene.currentBattle.battleType)) {
|
||||||
enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => {
|
enemyField.map(p => this.scene.pushConditionalPhase(new PostSummonPhase(this.scene, p.getBattlerIndex()), () => {
|
||||||
// if there is not a player party, we can't continue
|
// if there is not a player party, we can't continue
|
||||||
if (!this.scene.getParty()?.length) {
|
if (!this.scene.getPlayerParty().length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// how many player pokemon are on the field ?
|
// how many player pokemon are on the field ?
|
||||||
const pokemonsOnFieldCount = this.scene.getParty().filter(p => p.isOnField()).length;
|
const pokemonsOnFieldCount = this.scene.getPlayerParty().filter(p => p.isOnField()).length;
|
||||||
// if it's a 2vs1, there will never be a 2nd pokemon on our field even
|
// if it's a 2vs1, there will never be a 2nd pokemon on our field even
|
||||||
const requiredPokemonsOnField = Math.min(this.scene.getParty().filter((p) => !p.isFainted()).length, 2);
|
const requiredPokemonsOnField = Math.min(this.scene.getPlayerParty().filter((p) => !p.isFainted()).length, 2);
|
||||||
// if it's a double, there should be 2, otherwise 1
|
// if it's a double, there should be 2, otherwise 1
|
||||||
if (this.scene.currentBattle.double) {
|
if (this.scene.currentBattle.double) {
|
||||||
return pokemonsOnFieldCount === requiredPokemonsOnField;
|
return pokemonsOnFieldCount === requiredPokemonsOnField;
|
||||||
@ -467,7 +467,7 @@ export class EncounterPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this.loaded) {
|
if (!this.loaded) {
|
||||||
const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
const availablePartyMembers = this.scene.getPokemonAllowedInBattle();
|
||||||
|
|
||||||
if (!availablePartyMembers[0].isOnField()) {
|
if (!availablePartyMembers[0].isOnField()) {
|
||||||
this.scene.pushPhase(new SummonPhase(this.scene, 0));
|
this.scene.pushPhase(new SummonPhase(this.scene, 0));
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene, { AnySound } from "#app/battle-scene";
|
||||||
import { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
import { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
||||||
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
|
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { cos, sin } from "#app/field/anims";
|
import { cos, sin } from "#app/field/anims";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import { getTypeRgb } from "#app/data/type";
|
import { getTypeRgb } from "#app/data/type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
@ -17,7 +17,11 @@ export class EvolutionPhase extends Phase {
|
|||||||
protected pokemon: PlayerPokemon;
|
protected pokemon: PlayerPokemon;
|
||||||
protected lastLevel: integer;
|
protected lastLevel: integer;
|
||||||
|
|
||||||
|
private preEvolvedPokemonName: string;
|
||||||
|
|
||||||
private evolution: SpeciesFormEvolution | null;
|
private evolution: SpeciesFormEvolution | null;
|
||||||
|
private evolutionBgm: AnySound;
|
||||||
|
private evolutionHandler: EvolutionSceneHandler;
|
||||||
|
|
||||||
protected evolutionContainer: Phaser.GameObjects.Container;
|
protected evolutionContainer: Phaser.GameObjects.Container;
|
||||||
protected evolutionBaseBg: Phaser.GameObjects.Image;
|
protected evolutionBaseBg: Phaser.GameObjects.Image;
|
||||||
@ -35,6 +39,8 @@ export class EvolutionPhase extends Phase {
|
|||||||
this.pokemon = pokemon;
|
this.pokemon = pokemon;
|
||||||
this.evolution = evolution;
|
this.evolution = evolution;
|
||||||
this.lastLevel = lastLevel;
|
this.lastLevel = lastLevel;
|
||||||
|
this.evolutionBgm = this.scene.playSoundWithoutBgm("evolution");
|
||||||
|
this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
validate(): boolean {
|
validate(): boolean {
|
||||||
@ -117,10 +123,9 @@ export class EvolutionPhase extends Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
doEvolution(): void {
|
doEvolution(): void {
|
||||||
const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
this.evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
||||||
const preName = getPokemonNameWithAffix(this.pokemon);
|
|
||||||
|
|
||||||
this.scene.ui.showText(i18next.t("menu:evolving", { pokemonName: preName }), null, () => {
|
this.scene.ui.showText(i18next.t("menu:evolving", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||||
this.pokemon.cry();
|
this.pokemon.cry();
|
||||||
|
|
||||||
this.pokemon.getPossibleEvolution(this.evolution).then(evolvedPokemon => {
|
this.pokemon.getPossibleEvolution(this.evolution).then(evolvedPokemon => {
|
||||||
@ -140,7 +145,6 @@ export class EvolutionPhase extends Phase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.scene.time.delayedCall(1000, () => {
|
this.scene.time.delayedCall(1000, () => {
|
||||||
const evolutionBgm = this.scene.playSoundWithoutBgm("evolution");
|
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this.evolutionBgOverlay,
|
targets: this.evolutionBgOverlay,
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
@ -174,10 +178,30 @@ export class EvolutionPhase extends Phase {
|
|||||||
this.scene.time.delayedCall(1500, () => {
|
this.scene.time.delayedCall(1500, () => {
|
||||||
this.pokemonEvoTintSprite.setScale(0.25);
|
this.pokemonEvoTintSprite.setScale(0.25);
|
||||||
this.pokemonEvoTintSprite.setVisible(true);
|
this.pokemonEvoTintSprite.setVisible(true);
|
||||||
evolutionHandler.canCancel = true;
|
this.evolutionHandler.canCancel = true;
|
||||||
this.doCycle(1).then(success => {
|
this.doCycle(1).then(success => {
|
||||||
if (!success) {
|
if (success) {
|
||||||
|
this.handleSuccessEvolution(evolvedPokemon);
|
||||||
|
} else {
|
||||||
|
this.handleFailedEvolution(evolvedPokemon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a failed/stopped evolution
|
||||||
|
* @param evolvedPokemon - The evolved Pokemon
|
||||||
|
*/
|
||||||
|
private handleFailedEvolution(evolvedPokemon: Pokemon): void {
|
||||||
this.pokemonSprite.setVisible(true);
|
this.pokemonSprite.setVisible(true);
|
||||||
this.pokemonTintSprite.setScale(1);
|
this.pokemonTintSprite.setScale(1);
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
@ -189,12 +213,12 @@ export class EvolutionPhase extends Phase {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
SoundFade.fadeOut(this.scene, evolutionBgm, 100);
|
SoundFade.fadeOut(this.scene, this.evolutionBgm, 100);
|
||||||
|
|
||||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||||
|
|
||||||
this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: preName }), null, () => {
|
this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||||
this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => {
|
this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||||
const end = () => {
|
const end = () => {
|
||||||
this.scene.ui.showText("", 0);
|
this.scene.ui.showText("", 0);
|
||||||
this.scene.playBgm();
|
this.scene.playBgm();
|
||||||
@ -204,26 +228,45 @@ export class EvolutionPhase extends Phase {
|
|||||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
|
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
this.pokemon.pauseEvolutions = true;
|
this.pokemon.pauseEvolutions = true;
|
||||||
this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: preName }), null, end, 3000);
|
this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: this.preEvolvedPokemonName }), null, end, 3000);
|
||||||
}, () => {
|
}, () => {
|
||||||
this.scene.ui.revertMode();
|
this.scene.ui.revertMode();
|
||||||
this.scene.time.delayedCall(3000, end);
|
this.scene.time.delayedCall(3000, end);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, null, true);
|
}, null, true);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a successful evolution
|
||||||
|
* @param evolvedPokemon - The evolved Pokemon
|
||||||
|
*/
|
||||||
|
private handleSuccessEvolution(evolvedPokemon: Pokemon): void {
|
||||||
this.scene.playSound("se/sparkle");
|
this.scene.playSound("se/sparkle");
|
||||||
this.pokemonEvoSprite.setVisible(true);
|
this.pokemonEvoSprite.setVisible(true);
|
||||||
this.doCircleInward();
|
this.doCircleInward();
|
||||||
|
|
||||||
|
const onEvolutionComplete = () => {
|
||||||
|
SoundFade.fadeOut(this.scene, this.evolutionBgm, 100);
|
||||||
|
this.scene.time.delayedCall(250, () => {
|
||||||
|
this.pokemon.cry();
|
||||||
|
this.scene.time.delayedCall(1250, () => {
|
||||||
|
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
||||||
|
|
||||||
|
evolvedPokemon.destroy();
|
||||||
|
this.scene.ui.showText(i18next.t("menu:evolutionDone", { pokemonName: this.preEvolvedPokemonName, evolvedPokemonName: this.pokemon.name }), null, () => this.end(), null, true, Utils.fixedInt(4000));
|
||||||
|
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
this.scene.time.delayedCall(900, () => {
|
this.scene.time.delayedCall(900, () => {
|
||||||
evolutionHandler.canCancel = false;
|
this.evolutionHandler.canCancel = false;
|
||||||
|
|
||||||
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
||||||
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
|
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
|
||||||
for (const lm of levelMoves) {
|
for (const lm of levelMoves) {
|
||||||
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getParty().indexOf(this.pokemon), lm[1]));
|
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getPlayerParty().indexOf(this.pokemon), lm[1]));
|
||||||
}
|
}
|
||||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||||
|
|
||||||
@ -248,28 +291,7 @@ export class EvolutionPhase extends Phase {
|
|||||||
targets: this.evolutionBgOverlay,
|
targets: this.evolutionBgOverlay,
|
||||||
alpha: 0,
|
alpha: 0,
|
||||||
duration: 250,
|
duration: 250,
|
||||||
onComplete: () => {
|
onComplete: onEvolutionComplete
|
||||||
SoundFade.fadeOut(this.scene, evolutionBgm, 100);
|
|
||||||
this.scene.time.delayedCall(250, () => {
|
|
||||||
this.pokemon.cry();
|
|
||||||
this.scene.time.delayedCall(1250, () => {
|
|
||||||
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
|
||||||
|
|
||||||
evolvedPokemon.destroy();
|
|
||||||
this.scene.ui.showText(i18next.t("menu:evolutionDone", { pokemonName: preName, evolvedPokemonName: this.pokemon.name }), null, () => this.end(), null, true, Utils.fixedInt(4000));
|
|
||||||
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -277,7 +299,6 @@ export class EvolutionPhase extends Phase {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doSpiralUpward() {
|
doSpiralUpward() {
|
||||||
@ -320,7 +341,6 @@ export class EvolutionPhase extends Phase {
|
|||||||
|
|
||||||
doCycle(l: number, lastCycle: integer = 15): Promise<boolean> {
|
doCycle(l: number, lastCycle: integer = 15): Promise<boolean> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
|
||||||
const isLastCycle = l === lastCycle;
|
const isLastCycle = l === lastCycle;
|
||||||
this.scene.tweens.add({
|
this.scene.tweens.add({
|
||||||
targets: this.pokemonTintSprite,
|
targets: this.pokemonTintSprite,
|
||||||
@ -336,7 +356,7 @@ export class EvolutionPhase extends Phase {
|
|||||||
duration: 500 / l,
|
duration: 500 / l,
|
||||||
yoyo: !isLastCycle,
|
yoyo: !isLastCycle,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
if (evolutionHandler.cancelled) {
|
if (this.evolutionHandler.cancelled) {
|
||||||
return resolve(false);
|
return resolve(false);
|
||||||
}
|
}
|
||||||
if (l < lastCycle) {
|
if (l < lastCycle) {
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex, BattleType } from "#app/battle";
|
import { BattlerIndex, BattleType } from "#app/battle";
|
||||||
import { applyPostFaintAbAttrs, PostFaintAbAttr, applyPostKnockOutAbAttrs, PostKnockOutAbAttr, applyPostVictoryAbAttrs, PostVictoryAbAttr } from "#app/data/ability";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { applyPostFaintAbAttrs, applyPostKnockOutAbAttrs, applyPostVictoryAbAttrs, PostFaintAbAttr, PostKnockOutAbAttr, PostVictoryAbAttr } from "#app/data/ability";
|
||||||
import { BattlerTagLapseType, DestinyBondTag } from "#app/data/battler-tags";
|
import { BattlerTagLapseType, DestinyBondTag } from "#app/data/battler-tags";
|
||||||
import { battleSpecDialogue } from "#app/data/dialogue";
|
import { battleSpecDialogue } from "#app/data/dialogue";
|
||||||
import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/move";
|
import { allMoves, PostVictoryStatStageChangeAttr } from "#app/data/move";
|
||||||
|
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
import { StatusEffect } from "#app/enums/status-effect";
|
import { StatusEffect } from "#app/enums/status-effect";
|
||||||
import Pokemon, { PokemonMove, EnemyPokemon, PlayerPokemon, HitResult } from "#app/field/pokemon";
|
import Pokemon, { EnemyPokemon, HitResult, PlayerPokemon, PokemonMove } from "#app/field/pokemon";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
import { PokemonInstantReviveModifier } from "#app/modifier/modifier";
|
||||||
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { DamagePhase } from "./damage-phase";
|
import { DamagePhase } from "./damage-phase";
|
||||||
|
import { GameOverPhase } from "./game-over-phase";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
|
import { SwitchPhase } from "./switch-phase";
|
||||||
import { SwitchSummonPhase } from "./switch-summon-phase";
|
import { SwitchSummonPhase } from "./switch-summon-phase";
|
||||||
import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
|
||||||
import { SwitchPhase } from "./switch-phase";
|
|
||||||
import { VictoryPhase } from "./victory-phase";
|
import { VictoryPhase } from "./victory-phase";
|
||||||
import { SpeciesFormChangeActiveTrigger } from "#app/data/pokemon-forms";
|
|
||||||
import { SwitchType } from "#enums/switch-type";
|
|
||||||
import { isNullOrUndefined } from "#app/utils";
|
import { isNullOrUndefined } from "#app/utils";
|
||||||
import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters";
|
import { FRIENDSHIP_LOSS_FROM_FAINT } from "#app/data/balance/starters";
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
|
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
/** The total number of Pokemon in the player's party that can legally fight */
|
/** The total number of Pokemon in the player's party that can legally fight */
|
||||||
const legalPlayerPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
const legalPlayerPokemon = this.scene.getPokemonAllowedInBattle();
|
||||||
/** The total number of legal player Pokemon that aren't currently on the field */
|
/** The total number of legal player Pokemon that aren't currently on the field */
|
||||||
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
|
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
|
||||||
if (!legalPlayerPokemon.length) {
|
if (!legalPlayerPokemon.length) {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { clientSessionId } from "#app/account";
|
import { clientSessionId } from "#app/account";
|
||||||
import { BattleType } from "#app/battle";
|
import { BattleType } from "#app/battle";
|
||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
|
||||||
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
import { pokemonEvolutions } from "#app/data/balance/pokemon-evolutions";
|
||||||
|
import { getCharVariantFromDialogue } from "#app/data/dialogue";
|
||||||
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
import PokemonSpecies, { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { trainerConfigs } from "#app/data/trainer-config";
|
import { trainerConfigs } from "#app/data/trainer-config";
|
||||||
import Pokemon from "#app/field/pokemon";
|
import Pokemon from "#app/field/pokemon";
|
||||||
@ -65,7 +65,7 @@ export class GameOverPhase extends BattlePhase {
|
|||||||
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
this.scene.gameData.loadSession(this.scene, this.scene.sessionSlotId).then(() => {
|
||||||
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
this.scene.pushPhase(new EncounterPhase(this.scene, true));
|
||||||
|
|
||||||
const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length;
|
const availablePartyMembers = this.scene.getPokemonAllowedInBattle().length;
|
||||||
|
|
||||||
this.scene.pushPhase(new SummonPhase(this.scene, 0));
|
this.scene.pushPhase(new SummonPhase(this.scene, 0));
|
||||||
if (this.scene.currentBattle.double && availablePartyMembers > 1) {
|
if (this.scene.currentBattle.double && availablePartyMembers > 1) {
|
||||||
@ -97,7 +97,7 @@ export class GameOverPhase extends BattlePhase {
|
|||||||
firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY);
|
firstClear = this.scene.validateAchv(achvs.CLASSIC_VICTORY);
|
||||||
this.scene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY);
|
this.scene.validateAchv(achvs.UNEVOLVED_CLASSIC_VICTORY);
|
||||||
this.scene.gameData.gameStats.sessionsWon++;
|
this.scene.gameData.gameStats.sessionsWon++;
|
||||||
for (const pokemon of this.scene.getParty()) {
|
for (const pokemon of this.scene.getPlayerParty()) {
|
||||||
this.awardRibbon(pokemon);
|
this.awardRibbon(pokemon);
|
||||||
|
|
||||||
if (pokemon.species.getRootSpeciesId() !== pokemon.species.getRootSpeciesId(true)) {
|
if (pokemon.species.getRootSpeciesId() !== pokemon.species.getRootSpeciesId(true)) {
|
||||||
@ -195,13 +195,13 @@ export class GameOverPhase extends BattlePhase {
|
|||||||
if (!this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
|
if (!this.scene.gameData.unlocks[Unlockables.ENDLESS_MODE]) {
|
||||||
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.ENDLESS_MODE));
|
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.ENDLESS_MODE));
|
||||||
}
|
}
|
||||||
if (this.scene.getParty().filter(p => p.fusionSpecies).length && !this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) {
|
if (this.scene.getPlayerParty().filter(p => p.fusionSpecies).length && !this.scene.gameData.unlocks[Unlockables.SPLICED_ENDLESS_MODE]) {
|
||||||
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.SPLICED_ENDLESS_MODE));
|
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.SPLICED_ENDLESS_MODE));
|
||||||
}
|
}
|
||||||
if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) {
|
if (!this.scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) {
|
||||||
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE));
|
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.MINI_BLACK_HOLE));
|
||||||
}
|
}
|
||||||
if (!this.scene.gameData.unlocks[Unlockables.EVIOLITE] && this.scene.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)) {
|
if (!this.scene.gameData.unlocks[Unlockables.EVIOLITE] && this.scene.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)) {
|
||||||
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.EVIOLITE));
|
this.scene.unshiftPhase(new UnlockPhase(this.scene, Unlockables.EVIOLITE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,62 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyPreAttackAbAttrs, AddSecondStrikeAbAttr, IgnoreMoveEffectsAbAttr, applyPostDefendAbAttrs, PostDefendAbAttr, applyPostAttackAbAttrs, PostAttackAbAttr, MaxMultiHitAbAttr, AlwaysHitAbAttr, TypeImmunityAbAttr } from "#app/data/ability";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import {
|
||||||
|
AddSecondStrikeAbAttr,
|
||||||
|
AlwaysHitAbAttr,
|
||||||
|
applyPostAttackAbAttrs,
|
||||||
|
applyPostDefendAbAttrs,
|
||||||
|
applyPreAttackAbAttrs,
|
||||||
|
IgnoreMoveEffectsAbAttr,
|
||||||
|
MaxMultiHitAbAttr,
|
||||||
|
PostAttackAbAttr,
|
||||||
|
PostDefendAbAttr,
|
||||||
|
TypeImmunityAbAttr,
|
||||||
|
} from "#app/data/ability";
|
||||||
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
|
import { ArenaTagSide, ConditionalProtectTag } from "#app/data/arena-tag";
|
||||||
import { MoveAnim } from "#app/data/battle-anims";
|
import { MoveAnim } from "#app/data/battle-anims";
|
||||||
import { BattlerTagLapseType, DamageProtectedTag, ProtectedTag, SemiInvulnerableTag, SubstituteTag } from "#app/data/battler-tags";
|
import {
|
||||||
import { MoveTarget, applyMoveAttrs, OverrideMoveEffectAttr, MultiHitAttr, AttackMove, FixedDamageAttr, VariableTargetAttr, MissEffectAttr, MoveFlags, applyFilteredMoveAttrs, MoveAttr, MoveEffectAttr, OneHitKOAttr, MoveEffectTrigger, MoveCategory, NoEffectAttr, HitsTagAttr, ToxicAccuracyAttr } from "#app/data/move";
|
BattlerTagLapseType,
|
||||||
|
DamageProtectedTag,
|
||||||
|
ProtectedTag,
|
||||||
|
SemiInvulnerableTag,
|
||||||
|
SubstituteTag,
|
||||||
|
} from "#app/data/battler-tags";
|
||||||
|
import {
|
||||||
|
applyFilteredMoveAttrs,
|
||||||
|
applyMoveAttrs,
|
||||||
|
AttackMove,
|
||||||
|
FixedDamageAttr,
|
||||||
|
HitsTagAttr,
|
||||||
|
MissEffectAttr,
|
||||||
|
MoveAttr,
|
||||||
|
MoveCategory,
|
||||||
|
MoveEffectAttr,
|
||||||
|
MoveEffectTrigger,
|
||||||
|
MoveFlags,
|
||||||
|
MoveTarget,
|
||||||
|
MultiHitAttr,
|
||||||
|
NoEffectAttr,
|
||||||
|
OneHitKOAttr,
|
||||||
|
OverrideMoveEffectAttr,
|
||||||
|
ToxicAccuracyAttr,
|
||||||
|
VariableTargetAttr,
|
||||||
|
} from "#app/data/move";
|
||||||
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangePostMoveTrigger } from "#app/data/pokemon-forms";
|
||||||
import { BattlerTagType } from "#app/enums/battler-tag-type";
|
|
||||||
import { Moves } from "#app/enums/moves";
|
|
||||||
import Pokemon, { PokemonMove, MoveResult, HitResult } from "#app/field/pokemon";
|
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
|
||||||
import { PokemonMultiHitModifier, FlinchChanceModifier, EnemyAttackStatusEffectChanceModifier, ContactHeldItemTransferChanceModifier, HitHealModifier } from "#app/modifier/modifier";
|
|
||||||
import i18next from "i18next";
|
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
|
||||||
import { Type } from "#app/data/type";
|
import { Type } from "#app/data/type";
|
||||||
|
import Pokemon, { HitResult, MoveResult, PokemonMove } from "#app/field/pokemon";
|
||||||
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import {
|
||||||
|
ContactHeldItemTransferChanceModifier,
|
||||||
|
EnemyAttackStatusEffectChanceModifier,
|
||||||
|
FlinchChanceModifier,
|
||||||
|
HitHealModifier,
|
||||||
|
PokemonMultiHitModifier,
|
||||||
|
} from "#app/modifier/modifier";
|
||||||
|
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||||
|
import { BooleanHolder, executeIf, NumberHolder } from "#app/utils";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class MoveEffectPhase extends PokemonPhase {
|
export class MoveEffectPhase extends PokemonPhase {
|
||||||
public move: PokemonMove;
|
public move: PokemonMove;
|
||||||
@ -35,7 +77,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
this.targets = targets;
|
this.targets = targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
public override start(): void {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
/** The Pokemon using this phase's invoked move */
|
/** The Pokemon using this phase's invoked move */
|
||||||
@ -52,12 +94,12 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* Does an effect from this move override other effects on this turn?
|
* Does an effect from this move override other effects on this turn?
|
||||||
* e.g. Charging moves (Fly, etc.) on their first turn of use.
|
* e.g. Charging moves (Fly, etc.) on their first turn of use.
|
||||||
*/
|
*/
|
||||||
const overridden = new Utils.BooleanHolder(false);
|
const overridden = new BooleanHolder(false);
|
||||||
/** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */
|
/** The {@linkcode Move} object from {@linkcode allMoves} invoked by this phase */
|
||||||
const move = this.move.getMove();
|
const move = this.move.getMove();
|
||||||
|
|
||||||
// Assume single target for override
|
// Assume single target for override
|
||||||
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => {
|
applyMoveAttrs(OverrideMoveEffectAttr, user, this.getFirstTarget() ?? null, move, overridden, this.move.virtual).then(() => {
|
||||||
// If other effects were overriden, stop this phase before they can be applied
|
// If other effects were overriden, stop this phase before they can be applied
|
||||||
if (overridden.value) {
|
if (overridden.value) {
|
||||||
return this.end();
|
return this.end();
|
||||||
@ -71,14 +113,14 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
* effects of the move itself, Parental Bond, and Multi-Lens to do so.
|
||||||
*/
|
*/
|
||||||
if (user.turnData.hitsLeft === -1) {
|
if (user.turnData.hitsLeft === -1) {
|
||||||
const hitCount = new Utils.IntegerHolder(1);
|
const hitCount = new NumberHolder(1);
|
||||||
// Assume single target for multi hit
|
// Assume single target for multi hit
|
||||||
applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount);
|
applyMoveAttrs(MultiHitAttr, user, this.getFirstTarget() ?? null, move, hitCount);
|
||||||
// If Parental Bond is applicable, double the hit count
|
// If Parental Bond is applicable, double the hit count
|
||||||
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new Utils.IntegerHolder(0));
|
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, false, targets.length, hitCount, new NumberHolder(0));
|
||||||
// If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user
|
// If Multi-Lens is applicable, multiply the hit count by 1 + the number of Multi-Lenses held by the user
|
||||||
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) {
|
||||||
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0));
|
this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new NumberHolder(0));
|
||||||
}
|
}
|
||||||
// Set the user's relevant turnData fields to reflect the final hit count
|
// Set the user's relevant turnData fields to reflect the final hit count
|
||||||
user.turnData.hitCount = hitCount.value;
|
user.turnData.hitCount = hitCount.value;
|
||||||
@ -100,7 +142,8 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const hasActiveTargets = targets.some(t => t.isActive(true));
|
const hasActiveTargets = targets.some(t => t.isActive(true));
|
||||||
|
|
||||||
/** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */
|
/** Check if the target is immune via ability to the attacking move, and NOT in semi invulnerable state */
|
||||||
const isImmune = targets[0].hasAbilityWithAttr(TypeImmunityAbAttr) && (targets[0].getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
const isImmune = targets[0].hasAbilityWithAttr(TypeImmunityAbAttr)
|
||||||
|
&& (targets[0].getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
||||||
&& !targets[0].getTag(SemiInvulnerableTag);
|
&& !targets[0].getTag(SemiInvulnerableTag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,7 +154,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag) && !isImmune)) {
|
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag) && !isImmune)) {
|
||||||
this.stopMultiHit();
|
this.stopMultiHit();
|
||||||
if (hasActiveTargets) {
|
if (hasActiveTargets) {
|
||||||
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget() ? getPokemonNameWithAffix(this.getTarget()!) : "" }));
|
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getFirstTarget() ? getPokemonNameWithAffix(this.getFirstTarget()!) : "" }));
|
||||||
moveHistoryEntry.result = MoveResult.MISS;
|
moveHistoryEntry.result = MoveResult.MISS;
|
||||||
applyMoveAttrs(MissEffectAttr, user, null, move);
|
applyMoveAttrs(MissEffectAttr, user, null, move);
|
||||||
} else {
|
} else {
|
||||||
@ -127,29 +170,39 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
const playOnEmptyField = this.scene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false;
|
const playOnEmptyField = this.scene.currentBattle?.mysteryEncounter?.hasBattleAnimationsWithoutTargets ?? false;
|
||||||
// Move animation only needs one target
|
// Move animation only needs one target
|
||||||
new MoveAnim(move.id as Moves, user, this.getTarget()!.getBattlerIndex()!, playOnEmptyField).play(this.scene, move.hitsSubstitute(user, this.getTarget()!), () => {
|
new MoveAnim(move.id as Moves, user, this.getFirstTarget()!.getBattlerIndex()!, playOnEmptyField).play(this.scene, move.hitsSubstitute(user, this.getFirstTarget()!), () => {
|
||||||
/** Has the move successfully hit a target (for damage) yet? */
|
/** Has the move successfully hit a target (for damage) yet? */
|
||||||
let hasHit: boolean = false;
|
let hasHit: boolean = false;
|
||||||
for (const target of targets) {
|
for (const target of targets) {
|
||||||
|
// Prevent ENEMY_SIDE targeted moves from occurring twice in double battles
|
||||||
|
if (move.moveTarget === MoveTarget.ENEMY_SIDE && target !== targets[targets.length - 1]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/** The {@linkcode ArenaTagSide} to which the target belongs */
|
/** The {@linkcode ArenaTagSide} to which the target belongs */
|
||||||
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
|
||||||
/** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */
|
/** Has the invoked move been cancelled by conditional protection (e.g Quick Guard)? */
|
||||||
const hasConditionalProtectApplied = new Utils.BooleanHolder(false);
|
const hasConditionalProtectApplied = new BooleanHolder(false);
|
||||||
/** Does the applied conditional protection bypass Protect-ignoring effects? */
|
/** Does the applied conditional protection bypass Protect-ignoring effects? */
|
||||||
const bypassIgnoreProtect = new Utils.BooleanHolder(false);
|
const bypassIgnoreProtect = new BooleanHolder(false);
|
||||||
/** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */
|
/** If the move is not targeting a Pokemon on the user's side, try to apply conditional protection effects */
|
||||||
if (!this.move.getMove().isAllyTarget()) {
|
if (!this.move.getMove().isAllyTarget()) {
|
||||||
this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, false, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect);
|
this.scene.arena.applyTagsForSide(ConditionalProtectTag, targetSide, false, hasConditionalProtectApplied, user, target, move.id, bypassIgnoreProtect);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
|
/** Is the target protected by Protect, etc. or a relevant conditional protection effect? */
|
||||||
const isProtected = (bypassIgnoreProtect.value || !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target))
|
const isProtected = (
|
||||||
&& (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)))
|
bypassIgnoreProtect.value
|
||||||
|| (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
|
|| !this.move.getMove().checkFlag(MoveFlags.IGNORE_PROTECT, user, target))
|
||||||
|
&& (hasConditionalProtectApplied.value
|
||||||
|
|| (!target.findTags(t => t instanceof DamageProtectedTag).length
|
||||||
|
&& target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)))
|
||||||
|
|| (this.move.getMove().category !== MoveCategory.STATUS
|
||||||
|
&& target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
|
||||||
|
|
||||||
/** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */
|
/** Is the pokemon immune due to an ablility, and also not in a semi invulnerable state? */
|
||||||
const isImmune = target.hasAbilityWithAttr(TypeImmunityAbAttr) && (target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
const isImmune = target.hasAbilityWithAttr(TypeImmunityAbAttr)
|
||||||
|
&& (target.getAbility()?.getAttrs(TypeImmunityAbAttr)?.[0]?.getImmuneType() === user.getMoveType(move))
|
||||||
&& !target.getTag(SemiInvulnerableTag);
|
&& !target.getTag(SemiInvulnerableTag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,7 +271,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Does this phase represent the invoked move's last strike? */
|
/** Does this phase represent the invoked move's last strike? */
|
||||||
const lastHit = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive());
|
const lastHit = (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the user can change forms by using the invoked move,
|
* If the user can change forms by using the invoked move,
|
||||||
@ -234,85 +287,48 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
|
* These are ordered by trigger type (see {@linkcode MoveEffectTrigger}), and each trigger
|
||||||
* type requires different conditions to be met with respect to the move's hit result.
|
* type requires different conditions to be met with respect to the move's hit result.
|
||||||
*/
|
*/
|
||||||
applyAttrs.push(new Promise(resolve => {
|
const k = new Promise<void>((resolve) => {
|
||||||
// Apply all effects with PRE_MOVE triggers (if the target isn't immune to the move)
|
//Start promise chain and apply PRE_APPLY move attributes
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT,
|
let promiseChain: Promise<void | null> = applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
user, target, move).then(() => {
|
attr instanceof MoveEffectAttr
|
||||||
// All other effects require the move to not have failed or have been cancelled to trigger
|
&& attr.trigger === MoveEffectTrigger.PRE_APPLY
|
||||||
if (hitResult !== HitResult.FAIL) {
|
&& (!attr.firstHitOnly || firstHit)
|
||||||
/**
|
&& (!attr.lastHitOnly || lastHit)
|
||||||
* If the invoked move's effects are meant to trigger during the move's "charge turn,"
|
&& hitResult !== HitResult.NO_EFFECT, user, target, move);
|
||||||
* ignore all effects after this point.
|
|
||||||
* Otherwise, apply all self-targeted POST_APPLY effects.
|
/** Don't complete if the move failed */
|
||||||
*/
|
if (hitResult === HitResult.FAIL) {
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY
|
|
||||||
&& attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move).then(() => {
|
|
||||||
// All effects past this point require the move to have hit the target
|
|
||||||
if (hitResult !== HitResult.NO_EFFECT) {
|
|
||||||
// Apply all non-self-targeted POST_APPLY effects
|
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.POST_APPLY
|
|
||||||
&& !(attr as MoveEffectAttr).selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, this.move.getMove()).then(() => {
|
|
||||||
/**
|
|
||||||
* If the move hit, and the target doesn't have Shield Dust,
|
|
||||||
* apply the chance to flinch the target gained from King's Rock
|
|
||||||
*/
|
|
||||||
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !move.hitsSubstitute(user, target)) {
|
|
||||||
const flinched = new Utils.BooleanHolder(false);
|
|
||||||
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
|
||||||
if (flinched.value) {
|
|
||||||
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the move was not protected against, apply all HIT effects
|
|
||||||
Utils.executeIf(!isProtected, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && (attr as MoveEffectAttr).trigger === MoveEffectTrigger.HIT
|
|
||||||
&& (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove()).then(() => {
|
|
||||||
// Apply the target's post-defend ability effects (as long as the target is active or can otherwise apply them)
|
|
||||||
return Utils.executeIf(!target.isFainted() || target.canApplyAbility(), () => applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult).then(() => {
|
|
||||||
// Only apply the following effects if the move was not deflected by a substitute
|
|
||||||
if (move.hitsSubstitute(user, target)) {
|
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the invoked move is an enemy attack, apply the enemy's status effect-inflicting tokens
|
/** Apply Move/Ability Effects in correct order */
|
||||||
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
promiseChain = promiseChain
|
||||||
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
|
.then(this.applySelfTargetEffects(user, target, firstHit, lastHit));
|
||||||
}
|
|
||||||
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
|
||||||
|
|
||||||
})).then(() => {
|
if (hitResult !== HitResult.NO_EFFECT) {
|
||||||
// Apply the user's post-attack ability effects
|
promiseChain
|
||||||
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult).then(() => {
|
.then(this.applyPostApplyEffects(user, target, firstHit, lastHit))
|
||||||
/**
|
.then(this.applyHeldItemFlinchCheck(user, target, dealsDamage))
|
||||||
* If the invoked move is an attack, apply the user's chance to
|
.then(this.applySuccessfulAttackEffects(user, target, firstHit, lastHit, !!isProtected, hitResult, firstTarget))
|
||||||
* steal an item from the target granted by Grip Claw
|
.then(() => resolve());
|
||||||
*/
|
|
||||||
if (this.move.getMove() instanceof AttackMove) {
|
|
||||||
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
).then(() => resolve());
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
applyMoveAttrs(NoEffectAttr, user, null, move).then(() => resolve());
|
promiseChain
|
||||||
|
.then(() => applyMoveAttrs(NoEffectAttr, user, null, move))
|
||||||
|
.then(resolve);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
resolve();
|
applyAttrs.push(k);
|
||||||
}
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
|
// Apply the move's POST_TARGET effects on the move's last hit, after all targeted effects have resolved
|
||||||
const postTarget = (user.turnData.hitsLeft === 1 || !this.getTarget()?.isActive()) ?
|
const postTarget = (user.turnData.hitsLeft === 1 || !this.getFirstTarget()?.isActive()) ?
|
||||||
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
|
applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_TARGET, user, null, move) :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
if (!!postTarget) {
|
if (postTarget) {
|
||||||
if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after
|
if (applyAttrs.length) { // If there is a pending asynchronous move effect, do this after
|
||||||
applyAttrs[applyAttrs.length - 1]?.then(() => postTarget);
|
applyAttrs[applyAttrs.length - 1].then(() => postTarget);
|
||||||
} else { // Otherwise, push a new asynchronous move effect
|
} else { // Otherwise, push a new asynchronous move effect
|
||||||
applyAttrs.push(postTarget);
|
applyAttrs.push(postTarget);
|
||||||
}
|
}
|
||||||
@ -327,7 +343,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
*/
|
*/
|
||||||
targets.forEach(target => {
|
targets.forEach(target => {
|
||||||
const substitute = target.getTag(SubstituteTag);
|
const substitute = target.getTag(SubstituteTag);
|
||||||
if (!!substitute && substitute.hp <= 0) {
|
if (substitute && substitute.hp <= 0) {
|
||||||
target.lapseTag(BattlerTagType.SUBSTITUTE);
|
target.lapseTag(BattlerTagType.SUBSTITUTE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -337,7 +353,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
end() {
|
public override end(): void {
|
||||||
const user = this.getUserPokemon();
|
const user = this.getUserPokemon();
|
||||||
/**
|
/**
|
||||||
* If this phase isn't for the invoked move's last strike,
|
* If this phase isn't for the invoked move's last strike,
|
||||||
@ -347,7 +363,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* to the user.
|
* to the user.
|
||||||
*/
|
*/
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) {
|
if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getFirstTarget()?.isActive()) {
|
||||||
this.scene.unshiftPhase(this.getNewHitPhase());
|
this.scene.unshiftPhase(this.getNewHitPhase());
|
||||||
} else {
|
} else {
|
||||||
// Queue message for number of hits made by multi-move
|
// Queue message for number of hits made by multi-move
|
||||||
@ -367,17 +383,145 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves whether this phase's invoked move hits or misses the given target
|
* Apply self-targeted effects that trigger `POST_APPLY`
|
||||||
* @param target {@linkcode Pokemon} the Pokemon targeted by the invoked move
|
*
|
||||||
* @returns `true` if the move does not miss the target; `false` otherwise
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param firstHit - `true` if this is the first hit in a multi-hit attack
|
||||||
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
*/
|
*/
|
||||||
hitCheck(target: Pokemon): boolean {
|
protected applySelfTargetEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): () => Promise<void | null> {
|
||||||
|
return () => applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
|
attr instanceof MoveEffectAttr
|
||||||
|
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||||
|
&& attr.selfTarget
|
||||||
|
&& (!attr.firstHitOnly || firstHit)
|
||||||
|
&& (!attr.lastHitOnly || lastHit), user, target, this.move.getMove());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies non-self-targeted effects that trigger `POST_APPLY`
|
||||||
|
* (i.e. Smelling Salts curing Paralysis, and the forced switch from U-Turn, Dragon Tail, etc)
|
||||||
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param firstHit - `true` if this is the first hit in a multi-hit attack
|
||||||
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
|
*/
|
||||||
|
protected applyPostApplyEffects(user: Pokemon, target: Pokemon, firstHit: boolean, lastHit: boolean): () => Promise<void | null> {
|
||||||
|
return () => applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
|
attr instanceof MoveEffectAttr
|
||||||
|
&& attr.trigger === MoveEffectTrigger.POST_APPLY
|
||||||
|
&& !attr.selfTarget
|
||||||
|
&& (!attr.firstHitOnly || firstHit)
|
||||||
|
&& (!attr.lastHitOnly || lastHit), user, target, this.move.getMove());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies effects that trigger on HIT
|
||||||
|
* (i.e. Final Gambit, Power-Up Punch, Drain Punch)
|
||||||
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param firstHit - `true` if this is the first hit in a multi-hit attack
|
||||||
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
|
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
||||||
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
|
*/
|
||||||
|
protected applyOnHitEffects(user: Pokemon, target: Pokemon, firstHit : boolean, lastHit: boolean, firstTarget: boolean): Promise<void> {
|
||||||
|
return applyFilteredMoveAttrs((attr: MoveAttr) =>
|
||||||
|
attr instanceof MoveEffectAttr
|
||||||
|
&& attr.trigger === MoveEffectTrigger.HIT
|
||||||
|
&& (!attr.firstHitOnly || firstHit)
|
||||||
|
&& (!attr.lastHitOnly || lastHit)
|
||||||
|
&& (!attr.firstTargetOnly || firstTarget), user, target, this.move.getMove());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies reactive effects that occur when a Pokémon is hit.
|
||||||
|
* (i.e. Effect Spore, Disguise, Liquid Ooze, Beak Blast)
|
||||||
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
||||||
|
* @returns a `Promise` intended to be passed into a `then()` call.
|
||||||
|
*/
|
||||||
|
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): Promise<void | null> {
|
||||||
|
return executeIf(!target.isFainted() || target.canApplyAbility(), () =>
|
||||||
|
applyPostDefendAbAttrs(PostDefendAbAttr, target, user, this.move.getMove(), hitResult)
|
||||||
|
.then(() => {
|
||||||
|
|
||||||
|
if (!this.move.getMove().hitsSubstitute(user, target)) {
|
||||||
|
if (!user.isPlayer() && this.move.getMove() instanceof AttackMove) {
|
||||||
|
user.scene.applyShuffledModifiers(this.scene, EnemyAttackStatusEffectChanceModifier, false, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies all effects and attributes that require a move to connect with a target,
|
||||||
|
* namely reactive effects like Weak Armor, on-hit effects like that of Power-Up Punch, and item stealing effects
|
||||||
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param firstHit - `true` if this is the first hit in a multi-hit attack
|
||||||
|
* @param lastHit - `true` if this is the last hit in a multi-hit attack
|
||||||
|
* @param isProtected - `true` if the target is protected by effects such as Protect
|
||||||
|
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
||||||
|
* @param firstTarget - `true` if {@linkcode target} is the first target hit by this strike of {@linkcode move}
|
||||||
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
|
*/
|
||||||
|
protected applySuccessfulAttackEffects(user: Pokemon, target: Pokemon, firstHit : boolean, lastHit: boolean, isProtected : boolean, hitResult: HitResult, firstTarget: boolean) : () => Promise<void | null> {
|
||||||
|
return () => executeIf(!isProtected, () =>
|
||||||
|
this.applyOnHitEffects(user, target, firstHit, lastHit, firstTarget).then(() =>
|
||||||
|
this.applyOnGetHitAbEffects(user, target, hitResult)).then(() =>
|
||||||
|
applyPostAttackAbAttrs(PostAttackAbAttr, user, target, this.move.getMove(), hitResult)).then(() => { // Item Stealing Effects
|
||||||
|
|
||||||
|
if (this.move.getMove() instanceof AttackMove) {
|
||||||
|
this.scene.applyModifiers(ContactHeldItemTransferChanceModifier, this.player, user, target);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles checking for and applying Flinches
|
||||||
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
|
* @param target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
|
* @param dealsDamage - `true` if the attempted move successfully dealt damage
|
||||||
|
* @returns a function intended to be passed into a `then()` call.
|
||||||
|
*/
|
||||||
|
protected applyHeldItemFlinchCheck(user: Pokemon, target: Pokemon, dealsDamage: boolean) : () => void {
|
||||||
|
return () => {
|
||||||
|
if (dealsDamage && !target.hasAbilityWithAttr(IgnoreMoveEffectsAbAttr) && !this.move.getMove().hitsSubstitute(user, target)) {
|
||||||
|
const flinched = new BooleanHolder(false);
|
||||||
|
user.scene.applyModifiers(FlinchChanceModifier, user.isPlayer(), user, flinched);
|
||||||
|
if (flinched.value) {
|
||||||
|
target.addTag(BattlerTagType.FLINCHED, undefined, this.move.moveId, user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves whether this phase's invoked move hits the given target
|
||||||
|
* @param target - The {@linkcode Pokemon} targeted by the invoked move
|
||||||
|
* @returns `true` if the move hits the target
|
||||||
|
*/
|
||||||
|
public hitCheck(target: Pokemon): boolean {
|
||||||
// Moves targeting the user and entry hazards can't miss
|
// Moves targeting the user and entry hazards can't miss
|
||||||
if ([ MoveTarget.USER, MoveTarget.ENEMY_SIDE ].includes(this.move.getMove().moveTarget)) {
|
if ([ MoveTarget.USER, MoveTarget.ENEMY_SIDE ].includes(this.move.getMove().moveTarget)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = this.getUserPokemon()!; // TODO: is this bang correct?
|
const user = this.getUserPokemon();
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits.
|
// Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits.
|
||||||
// However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal
|
// However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal
|
||||||
@ -425,29 +569,29 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
return rand < (moveAccuracy * accuracyMultiplier);
|
return rand < (moveAccuracy * accuracyMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the {@linkcode Pokemon} using this phase's invoked move */
|
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
||||||
getUserPokemon(): Pokemon | undefined {
|
public getUserPokemon(): Pokemon | null {
|
||||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||||
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
return this.scene.getPokemonById(this.battlerIndex);
|
||||||
}
|
}
|
||||||
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns an array of all {@linkcode Pokemon} targeted by this phase's invoked move */
|
/** @returns An array of all {@linkcode Pokemon} targeted by this phase's invoked move */
|
||||||
getTargets(): Pokemon[] {
|
public getTargets(): Pokemon[] {
|
||||||
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
|
return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the first target of this phase's invoked move */
|
/** @returns The first target of this phase's invoked move */
|
||||||
getTarget(): Pokemon | undefined {
|
public getFirstTarget(): Pokemon | undefined {
|
||||||
return this.getTargets()[0];
|
return this.getTargets()[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the given {@linkcode Pokemon} from this phase's target list
|
* Removes the given {@linkcode Pokemon} from this phase's target list
|
||||||
* @param target {@linkcode Pokemon} the Pokemon to be removed
|
* @param target - The {@linkcode Pokemon} to be removed
|
||||||
*/
|
*/
|
||||||
removeTarget(target: Pokemon): void {
|
protected removeTarget(target: Pokemon): void {
|
||||||
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex());
|
const targetIndex = this.targets.findIndex(ind => ind === target.getBattlerIndex());
|
||||||
if (targetIndex !== -1) {
|
if (targetIndex !== -1) {
|
||||||
this.targets.splice(this.targets.findIndex(ind => ind === target.getBattlerIndex()), 1);
|
this.targets.splice(this.targets.findIndex(ind => ind === target.getBattlerIndex()), 1);
|
||||||
@ -456,26 +600,27 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents subsequent strikes of this phase's invoked move from occurring
|
* Prevents subsequent strikes of this phase's invoked move from occurring
|
||||||
* @param target {@linkcode Pokemon} if defined, only stop subsequent
|
* @param target - If defined, only stop subsequent strikes against this {@linkcode Pokemon}
|
||||||
* strikes against this Pokemon
|
|
||||||
*/
|
*/
|
||||||
stopMultiHit(target?: Pokemon): void {
|
public stopMultiHit(target?: Pokemon): void {
|
||||||
/** If given a specific target, remove the target from subsequent strikes */
|
// If given a specific target, remove the target from subsequent strikes
|
||||||
if (target) {
|
if (target) {
|
||||||
this.removeTarget(target);
|
this.removeTarget(target);
|
||||||
}
|
}
|
||||||
/**
|
const user = this.getUserPokemon();
|
||||||
* If no target specified, or the specified target was the last of this move's
|
if (!user) {
|
||||||
* targets, completely cancel all subsequent strikes.
|
return;
|
||||||
*/
|
}
|
||||||
|
// If no target specified, or the specified target was the last of this move's
|
||||||
|
// targets, completely cancel all subsequent strikes.
|
||||||
if (!target || this.targets.length === 0 ) {
|
if (!target || this.targets.length === 0 ) {
|
||||||
this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here?
|
user.turnData.hitCount = 1;
|
||||||
this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here?
|
user.turnData.hitsLeft = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a new MoveEffectPhase with the same properties as this phase */
|
/** @returns A new `MoveEffectPhase` with the same properties as this phase */
|
||||||
getNewHitPhase() {
|
protected getNewHitPhase(): MoveEffectPhase {
|
||||||
return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move);
|
return new MoveEffectPhase(this.scene, this.battlerIndex, this.targets, this.move);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
|
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
||||||
|
import MysteryEncounterOption, { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option";
|
||||||
|
import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
||||||
|
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
||||||
|
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
||||||
|
import { GameOverPhase } from "#app/phases/game-over-phase";
|
||||||
|
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
||||||
|
import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
|
||||||
|
import { ReturnPhase } from "#app/phases/return-phase";
|
||||||
|
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
||||||
|
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||||
|
import { SummonPhase } from "#app/phases/summon-phase";
|
||||||
|
import { SwitchPhase } from "#app/phases/switch-phase";
|
||||||
|
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
||||||
|
import { BattleSpec } from "#enums/battle-spec";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
||||||
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import BattleScene from "../battle-scene";
|
import BattleScene from "../battle-scene";
|
||||||
|
import { getCharVariantFromDialogue } from "../data/dialogue";
|
||||||
|
import { OptionSelectSettings, transitionMysteryEncounterIntroVisuals } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { TrainerSlot } from "../data/trainer-config";
|
||||||
|
import { IvScannerModifier } from "../modifier/modifier";
|
||||||
import { Phase } from "../phase";
|
import { Phase } from "../phase";
|
||||||
import { Mode } from "../ui/ui";
|
import { Mode } from "../ui/ui";
|
||||||
import { transitionMysteryEncounterIntroVisuals, OptionSelectSettings } from "../data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
import MysteryEncounterOption, { OptionPhaseCallback } from "#app/data/mystery-encounters/mystery-encounter-option";
|
|
||||||
import { getCharVariantFromDialogue } from "../data/dialogue";
|
|
||||||
import { TrainerSlot } from "../data/trainer-config";
|
|
||||||
import { BattleSpec } from "#enums/battle-spec";
|
|
||||||
import { IvScannerModifier } from "../modifier/modifier";
|
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
import { isNullOrUndefined } from "../utils";
|
import { isNullOrUndefined } from "../utils";
|
||||||
import { getEncounterText } from "#app/data/mystery-encounters/utils/encounter-dialogue-utils";
|
|
||||||
import { BattlerTagLapseType } from "#app/data/battler-tags";
|
|
||||||
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
|
|
||||||
import { PostTurnStatusEffectPhase } from "#app/phases/post-turn-status-effect-phase";
|
|
||||||
import { SummonPhase } from "#app/phases/summon-phase";
|
|
||||||
import { ScanIvsPhase } from "#app/phases/scan-ivs-phase";
|
|
||||||
import { ToggleDoublePositionPhase } from "#app/phases/toggle-double-position-phase";
|
|
||||||
import { ReturnPhase } from "#app/phases/return-phase";
|
|
||||||
import { CheckSwitchPhase } from "#app/phases/check-switch-phase";
|
|
||||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
|
||||||
import { NewBattlePhase } from "#app/phases/new-battle-phase";
|
|
||||||
import { GameOverPhase } from "#app/phases/game-over-phase";
|
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
|
||||||
import { SeenEncounterData } from "#app/data/mystery-encounters/mystery-encounter-save-data";
|
|
||||||
import { SwitchType } from "#enums/switch-type";
|
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will handle (in order):
|
* Will handle (in order):
|
||||||
@ -238,7 +238,7 @@ export class MysteryEncounterBattleStartCleanupPhase extends Phase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The total number of Pokemon in the player's party that can legally fight
|
// The total number of Pokemon in the player's party that can legally fight
|
||||||
const legalPlayerPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
const legalPlayerPokemon = this.scene.getPokemonAllowedInBattle();
|
||||||
// The total number of legal player Pokemon that aren't currently on the field
|
// The total number of legal player Pokemon that aren't currently on the field
|
||||||
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
|
const legalPlayerPartyPokemon = legalPlayerPokemon.filter(p => !p.isActive(true));
|
||||||
if (!legalPlayerPokemon.length) {
|
if (!legalPlayerPokemon.length) {
|
||||||
@ -343,7 +343,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
const doSummon = () => {
|
const doSummon = () => {
|
||||||
scene.currentBattle.started = true;
|
scene.currentBattle.started = true;
|
||||||
scene.playBgm(undefined);
|
scene.playBgm(undefined);
|
||||||
scene.pbTray.showPbTray(scene.getParty());
|
scene.pbTray.showPbTray(scene.getPlayerParty());
|
||||||
scene.pbTrayEnemy.showPbTray(scene.getEnemyParty());
|
scene.pbTrayEnemy.showPbTray(scene.getEnemyParty());
|
||||||
const doTrainerSummon = () => {
|
const doTrainerSummon = () => {
|
||||||
this.hideEnemyTrainer();
|
this.hideEnemyTrainer();
|
||||||
@ -402,7 +402,7 @@ export class MysteryEncounterBattlePhase extends Phase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const availablePartyMembers = scene.getParty().filter(p => p.isAllowedInBattle());
|
const availablePartyMembers = scene.getPlayerParty().filter(p => p.isAllowedInBattle());
|
||||||
|
|
||||||
if (!availablePartyMembers[0].isOnField()) {
|
if (!availablePartyMembers[0].isOnField()) {
|
||||||
scene.pushPhase(new SummonPhase(scene, 0));
|
scene.pushPhase(new SummonPhase(scene, 0));
|
||||||
|
@ -11,13 +11,13 @@ export class NewBiomeEncounterPhase extends NextEncounterPhase {
|
|||||||
doEncounter(): void {
|
doEncounter(): void {
|
||||||
this.scene.playBgm(undefined, true);
|
this.scene.playBgm(undefined, true);
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty()) {
|
for (const pokemon of this.scene.getPlayerParty()) {
|
||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
pokemon.resetBattleData();
|
pokemon.resetBattleData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty().filter(p => p.isOnField())) {
|
for (const pokemon of this.scene.getPlayerParty().filter(p => p.isOnField())) {
|
||||||
applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null);
|
applyAbAttrs(PostBiomeChangeAbAttr, pokemon, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export class NextEncounterPhase extends EncounterPhase {
|
|||||||
doEncounter(): void {
|
doEncounter(): void {
|
||||||
this.scene.playBgm(undefined, true);
|
this.scene.playBgm(undefined, true);
|
||||||
|
|
||||||
for (const pokemon of this.scene.getParty()) {
|
for (const pokemon of this.scene.getPlayerParty()) {
|
||||||
if (pokemon) {
|
if (pokemon) {
|
||||||
pokemon.resetBattleData();
|
pokemon.resetBattleData();
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export class PartyHealPhase extends BattlePhase {
|
|||||||
this.scene.fadeOutBgm(1000, false);
|
this.scene.fadeOutBgm(1000, false);
|
||||||
}
|
}
|
||||||
this.scene.ui.fadeOut(1000).then(() => {
|
this.scene.ui.fadeOut(1000).then(() => {
|
||||||
for (const pokemon of this.scene.getParty()) {
|
for (const pokemon of this.scene.getPlayerParty()) {
|
||||||
pokemon.hp = pokemon.getMaxHp();
|
pokemon.hp = pokemon.getMaxHp();
|
||||||
pokemon.resetStatus();
|
pokemon.resetStatus();
|
||||||
for (const move of pokemon.moveset) {
|
for (const move of pokemon.moveset) {
|
||||||
|
@ -18,7 +18,7 @@ export abstract class PartyMemberPokemonPhase extends FieldPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getParty(): Pokemon[] {
|
getParty(): Pokemon[] {
|
||||||
return this.player ? this.scene.getParty() : this.scene.getEnemyParty();
|
return this.player ? this.scene.getPlayerParty() : this.scene.getEnemyParty();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPokemon(): Pokemon {
|
getPokemon(): Pokemon {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, BlockNonDirectDamageAbAttr, BlockStatusDamageAbAttr, ReduceBurnDamageAbAttr } from "#app/data/ability";
|
import { applyAbAttrs, applyPostDamageAbAttrs, BlockNonDirectDamageAbAttr, BlockStatusDamageAbAttr, PostDamageAbAttr, ReduceBurnDamageAbAttr } from "#app/data/ability";
|
||||||
import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims";
|
import { CommonBattleAnim, CommonAnim } from "#app/data/battle-anims";
|
||||||
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
import { getStatusEffectActivationText } from "#app/data/status-effect";
|
||||||
import { BattleSpec } from "#app/enums/battle-spec";
|
import { BattleSpec } from "#app/enums/battle-spec";
|
||||||
@ -41,6 +41,7 @@ export class PostTurnStatusEffectPhase extends PokemonPhase {
|
|||||||
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
// Set preventEndure flag to avoid pokemon surviving thanks to focus band, sturdy, endure ...
|
||||||
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
this.scene.damageNumberHandler.add(this.getPokemon(), pokemon.damage(damage.value, false, true));
|
||||||
pokemon.updateInfo();
|
pokemon.updateInfo();
|
||||||
|
applyPostDamageAbAttrs(PostDamageAbAttr, pokemon, damage.value, pokemon.hasPassive(), false, []);
|
||||||
}
|
}
|
||||||
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, false, () => this.end());
|
new CommonBattleAnim(CommonAnim.POISON + (pokemon.status.effect - 1), pokemon).play(this.scene, false, () => this.end());
|
||||||
} else {
|
} else {
|
||||||
|
@ -38,7 +38,7 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
this.scene.reroll = false;
|
this.scene.reroll = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getPlayerParty();
|
||||||
if (!this.isCopy) {
|
if (!this.isCopy) {
|
||||||
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
|
regenerateModifierPoolThresholds(party, this.getPoolType(), this.rerollCount);
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ export class SelectModifierPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] {
|
getModifierTypeOptions(modifierCount: integer): ModifierTypeOption[] {
|
||||||
return getPlayerModifierTypeOptions(modifierCount, this.scene.getParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.customModifierSettings);
|
return getPlayerModifierTypeOptions(modifierCount, this.scene.getPlayerParty(), this.scene.lockModifierTiers ? this.modifierTiers : undefined, this.customModifierSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(): SelectModifierPhase {
|
copy(): SelectModifierPhase {
|
||||||
|
@ -3,16 +3,15 @@ import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
|||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
|
import { SpeciesFormChangeMoveLearnedTrigger } from "#app/data/pokemon-forms";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Species } from "#app/enums/species";
|
import { overrideHeldItems, overrideModifiers } from "#app/modifier/modifier";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import Overrides from "#app/overrides";
|
||||||
import { overrideModifiers, overrideHeldItems } from "#app/modifier/modifier";
|
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
|
import { TitlePhase } from "#app/phases/title-phase";
|
||||||
import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler";
|
import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler";
|
||||||
import { Starter } from "#app/ui/starter-select-ui-handler";
|
import { Starter } from "#app/ui/starter-select-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||||
import { TitlePhase } from "./title-phase";
|
|
||||||
import Overrides from "#app/overrides";
|
|
||||||
|
|
||||||
export class SelectStarterPhase extends Phase {
|
export class SelectStarterPhase extends Phase {
|
||||||
|
|
||||||
@ -44,7 +43,7 @@ export class SelectStarterPhase extends Phase {
|
|||||||
* @param starters {@linkcode Pokemon} with which to start the first battle
|
* @param starters {@linkcode Pokemon} with which to start the first battle
|
||||||
*/
|
*/
|
||||||
initBattle(starters: Starter[]) {
|
initBattle(starters: Starter[]) {
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getPlayerParty();
|
||||||
const loadPokemonAssets: Promise<void>[] = [];
|
const loadPokemonAssets: Promise<void>[] = [];
|
||||||
starters.forEach((starter: Starter, i: integer) => {
|
starters.forEach((starter: Starter, i: integer) => {
|
||||||
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
if (!i && Overrides.STARTER_SPECIES_OVERRIDE) {
|
||||||
@ -103,7 +102,7 @@ export class SelectStarterPhase extends Phase {
|
|||||||
this.scene.sessionPlayTime = 0;
|
this.scene.sessionPlayTime = 0;
|
||||||
this.scene.lastSavePlayTime = 0;
|
this.scene.lastSavePlayTime = 0;
|
||||||
// Ensures Keldeo (or any future Pokemon that have this type of form change) starts in the correct form
|
// Ensures Keldeo (or any future Pokemon that have this type of form change) starts in the correct form
|
||||||
this.scene.getParty().forEach((p: PlayerPokemon) => {
|
this.scene.getPlayerParty().forEach((p) => {
|
||||||
this.scene.triggerPokemonFormChange(p, SpeciesFormChangeMoveLearnedTrigger);
|
this.scene.triggerPokemonFormChange(p, SpeciesFormChangeMoveLearnedTrigger);
|
||||||
});
|
});
|
||||||
this.end();
|
this.end();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
import BattleScene from "#app/battle-scene";
|
||||||
import { SwitchType } from "#enums/switch-type";
|
import PartyUiHandler, { PartyOption, PartyUiMode } from "#app/ui/party-ui-handler";
|
||||||
import PartyUiHandler, { PartyUiMode, PartyOption } from "#app/ui/party-ui-handler";
|
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
import { SwitchSummonPhase } from "./switch-summon-phase";
|
import { SwitchSummonPhase } from "./switch-summon-phase";
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ export class SwitchPhase extends BattlePhase {
|
|||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
// Skip modal switch if impossible (no remaining party members that aren't in battle)
|
// Skip modal switch if impossible (no remaining party members that aren't in battle)
|
||||||
if (this.isModal && !this.scene.getParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) {
|
if (this.isModal && !this.scene.getPlayerParty().filter(p => p.isAllowedInBattle() && !p.isActive(true)).length) {
|
||||||
return super.end();
|
return super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ export class SwitchPhase extends BattlePhase {
|
|||||||
* if the mon should have already been returned but is still alive and well
|
* if the mon should have already been returned but is still alive and well
|
||||||
* on the field. see also; battle.test.ts
|
* on the field. see also; battle.test.ts
|
||||||
*/
|
*/
|
||||||
if (this.isModal && !this.doReturn && !this.scene.getParty()[this.fieldIndex].isFainted()) {
|
if (this.isModal && !this.doReturn && !this.scene.getPlayerParty()[this.fieldIndex].isFainted()) {
|
||||||
return super.end();
|
return super.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ export class SwitchPhase extends BattlePhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once
|
// Override field index to 0 in case of double battle where 2/3 remaining legal party members fainted at once
|
||||||
const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? this.fieldIndex : 0;
|
const fieldIndex = this.scene.currentBattle.getBattlerCount() === 1 || this.scene.getPokemonAllowedInBattle().length > 1 ? this.fieldIndex : 0;
|
||||||
|
|
||||||
this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => {
|
this.scene.ui.setMode(Mode.PARTY, this.isModal ? PartyUiMode.FAINT_SWITCH : PartyUiMode.POST_BATTLE_SWITCH, fieldIndex, (slotIndex: integer, option: PartyOption) => {
|
||||||
if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) {
|
if (slotIndex >= this.scene.currentBattle.getBattlerCount() && slotIndex < 6) {
|
||||||
|
@ -54,7 +54,7 @@ export class SwitchSummonPhase extends SummonPhase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getParty() : this.scene.getEnemyParty())[this.slotIndex])) {
|
if (!this.doReturn || (this.slotIndex !== -1 && !(this.player ? this.scene.getPlayerParty() : this.scene.getEnemyParty())[this.slotIndex])) {
|
||||||
if (this.player) {
|
if (this.player) {
|
||||||
return this.switchAndSummon();
|
return this.switchAndSummon();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import { loggedInUser } from "#app/account";
|
import { loggedInUser } from "#app/account";
|
||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattleType } from "#app/battle";
|
import { BattleType } from "#app/battle";
|
||||||
import { getDailyRunStarters, fetchDailyRunSeed } from "#app/data/daily-run";
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { fetchDailyRunSeed, getDailyRunStarters } from "#app/data/daily-run";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
import { getBiomeKey } from "#app/field/arena";
|
import { getBiomeKey } from "#app/field/arena";
|
||||||
import { GameModes, GameMode, getGameMode } from "#app/game-mode";
|
import { GameMode, GameModes, getGameMode } from "#app/game-mode";
|
||||||
import { regenerateModifierPoolThresholds, ModifierPoolType, modifierTypes, getDailyRunStarterModifiers } from "#app/modifier/modifier-type";
|
import { Modifier } from "#app/modifier/modifier";
|
||||||
|
import { getDailyRunStarterModifiers, ModifierPoolType, modifierTypes, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||||
import { Phase } from "#app/phase";
|
import { Phase } from "#app/phase";
|
||||||
import { SessionSaveData } from "#app/system/game-data";
|
import { SessionSaveData } from "#app/system/game-data";
|
||||||
import { Unlockables } from "#app/system/unlockables";
|
import { Unlockables } from "#app/system/unlockables";
|
||||||
import { vouchers } from "#app/system/voucher";
|
import { vouchers } from "#app/system/voucher";
|
||||||
import { OptionSelectItem, OptionSelectConfig } from "#app/ui/abstact-option-select-ui-handler";
|
import { OptionSelectConfig, OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler";
|
import { SaveSlotUiMode } from "#app/ui/save-slot-select-ui-handler";
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import i18next from "i18next";
|
|
||||||
import * as Utils from "#app/utils";
|
import * as Utils from "#app/utils";
|
||||||
import { Modifier } from "#app/modifier/modifier";
|
import i18next from "i18next";
|
||||||
import { CheckSwitchPhase } from "./check-switch-phase";
|
import { CheckSwitchPhase } from "./check-switch-phase";
|
||||||
import { EncounterPhase } from "./encounter-phase";
|
import { EncounterPhase } from "./encounter-phase";
|
||||||
import { SelectChallengePhase } from "./select-challenge-phase";
|
import { SelectChallengePhase } from "./select-challenge-phase";
|
||||||
@ -203,7 +203,7 @@ export class TitlePhase extends Phase {
|
|||||||
const starters = getDailyRunStarters(this.scene, seed);
|
const starters = getDailyRunStarters(this.scene, seed);
|
||||||
const startingLevel = this.scene.gameMode.getStartingLevel();
|
const startingLevel = this.scene.gameMode.getStartingLevel();
|
||||||
|
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getPlayerParty();
|
||||||
const loadPokemonAssets: Promise<void>[] = [];
|
const loadPokemonAssets: Promise<void>[] = [];
|
||||||
for (const starter of starters) {
|
for (const starter of starters) {
|
||||||
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
const starterProps = this.scene.gameData.getSpeciesDexAttrProps(starter.species, starter.dexAttr);
|
||||||
@ -276,7 +276,7 @@ export class TitlePhase extends Phase {
|
|||||||
this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded));
|
this.scene.pushPhase(new EncounterPhase(this.scene, this.loaded));
|
||||||
|
|
||||||
if (this.loaded) {
|
if (this.loaded) {
|
||||||
const availablePartyMembers = this.scene.getParty().filter(p => p.isAllowedInBattle()).length;
|
const availablePartyMembers = this.scene.getPokemonAllowedInBattle().length;
|
||||||
|
|
||||||
this.scene.pushPhase(new SummonPhase(this.scene, 0, true, true));
|
this.scene.pushPhase(new SummonPhase(this.scene, 0, true, true));
|
||||||
if (this.scene.currentBattle.double && availablePartyMembers > 1) {
|
if (this.scene.currentBattle.double && availablePartyMembers > 1) {
|
||||||
|
@ -16,9 +16,9 @@ export class ToggleDoublePositionPhase extends BattlePhase {
|
|||||||
|
|
||||||
const playerPokemon = this.scene.getPlayerField().find(p => p.isActive(true));
|
const playerPokemon = this.scene.getPlayerField().find(p => p.isActive(true));
|
||||||
if (playerPokemon) {
|
if (playerPokemon) {
|
||||||
playerPokemon.setFieldPosition(this.double && this.scene.getParty().filter(p => p.isAllowedInBattle()).length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER, 500).then(() => {
|
playerPokemon.setFieldPosition(this.double && this.scene.getPokemonAllowedInBattle().length > 1 ? FieldPosition.LEFT : FieldPosition.CENTER, 500).then(() => {
|
||||||
if (playerPokemon.getFieldIndex() === 1) {
|
if (playerPokemon.getFieldIndex() === 1) {
|
||||||
const party = this.scene.getParty();
|
const party = this.scene.getPlayerParty();
|
||||||
party[1] = party[0];
|
party[1] = party[0];
|
||||||
party[0] = playerPokemon;
|
party[0] = playerPokemon;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import BattleScene from "#app/battle-scene";
|
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import BattleScene from "#app/battle-scene";
|
||||||
|
import { handleMysteryEncounterBattleStartEffects, handleMysteryEncounterTurnStartEffects } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
import { TurnInitEvent } from "#app/events/battle-scene";
|
import { TurnInitEvent } from "#app/events/battle-scene";
|
||||||
import { PlayerPokemon } from "#app/field/pokemon";
|
import { PlayerPokemon } from "#app/field/pokemon";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { FieldPhase } from "./field-phase";
|
|
||||||
import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
|
||||||
import { CommandPhase } from "./command-phase";
|
import { CommandPhase } from "./command-phase";
|
||||||
import { EnemyCommandPhase } from "./enemy-command-phase";
|
import { EnemyCommandPhase } from "./enemy-command-phase";
|
||||||
|
import { FieldPhase } from "./field-phase";
|
||||||
import { GameOverPhase } from "./game-over-phase";
|
import { GameOverPhase } from "./game-over-phase";
|
||||||
|
import { ToggleDoublePositionPhase } from "./toggle-double-position-phase";
|
||||||
import { TurnStartPhase } from "./turn-start-phase";
|
import { TurnStartPhase } from "./turn-start-phase";
|
||||||
import { handleMysteryEncounterBattleStartEffects, handleMysteryEncounterTurnStartEffects } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
|
||||||
|
|
||||||
export class TurnInitPhase extends FieldPhase {
|
export class TurnInitPhase extends FieldPhase {
|
||||||
constructor(scene: BattleScene) {
|
constructor(scene: BattleScene) {
|
||||||
@ -24,7 +24,7 @@ export class TurnInitPhase extends FieldPhase {
|
|||||||
if (p.isOnField() && !p.isAllowedInBattle()) {
|
if (p.isOnField() && !p.isAllowedInBattle()) {
|
||||||
this.scene.queueMessage(i18next.t("challenges:illegalEvolution", { "pokemon": p.name }), null, true);
|
this.scene.queueMessage(i18next.t("challenges:illegalEvolution", { "pokemon": p.name }), null, true);
|
||||||
|
|
||||||
const allowedPokemon = this.scene.getParty().filter(p => p.isAllowedInBattle());
|
const allowedPokemon = this.scene.getPokemonAllowedInBattle();
|
||||||
|
|
||||||
if (!allowedPokemon.length) {
|
if (!allowedPokemon.length) {
|
||||||
// If there are no longer any legal pokemon in the party, game over.
|
// If there are no longer any legal pokemon in the party, game over.
|
||||||
|
@ -328,7 +328,7 @@ export const achvs = {
|
|||||||
HIDDEN_ABILITY: new Achv("HIDDEN_ABILITY", "", "HIDDEN_ABILITY.description", "ability_charm", 75),
|
HIDDEN_ABILITY: new Achv("HIDDEN_ABILITY", "", "HIDDEN_ABILITY.description", "ability_charm", 75),
|
||||||
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
PERFECT_IVS: new Achv("PERFECT_IVS", "", "PERFECT_IVS.description", "blunder_policy", 100),
|
||||||
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150, c => c.gameData.gameStats.sessionsWon === 0),
|
CLASSIC_VICTORY: new Achv("CLASSIC_VICTORY", "", "CLASSIC_VICTORY.description", "relic_crown", 150, c => c.gameData.gameStats.sessionsWon === 0),
|
||||||
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
UNEVOLVED_CLASSIC_VICTORY: new Achv("UNEVOLVED_CLASSIC_VICTORY", "", "UNEVOLVED_CLASSIC_VICTORY.description", "eviolite", 175, c => c.getPlayerParty().some(p => p.getSpeciesForm(true).speciesId in pokemonEvolutions)),
|
||||||
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_ONE_VICTORY: new ChallengeAchv("MONO_GEN_ONE", "", "MONO_GEN_ONE.description", "ribbon_gen1", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 1 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_TWO_VICTORY: new ChallengeAchv("MONO_GEN_TWO", "", "MONO_GEN_TWO.description", "ribbon_gen2", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 2 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
MONO_GEN_THREE_VICTORY: new ChallengeAchv("MONO_GEN_THREE", "", "MONO_GEN_THREE.description", "ribbon_gen3", 100, (c, scene) => c instanceof SingleGenerationChallenge && c.value === 3 && !scene.gameMode.challenges.some(c => c.id === Challenges.INVERSE_BATTLE && c.value > 0)),
|
||||||
|
@ -949,7 +949,7 @@ export class GameData {
|
|||||||
seed: scene.seed,
|
seed: scene.seed,
|
||||||
playTime: scene.sessionPlayTime,
|
playTime: scene.sessionPlayTime,
|
||||||
gameMode: scene.gameMode.modeId,
|
gameMode: scene.gameMode.modeId,
|
||||||
party: scene.getParty().map(p => new PokemonData(p)),
|
party: scene.getPlayerParty().map(p => new PokemonData(p)),
|
||||||
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
|
enemyParty: scene.getEnemyParty().map(p => new PokemonData(p)),
|
||||||
modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)),
|
modifiers: scene.findModifiers(() => true).map(m => new PersistentModifierData(m, true)),
|
||||||
enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)),
|
enemyModifiers: scene.findModifiers(() => true, false).map(m => new PersistentModifierData(m, false)),
|
||||||
@ -1028,7 +1028,7 @@ export class GameData {
|
|||||||
|
|
||||||
const loadPokemonAssets: Promise<void>[] = [];
|
const loadPokemonAssets: Promise<void>[] = [];
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
party.splice(0, party.length);
|
party.splice(0, party.length);
|
||||||
|
|
||||||
for (const p of sessionData.party) {
|
for (const p of sessionData.party) {
|
||||||
@ -1829,17 +1829,40 @@ export class GameData {
|
|||||||
return starterCount;
|
return starterCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSpeciesDefaultDexAttr(species: PokemonSpecies, forSeen: boolean = false, optimistic: boolean = false): bigint {
|
getSpeciesDefaultDexAttr(species: PokemonSpecies, _forSeen: boolean = false, optimistic: boolean = false): bigint {
|
||||||
let ret = 0n;
|
let ret = 0n;
|
||||||
const dexEntry = this.dexData[species.speciesId];
|
const dexEntry = this.dexData[species.speciesId];
|
||||||
const attr = dexEntry.caughtAttr;
|
const attr = dexEntry.caughtAttr;
|
||||||
ret |= optimistic
|
if (optimistic) {
|
||||||
? attr & DexAttr.SHINY ? DexAttr.SHINY : DexAttr.NON_SHINY
|
if (attr & DexAttr.SHINY) {
|
||||||
: attr & DexAttr.NON_SHINY || !(attr & DexAttr.SHINY) ? DexAttr.NON_SHINY : DexAttr.SHINY;
|
ret |= DexAttr.SHINY;
|
||||||
|
|
||||||
|
if (attr & DexAttr.VARIANT_3) {
|
||||||
|
ret |= DexAttr.VARIANT_3;
|
||||||
|
} else if (attr & DexAttr.VARIANT_2) {
|
||||||
|
ret |= DexAttr.VARIANT_2;
|
||||||
|
} else {
|
||||||
|
ret |= DexAttr.DEFAULT_VARIANT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret |= DexAttr.NON_SHINY;
|
||||||
|
ret |= DexAttr.DEFAULT_VARIANT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Default to non shiny. Fallback to shiny if it's the only thing that's unlocked
|
||||||
|
ret |= (attr & DexAttr.NON_SHINY || !(attr & DexAttr.SHINY)) ? DexAttr.NON_SHINY : DexAttr.SHINY;
|
||||||
|
|
||||||
|
if (attr & DexAttr.DEFAULT_VARIANT) {
|
||||||
|
ret |= DexAttr.DEFAULT_VARIANT;
|
||||||
|
} else if (attr & DexAttr.VARIANT_2) {
|
||||||
|
ret |= DexAttr.VARIANT_2;
|
||||||
|
} else if (attr & DexAttr.VARIANT_3) {
|
||||||
|
ret |= DexAttr.VARIANT_3;
|
||||||
|
} else {
|
||||||
|
ret |= DexAttr.DEFAULT_VARIANT;
|
||||||
|
}
|
||||||
|
}
|
||||||
ret |= attr & DexAttr.MALE || !(attr & DexAttr.FEMALE) ? DexAttr.MALE : DexAttr.FEMALE;
|
ret |= attr & DexAttr.MALE || !(attr & DexAttr.FEMALE) ? DexAttr.MALE : DexAttr.FEMALE;
|
||||||
ret |= optimistic
|
|
||||||
? attr & DexAttr.SHINY ? attr & DexAttr.VARIANT_3 ? DexAttr.VARIANT_3 : attr & DexAttr.VARIANT_2 ? DexAttr.VARIANT_2 : DexAttr.DEFAULT_VARIANT : DexAttr.DEFAULT_VARIANT
|
|
||||||
: attr & DexAttr.DEFAULT_VARIANT ? DexAttr.DEFAULT_VARIANT : attr & DexAttr.VARIANT_2 ? DexAttr.VARIANT_2 : attr & DexAttr.VARIANT_3 ? DexAttr.VARIANT_3 : DexAttr.DEFAULT_VARIANT;
|
|
||||||
ret |= this.getFormAttr(this.getFormIndex(attr));
|
ret |= this.getFormAttr(this.getFormIndex(attr));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1847,7 +1870,14 @@ export class GameData {
|
|||||||
getSpeciesDexAttrProps(species: PokemonSpecies, dexAttr: bigint): DexAttrProps {
|
getSpeciesDexAttrProps(species: PokemonSpecies, dexAttr: bigint): DexAttrProps {
|
||||||
const shiny = !(dexAttr & DexAttr.NON_SHINY);
|
const shiny = !(dexAttr & DexAttr.NON_SHINY);
|
||||||
const female = !(dexAttr & DexAttr.MALE);
|
const female = !(dexAttr & DexAttr.MALE);
|
||||||
const variant = dexAttr & DexAttr.DEFAULT_VARIANT ? 0 : dexAttr & DexAttr.VARIANT_2 ? 1 : dexAttr & DexAttr.VARIANT_3 ? 2 : 0;
|
let variant: Variant = 0;
|
||||||
|
if (dexAttr & DexAttr.DEFAULT_VARIANT) {
|
||||||
|
variant = 0;
|
||||||
|
} else if (dexAttr & DexAttr.VARIANT_2) {
|
||||||
|
variant = 1;
|
||||||
|
} else if (dexAttr & DexAttr.VARIANT_3) {
|
||||||
|
variant = 2;
|
||||||
|
}
|
||||||
const formIndex = this.getFormIndex(dexAttr);
|
const formIndex = this.getFormIndex(dexAttr);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -38,7 +38,7 @@ export default class ModifierData {
|
|||||||
type.id = this.typeId;
|
type.id = this.typeId;
|
||||||
|
|
||||||
if (type instanceof ModifierTypeGenerator) {
|
if (type instanceof ModifierTypeGenerator) {
|
||||||
type = (type as ModifierTypeGenerator).generateType(this.player ? scene.getParty() : scene.getEnemyField(), this.typePregenArgs);
|
type = (type as ModifierTypeGenerator).generateType(this.player ? scene.getPlayerParty() : scene.getEnemyField(), this.typePregenArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ret = Reflect.construct(constructor, ([ type ] as any[]).concat(this.args).concat(this.stackCount)) as PersistentModifier;
|
const ret = Reflect.construct(constructor, ([ type ] as any[]).concat(this.args).concat(this.stackCount)) as PersistentModifier;
|
||||||
|
@ -163,6 +163,11 @@ export const SettingKeys = {
|
|||||||
Shop_Overlay_Opacity: "SHOP_OVERLAY_OPACITY"
|
Shop_Overlay_Opacity: "SHOP_OVERLAY_OPACITY"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum MusicPreference {
|
||||||
|
CONSISTENT,
|
||||||
|
MIXED
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All Settings not related to controls
|
* All Settings not related to controls
|
||||||
*/
|
*/
|
||||||
@ -634,7 +639,7 @@ export const Setting: Array<Setting> = [
|
|||||||
label: i18next.t("settings:mixed")
|
label: i18next.t("settings:mixed")
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
default: 0,
|
default: MusicPreference.MIXED,
|
||||||
type: SettingType.AUDIO,
|
type: SettingType.AUDIO,
|
||||||
requireReload: true
|
requireReload: true
|
||||||
},
|
},
|
||||||
|
@ -36,7 +36,7 @@ describe("Moves - Aroma Veil", () => {
|
|||||||
it("Aroma Veil protects the Pokemon's side against most Move Restriction Battler Tags", async () => {
|
it("Aroma Veil protects the Pokemon's side against most Move Restriction Battler Tags", async () => {
|
||||||
await game.classicMode.startBattle([ Species.REGIELEKI, Species.BULBASAUR ]);
|
await game.classicMode.startBattle([ Species.REGIELEKI, Species.BULBASAUR ]);
|
||||||
|
|
||||||
const party = game.scene.getParty()! as PlayerPokemon[];
|
const party = game.scene.getPlayerParty()! as PlayerPokemon[];
|
||||||
|
|
||||||
game.move.select(Moves.GROWL);
|
game.move.select(Moves.GROWL);
|
||||||
game.move.select(Moves.GROWL);
|
game.move.select(Moves.GROWL);
|
||||||
@ -50,7 +50,7 @@ describe("Moves - Aroma Veil", () => {
|
|||||||
it("Aroma Veil does not protect against Imprison", async () => {
|
it("Aroma Veil does not protect against Imprison", async () => {
|
||||||
await game.classicMode.startBattle([ Species.REGIELEKI, Species.BULBASAUR ]);
|
await game.classicMode.startBattle([ Species.REGIELEKI, Species.BULBASAUR ]);
|
||||||
|
|
||||||
const party = game.scene.getParty()! as PlayerPokemon[];
|
const party = game.scene.getPlayerParty()! as PlayerPokemon[];
|
||||||
|
|
||||||
game.move.select(Moves.GROWL);
|
game.move.select(Moves.GROWL);
|
||||||
game.move.select(Moves.GROWL, 1);
|
game.move.select(Moves.GROWL, 1);
|
||||||
|
@ -40,7 +40,7 @@ describe("Abilities - BATTLE BOND", () => {
|
|||||||
it("check if fainted pokemon switches to base form on arena reset", async () => {
|
it("check if fainted pokemon switches to base form on arena reset", async () => {
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.GRENINJA ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP, Species.GRENINJA ]);
|
||||||
|
|
||||||
const greninja = game.scene.getParty()[1];
|
const greninja = game.scene.getPlayerParty()[1];
|
||||||
expect(greninja.formIndex).toBe(ashForm);
|
expect(greninja.formIndex).toBe(ashForm);
|
||||||
|
|
||||||
greninja.hp = 0;
|
greninja.hp = 0;
|
||||||
|
@ -138,7 +138,7 @@ describe("Abilities - Disguise", () => {
|
|||||||
});
|
});
|
||||||
await game.classicMode.startBattle([ Species.FURRET, Species.MIMIKYU ]);
|
await game.classicMode.startBattle([ Species.FURRET, Species.MIMIKYU ]);
|
||||||
|
|
||||||
const mimikyu = game.scene.getParty()[1]!;
|
const mimikyu = game.scene.getPlayerParty()[1]!;
|
||||||
expect(mimikyu.formIndex).toBe(bustedForm);
|
expect(mimikyu.formIndex).toBe(bustedForm);
|
||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
|
@ -81,7 +81,7 @@ describe("Abilities - Forecast", () => {
|
|||||||
});
|
});
|
||||||
await game.startBattle([ Species.CASTFORM, Species.FEEBAS, Species.KYOGRE, Species.GROUDON, Species.RAYQUAZA, Species.ALTARIA ]);
|
await game.startBattle([ Species.CASTFORM, Species.FEEBAS, Species.KYOGRE, Species.GROUDON, Species.RAYQUAZA, Species.ALTARIA ]);
|
||||||
|
|
||||||
vi.spyOn(game.scene.getParty()[5], "getAbility").mockReturnValue(allAbilities[Abilities.CLOUD_NINE]);
|
vi.spyOn(game.scene.getPlayerParty()[5], "getAbility").mockReturnValue(allAbilities[Abilities.CLOUD_NINE]);
|
||||||
|
|
||||||
const castform = game.scene.getPlayerField()[0];
|
const castform = game.scene.getPlayerField()[0];
|
||||||
expect(castform.formIndex).toBe(NORMAL_FORM);
|
expect(castform.formIndex).toBe(NORMAL_FORM);
|
||||||
|
@ -192,7 +192,7 @@ describe("Abilities - Ice Face", () => {
|
|||||||
game.doSwitchPokemon(1);
|
game.doSwitchPokemon(1);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to(TurnEndPhase);
|
||||||
eiscue = game.scene.getParty()[1];
|
eiscue = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
expect(eiscue.formIndex).toBe(noiceForm);
|
expect(eiscue.formIndex).toBe(noiceForm);
|
||||||
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined();
|
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined();
|
||||||
|
@ -35,7 +35,7 @@ describe("Abilities - Mimicry", () => {
|
|||||||
game.override.enemyAbility(Abilities.MISTY_SURGE);
|
game.override.enemyAbility(Abilities.MISTY_SURGE);
|
||||||
await game.classicMode.startBattle([ Species.FEEBAS, Species.ABRA ]);
|
await game.classicMode.startBattle([ Species.FEEBAS, Species.ABRA ]);
|
||||||
|
|
||||||
const [ playerPokemon1, playerPokemon2 ] = game.scene.getParty();
|
const [ playerPokemon1, playerPokemon2 ] = game.scene.getPlayerParty();
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.toNextTurn();
|
await game.toNextTurn();
|
||||||
expect(playerPokemon1.getTypes().includes(Type.FAIRY)).toBe(true);
|
expect(playerPokemon1.getTypes().includes(Type.FAIRY)).toBe(true);
|
||||||
|
@ -51,7 +51,7 @@ describe("Abilities - Pastel Veil", () => {
|
|||||||
|
|
||||||
it("it heals the poisoned status condition of allies if user is sent out into battle", async () => {
|
it("it heals the poisoned status condition of allies if user is sent out into battle", async () => {
|
||||||
await game.startBattle([ Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA ]);
|
await game.startBattle([ Species.MAGIKARP, Species.FEEBAS, Species.GALAR_PONYTA ]);
|
||||||
const ponyta = game.scene.getParty()[2];
|
const ponyta = game.scene.getPlayerParty()[2];
|
||||||
const magikarp = game.scene.getPlayerField()[0];
|
const magikarp = game.scene.getPlayerField()[0];
|
||||||
ponyta.abilityIndex = 1;
|
ponyta.abilityIndex = 1;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ describe("Abilities - POWER CONSTRUCT", () => {
|
|||||||
|
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.ZYGARDE ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP, Species.ZYGARDE ]);
|
||||||
|
|
||||||
const zygarde = game.scene.getParty().find((p) => p.species.speciesId === Species.ZYGARDE);
|
const zygarde = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.ZYGARDE);
|
||||||
expect(zygarde).not.toBe(undefined);
|
expect(zygarde).not.toBe(undefined);
|
||||||
expect(zygarde!.formIndex).toBe(completeForm);
|
expect(zygarde!.formIndex).toBe(completeForm);
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ describe("Abilities - POWER CONSTRUCT", () => {
|
|||||||
|
|
||||||
await game.classicMode.startBattle([ Species.MAGIKARP, Species.ZYGARDE ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP, Species.ZYGARDE ]);
|
||||||
|
|
||||||
const zygarde = game.scene.getParty().find((p) => p.species.speciesId === Species.ZYGARDE);
|
const zygarde = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.ZYGARDE);
|
||||||
expect(zygarde).not.toBe(undefined);
|
expect(zygarde).not.toBe(undefined);
|
||||||
expect(zygarde!.formIndex).toBe(completeForm);
|
expect(zygarde!.formIndex).toBe(completeForm);
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ describe("Abilities - SCHOOLING", () => {
|
|||||||
|
|
||||||
await game.startBattle([ Species.MAGIKARP, Species.WISHIWASHI ]);
|
await game.startBattle([ Species.MAGIKARP, Species.WISHIWASHI ]);
|
||||||
|
|
||||||
const wishiwashi = game.scene.getParty().find((p) => p.species.speciesId === Species.WISHIWASHI)!;
|
const wishiwashi = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.WISHIWASHI)!;
|
||||||
expect(wishiwashi).not.toBe(undefined);
|
expect(wishiwashi).not.toBe(undefined);
|
||||||
expect(wishiwashi.formIndex).toBe(schoolForm);
|
expect(wishiwashi.formIndex).toBe(schoolForm);
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ describe("Abilities - Serene Grace", () => {
|
|||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerParty()[0].formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ describe("Abilities - Serene Grace", () => {
|
|||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
|
console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
expect(chance.value).toBe(30);
|
expect(chance.value).toBe(30);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
@ -70,7 +70,7 @@ describe("Abilities - Serene Grace", () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerParty()[0].formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ describe("Abilities - Serene Grace", () => {
|
|||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new Utils.IntegerHolder(move.chance);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
expect(chance.value).toBe(60);
|
expect(chance.value).toBe(60);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability";
|
import { applyAbAttrs, applyPostDefendAbAttrs, applyPreAttackAbAttrs, MoveEffectChanceMultiplierAbAttr, MovePowerBoostAbAttr, PostDefendTypeChangeAbAttr } from "#app/data/ability";
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import * as Utils from "#app/utils";
|
import { NumberHolder } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { allMoves } from "#app/data/move";
|
import { allMoves } from "#app/data/move";
|
||||||
|
|
||||||
|
|
||||||
describe("Abilities - Sheer Force", () => {
|
describe("Abilities - Sheer Force", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
let game: GameManager;
|
let game: GameManager;
|
||||||
@ -39,13 +38,10 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
it("Sheer Force", async () => {
|
it("Sheer Force", async () => {
|
||||||
const moveToUse = Moves.AIR_SLASH;
|
const moveToUse = Moves.AIR_SLASH;
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
game.override.ability(Abilities.SHEER_FORCE);
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
game.scene.getEnemyPokemon()!.stats[Stat.SPDEF] = 10000;
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -57,11 +53,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||||
|
|
||||||
//Verify the move is boosted and has no chance of secondary effects
|
//Verify the move is boosted and has no chance of secondary effects
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new NumberHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new NumberHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(0);
|
expect(chance.value).toBe(0);
|
||||||
expect(power.value).toBe(move.power * 5461 / 4096);
|
expect(power.value).toBe(move.power * 5461 / 4096);
|
||||||
@ -72,13 +68,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
it("Sheer Force with exceptions including binding moves", async () => {
|
it("Sheer Force with exceptions including binding moves", async () => {
|
||||||
const moveToUse = Moves.BIND;
|
const moveToUse = Moves.BIND;
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
game.override.ability(Abilities.SHEER_FORCE);
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
|
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -90,11 +84,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
expect(move.id).toBe(Moves.BIND);
|
expect(move.id).toBe(Moves.BIND);
|
||||||
|
|
||||||
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new NumberHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new NumberHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
expect(chance.value).toBe(-1);
|
||||||
expect(power.value).toBe(move.power);
|
expect(power.value).toBe(move.power);
|
||||||
@ -105,13 +99,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
it("Sheer Force with moves with no secondary effect", async () => {
|
it("Sheer Force with moves with no secondary effect", async () => {
|
||||||
const moveToUse = Moves.TACKLE;
|
const moveToUse = Moves.TACKLE;
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
game.override.ability(Abilities.SHEER_FORCE);
|
||||||
await game.startBattle([
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
|
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -123,11 +115,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
expect(move.id).toBe(Moves.TACKLE);
|
expect(move.id).toBe(Moves.TACKLE);
|
||||||
|
|
||||||
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
//Binding moves and other exceptions are not affected by Sheer Force and have a chance.value of -1
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new NumberHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new NumberHolder(move.chance);
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, false, power);
|
applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getFirstTarget()!, move, false, power);
|
||||||
|
|
||||||
expect(chance.value).toBe(-1);
|
expect(chance.value).toBe(-1);
|
||||||
expect(power.value).toBe(move.power);
|
expect(power.value).toBe(move.power);
|
||||||
@ -140,13 +132,11 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
game.override.enemyAbility(Abilities.COLOR_CHANGE);
|
game.override.enemyAbility(Abilities.COLOR_CHANGE);
|
||||||
game.override.startingHeldItems([{ name: "KINGS_ROCK", count: 1 }]);
|
game.override.startingHeldItems([{ name: "KINGS_ROCK", count: 1 }]);
|
||||||
game.override.ability(Abilities.SHEER_FORCE);
|
game.override.ability(Abilities.SHEER_FORCE);
|
||||||
await game.startBattle([
|
await game.startBattle([ Species.PIDGEOT ]);
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.DEF] = 10000;
|
game.scene.getEnemyPokemon()!.stats[Stat.DEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
@ -158,10 +148,10 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
expect(move.id).toBe(Moves.CRUSH_CLAW);
|
expect(move.id).toBe(Moves.CRUSH_CLAW);
|
||||||
|
|
||||||
//Disable color change due to being hit by Sheer Force
|
//Disable color change due to being hit by Sheer Force
|
||||||
const power = new Utils.IntegerHolder(move.power);
|
const power = new NumberHolder(move.power);
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new NumberHolder(move.chance);
|
||||||
const user = phase.getUserPokemon()!;
|
const user = phase.getUserPokemon()!;
|
||||||
const target = phase.getTarget()!;
|
const target = phase.getFirstTarget()!;
|
||||||
const opponentType = target.getTypes()[0];
|
const opponentType = target.getTypes()[0];
|
||||||
|
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, false, chance, move, target, false);
|
||||||
@ -186,7 +176,7 @@ describe("Abilities - Sheer Force", () => {
|
|||||||
Species.PIDGEOT
|
Species.PIDGEOT
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const pidgeot = game.scene.getParty()[0];
|
const pidgeot = game.scene.getPlayerParty()[0];
|
||||||
const onix = game.scene.getEnemyParty()[0];
|
const onix = game.scene.getEnemyParty()[0];
|
||||||
|
|
||||||
pidgeot.stats[Stat.DEF] = 10000;
|
pidgeot.stats[Stat.DEF] = 10000;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability";
|
import { applyAbAttrs, applyPreDefendAbAttrs, IgnoreMoveEffectsAbAttr, MoveEffectChanceMultiplierAbAttr } from "#app/data/ability";
|
||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||||
import * as Utils from "#app/utils";
|
import { NumberHolder } from "#app/utils";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
@ -27,26 +27,22 @@ describe("Abilities - Shield Dust", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
const movesToUse = [ Moves.AIR_SLASH ];
|
|
||||||
game.override.battleType("single");
|
game.override.battleType("single");
|
||||||
game.override.enemySpecies(Species.ONIX);
|
game.override.enemySpecies(Species.ONIX);
|
||||||
game.override.enemyAbility(Abilities.SHIELD_DUST);
|
game.override.enemyAbility(Abilities.SHIELD_DUST);
|
||||||
game.override.startingLevel(100);
|
game.override.startingLevel(100);
|
||||||
game.override.moveset(movesToUse);
|
game.override.moveset(Moves.AIR_SLASH);
|
||||||
game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]);
|
game.override.enemyMoveset(Moves.TACKLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Shield Dust", async () => {
|
it("Shield Dust", async () => {
|
||||||
const moveToUse = Moves.AIR_SLASH;
|
await game.classicMode.startBattle([ Species.PIDGEOT ]);
|
||||||
await game.startBattle([
|
|
||||||
Species.PIDGEOT
|
|
||||||
]);
|
|
||||||
|
|
||||||
|
|
||||||
game.scene.getEnemyParty()[0].stats[Stat.SPDEF] = 10000;
|
game.scene.getEnemyPokemon()!.stats[Stat.SPDEF] = 10000;
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
expect(game.scene.getPlayerPokemon()!.formIndex).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(Moves.AIR_SLASH);
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]);
|
||||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||||
@ -56,9 +52,9 @@ describe("Abilities - Shield Dust", () => {
|
|||||||
const move = phase.move.getMove();
|
const move = phase.move.getMove();
|
||||||
expect(move.id).toBe(Moves.AIR_SLASH);
|
expect(move.id).toBe(Moves.AIR_SLASH);
|
||||||
|
|
||||||
const chance = new Utils.IntegerHolder(move.chance);
|
const chance = new NumberHolder(move.chance);
|
||||||
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getTarget(), false);
|
applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, false, chance, move, phase.getFirstTarget(), false);
|
||||||
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null, null, false, chance);
|
applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getFirstTarget()!, phase.getUserPokemon()!, null, null, false, chance);
|
||||||
expect(chance.value).toBe(0);
|
expect(chance.value).toBe(0);
|
||||||
|
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
@ -43,7 +43,7 @@ describe("Abilities - SHIELDS DOWN", () => {
|
|||||||
|
|
||||||
await game.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
|
await game.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
|
||||||
|
|
||||||
const minior = game.scene.getParty().find((p) => p.species.speciesId === Species.MINIOR)!;
|
const minior = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.MINIOR)!;
|
||||||
expect(minior).not.toBe(undefined);
|
expect(minior).not.toBe(undefined);
|
||||||
expect(minior.formIndex).toBe(coreForm);
|
expect(minior.formIndex).toBe(coreForm);
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { StatusEffect } from "#app/data/status-effect";
|
import { StatusEffect } from "#app/data/status-effect";
|
||||||
import GameManager from "#app/test/utils/gameManager";
|
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ describe("Abilities - Synchronize", () => {
|
|||||||
.enemyAbility(Abilities.SYNCHRONIZE)
|
.enemyAbility(Abilities.SYNCHRONIZE)
|
||||||
.moveset([ Moves.SPLASH, Moves.THUNDER_WAVE, Moves.SPORE, Moves.PSYCHO_SHIFT ])
|
.moveset([ Moves.SPLASH, Moves.THUNDER_WAVE, Moves.SPORE, Moves.PSYCHO_SHIFT ])
|
||||||
.ability(Abilities.NO_GUARD);
|
.ability(Abilities.NO_GUARD);
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("does not trigger when no status is applied by opponent Pokemon", async () => {
|
it("does not trigger when no status is applied by opponent Pokemon", async () => {
|
||||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
@ -38,9 +38,9 @@ describe("Abilities - Synchronize", () => {
|
|||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status).toBeUndefined();
|
expect(game.scene.getPlayerPokemon()!.status).toBeUndefined();
|
||||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("sets the status of the source pokemon to Paralysis when paralyzed by it", async () => {
|
it("sets the status of the source pokemon to Paralysis when paralyzed by it", async () => {
|
||||||
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
await game.classicMode.startBattle([ Species.FEEBAS ]);
|
||||||
@ -48,10 +48,10 @@ describe("Abilities - Synchronize", () => {
|
|||||||
game.move.select(Moves.THUNDER_WAVE);
|
game.move.select(Moves.THUNDER_WAVE);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("does not trigger on Sleep", async () => {
|
it("does not trigger on Sleep", async () => {
|
||||||
await game.classicMode.startBattle();
|
await game.classicMode.startBattle();
|
||||||
@ -60,10 +60,10 @@ describe("Abilities - Synchronize", () => {
|
|||||||
|
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status?.effect).toBeUndefined();
|
expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined();
|
||||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.SLEEP);
|
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
|
||||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("does not trigger when Pokemon is statused by Toxic Spikes", async () => {
|
it("does not trigger when Pokemon is statused by Toxic Spikes", async () => {
|
||||||
game.override
|
game.override
|
||||||
@ -79,10 +79,10 @@ describe("Abilities - Synchronize", () => {
|
|||||||
game.doSwitchPokemon(1);
|
game.doSwitchPokemon(1);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.POISON);
|
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON);
|
||||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBeUndefined();
|
expect(game.scene.getEnemyPokemon()!.status?.effect).toBeUndefined();
|
||||||
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).not.toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("shows ability even if it fails to set the status of the opponent Pokemon", async () => {
|
it("shows ability even if it fails to set the status of the opponent Pokemon", async () => {
|
||||||
await game.classicMode.startBattle([ Species.PIKACHU ]);
|
await game.classicMode.startBattle([ Species.PIKACHU ]);
|
||||||
@ -90,10 +90,10 @@ describe("Abilities - Synchronize", () => {
|
|||||||
game.move.select(Moves.THUNDER_WAVE);
|
game.move.select(Moves.THUNDER_WAVE);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status?.effect).toBeUndefined();
|
expect(game.scene.getPlayerPokemon()!.status?.effect).toBeUndefined();
|
||||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
|
|
||||||
it("should activate with Psycho Shift after the move clears the status", async () => {
|
it("should activate with Psycho Shift after the move clears the status", async () => {
|
||||||
game.override.statusEffect(StatusEffect.PARALYSIS);
|
game.override.statusEffect(StatusEffect.PARALYSIS);
|
||||||
@ -102,8 +102,8 @@ describe("Abilities - Synchronize", () => {
|
|||||||
game.move.select(Moves.PSYCHO_SHIFT);
|
game.move.select(Moves.PSYCHO_SHIFT);
|
||||||
await game.phaseInterceptor.to("BerryPhase");
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
expect(game.scene.getParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS); // keeping old gen < V impl for now since it's buggy otherwise
|
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS); // keeping old gen < V impl for now since it's buggy otherwise
|
||||||
expect(game.scene.getEnemyParty()[0].status?.effect).toBe(StatusEffect.PARALYSIS);
|
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.PARALYSIS);
|
||||||
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
expect(game.phaseInterceptor.log).toContain("ShowAbilityPhase");
|
||||||
}, 20000);
|
});
|
||||||
});
|
});
|
||||||
|
245
src/test/abilities/unburden.test.ts
Normal file
245
src/test/abilities/unburden.test.ts
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
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";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { BerryType } from "#app/enums/berry-type";
|
||||||
|
import { allMoves, StealHeldItemChanceAttr } from "#app/data/move";
|
||||||
|
|
||||||
|
|
||||||
|
describe("Abilities - Unburden", () => {
|
||||||
|
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
|
||||||
|
.battleType("single")
|
||||||
|
.starterSpecies(Species.TREECKO)
|
||||||
|
.startingLevel(1)
|
||||||
|
.moveset([ Moves.POPULATION_BOMB, Moves.KNOCK_OFF, Moves.PLUCK, Moves.THIEF ])
|
||||||
|
.startingHeldItems([
|
||||||
|
{ name: "BERRY", count: 1, type: BerryType.SITRUS },
|
||||||
|
{ name: "BERRY", count: 2, type: BerryType.APICOT },
|
||||||
|
{ name: "BERRY", count: 2, type: BerryType.LUM },
|
||||||
|
])
|
||||||
|
.enemySpecies(Species.NINJASK)
|
||||||
|
.enemyLevel(100)
|
||||||
|
.enemyMoveset([ Moves.FALSE_SWIPE ])
|
||||||
|
.enemyAbility(Abilities.UNBURDEN)
|
||||||
|
.enemyPassiveAbility(Abilities.NO_GUARD)
|
||||||
|
.enemyHeldItems([
|
||||||
|
{ name: "BERRY", type: BerryType.SITRUS, count: 1 },
|
||||||
|
{ name: "BERRY", type: BerryType.LUM, count: 1 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when a berry is eaten", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
playerPokemon.abilityIndex = 2;
|
||||||
|
const playerHeldItems = playerPokemon.getHeldItems().length;
|
||||||
|
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.FALSE_SWIPE);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(playerPokemon.getHeldItems().length).toBeLessThan(playerHeldItems);
|
||||||
|
expect(playerPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialPlayerSpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when a berry is stolen", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.PLUCK);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when an item is knocked off", async () => {
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.KNOCK_OFF);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when an item is stolen via attacking ability", async () => {
|
||||||
|
game.override
|
||||||
|
.ability(Abilities.MAGICIAN)
|
||||||
|
.startingHeldItems([
|
||||||
|
{ name: "MULTI_LENS", count: 3 },
|
||||||
|
]);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.POPULATION_BOMB);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when an item is stolen via defending ability", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(45)
|
||||||
|
.enemyAbility(Abilities.PICKPOCKET)
|
||||||
|
.startingHeldItems([
|
||||||
|
{ name: "MULTI_LENS", count: 3 },
|
||||||
|
{ name: "SOUL_DEW", count: 1 },
|
||||||
|
{ name: "LUCKY_EGG", count: 1 },
|
||||||
|
]);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
playerPokemon.abilityIndex = 2;
|
||||||
|
const playerHeldItems = playerPokemon.getHeldItems().length;
|
||||||
|
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.POPULATION_BOMB);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(playerPokemon.getHeldItems().length).toBeLessThan(playerHeldItems);
|
||||||
|
expect(playerPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialPlayerSpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when an item is stolen via move", async () => {
|
||||||
|
vi.spyOn(allMoves[Moves.THIEF], "attrs", "get").mockReturnValue([ new StealHeldItemChanceAttr(1.0) ]); // give Thief 100% steal rate
|
||||||
|
game.override.startingHeldItems([
|
||||||
|
{ name: "MULTI_LENS", count: 3 },
|
||||||
|
]);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.THIEF);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when an item is stolen via grip claw", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(5)
|
||||||
|
.startingHeldItems([
|
||||||
|
{ name: "GRIP_CLAW", count: 5 },
|
||||||
|
{ name: "MULTI_LENS", count: 3 },
|
||||||
|
])
|
||||||
|
.enemyHeldItems([
|
||||||
|
{ name: "SOUL_DEW", count: 1 },
|
||||||
|
{ name: "LUCKY_EGG", count: 1 },
|
||||||
|
{ name: "LEFTOVERS", count: 1 },
|
||||||
|
{ name: "GRIP_CLAW", count: 1 },
|
||||||
|
{ name: "MULTI_LENS", count: 1 },
|
||||||
|
{ name: "BERRY", type: BerryType.SITRUS, count: 1 },
|
||||||
|
{ name: "BERRY", type: BerryType.LUM, count: 1 },
|
||||||
|
]);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
while (enemyPokemon.getHeldItems().length === enemyHeldItemCt) {
|
||||||
|
game.move.select(Moves.POPULATION_BOMB);
|
||||||
|
await game.toNextTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not activate when a neutralizing ability is present", async () => {
|
||||||
|
game.override.enemyAbility(Abilities.NEUTRALIZING_GAS);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
const playerHeldItems = playerPokemon.getHeldItems().length;
|
||||||
|
const initialPlayerSpeed = playerPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.FALSE_SWIPE);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(playerPokemon.getHeldItems().length).toBeLessThan(playerHeldItems);
|
||||||
|
expect(playerPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialPlayerSpeed);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should activate when a move that consumes a berry is used", async () => {
|
||||||
|
game.override.enemyMoveset([ Moves.STUFF_CHEEKS ]);
|
||||||
|
await game.classicMode.startBattle();
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const enemyHeldItemCt = enemyPokemon.getHeldItems().length;
|
||||||
|
const initialEnemySpeed = enemyPokemon.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.STUFF_CHEEKS);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
expect(enemyPokemon.getHeldItems().length).toBeLessThan(enemyHeldItemCt);
|
||||||
|
expect(enemyPokemon.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialEnemySpeed * 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deactivate when a neutralizing gas user enters the field", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.moveset([ Moves.SPLASH ]);
|
||||||
|
await game.classicMode.startBattle([ Species.TREECKO, Species.MEOWTH, Species.WEEZING ]);
|
||||||
|
|
||||||
|
const playerPokemon = game.scene.getPlayerParty();
|
||||||
|
const treecko = playerPokemon[0];
|
||||||
|
const weezing = playerPokemon[2];
|
||||||
|
treecko.abilityIndex = 2;
|
||||||
|
weezing.abilityIndex = 1;
|
||||||
|
const playerHeldItems = treecko.getHeldItems().length;
|
||||||
|
const initialPlayerSpeed = treecko.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.forceEnemyMove(Moves.FALSE_SWIPE, 0);
|
||||||
|
await game.forceEnemyMove(Moves.FALSE_SWIPE, 0);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(treecko.getHeldItems().length).toBeLessThan(playerHeldItems);
|
||||||
|
expect(treecko.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialPlayerSpeed * 2);
|
||||||
|
|
||||||
|
await game.toNextTurn();
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSwitchPokemon(2);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(treecko.getHeldItems().length).toBeLessThan(playerHeldItems);
|
||||||
|
expect(treecko.getEffectiveStat(Stat.SPD)).toBeCloseTo(initialPlayerSpeed);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
614
src/test/abilities/wimp_out.test.ts
Normal file
614
src/test/abilities/wimp_out.test.ts
Normal file
@ -0,0 +1,614 @@
|
|||||||
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { ArenaTagSide } from "#app/data/arena-tag";
|
||||||
|
import { allMoves } from "#app/data/move";
|
||||||
|
import GameManager from "#app/test/utils/gameManager";
|
||||||
|
import { Abilities } from "#enums/abilities";
|
||||||
|
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||||
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { WeatherType } from "#enums/weather-type";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Abilities - Wimp Out", () => {
|
||||||
|
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
|
||||||
|
.battleType("single")
|
||||||
|
.ability(Abilities.WIMP_OUT)
|
||||||
|
.enemySpecies(Species.NINJASK)
|
||||||
|
.enemyPassiveAbility(Abilities.NO_GUARD)
|
||||||
|
.startingLevel(90)
|
||||||
|
.enemyLevel(70)
|
||||||
|
.moveset([ Moves.SPLASH, Moves.FALSE_SWIPE, Moves.ENDURE ])
|
||||||
|
.enemyMoveset(Moves.FALSE_SWIPE)
|
||||||
|
.disableCrits();
|
||||||
|
});
|
||||||
|
|
||||||
|
function confirmSwitch(): void {
|
||||||
|
const [ pokemon1, pokemon2 ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
|
|
||||||
|
expect(pokemon1.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
|
|
||||||
|
expect(pokemon2.species.speciesId).toBe(Species.WIMPOD);
|
||||||
|
expect(pokemon2.isFainted()).toBe(false);
|
||||||
|
expect(pokemon2.getHpRatio()).toBeLessThan(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmNoSwitch(): void {
|
||||||
|
const [ pokemon1, pokemon2 ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
|
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||||
|
|
||||||
|
expect(pokemon2.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
|
|
||||||
|
expect(pokemon1.species.speciesId).toBe(Species.WIMPOD);
|
||||||
|
expect(pokemon1.isFainted()).toBe(false);
|
||||||
|
expect(pokemon1.getHpRatio()).toBeLessThan(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("triggers regenerator passive single time when switching out with wimp out", async () => {
|
||||||
|
game.override
|
||||||
|
.passiveAbility(Abilities.REGENERATOR)
|
||||||
|
.startingLevel(5)
|
||||||
|
.enemyLevel(100);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(wimpod.hp).toEqual(Math.floor(wimpod.getMaxHp() * 0.33 + 1));
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("It makes wild pokemon flee if triggered", async () => {
|
||||||
|
game.override.enemyAbility(Abilities.WIMP_OUT);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.GOLISOPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
enemyPokemon.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.FALSE_SWIPE);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
const isVisible = enemyPokemon.visible;
|
||||||
|
const hasFled = enemyPokemon.switchOutStatus;
|
||||||
|
expect(!isVisible && hasFled).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not trigger when HP already below half", async () => {
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
|
wimpod.hp = 5;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(wimpod.hp).toEqual(1);
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Trapping moves do not prevent Wimp Out from activating.", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset([ Moves.SPIRIT_SHACKLE ])
|
||||||
|
.startingLevel(53)
|
||||||
|
.enemyLevel(45);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
|
expect(game.scene.getPlayerPokemon()!.getTag(BattlerTagType.TRAPPED)).toBeUndefined();
|
||||||
|
expect(game.scene.getPlayerParty()[1].getTag(BattlerTagType.TRAPPED)).toBeUndefined();
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("If this Ability activates due to being hit by U-turn or Volt Switch, the user of that move will not be switched out.", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(95)
|
||||||
|
.enemyMoveset([ Moves.U_TURN ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
const hasFled = enemyPokemon.switchOutStatus;
|
||||||
|
expect(hasFled).toBe(false);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("If this Ability does not activate due to being hit by U-turn or Volt Switch, the user of that move will be switched out.", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(190)
|
||||||
|
.startingWave(8)
|
||||||
|
.enemyMoveset([ Moves.U_TURN ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.GOLISOPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
const RIVAL_NINJASK1 = game.scene.getEnemyPokemon()?.id;
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase", false);
|
||||||
|
expect(game.scene.getEnemyPokemon()?.id !== RIVAL_NINJASK1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Dragon Tail and Circle Throw switch out Pokémon before the Ability activates.", async () => {
|
||||||
|
game.override
|
||||||
|
.startingLevel(69)
|
||||||
|
.enemyMoveset([ Moves.DRAGON_TAIL ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("SwitchSummonPhase", false);
|
||||||
|
|
||||||
|
expect(wimpod.summonData.abilitiesApplied).not.toContain(Abilities.WIMP_OUT);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).not.toBe(Species.WIMPOD);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers when recoil damage is taken", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.HEAD_SMASH ])
|
||||||
|
.enemyMoveset([ Moves.SPLASH ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.HEAD_SMASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("It does not activate when the Pokémon cuts its own HP", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SUBSTITUTE ])
|
||||||
|
.enemyMoveset([ Moves.SPLASH ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
const wimpod = game.scene.getPlayerPokemon()!;
|
||||||
|
wimpod.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.SUBSTITUTE);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not trigger when neutralized", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyAbility(Abilities.NEUTRALIZING_GAS)
|
||||||
|
.startingLevel(5);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("If it falls below half and recovers back above half from a Shell Bell, Wimp Out will activate even after the Shell Bell recovery", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.DOUBLE_EDGE ])
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.startingHeldItems([
|
||||||
|
{ name: "SHELL_BELL", count: 3 },
|
||||||
|
{ name: "HEALING_CHARM", count: 5 },
|
||||||
|
]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.75;
|
||||||
|
|
||||||
|
game.move.select(Moves.DOUBLE_EDGE);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()[1].getHpRatio()).toBeGreaterThan(0.5);
|
||||||
|
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
|
||||||
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.TYRUNT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to weather damage", async () => {
|
||||||
|
game.override
|
||||||
|
.weather(WeatherType.HAIL)
|
||||||
|
.enemyMoveset([ Moves.SPLASH ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Does not trigger when enemy has sheer force", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyAbility(Abilities.SHEER_FORCE)
|
||||||
|
.enemyMoveset(Moves.SLUDGE_BOMB)
|
||||||
|
.startingLevel(95);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to post turn status damage", async () => {
|
||||||
|
game.override
|
||||||
|
.statusEffect(StatusEffect.POISON)
|
||||||
|
.enemyMoveset([ Moves.SPLASH ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to bad dreams", async () => {
|
||||||
|
game.override
|
||||||
|
.statusEffect(StatusEffect.SLEEP)
|
||||||
|
.enemyAbility(Abilities.BAD_DREAMS);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to leech seed", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset([ Moves.LEECH_SEED ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to curse damage", async () => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.DUSKNOIR)
|
||||||
|
.enemyMoveset([ Moves.CURSE ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.52;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to salt cure damage", async () => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.NACLI)
|
||||||
|
.enemyMoveset([ Moves.SALT_CURE ])
|
||||||
|
.enemyLevel(1);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.70;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to damaging trap damage", async () => {
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyMoveset([ Moves.WHIRLPOOL ])
|
||||||
|
.enemyLevel(1);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.55;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Magic Guard passive should not allow indirect damage to trigger Wimp Out", async () => {
|
||||||
|
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
||||||
|
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||||
|
game.override
|
||||||
|
.passiveAbility(Abilities.MAGIC_GUARD)
|
||||||
|
.enemyMoveset([ Moves.LEECH_SEED ])
|
||||||
|
.weather(WeatherType.HAIL)
|
||||||
|
.statusEffect(StatusEffect.POISON);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()[0].getHpRatio()).toEqual(0.51);
|
||||||
|
expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase");
|
||||||
|
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.WIMPOD);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out activating should not cancel a double battle", async () => {
|
||||||
|
game.override
|
||||||
|
.battleType("double")
|
||||||
|
.enemyAbility(Abilities.WIMP_OUT)
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.enemyLevel(1);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
const enemyLeadPokemon = game.scene.getEnemyParty()[0];
|
||||||
|
const enemySecPokemon = game.scene.getEnemyParty()[1];
|
||||||
|
|
||||||
|
game.move.select(Moves.FALSE_SWIPE, 0, BattlerIndex.ENEMY);
|
||||||
|
game.move.select(Moves.SPLASH, 1);
|
||||||
|
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
const isVisibleLead = enemyLeadPokemon.visible;
|
||||||
|
const hasFledLead = enemyLeadPokemon.switchOutStatus;
|
||||||
|
const isVisibleSec = enemySecPokemon.visible;
|
||||||
|
const hasFledSec = enemySecPokemon.switchOutStatus;
|
||||||
|
expect(!isVisibleLead && hasFledLead && isVisibleSec && !hasFledSec).toBe(true);
|
||||||
|
expect(enemyLeadPokemon.hp).toBeLessThan(enemyLeadPokemon.getMaxHp());
|
||||||
|
expect(enemySecPokemon.hp).toEqual(enemySecPokemon.getMaxHp());
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to aftermath", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.THUNDER_PUNCH ])
|
||||||
|
.enemySpecies(Species.MAGIKARP)
|
||||||
|
.enemyAbility(Abilities.AFTERMATH)
|
||||||
|
.enemyMoveset([ Moves.SPLASH ])
|
||||||
|
.enemyLevel(1);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.THUNDER_PUNCH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Activates due to entry hazards", async () => {
|
||||||
|
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0, ArenaTagSide.ENEMY);
|
||||||
|
game.scene.arena.addTag(ArenaTagType.SPIKES, 1, Moves.SPIKES, 0, ArenaTagSide.ENEMY);
|
||||||
|
game.override
|
||||||
|
.enemySpecies(Species.CENTISKORCH)
|
||||||
|
.enemyAbility(Abilities.WIMP_OUT)
|
||||||
|
.startingWave(4);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(game.phaseInterceptor.log).not.toContain("MovePhase");
|
||||||
|
expect(game.phaseInterceptor.log).toContain("BattleEndPhase");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Wimp Out will activate due to Nightmare", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset([ Moves.NIGHTMARE ])
|
||||||
|
.statusEffect(StatusEffect.SLEEP);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.65;
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toNextTurn();
|
||||||
|
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers status on the wimp out user before a new pokemon is switched in", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset(Moves.SLUDGE_BOMB)
|
||||||
|
.startingLevel(80);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
vi.spyOn(allMoves[Moves.SLUDGE_BOMB], "chance", "get").mockReturnValue(100);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
expect(game.scene.getPlayerParty()[1].status?.effect).toEqual(StatusEffect.POISON);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers after last hit of multi hit move", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset(Moves.BULLET_SEED)
|
||||||
|
.enemyAbility(Abilities.SKILL_LINK);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.ENDURE);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
|
||||||
|
expect(enemyPokemon.turnData.hitCount).toBe(5);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers after last hit of multi hit move (multi lens)", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset(Moves.TACKLE)
|
||||||
|
.enemyHeldItems([{ name: "MULTI_LENS", count: 1 }]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.ENDURE);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
|
||||||
|
expect(enemyPokemon.turnData.hitCount).toBe(2);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
it("triggers after last hit of Parental Bond", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyMoveset(Moves.TACKLE)
|
||||||
|
.enemyAbility(Abilities.PARENTAL_BOND);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
|
||||||
|
game.scene.getPlayerPokemon()!.hp *= 0.51;
|
||||||
|
|
||||||
|
game.move.select(Moves.ENDURE);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
|
const enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||||
|
expect(enemyPokemon.turnData.hitsLeft).toBe(0);
|
||||||
|
expect(enemyPokemon.turnData.hitCount).toBe(2);
|
||||||
|
confirmSwitch();
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: This interaction is not implemented yet
|
||||||
|
it.todo("Wimp Out will not activate if the Pokémon's HP falls below half due to hurting itself in confusion", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset([ Moves.SWORDS_DANCE ])
|
||||||
|
.enemyMoveset([ Moves.SWAGGER ]);
|
||||||
|
await game.classicMode.startBattle([
|
||||||
|
Species.WIMPOD,
|
||||||
|
Species.TYRUNT
|
||||||
|
]);
|
||||||
|
const playerPokemon = game.scene.getPlayerPokemon()!;
|
||||||
|
playerPokemon.hp *= 0.51;
|
||||||
|
playerPokemon.setStatStage(Stat.ATK, 6);
|
||||||
|
playerPokemon.addTag(BattlerTagType.CONFUSED);
|
||||||
|
|
||||||
|
// TODO: add helper function to force confusion self-hits
|
||||||
|
|
||||||
|
while (playerPokemon.getHpRatio() > 0.49) {
|
||||||
|
game.move.select(Moves.SWORDS_DANCE);
|
||||||
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmNoSwitch();
|
||||||
|
});
|
||||||
|
});
|
@ -1,29 +1,23 @@
|
|||||||
import { Stat } from "#enums/stat";
|
|
||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
|
import { Status, StatusEffect } from "#app/data/status-effect";
|
||||||
import { DamagePhase } from "#app/phases/damage-phase";
|
import { DamagePhase } from "#app/phases/damage-phase";
|
||||||
import { EnemyCommandPhase } from "#app/phases/enemy-command-phase";
|
|
||||||
import { MessagePhase } from "#app/phases/message-phase";
|
|
||||||
import { PostSummonPhase } from "#app/phases/post-summon-phase";
|
|
||||||
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
|
|
||||||
import { SwitchPhase } from "#app/phases/switch-phase";
|
|
||||||
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
import { SwitchSummonPhase } from "#app/phases/switch-summon-phase";
|
||||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
|
||||||
import { TurnInitPhase } from "#app/phases/turn-init-phase";
|
|
||||||
import { TurnStartPhase } from "#app/phases/turn-start-phase";
|
|
||||||
import { Mode } from "#app/ui/ui";
|
import { Mode } from "#app/ui/ui";
|
||||||
import { Abilities } from "#enums/abilities";
|
import { Abilities } from "#enums/abilities";
|
||||||
import { Moves } from "#enums/moves";
|
import { Moves } from "#enums/moves";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import { SwitchType } from "#enums/switch-type";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
import { Status, StatusEffect } from "#app/data/status-effect";
|
|
||||||
import { SwitchType } from "#enums/switch-type";
|
|
||||||
|
|
||||||
|
|
||||||
describe("Abilities - ZEN MODE", () => {
|
describe("Abilities - ZEN MODE", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
let game: GameManager;
|
let game: GameManager;
|
||||||
|
const baseForm = 0;
|
||||||
|
const zenForm = 1;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
phaserGame = new Phaser.Game({
|
phaserGame = new Phaser.Game({
|
||||||
@ -37,81 +31,71 @@ describe("Abilities - ZEN MODE", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
const moveToUse = Moves.SPLASH;
|
game.override
|
||||||
game.override.battleType("single");
|
.battleType("single")
|
||||||
game.override.enemySpecies(Species.RATTATA);
|
.enemySpecies(Species.RATTATA)
|
||||||
game.override.enemyAbility(Abilities.HYDRATION);
|
.enemyAbility(Abilities.HYDRATION)
|
||||||
game.override.ability(Abilities.ZEN_MODE);
|
.ability(Abilities.ZEN_MODE)
|
||||||
game.override.startingLevel(100);
|
.startingLevel(100)
|
||||||
game.override.moveset([ moveToUse ]);
|
.moveset(Moves.SPLASH)
|
||||||
game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]);
|
.enemyMoveset(Moves.TACKLE);
|
||||||
});
|
});
|
||||||
|
|
||||||
test(
|
it("shouldn't change form when taking damage if not dropping below 50% HP", async () => {
|
||||||
"not enough damage to change form",
|
await game.classicMode.startBattle([ Species.DARMANITAN ]);
|
||||||
async () => {
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
const moveToUse = Moves.SPLASH;
|
player.stats[Stat.HP] = 100;
|
||||||
await game.startBattle([ Species.DARMANITAN ]);
|
player.hp = 100;
|
||||||
game.scene.getParty()[0].stats[Stat.HP] = 100;
|
expect(player.formIndex).toBe(baseForm);
|
||||||
game.scene.getParty()[0].hp = 100;
|
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(Moves.SPLASH);
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
|
||||||
|
expect(player.hp).toBeLessThan(100);
|
||||||
|
expect(player.formIndex).toBe(baseForm);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should change form when falling below 50% HP", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.DARMANITAN ]);
|
||||||
|
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
player.stats[Stat.HP] = 1000;
|
||||||
|
player.hp = 100;
|
||||||
|
expect(player.formIndex).toBe(baseForm);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
|
||||||
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
|
await game.phaseInterceptor.to("QuietFormChangePhase");
|
||||||
|
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||||
|
|
||||||
|
expect(player.hp).not.toBe(100);
|
||||||
|
expect(player.formIndex).toBe(zenForm);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should stay zen mode when fainted", async () => {
|
||||||
|
await game.classicMode.startBattle([ Species.DARMANITAN, Species.CHARIZARD ]);
|
||||||
|
const player = game.scene.getPlayerPokemon()!;
|
||||||
|
player.stats[Stat.HP] = 1000;
|
||||||
|
player.hp = 100;
|
||||||
|
expect(player.formIndex).toBe(baseForm);
|
||||||
|
|
||||||
|
game.move.select(Moves.SPLASH);
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
||||||
await game.phaseInterceptor.to(DamagePhase, false);
|
await game.phaseInterceptor.to(DamagePhase, false);
|
||||||
// await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false);
|
|
||||||
const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
|
|
||||||
damagePhase.updateAmount(40);
|
|
||||||
await game.phaseInterceptor.runFrom(DamagePhase).to(TurnEndPhase, false);
|
|
||||||
expect(game.scene.getParty()[0].hp).toBeLessThan(100);
|
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test(
|
|
||||||
"enough damage to change form",
|
|
||||||
async () => {
|
|
||||||
const moveToUse = Moves.SPLASH;
|
|
||||||
await game.startBattle([ Species.DARMANITAN ]);
|
|
||||||
game.scene.getParty()[0].stats[Stat.HP] = 1000;
|
|
||||||
game.scene.getParty()[0].hp = 100;
|
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
|
||||||
await game.phaseInterceptor.to(QuietFormChangePhase);
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
|
||||||
expect(game.scene.getParty()[0].hp).not.toBe(100);
|
|
||||||
expect(game.scene.getParty()[0].formIndex).not.toBe(0);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test(
|
|
||||||
"kill pokemon while on zen mode",
|
|
||||||
async () => {
|
|
||||||
const moveToUse = Moves.SPLASH;
|
|
||||||
await game.startBattle([ Species.DARMANITAN, Species.CHARIZARD ]);
|
|
||||||
game.scene.getParty()[0].stats[Stat.HP] = 1000;
|
|
||||||
game.scene.getParty()[0].hp = 100;
|
|
||||||
expect(game.scene.getParty()[0].formIndex).toBe(0);
|
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
|
||||||
|
|
||||||
await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]);
|
|
||||||
await game.phaseInterceptor.to(DamagePhase, false);
|
|
||||||
// await game.phaseInterceptor.runFrom(DamagePhase).to(DamagePhase, false);
|
|
||||||
const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
|
const damagePhase = game.scene.getCurrentPhase() as DamagePhase;
|
||||||
damagePhase.updateAmount(80);
|
damagePhase.updateAmount(80);
|
||||||
await game.phaseInterceptor.runFrom(DamagePhase).to(QuietFormChangePhase);
|
await game.phaseInterceptor.to("QuietFormChangePhase");
|
||||||
expect(game.scene.getParty()[0].hp).not.toBe(100);
|
|
||||||
expect(game.scene.getParty()[0].formIndex).not.toBe(0);
|
expect(player.hp).not.toBe(100);
|
||||||
await game.killPokemon(game.scene.getParty()[0]);
|
expect(player.formIndex).toBe(zenForm);
|
||||||
expect(game.scene.getParty()[0].isFainted()).toBe(true);
|
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
await game.killPokemon(player);
|
||||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
expect(player.isFainted()).toBe(true);
|
||||||
await game.phaseInterceptor.run(TurnStartPhase);
|
|
||||||
|
await game.phaseInterceptor.to("TurnStartPhase");
|
||||||
game.onNextPrompt("SwitchPhase", Mode.PARTY, () => {
|
game.onNextPrompt("SwitchPhase", Mode.PARTY, () => {
|
||||||
game.scene.unshiftPhase(new SwitchSummonPhase(game.scene, SwitchType.SWITCH, 0, 1, false));
|
game.scene.unshiftPhase(new SwitchSummonPhase(game.scene, SwitchType.SWITCH, 0, 1, false));
|
||||||
game.scene.ui.setMode(Mode.MESSAGE);
|
game.scene.ui.setMode(Mode.MESSAGE);
|
||||||
@ -119,26 +103,20 @@ describe("Abilities - ZEN MODE", () => {
|
|||||||
game.onNextPrompt("SwitchPhase", Mode.MESSAGE, () => {
|
game.onNextPrompt("SwitchPhase", Mode.MESSAGE, () => {
|
||||||
game.endPhase();
|
game.endPhase();
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SwitchPhase);
|
await game.phaseInterceptor.to("PostSummonPhase");
|
||||||
await game.phaseInterceptor.to(PostSummonPhase);
|
|
||||||
expect(game.scene.getParty()[1].formIndex).toBe(1);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test(
|
expect(game.scene.getPlayerParty()[1].formIndex).toBe(zenForm);
|
||||||
"check if fainted pokemon switches to base form on arena reset",
|
});
|
||||||
async () => {
|
|
||||||
const baseForm = 0,
|
it("should switch to base form on arena reset", async () => {
|
||||||
zenForm = 1;
|
|
||||||
game.override.startingWave(4);
|
game.override.startingWave(4);
|
||||||
game.override.starterForms({
|
game.override.starterForms({
|
||||||
[Species.DARMANITAN]: zenForm,
|
[Species.DARMANITAN]: zenForm,
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.startBattle([ Species.MAGIKARP, Species.DARMANITAN ]);
|
await game.classicMode.startBattle([ Species.MAGIKARP, Species.DARMANITAN ]);
|
||||||
|
|
||||||
const darmanitan = game.scene.getParty().find((p) => p.species.speciesId === Species.DARMANITAN)!;
|
const darmanitan = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.DARMANITAN)!;
|
||||||
expect(darmanitan).not.toBe(undefined);
|
|
||||||
expect(darmanitan.formIndex).toBe(zenForm);
|
expect(darmanitan.formIndex).toBe(zenForm);
|
||||||
|
|
||||||
darmanitan.hp = 0;
|
darmanitan.hp = 0;
|
||||||
@ -147,11 +125,10 @@ describe("Abilities - ZEN MODE", () => {
|
|||||||
|
|
||||||
game.move.select(Moves.SPLASH);
|
game.move.select(Moves.SPLASH);
|
||||||
await game.doKillOpponents();
|
await game.doKillOpponents();
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
game.doSelectModifier();
|
game.doSelectModifier();
|
||||||
await game.phaseInterceptor.to(QuietFormChangePhase);
|
await game.phaseInterceptor.to("QuietFormChangePhase");
|
||||||
|
|
||||||
expect(darmanitan.formIndex).toBe(baseForm);
|
expect(darmanitan.formIndex).toBe(baseForm);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
@ -41,8 +41,8 @@ describe("Abilities - ZERO TO HERO", () => {
|
|||||||
|
|
||||||
await game.startBattle([ Species.FEEBAS, Species.PALAFIN, Species.PALAFIN ]);
|
await game.startBattle([ Species.FEEBAS, Species.PALAFIN, Species.PALAFIN ]);
|
||||||
|
|
||||||
const palafin1 = game.scene.getParty()[1];
|
const palafin1 = game.scene.getPlayerParty()[1];
|
||||||
const palafin2 = game.scene.getParty()[2];
|
const palafin2 = game.scene.getPlayerParty()[2];
|
||||||
expect(palafin1.formIndex).toBe(heroForm);
|
expect(palafin1.formIndex).toBe(heroForm);
|
||||||
expect(palafin2.formIndex).toBe(heroForm);
|
expect(palafin2.formIndex).toBe(heroForm);
|
||||||
palafin2.hp = 0;
|
palafin2.hp = 0;
|
||||||
|
@ -136,9 +136,9 @@ describe("Test Battle Phase", () => {
|
|||||||
Species.CHANSEY,
|
Species.CHANSEY,
|
||||||
Species.MEW
|
Species.MEW
|
||||||
]);
|
]);
|
||||||
expect(game.scene.getParty()[0].species.speciesId).toBe(Species.CHARIZARD);
|
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(Species.CHARIZARD);
|
||||||
expect(game.scene.getParty()[1].species.speciesId).toBe(Species.CHANSEY);
|
expect(game.scene.getPlayerParty()[1].species.speciesId).toBe(Species.CHANSEY);
|
||||||
expect(game.scene.getParty()[2].species.speciesId).toBe(Species.MEW);
|
expect(game.scene.getPlayerParty()[2].species.speciesId).toBe(Species.MEW);
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
it("test remove random battle seed int", async () => {
|
it("test remove random battle seed int", async () => {
|
||||||
|
@ -49,7 +49,7 @@ describe("Double Battles", () => {
|
|||||||
await game.phaseInterceptor.to(BattleEndPhase);
|
await game.phaseInterceptor.to(BattleEndPhase);
|
||||||
game.doSelectModifier();
|
game.doSelectModifier();
|
||||||
|
|
||||||
const charizard = game.scene.getParty().findIndex(p => p.species.speciesId === Species.CHARIZARD);
|
const charizard = game.scene.getPlayerParty().findIndex(p => p.species.speciesId === Species.CHARIZARD);
|
||||||
game.doRevivePokemon(charizard);
|
game.doRevivePokemon(charizard);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnInitPhase);
|
await game.phaseInterceptor.to(TurnInitPhase);
|
||||||
|
@ -30,7 +30,7 @@ describe("Daily Mode", () => {
|
|||||||
it("should initialize properly", async () => {
|
it("should initialize properly", async () => {
|
||||||
await game.dailyMode.runToSummon();
|
await game.dailyMode.runToSummon();
|
||||||
|
|
||||||
const party = game.scene.getParty();
|
const party = game.scene.getPlayerParty();
|
||||||
expect(party).toHaveLength(3);
|
expect(party).toHaveLength(3);
|
||||||
party.forEach(pkm => {
|
party.forEach(pkm => {
|
||||||
expect(pkm.level).toBe(20);
|
expect(pkm.level).toBe(20);
|
||||||
|
@ -35,8 +35,8 @@ describe("Evolution", () => {
|
|||||||
it("should keep hidden ability after evolving", async () => {
|
it("should keep hidden ability after evolving", async () => {
|
||||||
await game.classicMode.runToSummon([ Species.EEVEE, Species.TRAPINCH ]);
|
await game.classicMode.runToSummon([ Species.EEVEE, Species.TRAPINCH ]);
|
||||||
|
|
||||||
const eevee = game.scene.getParty()[0];
|
const eevee = game.scene.getPlayerParty()[0];
|
||||||
const trapinch = game.scene.getParty()[1];
|
const trapinch = game.scene.getPlayerParty()[1];
|
||||||
eevee.abilityIndex = 2;
|
eevee.abilityIndex = 2;
|
||||||
trapinch.abilityIndex = 2;
|
trapinch.abilityIndex = 2;
|
||||||
|
|
||||||
@ -50,8 +50,8 @@ describe("Evolution", () => {
|
|||||||
it("should keep same ability slot after evolving", async () => {
|
it("should keep same ability slot after evolving", async () => {
|
||||||
await game.classicMode.runToSummon([ Species.BULBASAUR, Species.CHARMANDER ]);
|
await game.classicMode.runToSummon([ Species.BULBASAUR, Species.CHARMANDER ]);
|
||||||
|
|
||||||
const bulbasaur = game.scene.getParty()[0];
|
const bulbasaur = game.scene.getPlayerParty()[0];
|
||||||
const charmander = game.scene.getParty()[1];
|
const charmander = game.scene.getPlayerParty()[1];
|
||||||
bulbasaur.abilityIndex = 0;
|
bulbasaur.abilityIndex = 0;
|
||||||
charmander.abilityIndex = 1;
|
charmander.abilityIndex = 1;
|
||||||
|
|
||||||
@ -80,8 +80,8 @@ describe("Evolution", () => {
|
|||||||
nincada.metBiome = -1;
|
nincada.metBiome = -1;
|
||||||
|
|
||||||
nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm());
|
nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm());
|
||||||
const ninjask = game.scene.getParty()[0];
|
const ninjask = game.scene.getPlayerParty()[0];
|
||||||
const shedinja = game.scene.getParty()[1];
|
const shedinja = game.scene.getPlayerParty()[1];
|
||||||
expect(ninjask.abilityIndex).toBe(2);
|
expect(ninjask.abilityIndex).toBe(2);
|
||||||
expect(shedinja.abilityIndex).toBe(1);
|
expect(shedinja.abilityIndex).toBe(1);
|
||||||
// Regression test for https://github.com/pagefaultgames/pokerogue/issues/3842
|
// Regression test for https://github.com/pagefaultgames/pokerogue/issues/3842
|
||||||
|
@ -45,7 +45,7 @@ describe("Spec - Pokemon", () => {
|
|||||||
const zubat = scene.getEnemyPokemon()!;
|
const zubat = scene.getEnemyPokemon()!;
|
||||||
zubat.addToParty(PokeballType.LUXURY_BALL);
|
zubat.addToParty(PokeballType.LUXURY_BALL);
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
expect(party).toHaveLength(6);
|
expect(party).toHaveLength(6);
|
||||||
party.forEach((pkm, index) =>{
|
party.forEach((pkm, index) =>{
|
||||||
expect(pkm.species.speciesId).toBe(index === 5 ? Species.ZUBAT : Species.ABRA);
|
expect(pkm.species.speciesId).toBe(index === 5 ? Species.ZUBAT : Species.ABRA);
|
||||||
@ -57,7 +57,7 @@ describe("Spec - Pokemon", () => {
|
|||||||
const zubat = scene.getEnemyPokemon()!;
|
const zubat = scene.getEnemyPokemon()!;
|
||||||
zubat.addToParty(PokeballType.LUXURY_BALL, slotIndex);
|
zubat.addToParty(PokeballType.LUXURY_BALL, slotIndex);
|
||||||
|
|
||||||
const party = scene.getParty();
|
const party = scene.getPlayerParty();
|
||||||
expect(party).toHaveLength(6);
|
expect(party).toHaveLength(6);
|
||||||
party.forEach((pkm, index) =>{
|
party.forEach((pkm, index) =>{
|
||||||
expect(pkm.species.speciesId).toBe(index === slotIndex ? Species.ZUBAT : Species.ABRA);
|
expect(pkm.species.speciesId).toBe(index === slotIndex ? Species.ZUBAT : Species.ABRA);
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { Stat } from "#enums/stat";
|
import { StatBoosterModifier } from "#app/modifier/modifier";
|
||||||
|
import { NumberHolder, randItem } from "#app/utils";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
import GameManager from "#test/utils/gameManager";
|
import GameManager from "#test/utils/gameManager";
|
||||||
import Phase from "phaser";
|
import Phase from "phaser";
|
||||||
import * as Utils from "#app/utils";
|
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { StatBoosterModifier } from "#app/modifier/modifier";
|
|
||||||
|
|
||||||
describe("Items - Eviolite", () => {
|
describe("Items - Eviolite", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -28,14 +28,12 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async() => {
|
it("should provide 50% boost to DEF and SPDEF for unevolved, unfused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.PICHU ]);
|
||||||
Species.PICHU
|
|
||||||
]);
|
|
||||||
|
|
||||||
const partyMember = game.scene.getPlayerPokemon()!;
|
const partyMember = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -51,14 +49,12 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not provide a boost for fully evolved, unfused pokemon", async() => {
|
it("should not provide a boost for fully evolved, unfused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.RAICHU ]);
|
||||||
Species.RAICHU,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -75,12 +71,9 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should provide 50% boost to DEF and SPDEF for completely unevolved, fused pokemon", async() => {
|
it("should provide 50% boost to DEF and SPDEF for completely unevolved, fused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.PICHU, Species.CLEFFA ]);
|
||||||
Species.PICHU,
|
|
||||||
Species.CLEFFA
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -92,7 +85,7 @@ describe("Items - Eviolite", () => {
|
|||||||
partyMember.fusionLuck = ally.luck;
|
partyMember.fusionLuck = ally.luck;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -108,12 +101,9 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should provide 25% boost to DEF and SPDEF for partially unevolved (base), fused pokemon", async() => {
|
it("should provide 25% boost to DEF and SPDEF for partially unevolved (base), fused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.PICHU, Species.CLEFABLE ]);
|
||||||
Species.PICHU,
|
|
||||||
Species.CLEFABLE
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -125,7 +115,7 @@ describe("Items - Eviolite", () => {
|
|||||||
partyMember.fusionLuck = ally.luck;
|
partyMember.fusionLuck = ally.luck;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -141,12 +131,9 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should provide 25% boost to DEF and SPDEF for partially unevolved (fusion), fused pokemon", async() => {
|
it("should provide 25% boost to DEF and SPDEF for partially unevolved (fusion), fused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.RAICHU, Species.CLEFFA ]);
|
||||||
Species.RAICHU,
|
|
||||||
Species.CLEFFA
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -158,7 +145,7 @@ describe("Items - Eviolite", () => {
|
|||||||
partyMember.fusionLuck = ally.luck;
|
partyMember.fusionLuck = ally.luck;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -174,12 +161,9 @@ describe("Items - Eviolite", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should not provide a boost for fully evolved, fused pokemon", async() => {
|
it("should not provide a boost for fully evolved, fused pokemon", async() => {
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ Species.RAICHU, Species.CLEFABLE ]);
|
||||||
Species.RAICHU,
|
|
||||||
Species.CLEFABLE
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -191,7 +175,7 @@ describe("Items - Eviolite", () => {
|
|||||||
partyMember.fusionLuck = ally.luck;
|
partyMember.fusionLuck = ally.luck;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
@ -216,14 +200,12 @@ describe("Items - Eviolite", () => {
|
|||||||
|
|
||||||
const gMaxablePokemon = [ Species.PIKACHU, Species.EEVEE, Species.DURALUDON, Species.MEOWTH ];
|
const gMaxablePokemon = [ Species.PIKACHU, Species.EEVEE, Species.DURALUDON, Species.MEOWTH ];
|
||||||
|
|
||||||
await game.classicMode.startBattle([
|
await game.classicMode.startBattle([ randItem(gMaxablePokemon) ]);
|
||||||
Utils.randItem(gMaxablePokemon)
|
|
||||||
]);
|
|
||||||
|
|
||||||
const partyMember = game.scene.getPlayerPokemon()!;
|
const partyMember = game.scene.getPlayerPokemon()!;
|
||||||
|
|
||||||
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
vi.spyOn(partyMember, "getEffectiveStat").mockImplementation((stat, _opponent?, _move?, _isCritical?) => {
|
||||||
const statValue = new Utils.NumberHolder(partyMember.getStat(stat, false));
|
const statValue = new NumberHolder(partyMember.getStat(stat, false));
|
||||||
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
game.scene.applyModifiers(StatBoosterModifier, partyMember.isPlayer(), partyMember, stat, statValue);
|
||||||
|
|
||||||
// Ignore other calculations for simplicity
|
// Ignore other calculations for simplicity
|
||||||
|
@ -89,7 +89,7 @@ describe("Items - Leek", () => {
|
|||||||
Species.PIKACHU,
|
Species.PIKACHU,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -120,7 +120,7 @@ describe("Items - Leek", () => {
|
|||||||
species[Utils.randInt(species.length)]
|
species[Utils.randInt(species.length)]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const [ partyMember, ally ] = game.scene.getParty();
|
const [ partyMember, ally ] = game.scene.getPlayerParty();
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
|
@ -35,7 +35,7 @@ describe("Items - Light Ball", () => {
|
|||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
// Checking console log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called
|
// Checking console log to make sure Light Ball is applied when getEffectiveStat (with the appropriate stat) is called
|
||||||
partyMember.getEffectiveStat(Stat.DEF);
|
partyMember.getEffectiveStat(Stat.DEF);
|
||||||
@ -68,7 +68,7 @@ describe("Items - Light Ball", () => {
|
|||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
const spAtkStat = partyMember.getStat(Stat.SPATK);
|
const spAtkStat = partyMember.getStat(Stat.SPATK);
|
||||||
@ -97,8 +97,8 @@ describe("Items - Light Ball", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -136,8 +136,8 @@ describe("Items - Light Ball", () => {
|
|||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -174,7 +174,7 @@ describe("Items - Light Ball", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
const spAtkStat = partyMember.getStat(Stat.SPATK);
|
const spAtkStat = partyMember.getStat(Stat.SPATK);
|
||||||
|
@ -35,7 +35,7 @@ describe("Items - Metal Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
// Checking console log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
// Checking console log to make sure Metal Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
||||||
partyMember.getEffectiveStat(Stat.DEF);
|
partyMember.getEffectiveStat(Stat.DEF);
|
||||||
@ -68,7 +68,7 @@ describe("Items - Metal Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const defStat = partyMember.getStat(Stat.DEF);
|
const defStat = partyMember.getStat(Stat.DEF);
|
||||||
|
|
||||||
@ -91,8 +91,8 @@ describe("Items - Metal Powder", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -124,8 +124,8 @@ describe("Items - Metal Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -156,7 +156,7 @@ describe("Items - Metal Powder", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const defStat = partyMember.getStat(Stat.DEF);
|
const defStat = partyMember.getStat(Stat.DEF);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ describe("Items - Quick Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
// Checking console log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
// Checking console log to make sure Quick Powder is applied when getEffectiveStat (with the appropriate stat) is called
|
||||||
partyMember.getEffectiveStat(Stat.DEF);
|
partyMember.getEffectiveStat(Stat.DEF);
|
||||||
@ -68,7 +68,7 @@ describe("Items - Quick Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const spdStat = partyMember.getStat(Stat.SPD);
|
const spdStat = partyMember.getStat(Stat.SPD);
|
||||||
|
|
||||||
@ -91,8 +91,8 @@ describe("Items - Quick Powder", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -124,8 +124,8 @@ describe("Items - Quick Powder", () => {
|
|||||||
Species.DITTO
|
Species.DITTO
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -156,7 +156,7 @@ describe("Items - Quick Powder", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const spdStat = partyMember.getStat(Stat.SPD);
|
const spdStat = partyMember.getStat(Stat.SPD);
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.CUBONE
|
Species.CUBONE
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
// Checking console log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called
|
// Checking console log to make sure Thick Club is applied when getEffectiveStat (with the appropriate stat) is called
|
||||||
partyMember.getEffectiveStat(Stat.DEF);
|
partyMember.getEffectiveStat(Stat.DEF);
|
||||||
@ -68,7 +68,7 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.CUBONE
|
Species.CUBONE
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.MAROWAK
|
Species.MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
|
|
||||||
@ -112,7 +112,7 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.ALOLA_MAROWAK
|
Species.ALOLA_MAROWAK
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
|
|
||||||
@ -139,8 +139,8 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -176,8 +176,8 @@ describe("Items - Thick Club", () => {
|
|||||||
species[randSpecies]
|
species[randSpecies]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
const ally = game.scene.getParty()[1];
|
const ally = game.scene.getPlayerParty()[1];
|
||||||
|
|
||||||
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
// Fuse party members (taken from PlayerPokemon.fuse(...) function)
|
||||||
partyMember.fusionSpecies = ally.species;
|
partyMember.fusionSpecies = ally.species;
|
||||||
@ -208,7 +208,7 @@ describe("Items - Thick Club", () => {
|
|||||||
Species.PIKACHU
|
Species.PIKACHU
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const partyMember = game.scene.getParty()[0];
|
const partyMember = game.scene.getPlayerParty()[0];
|
||||||
|
|
||||||
const atkStat = partyMember.getStat(Stat.ATK);
|
const atkStat = partyMember.getStat(Stat.ATK);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user