mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-07-01 05:52:17 +02:00
Implement NuzLocke related challenges and AI changes
The Nuzlocke challenges that were implemented were the following: - "No Free Heal" which consists in disabling the auto heal that occurs every 10th wave, replacing it with a normal shop phase. Changes made: - Added a new challenge "NO_AUTO_HEAL" with a challenge type "NO_HEAL_PHASE" and created a new function "applyNoHealPhase" to determine whether the Pokémon can heal if the challenge is active ("challenge.ts"). - Added a confirmation on the healing phase to check if the challenge is active or not, and if it is, it should skip the phase ("party-heal-phase.ts"). - Changed the logistic of when the shop should be displayed, so when the challenge is active the shop appears every 10th wave ("modifier-type.ts") and actually push the shop phase ("victory-phase.ts"). - "Hardcore": Challenge divided into two modes, normal and hard, where fainted Pokémon can't be revived, in addition, the hard mode deletes the fainted Pokémon so the player can't switch it's items after death. Changes made: - Added a new challenge "HARDCORE" with several challenge types with the correspondent apply functions ("challenge.ts"), each one is used as follows: - RANDOM_ITEM_BLACKLIST: filter the reward items with only the valid one's ("modifier-type.ts"). - SHOP_ITEM_BLACKLIST: filter the shop items with only the valid one's ("modifier-select-ui-handler.ts"). - MOVE_BLACKLIST: checks if the move selected is allowed and if not sends a message of no apply ("pokemon.ts"). - DELETE_POKEMON: if hard mode was selected, automatically delete the fainted Pokémon from the party ("battle-end-pahse.ts"). - SHOULD_FUSE: changed the logic of should apply function to prohibit the fusion with dead Pokémon ("modifier.ts"). - PREVENT_REVIVE: prevent the gain of hp of fainted Pokémon ("party-heal-phase.ts"). - "Limited Catch": Only the first wild Pokémon encounter of every biome can be added to the player's current party. Changes made: - Added a new challenge LIMITED_CATCH with a challenge type ADD_POKEMON_TO_PARTY and created a new function "applyAddPokemonToParty" to determine whether the Pokémon can be added to the party, which should only occur every 11th wave if it isn't a catchable mystery encounter or every 12th wave if the 11th wave was a catchable mystery encounter ("challenge.ts"). - Changed the logistic of adding a Pokémon where it can be caught so that the "pokedex" is updated but the Pokémon isn't added to the party of the player affecting specifically mystery encounters ("encounter-pokemon-utils.ts") and added the same logic to normal encounters. ("attempt-capture-phase.ts") The changes in the game AI were as follows ("pokemon.ts"): - More accurately accounts for the Pokémon's actual moves and their effectiveness against the player instead of only the pokemon type - Introduced logic to decide when a Pokémon should be sacrificed or switched based on its HP and speed. Signed-off-by: Matilde Simões <matilde.simoes@tecnico.ulisboa.pt> Co-authored-by: Fuad Ali <fuad.ali@tecnico.ulisboa.pt>
This commit is contained in:
parent
ff9aefb0e5
commit
4afb9b695d
@ -25,6 +25,8 @@ import { ModifierTier } from "#enums/modifier-tier";
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { pokemonFormChanges } from "./pokemon-forms";
|
import { pokemonFormChanges } from "./pokemon-forms";
|
||||||
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
import { pokemonEvolutions } from "./balance/pokemon-evolutions";
|
||||||
|
import type { ModifierTypeOption } from "#app/modifier/modifier-type";
|
||||||
|
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||||
import { ChallengeType } from "#enums/challenge-type";
|
import { ChallengeType } from "#enums/challenge-type";
|
||||||
import type { MoveSourceType } from "#enums/move-source-type";
|
import type { MoveSourceType } from "#enums/move-source-type";
|
||||||
|
|
||||||
@ -344,6 +346,84 @@ export abstract class Challenge {
|
|||||||
applyFlipStat(_pokemon: Pokemon, _baseStats: number[]) {
|
applyFlipStat(_pokemon: Pokemon, _baseStats: number[]) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for NO_AUTO_HEAL challenges. Derived classes should alter this.
|
||||||
|
* @param _applyHealPhase {@link BooleanHolder} Whether it should apply the heal phase.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyNoHealPhase(_applyHealPhase: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for PREVENT_REVIVE. Derived classes should alter this.
|
||||||
|
* @param _canBeRevived {@link BooleanHolder} Whether it should revive the fainted Pokemon.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyRevivePrevention(_canBeRevived: BooleanHolder): boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for RANDOM_ITEM_BLACKLIST. Derived classes should alter this.
|
||||||
|
* @param _randomItem {@link ModifierTypeOption} The random item in question.
|
||||||
|
* @param _isValid {@link BooleanHolder} Whether it should load the random item.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyRandomItemBlacklist(_randomItem: ModifierTypeOption | null, _isValid: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for SHOP_ITEM_BLACKLIST. Derived classes should alter this.
|
||||||
|
* @param _shopItem {@link ModifierTypeOption} The shop item in question.
|
||||||
|
* @param _isValid {@link BooleanHolder} Whether the shop should have the item.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyShopItemBlacklist(_shopItem: ModifierTypeOption | null, _isValid: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for MOVE_BLACKLIST. Derived classes should alter this.
|
||||||
|
* @param _move {@link PokemonMove} The move in question.
|
||||||
|
* @param _isValid {@link BooleanHolder} Whether the move should be allowed.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyMoveBlacklist(_move: PokemonMove, _isValid: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for DELETE_POKEMON. Derived classes should alter this.
|
||||||
|
* @param _canStay {@link BooleanHolder} Whether the pokemon can stay in team after death.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyDeletePokemon(_canStay: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for ADD_POKEMON_TO_PARTY. Derived classes should alter this.
|
||||||
|
* @param _waveIndex {@link BooleanHolder} The current wave.
|
||||||
|
* @param _canAddToParty {@link BooleanHolder} Whether the pokemon can be caught.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyAddPokemonToParty(_waveIndex: number, _canAddToParty: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An apply function for SHOULD_FUSE. Derived classes should alter this.
|
||||||
|
* @param _pokemon {@link Pokemon} The first chosen pokemon for fusion.
|
||||||
|
* @param _pokemonTofuse {@link Pokemon} The second chosen pokemon for fusion.
|
||||||
|
* @param _canFuse {@link BooleanHolder} Whether the pokemons can fuse.
|
||||||
|
* @returns {@link boolean} if this function did anything.
|
||||||
|
*/
|
||||||
|
applyShouldFuse(_pokemon: Pokemon, _pokemonTofuse: Pokemon, _canFuse: BooleanHolder): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChallengeCondition = (data: GameData) => boolean;
|
type ChallengeCondition = (data: GameData) => boolean;
|
||||||
@ -888,6 +968,123 @@ export class LowerStarterPointsChallenge extends Challenge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Challenge stops pokemon from healing every 10th wave
|
||||||
|
*/
|
||||||
|
export class NoFreeHealsChallenge extends Challenge {
|
||||||
|
constructor() {
|
||||||
|
super(Challenges.NO_AUTO_HEAL, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyNoHealPhase(applyHealPhase: BooleanHolder): boolean {
|
||||||
|
applyHealPhase.value = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static loadChallenge(source: NoFreeHealsChallenge | any): NoFreeHealsChallenge {
|
||||||
|
const newChallenge = new NoFreeHealsChallenge();
|
||||||
|
newChallenge.value = source.value;
|
||||||
|
newChallenge.severity = source.severity;
|
||||||
|
return newChallenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Challenge that removes the ability to revive fallen pokemon
|
||||||
|
*/
|
||||||
|
export class HardcoreChallenge extends Challenge {
|
||||||
|
private itemBlackList = [
|
||||||
|
"modifierType:ModifierType.REVIVE",
|
||||||
|
"modifierType:ModifierType.MAX_REVIVE",
|
||||||
|
"modifierType:ModifierType.SACRED_ASH",
|
||||||
|
"modifierType:ModifierType.REVIVER_SEED",
|
||||||
|
];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super(Challenges.HARDCORE, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRandomItemBlacklist(randomItem: ModifierTypeOption, isValid: BooleanHolder): boolean {
|
||||||
|
if (randomItem !== null) {
|
||||||
|
isValid.value = !this.itemBlackList.includes(randomItem.type.localeKey);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyShopItemBlacklist(shopItem: ModifierTypeOption, isValid: BooleanHolder): boolean {
|
||||||
|
isValid.value = !this.itemBlackList.includes(shopItem.type.localeKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMoveBlacklist(move: PokemonMove, moveCanBeUsed: BooleanHolder): boolean {
|
||||||
|
const moveBlacklist = [Moves.REVIVAL_BLESSING];
|
||||||
|
moveCanBeUsed.value = !moveBlacklist.includes(move.moveId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyRevivePrevention(canBeRevived: BooleanHolder): boolean {
|
||||||
|
canBeRevived.value = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyDeletePokemon(canStay: BooleanHolder): boolean {
|
||||||
|
if (this.value === 2) {
|
||||||
|
canStay.value = false;
|
||||||
|
} else {
|
||||||
|
canStay.value = true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
override applyShouldFuse(pokemon: Pokemon, pokemonToFuse: Pokemon, canFuse: BooleanHolder): boolean {
|
||||||
|
if (pokemon!.isFainted() || pokemonToFuse.isFainted()) {
|
||||||
|
canFuse.value = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static override loadChallenge(source: HardcoreChallenge | any): HardcoreChallenge {
|
||||||
|
const newChallenge = new HardcoreChallenge();
|
||||||
|
newChallenge.value = source.value;
|
||||||
|
newChallenge.severity = source.severity;
|
||||||
|
return newChallenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Challenge that limits the amount of caught pokemons by 1 per biome stage
|
||||||
|
*/
|
||||||
|
export class LimitedCatchChallenge extends Challenge {
|
||||||
|
private mysteryEncounterBlacklist = [
|
||||||
|
MysteryEncounterType.ABSOLUTE_AVARICE,
|
||||||
|
MysteryEncounterType.DANCING_LESSONS,
|
||||||
|
MysteryEncounterType.SAFARI_ZONE,
|
||||||
|
MysteryEncounterType.THE_POKEMON_SALESMAN,
|
||||||
|
MysteryEncounterType.UNCOMMON_BREED,
|
||||||
|
];
|
||||||
|
constructor() {
|
||||||
|
super(Challenges.LIMITED_CATCH, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
override applyAddPokemonToParty(waveIndex: number, canAddToParty: BooleanHolder): boolean {
|
||||||
|
const lastMystery = globalScene.lastMysteryEncounter?.encounterType;
|
||||||
|
if (lastMystery === undefined && !(waveIndex % 10 === 1)) {
|
||||||
|
canAddToParty.value = false;
|
||||||
|
}
|
||||||
|
if (!(waveIndex % 10 === 1) && !(!this.mysteryEncounterBlacklist.includes(lastMystery!) && waveIndex % 10 === 2)) {
|
||||||
|
canAddToParty.value = false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static override loadChallenge(source: LimitedCatchChallenge | any): LimitedCatchChallenge {
|
||||||
|
const newChallenge = new LimitedCatchChallenge();
|
||||||
|
newChallenge.value = source.value;
|
||||||
|
newChallenge.severity = source.severity;
|
||||||
|
return newChallenge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply all challenges that modify starter choice.
|
* Apply all challenges that modify starter choice.
|
||||||
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE
|
* @param challengeType {@link ChallengeType} ChallengeType.STARTER_CHOICE
|
||||||
@ -1039,6 +1236,90 @@ export function applyChallenges(
|
|||||||
): boolean;
|
): boolean;
|
||||||
|
|
||||||
export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
|
export function applyChallenges(challengeType: ChallengeType.FLIP_STAT, pokemon: Pokemon, baseStats: number[]): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon can be auto healed or not in wave 10m.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.NO_HEAL_PHASE
|
||||||
|
* @param applyHealPhase {@link BooleanHolder} Whether it should apply the heal phase.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(challengeType: ChallengeType.NO_HEAL_PHASE, applyHealPhase: BooleanHolder): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a shop item should be blacklisted.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.SHOP_ITEM_BLACKLIST
|
||||||
|
* @param shopItem {@link ModifierTypeOption} The shop item in question.
|
||||||
|
* @param isValid {@link BooleanHolder} Whether the shop should have the item.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(
|
||||||
|
challengeType: ChallengeType.SHOP_ITEM_BLACKLIST,
|
||||||
|
shopItem: ModifierTypeOption | null,
|
||||||
|
isValid: BooleanHolder,
|
||||||
|
): boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a reward item should be blacklisted.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.RANDOM_ITEM_BLACKLIST
|
||||||
|
* @param randomItem {@link ModifierTypeOption} The random item in question.
|
||||||
|
* @param isValid {@link BooleanHolder} Whether it should load the random item.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(
|
||||||
|
challengeType: ChallengeType.RANDOM_ITEM_BLACKLIST,
|
||||||
|
randomItem: ModifierTypeOption | null,
|
||||||
|
isValid: BooleanHolder,
|
||||||
|
): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon move should be blacklisted.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.MOVE_BLACKLIST
|
||||||
|
* @param move {@link PokemonMove} The move in question.
|
||||||
|
* @param isValid {@link BooleanHolder} Whether the move should be allowed.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(
|
||||||
|
challengeType: ChallengeType.MOVE_BLACKLIST,
|
||||||
|
move: PokemonMove,
|
||||||
|
isValid: BooleanHolder,
|
||||||
|
): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon should be removed from the team.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.DELETE_POKEMON
|
||||||
|
* @param canStay {@link BooleanHolder} Whether the pokemon can stay in team after death.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(challengeType: ChallengeType.DELETE_POKEMON, canStay: BooleanHolder): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon should revive.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.PREVENT_REVIVE
|
||||||
|
* @param canBeRevived {@link BooleanHolder} Whether it should revive the fainted Pokemon.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(challengeType: ChallengeType.PREVENT_REVIVE, canBeRevived: BooleanHolder): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon can be caught.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.ADD_POKEMON_TO_PARTY
|
||||||
|
* @param waveIndex {@link BooleanHolder} The current wave.
|
||||||
|
* @param canAddToParty {@link BooleanHolder} Whether the pokemon can be caught.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(
|
||||||
|
challengeType: ChallengeType.ADD_POKEMON_TO_PARTY,
|
||||||
|
waveIndex: number,
|
||||||
|
canAddToParty: BooleanHolder,
|
||||||
|
): boolean;
|
||||||
|
/**
|
||||||
|
* Apply all challenges that modify whether a pokemon can fuse.
|
||||||
|
* @param challengeType {@link ChallengeType} ChallengeType.SHOULD_FUSE
|
||||||
|
* @param pokemon {@link Pokemon} The first chosen pokemon for fusion.
|
||||||
|
* @param pokemonTofuse {@link Pokemon} The second chosen pokemon for fusion.
|
||||||
|
* @param canFuse {@link BooleanHolder} Whether the pokemons can fuse.
|
||||||
|
* @returns True if any challenge was successfully applied.
|
||||||
|
*/
|
||||||
|
export function applyChallenges(
|
||||||
|
challengeType: ChallengeType.SHOULD_FUSE,
|
||||||
|
pokemon: Pokemon,
|
||||||
|
pokemonTofuse: Pokemon,
|
||||||
|
canFuse: BooleanHolder,
|
||||||
|
): boolean;
|
||||||
|
|
||||||
export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean {
|
export function applyChallenges(challengeType: ChallengeType, ...args: any[]): boolean {
|
||||||
let ret = false;
|
let ret = false;
|
||||||
@ -1087,6 +1368,30 @@ export function applyChallenges(challengeType: ChallengeType, ...args: any[]): b
|
|||||||
case ChallengeType.FLIP_STAT:
|
case ChallengeType.FLIP_STAT:
|
||||||
ret ||= c.applyFlipStat(args[0], args[1]);
|
ret ||= c.applyFlipStat(args[0], args[1]);
|
||||||
break;
|
break;
|
||||||
|
case ChallengeType.NO_HEAL_PHASE:
|
||||||
|
ret ||= c.applyNoHealPhase(args[0]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.SHOP_ITEM_BLACKLIST:
|
||||||
|
ret ||= c.applyShopItemBlacklist(args[0], args[1]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.RANDOM_ITEM_BLACKLIST:
|
||||||
|
ret ||= c.applyRandomItemBlacklist(args[0], args[1]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.MOVE_BLACKLIST:
|
||||||
|
ret ||= c.applyMoveBlacklist(args[0], args[1]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.DELETE_POKEMON:
|
||||||
|
ret ||= c.applyDeletePokemon(args[0]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.PREVENT_REVIVE:
|
||||||
|
ret ||= c.applyRevivePrevention(args[0]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.ADD_POKEMON_TO_PARTY:
|
||||||
|
ret ||= c.applyAddPokemonToParty(args[0], args[1]);
|
||||||
|
break;
|
||||||
|
case ChallengeType.SHOULD_FUSE:
|
||||||
|
ret ||= c.applyShouldFuse(args[0], args[1], args[2]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -1114,6 +1419,12 @@ export function copyChallenge(source: Challenge | any): Challenge {
|
|||||||
return InverseBattleChallenge.loadChallenge(source);
|
return InverseBattleChallenge.loadChallenge(source);
|
||||||
case Challenges.FLIP_STAT:
|
case Challenges.FLIP_STAT:
|
||||||
return FlipStatChallenge.loadChallenge(source);
|
return FlipStatChallenge.loadChallenge(source);
|
||||||
|
case Challenges.NO_AUTO_HEAL:
|
||||||
|
return NoFreeHealsChallenge.loadChallenge(source);
|
||||||
|
case Challenges.HARDCORE:
|
||||||
|
return HardcoreChallenge.loadChallenge(source);
|
||||||
|
case Challenges.LIMITED_CATCH:
|
||||||
|
return LimitedCatchChallenge.loadChallenge(source);
|
||||||
}
|
}
|
||||||
throw new Error("Unknown challenge copied");
|
throw new Error("Unknown challenge copied");
|
||||||
}
|
}
|
||||||
@ -1127,6 +1438,9 @@ export function initChallenges() {
|
|||||||
new FreshStartChallenge(),
|
new FreshStartChallenge(),
|
||||||
new InverseBattleChallenge(),
|
new InverseBattleChallenge(),
|
||||||
new FlipStatChallenge(),
|
new FlipStatChallenge(),
|
||||||
|
new NoFreeHealsChallenge(),
|
||||||
|
new LimitedCatchChallenge(),
|
||||||
|
new HardcoreChallenge(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import { EggTier } from "#enums/egg-type";
|
|||||||
import { ModifierTier } from "#enums/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { modifierTypes } from "#app/data/data-lists";
|
import { modifierTypes } from "#app/data/data-lists";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/aTrainersTest";
|
const namespace = "mysteryEncounters/aTrainersTest";
|
||||||
@ -34,6 +35,7 @@ const namespace = "mysteryEncounters/aTrainersTest";
|
|||||||
export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
export const ATrainersTestEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.A_TRAINERS_TEST,
|
MysteryEncounterType.A_TRAINERS_TEST,
|
||||||
)
|
)
|
||||||
|
.withDisallowedChallenges(Challenges.NO_AUTO_HEAL)
|
||||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
.withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
|
.withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
|
||||||
.withIntroSpriteConfigs([]) // These are set in onInit()
|
.withIntroSpriteConfigs([]) // These are set in onInit()
|
||||||
|
@ -31,6 +31,7 @@ import { BerryType } from "#enums/berry-type";
|
|||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
||||||
import { randSeedInt } from "#app/utils/common";
|
import { randSeedInt } from "#app/utils/common";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
|
||||||
/** i18n namespace for the encounter */
|
/** i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/slumberingSnorlax";
|
const namespace = "mysteryEncounters/slumberingSnorlax";
|
||||||
@ -43,6 +44,7 @@ const namespace = "mysteryEncounters/slumberingSnorlax";
|
|||||||
export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
export const SlumberingSnorlaxEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.SLUMBERING_SNORLAX,
|
MysteryEncounterType.SLUMBERING_SNORLAX,
|
||||||
)
|
)
|
||||||
|
.withDisallowedChallenges(Challenges.NO_AUTO_HEAL)
|
||||||
.withEncounterTier(MysteryEncounterTier.GREAT)
|
.withEncounterTier(MysteryEncounterTier.GREAT)
|
||||||
.withSceneWaveRangeRequirement(15, 150)
|
.withSceneWaveRangeRequirement(15, 150)
|
||||||
.withCatchAllowed(true)
|
.withCatchAllowed(true)
|
||||||
|
@ -31,6 +31,7 @@ import i18next from "i18next";
|
|||||||
import { ModifierTier } from "#enums/modifier-tier";
|
import { ModifierTier } from "#enums/modifier-tier";
|
||||||
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/constants";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
|
import { Challenges } from "#enums/challenges";
|
||||||
|
|
||||||
/** the i18n namespace for the encounter */
|
/** the i18n namespace for the encounter */
|
||||||
const namespace = "mysteryEncounters/theWinstrateChallenge";
|
const namespace = "mysteryEncounters/theWinstrateChallenge";
|
||||||
@ -43,6 +44,7 @@ const namespace = "mysteryEncounters/theWinstrateChallenge";
|
|||||||
export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
export const TheWinstrateChallengeEncounter: MysteryEncounter = MysteryEncounterBuilder.withEncounterType(
|
||||||
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
MysteryEncounterType.THE_WINSTRATE_CHALLENGE,
|
||||||
)
|
)
|
||||||
|
.withDisallowedChallenges(Challenges.NO_AUTO_HEAL)
|
||||||
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
.withEncounterTier(MysteryEncounterTier.ROGUE)
|
||||||
.withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
|
.withSceneWaveRangeRequirement(100, CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES[1])
|
||||||
.withIntroSpriteConfigs([
|
.withIntroSpriteConfigs([
|
||||||
|
@ -37,6 +37,8 @@ import { CustomPokemonData } from "#app/data/custom-pokemon-data";
|
|||||||
import type { AbilityId } from "#enums/ability-id";
|
import type { AbilityId } from "#enums/ability-id";
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
import { StatusEffect } from "#enums/status-effect";
|
import { StatusEffect } from "#enums/status-effect";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { ChallengeType, applyChallenges } from "#app/data/challenge";
|
||||||
|
|
||||||
/** Will give +1 level every 10 waves */
|
/** Will give +1 level every 10 waves */
|
||||||
export const STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER = 1;
|
export const STANDARD_ENCOUNTER_BOOSTED_LEVEL_MODIFIER = 1;
|
||||||
@ -703,6 +705,17 @@ export async function catchPokemon(
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
|
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
|
||||||
|
const challengeCanAddToParty = new BooleanHolder(true);
|
||||||
|
applyChallenges(
|
||||||
|
ChallengeType.ADD_POKEMON_TO_PARTY,
|
||||||
|
globalScene.currentBattle.waveIndex,
|
||||||
|
challengeCanAddToParty,
|
||||||
|
);
|
||||||
|
if (!challengeCanAddToParty.value) {
|
||||||
|
removePokemon();
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (globalScene.getPlayerParty().length === 6) {
|
if (globalScene.getPlayerParty().length === 6) {
|
||||||
const promptRelease = () => {
|
const promptRelease = () => {
|
||||||
globalScene.ui.showText(
|
globalScene.ui.showText(
|
||||||
|
@ -6,4 +6,7 @@ export enum Challenges {
|
|||||||
FRESH_START,
|
FRESH_START,
|
||||||
INVERSE_BATTLE,
|
INVERSE_BATTLE,
|
||||||
FLIP_STAT,
|
FLIP_STAT,
|
||||||
|
NO_AUTO_HEAL,
|
||||||
|
HARDCORE,
|
||||||
|
LIMITED_CATCH,
|
||||||
}
|
}
|
||||||
|
@ -2504,14 +2504,36 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
defScore *=
|
defScore *=
|
||||||
1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25);
|
1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[1], opponent, false, false, undefined, true), 0.25);
|
||||||
}
|
}
|
||||||
|
atkScore *= 1.25; //give more value for the pokemon's typing
|
||||||
|
const moveset = this.moveset;
|
||||||
|
let moveAtkScoreLenght = 0;
|
||||||
|
for (const move of moveset) {
|
||||||
|
if (move.getMove().category === MoveCategory.SPECIAL || move.getMove().category === MoveCategory.PHYSICAL) {
|
||||||
|
atkScore += opponent.getAttackTypeEffectiveness(move.getMove().type, this, false, true, undefined, true);
|
||||||
|
moveAtkScoreLenght++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
atkScore = atkScore / (moveAtkScoreLenght + 1); //calculate the median for the attack score
|
||||||
/**
|
/**
|
||||||
* Based on this Pokemon's HP ratio compared to that of the opponent.
|
* Based on this Pokemon's HP ratio compared to that of the opponent.
|
||||||
* This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent;
|
* This ratio is multiplied by 1.5 if this Pokemon outspeeds the opponent;
|
||||||
* however, the final ratio cannot be higher than 1.
|
* however, the final ratio cannot be higher than 1.
|
||||||
*/
|
*/
|
||||||
let hpDiffRatio = this.getHpRatio() + (1 - opponent.getHpRatio());
|
const hpRatio = this.getHpRatio();
|
||||||
if (outspeed) {
|
const oppHpRatio = opponent.getHpRatio();
|
||||||
hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1);
|
const isDying = hpRatio <= 0.2;
|
||||||
|
let hpDiffRatio = hpRatio + (1 - oppHpRatio);
|
||||||
|
if (isDying && this.isActive(true)) { //It might be a sacrifice candidate if hp under 20%
|
||||||
|
const badMatchup = atkScore < 1.5 && defScore < 1.5;
|
||||||
|
if (!outspeed && badMatchup) { //It might not be a worthy sacrifice if it doesn't outspeed or doesn't do enough damage
|
||||||
|
hpDiffRatio *= 0.85;
|
||||||
|
} else {
|
||||||
|
hpDiffRatio = Math.min((1 - hpRatio) + (outspeed ? 0.2 : 0.1), 1);
|
||||||
|
}
|
||||||
|
} else if (outspeed) {
|
||||||
|
hpDiffRatio = Math.min(hpDiffRatio * 1.25, 1);
|
||||||
|
} else if (hpRatio > 0.2 && hpRatio <= 0.4) { //Might be considered to be switched because it's not in low enough health
|
||||||
|
hpDiffRatio = Math.min (hpDiffRatio * 0.5, 1);
|
||||||
}
|
}
|
||||||
return (atkScore + defScore) * hpDiffRatio;
|
return (atkScore + defScore) * hpDiffRatio;
|
||||||
}
|
}
|
||||||
@ -3206,6 +3228,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
public trySelectMove(moveIndex: number, ignorePp?: boolean): boolean {
|
public trySelectMove(moveIndex: number, ignorePp?: boolean): boolean {
|
||||||
const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null;
|
const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null;
|
||||||
|
if (move !== null) {
|
||||||
|
const isValid = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.MOVE_BLACKLIST, move!, isValid);
|
||||||
|
if (!isValid.value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return move?.isUsable(this, ignorePp) ?? false;
|
return move?.isUsable(this, ignorePp) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +111,7 @@ import {
|
|||||||
NumberHolder,
|
NumberHolder,
|
||||||
padInt,
|
padInt,
|
||||||
randSeedInt,
|
randSeedInt,
|
||||||
|
BooleanHolder,
|
||||||
} from "#app/utils/common";
|
} from "#app/utils/common";
|
||||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||||
import { BerryType } from "#enums/berry-type";
|
import { BerryType } from "#enums/berry-type";
|
||||||
@ -128,6 +129,7 @@ import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants";
|
|||||||
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
import { ModifierPoolType } from "#enums/modifier-pool-type";
|
||||||
import { getModifierPoolForType, getModifierType } from "#app/utils/modifier-utils";
|
import { getModifierPoolForType, getModifierType } from "#app/utils/modifier-utils";
|
||||||
import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types";
|
import type { ModifierTypeFunc, WeightedModifierTypeWeightFunc } from "#app/@types/modifier-types";
|
||||||
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
|
|
||||||
const outputModifierData = false;
|
const outputModifierData = false;
|
||||||
const useMaxWeightForOutput = false;
|
const useMaxWeightForOutput = false;
|
||||||
@ -2632,10 +2634,14 @@ function getModifierTypeOptionWithRetry(
|
|||||||
allowLuckUpgrades = allowLuckUpgrades ?? true;
|
allowLuckUpgrades = allowLuckUpgrades ?? true;
|
||||||
let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades);
|
let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, tier, undefined, 0, allowLuckUpgrades);
|
||||||
let r = 0;
|
let r = 0;
|
||||||
|
const isValidForChallenge = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.RANDOM_ITEM_BLACKLIST, candidate, isValidForChallenge);
|
||||||
while (
|
while (
|
||||||
existingOptions.length &&
|
(existingOptions.length &&
|
||||||
++r < retryCount &&
|
++r < retryCount &&
|
||||||
existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group).length
|
existingOptions.filter(o => o.type.name === candidate?.type.name || o.type.group === candidate?.type.group)
|
||||||
|
.length) ||
|
||||||
|
!isValidForChallenge.value
|
||||||
) {
|
) {
|
||||||
candidate = getNewModifierTypeOption(
|
candidate = getNewModifierTypeOption(
|
||||||
party,
|
party,
|
||||||
@ -2645,6 +2651,7 @@ function getModifierTypeOptionWithRetry(
|
|||||||
0,
|
0,
|
||||||
allowLuckUpgrades,
|
allowLuckUpgrades,
|
||||||
);
|
);
|
||||||
|
applyChallenges(ChallengeType.RANDOM_ITEM_BLACKLIST, candidate, isValidForChallenge);
|
||||||
}
|
}
|
||||||
return candidate!;
|
return candidate!;
|
||||||
}
|
}
|
||||||
@ -2675,7 +2682,9 @@ export function overridePlayerModifierTypeOptions(options: ModifierTypeOption[],
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseCost: number): ModifierTypeOption[] {
|
export function getPlayerShopModifierTypeOptionsForWave(waveIndex: number, baseCost: number): ModifierTypeOption[] {
|
||||||
if (!(waveIndex % 10)) {
|
const isHealPhaseActive = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.NO_HEAL_PHASE, isHealPhaseActive);
|
||||||
|
if (!(waveIndex % 10) && isHealPhaseActive.value) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ import { FRIENDSHIP_GAIN_FROM_RARE_CANDY } from "#app/data/balance/starters";
|
|||||||
import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
import { applyAbAttrs, applyPostItemLostAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types";
|
import type { ModifierInstanceMap, ModifierString } from "#app/@types/modifier-types";
|
||||||
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
|
|
||||||
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
export type ModifierPredicate = (modifier: Modifier) => boolean;
|
||||||
|
|
||||||
@ -2446,8 +2447,13 @@ export class FusePokemonModifier extends ConsumablePokemonModifier {
|
|||||||
* @returns `true` if {@linkcode FusePokemonModifier} should be applied
|
* @returns `true` if {@linkcode FusePokemonModifier} should be applied
|
||||||
*/
|
*/
|
||||||
override shouldApply(playerPokemon?: PlayerPokemon, playerPokemon2?: PlayerPokemon): boolean {
|
override shouldApply(playerPokemon?: PlayerPokemon, playerPokemon2?: PlayerPokemon): boolean {
|
||||||
|
const shouldFuse = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.SHOULD_FUSE, playerPokemon!, playerPokemon2!, shouldFuse);
|
||||||
return (
|
return (
|
||||||
super.shouldApply(playerPokemon, playerPokemon2) && !!playerPokemon2 && this.fusePokemonId === playerPokemon2.id
|
super.shouldApply(playerPokemon, playerPokemon2) &&
|
||||||
|
!!playerPokemon2 &&
|
||||||
|
this.fusePokemonId === playerPokemon2.id &&
|
||||||
|
shouldFuse.value
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ import { StatusEffect } from "#enums/status-effect";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Gender } from "#app/data/gender";
|
import { Gender } from "#app/data/gender";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { ChallengeType, applyChallenges } from "#app/data/challenge";
|
||||||
|
|
||||||
export class AttemptCapturePhase extends PokemonPhase {
|
export class AttemptCapturePhase extends PokemonPhase {
|
||||||
public readonly phaseName = "AttemptCapturePhase";
|
public readonly phaseName = "AttemptCapturePhase";
|
||||||
@ -285,6 +287,17 @@ export class AttemptCapturePhase extends PokemonPhase {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
|
Promise.all([pokemon.hideInfo(), globalScene.gameData.setPokemonCaught(pokemon)]).then(() => {
|
||||||
|
const challengeCanAddToParty = new BooleanHolder(true);
|
||||||
|
applyChallenges(
|
||||||
|
ChallengeType.ADD_POKEMON_TO_PARTY,
|
||||||
|
globalScene.currentBattle.waveIndex,
|
||||||
|
challengeCanAddToParty,
|
||||||
|
);
|
||||||
|
if (!challengeCanAddToParty.value) {
|
||||||
|
removePokemon();
|
||||||
|
end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (globalScene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) {
|
if (globalScene.getPlayerParty().length === PLAYER_PARTY_MAX_SIZE) {
|
||||||
const promptRelease = () => {
|
const promptRelease = () => {
|
||||||
globalScene.ui.showText(
|
globalScene.ui.showText(
|
||||||
|
@ -2,6 +2,8 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
import { applyPostBattleAbAttrs } from "#app/data/abilities/apply-ab-attrs";
|
||||||
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
import { LapsingPersistentModifier, LapsingPokemonHeldItemModifier } from "#app/modifier/modifier";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
||||||
|
|
||||||
export class BattleEndPhase extends BattlePhase {
|
export class BattleEndPhase extends BattlePhase {
|
||||||
public readonly phaseName = "BattleEndPhase";
|
public readonly phaseName = "BattleEndPhase";
|
||||||
@ -67,6 +69,16 @@ export class BattleEndPhase extends BattlePhase {
|
|||||||
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
|
for (const pokemon of globalScene.getPokemonAllowedInBattle()) {
|
||||||
applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory);
|
applyPostBattleAbAttrs("PostBattleAbAttr", pokemon, false, this.isVictory);
|
||||||
}
|
}
|
||||||
|
const canStay = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.DELETE_POKEMON, canStay);
|
||||||
|
if (!canStay.value) {
|
||||||
|
const party = globalScene.getPlayerParty().slice();
|
||||||
|
for (const pokemon of party) {
|
||||||
|
if (pokemon.isFainted()) {
|
||||||
|
globalScene.removePokemonFromPlayerParty(pokemon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (globalScene.currentBattle.moneyScattered) {
|
if (globalScene.currentBattle.moneyScattered) {
|
||||||
globalScene.currentBattle.pickUpScatteredMoney();
|
globalScene.currentBattle.pickUpScatteredMoney();
|
||||||
|
@ -218,7 +218,9 @@ export class CommandPhase extends FieldPhase {
|
|||||||
.selectionDeniedText(playerPokemon, move.moveId)
|
.selectionDeniedText(playerPokemon, move.moveId)
|
||||||
: move.getName().endsWith(" (N)")
|
: move.getName().endsWith(" (N)")
|
||||||
? "battle:moveNotImplemented"
|
? "battle:moveNotImplemented"
|
||||||
: "battle:moveNoPP";
|
: move.getPpRatio()
|
||||||
|
? "battle:moveDisabled"
|
||||||
|
: "battle:moveNoPP";
|
||||||
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
|
const moveName = move.getName().replace(" (N)", ""); // Trims off the indicator
|
||||||
|
|
||||||
globalScene.ui.showText(
|
globalScene.ui.showText(
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { fixedInt } from "#app/utils/common";
|
import { BooleanHolder, fixedInt } from "#app/utils/common";
|
||||||
import { BattlePhase } from "./battle-phase";
|
import { BattlePhase } from "./battle-phase";
|
||||||
|
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
||||||
|
|
||||||
export class PartyHealPhase extends BattlePhase {
|
export class PartyHealPhase extends BattlePhase {
|
||||||
public readonly phaseName = "PartyHealPhase";
|
public readonly phaseName = "PartyHealPhase";
|
||||||
@ -15,18 +16,27 @@ export class PartyHealPhase extends BattlePhase {
|
|||||||
start() {
|
start() {
|
||||||
super.start();
|
super.start();
|
||||||
|
|
||||||
|
const isHealPhaseActive = new BooleanHolder(true);
|
||||||
|
const isReviveActive = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.NO_HEAL_PHASE, isHealPhaseActive);
|
||||||
|
if (!isHealPhaseActive.value) {
|
||||||
|
return this.end();
|
||||||
|
}
|
||||||
const bgmPlaying = globalScene.isBgmPlaying();
|
const bgmPlaying = globalScene.isBgmPlaying();
|
||||||
if (bgmPlaying) {
|
if (bgmPlaying) {
|
||||||
globalScene.fadeOutBgm(1000, false);
|
globalScene.fadeOutBgm(1000, false);
|
||||||
}
|
}
|
||||||
globalScene.ui.fadeOut(1000).then(() => {
|
globalScene.ui.fadeOut(1000).then(() => {
|
||||||
for (const pokemon of globalScene.getPlayerParty()) {
|
for (const pokemon of globalScene.getPlayerParty()) {
|
||||||
pokemon.hp = pokemon.getMaxHp();
|
applyChallenges(ChallengeType.PREVENT_REVIVE, isReviveActive);
|
||||||
pokemon.resetStatus(true, false, false, true);
|
if (isReviveActive.value || !pokemon.isFainted()) {
|
||||||
for (const move of pokemon.moveset) {
|
pokemon.hp = pokemon.getMaxHp();
|
||||||
move.ppUsed = 0;
|
pokemon.resetStatus(true, false, false, true);
|
||||||
|
for (const move of pokemon.moveset) {
|
||||||
|
move.ppUsed = 0;
|
||||||
|
}
|
||||||
|
pokemon.updateInfo(true);
|
||||||
}
|
}
|
||||||
pokemon.updateInfo(true);
|
|
||||||
}
|
}
|
||||||
const healSong = globalScene.playSoundWithoutBgm("heal");
|
const healSong = globalScene.playSoundWithoutBgm("heal");
|
||||||
globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => {
|
globalScene.time.delayedCall(fixedInt(healSong.totalDuration * 1000), () => {
|
||||||
|
@ -8,6 +8,9 @@ import { handleMysteryEncounterVictory } from "#app/data/mystery-encounters/util
|
|||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { timedEventManager } from "#app/global-event-manager";
|
import { timedEventManager } from "#app/global-event-manager";
|
||||||
|
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
import { applyChallenges } from "#app/data/challenge";
|
||||||
|
|
||||||
export class VictoryPhase extends PokemonPhase {
|
export class VictoryPhase extends PokemonPhase {
|
||||||
public readonly phaseName = "VictoryPhase";
|
public readonly phaseName = "VictoryPhase";
|
||||||
/** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */
|
/** If true, indicates that the phase is intended for EXP purposes only, and not to continue a battle to next phase */
|
||||||
@ -37,6 +40,8 @@ export class VictoryPhase extends PokemonPhase {
|
|||||||
return this.end();
|
return this.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isHealPhaseActive = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.NO_HEAL_PHASE, isHealPhaseActive);
|
||||||
if (
|
if (
|
||||||
!globalScene
|
!globalScene
|
||||||
.getEnemyParty()
|
.getEnemyParty()
|
||||||
@ -104,6 +109,10 @@ export class VictoryPhase extends PokemonPhase {
|
|||||||
);
|
);
|
||||||
globalScene.phaseManager.pushNew("AddEnemyBuffModifierPhase");
|
globalScene.phaseManager.pushNew("AddEnemyBuffModifierPhase");
|
||||||
}
|
}
|
||||||
|
if (!isHealPhaseActive.value) {
|
||||||
|
//Push shop instead of healing phase for NoHealChallenge
|
||||||
|
globalScene.pushPhase(new SelectModifierPhase(undefined, undefined, this.getFixedBattleCustomModifiers()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) {
|
if (globalScene.gameMode.hasRandomBiomes || globalScene.isNewBiome()) {
|
||||||
|
@ -16,6 +16,8 @@ import i18next from "i18next";
|
|||||||
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
import { ShopCursorTarget } from "#app/enums/shop-cursor-target";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import type { PokeballType } from "#enums/pokeball";
|
import type { PokeballType } from "#enums/pokeball";
|
||||||
|
import { applyChallenges, ChallengeType } from "#app/data/challenge";
|
||||||
|
import { BooleanHolder } from "#app/utils/common";
|
||||||
|
|
||||||
export const SHOP_OPTIONS_ROW_LIMIT = 7;
|
export const SHOP_OPTIONS_ROW_LIMIT = 7;
|
||||||
const SINGLE_SHOP_ROW_YOFFSET = 12;
|
const SINGLE_SHOP_ROW_YOFFSET = 12;
|
||||||
@ -211,9 +213,16 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
|
|||||||
const removeHealShop = globalScene.gameMode.hasNoShop;
|
const removeHealShop = globalScene.gameMode.hasNoShop;
|
||||||
const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1));
|
const baseShopCost = new NumberHolder(globalScene.getWaveMoneyAmount(1));
|
||||||
globalScene.applyModifier(HealShopCostModifier, true, baseShopCost);
|
globalScene.applyModifier(HealShopCostModifier, true, baseShopCost);
|
||||||
const shopTypeOptions = !removeHealShop
|
const shopTypeOptions = removeHealShop
|
||||||
? getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value)
|
? []
|
||||||
: [];
|
: getPlayerShopModifierTypeOptionsForWave(globalScene.currentBattle.waveIndex, baseShopCost.value).filter(
|
||||||
|
shopItem => {
|
||||||
|
const isValidForChallenge = new BooleanHolder(true);
|
||||||
|
applyChallenges(ChallengeType.SHOP_ITEM_BLACKLIST, shopItem, isValidForChallenge);
|
||||||
|
return isValidForChallenge.value;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const optionsYOffset =
|
const optionsYOffset =
|
||||||
shopTypeOptions.length > SHOP_OPTIONS_ROW_LIMIT ? -SINGLE_SHOP_ROW_YOFFSET : -DOUBLE_SHOP_ROW_YOFFSET;
|
shopTypeOptions.length > SHOP_OPTIONS_ROW_LIMIT ? -SINGLE_SHOP_ROW_YOFFSET : -DOUBLE_SHOP_ROW_YOFFSET;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user