Hotfixes 1.18.2

This commit is contained in:
damocleas 2025-04-02 03:05:51 -04:00 committed by GitHub
commit cf1616212c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 200 additions and 128 deletions

4
package-lock.json generated
View File

@ -1,12 +1,12 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.8.1", "version": "1.8.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"version": "1.8.1", "version": "1.8.2",
"hasInstallScript": true, "hasInstallScript": true,
"dependencies": { "dependencies": {
"@material/material-color-utilities": "^0.2.7", "@material/material-color-utilities": "^0.2.7",

View File

@ -1,7 +1,7 @@
{ {
"name": "pokemon-rogue-battle", "name": "pokemon-rogue-battle",
"private": true, "private": true,
"version": "1.8.1", "version": "1.8.2",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "vite", "start": "vite",

@ -1 +1 @@
Subproject commit 8538aa3e0f6f38c9c9c74fd0cf6df1e2f8a0bd6d Subproject commit 213701f80471d38142827dec2d798f047db471d1

View File

@ -167,7 +167,7 @@ import { ExpGainsSpeed } from "#enums/exp-gains-speed";
import { BattlerTagType } from "#enums/battler-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type";
import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters"; import { FRIENDSHIP_GAIN_FROM_BATTLE } from "#app/data/balance/starters";
import { StatusEffect } from "#enums/status-effect"; import { StatusEffect } from "#enums/status-effect";
import { globalScene, initGlobalScene } from "#app/global-scene"; import { initGlobalScene } from "#app/global-scene";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase"; import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { HideAbilityPhase } from "#app/phases/hide-ability-phase"; import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
import { timedEventManager } from "./global-event-manager"; import { timedEventManager } from "./global-event-manager";
@ -2665,7 +2665,7 @@ export default class BattleScene extends SceneBase {
case "mystery_encounter_delibirdy": // Firel Delibirdy case "mystery_encounter_delibirdy": // Firel Delibirdy
return 82.28; return 82.28;
case "title_afd": // Andr06 - PokéRogue Title Remix (AFD) case "title_afd": // Andr06 - PokéRogue Title Remix (AFD)
return 47.660; return 47.66;
case "battle_rival_3_afd": // Andr06 - Final N Battle Remix (AFD) case "battle_rival_3_afd": // Andr06 - Final N Battle Remix (AFD)
return 49.147; return 49.147;
} }
@ -2937,14 +2937,19 @@ export default class BattleScene extends SceneBase {
* @param show Whether to show or hide the bar * @param show Whether to show or hide the bar
*/ */
public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void { public queueAbilityDisplay(pokemon: Pokemon, passive: boolean, show: boolean): void {
this.unshiftPhase( this.unshiftPhase(show ? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive) : new HideAbilityPhase());
show
? new ShowAbilityPhase(pokemon.getBattlerIndex(), passive)
: new HideAbilityPhase(pokemon.getBattlerIndex(), passive),
);
this.clearPhaseQueueSplice(); this.clearPhaseQueueSplice();
} }
/**
* Hides the ability bar if it is currently visible
*/
public hideAbilityBar(): void {
if (this.abilityBar.isVisible()) {
this.unshiftPhase(new HideAbilityPhase());
}
}
/** /**
* Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order) * Moves everything from nextCommandPhaseQueue to phaseQueue (keeping order)
*/ */

View File

@ -28,7 +28,6 @@ import { BattlerTagType } from "#enums/battler-tag-type";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-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 { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase";
@ -1160,9 +1159,11 @@ class TailwindTag extends ArenaTag {
); );
} }
// Raise attack by one stage if party member has WIND_RIDER ability // Raise attack by one stage if party member has WIND_RIDER ability
// TODO: Ability displays should be handled by the ability
if (pokemon.hasAbility(Abilities.WIND_RIDER)) { if (pokemon.hasAbility(Abilities.WIND_RIDER)) {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.getBattlerIndex())); globalScene.queueAbilityDisplay(pokemon, false, true);
globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true)); globalScene.unshiftPhase(new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.ATK], 1, true));
globalScene.queueAbilityDisplay(pokemon, false, false);
} }
} }
} }

View File

@ -15,7 +15,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.VENUSAUR]: { 0: Abilities.GRASSY_SURGE, 1: Abilities.SEED_SOWER, 2: Abilities.FLOWER_VEIL }, [Species.VENUSAUR]: { 0: Abilities.GRASSY_SURGE, 1: Abilities.SEED_SOWER, 2: Abilities.FLOWER_VEIL },
[Species.CHARMANDER]: { 0: Abilities.SHEER_FORCE }, [Species.CHARMANDER]: { 0: Abilities.SHEER_FORCE },
[Species.CHARMELEON]: { 0: Abilities.BEAST_BOOST }, [Species.CHARMELEON]: { 0: Abilities.BEAST_BOOST },
[Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.INTIMIDATE, 3: Abilities.UNNERVE }, [Species.CHARIZARD]: { 0: Abilities.BEAST_BOOST, 1: Abilities.LEVITATE, 2: Abilities.TURBOBLAZE, 3: Abilities.UNNERVE },
[Species.SQUIRTLE]: { 0: Abilities.DAUNTLESS_SHIELD }, [Species.SQUIRTLE]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.WARTORTLE]: { 0: Abilities.DAUNTLESS_SHIELD }, [Species.WARTORTLE]: { 0: Abilities.DAUNTLESS_SHIELD },
[Species.BLASTOISE]: { 0: Abilities.DAUNTLESS_SHIELD, 1: Abilities.BULLETPROOF, 2: Abilities.BULLETPROOF }, [Species.BLASTOISE]: { 0: Abilities.DAUNTLESS_SHIELD, 1: Abilities.BULLETPROOF, 2: Abilities.BULLETPROOF },
@ -154,14 +154,14 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.LEAFEON]: { 0: Abilities.GRASSY_SURGE }, [Species.LEAFEON]: { 0: Abilities.GRASSY_SURGE },
[Species.GLACEON]: { 0: Abilities.SNOW_WARNING }, [Species.GLACEON]: { 0: Abilities.SNOW_WARNING },
[Species.SYLVEON]: { 0: Abilities.COMPETITIVE }, [Species.SYLVEON]: { 0: Abilities.COMPETITIVE },
[Species.PORYGON]: { 0: Abilities.LEVITATE }, [Species.PORYGON]: { 0: Abilities.TRANSISTOR },
[Species.PORYGON2]: { 0: Abilities.LEVITATE }, [Species.PORYGON2]: { 0: Abilities.TRANSISTOR },
[Species.PORYGON_Z]: { 0: Abilities.PROTEAN }, [Species.PORYGON_Z]: { 0: Abilities.PROTEAN },
[Species.OMANYTE]: { 0: Abilities.STURDY }, [Species.OMANYTE]: { 0: Abilities.STURDY },
[Species.OMASTAR]: { 0: Abilities.STURDY }, [Species.OMASTAR]: { 0: Abilities.STURDY },
[Species.KABUTO]: { 0: Abilities.TOUGH_CLAWS }, [Species.KABUTO]: { 0: Abilities.TOUGH_CLAWS },
[Species.KABUTOPS]: { 0: Abilities.TOUGH_CLAWS }, [Species.KABUTOPS]: { 0: Abilities.TOUGH_CLAWS },
[Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.DELTA_STREAM }, [Species.AERODACTYL]: { 0: Abilities.INTIMIDATE, 1: Abilities.INTIMIDATE },
[Species.ARTICUNO]: { 0: Abilities.SNOW_WARNING }, [Species.ARTICUNO]: { 0: Abilities.SNOW_WARNING },
[Species.ZAPDOS]: { 0: Abilities.DRIZZLE }, [Species.ZAPDOS]: { 0: Abilities.DRIZZLE },
[Species.MOLTRES]: { 0: Abilities.DROUGHT }, [Species.MOLTRES]: { 0: Abilities.DROUGHT },
@ -309,7 +309,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.SHIFTRY]: { 0: Abilities.SHARPNESS }, [Species.SHIFTRY]: { 0: Abilities.SHARPNESS },
[Species.TAILLOW]: { 0: Abilities.AERILATE }, [Species.TAILLOW]: { 0: Abilities.AERILATE },
[Species.SWELLOW]: { 0: Abilities.AERILATE }, [Species.SWELLOW]: { 0: Abilities.AERILATE },
[Species.WINGULL]: { 0: Abilities.DRIZZLE }, [Species.WINGULL]: { 0: Abilities.WATER_ABSORB },
[Species.PELIPPER]: { 0: Abilities.SWIFT_SWIM }, [Species.PELIPPER]: { 0: Abilities.SWIFT_SWIM },
[Species.RALTS]: { 0: Abilities.NEUROFORCE }, [Species.RALTS]: { 0: Abilities.NEUROFORCE },
[Species.KIRLIA]: { 0: Abilities.NEUROFORCE }, [Species.KIRLIA]: { 0: Abilities.NEUROFORCE },
@ -612,8 +612,8 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.REUNICLUS]: { 0: Abilities.PSYCHIC_SURGE }, [Species.REUNICLUS]: { 0: Abilities.PSYCHIC_SURGE },
[Species.DUCKLETT]: { 0: Abilities.DRIZZLE }, [Species.DUCKLETT]: { 0: Abilities.DRIZZLE },
[Species.SWANNA]: { 0: Abilities.DRIZZLE }, [Species.SWANNA]: { 0: Abilities.DRIZZLE },
[Species.VANILLITE]: { 0: Abilities.SNOW_WARNING }, [Species.VANILLITE]: { 0: Abilities.REFRIGERATE },
[Species.VANILLISH]: { 0: Abilities.SNOW_WARNING }, [Species.VANILLISH]: { 0: Abilities.REFRIGERATE },
[Species.VANILLUXE]: { 0: Abilities.SLUSH_RUSH }, [Species.VANILLUXE]: { 0: Abilities.SLUSH_RUSH },
[Species.DEERLING]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT }, [Species.DEERLING]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT },
[Species.SAWSBUCK]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT }, [Species.SAWSBUCK]: { 0: Abilities.FLOWER_VEIL, 1: Abilities.CUD_CHEW, 2: Abilities.HARVEST, 3: Abilities.FUR_COAT },
@ -838,7 +838,7 @@ export const starterPassiveAbilities: StarterPassiveAbilities = {
[Species.CELESTEELA]: { 0: Abilities.HEATPROOF }, [Species.CELESTEELA]: { 0: Abilities.HEATPROOF },
[Species.KARTANA]: { 0: Abilities.TECHNICIAN }, [Species.KARTANA]: { 0: Abilities.TECHNICIAN },
[Species.GUZZLORD]: { 0: Abilities.POISON_HEAL }, [Species.GUZZLORD]: { 0: Abilities.POISON_HEAL },
[Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.PRISM_ARMOR }, [Species.NECROZMA]: { 0: Abilities.BEAST_BOOST, 1: Abilities.FULL_METAL_BODY, 2: Abilities.SHADOW_SHIELD, 3: Abilities.UNNERVE },
[Species.MAGEARNA]: { 0: Abilities.STEELY_SPIRIT, 1: Abilities.STEELY_SPIRIT }, [Species.MAGEARNA]: { 0: Abilities.STEELY_SPIRIT, 1: Abilities.STEELY_SPIRIT },
[Species.MARSHADOW]: { 0: Abilities.IRON_FIST }, [Species.MARSHADOW]: { 0: Abilities.IRON_FIST },
[Species.POIPOLE]: { 0: Abilities.LEVITATE }, [Species.POIPOLE]: { 0: Abilities.LEVITATE },

View File

@ -30,7 +30,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MovePhase } from "#app/phases/move-phase"; import { MovePhase } from "#app/phases/move-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase"; import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase"; import type { StatStageChangeCallback } from "#app/phases/stat-stage-change-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import i18next from "#app/plugins/i18n"; import i18next from "#app/plugins/i18n";
@ -1901,12 +1900,14 @@ export class TruantTag extends AbilityBattlerTag {
if (lastMove && lastMove.move !== Moves.NONE) { if (lastMove && lastMove.move !== Moves.NONE) {
(globalScene.getCurrentPhase() as MovePhase).cancel(); (globalScene.getCurrentPhase() as MovePhase).cancel();
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, passive)); // TODO: Ability displays should be handled by the ability
globalScene.queueAbilityDisplay(pokemon, passive, true);
globalScene.queueMessage( globalScene.queueMessage(
i18next.t("battlerTags:truantLapse", { i18next.t("battlerTags:truantLapse", {
pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokemonNameWithAffix: getPokemonNameWithAffix(pokemon),
}), }),
); );
globalScene.queueAbilityDisplay(pokemon, passive, false);
} }
return true; return true;

View File

@ -106,7 +106,6 @@ import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
import { SwitchPhase } from "#app/phases/switch-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 { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms"; import { SpeciesFormChangeRevertWeatherFormTrigger } from "../pokemon-forms";
import type { GameMode } from "#app/game-mode"; import type { GameMode } from "#app/game-mode";
import { applyChallenges, ChallengeType } from "../challenge"; import { applyChallenges, ChallengeType } from "../challenge";
@ -912,7 +911,7 @@ export default class Move implements Localizable {
]; ];
// ...and cannot enhance Pollen Puff when targeting an ally. // ...and cannot enhance Pollen Puff when targeting an ally.
const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && targets.includes(user.getAlly().getBattlerIndex()) const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && targets.includes(user.getAlly()?.getBattlerIndex())
return (!restrictSpread || !isMultiTarget) return (!restrictSpread || !isMultiTarget)
&& !this.isChargingMove() && !this.isChargingMove()
@ -1924,7 +1923,9 @@ export class PartyStatusCureAttr extends MoveEffectAttr {
pokemon.resetStatus(); pokemon.resetStatus();
pokemon.updateInfo(); pokemon.updateInfo();
} else { } else {
globalScene.unshiftPhase(new ShowAbilityPhase(pokemon.id, pokemon.getPassiveAbility()?.id === this.abilityCondition)); // TODO: Ability displays should be handled by the ability
globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, true);
globalScene.queueAbilityDisplay(pokemon, pokemon.getPassiveAbility()?.id === this.abilityCondition, false);
} }
} }
} }

View File

@ -985,12 +985,11 @@ function doTradeReceivedSequence(
function generateRandomTraderName() { function generateRandomTraderName() {
const length = TrainerType.YOUNGSTER - TrainerType.ACE_TRAINER + 1; const length = TrainerType.YOUNGSTER - TrainerType.ACE_TRAINER + 1;
// +1 avoids TrainerType.UNKNOWN // +1 avoids TrainerType.UNKNOWN
const trainerTypePool = i18next.t("trainersCommon:" + TrainerType[randInt(length) + 1], { returnObjects: true }); const classKey = `trainersCommon:${TrainerType[randInt(length) + 1]}`;
// Some trainers have 2 gendered pools, some do not // Some trainers have 2 gendered pools, some do not
const gender = randInt(2) === 0 ? "MALE" : "FEMALE"; const genderKey = i18next.exists(`${classKey}.MALE`) ? (randInt(2) === 0 ? ".MALE" : ".FEMALE") : "";
const trainerNameString = randSeedItem( const trainerNameKey = randSeedItem(Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })));
Object.values(trainerTypePool.hasOwnProperty(gender) ? trainerTypePool[gender] : trainerTypePool), const trainerNameString = i18next.t(`${classKey}${genderKey}.${trainerNameKey}`);
) as string;
// Some names have an '&' symbol and need to be trimmed to a single name instead of a double name // Some names have an '&' symbol and need to be trimmed to a single name instead of a double name
const trainerNames = trainerNameString.split(" & "); const trainerNames = trainerNameString.split(" & ");
return trainerNames[randInt(trainerNames.length)]; return trainerNames[randInt(trainerNames.length)];

View File

@ -40,7 +40,6 @@ import { TrainerType } from "#enums/trainer-type";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms"; import { SpeciesFormChangeRevertWeatherFormTrigger, SpeciesFormChangeWeatherTrigger } from "#app/data/pokemon-forms";
import { CommonAnimPhase } from "#app/phases/common-anim-phase"; import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { WeatherType } from "#enums/weather-type"; import { WeatherType } from "#enums/weather-type";
import { FieldEffectModifier } from "#app/modifier/modifier"; import { FieldEffectModifier } from "#app/modifier/modifier";
@ -378,7 +377,6 @@ export class Arena {
const isCherrimWithFlowerGift = p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM; const isCherrimWithFlowerGift = p.hasAbility(Abilities.FLOWER_GIFT) && p.species.speciesId === Species.CHERRIM;
if (isCastformWithForecast || isCherrimWithFlowerGift) { if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(p.getBattlerIndex());
globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger); globalScene.triggerPokemonFormChange(p, SpeciesFormChangeWeatherTrigger);
} }
}); });
@ -395,7 +393,6 @@ export class Arena {
p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM; p.hasAbility(Abilities.FLOWER_GIFT, false, true) && p.species.speciesId === Species.CHERRIM;
if (isCastformWithForecast || isCherrimWithFlowerGift) { if (isCastformWithForecast || isCherrimWithFlowerGift) {
new ShowAbilityPhase(p.getBattlerIndex());
return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger); return globalScene.triggerPokemonFormChange(p, SpeciesFormChangeRevertWeatherFormTrigger);
} }
}); });

View File

@ -33,14 +33,16 @@ export default class Trainer extends Phaser.GameObjects.Container {
public partyTemplateIndex: number; public partyTemplateIndex: number;
public name: string; public name: string;
public partnerName: string; public partnerName: string;
public nameKey: string;
public partnerNameKey: string | undefined;
public originalIndexes: { [key: number]: number } = {}; public originalIndexes: { [key: number]: number } = {};
constructor( constructor(
trainerType: TrainerType, trainerType: TrainerType,
variant: TrainerVariant, variant: TrainerVariant,
partyTemplateIndex?: number, partyTemplateIndex?: number,
name?: string, nameKey?: string,
partnerName?: string, partnerNameKey?: string,
trainerConfigOverride?: TrainerConfig, trainerConfigOverride?: TrainerConfig,
) { ) {
super(globalScene, -72, 80); super(globalScene, -72, 80);
@ -59,28 +61,41 @@ export default class Trainer extends Phaser.GameObjects.Container {
: Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)), : Utils.randSeedWeightedItem(this.config.partyTemplates.map((_, i) => i)),
this.config.partyTemplates.length - 1, this.config.partyTemplates.length - 1,
); );
if (i18next.exists("trainersCommon:" + TrainerType[trainerType], { returnObjects: true })) { const classKey = `trainersCommon:${TrainerType[trainerType]}`;
const namePool = i18next.t("trainersCommon:" + TrainerType[trainerType], { returnObjects: true }); if (i18next.exists(classKey, { returnObjects: true })) {
this.name = if (nameKey) {
name || this.nameKey = nameKey;
Utils.randSeedItem( } else {
Object.values( const genderKey = i18next.exists(`${classKey}.MALE`)
namePool.hasOwnProperty("MALE") ? variant === TrainerVariant.FEMALE
? namePool[variant === TrainerVariant.FEMALE ? "FEMALE" : "MALE"] ? ".FEMALE"
: namePool, : ".MALE"
), : "";
const trainerKey = Utils.randSeedItem(
Object.keys(i18next.t(`${classKey}${genderKey}`, { returnObjects: true })),
); );
this.nameKey = `${classKey}${genderKey}.${trainerKey}`;
}
this.name = i18next.t(this.nameKey);
if (variant === TrainerVariant.DOUBLE) { if (variant === TrainerVariant.DOUBLE) {
if (this.config.doubleOnly) { if (this.config.doubleOnly) {
if (partnerName) { if (partnerNameKey) {
this.partnerName = partnerName; this.partnerNameKey = partnerNameKey;
this.partnerName = i18next.t(this.partnerNameKey);
} else { } else {
[this.name, this.partnerName] = this.name.split(" & "); [this.name, this.partnerName] = this.name.split(" & ");
} }
} else { } else {
this.partnerName = const partnerGenderKey = i18next.exists(`${classKey}.FEMALE`) ? ".FEMALE" : "";
partnerName || const partnerTrainerKey = Utils.randSeedItem(
Utils.randSeedItem(Object.values(namePool.hasOwnProperty("FEMALE") ? namePool["FEMALE"] : namePool)); Object.keys(
i18next.t(`${classKey}${partnerGenderKey}`, {
returnObjects: true,
}),
),
);
this.partnerNameKey = `${classKey}${partnerGenderKey}.${partnerTrainerKey}`;
this.partnerName = i18next.t(this.partnerNameKey);
} }
} }
} }

View File

@ -2823,10 +2823,19 @@ const modifierPool: ModifierPool = {
modifierTypes.MYSTICAL_ROCK, modifierTypes.MYSTICAL_ROCK,
(party: Pokemon[]) => { (party: Pokemon[]) => {
return party.some(p => { return party.some(p => {
let isHoldingMax = false;
for (const i of p.getHeldItems()) {
if (i.type.id === "MYSTICAL_ROCK") {
isHoldingMax = i.getStackCount() === i.getMaxStackCount();
break;
}
}
if (!isHoldingMax) {
const moveset = p.getMoveset(true).map(m => m.moveId); const moveset = p.getMoveset(true).map(m => m.moveId);
const hasAbility = [ const hasAbility = [
Abilities.DRIZZLE, Abilities.DROUGHT,
Abilities.ORICHALCUM_PULSE, Abilities.ORICHALCUM_PULSE,
Abilities.DRIZZLE, Abilities.DRIZZLE,
Abilities.SAND_STREAM, Abilities.SAND_STREAM,
@ -2854,6 +2863,8 @@ const modifierPool: ModifierPool = {
].some(m => moveset.includes(m)); ].some(m => moveset.includes(m));
return hasAbility || hasMoves; return hasAbility || hasMoves;
}
return false;
}) })
? 10 ? 10
: 0; : 0;

View File

@ -45,6 +45,8 @@ export class GameOverPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
globalScene.hideAbilityBar();
// Failsafe if players somehow skip floor 200 in classic mode // Failsafe if players somehow skip floor 200 in classic mode
if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) { if (globalScene.gameMode.isClassic && globalScene.currentBattle.waveIndex > 200) {
this.isVictory = true; this.isVictory = true;

View File

@ -1,27 +1,12 @@
import { globalScene } from "#app/global-scene"; import { globalScene } from "#app/global-scene";
import type { BattlerIndex } from "#app/battle"; import { Phase } from "#app/phase";
import { PokemonPhase } from "./pokemon-phase";
export class HideAbilityPhase extends PokemonPhase {
private passive: boolean;
constructor(battlerIndex: BattlerIndex, passive = false) {
super(battlerIndex);
this.passive = passive;
}
export class HideAbilityPhase extends Phase {
start() { start() {
super.start(); super.start();
const pokemon = this.getPokemon();
if (pokemon) {
globalScene.abilityBar.hide().then(() => { globalScene.abilityBar.hide().then(() => {
this.end(); this.end();
}); });
} else {
this.end();
}
} }
} }

View File

@ -69,6 +69,7 @@ import type { Phase } from "#app/phase";
import { ShowAbilityPhase } from "./show-ability-phase"; import { ShowAbilityPhase } from "./show-ability-phase";
import { MovePhase } from "./move-phase"; import { MovePhase } from "./move-phase";
import { MoveEndPhase } from "./move-end-phase"; import { MoveEndPhase } from "./move-end-phase";
import { HideAbilityPhase } from "#app/phases/hide-ability-phase";
export class MoveEffectPhase extends PokemonPhase { export class MoveEffectPhase extends PokemonPhase {
public move: PokemonMove; public move: PokemonMove;
@ -326,12 +327,14 @@ export class MoveEffectPhase extends PokemonPhase {
? getMoveTargets(target, move.id).targets ? getMoveTargets(target, move.id).targets
: [user.getBattlerIndex()]; : [user.getBattlerIndex()];
if (!isReflecting) { if (!isReflecting) {
// TODO: Ability displays should be handled by the ability
queuedPhases.push( queuedPhases.push(
new ShowAbilityPhase( new ShowAbilityPhase(
target.getBattlerIndex(), target.getBattlerIndex(),
target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr), target.getPassiveAbility().hasAttr(ReflectStatusMoveAbAttr),
), ),
); );
queuedPhases.push(new HideAbilityPhase());
} }
queuedPhases.push( queuedPhases.push(

View File

@ -42,7 +42,6 @@ import { CommonAnimPhase } from "#app/phases/common-anim-phase";
import { MoveChargePhase } from "#app/phases/move-charge-phase"; import { MoveChargePhase } from "#app/phases/move-charge-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MoveEndPhase } from "#app/phases/move-end-phase"; import { MoveEndPhase } from "#app/phases/move-end-phase";
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
import { NumberHolder } from "#app/utils"; import { NumberHolder } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type"; import { ArenaTagType } from "#enums/arena-tag-type";
@ -535,11 +534,16 @@ export class MovePhase extends BattlePhase {
if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) { if (this.pokemon.hasAbilityWithAttr(BlockRedirectAbAttr)) {
redirectTarget.value = currentTarget; redirectTarget.value = currentTarget;
globalScene.unshiftPhase( // TODO: Ability displays should be handled by the ability
new ShowAbilityPhase( globalScene.queueAbilityDisplay(
this.pokemon.getBattlerIndex(), this.pokemon,
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr), this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
), true,
);
globalScene.queueAbilityDisplay(
this.pokemon,
this.pokemon.getPassiveAbility().hasAttr(BlockRedirectAbAttr),
false,
); );
} }

View File

@ -35,7 +35,7 @@ export class ShowAbilityPhase extends PokemonPhase {
// If the bar is already out, hide it before showing the new one // If the bar is already out, hide it before showing the new one
if (globalScene.abilityBar.isVisible()) { if (globalScene.abilityBar.isVisible()) {
globalScene.unshiftPhase(new HideAbilityPhase(this.battlerIndex, this.passive)); globalScene.unshiftPhase(new HideAbilityPhase());
globalScene.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive)); globalScene.unshiftPhase(new ShowAbilityPhase(this.battlerIndex, this.passive));
return this.end(); return this.end();
} }

View File

@ -28,6 +28,8 @@ export class TurnEndPhase extends FieldPhase {
globalScene.currentBattle.incrementTurn(); globalScene.currentBattle.incrementTurn();
globalScene.eventTarget.dispatchEvent(new TurnEndEvent(globalScene.currentBattle.turn)); globalScene.eventTarget.dispatchEvent(new TurnEndEvent(globalScene.currentBattle.turn));
globalScene.hideAbilityBar();
const handlePokemon = (pokemon: Pokemon) => { const handlePokemon = (pokemon: Pokemon) => {
if (!pokemon.switchOutStatus) { if (!pokemon.switchOutStatus) {
pokemon.lapseTags(BattlerTagLapseType.TURN_END); pokemon.lapseTags(BattlerTagLapseType.TURN_END);

View File

@ -5,8 +5,8 @@ export default class TrainerData {
public trainerType: TrainerType; public trainerType: TrainerType;
public variant: TrainerVariant; public variant: TrainerVariant;
public partyTemplateIndex: number; public partyTemplateIndex: number;
public name: string; public nameKey: string;
public partnerName: string; public partnerNameKey: string | undefined;
constructor(source: Trainer | any) { constructor(source: Trainer | any) {
const sourceTrainer = source instanceof Trainer ? (source as Trainer) : null; const sourceTrainer = source instanceof Trainer ? (source as Trainer) : null;
@ -17,11 +17,11 @@ export default class TrainerData {
? TrainerVariant.FEMALE ? TrainerVariant.FEMALE
: TrainerVariant.DEFAULT; : TrainerVariant.DEFAULT;
this.partyTemplateIndex = source.partyMemberTemplateIndex; this.partyTemplateIndex = source.partyMemberTemplateIndex;
this.name = source.name; this.nameKey = source.nameKey;
this.partnerName = source.partnerName; this.partnerNameKey = source.partnerNameKey;
} }
toTrainer(): Trainer { toTrainer(): Trainer {
return new Trainer(this.trainerType, this.variant, this.partyTemplateIndex, this.name, this.partnerName); return new Trainer(this.trainerType, this.variant, this.partyTemplateIndex, this.nameKey, this.partnerNameKey);
} }
} }

View File

@ -9,7 +9,6 @@ import { StatusEffect } from "#enums/status-effect";
import GameManager from "#test/testUtils/gameManager"; import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { BattlerIndex } from "#app/battle";
describe("Abilities - Parental Bond", () => { describe("Abilities - Parental Bond", () => {
let phaserGame: Phaser.Game; let phaserGame: Phaser.Game;
@ -427,21 +426,4 @@ describe("Abilities - Parental Bond", () => {
// TODO: Update hit count to 1 once Future Sight is fixed to not activate abilities if user is off the field // TODO: Update hit count to 1 once Future Sight is fixed to not activate abilities if user is off the field
expect(enemyPokemon.damageAndUpdate).toHaveBeenCalledTimes(2); expect(enemyPokemon.damageAndUpdate).toHaveBeenCalledTimes(2);
}); });
it("should not allow Pollen Puff to heal ally more than once", async () => {
game.override.battleType("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]);
await game.classicMode.startBattle([Species.BULBASAUR, Species.OMANYTE]);
const [, rightPokemon] = game.scene.getPlayerField();
rightPokemon.damageAndUpdate(rightPokemon.hp - 1);
game.move.select(Moves.POLLEN_PUFF, 0, BattlerIndex.PLAYER_2);
game.move.select(Moves.ENDURE, 1);
await game.toNextTurn();
// Pollen Puff heals with a ratio of 0.5, as long as Pollen Puff triggers only once the pokemon will always be <= (0.5 * Max HP) + 1
expect(rightPokemon.hp).toBeLessThanOrEqual(0.5 * rightPokemon.getMaxHp() + 1);
});
}); });

View File

@ -0,0 +1,64 @@
import { BattlerIndex } from "#app/battle";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/testUtils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
describe("Moves - Pollen Puff", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.POLLEN_PUFF])
.ability(Abilities.BALL_FETCH)
.battleType("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH);
});
it("should not heal more than once when the user has a source of multi-hit", async () => {
game.override.battleType("double").moveset([Moves.POLLEN_PUFF, Moves.ENDURE]).ability(Abilities.PARENTAL_BOND);
await game.classicMode.startBattle([Species.BULBASAUR, Species.OMANYTE]);
const [_, rightPokemon] = game.scene.getPlayerField();
rightPokemon.damageAndUpdate(rightPokemon.hp - 1);
game.move.select(Moves.POLLEN_PUFF, 0, BattlerIndex.PLAYER_2);
game.move.select(Moves.ENDURE, 1);
await game.phaseInterceptor.to("BerryPhase");
// Pollen Puff heals with a ratio of 0.5, as long as Pollen Puff triggers only once the pokemon will always be <= (0.5 * Max HP) + 1
expect(rightPokemon.hp).toBeLessThanOrEqual(0.5 * rightPokemon.getMaxHp() + 1);
});
it("should damage an enemy multiple times when the user has a source of multi-hit", async () => {
game.override.moveset([Moves.POLLEN_PUFF]).ability(Abilities.PARENTAL_BOND).enemyLevel(100);
await game.classicMode.startBattle([Species.MAGIKARP]);
const target = game.scene.getEnemyPokemon()!;
game.move.select(Moves.POLLEN_PUFF);
await game.phaseInterceptor.to("BerryPhase");
expect(target.battleData.hitCount).toBe(2);
});
});