Merge branch 'beta' into hebrew-pr
1595
package-lock.json
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "pokemon-rogue-battle",
|
"name": "pokemon-rogue-battle",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.7",
|
"version": "1.8.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"i18next-korean-postposition-processor": "^1.0.0",
|
"i18next-korean-postposition-processor": "^1.0.0",
|
||||||
"json-stable-stringify": "^1.2.0",
|
"json-stable-stringify": "^1.2.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"phaser": "^3.88.2",
|
"phaser": "^3.70.0",
|
||||||
"phaser3-rex-plugins": "^1.80.14"
|
"phaser3-rex-plugins": "^1.80.14"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
BIN
public/audio/bgm/battle_rival_3_afd.mp3
Normal file
BIN
public/audio/bgm/title_afd.mp3
Normal file
BIN
public/images/events/aprf25-de.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/images/events/aprf25-en.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
public/images/events/aprf25-es-ES.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
public/images/events/aprf25-es-MX.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
public/images/events/aprf25-fr.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/images/events/aprf25-it.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
public/images/events/aprf25-ja.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/images/events/aprf25-ko.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
public/images/events/aprf25-pt-BR.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
public/images/events/aprf25-zh-CN.png
Normal file
After Width: | Height: | Size: 32 KiB |
@ -1 +1 @@
|
|||||||
Subproject commit e599780a369f87a96ab0469a8908cea86628145f
|
Subproject commit e98f0eb9c2022bc78b53f0444424c636498e725a
|
@ -170,6 +170,7 @@ import { StatusEffect } from "#enums/status-effect";
|
|||||||
import { 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";
|
||||||
|
|
||||||
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
|
||||||
|
|
||||||
@ -2268,6 +2269,9 @@ export default class BattleScene extends SceneBase {
|
|||||||
if (bgmName === undefined) {
|
if (bgmName === undefined) {
|
||||||
bgmName = this.currentBattle?.getBgmOverride() || this.arena?.bgm;
|
bgmName = this.currentBattle?.getBgmOverride() || this.arena?.bgm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bgmName = timedEventManager.getEventBgmReplacement(bgmName);
|
||||||
|
|
||||||
if (this.bgm && bgmName === this.bgm.key) {
|
if (this.bgm && bgmName === this.bgm.key) {
|
||||||
if (!this.bgm.isPlaying) {
|
if (!this.bgm.isPlaying) {
|
||||||
this.bgm.play({
|
this.bgm.play({
|
||||||
@ -2660,6 +2664,10 @@ export default class BattleScene extends SceneBase {
|
|||||||
return 41.42;
|
return 41.42;
|
||||||
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)
|
||||||
|
return 47.66;
|
||||||
|
case "battle_rival_3_afd": // Andr06 - Final N Battle Remix (AFD)
|
||||||
|
return 49.147;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2929,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)
|
||||||
*/
|
*/
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
export const PLAYER_PARTY_MAX_SIZE: number = 6;
|
||||||
|
|
||||||
/** Whether to use seasonal splash messages in general */
|
/** Whether to use seasonal splash messages in general */
|
||||||
export const USE_SEASONAL_SPLASH_MESSAGES: boolean = false;
|
export const USE_SEASONAL_SPLASH_MESSAGES: boolean = true;
|
||||||
|
|
||||||
/** Name of the session ID cookie */
|
/** Name of the session ID cookie */
|
||||||
export const SESSION_ID_COOKIE_NAME: string = "pokerogue_sessionId";
|
export const SESSION_ID_COOKIE_NAME: string = "pokerogue_sessionId";
|
||||||
|
@ -854,7 +854,8 @@ export class PostDefendStatStageChangeAbAttr extends PostDefendAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.allOthers) {
|
if (this.allOthers) {
|
||||||
const otherPokemon = pokemon.getAlly() ? pokemon.getOpponents().concat([ pokemon.getAlly() ]) : pokemon.getOpponents();
|
const ally = pokemon.getAlly();
|
||||||
|
const otherPokemon = !Utils.isNullOrUndefined(ally) ? pokemon.getOpponents().concat([ ally ]) : pokemon.getOpponents();
|
||||||
for (const other of otherPokemon) {
|
for (const other of otherPokemon) {
|
||||||
globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages));
|
globalScene.unshiftPhase(new StatStageChangePhase((other).getBattlerIndex(), false, [ this.stat ], this.stages));
|
||||||
}
|
}
|
||||||
@ -2460,12 +2461,12 @@ export class PostSummonAllyHealAbAttr extends PostSummonAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||||
return pokemon.getAlly()?.isActive(true);
|
return pokemon.getAlly()?.isActive(true) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||||
const target = pokemon.getAlly();
|
const target = pokemon.getAlly();
|
||||||
if (!simulated) {
|
if (!simulated && !Utils.isNullOrUndefined(target)) {
|
||||||
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(),
|
globalScene.unshiftPhase(new PokemonHealPhase(target.getBattlerIndex(),
|
||||||
Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim));
|
Utils.toDmgValue(pokemon.getMaxHp() / this.healRatio), i18next.t("abilityTriggers:postSummonAllyHeal", { pokemonNameWithAffix: getPokemonNameWithAffix(target), pokemonName: pokemon.name }), true, !this.showAnim));
|
||||||
}
|
}
|
||||||
@ -2486,12 +2487,12 @@ export class PostSummonClearAllyStatStagesAbAttr extends PostSummonAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
override canApplyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): boolean {
|
||||||
return pokemon.getAlly()?.isActive(true);
|
return pokemon.getAlly()?.isActive(true) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||||
const target = pokemon.getAlly();
|
const target = pokemon.getAlly();
|
||||||
if (!simulated) {
|
if (!simulated && !Utils.isNullOrUndefined(target)) {
|
||||||
for (const s of BATTLE_STATS) {
|
for (const s of BATTLE_STATS) {
|
||||||
target.setStatStage(s, 0);
|
target.setStatStage(s, 0);
|
||||||
}
|
}
|
||||||
@ -2712,7 +2713,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ally = pokemon.getAlly();
|
const ally = pokemon.getAlly();
|
||||||
if (!ally || ally.getStatStages().every(s => s === 0)) {
|
if (Utils.isNullOrUndefined(ally) || ally.getStatStages().every(s => s === 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2721,7 +2722,7 @@ export class PostSummonCopyAllyStatsAbAttr extends PostSummonAbAttr {
|
|||||||
|
|
||||||
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
override applyPostSummon(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||||
const ally = pokemon.getAlly();
|
const ally = pokemon.getAlly();
|
||||||
if (!simulated) {
|
if (!simulated && !Utils.isNullOrUndefined(ally)) {
|
||||||
for (const s of BATTLE_STATS) {
|
for (const s of BATTLE_STATS) {
|
||||||
pokemon.setStatStage(s, ally.getStatStage(s));
|
pokemon.setStatStage(s, ally.getStatStage(s));
|
||||||
}
|
}
|
||||||
@ -2822,7 +2823,7 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
|
|||||||
private ability: Abilities;
|
private ability: Abilities;
|
||||||
|
|
||||||
constructor(ability: Abilities) {
|
constructor(ability: Abilities) {
|
||||||
super(false);
|
super(true);
|
||||||
|
|
||||||
this.ability = ability;
|
this.ability = ability;
|
||||||
}
|
}
|
||||||
@ -2846,7 +2847,6 @@ export class PostSummonFormChangeByWeatherAbAttr extends PostSummonAbAttr {
|
|||||||
if (!simulated) {
|
if (!simulated) {
|
||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger);
|
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeWeatherTrigger);
|
||||||
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeRevertWeatherFormTrigger);
|
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeRevertWeatherFormTrigger);
|
||||||
globalScene.queueAbilityDisplay(pokemon, passive, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2867,8 +2867,9 @@ export class CommanderAbAttr extends AbAttr {
|
|||||||
// another Pokemon, this effect cannot apply.
|
// another Pokemon, this effect cannot apply.
|
||||||
|
|
||||||
// TODO: Should this work with X + Dondozo fusions?
|
// TODO: Should this work with X + Dondozo fusions?
|
||||||
return globalScene.currentBattle?.double && pokemon.getAlly()?.species.speciesId === Species.DONDOZO
|
const ally = pokemon.getAlly();
|
||||||
&& !(pokemon.getAlly().isFainted() || pokemon.getAlly().getTag(BattlerTagType.COMMANDED));
|
return globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.species.speciesId === Species.DONDOZO
|
||||||
|
&& !(ally.isFainted() || ally.getTag(BattlerTagType.COMMANDED));
|
||||||
}
|
}
|
||||||
|
|
||||||
override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void {
|
override apply(pokemon: Pokemon, passive: boolean, simulated: boolean, cancelled: null, args: any[]): void {
|
||||||
@ -2878,7 +2879,7 @@ export class CommanderAbAttr extends AbAttr {
|
|||||||
// Play an animation of the source jumping into the ally Dondozo's mouth
|
// Play an animation of the source jumping into the ally Dondozo's mouth
|
||||||
globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.COMMANDER_APPLY);
|
globalScene.triggerPokemonBattleAnim(pokemon, PokemonAnimType.COMMANDER_APPLY);
|
||||||
// Apply boosts from this effect to the ally Dondozo
|
// Apply boosts from this effect to the ally Dondozo
|
||||||
pokemon.getAlly().addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id);
|
pokemon.getAlly()?.addTag(BattlerTagType.COMMANDED, 0, Moves.NONE, pokemon.id);
|
||||||
// Cancel the source Pokemon's next move (if a move is queued)
|
// Cancel the source Pokemon's next move (if a move is queued)
|
||||||
globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon);
|
globalScene.tryRemovePhase((phase) => phase instanceof MovePhase && phase.pokemon === pokemon);
|
||||||
}
|
}
|
||||||
@ -4078,7 +4079,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr {
|
|||||||
*/
|
*/
|
||||||
export class PostTurnResetStatusAbAttr extends PostTurnAbAttr {
|
export class PostTurnResetStatusAbAttr extends PostTurnAbAttr {
|
||||||
private allyTarget: boolean;
|
private allyTarget: boolean;
|
||||||
private target: Pokemon;
|
private target: Pokemon | undefined;
|
||||||
|
|
||||||
constructor(allyTarget = false) {
|
constructor(allyTarget = false) {
|
||||||
super(true);
|
super(true);
|
||||||
@ -4091,11 +4092,11 @@ export class PostTurnResetStatusAbAttr extends PostTurnAbAttr {
|
|||||||
} else {
|
} else {
|
||||||
this.target = pokemon;
|
this.target = pokemon;
|
||||||
}
|
}
|
||||||
return !Utils.isNullOrUndefined(this.target.status);
|
return !Utils.isNullOrUndefined(this.target?.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
override applyPostTurn(pokemon: Pokemon, passive: boolean, simulated: boolean, args: any[]): void {
|
||||||
if (!simulated && this.target.status) {
|
if (!simulated && this.target?.status) {
|
||||||
globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target)));
|
globalScene.queueMessage(getStatusEffectHealText(this.target.status?.effect, getPokemonNameWithAffix(this.target)));
|
||||||
this.target.resetStatus(false);
|
this.target.resetStatus(false);
|
||||||
this.target.updateInfo();
|
this.target.updateInfo();
|
||||||
@ -5441,6 +5442,8 @@ class ForceSwitchOutHelper {
|
|||||||
* It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss
|
* It will not flee if it is a Mystery Encounter with fleeing disabled (checked in `getSwitchOutCondition()`) or if it is a wave 10x wild boss
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
|
const allyPokemon = switchOutTarget.getAlly();
|
||||||
|
|
||||||
if (!globalScene.currentBattle.waveIndex || globalScene.currentBattle.waveIndex % 10 === 0) {
|
if (!globalScene.currentBattle.waveIndex || globalScene.currentBattle.waveIndex % 10 === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -5448,14 +5451,12 @@ class ForceSwitchOutHelper {
|
|||||||
if (switchOutTarget.hp > 0) {
|
if (switchOutTarget.hp > 0) {
|
||||||
switchOutTarget.leaveField(false);
|
switchOutTarget.leaveField(false);
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
|
globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
|
||||||
|
if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) {
|
||||||
if (globalScene.currentBattle.double) {
|
|
||||||
const allyPokemon = switchOutTarget.getAlly();
|
|
||||||
globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon);
|
globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!switchOutTarget.getAlly()?.isActive(true)) {
|
if (!allyPokemon?.isActive(true)) {
|
||||||
globalScene.clearEnemyHeldItemModifiers();
|
globalScene.clearEnemyHeldItemModifiers();
|
||||||
|
|
||||||
if (switchOutTarget.hp) {
|
if (switchOutTarget.hp) {
|
||||||
@ -6441,9 +6442,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 => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
.conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => (p.getAlly()?.hasAbility(a) ?? false)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
||||||
new Ability(Abilities.MINUS, 3)
|
new Ability(Abilities.MINUS, 3)
|
||||||
.conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => p.getAlly().hasAbility(a)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
.conditionalAttr(p => globalScene.currentBattle.double && [ Abilities.PLUS, Abilities.MINUS ].some(a => (p.getAlly()?.hasAbility(a) ?? false)), StatMultiplierAbAttr, Stat.SPATK, 1.5),
|
||||||
new Ability(Abilities.FORECAST, 3)
|
new Ability(Abilities.FORECAST, 3)
|
||||||
.uncopiable()
|
.uncopiable()
|
||||||
.unreplaceable()
|
.unreplaceable()
|
||||||
@ -6670,7 +6671,7 @@ export function initAbilities() {
|
|||||||
.attr(PostDefendMoveDisableAbAttr, 30)
|
.attr(PostDefendMoveDisableAbAttr, 30)
|
||||||
.bypassFaint(),
|
.bypassFaint(),
|
||||||
new Ability(Abilities.HEALER, 5)
|
new Ability(Abilities.HEALER, 5)
|
||||||
.conditionalAttr(pokemon => pokemon.getAlly() && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true),
|
.conditionalAttr(pokemon => !Utils.isNullOrUndefined(pokemon.getAlly()) && Utils.randSeedInt(10) < 3, PostTurnResetStatusAbAttr, true),
|
||||||
new Ability(Abilities.FRIEND_GUARD, 5)
|
new Ability(Abilities.FRIEND_GUARD, 5)
|
||||||
.attr(AlliedFieldDamageReductionAbAttr, 0.75)
|
.attr(AlliedFieldDamageReductionAbAttr, 0.75)
|
||||||
.ignorable(),
|
.ignorable(),
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 },
|
||||||
|
@ -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;
|
||||||
|
@ -1272,23 +1272,22 @@ function checkSpeciesValidForChallenge(species: PokemonSpecies, props: DexAttrPr
|
|||||||
if (soft && isValidForChallenge.value) {
|
if (soft && isValidForChallenge.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
pokemonFormChanges[species.speciesId].forEach(f1 => {
|
|
||||||
// Exclude form changes that require the mon to be on the field to begin with,
|
const result = pokemonFormChanges[species.speciesId].some(f1 => {
|
||||||
// such as Castform
|
// Exclude form changes that require the mon to be on the field to begin with
|
||||||
if (!("item" in f1)) {
|
if (!("item" in f1.trigger)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
species.forms.forEach((f2, formIndex) => {
|
|
||||||
if (f1.formKey === f2.formKey) {
|
|
||||||
const formProps = { ...props };
|
|
||||||
formProps.formIndex = formIndex;
|
|
||||||
const isFormValidForChallenge = new Utils.BooleanHolder(true);
|
|
||||||
applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps);
|
|
||||||
if (isFormValidForChallenge.value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return species.forms.some((f2, formIndex) => {
|
||||||
|
if (f1.formKey === f2.formKey) {
|
||||||
|
const formProps = { ...props, formIndex };
|
||||||
|
const isFormValidForChallenge = new Utils.BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.STARTER_CHOICE, species, isFormValidForChallenge, formProps);
|
||||||
|
return isFormValidForChallenge.value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -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";
|
||||||
@ -789,9 +788,9 @@ export default class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power);
|
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, target, this, simulated, power);
|
||||||
|
const ally = source.getAlly();
|
||||||
if (source.getAlly()) {
|
if (!Utils.isNullOrUndefined(ally)) {
|
||||||
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, source.getAlly(), target, this, simulated, power);
|
applyPreAttackAbAttrs(AllyMoveCategoryPowerBoostAbAttr, ally, target, this, simulated, power);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldAuras = new Set(
|
const fieldAuras = new Set(
|
||||||
@ -912,7 +911,8 @@ 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 ally = user.getAlly();
|
||||||
|
const exceptPollenPuffAlly: boolean = this.id === Moves.POLLEN_PUFF && !Utils.isNullOrUndefined(ally) && targets.includes(ally.getBattlerIndex())
|
||||||
|
|
||||||
return (!restrictSpread || !isMultiTarget)
|
return (!restrictSpread || !isMultiTarget)
|
||||||
&& !this.isChargingMove()
|
&& !this.isChargingMove()
|
||||||
@ -1924,7 +1924,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1945,7 +1947,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
|||||||
const targetAlly = target.getAlly();
|
const targetAlly = target.getAlly();
|
||||||
const cancelled = new Utils.BooleanHolder(false);
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
|
||||||
if (targetAlly) {
|
if (!Utils.isNullOrUndefined(targetAlly)) {
|
||||||
applyAbAttrs(BlockNonDirectDamageAbAttr, targetAlly, cancelled);
|
applyAbAttrs(BlockNonDirectDamageAbAttr, targetAlly, cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1958,7 +1960,7 @@ export class FlameBurstAttr extends MoveEffectAttr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||||
return target.getAlly() ? -5 : 0;
|
return !Utils.isNullOrUndefined(target.getAlly()) ? -5 : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4356,10 +4358,10 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr {
|
|||||||
const userAlly = user.getAlly();
|
const userAlly = user.getAlly();
|
||||||
const enemyAlly = enemy?.getAlly();
|
const enemyAlly = enemy?.getAlly();
|
||||||
|
|
||||||
if (userAlly && userAlly.turnData.acted) {
|
if (!Utils.isNullOrUndefined(userAlly) && userAlly.turnData.acted) {
|
||||||
pokemonActed.push(userAlly);
|
pokemonActed.push(userAlly);
|
||||||
}
|
}
|
||||||
if (enemyAlly && enemyAlly.turnData.acted) {
|
if (!Utils.isNullOrUndefined(enemyAlly) && enemyAlly.turnData.acted) {
|
||||||
pokemonActed.push(enemyAlly);
|
pokemonActed.push(enemyAlly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6161,9 +6163,8 @@ export class RevivalBlessingAttr extends MoveEffectAttr {
|
|||||||
pokemon.resetStatus();
|
pokemon.resetStatus();
|
||||||
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
pokemon.heal(Math.min(Utils.toDmgValue(0.5 * pokemon.getMaxHp()), pokemon.getMaxHp()));
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true);
|
globalScene.queueMessage(i18next.t("moveTriggers:revivalBlessing", { pokemonName: getPokemonNameWithAffix(pokemon) }), 0, true);
|
||||||
|
|
||||||
if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1) {
|
|
||||||
const allyPokemon = user.getAlly();
|
const allyPokemon = user.getAlly();
|
||||||
|
if (globalScene.currentBattle.double && globalScene.getEnemyParty().length > 1 && !Utils.isNullOrUndefined(allyPokemon)) {
|
||||||
// Handle cases where revived pokemon needs to get switched in on same turn
|
// Handle cases where revived pokemon needs to get switched in on same turn
|
||||||
if (allyPokemon.isFainted() || allyPokemon === pokemon) {
|
if (allyPokemon.isFainted() || allyPokemon === pokemon) {
|
||||||
// Enemy switch phase should be removed and replaced with the revived pkmn switching in
|
// Enemy switch phase should be removed and replaced with the revived pkmn switching in
|
||||||
@ -6342,18 +6343,19 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allyPokemon = switchOutTarget.getAlly();
|
||||||
|
|
||||||
if (switchOutTarget.hp > 0) {
|
if (switchOutTarget.hp > 0) {
|
||||||
switchOutTarget.leaveField(false);
|
switchOutTarget.leaveField(false);
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
|
globalScene.queueMessage(i18next.t("moveTriggers:fled", { pokemonName: getPokemonNameWithAffix(switchOutTarget) }), null, true, 500);
|
||||||
|
|
||||||
// in double battles redirect potential moves off fled pokemon
|
// in double battles redirect potential moves off fled pokemon
|
||||||
if (globalScene.currentBattle.double) {
|
if (globalScene.currentBattle.double && !Utils.isNullOrUndefined(allyPokemon)) {
|
||||||
const allyPokemon = switchOutTarget.getAlly();
|
|
||||||
globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon);
|
globalScene.redirectPokemonMoves(switchOutTarget, allyPokemon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!switchOutTarget.getAlly()?.isActive(true)) {
|
if (!allyPokemon?.isActive(true)) {
|
||||||
globalScene.clearEnemyHeldItemModifiers();
|
globalScene.clearEnemyHeldItemModifiers();
|
||||||
|
|
||||||
if (switchOutTarget.hp) {
|
if (switchOutTarget.hp) {
|
||||||
@ -7029,7 +7031,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
|
|||||||
const firstTarget = globalScene.getField()[moveTargets[0]];
|
const firstTarget = globalScene.getField()[moveTargets[0]];
|
||||||
if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) {
|
if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) {
|
||||||
const ally = firstTarget.getAlly();
|
const ally = firstTarget.getAlly();
|
||||||
if (ally.isActive()) { // ally exists, is not dead and can sponge the blast
|
if (!Utils.isNullOrUndefined(ally) && ally.isActive()) { // ally exists, is not dead and can sponge the blast
|
||||||
moveTargets = [ ally.getBattlerIndex() ];
|
moveTargets = [ ally.getBattlerIndex() ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7403,10 +7405,11 @@ export class AbilityCopyAttr extends MoveEffectAttr {
|
|||||||
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
||||||
|
|
||||||
user.setTempAbility(target.getAbility());
|
user.setTempAbility(target.getAbility());
|
||||||
|
const ally = user.getAlly();
|
||||||
|
|
||||||
if (this.copyToPartner && globalScene.currentBattle?.double && user.getAlly().hp) {
|
if (this.copyToPartner && globalScene.currentBattle?.double && !Utils.isNullOrUndefined(ally) && ally.hp) { // TODO is this the best way to check that the ally is active?
|
||||||
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(user.getAlly()), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
globalScene.queueMessage(i18next.t("moveTriggers:copiedTargetAbility", { pokemonName: getPokemonNameWithAffix(ally), targetName: getPokemonNameWithAffix(target), abilityName: allAbilities[target.getAbility().id].name }));
|
||||||
user.getAlly().setTempAbility(target.getAbility());
|
ally.setTempAbility(target.getAbility());
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -7414,9 +7417,10 @@ export class AbilityCopyAttr extends MoveEffectAttr {
|
|||||||
|
|
||||||
getCondition(): MoveConditionFunc {
|
getCondition(): MoveConditionFunc {
|
||||||
return (user, target, move) => {
|
return (user, target, move) => {
|
||||||
|
const ally = user.getAlly();
|
||||||
let ret = target.getAbility().isCopiable && user.getAbility().isReplaceable;
|
let ret = target.getAbility().isCopiable && user.getAbility().isReplaceable;
|
||||||
if (this.copyToPartner && globalScene.currentBattle?.double) {
|
if (this.copyToPartner && globalScene.currentBattle?.double) {
|
||||||
ret = ret && (!user.getAlly().hp || user.getAlly().getAbility().isReplaceable);
|
ret = ret && (!ally?.hp || ally?.getAbility().isReplaceable);
|
||||||
} else {
|
} else {
|
||||||
ret = ret && user.getAbility().id !== target.getAbility().id;
|
ret = ret && user.getAbility().id !== target.getAbility().id;
|
||||||
}
|
}
|
||||||
@ -8187,6 +8191,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT
|
|||||||
|
|
||||||
let set: Pokemon[] = [];
|
let set: Pokemon[] = [];
|
||||||
let multiple = false;
|
let multiple = false;
|
||||||
|
const ally: Pokemon | undefined = user.getAlly();
|
||||||
|
|
||||||
switch (moveTarget) {
|
switch (moveTarget) {
|
||||||
case MoveTarget.USER:
|
case MoveTarget.USER:
|
||||||
@ -8197,7 +8202,7 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT
|
|||||||
case MoveTarget.OTHER:
|
case MoveTarget.OTHER:
|
||||||
case MoveTarget.ALL_NEAR_OTHERS:
|
case MoveTarget.ALL_NEAR_OTHERS:
|
||||||
case MoveTarget.ALL_OTHERS:
|
case MoveTarget.ALL_OTHERS:
|
||||||
set = (opponents.concat([ user.getAlly() ]));
|
set = !Utils.isNullOrUndefined(ally) ? (opponents.concat([ ally ])) : opponents;
|
||||||
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
|
multiple = moveTarget === MoveTarget.ALL_NEAR_OTHERS || moveTarget === MoveTarget.ALL_OTHERS;
|
||||||
break;
|
break;
|
||||||
case MoveTarget.NEAR_ENEMY:
|
case MoveTarget.NEAR_ENEMY:
|
||||||
@ -8214,21 +8219,22 @@ export function getMoveTargets(user: Pokemon, move: Moves, replaceTarget?: MoveT
|
|||||||
return { targets: [ -1 as BattlerIndex ], multiple: false };
|
return { targets: [ -1 as BattlerIndex ], multiple: false };
|
||||||
case MoveTarget.NEAR_ALLY:
|
case MoveTarget.NEAR_ALLY:
|
||||||
case MoveTarget.ALLY:
|
case MoveTarget.ALLY:
|
||||||
set = [ user.getAlly() ];
|
set = !Utils.isNullOrUndefined(ally) ? [ ally ] : [];
|
||||||
break;
|
break;
|
||||||
case MoveTarget.USER_OR_NEAR_ALLY:
|
case MoveTarget.USER_OR_NEAR_ALLY:
|
||||||
case MoveTarget.USER_AND_ALLIES:
|
case MoveTarget.USER_AND_ALLIES:
|
||||||
case MoveTarget.USER_SIDE:
|
case MoveTarget.USER_SIDE:
|
||||||
set = [ user, user.getAlly() ];
|
set = !Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ];
|
||||||
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
multiple = moveTarget !== MoveTarget.USER_OR_NEAR_ALLY;
|
||||||
break;
|
break;
|
||||||
case MoveTarget.ALL:
|
case MoveTarget.ALL:
|
||||||
case MoveTarget.BOTH_SIDES:
|
case MoveTarget.BOTH_SIDES:
|
||||||
set = [ user, user.getAlly() ].concat(opponents);
|
set = (!Utils.isNullOrUndefined(ally) ? [ user, ally ] : [ user ]).concat(opponents);
|
||||||
multiple = true;
|
multiple = true;
|
||||||
break;
|
break;
|
||||||
case MoveTarget.CURSE:
|
case MoveTarget.CURSE:
|
||||||
set = user.getTypes(true).includes(PokemonType.GHOST) ? (opponents.concat([ user.getAlly() ])) : [ user ];
|
const extraTargets = !Utils.isNullOrUndefined(ally) ? [ ally ] : [];
|
||||||
|
set = user.getTypes(true).includes(PokemonType.GHOST) ? (opponents.concat(extraTargets)) : [ user ];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10122,7 +10128,7 @@ export function initMoves() {
|
|||||||
.attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) })
|
.attr(StatStageChangeAttr, [ Stat.DEF, Stat.SPDEF ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) })
|
||||||
.ignoresSubstitute()
|
.ignoresSubstitute()
|
||||||
.target(MoveTarget.USER_AND_ALLIES)
|
.target(MoveTarget.USER_AND_ALLIES)
|
||||||
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p.hasAbility(a, false)))),
|
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p?.hasAbility(a, false)))),
|
||||||
new StatusMove(Moves.HAPPY_HOUR, PokemonType.NORMAL, -1, 30, -1, 0, 6) // No animation
|
new StatusMove(Moves.HAPPY_HOUR, PokemonType.NORMAL, -1, 30, -1, 0, 6) // No animation
|
||||||
.attr(AddArenaTagAttr, ArenaTagType.HAPPY_HOUR, null, true)
|
.attr(AddArenaTagAttr, ArenaTagType.HAPPY_HOUR, null, true)
|
||||||
.target(MoveTarget.USER_SIDE),
|
.target(MoveTarget.USER_SIDE),
|
||||||
@ -10312,7 +10318,7 @@ export function initMoves() {
|
|||||||
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) })
|
.attr(StatStageChangeAttr, [ Stat.ATK, Stat.SPATK ], 1, false, { condition: (user, target, move) => !![ Abilities.PLUS, Abilities.MINUS ].find(a => target.hasAbility(a, false)) })
|
||||||
.ignoresSubstitute()
|
.ignoresSubstitute()
|
||||||
.target(MoveTarget.USER_AND_ALLIES)
|
.target(MoveTarget.USER_AND_ALLIES)
|
||||||
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p.hasAbility(a, false)))),
|
.condition((user, target, move) => !![ user, user.getAlly() ].filter(p => p?.isActive()).find(p => !![ Abilities.PLUS, Abilities.MINUS ].find(a => p?.hasAbility(a, false)))),
|
||||||
new AttackMove(Moves.THROAT_CHOP, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7)
|
new AttackMove(Moves.THROAT_CHOP, PokemonType.DARK, MoveCategory.PHYSICAL, 80, 100, 15, 100, 0, 7)
|
||||||
.attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED),
|
.attr(AddBattlerTagAttr, BattlerTagType.THROAT_CHOPPED),
|
||||||
new AttackMove(Moves.POLLEN_PUFF, PokemonType.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
|
new AttackMove(Moves.POLLEN_PUFF, PokemonType.BUG, MoveCategory.SPECIAL, 90, 100, 15, -1, 0, 7)
|
||||||
|
@ -37,6 +37,7 @@ import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode
|
|||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { Species } from "#enums/species";
|
import { Species } from "#enums/species";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/delibirdy";
|
const namespace = "mysteryEncounters/delibirdy";
|
||||||
@ -56,7 +57,7 @@ const OPTION_3_DISALLOWED_MODIFIERS = [
|
|||||||
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
|
const DELIBIRDY_MONEY_PRICE_MULTIPLIER = 2;
|
||||||
|
|
||||||
const doEventReward = () => {
|
const doEventReward = () => {
|
||||||
const event_buff = globalScene.eventManager.getDelibirdyBuff();
|
const event_buff = timedEventManager.getDelibirdyBuff();
|
||||||
if (event_buff.length > 0) {
|
if (event_buff.length > 0) {
|
||||||
const candidates = event_buff.filter(c => {
|
const candidates = event_buff.filter(c => {
|
||||||
const mtype = generateModifierType(modifierTypes[c]);
|
const mtype = generateModifierType(modifierTypes[c]);
|
||||||
|
@ -46,6 +46,7 @@ import { addPokemonDataToDexAndValidateAchievements } from "#app/data/mystery-en
|
|||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
import { doShinySparkleAnim } from "#app/field/anims";
|
import { doShinySparkleAnim } from "#app/field/anims";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/globalTradeSystem";
|
const namespace = "mysteryEncounters/globalTradeSystem";
|
||||||
@ -273,8 +274,8 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
|
|||||||
// 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) {
|
||||||
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
|
const shinyThreshold = new NumberHolder(WONDER_TRADE_SHINY_CHANCE);
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
shinyThreshold.value *= globalScene.eventManager.getShinyMultiplier();
|
shinyThreshold.value *= timedEventManager.getShinyMultiplier();
|
||||||
}
|
}
|
||||||
globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
|
globalScene.applyModifiers(ShinyRateBoosterModifier, true, shinyThreshold);
|
||||||
|
|
||||||
@ -984,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)];
|
||||||
|
@ -93,7 +93,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
bossSegments: 5,
|
bossSegments: 5,
|
||||||
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
shiny: false, // Shiny lock because shiny is rolled only if the battle option is picked
|
||||||
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
||||||
nature: Nature.BOLD,
|
nature: Nature.HARDY,
|
||||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||||
modifierConfigs: [
|
modifierConfigs: [
|
||||||
{
|
{
|
||||||
@ -117,7 +117,7 @@ export const TheStrongStuffEncounter: MysteryEncounter = MysteryEncounterBuilder
|
|||||||
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
mysteryEncounterBattleEffects: (pokemon: Pokemon) => {
|
||||||
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
queueEncounterMessage(`${namespace}:option.2.stat_boost`);
|
||||||
globalScene.unshiftPhase(
|
globalScene.unshiftPhase(
|
||||||
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 2),
|
new StatStageChangePhase(pokemon.getBattlerIndex(), true, [Stat.DEF, Stat.SPDEF], 1),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@ import { Moves } from "#enums/moves";
|
|||||||
import { BattlerIndex } from "#app/battle";
|
import { BattlerIndex } from "#app/battle";
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
import { PokemonMove } 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 { randSeedInt } from "#app/utils";
|
||||||
|
|
||||||
/** the i18n namespace for this encounter */
|
/** the i18n namespace for this encounter */
|
||||||
const namespace = "mysteryEncounters/trashToTreasure";
|
const namespace = "mysteryEncounters/trashToTreasure";
|
||||||
@ -80,7 +81,43 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
shiny: false, // Shiny lock because of custom intro sprite
|
shiny: false, // Shiny lock because of custom intro sprite
|
||||||
formIndex: 1, // Gmax
|
formIndex: 1, // Gmax
|
||||||
bossSegmentModifier: 1, // +1 Segment from normal
|
bossSegmentModifier: 1, // +1 Segment from normal
|
||||||
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH],
|
moveSet: [Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.HAMMER_ARM, Moves.PAYBACK],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: randSeedInt(2, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.SOOTHE_BELL) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: randSeedInt(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.LUCKY_EGG) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: randSeedInt(3, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.GOLDEN_EGG) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: randSeedInt(2, 0),
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
const config: EnemyPartyConfig = {
|
const config: EnemyPartyConfig = {
|
||||||
levelAdditiveModifier: 0.5,
|
levelAdditiveModifier: 0.5,
|
||||||
@ -90,7 +127,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
encounter.enemyPartyConfigs = [config];
|
encounter.enemyPartyConfigs = [config];
|
||||||
|
|
||||||
// Load animations/sfx for Garbodor fight start moves
|
// Load animations/sfx for Garbodor fight start moves
|
||||||
loadCustomMovesForEncounter([Moves.TOXIC, Moves.AMNESIA]);
|
loadCustomMovesForEncounter([Moves.TOXIC, Moves.STOCKPILE]);
|
||||||
|
|
||||||
globalScene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
globalScene.loadSe("PRSFX- Dig2", "battle_anims", "PRSFX- Dig2.wav");
|
||||||
globalScene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
globalScene.loadSe("PRSFX- Venom Drench", "battle_anims", "PRSFX- Venom Drench.wav");
|
||||||
@ -115,7 +152,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
doGarbageDig();
|
doGarbageDig();
|
||||||
})
|
})
|
||||||
.withOptionPhase(async () => {
|
.withOptionPhase(async () => {
|
||||||
// Gain 2 Leftovers and 2 Shell Bell
|
// Gain 2 Leftovers and 1 Shell Bell
|
||||||
await transitionMysteryEncounterIntroVisuals();
|
await transitionMysteryEncounterIntroVisuals();
|
||||||
await tryApplyDigRewardItems();
|
await tryApplyDigRewardItems();
|
||||||
|
|
||||||
@ -175,7 +212,7 @@ export const TrashToTreasureEncounter: MysteryEncounter = MysteryEncounterBuilde
|
|||||||
{
|
{
|
||||||
sourceBattlerIndex: BattlerIndex.ENEMY,
|
sourceBattlerIndex: BattlerIndex.ENEMY,
|
||||||
targets: [BattlerIndex.ENEMY],
|
targets: [BattlerIndex.ENEMY],
|
||||||
move: new PokemonMove(Moves.AMNESIA),
|
move: new PokemonMove(Moves.STOCKPILE),
|
||||||
ignorePp: true,
|
ignorePp: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -231,21 +268,7 @@ async function tryApplyDigRewardItems() {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// First Shell bell
|
// Only Shell bell
|
||||||
for (const pokemon of party) {
|
|
||||||
const heldItems = globalScene.findModifiers(
|
|
||||||
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id,
|
|
||||||
true,
|
|
||||||
) as PokemonHeldItemModifier[];
|
|
||||||
const existingShellBell = heldItems.find(m => m instanceof HitHealModifier) as HitHealModifier;
|
|
||||||
|
|
||||||
if (!existingShellBell || existingShellBell.getStackCount() < existingShellBell.getMaxStackCount()) {
|
|
||||||
await applyModifierTypeToPlayerPokemon(pokemon, shellBell);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Second Shell bell
|
|
||||||
for (const pokemon of party) {
|
for (const pokemon of party) {
|
||||||
const heldItems = globalScene.findModifiers(
|
const heldItems = globalScene.findModifiers(
|
||||||
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id,
|
m => m instanceof PokemonHeldItemModifier && m.pokemonId === pokemon.id,
|
||||||
@ -263,7 +286,7 @@ async function tryApplyDigRewardItems() {
|
|||||||
await showEncounterText(
|
await showEncounterText(
|
||||||
i18next.t("battle:rewardGainCount", {
|
i18next.t("battle:rewardGainCount", {
|
||||||
modifierName: shellBell.name,
|
modifierName: shellBell.name,
|
||||||
count: 2,
|
count: 1,
|
||||||
}),
|
}),
|
||||||
null,
|
null,
|
||||||
undefined,
|
undefined,
|
||||||
|
@ -65,6 +65,7 @@ import { getPokemonSpecies } from "#app/data/pokemon-species";
|
|||||||
import { PokemonType } from "#enums/pokemon-type";
|
import { PokemonType } from "#enums/pokemon-type";
|
||||||
import { getNatureName } from "#app/data/nature";
|
import { getNatureName } from "#app/data/nature";
|
||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Animates exclamation sprite over trainer's head at start of encounter
|
* Animates exclamation sprite over trainer's head at start of encounter
|
||||||
@ -1046,7 +1047,7 @@ export function handleMysteryEncounterTurnStartEffects(): boolean {
|
|||||||
export function getRandomEncounterSpecies(level: number, isBoss = false, rerollHidden = false): EnemyPokemon {
|
export function getRandomEncounterSpecies(level: number, isBoss = false, rerollHidden = false): EnemyPokemon {
|
||||||
let bossSpecies: PokemonSpecies;
|
let bossSpecies: PokemonSpecies;
|
||||||
let isEventEncounter = false;
|
let isEventEncounter = false;
|
||||||
const eventEncounters = globalScene.eventManager.getEventEncounters();
|
const eventEncounters = timedEventManager.getEventEncounters();
|
||||||
let formIndex: number | undefined;
|
let formIndex: number | undefined;
|
||||||
|
|
||||||
if (eventEncounters.length > 0 && randSeedInt(2) === 1) {
|
if (eventEncounters.length > 0 && randSeedInt(2) === 1) {
|
||||||
|
@ -3054,11 +3054,11 @@ export function initSpecies() {
|
|||||||
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
|
new PokemonSpecies(Species.DIPPLIN, 9, false, false, false, "Candy Apple Pokémon", PokemonType.GRASS, PokemonType.DRAGON, 0.4, 9.7, Abilities.SUPERSWEET_SYRUP, Abilities.GLUTTONY, Abilities.STICKY_HOLD, 485, 80, 80, 110, 95, 80, 40, 45, 50, 170, GrowthRate.ERRATIC, 50, false),
|
||||||
new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
|
new PokemonSpecies(Species.POLTCHAGEIST, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, GrowthRate.SLOW, null, false, false,
|
||||||
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
|
new PokemonForm("Counterfeit Form", "counterfeit", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
|
||||||
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, true),
|
new PokemonForm("Artisan Form", "artisan", PokemonType.GRASS, PokemonType.GHOST, 0.1, 1.1, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 308, 40, 45, 45, 74, 54, 50, 120, 50, 62, false, null, false, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.SINISTCHA, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, GrowthRate.SLOW, null, false, false,
|
new PokemonSpecies(Species.SINISTCHA, 9, false, false, false, "Matcha Pokémon", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, GrowthRate.SLOW, null, false, false,
|
||||||
new PokemonForm("Unremarkable Form", "unremarkable", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178),
|
new PokemonForm("Unremarkable Form", "unremarkable", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178),
|
||||||
new PokemonForm("Masterpiece Form", "masterpiece", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178),
|
new PokemonForm("Masterpiece Form", "masterpiece", PokemonType.GRASS, PokemonType.GHOST, 0.2, 2.2, Abilities.HOSPITALITY, Abilities.NONE, Abilities.HEATPROOF, 508, 71, 60, 106, 121, 80, 70, 60, 50, 178, false, null, false, true),
|
||||||
),
|
),
|
||||||
new PokemonSpecies(Species.OKIDOGI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.FIGHTING, 1.8, 92.2, Abilities.TOXIC_CHAIN, Abilities.NONE, Abilities.GUARD_DOG, 555, 88, 128, 115, 58, 86, 80, 3, 0, 276, GrowthRate.SLOW, 100, false),
|
new PokemonSpecies(Species.OKIDOGI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.FIGHTING, 1.8, 92.2, Abilities.TOXIC_CHAIN, Abilities.NONE, Abilities.GUARD_DOG, 555, 88, 128, 115, 58, 86, 80, 3, 0, 276, GrowthRate.SLOW, 100, false),
|
||||||
new PokemonSpecies(Species.MUNKIDORI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.PSYCHIC, 1, 12.2, Abilities.TOXIC_CHAIN, Abilities.NONE, Abilities.FRISK, 555, 88, 75, 66, 130, 90, 106, 3, 0, 276, GrowthRate.SLOW, 100, false),
|
new PokemonSpecies(Species.MUNKIDORI, 9, true, false, false, "Retainer Pokémon", PokemonType.POISON, PokemonType.PSYCHIC, 1, 12.2, Abilities.TOXIC_CHAIN, Abilities.NONE, Abilities.FRISK, 555, 88, 75, 66, 130, 90, 106, 3, 0, 276, GrowthRate.SLOW, 100, false),
|
||||||
|
@ -32,6 +32,7 @@ import { TeraAIMode } from "#enums/tera-ai-mode";
|
|||||||
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
|
import { TrainerPoolTier } from "#enums/trainer-pool-tier";
|
||||||
import { TrainerSlot } from "#enums/trainer-slot";
|
import { TrainerSlot } from "#enums/trainer-slot";
|
||||||
import { TrainerType } from "#enums/trainer-type";
|
import { TrainerType } from "#enums/trainer-type";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
// Type imports
|
// Type imports
|
||||||
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
import type { PokemonSpeciesFilter } from "#app/data/pokemon-species";
|
||||||
@ -516,13 +517,13 @@ export class TrainerConfig {
|
|||||||
// return ret;
|
// return ret;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
setEventModifierRewardFuncs(...modifierTypeFuncs: (() => ModifierTypeFunc)[]): TrainerConfig {
|
/**
|
||||||
this.eventRewardFuncs = modifierTypeFuncs.map(func => () => {
|
* Sets eventRewardFuncs to the active event rewards for the specified wave
|
||||||
const modifierTypeFunc = func();
|
* @param wave Associated with {@linkcode getFixedBattleEventRewards}
|
||||||
const modifierType = modifierTypeFunc();
|
* @returns this
|
||||||
modifierType.withIdFromFunc(modifierTypeFunc);
|
*/
|
||||||
return modifierType;
|
setEventModifierRewardFuncs(wave: number): TrainerConfig {
|
||||||
});
|
this.eventRewardFuncs = timedEventManager.getFixedBattleEventRewards(wave).map(r => modifierTypes[r]);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3696,11 +3697,7 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
() => modifierTypes.SUPER_EXP_CHARM,
|
() => modifierTypes.SUPER_EXP_CHARM,
|
||||||
() => modifierTypes.EXP_SHARE,
|
() => modifierTypes.EXP_SHARE,
|
||||||
)
|
)
|
||||||
.setEventModifierRewardFuncs(
|
.setEventModifierRewardFuncs(8)
|
||||||
() => modifierTypes.SHINY_CHARM,
|
|
||||||
() => modifierTypes.ABILITY_CHARM,
|
|
||||||
() => modifierTypes.CATCHING_CHARM,
|
|
||||||
)
|
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(
|
||||||
0,
|
0,
|
||||||
getRandomPartyMemberFunc(
|
getRandomPartyMemberFunc(
|
||||||
@ -3768,7 +3765,7 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
.setMixedBattleBgm("battle_rival")
|
.setMixedBattleBgm("battle_rival")
|
||||||
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
|
.setPartyTemplates(trainerPartyTemplates.RIVAL_2)
|
||||||
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
|
.setModifierRewardFuncs(() => modifierTypes.EXP_SHARE)
|
||||||
.setEventModifierRewardFuncs(() => modifierTypes.SHINY_CHARM)
|
.setEventModifierRewardFuncs(25)
|
||||||
.setPartyMemberFunc(
|
.setPartyMemberFunc(
|
||||||
0,
|
0,
|
||||||
getRandomPartyMemberFunc(
|
getRandomPartyMemberFunc(
|
||||||
@ -4077,7 +4074,7 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
getRandomPartyMemberFunc([Species.RAYQUAZA], TrainerSlot.TRAINER, true, p => {
|
getRandomPartyMemberFunc([Species.RAYQUAZA], TrainerSlot.TRAINER, true, p => {
|
||||||
p.setBoss(true, 3);
|
p.setBoss(true, 3);
|
||||||
p.pokeball = PokeballType.MASTER_BALL;
|
p.pokeball = PokeballType.MASTER_BALL;
|
||||||
p.shiny = true;
|
p.shiny = timedEventManager.getClassicTrainerShinyChance() === 0;
|
||||||
p.variant = 1;
|
p.variant = 1;
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -4174,7 +4171,7 @@ export const trainerConfigs: TrainerConfigs = {
|
|||||||
p.setBoss();
|
p.setBoss();
|
||||||
p.generateAndPopulateMoveset();
|
p.generateAndPopulateMoveset();
|
||||||
p.pokeball = PokeballType.MASTER_BALL;
|
p.pokeball = PokeballType.MASTER_BALL;
|
||||||
p.shiny = true;
|
p.shiny = timedEventManager.getClassicTrainerShinyChance() === 0;
|
||||||
p.variant = 1;
|
p.variant = 1;
|
||||||
p.formIndex = 1; // Mega Rayquaza
|
p.formIndex = 1; // Mega Rayquaza
|
||||||
p.generateName();
|
p.generateName();
|
||||||
|
@ -11,6 +11,7 @@ import { TerrainType, getTerrainName } from "./terrain";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { Arena } from "#app/field/arena";
|
import type { Arena } from "#app/field/arena";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
export class Weather {
|
export class Weather {
|
||||||
public weatherType: WeatherType;
|
public weatherType: WeatherType;
|
||||||
@ -405,8 +406,8 @@ export function getRandomWeatherType(arena: Arena): WeatherType {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arena.biomeType === Biome.TOWN && globalScene.eventManager.isEventActive()) {
|
if (arena.biomeType === Biome.TOWN && timedEventManager.isEventActive()) {
|
||||||
globalScene.eventManager.getWeather()?.map(w => weatherPool.push(w));
|
timedEventManager.getWeather()?.map(w => weatherPool.push(w));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weatherPool.length > 1) {
|
if (weatherPool.length > 1) {
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -263,6 +263,7 @@ import { Nature } from "#enums/nature";
|
|||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import { doShinySparkleAnim } from "#app/field/anims";
|
import { doShinySparkleAnim } from "#app/field/anims";
|
||||||
import { MoveFlags } from "#enums/MoveFlags";
|
import { MoveFlags } from "#enums/MoveFlags";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
export enum LearnMoveSituation {
|
export enum LearnMoveSituation {
|
||||||
MISC,
|
MISC,
|
||||||
@ -1446,7 +1447,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ally = this.getAlly();
|
const ally = this.getAlly();
|
||||||
if (ally) {
|
if (!Utils.isNullOrUndefined(ally)) {
|
||||||
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, stat, statValue, simulated, this, move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility);
|
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, stat, statValue, simulated, this, move?.hasFlag(MoveFlags.IGNORE_ABILITIES) || ignoreAllyAbility);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2983,8 +2984,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE);
|
const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE);
|
||||||
if (thresholdOverride === undefined) {
|
if (thresholdOverride === undefined) {
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
shinyThreshold.value *= globalScene.eventManager.getShinyMultiplier();
|
const tchance = timedEventManager.getClassicTrainerShinyChance();
|
||||||
|
shinyThreshold.value *= timedEventManager.getShinyMultiplier();
|
||||||
|
if (this.hasTrainer() && tchance > 0) {
|
||||||
|
shinyThreshold.value = Math.max(tchance, shinyThreshold.value); // Choose the higher boost
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!this.hasTrainer()) {
|
if (!this.hasTrainer()) {
|
||||||
globalScene.applyModifiers(
|
globalScene.applyModifiers(
|
||||||
@ -3025,8 +3030,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||||
shinyThreshold.value = thresholdOverride;
|
shinyThreshold.value = thresholdOverride;
|
||||||
}
|
}
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
shinyThreshold.value *= globalScene.eventManager.getShinyMultiplier();
|
shinyThreshold.value *= timedEventManager.getShinyMultiplier();
|
||||||
}
|
}
|
||||||
if (!this.hasTrainer()) {
|
if (!this.hasTrainer()) {
|
||||||
globalScene.applyModifiers(
|
globalScene.applyModifiers(
|
||||||
@ -3709,7 +3714,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
: i18next.t("arenaTag:yourTeam");
|
: i18next.t("arenaTag:yourTeam");
|
||||||
}
|
}
|
||||||
|
|
||||||
getAlly(): Pokemon {
|
getAlly(): Pokemon | undefined {
|
||||||
return (
|
return (
|
||||||
this.isPlayer()
|
this.isPlayer()
|
||||||
? globalScene.getPlayerField()
|
? globalScene.getPlayerField()
|
||||||
@ -3895,7 +3900,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const ally = this.getAlly();
|
const ally = this.getAlly();
|
||||||
if (ally) {
|
if (!isNullOrUndefined(ally)) {
|
||||||
const ignore = this.hasAbilityWithAttr(MoveAbilityBypassAbAttr) || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES);
|
const ignore = this.hasAbilityWithAttr(MoveAbilityBypassAbAttr) || sourceMove.hasFlag(MoveFlags.IGNORE_ABILITIES);
|
||||||
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.ACC, accuracyMultiplier, false, this, ignore);
|
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.ACC, accuracyMultiplier, false, this, ignore);
|
||||||
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.EVA, evasionMultiplier, false, this, ignore);
|
applyAllyStatMultiplierAbAttrs(AllyStatMultiplierAbAttr, ally, Stat.EVA, evasionMultiplier, false, this, ignore);
|
||||||
@ -4331,11 +4336,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
damage,
|
damage,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ally = this.getAlly();
|
||||||
/** Additionally apply friend guard damage reduction if ally has it. */
|
/** Additionally apply friend guard damage reduction if ally has it. */
|
||||||
if (globalScene.currentBattle.double && this.getAlly()?.isActive(true)) {
|
if (globalScene.currentBattle.double && !isNullOrUndefined(ally) && ally.isActive(true)) {
|
||||||
applyPreDefendAbAttrs(
|
applyPreDefendAbAttrs(
|
||||||
AlliedFieldDamageReductionAbAttr,
|
AlliedFieldDamageReductionAbAttr,
|
||||||
this.getAlly(),
|
ally,
|
||||||
source,
|
source,
|
||||||
move,
|
move,
|
||||||
cancelled,
|
cancelled,
|
||||||
@ -6469,10 +6475,10 @@ export class PlayerPokemon extends Pokemon {
|
|||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
const candyFriendshipMultiplier = globalScene.gameMode.isClassic
|
const candyFriendshipMultiplier = globalScene.gameMode.isClassic
|
||||||
? globalScene.eventManager.getClassicFriendshipMultiplier()
|
? timedEventManager.getClassicFriendshipMultiplier()
|
||||||
: 1;
|
: 1;
|
||||||
const fusionReduction = fusionStarterSpeciesId
|
const fusionReduction = fusionStarterSpeciesId
|
||||||
? globalScene.eventManager.areFusionsBoosted()
|
? timedEventManager.areFusionsBoosted()
|
||||||
? 1.5 // Divide candy gain for fusions by 1.5 during events
|
? 1.5 // Divide candy gain for fusions by 1.5 during events
|
||||||
: 2 // 2 for fusions outside events
|
: 2 // 2 for fusions outside events
|
||||||
: 1; // 1 for non-fused mons
|
: 1; // 1 for non-fused mons
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,19 @@ export class GameMode implements GameModeConfig {
|
|||||||
this.battleConfig = battleConfig || {};
|
this.battleConfig = battleConfig || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables challenges if they are disabled and sets the specified challenge's value
|
||||||
|
* @param challenge The challenge to set
|
||||||
|
* @param value The value to give the challenge. Impact depends on the specific challenge
|
||||||
|
*/
|
||||||
|
setChallengeValue(challenge: Challenges, value: number) {
|
||||||
|
if (!this.isChallenge) {
|
||||||
|
this.isChallenge = true;
|
||||||
|
this.challenges = allChallenges.map(c => copyChallenge(c));
|
||||||
|
}
|
||||||
|
this.challenges.filter((chal: Challenge) => chal.id === challenge).map((chal: Challenge) => (chal.value = value));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to see if a GameMode has a specific challenge type
|
* Helper function to see if a GameMode has a specific challenge type
|
||||||
* @param challenge the Challenges it looks for
|
* @param challenge the Challenges it looks for
|
||||||
|
3
src/global-event-manager.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { TimedEventManager } from "./timed-event-manager";
|
||||||
|
|
||||||
|
export const timedEventManager = new TimedEventManager();
|
@ -20,6 +20,7 @@ import { initStatsKeys } from "#app/ui/game-stats-ui-handler";
|
|||||||
import { initVouchers } from "#app/system/voucher";
|
import { initVouchers } from "#app/system/voucher";
|
||||||
import { Biome } from "#enums/biome";
|
import { Biome } from "#enums/biome";
|
||||||
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
import { initMysteryEncounters } from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import { timedEventManager } from "./global-event-manager";
|
||||||
|
|
||||||
export class LoadingScene extends SceneBase {
|
export class LoadingScene extends SceneBase {
|
||||||
public static readonly KEY = "loading";
|
public static readonly KEY = "loading";
|
||||||
@ -250,11 +251,13 @@ export class LoadingScene extends SceneBase {
|
|||||||
this.loadAtlas("statuses", "");
|
this.loadAtlas("statuses", "");
|
||||||
this.loadAtlas("types", "");
|
this.loadAtlas("types", "");
|
||||||
}
|
}
|
||||||
const availableLangs = ["en", "de", "it", "fr", "ja", "ko", "es-ES", "es-MX", "pt-BR", "zh-CN", "zh-TW", "ca-ES"];
|
if (timedEventManager.activeEventHasBanner()) {
|
||||||
|
const availableLangs = timedEventManager.getEventBannerLangs();
|
||||||
if (lang && availableLangs.includes(lang)) {
|
if (lang && availableLangs.includes(lang)) {
|
||||||
this.loadImage(`pkmnday2025event-${lang}`, "events");
|
this.loadImage(`${timedEventManager.getEventBannerFilename()}-${lang}`, "events");
|
||||||
} else {
|
} else {
|
||||||
this.loadImage("pkmnday2025event-en", "events");
|
this.loadImage(`${timedEventManager.getEventBannerFilename()}-en`, "events");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadAtlas("statuses", "");
|
this.loadAtlas("statuses", "");
|
||||||
|
@ -127,6 +127,7 @@ import type { PermanentStat, TempBattleStat } from "#enums/stat";
|
|||||||
import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
|
import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
const outputModifierData = false;
|
const outputModifierData = false;
|
||||||
const useMaxWeightForOutput = false;
|
const useMaxWeightForOutput = false;
|
||||||
@ -2655,7 +2656,7 @@ const modifierPool: ModifierPool = {
|
|||||||
if (globalScene.gameMode.isSplicedOnly) {
|
if (globalScene.gameMode.isSplicedOnly) {
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
if (globalScene.gameMode.isClassic && globalScene.eventManager.areFusionsBoosted()) {
|
if (globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2822,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,
|
||||||
@ -2853,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;
|
||||||
@ -2939,7 +2951,7 @@ const modifierPool: ModifierPool = {
|
|||||||
new WeightedModifierType(
|
new WeightedModifierType(
|
||||||
modifierTypes.DNA_SPLICERS,
|
modifierTypes.DNA_SPLICERS,
|
||||||
(party: Pokemon[]) =>
|
(party: Pokemon[]) =>
|
||||||
!(globalScene.gameMode.isClassic && globalScene.eventManager.areFusionsBoosted()) &&
|
!(globalScene.gameMode.isClassic && timedEventManager.areFusionsBoosted()) &&
|
||||||
!globalScene.gameMode.isSplicedOnly &&
|
!globalScene.gameMode.isSplicedOnly &&
|
||||||
party.filter(p => !p.fusionSpecies).length > 1
|
party.filter(p => !p.fusionSpecies).length > 1
|
||||||
? 24
|
? 24
|
||||||
@ -3703,7 +3715,7 @@ export function getPartyLuckValue(party: Pokemon[]): number {
|
|||||||
);
|
);
|
||||||
return DailyLuck.value;
|
return DailyLuck.value;
|
||||||
}
|
}
|
||||||
const eventSpecies = globalScene.eventManager.getEventLuckBoostedSpecies();
|
const eventSpecies = timedEventManager.getEventLuckBoostedSpecies();
|
||||||
const luck = Phaser.Math.Clamp(
|
const luck = Phaser.Math.Clamp(
|
||||||
party
|
party
|
||||||
.map(p => (p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 1 : 0) : 0))
|
.map(p => (p.isAllowedInBattle() ? p.getLuck() + (eventSpecies.includes(p.species.speciesId) ? 1 : 0) : 0))
|
||||||
@ -3711,7 +3723,7 @@ export function getPartyLuckValue(party: Pokemon[]): number {
|
|||||||
0,
|
0,
|
||||||
14,
|
14,
|
||||||
);
|
);
|
||||||
return Math.min(globalScene.eventManager.getEventLuckBoost() + (luck ?? 0), 14);
|
return Math.min(timedEventManager.getEventLuckBoost() + (luck ?? 0), 14);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLuckString(luckValue: number): string {
|
export function getLuckString(luckValue: number): string {
|
||||||
|
@ -39,7 +39,7 @@ export class EnemyCommandPhase extends FieldPhase {
|
|||||||
if (
|
if (
|
||||||
battle.double &&
|
battle.double &&
|
||||||
enemyPokemon.hasAbility(Abilities.COMMANDER) &&
|
enemyPokemon.hasAbility(Abilities.COMMANDER) &&
|
||||||
enemyPokemon.getAlly().getTag(BattlerTagType.COMMANDED)
|
enemyPokemon.getAlly()?.getTag(BattlerTagType.COMMANDED)
|
||||||
) {
|
) {
|
||||||
this.skipTurn = true;
|
this.skipTurn = true;
|
||||||
}
|
}
|
||||||
|
@ -209,8 +209,8 @@ export class FaintPhase extends PokemonPhase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// in double battles redirect potential moves off fainted pokemon
|
// in double battles redirect potential moves off fainted pokemon
|
||||||
if (globalScene.currentBattle.double) {
|
|
||||||
const allyPokemon = pokemon.getAlly();
|
const allyPokemon = pokemon.getAlly();
|
||||||
|
if (globalScene.currentBattle.double && !isNullOrUndefined(allyPokemon)) {
|
||||||
globalScene.redirectPokemonMoves(pokemon, allyPokemon);
|
globalScene.redirectPokemonMoves(pokemon, allyPokemon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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(
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,8 +42,12 @@ export class RevivalBlessingPhase extends BattlePhase {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (globalScene.currentBattle.double && globalScene.getPlayerParty().length > 1) {
|
|
||||||
const allyPokemon = this.user.getAlly();
|
const allyPokemon = this.user.getAlly();
|
||||||
|
if (
|
||||||
|
globalScene.currentBattle.double &&
|
||||||
|
globalScene.getPlayerParty().length > 1 &&
|
||||||
|
!Utils.isNullOrUndefined(allyPokemon)
|
||||||
|
) {
|
||||||
if (slotIndex <= 1) {
|
if (slotIndex <= 1) {
|
||||||
// Revived ally pokemon
|
// Revived ally pokemon
|
||||||
globalScene.unshiftPhase(
|
globalScene.unshiftPhase(
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ import type Pokemon from "#app/field/pokemon";
|
|||||||
import { getPokemonNameWithAffix } from "#app/messages";
|
import { getPokemonNameWithAffix } from "#app/messages";
|
||||||
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
import { ResetNegativeStatStageModifier } from "#app/modifier/modifier";
|
||||||
import { handleTutorial, Tutorial } from "#app/tutorial";
|
import { handleTutorial, Tutorial } from "#app/tutorial";
|
||||||
import { NumberHolder, BooleanHolder } from "#app/utils";
|
import { NumberHolder, BooleanHolder, isNullOrUndefined } from "#app/utils";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { PokemonPhase } from "./pokemon-phase";
|
import { PokemonPhase } from "./pokemon-phase";
|
||||||
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
import { Stat, type BattleStat, getStatKey, getStatStageChangeDescriptionKey } from "#enums/stat";
|
||||||
@ -161,7 +161,7 @@ export class StatStageChangePhase extends PokemonPhase {
|
|||||||
pokemon,
|
pokemon,
|
||||||
);
|
);
|
||||||
const ally = pokemon.getAlly();
|
const ally = pokemon.getAlly();
|
||||||
if (ally) {
|
if (!isNullOrUndefined(ally)) {
|
||||||
applyPreStatStageChangeAbAttrs(
|
applyPreStatStageChangeAbAttrs(
|
||||||
ConditionalUserFieldProtectStatAbAttr,
|
ConditionalUserFieldProtectStatAbAttr,
|
||||||
ally,
|
ally,
|
||||||
|
@ -212,6 +212,8 @@ export class TitlePhase extends Phase {
|
|||||||
|
|
||||||
const generateDaily = (seed: string) => {
|
const generateDaily = (seed: string) => {
|
||||||
globalScene.gameMode = getGameMode(GameModes.DAILY);
|
globalScene.gameMode = getGameMode(GameModes.DAILY);
|
||||||
|
// Daily runs don't support all challenges yet (starter select restrictions aren't considered)
|
||||||
|
globalScene.eventManager.startEventChallenges();
|
||||||
|
|
||||||
globalScene.setSeed(seed);
|
globalScene.setSeed(seed);
|
||||||
globalScene.resetSeed(0);
|
globalScene.resetSeed(0);
|
||||||
|
@ -11,6 +11,7 @@ import { TrainerSlot } from "#enums/trainer-slot";
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Biome } from "#app/enums/biome";
|
import { Biome } from "#app/enums/biome";
|
||||||
import { achvs } from "#app/system/achv";
|
import { achvs } from "#app/system/achv";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
export class TrainerVictoryPhase extends BattlePhase {
|
export class TrainerVictoryPhase extends BattlePhase {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -29,7 +30,7 @@ export class TrainerVictoryPhase extends BattlePhase {
|
|||||||
globalScene.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc));
|
globalScene.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
for (const rewardFunc of globalScene.currentBattle.trainer?.config.eventRewardFuncs!) {
|
for (const rewardFunc of globalScene.currentBattle.trainer?.config.eventRewardFuncs!) {
|
||||||
globalScene.unshiftPhase(new ModifierRewardPhase(rewardFunc));
|
globalScene.unshiftPhase(new ModifierRewardPhase(rewardFunc));
|
||||||
}
|
}
|
||||||
@ -42,7 +43,7 @@ export class TrainerVictoryPhase extends BattlePhase {
|
|||||||
!globalScene.validateVoucher(vouchers[TrainerType[trainerType]]) &&
|
!globalScene.validateVoucher(vouchers[TrainerType[trainerType]]) &&
|
||||||
globalScene.currentBattle.trainer?.config.isBoss
|
globalScene.currentBattle.trainer?.config.isBoss
|
||||||
) {
|
) {
|
||||||
if (globalScene.eventManager.getUpgradeUnlockedVouchers()) {
|
if (timedEventManager.getUpgradeUnlockedVouchers()) {
|
||||||
globalScene.unshiftPhase(
|
globalScene.unshiftPhase(
|
||||||
new ModifierRewardPhase(
|
new ModifierRewardPhase(
|
||||||
[
|
[
|
||||||
|
@ -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);
|
||||||
|
@ -1793,7 +1793,9 @@ export class GameData {
|
|||||||
const dexEntry = this.dexData[species.speciesId];
|
const dexEntry = this.dexData[species.speciesId];
|
||||||
const caughtAttr = dexEntry.caughtAttr;
|
const caughtAttr = dexEntry.caughtAttr;
|
||||||
const formIndex = pokemon.formIndex;
|
const formIndex = pokemon.formIndex;
|
||||||
const dexAttr = pokemon.getDexAttr();
|
|
||||||
|
// This makes sure that we do not try to unlock data which cannot be unlocked
|
||||||
|
const dexAttr = pokemon.getDexAttr() & species.getFullUnlocksData();
|
||||||
|
|
||||||
// Mark as caught
|
// Mark as caught
|
||||||
dexEntry.caughtAttr |= dexAttr;
|
dexEntry.caughtAttr |= dexAttr;
|
||||||
@ -1803,6 +1805,10 @@ export class GameData {
|
|||||||
// always true except for the case of Urshifu.
|
// always true except for the case of Urshifu.
|
||||||
const formKey = pokemon.getFormKey();
|
const formKey = pokemon.getFormKey();
|
||||||
if (formIndex > 0) {
|
if (formIndex > 0) {
|
||||||
|
// In case a Pikachu with formIndex > 0 was unlocked, base form Pichu is also unlocked
|
||||||
|
if (pokemon.species.speciesId === Species.PIKACHU && species.speciesId === Species.PICHU) {
|
||||||
|
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0);
|
||||||
|
}
|
||||||
if (pokemon.species.speciesId === Species.URSHIFU) {
|
if (pokemon.species.speciesId === Species.URSHIFU) {
|
||||||
if (formIndex === 2) {
|
if (formIndex === 2) {
|
||||||
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0);
|
dexEntry.caughtAttr |= globalScene.gameData.getFormAttr(0);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,9 @@ import * as v1_1_0 from "./versions/v1_1_0";
|
|||||||
// --- v1.7.0 PATCHES --- //
|
// --- v1.7.0 PATCHES --- //
|
||||||
import * as v1_7_0 from "./versions/v1_7_0";
|
import * as v1_7_0 from "./versions/v1_7_0";
|
||||||
|
|
||||||
|
// --- v1.8.3 PATCHES --- //
|
||||||
|
import * as v1_8_3 from "./versions/v1_8_3";
|
||||||
|
|
||||||
const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value));
|
const LATEST_VERSION = version.split(".").map(value => Number.parseInt(value));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -174,6 +177,12 @@ class SystemVersionConverter extends VersionConverter {
|
|||||||
console.log("Applying v1.7.0 system data migration!");
|
console.log("Applying v1.7.0 system data migration!");
|
||||||
this.callMigrators(data, v1_7_0.systemMigrators);
|
this.callMigrators(data, v1_7_0.systemMigrators);
|
||||||
}
|
}
|
||||||
|
if (curMinor === 8) {
|
||||||
|
if (curPatch <= 2) {
|
||||||
|
console.log("Applying v1.8.3 system data migration!");
|
||||||
|
this.callMigrators(data, v1_8_3.systemMigrators);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`System data successfully migrated to v${version}!`);
|
console.log(`System data successfully migrated to v${version}!`);
|
||||||
|
30
src/system/version_migration/versions/v1_8_3.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
|
import { DexAttr, type SystemSaveData } from "#app/system/game-data";
|
||||||
|
import { Species } from "#enums/species";
|
||||||
|
|
||||||
|
export const systemMigrators = [
|
||||||
|
/**
|
||||||
|
* If a starter is caught, but the only forms registered as caught are not starterSelectable,
|
||||||
|
* unlock the default form.
|
||||||
|
* @param data {@linkcode SystemSaveData}
|
||||||
|
*/
|
||||||
|
function migratePichuForms(data: SystemSaveData) {
|
||||||
|
if (data.starterData && data.dexData) {
|
||||||
|
// This is Pichu's Pokédex number
|
||||||
|
const sd = 172;
|
||||||
|
const caughtAttr = data.dexData[sd]?.caughtAttr;
|
||||||
|
const species = getPokemonSpecies(sd);
|
||||||
|
// An extra check because you never know
|
||||||
|
if (species.speciesId === Species.PICHU && caughtAttr) {
|
||||||
|
// Ensuring that only existing forms are unlocked
|
||||||
|
data.dexData[sd].caughtAttr &= species.getFullUnlocksData();
|
||||||
|
// If no forms are unlocked now, since Pichu is caught, we unlock form 0
|
||||||
|
data.dexData[sd].caughtAttr |= DexAttr.DEFAULT_FORM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export const settingsMigrators = [] as const;
|
||||||
|
|
||||||
|
export const sessionMigrators = [] as const;
|
@ -9,6 +9,7 @@ import { WeatherType } from "#enums/weather-type";
|
|||||||
import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "./data/balance/starters";
|
import { CLASSIC_CANDY_FRIENDSHIP_MULTIPLIER } from "./data/balance/starters";
|
||||||
import { MysteryEncounterType } from "./enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "./enums/mystery-encounter-type";
|
||||||
import { MysteryEncounterTier } from "./enums/mystery-encounter-tier";
|
import { MysteryEncounterTier } from "./enums/mystery-encounter-tier";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
|
||||||
export enum EventType {
|
export enum EventType {
|
||||||
SHINY,
|
SHINY,
|
||||||
@ -36,6 +37,18 @@ interface EventMysteryEncounterTier {
|
|||||||
disable?: boolean;
|
disable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface EventWaveReward {
|
||||||
|
wave: number;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventMusicReplacement = [string, string];
|
||||||
|
|
||||||
|
interface EventChallenge {
|
||||||
|
challenge: Challenges;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface TimedEvent extends EventBanner {
|
interface TimedEvent extends EventBanner {
|
||||||
name: string;
|
name: string;
|
||||||
eventType: EventType;
|
eventType: EventType;
|
||||||
@ -51,6 +64,10 @@ interface TimedEvent extends EventBanner {
|
|||||||
mysteryEncounterTierChanges?: EventMysteryEncounterTier[];
|
mysteryEncounterTierChanges?: EventMysteryEncounterTier[];
|
||||||
luckBoostedSpecies?: Species[];
|
luckBoostedSpecies?: Species[];
|
||||||
boostFusions?: boolean; //MODIFIER REWORK PLEASE
|
boostFusions?: boolean; //MODIFIER REWORK PLEASE
|
||||||
|
classicWaveRewards?: EventWaveReward[]; // Rival battle rewards
|
||||||
|
trainerShinyChance?: number; // Odds over 65536 of trainer mon generating as shiny
|
||||||
|
music?: EventMusicReplacement[];
|
||||||
|
dailyRunChallenges?: EventChallenge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const timedEvents: TimedEvent[] = [
|
const timedEvents: TimedEvent[] = [
|
||||||
@ -61,7 +78,7 @@ const timedEvents: TimedEvent[] = [
|
|||||||
upgradeUnlockedVouchers: true,
|
upgradeUnlockedVouchers: true,
|
||||||
startDate: new Date(Date.UTC(2024, 11, 21, 0)),
|
startDate: new Date(Date.UTC(2024, 11, 21, 0)),
|
||||||
endDate: new Date(Date.UTC(2025, 0, 4, 0)),
|
endDate: new Date(Date.UTC(2025, 0, 4, 0)),
|
||||||
bannerKey: "winter_holidays2024-event-",
|
bannerKey: "winter_holidays2024-event",
|
||||||
scale: 0.21,
|
scale: 0.21,
|
||||||
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
||||||
eventEncounters: [
|
eventEncounters: [
|
||||||
@ -104,6 +121,12 @@ const timedEvents: TimedEvent[] = [
|
|||||||
disable: true,
|
disable: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
classicWaveRewards: [
|
||||||
|
{ wave: 8, type: "SHINY_CHARM" },
|
||||||
|
{ wave: 8, type: "ABILITY_CHARM" },
|
||||||
|
{ wave: 8, type: "CATCHING_CHARM" },
|
||||||
|
{ wave: 25, type: "SHINY_CHARM" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Year of the Snake",
|
name: "Year of the Snake",
|
||||||
@ -111,7 +134,7 @@ const timedEvents: TimedEvent[] = [
|
|||||||
luckBoost: 1,
|
luckBoost: 1,
|
||||||
startDate: new Date(Date.UTC(2025, 0, 29, 0)),
|
startDate: new Date(Date.UTC(2025, 0, 29, 0)),
|
||||||
endDate: new Date(Date.UTC(2025, 1, 3, 0)),
|
endDate: new Date(Date.UTC(2025, 1, 3, 0)),
|
||||||
bannerKey: "yearofthesnakeevent-",
|
bannerKey: "yearofthesnakeevent",
|
||||||
scale: 0.21,
|
scale: 0.21,
|
||||||
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
||||||
eventEncounters: [
|
eventEncounters: [
|
||||||
@ -169,6 +192,12 @@ const timedEvents: TimedEvent[] = [
|
|||||||
Species.ROARING_MOON,
|
Species.ROARING_MOON,
|
||||||
Species.BLOODMOON_URSALUNA,
|
Species.BLOODMOON_URSALUNA,
|
||||||
],
|
],
|
||||||
|
classicWaveRewards: [
|
||||||
|
{ wave: 8, type: "SHINY_CHARM" },
|
||||||
|
{ wave: 8, type: "ABILITY_CHARM" },
|
||||||
|
{ wave: 8, type: "CATCHING_CHARM" },
|
||||||
|
{ wave: 25, type: "SHINY_CHARM" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Valentine",
|
name: "Valentine",
|
||||||
@ -177,7 +206,7 @@ const timedEvents: TimedEvent[] = [
|
|||||||
endDate: new Date(Date.UTC(2025, 1, 21)),
|
endDate: new Date(Date.UTC(2025, 1, 21)),
|
||||||
boostFusions: true,
|
boostFusions: true,
|
||||||
shinyMultiplier: 2,
|
shinyMultiplier: 2,
|
||||||
bannerKey: "valentines2025event-",
|
bannerKey: "valentines2025event",
|
||||||
scale: 0.21,
|
scale: 0.21,
|
||||||
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
||||||
eventEncounters: [
|
eventEncounters: [
|
||||||
@ -203,6 +232,12 @@ const timedEvents: TimedEvent[] = [
|
|||||||
{ species: Species.ENAMORUS },
|
{ species: Species.ENAMORUS },
|
||||||
],
|
],
|
||||||
luckBoostedSpecies: [Species.LUVDISC],
|
luckBoostedSpecies: [Species.LUVDISC],
|
||||||
|
classicWaveRewards: [
|
||||||
|
{ wave: 8, type: "SHINY_CHARM" },
|
||||||
|
{ wave: 8, type: "ABILITY_CHARM" },
|
||||||
|
{ wave: 8, type: "CATCHING_CHARM" },
|
||||||
|
{ wave: 25, type: "SHINY_CHARM" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "PKMNDAY2025",
|
name: "PKMNDAY2025",
|
||||||
@ -210,7 +245,7 @@ const timedEvents: TimedEvent[] = [
|
|||||||
startDate: new Date(Date.UTC(2025, 1, 27)),
|
startDate: new Date(Date.UTC(2025, 1, 27)),
|
||||||
endDate: new Date(Date.UTC(2025, 2, 4)),
|
endDate: new Date(Date.UTC(2025, 2, 4)),
|
||||||
classicFriendshipMultiplier: 4,
|
classicFriendshipMultiplier: 4,
|
||||||
bannerKey: "pkmnday2025event-",
|
bannerKey: "pkmnday2025event",
|
||||||
scale: 0.21,
|
scale: 0.21,
|
||||||
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN"],
|
||||||
eventEncounters: [
|
eventEncounters: [
|
||||||
@ -248,6 +283,32 @@ const timedEvents: TimedEvent[] = [
|
|||||||
Species.ZYGARDE,
|
Species.ZYGARDE,
|
||||||
Species.ETERNAL_FLOETTE,
|
Species.ETERNAL_FLOETTE,
|
||||||
],
|
],
|
||||||
|
classicWaveRewards: [
|
||||||
|
{ wave: 8, type: "SHINY_CHARM" },
|
||||||
|
{ wave: 8, type: "ABILITY_CHARM" },
|
||||||
|
{ wave: 8, type: "CATCHING_CHARM" },
|
||||||
|
{ wave: 25, type: "SHINY_CHARM" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "April Fools 2025",
|
||||||
|
eventType: EventType.LUCK,
|
||||||
|
startDate: new Date(Date.UTC(2025, 2, 31)),
|
||||||
|
endDate: new Date(Date.UTC(2025, 3, 3)),
|
||||||
|
bannerKey: "aprf25",
|
||||||
|
scale: 0.21,
|
||||||
|
availableLangs: ["en", "de", "it", "fr", "ja", "ko", "es-ES", "es-MX", "pt-BR", "zh-CN"],
|
||||||
|
trainerShinyChance: 13107, // 13107/65536 = 1/5
|
||||||
|
music: [
|
||||||
|
["title", "title_afd"],
|
||||||
|
["battle_rival_3", "battle_rival_3_afd"],
|
||||||
|
],
|
||||||
|
dailyRunChallenges: [
|
||||||
|
{
|
||||||
|
challenge: Challenges.INVERSE_BATTLE,
|
||||||
|
value: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -265,7 +326,7 @@ export class TimedEventManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
activeEventHasBanner(): boolean {
|
activeEventHasBanner(): boolean {
|
||||||
const activeEvents = timedEvents.filter(te => this.isActive(te) && te.hasOwnProperty("bannerFilename"));
|
const activeEvents = timedEvents.filter(te => this.isActive(te) && te.hasOwnProperty("bannerKey"));
|
||||||
return activeEvents.length > 0;
|
return activeEvents.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,6 +344,12 @@ export class TimedEventManager {
|
|||||||
return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerKey ?? "";
|
return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerKey ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEventBannerLangs(): string[] {
|
||||||
|
const ret: string[] = [];
|
||||||
|
ret.push(...timedEvents.find(te => this.isActive(te) && !isNullOrUndefined(te.availableLangs))?.availableLangs!);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
getEventEncounters(): EventEncounter[] {
|
getEventEncounters(): EventEncounter[] {
|
||||||
const ret: EventEncounter[] = [];
|
const ret: EventEncounter[] = [];
|
||||||
timedEvents
|
timedEvents
|
||||||
@ -417,6 +484,55 @@ export class TimedEventManager {
|
|||||||
areFusionsBoosted(): boolean {
|
areFusionsBoosted(): boolean {
|
||||||
return timedEvents.some(te => this.isActive(te) && te.boostFusions);
|
return timedEvents.some(te => this.isActive(te) && te.boostFusions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all the modifier types associated with a certain wave during an event
|
||||||
|
* @see EventWaveReward
|
||||||
|
* @param wave the wave to check for associated rewards
|
||||||
|
* @returns array of strings of the event modifier reward types
|
||||||
|
*/
|
||||||
|
getFixedBattleEventRewards(wave: number): string[] {
|
||||||
|
const ret: string[] = [];
|
||||||
|
timedEvents
|
||||||
|
.filter(te => this.isActive(te) && !isNullOrUndefined(te.classicWaveRewards))
|
||||||
|
.map(te => {
|
||||||
|
ret.push(...te.classicWaveRewards!.filter(cwr => cwr.wave === wave).map(cwr => cwr.type));
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the extra shiny chance for trainers due to event (odds/65536)
|
||||||
|
getClassicTrainerShinyChance(): number {
|
||||||
|
let ret = 0;
|
||||||
|
const tsEvents = timedEvents.filter(te => this.isActive(te) && !isNullOrUndefined(te.trainerShinyChance));
|
||||||
|
tsEvents.map(t => (ret += t.trainerShinyChance!));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEventBgmReplacement(bgm: string): string {
|
||||||
|
let ret = bgm;
|
||||||
|
timedEvents.map(te => {
|
||||||
|
if (this.isActive(te) && !isNullOrUndefined(te.music)) {
|
||||||
|
te.music.map(mr => {
|
||||||
|
if (mr[0] === bgm) {
|
||||||
|
console.log(`it is ${te.name} so instead of ${mr[0]} we play ${mr[1]}`);
|
||||||
|
ret = mr[1];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activates any challenges on {@linkcode globalScene.gameMode} for the currently active event
|
||||||
|
*/
|
||||||
|
startEventChallenges(): void {
|
||||||
|
const challenges = this.activeEvent()?.dailyRunChallenges;
|
||||||
|
challenges?.forEach((eventChal: EventChallenge) =>
|
||||||
|
globalScene.gameMode.setChallengeValue(eventChal.challenge, eventChal.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TimedEventDisplay extends Phaser.GameObjects.Container {
|
export class TimedEventDisplay extends Phaser.GameObjects.Container {
|
||||||
@ -456,11 +572,12 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container {
|
|||||||
let key = this.event.bannerKey;
|
let key = this.event.bannerKey;
|
||||||
if (lang && this.event.availableLangs && this.event.availableLangs.length > 0) {
|
if (lang && this.event.availableLangs && this.event.availableLangs.length > 0) {
|
||||||
if (this.event.availableLangs.includes(lang)) {
|
if (this.event.availableLangs.includes(lang)) {
|
||||||
key += lang;
|
key += "-" + lang;
|
||||||
} else {
|
} else {
|
||||||
key += "en";
|
key += "-en";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
console.log(key);
|
||||||
console.log(this.event.bannerKey);
|
console.log(this.event.bannerKey);
|
||||||
const padding = 5;
|
const padding = 5;
|
||||||
const showTimer = this.event.eventType !== EventType.NO_TIMER_DISPLAY;
|
const showTimer = this.event.eventType !== EventType.NO_TIMER_DISPLAY;
|
||||||
|
@ -11,6 +11,7 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import type { Species } from "#enums/species";
|
import type { Species } from "#enums/species";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
import { PlayerGender } from "#enums/player-gender";
|
||||||
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
export default class TitleUiHandler extends OptionSelectUiHandler {
|
export default class TitleUiHandler extends OptionSelectUiHandler {
|
||||||
/** If the stats can not be retrieved, use this fallback value */
|
/** If the stats can not be retrieved, use this fallback value */
|
||||||
@ -43,8 +44,8 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
|||||||
logo.setOrigin(0.5, 0);
|
logo.setOrigin(0.5, 0);
|
||||||
this.titleContainer.add(logo);
|
this.titleContainer.add(logo);
|
||||||
|
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
this.eventDisplay = new TimedEventDisplay(0, 0, globalScene.eventManager.activeEvent());
|
this.eventDisplay = new TimedEventDisplay(0, 0, timedEventManager.activeEvent());
|
||||||
this.eventDisplay.setup();
|
this.eventDisplay.setup();
|
||||||
this.titleContainer.add(this.eventDisplay);
|
this.titleContainer.add(this.eventDisplay);
|
||||||
}
|
}
|
||||||
@ -142,7 +143,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler {
|
|||||||
|
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
|
|
||||||
if (globalScene.eventManager.isEventActive()) {
|
if (timedEventManager.isEventActive()) {
|
||||||
this.eventDisplay.setWidth(globalScene.scaledCanvas.width - this.optionSelectBg.width - this.optionSelectBg.x);
|
this.eventDisplay.setWidth(globalScene.scaledCanvas.width - this.optionSelectBg.width - this.optionSelectBg.x);
|
||||||
this.eventDisplay.show();
|
this.eventDisplay.show();
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
64
test/moves/pollen_puff.test.ts
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
@ -114,7 +114,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
bossSegments: 5,
|
bossSegments: 5,
|
||||||
shiny: false,
|
shiny: false,
|
||||||
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
customPokemonData: new CustomPokemonData({ spriteScale: 1.25 }),
|
||||||
nature: Nature.BOLD,
|
nature: Nature.HARDY,
|
||||||
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
moveSet: [Moves.INFESTATION, Moves.SALT_CURE, Moves.GASTRO_ACID, Moves.HEAL_ORDER],
|
||||||
modifierConfigs: expect.any(Array),
|
modifierConfigs: expect.any(Array),
|
||||||
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
tags: [BattlerTagType.MYSTERY_ENCOUNTER_POST_SUMMON],
|
||||||
@ -198,7 +198,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
|||||||
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
|
||||||
expect(enemyField.length).toBe(1);
|
expect(enemyField.length).toBe(1);
|
||||||
expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE);
|
expect(enemyField[0].species.speciesId).toBe(Species.SHUCKLE);
|
||||||
expect(enemyField[0].summonData.statStages).toEqual([0, 2, 0, 2, 0, 0, 0]);
|
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]);
|
||||||
const shuckleItems = enemyField[0].getHeldItems();
|
const shuckleItems = enemyField[0].getHeldItems();
|
||||||
expect(shuckleItems.length).toBe(5);
|
expect(shuckleItems.length).toBe(5);
|
||||||
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.SITRUS)?.stackCount).toBe(
|
expect(shuckleItems.find(m => m instanceof BerryModifier && m.berryType === BerryType.SITRUS)?.stackCount).toBe(
|
||||||
|
@ -1,30 +1,37 @@
|
|||||||
|
import type BattleScene from "#app/battle-scene";
|
||||||
|
import * as BattleAnims from "#app/data/battle-anims";
|
||||||
|
import { TrashToTreasureEncounter } from "#app/data/mystery-encounters/encounters/trash-to-treasure-encounter";
|
||||||
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encounters";
|
||||||
|
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import {
|
||||||
|
type EnemyPartyConfig,
|
||||||
|
type EnemyPokemonConfig,
|
||||||
|
generateModifierType,
|
||||||
|
} from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||||
|
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
||||||
import { Biome } from "#app/enums/biome";
|
import { Biome } from "#app/enums/biome";
|
||||||
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
|
||||||
import { Species } from "#app/enums/species";
|
import { Species } from "#app/enums/species";
|
||||||
import GameManager from "#test/testUtils/gameManager";
|
import { PokemonMove } from "#app/field/pokemon";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { HealShopCostModifier, HitHealModifier, TurnHealModifier } from "#app/modifier/modifier";
|
||||||
import { getPokemonSpecies } from "#app/data/pokemon-species";
|
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||||
import * as BattleAnims from "#app/data/battle-anims";
|
import { modifierTypes, type PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||||
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
import { CommandPhase } from "#app/phases/command-phase";
|
||||||
|
import { MovePhase } from "#app/phases/move-phase";
|
||||||
|
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
||||||
|
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
||||||
|
import { Mode } from "#app/ui/ui";
|
||||||
|
import * as Utils from "#app/utils";
|
||||||
|
import { Moves } from "#enums/moves";
|
||||||
|
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
||||||
|
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||||
import {
|
import {
|
||||||
runMysteryEncounterToEnd,
|
runMysteryEncounterToEnd,
|
||||||
skipBattleRunMysteryEncounterRewardsPhase,
|
skipBattleRunMysteryEncounterRewardsPhase,
|
||||||
} from "#test/mystery-encounter/encounter-test-utils";
|
} from "#test/mystery-encounter/encounter-test-utils";
|
||||||
import { Moves } from "#enums/moves";
|
import GameManager from "#test/testUtils/gameManager";
|
||||||
import type BattleScene from "#app/battle-scene";
|
|
||||||
import { PokemonMove } from "#app/field/pokemon";
|
|
||||||
import { Mode } from "#app/ui/ui";
|
|
||||||
import ModifierSelectUiHandler from "#app/ui/modifier-select-ui-handler";
|
|
||||||
import { HitHealModifier, HealShopCostModifier, TurnHealModifier } from "#app/modifier/modifier";
|
|
||||||
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
|
|
||||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
|
||||||
import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils";
|
import { initSceneWithoutEncounterPhase } from "#test/testUtils/gameManagerUtils";
|
||||||
import { TrashToTreasureEncounter } from "#app/data/mystery-encounters/encounters/trash-to-treasure-encounter";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
|
||||||
import { SelectModifierPhase } from "#app/phases/select-modifier-phase";
|
|
||||||
import { CommandPhase } from "#app/phases/command-phase";
|
|
||||||
import { MovePhase } from "#app/phases/move-phase";
|
|
||||||
|
|
||||||
const namespace = "mysteryEncounters/trashToTreasure";
|
const namespace = "mysteryEncounters/trashToTreasure";
|
||||||
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
const defaultParty = [Species.LAPRAS, Species.GENGAR, Species.ABRA];
|
||||||
@ -73,6 +80,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("should initialize fully", async () => {
|
it("should initialize fully", async () => {
|
||||||
|
vi.spyOn(Utils, "randSeedInt").mockImplementation((range, min = 0) => min + range - 1);
|
||||||
initSceneWithoutEncounterPhase(scene, defaultParty);
|
initSceneWithoutEncounterPhase(scene, defaultParty);
|
||||||
scene.currentBattle.mysteryEncounter = TrashToTreasureEncounter;
|
scene.currentBattle.mysteryEncounter = TrashToTreasureEncounter;
|
||||||
const moveInitSpy = vi.spyOn(BattleAnims, "initMoveAnim");
|
const moveInitSpy = vi.spyOn(BattleAnims, "initMoveAnim");
|
||||||
@ -85,22 +93,61 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||||||
TrashToTreasureEncounter.populateDialogueTokensFromRequirements();
|
TrashToTreasureEncounter.populateDialogueTokensFromRequirements();
|
||||||
const onInitResult = onInit!();
|
const onInitResult = onInit!();
|
||||||
|
|
||||||
expect(TrashToTreasureEncounter.enemyPartyConfigs).toEqual([
|
const bossSpecies = getPokemonSpecies(Species.GARBODOR);
|
||||||
{
|
const pokemonConfig: EnemyPokemonConfig = {
|
||||||
levelAdditiveModifier: 0.5,
|
species: bossSpecies,
|
||||||
disableSwitch: true,
|
|
||||||
pokemonConfigs: [
|
|
||||||
{
|
|
||||||
species: getPokemonSpecies(Species.GARBODOR),
|
|
||||||
isBoss: true,
|
isBoss: true,
|
||||||
shiny: false,
|
shiny: false, // Shiny lock because of custom intro sprite
|
||||||
formIndex: 1,
|
formIndex: 1, // Gmax
|
||||||
bossSegmentModifier: 1,
|
bossSegmentModifier: 1, // +1 Segment from normal
|
||||||
moveSet: [Moves.PAYBACK, Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.DRAIN_PUNCH],
|
moveSet: [Moves.GUNK_SHOT, Moves.STOMPING_TANTRUM, Moves.HAMMER_ARM, Moves.PAYBACK],
|
||||||
|
modifierConfigs: [
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BERRY) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.BASE_STAT_BOOSTER) as PokemonHeldItemModifierType,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.TOXIC_ORB) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: Utils.randSeedInt(2, 0),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.SOOTHE_BELL) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: Utils.randSeedInt(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.LUCKY_EGG) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: Utils.randSeedInt(3, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
modifier: generateModifierType(modifierTypes.GOLDEN_EGG) as PokemonHeldItemModifierType,
|
||||||
|
stackCount: Utils.randSeedInt(2, 0),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
};
|
||||||
]);
|
const config: EnemyPartyConfig = {
|
||||||
|
levelAdditiveModifier: 0.5,
|
||||||
|
pokemonConfigs: [pokemonConfig],
|
||||||
|
disableSwitch: true,
|
||||||
|
};
|
||||||
|
const enemyPartyConfigs = [config];
|
||||||
|
|
||||||
|
expect(JSON.stringify(TrashToTreasureEncounter.enemyPartyConfigs, undefined, 2)).toEqual(
|
||||||
|
JSON.stringify(enemyPartyConfigs, undefined, 2),
|
||||||
|
);
|
||||||
await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled());
|
await vi.waitFor(() => expect(moveInitSpy).toHaveBeenCalled());
|
||||||
await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled());
|
await vi.waitFor(() => expect(moveLoadSpy).toHaveBeenCalled());
|
||||||
expect(onInitResult).toBe(true);
|
expect(onInitResult).toBe(true);
|
||||||
@ -122,7 +169,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should give 2 Leftovers, 2 Shell Bell, and Black Sludge", async () => {
|
it("should give 2 Leftovers, 1 Shell Bell, and Black Sludge", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||||
@ -134,7 +181,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||||||
|
|
||||||
const shellBell = scene.findModifier(m => m instanceof HitHealModifier) as HitHealModifier;
|
const shellBell = scene.findModifier(m => m instanceof HitHealModifier) as HitHealModifier;
|
||||||
expect(shellBell).toBeDefined();
|
expect(shellBell).toBeDefined();
|
||||||
expect(shellBell?.stackCount).toBe(2);
|
expect(shellBell?.stackCount).toBe(1);
|
||||||
|
|
||||||
const blackSludge = scene.findModifier(m => m instanceof HealShopCostModifier) as HealShopCostModifier;
|
const blackSludge = scene.findModifier(m => m instanceof HealShopCostModifier) as HealShopCostModifier;
|
||||||
expect(blackSludge).toBeDefined();
|
expect(blackSludge).toBeDefined();
|
||||||
@ -178,17 +225,17 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
|||||||
expect(enemyField.length).toBe(1);
|
expect(enemyField.length).toBe(1);
|
||||||
expect(enemyField[0].species.speciesId).toBe(Species.GARBODOR);
|
expect(enemyField[0].species.speciesId).toBe(Species.GARBODOR);
|
||||||
expect(enemyField[0].moveset).toEqual([
|
expect(enemyField[0].moveset).toEqual([
|
||||||
new PokemonMove(Moves.PAYBACK),
|
|
||||||
new PokemonMove(Moves.GUNK_SHOT),
|
new PokemonMove(Moves.GUNK_SHOT),
|
||||||
new PokemonMove(Moves.STOMPING_TANTRUM),
|
new PokemonMove(Moves.STOMPING_TANTRUM),
|
||||||
new PokemonMove(Moves.DRAIN_PUNCH),
|
new PokemonMove(Moves.HAMMER_ARM),
|
||||||
|
new PokemonMove(Moves.PAYBACK),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Should have used moves pre-battle
|
// Should have used moves pre-battle
|
||||||
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
|
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
|
||||||
expect(movePhases.length).toBe(2);
|
expect(movePhases.length).toBe(2);
|
||||||
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.TOXIC).length).toBe(1);
|
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.TOXIC).length).toBe(1);
|
||||||
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.AMNESIA).length).toBe(1);
|
expect(movePhases.filter(p => (p as MovePhase).move.moveId === Moves.STOCKPILE).length).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have 2 Rogue, 1 Ultra, 1 Great in rewards", async () => {
|
it("should have 2 Rogue, 1 Ultra, 1 Great in rewards", async () => {
|
||||||
|