mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-08 00:19:29 +02:00
Merge branch 'beta' into turn-start-phase
This commit is contained in:
commit
94d8d60826
BIN
public/audio/bgm/battle_legendary_eternatus_p1.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_eternatus_p1.mp3
Normal file
Binary file not shown.
BIN
public/audio/bgm/battle_legendary_eternatus_p2.mp3
Normal file
BIN
public/audio/bgm/battle_legendary_eternatus_p2.mp3
Normal file
Binary file not shown.
@ -8478,6 +8478,6 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"app": "https://www.codeandweb.com/texturepacker",
|
"app": "https://www.codeandweb.com/texturepacker",
|
||||||
"version": "3.0",
|
"version": "3.0",
|
||||||
"smartupdate": "$TexturePacker:SmartUpdate:0fa4b2b134eacc1b8e5cf03054124001:8eebc761c452a8a36eae96a30cd3d32b:110e074689c9edd2c54833ce2e4d9270$"
|
"smartupdate": "$TexturePacker:SmartUpdate:9b6fc7b241128f4f61686fe287e090cd:46e9caafcc91f3c30ff85a6e8d3f5227:110e074689c9edd2c54833ce2e4d9270$"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 327 B After Width: | Height: | Size: 785 B |
@ -2525,6 +2525,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
return 10.344;
|
return 10.344;
|
||||||
case "battle_legendary_zac_zam": //SWSH Zacian & Zamazenta Battle
|
case "battle_legendary_zac_zam": //SWSH Zacian & Zamazenta Battle
|
||||||
return 11.424;
|
return 11.424;
|
||||||
|
case "battle_legendary_eternatus_p1": //SWSH Eternatus Battle
|
||||||
|
return 11.102;
|
||||||
|
case "battle_legendary_eternatus_p2": //SWSH Eternamax Eternatus Battle
|
||||||
|
return 0.0;
|
||||||
case "battle_legendary_glas_spec": //SWSH Glastrier & Spectrier Battle
|
case "battle_legendary_glas_spec": //SWSH Glastrier & Spectrier Battle
|
||||||
return 12.503;
|
return 12.503;
|
||||||
case "battle_legendary_calyrex": //SWSH Calyrex Battle
|
case "battle_legendary_calyrex": //SWSH Calyrex Battle
|
||||||
|
@ -382,6 +382,11 @@ export default class Battle {
|
|||||||
case SpeciesId.ZACIAN:
|
case SpeciesId.ZACIAN:
|
||||||
case SpeciesId.ZAMAZENTA:
|
case SpeciesId.ZAMAZENTA:
|
||||||
return "battle_legendary_zac_zam";
|
return "battle_legendary_zac_zam";
|
||||||
|
case SpeciesId.ETERNATUS:
|
||||||
|
if (pokemon.getFormKey() === "eternamax") {
|
||||||
|
return "battle_legendary_eternatus_p2";
|
||||||
|
}
|
||||||
|
return "battle_legendary_eternatus_p1";
|
||||||
case SpeciesId.GLASTRIER:
|
case SpeciesId.GLASTRIER:
|
||||||
case SpeciesId.SPECTRIER:
|
case SpeciesId.SPECTRIER:
|
||||||
return "battle_legendary_glas_spec";
|
return "battle_legendary_glas_spec";
|
||||||
|
@ -1194,7 +1194,16 @@ export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr {
|
/**
|
||||||
|
* Set stat stages when the user gets hit by a critical hit
|
||||||
|
*
|
||||||
|
* @privateremarks
|
||||||
|
* It is the responsibility of the caller to ensure that this ability attribute is only applied
|
||||||
|
* when the user has been hit by a critical hit; such an event is not checked here.
|
||||||
|
*
|
||||||
|
* @sealed
|
||||||
|
*/
|
||||||
|
export class PostReceiveCritStatStageChangeAbAttr extends AbAttr {
|
||||||
private stat: BattleStat;
|
private stat: BattleStat;
|
||||||
private stages: number;
|
private stages: number;
|
||||||
|
|
||||||
@ -1216,12 +1225,6 @@ export class PostDefendCritStatStageChangeAbAttr extends PostDefendAbAttr {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override getCondition(): AbAttrCondition {
|
|
||||||
return (pokemon: Pokemon) =>
|
|
||||||
pokemon.turnData.attacksReceived.length !== 0 &&
|
|
||||||
pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
export class PostDefendContactDamageAbAttr extends PostDefendAbAttr {
|
||||||
@ -6417,7 +6420,7 @@ const AbilityAttrs = Object.freeze({
|
|||||||
PostDefendContactApplyStatusEffectAbAttr,
|
PostDefendContactApplyStatusEffectAbAttr,
|
||||||
EffectSporeAbAttr,
|
EffectSporeAbAttr,
|
||||||
PostDefendContactApplyTagChanceAbAttr,
|
PostDefendContactApplyTagChanceAbAttr,
|
||||||
PostDefendCritStatStageChangeAbAttr,
|
PostReceiveCritStatStageChangeAbAttr,
|
||||||
PostDefendContactDamageAbAttr,
|
PostDefendContactDamageAbAttr,
|
||||||
PostDefendPerishSongAbAttr,
|
PostDefendPerishSongAbAttr,
|
||||||
PostDefendWeatherChangeAbAttr,
|
PostDefendWeatherChangeAbAttr,
|
||||||
@ -6886,7 +6889,7 @@ export function initAbilities() {
|
|||||||
new Ability(AbilityId.GLUTTONY, 4)
|
new Ability(AbilityId.GLUTTONY, 4)
|
||||||
.attr(ReduceBerryUseThresholdAbAttr),
|
.attr(ReduceBerryUseThresholdAbAttr),
|
||||||
new Ability(AbilityId.ANGER_POINT, 4)
|
new Ability(AbilityId.ANGER_POINT, 4)
|
||||||
.attr(PostDefendCritStatStageChangeAbAttr, Stat.ATK, 6),
|
.attr(PostReceiveCritStatStageChangeAbAttr, Stat.ATK, 12),
|
||||||
new Ability(AbilityId.UNBURDEN, 4)
|
new Ability(AbilityId.UNBURDEN, 4)
|
||||||
.attr(PostItemLostApplyBattlerTagAbAttr, BattlerTagType.UNBURDEN)
|
.attr(PostItemLostApplyBattlerTagAbAttr, BattlerTagType.UNBURDEN)
|
||||||
.bypassFaint() // Allows reviver seed to activate Unburden
|
.bypassFaint() // Allows reviver seed to activate Unburden
|
||||||
|
@ -15,7 +15,7 @@ import type { PokemonHeldItemModifier } from "#app/modifier/modifier";
|
|||||||
import { AbilityAttr } from "#enums/ability-attr";
|
import { AbilityAttr } from "#enums/ability-attr";
|
||||||
import PokemonData from "#app/system/pokemon-data";
|
import PokemonData from "#app/system/pokemon-data";
|
||||||
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
import type { OptionSelectItem } from "#app/ui/abstact-option-select-ui-handler";
|
||||||
import { isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
import { getEnumValues, isNullOrUndefined, randSeedShuffle } from "#app/utils/common";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
@ -30,7 +30,7 @@ import i18next from "i18next";
|
|||||||
import { getStatKey } from "#enums/stat";
|
import { getStatKey } from "#enums/stat";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
import { isPokemonValidForEncounterOptionSelection } from "#app/data/mystery-encounters/utils/encounter-pokemon-utils";
|
||||||
import type { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
|
|
||||||
/** The i18n namespace for the encounter */
|
/** The i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/trainingSession";
|
const namespace = "mysteryEncounters/trainingSession";
|
||||||
@ -184,10 +184,9 @@ export const TrainingSessionEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
.withPreOptionPhase(async (): Promise<boolean> => {
|
.withPreOptionPhase(async (): Promise<boolean> => {
|
||||||
// Open menu for selecting pokemon and Nature
|
// Open menu for selecting pokemon and Nature
|
||||||
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
const encounter = globalScene.currentBattle.mysteryEncounter!;
|
||||||
const natures = new Array(25).fill(null).map((_val, i) => i as Nature);
|
|
||||||
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
const onPokemonSelected = (pokemon: PlayerPokemon) => {
|
||||||
// Return the options for nature selection
|
// Return the options for nature selection
|
||||||
return natures.map((nature: Nature) => {
|
return getEnumValues(Nature).map((nature: Nature) => {
|
||||||
const option: OptionSelectItem = {
|
const option: OptionSelectItem = {
|
||||||
label: getNatureName(nature, true, true, true, globalScene.uiTheme),
|
label: getNatureName(nature, true, true, true, globalScene.uiTheme),
|
||||||
handler: () => {
|
handler: () => {
|
||||||
|
@ -150,7 +150,7 @@ function doFanOutParticle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): void {
|
export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): void {
|
||||||
const addParticle = () => {
|
const addParticle = (): void => {
|
||||||
const particle = globalScene.add.sprite(pokeball.x, pokeball.y, "pb_particles", "4.png");
|
const particle = globalScene.add.sprite(pokeball.x, pokeball.y, "pb_particles", "4.png");
|
||||||
particle.setOrigin(pokeball.originX, pokeball.originY);
|
particle.setOrigin(pokeball.originX, pokeball.originY);
|
||||||
particle.setAlpha(0.5);
|
particle.setAlpha(0.5);
|
||||||
@ -188,7 +188,9 @@ export function addPokeballCaptureStars(pokeball: Phaser.GameObjects.Sprite): vo
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
new Array(3).fill(null).map(() => addParticle());
|
for (let i = 0; i < 3; i++) {
|
||||||
|
addParticle();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sin(index: number, amplitude: number): number {
|
export function sin(index: number, amplitude: number): number {
|
||||||
|
@ -981,14 +981,15 @@ export class ArenaBase extends Phaser.GameObjects.Container {
|
|||||||
this.base = globalScene.addFieldSprite(0, 0, "plains_a", undefined, 1);
|
this.base = globalScene.addFieldSprite(0, 0, "plains_a", undefined, 1);
|
||||||
this.base.setOrigin(0, 0);
|
this.base.setOrigin(0, 0);
|
||||||
|
|
||||||
this.props = !player
|
this.props = [];
|
||||||
? new Array(3).fill(null).map(() => {
|
if (!player) {
|
||||||
const ret = globalScene.addFieldSprite(0, 0, "plains_b", undefined, 1);
|
for (let i = 0; i < 3; i++) {
|
||||||
ret.setOrigin(0, 0);
|
const ret = globalScene.addFieldSprite(0, 0, "plains_b", undefined, 1);
|
||||||
ret.setVisible(false);
|
ret.setOrigin(0, 0);
|
||||||
return ret;
|
ret.setVisible(false);
|
||||||
})
|
this.props.push(ret);
|
||||||
: [];
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setBiome(biome: BiomeId, propValue?: number): void {
|
setBiome(biome: BiomeId, propValue?: number): void {
|
||||||
|
@ -2457,12 +2457,31 @@ export function regenerateModifierPoolThresholds(party: Pokemon[], poolType: Mod
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface CustomModifierSettings {
|
export interface CustomModifierSettings {
|
||||||
|
/** If specified, will override the next X items to be the specified tier. These can upgrade with luck. */
|
||||||
guaranteedModifierTiers?: ModifierTier[];
|
guaranteedModifierTiers?: ModifierTier[];
|
||||||
|
/** If specified, will override the first X items to be specific modifier options (these should be pre-genned). */
|
||||||
guaranteedModifierTypeOptions?: ModifierTypeOption[];
|
guaranteedModifierTypeOptions?: ModifierTypeOption[];
|
||||||
|
/** If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned). */
|
||||||
guaranteedModifierTypeFuncs?: ModifierTypeFunc[];
|
guaranteedModifierTypeFuncs?: ModifierTypeFunc[];
|
||||||
|
/**
|
||||||
|
* If set to `true`, will fill the remainder of shop items that were not overridden by the 3 options above, up to the `count` param value.
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* count = 4;
|
||||||
|
* customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true };
|
||||||
|
* ```
|
||||||
|
* The first item in the shop will be `GREAT` tier, and the remaining `3` items will be generated normally.
|
||||||
|
*
|
||||||
|
* If `fillRemaining: false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of the value of `count`).
|
||||||
|
* @defaultValue `false`
|
||||||
|
*/
|
||||||
fillRemaining?: boolean;
|
fillRemaining?: boolean;
|
||||||
/** Set to negative value to disable rerolls completely in shop */
|
/** If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all. */
|
||||||
rerollMultiplier?: number;
|
rerollMultiplier?: number;
|
||||||
|
/**
|
||||||
|
* If `false`, will prevent set item tiers from upgrading via luck.
|
||||||
|
* @defaultValue `true`
|
||||||
|
*/
|
||||||
allowLuckUpgrades?: boolean;
|
allowLuckUpgrades?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2472,19 +2491,10 @@ export function getModifierTypeFuncById(id: string): ModifierTypeFunc {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates modifier options for a {@linkcode SelectModifierPhase}
|
* Generates modifier options for a {@linkcode SelectModifierPhase}
|
||||||
* @param count Determines the number of items to generate
|
* @param count - Determines the number of items to generate
|
||||||
* @param party Party is required for generating proper modifier pools
|
* @param party - Party is required for generating proper modifier pools
|
||||||
* @param modifierTiers (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule.
|
* @param modifierTiers - (Optional) If specified, rolls items in the specified tiers. Commonly used for tier-locking with Lock Capsule.
|
||||||
* @param customModifierSettings (Optional) If specified, can customize the item shop rewards further.
|
* @param customModifierSettings - See {@linkcode CustomModifierSettings}
|
||||||
* - `guaranteedModifierTypeOptions?: ModifierTypeOption[]` If specified, will override the first X items to be specific modifier options (these should be pre-genned).
|
|
||||||
* - `guaranteedModifierTypeFuncs?: ModifierTypeFunc[]` If specified, will override the next X items to be auto-generated from specific modifier functions (these don't have to be pre-genned).
|
|
||||||
* - `guaranteedModifierTiers?: ModifierTier[]` If specified, will override the next X items to be the specified tier. These can upgrade with luck.
|
|
||||||
* - `fillRemaining?: boolean` Default 'false'. If set to true, will fill the remainder of shop items that were not overridden by the 3 options above, up to the 'count' param value.
|
|
||||||
* - Example: `count = 4`, `customModifierSettings = { guaranteedModifierTiers: [ModifierTier.GREAT], fillRemaining: true }`,
|
|
||||||
* - The first item in the shop will be `GREAT` tier, and the remaining 3 items will be generated normally.
|
|
||||||
* - If `fillRemaining = false` in the same scenario, only 1 `GREAT` tier item will appear in the shop (regardless of `count` value).
|
|
||||||
* - `rerollMultiplier?: number` If specified, can adjust the amount of money required for a shop reroll. If set to a negative value, the shop will not allow rerolls at all.
|
|
||||||
* - `allowLuckUpgrades?: boolean` Default `true`, if `false` will prevent set item tiers from upgrading via luck
|
|
||||||
*/
|
*/
|
||||||
export function getPlayerModifierTypeOptions(
|
export function getPlayerModifierTypeOptions(
|
||||||
count: number,
|
count: number,
|
||||||
@ -2495,16 +2505,10 @@ export function getPlayerModifierTypeOptions(
|
|||||||
const options: ModifierTypeOption[] = [];
|
const options: ModifierTypeOption[] = [];
|
||||||
const retryCount = Math.min(count * 5, 50);
|
const retryCount = Math.min(count * 5, 50);
|
||||||
if (!customModifierSettings) {
|
if (!customModifierSettings) {
|
||||||
new Array(count).fill(0).map((_, i) => {
|
for (let i = 0; i < count; i++) {
|
||||||
options.push(
|
const tier = modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined;
|
||||||
getModifierTypeOptionWithRetry(
|
options.push(getModifierTypeOptionWithRetry(options, retryCount, party, tier));
|
||||||
options,
|
}
|
||||||
retryCount,
|
|
||||||
party,
|
|
||||||
modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Guaranteed mod options first
|
// Guaranteed mod options first
|
||||||
if (
|
if (
|
||||||
|
@ -388,7 +388,7 @@ export class EvolutionPhase extends Phase {
|
|||||||
globalScene.ui.showText(
|
globalScene.ui.showText(
|
||||||
i18next.t("menu:evolutionDone", {
|
i18next.t("menu:evolutionDone", {
|
||||||
pokemonName: this.preEvolvedPokemonName,
|
pokemonName: this.preEvolvedPokemonName,
|
||||||
evolvedPokemonName: this.pokemon.species.getExpandedSpeciesName(),
|
evolvedPokemonName: this.pokemon.name,
|
||||||
}),
|
}),
|
||||||
null,
|
null,
|
||||||
() => this.end(),
|
() => this.end(),
|
||||||
|
@ -432,9 +432,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
* @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 target - {@linkcode Pokemon} the current target of this phase's invoked move
|
||||||
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
* @param hitResult - The {@linkcode HitResult} of the attempted move
|
||||||
|
* @param wasCritical - `true` if the move was a critical hit
|
||||||
*/
|
*/
|
||||||
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult): void {
|
protected applyOnGetHitAbEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, wasCritical = false): void {
|
||||||
applyAbAttrs("PostDefendAbAttr", { pokemon: target, opponent: user, move: this.move, hitResult });
|
const params = { pokemon: target, opponent: user, move: this.move, hitResult };
|
||||||
|
applyAbAttrs("PostDefendAbAttr", params);
|
||||||
|
|
||||||
|
if (wasCritical) {
|
||||||
|
applyAbAttrs("PostReceiveCritStatStageChangeAbAttr", params);
|
||||||
|
}
|
||||||
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
target.lapseTags(BattlerTagLapseType.AFTER_HIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -788,12 +794,12 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
this.triggerMoveEffects(MoveEffectTrigger.PRE_APPLY, user, target);
|
this.triggerMoveEffects(MoveEffectTrigger.PRE_APPLY, user, target);
|
||||||
|
|
||||||
const hitResult = this.applyMove(user, target, effectiveness);
|
const [hitResult, wasCritical] = this.applyMove(user, target, effectiveness);
|
||||||
|
|
||||||
// Apply effects to the user (always) and the target (if not blocked by substitute).
|
// Apply effects to the user (always) and the target (if not blocked by substitute).
|
||||||
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, true);
|
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, true);
|
||||||
if (!this.move.hitsSubstitute(user, target)) {
|
if (!this.move.hitsSubstitute(user, target)) {
|
||||||
this.applyOnTargetEffects(user, target, hitResult, firstTarget);
|
this.applyOnTargetEffects(user, target, hitResult, firstTarget, wasCritical);
|
||||||
}
|
}
|
||||||
if (this.lastHit) {
|
if (this.lastHit) {
|
||||||
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
globalScene.triggerPokemonFormChange(user, SpeciesFormChangePostMoveTrigger);
|
||||||
@ -813,8 +819,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
* @param user - The {@linkcode Pokemon} using this phase's invoked move
|
||||||
* @param target - The {@linkcode Pokemon} targeted by the move
|
* @param target - The {@linkcode Pokemon} targeted by the move
|
||||||
* @param effectiveness - The effectiveness of the move against the target
|
* @param effectiveness - The effectiveness of the move against the target
|
||||||
|
* @returns The {@linkcode HitResult} of the move against the target and a boolean indicating whether the target was crit
|
||||||
*/
|
*/
|
||||||
protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult {
|
protected applyMoveDamage(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] {
|
||||||
const isCritical = target.getCriticalHitResult(user, this.move);
|
const isCritical = target.getCriticalHitResult(user, this.move);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -845,7 +852,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
const isOneHitKo = result === HitResult.ONE_HIT_KO;
|
||||||
|
|
||||||
if (!dmg) {
|
if (!dmg) {
|
||||||
return result;
|
return [result, false];
|
||||||
}
|
}
|
||||||
|
|
||||||
target.lapseTags(BattlerTagLapseType.HIT);
|
target.lapseTags(BattlerTagLapseType.HIT);
|
||||||
@ -873,7 +880,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (damage <= 0) {
|
if (damage <= 0) {
|
||||||
return result;
|
return [result, isCritical];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.isPlayer()) {
|
if (user.isPlayer()) {
|
||||||
@ -902,7 +909,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage));
|
globalScene.applyModifiers(DamageMoneyRewardModifier, true, user, new NumberHolder(damage));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return [result, isCritical];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -956,17 +963,17 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param target - The {@linkcode Pokemon} struck by the move
|
* @param target - The {@linkcode Pokemon} struck by the move
|
||||||
* @param effectiveness - The effectiveness of the move against the target
|
* @param effectiveness - The effectiveness of the move against the target
|
||||||
*/
|
*/
|
||||||
protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): HitResult {
|
protected applyMove(user: Pokemon, target: Pokemon, effectiveness: TypeDamageMultiplier): [HitResult, boolean] {
|
||||||
const moveCategory = user.getMoveCategory(target, this.move);
|
const moveCategory = user.getMoveCategory(target, this.move);
|
||||||
|
|
||||||
if (moveCategory === MoveCategory.STATUS) {
|
if (moveCategory === MoveCategory.STATUS) {
|
||||||
return HitResult.STATUS;
|
return [HitResult.STATUS, false];
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = this.applyMoveDamage(user, target, effectiveness);
|
const result = this.applyMoveDamage(user, target, effectiveness);
|
||||||
|
|
||||||
if (user.turnData.hitsLeft === 1 || target.isFainted()) {
|
if (user.turnData.hitsLeft === 1 || target.isFainted()) {
|
||||||
this.queueHitResultMessage(result);
|
this.queueHitResultMessage(result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.isFainted()) {
|
if (target.isFainted()) {
|
||||||
@ -983,8 +990,15 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
* @param target - The {@linkcode Pokemon} targeted by the move
|
* @param target - The {@linkcode Pokemon} targeted by the move
|
||||||
* @param hitResult - The {@linkcode HitResult} obtained from applying the move
|
* @param hitResult - The {@linkcode HitResult} obtained from applying the move
|
||||||
* @param firstTarget - `true` if the target is the first Pokemon hit by the attack
|
* @param firstTarget - `true` if the target is the first Pokemon hit by the attack
|
||||||
|
* @param wasCritical - `true` if the move was a critical hit
|
||||||
*/
|
*/
|
||||||
protected applyOnTargetEffects(user: Pokemon, target: Pokemon, hitResult: HitResult, firstTarget: boolean): void {
|
protected applyOnTargetEffects(
|
||||||
|
user: Pokemon,
|
||||||
|
target: Pokemon,
|
||||||
|
hitResult: HitResult,
|
||||||
|
firstTarget: boolean,
|
||||||
|
wasCritical = false,
|
||||||
|
): void {
|
||||||
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
|
/** Does {@linkcode hitResult} indicate that damage was dealt to the target? */
|
||||||
const dealsDamage = [
|
const dealsDamage = [
|
||||||
HitResult.EFFECTIVE,
|
HitResult.EFFECTIVE,
|
||||||
@ -995,7 +1009,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||||||
|
|
||||||
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
|
this.triggerMoveEffects(MoveEffectTrigger.POST_APPLY, user, target, firstTarget, false);
|
||||||
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
this.applyHeldItemFlinchCheck(user, target, dealsDamage);
|
||||||
this.applyOnGetHitAbEffects(user, target, hitResult);
|
this.applyOnGetHitAbEffects(user, target, hitResult, wasCritical);
|
||||||
applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult });
|
applyAbAttrs("PostAttackAbAttr", { pokemon: user, opponent: target, move: this.move, hitResult });
|
||||||
|
|
||||||
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
// We assume only enemy Pokemon are able to have the EnemyAttackStatusEffectChanceModifier from tokens
|
||||||
|
@ -105,7 +105,7 @@ export default class PokemonData {
|
|||||||
|
|
||||||
// TODO: Can't we move some of this verification stuff to an upgrade script?
|
// TODO: Can't we move some of this verification stuff to an upgrade script?
|
||||||
this.nature = source.nature ?? Nature.HARDY;
|
this.nature = source.nature ?? Nature.HARDY;
|
||||||
this.moveset = source.moveset.map((m: any) => PokemonMove.loadMove(m));
|
this.moveset = source.moveset?.map((m: any) => PokemonMove.loadMove(m)) ?? [];
|
||||||
this.status = source.status
|
this.status = source.status
|
||||||
? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining)
|
? new Status(source.status.effect, source.status.toxicTurnCount, source.status.sleepTurnsRemaining)
|
||||||
: null;
|
: null;
|
||||||
|
@ -11,25 +11,22 @@ import { PlayerGender } from "#enums/player-gender";
|
|||||||
import { ShopCursorTarget } from "#enums/shop-cursor-target";
|
import { ShopCursorTarget } from "#enums/shop-cursor-target";
|
||||||
import { isLocal } from "#app/utils/common";
|
import { isLocal } from "#app/utils/common";
|
||||||
|
|
||||||
const VOLUME_OPTIONS: SettingOption[] = new Array(11).fill(null).map((_, i) =>
|
const VOLUME_OPTIONS: SettingOption[] = [
|
||||||
i
|
{
|
||||||
? {
|
value: "Mute",
|
||||||
value: (i * 10).toString(),
|
label: i18next.t("settings:mute"),
|
||||||
label: (i * 10).toString(),
|
},
|
||||||
}
|
];
|
||||||
: {
|
for (let i = 1; i < 11; i++) {
|
||||||
value: "Mute",
|
const value = (i * 10).toString();
|
||||||
label: i18next.t("settings:mute"),
|
VOLUME_OPTIONS.push({ value, label: value });
|
||||||
},
|
}
|
||||||
);
|
|
||||||
|
|
||||||
const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = new Array(9).fill(null).map((_, i) => {
|
const SHOP_OVERLAY_OPACITY_OPTIONS: SettingOption[] = [];
|
||||||
|
for (let i = 0; i < 9; i++) {
|
||||||
const value = ((i + 1) * 10).toString();
|
const value = ((i + 1) * 10).toString();
|
||||||
return {
|
SHOP_OVERLAY_OPACITY_OPTIONS.push({ value, label: value });
|
||||||
value,
|
}
|
||||||
label: value,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const OFF_ON: SettingOption[] = [
|
const OFF_ON: SettingOption[] = [
|
||||||
{
|
{
|
||||||
@ -183,6 +180,12 @@ export enum MusicPreference {
|
|||||||
ALLGENS,
|
ALLGENS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const windowTypeOptions: SettingOption[] = [];
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
const value = (i + 1).toString();
|
||||||
|
windowTypeOptions.push({ value, label: value });
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All Settings not related to controls
|
* All Settings not related to controls
|
||||||
*/
|
*/
|
||||||
@ -432,13 +435,7 @@ export const Setting: Array<Setting> = [
|
|||||||
{
|
{
|
||||||
key: SettingKeys.Window_Type,
|
key: SettingKeys.Window_Type,
|
||||||
label: i18next.t("settings:windowType"),
|
label: i18next.t("settings:windowType"),
|
||||||
options: new Array(5).fill(null).map((_, i) => {
|
options: windowTypeOptions,
|
||||||
const windowType = (i + 1).toString();
|
|
||||||
return {
|
|
||||||
value: windowType,
|
|
||||||
label: windowType,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
default: 0,
|
default: 0,
|
||||||
type: SettingType.DISPLAY,
|
type: SettingType.DISPLAY,
|
||||||
},
|
},
|
||||||
|
@ -299,8 +299,8 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.eggGachaContainer.add(this.eggGachaOptionsContainer);
|
this.eggGachaContainer.add(this.eggGachaOptionsContainer);
|
||||||
|
|
||||||
new Array(getEnumKeys(VoucherType).length).fill(null).map((_, i) => {
|
for (const voucher of getEnumValues(VoucherType)) {
|
||||||
const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * i, 0);
|
const container = globalScene.add.container(globalScene.game.canvas.width / 6 - 56 * voucher, 0);
|
||||||
|
|
||||||
const bg = addWindow(0, 0, 56, 22);
|
const bg = addWindow(0, 0, 56, 22);
|
||||||
bg.setOrigin(1, 0);
|
bg.setOrigin(1, 0);
|
||||||
@ -312,7 +312,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.voucherCountLabels.push(countLabel);
|
this.voucherCountLabels.push(countLabel);
|
||||||
|
|
||||||
const iconImage = getVoucherTypeIcon(i as VoucherType);
|
const iconImage = getVoucherTypeIcon(voucher);
|
||||||
|
|
||||||
const icon = globalScene.add.sprite(-19, 2, "items", iconImage);
|
const icon = globalScene.add.sprite(-19, 2, "items", iconImage);
|
||||||
icon.setOrigin(0, 0);
|
icon.setOrigin(0, 0);
|
||||||
@ -320,7 +320,7 @@ export default class EggGachaUiHandler extends MessageUiHandler {
|
|||||||
container.add(icon);
|
container.add(icon);
|
||||||
|
|
||||||
this.eggGachaContainer.add(container);
|
this.eggGachaContainer.add(container);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.eggGachaOverlay = globalScene.add.rectangle(0, 0, bg.displayWidth, bg.displayHeight, 0x000000);
|
this.eggGachaOverlay = globalScene.add.rectangle(0, 0, bg.displayWidth, bg.displayHeight, 0x000000);
|
||||||
this.eggGachaOverlay.setOrigin(0, 0);
|
this.eggGachaOverlay.setOrigin(0, 0);
|
||||||
|
@ -267,10 +267,10 @@ export default class GameStatsUiHandler extends UiHandler {
|
|||||||
|
|
||||||
this.statsContainer = globalScene.add.container(0, 0);
|
this.statsContainer = globalScene.add.container(0, 0);
|
||||||
|
|
||||||
new Array(18).fill(null).map((_, s) => {
|
for (let i = 0; i < 18; i++) {
|
||||||
const statLabel = addTextObject(
|
const statLabel = addTextObject(
|
||||||
8 + (s % 2 === 1 ? statsBgWidth : 0),
|
8 + (i % 2 === 1 ? statsBgWidth : 0),
|
||||||
28 + Math.floor(s / 2) * 16,
|
28 + Math.floor(i / 2) * 16,
|
||||||
"",
|
"",
|
||||||
TextStyle.STATS_LABEL,
|
TextStyle.STATS_LABEL,
|
||||||
);
|
);
|
||||||
@ -278,11 +278,11 @@ export default class GameStatsUiHandler extends UiHandler {
|
|||||||
this.statsContainer.add(statLabel);
|
this.statsContainer.add(statLabel);
|
||||||
this.statLabels.push(statLabel);
|
this.statLabels.push(statLabel);
|
||||||
|
|
||||||
const statValue = addTextObject(statsBgWidth * ((s % 2) + 1) - 8, statLabel.y, "", TextStyle.STATS_VALUE);
|
const statValue = addTextObject(statsBgWidth * ((i % 2) + 1) - 8, statLabel.y, "", TextStyle.STATS_VALUE);
|
||||||
statValue.setOrigin(1, 0);
|
statValue.setOrigin(1, 0);
|
||||||
this.statsContainer.add(statValue);
|
this.statsContainer.add(statValue);
|
||||||
this.statValues.push(statValue);
|
this.statValues.push(statValue);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.gameStatsContainer.add(headerBg);
|
this.gameStatsContainer.add(headerBg);
|
||||||
this.gameStatsContainer.add(headerText);
|
this.gameStatsContainer.add(headerText);
|
||||||
|
@ -483,13 +483,14 @@ export default class PokedexUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
starterBoxContainer.add(this.starterSelectScrollBar);
|
starterBoxContainer.add(this.starterSelectScrollBar);
|
||||||
|
|
||||||
this.pokerusCursorObjs = new Array(POKERUS_STARTER_COUNT).fill(null).map(() => {
|
this.pokerusCursorObjs = [];
|
||||||
|
for (let i = 0; i < POKERUS_STARTER_COUNT; i++) {
|
||||||
const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus");
|
const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus");
|
||||||
cursorObj.setVisible(false);
|
cursorObj.setVisible(false);
|
||||||
cursorObj.setOrigin(0, 0);
|
cursorObj.setOrigin(0, 0);
|
||||||
starterBoxContainer.add(cursorObj);
|
starterBoxContainer.add(cursorObj);
|
||||||
return cursorObj;
|
this.pokerusCursorObjs.push(cursorObj);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.cursorObj = globalScene.add.image(0, 0, "select_cursor");
|
this.cursorObj = globalScene.add.image(0, 0, "select_cursor");
|
||||||
this.cursorObj.setOrigin(0, 0);
|
this.cursorObj.setOrigin(0, 0);
|
||||||
|
@ -800,21 +800,23 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
starterBoxContainer.add(this.starterSelectScrollBar);
|
starterBoxContainer.add(this.starterSelectScrollBar);
|
||||||
|
|
||||||
this.pokerusCursorObjs = new Array(POKERUS_STARTER_COUNT).fill(null).map(() => {
|
this.pokerusCursorObjs = [];
|
||||||
|
for (let i = 0; i < POKERUS_STARTER_COUNT; i++) {
|
||||||
const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus");
|
const cursorObj = globalScene.add.image(0, 0, "select_cursor_pokerus");
|
||||||
cursorObj.setVisible(false);
|
cursorObj.setVisible(false);
|
||||||
cursorObj.setOrigin(0, 0);
|
cursorObj.setOrigin(0, 0);
|
||||||
starterBoxContainer.add(cursorObj);
|
starterBoxContainer.add(cursorObj);
|
||||||
return cursorObj;
|
this.pokerusCursorObjs.push(cursorObj);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.starterCursorObjs = new Array(6).fill(null).map(() => {
|
this.starterCursorObjs = [];
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
const cursorObj = globalScene.add.image(0, 0, "select_cursor_highlight");
|
const cursorObj = globalScene.add.image(0, 0, "select_cursor_highlight");
|
||||||
cursorObj.setVisible(false);
|
cursorObj.setVisible(false);
|
||||||
cursorObj.setOrigin(0, 0);
|
cursorObj.setOrigin(0, 0);
|
||||||
starterBoxContainer.add(cursorObj);
|
starterBoxContainer.add(cursorObj);
|
||||||
return cursorObj;
|
this.starterCursorObjs.push(cursorObj);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.cursorObj = globalScene.add.image(0, 0, "select_cursor");
|
this.cursorObj = globalScene.add.image(0, 0, "select_cursor");
|
||||||
this.cursorObj.setOrigin(0, 0);
|
this.cursorObj.setOrigin(0, 0);
|
||||||
@ -843,15 +845,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
this.starterSelectContainer.add(starterBoxContainer);
|
this.starterSelectContainer.add(starterBoxContainer);
|
||||||
|
|
||||||
this.starterIcons = new Array(6).fill(null).map((_, i) => {
|
this.starterIcons = [];
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
const icon = globalScene.add.sprite(teamWindowX + 7, calcStarterIconY(i), "pokemon_icons_0");
|
const icon = globalScene.add.sprite(teamWindowX + 7, calcStarterIconY(i), "pokemon_icons_0");
|
||||||
icon.setScale(0.5);
|
icon.setScale(0.5);
|
||||||
icon.setOrigin(0, 0);
|
icon.setOrigin(0, 0);
|
||||||
icon.setFrame("unknown");
|
icon.setFrame("unknown");
|
||||||
this.starterSelectContainer.add(icon);
|
this.starterSelectContainer.add(icon);
|
||||||
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE);
|
this.iconAnimHandler.addOrUpdate(icon, PokemonIconAnimMode.PASSIVE);
|
||||||
return icon;
|
this.starterIcons.push(icon);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types"));
|
this.type1Icon = globalScene.add.sprite(8, 98, getLocalizedSpriteKey("types"));
|
||||||
this.type1Icon.setScale(0.5);
|
this.type1Icon.setScale(0.5);
|
||||||
|
@ -19,7 +19,7 @@ const ivLabelOffset = [0, sideLabelOffset, -sideLabelOffset, sideLabelOffset, -s
|
|||||||
const ivChartLabelyOffset = [0, 5, 0, 5, 0, 0]; // doing this so attack does not overlap with (+N)
|
const ivChartLabelyOffset = [0, 5, 0, 5, 0, 0]; // doing this so attack does not overlap with (+N)
|
||||||
const ivChartStatIndexes = [0, 1, 2, 5, 4, 3]; // swap special attack and speed
|
const ivChartStatIndexes = [0, 1, 2, 5, 4, 3]; // swap special attack and speed
|
||||||
|
|
||||||
const defaultIvChartData = new Array(12).fill(null).map(() => 0);
|
const defaultIvChartData: number[] = new Array(12).fill(0);
|
||||||
|
|
||||||
export class StatsContainer extends Phaser.GameObjects.Container {
|
export class StatsContainer extends Phaser.GameObjects.Container {
|
||||||
private showDiff: boolean;
|
private showDiff: boolean;
|
||||||
|
78
test/abilities/anger-point.test.ts
Normal file
78
test/abilities/anger-point.test.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { PostReceiveCritStatStageChangeAbAttr } from "#app/data/abilities/ability";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { SpeciesId } from "#enums/species-id";
|
||||||
|
import { Stat } from "#enums/stat";
|
||||||
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("Ability - Anger Point", () => {
|
||||||
|
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
|
||||||
|
.ability(AbilityId.BALL_FETCH)
|
||||||
|
.battleStyle("single")
|
||||||
|
.criticalHits(false)
|
||||||
|
.enemySpecies(SpeciesId.MAGIKARP)
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.SPLASH)
|
||||||
|
.enemyLevel(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the user's attack stage to +6 when hit by a critical hit", async () => {
|
||||||
|
game.override.enemyAbility(AbilityId.ANGER_POINT).moveset(MoveId.FALSE_SWIPE);
|
||||||
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
|
||||||
|
// minimize the enemy's attack stage to ensure it is always set to +6
|
||||||
|
enemy.setStatStage(Stat.ATK, -6);
|
||||||
|
vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true);
|
||||||
|
game.move.select(MoveId.FALSE_SWIPE);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
expect(enemy.getStatStage(Stat.ATK)).toBe(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should only proc once when a multi-hit move crits on the first hit", async () => {
|
||||||
|
game.override
|
||||||
|
.moveset(MoveId.BULLET_SEED)
|
||||||
|
.enemyLevel(50)
|
||||||
|
.enemyAbility(AbilityId.ANGER_POINT)
|
||||||
|
.ability(AbilityId.SKILL_LINK);
|
||||||
|
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true);
|
||||||
|
const angerPointSpy = vi.spyOn(PostReceiveCritStatStageChangeAbAttr.prototype, "apply");
|
||||||
|
game.move.select(MoveId.BULLET_SEED);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
expect(angerPointSpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set a contrary user's attack stage to -6 when hit by a critical hit", async () => {
|
||||||
|
game.override
|
||||||
|
.enemyAbility(AbilityId.ANGER_POINT)
|
||||||
|
.enemyPassiveAbility(AbilityId.CONTRARY)
|
||||||
|
.enemyHasPassiveAbility(true)
|
||||||
|
.moveset(MoveId.FALSE_SWIPE);
|
||||||
|
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||||
|
const enemy = game.scene.getEnemyPokemon()!;
|
||||||
|
vi.spyOn(enemy, "getCriticalHitResult").mockReturnValueOnce(true);
|
||||||
|
enemy.setStatStage(Stat.ATK, 6);
|
||||||
|
game.move.select(MoveId.FALSE_SWIPE);
|
||||||
|
await game.phaseInterceptor.to("BerryPhase");
|
||||||
|
expect(enemy.getStatStage(Stat.ATK)).toBe(-6);
|
||||||
|
});
|
||||||
|
});
|
@ -2,7 +2,6 @@
|
|||||||
import type { NewArenaEvent } from "#app/events/battle-scene";
|
import type { NewArenaEvent } from "#app/events/battle-scene";
|
||||||
/** biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */
|
/** biome-ignore-end lint/correctness/noUnusedImports: tsdoc imports */
|
||||||
|
|
||||||
import { Weather } from "#app/data/weather";
|
|
||||||
import type { ModifierOverride } from "#app/modifier/modifier-type";
|
import type { ModifierOverride } from "#app/modifier/modifier-type";
|
||||||
import type { BattleStyle, RandomTrainerOverride } from "#app/overrides";
|
import type { BattleStyle, RandomTrainerOverride } from "#app/overrides";
|
||||||
import Overrides, { defaultOverrides } from "#app/overrides";
|
import Overrides, { defaultOverrides } from "#app/overrides";
|
||||||
@ -18,7 +17,7 @@ import { Nature } from "#enums/nature";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import type { Unlockables } from "#enums/unlockables";
|
import type { Unlockables } from "#enums/unlockables";
|
||||||
import type { WeatherType } from "#enums/weather-type";
|
import { WeatherType } from "#enums/weather-type";
|
||||||
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
||||||
import { expect, vi } from "vitest";
|
import { expect, vi } from "vitest";
|
||||||
|
|
||||||
@ -356,7 +355,7 @@ export class OverridesHelper extends GameManagerHelper {
|
|||||||
*/
|
*/
|
||||||
public weather(type: WeatherType): this {
|
public weather(type: WeatherType): this {
|
||||||
vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type);
|
vi.spyOn(Overrides, "WEATHER_OVERRIDE", "get").mockReturnValue(type);
|
||||||
this.log(`Weather set to ${Weather[type]} (=${type})!`);
|
this.log(`Weather set to ${WeatherType[type]} (=${type})!`);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user