Merge branch 'beta' into evil-team-monogen
This commit is contained in:
commit
c4232bca12
|
@ -13,6 +13,7 @@ import { Arena, ArenaBase } from "#app/field/arena";
|
|||
import { GameData } from "#app/system/game-data";
|
||||
import { addTextObject, getTextColor, TextStyle } from "#app/ui/text";
|
||||
import { allMoves } from "#app/data/move";
|
||||
import { MusicPreference } from "#app/system/settings/settings";
|
||||
import { getDefaultModifierTypeForTier, getEnemyModifierTypesForWave, getLuckString, getLuckTextTint, getModifierPoolForType, getModifierType, getPartyLuckValue, ModifierPoolType, modifierTypes, PokemonHeldItemModifierType } from "#app/modifier/modifier-type";
|
||||
import AbilityBar from "#app/ui/ability-bar";
|
||||
import { allAbilities, applyAbAttrs, applyPostBattleInitAbAttrs, applyPostItemLostAbAttrs, BlockItemTheftAbAttr, DoubleBattleChanceAbAttr, PostBattleInitAbAttr, PostItemLostAbAttr } from "#app/data/ability";
|
||||
|
@ -169,7 +170,7 @@ export default class BattleScene extends SceneBase {
|
|||
public uiTheme: UiTheme = UiTheme.DEFAULT;
|
||||
public windowType: integer = 0;
|
||||
public experimentalSprites: boolean = false;
|
||||
public musicPreference: integer = 0;
|
||||
public musicPreference: number = MusicPreference.MIXED;
|
||||
public moveAnimations: boolean = true;
|
||||
public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT;
|
||||
public skipSeenDialogues: boolean = false;
|
||||
|
@ -2998,22 +2999,16 @@ export default class BattleScene extends SceneBase {
|
|||
*/
|
||||
getActiveKeys(): string[] {
|
||||
const keys: string[] = [];
|
||||
const playerParty = this.getPlayerParty();
|
||||
playerParty.forEach(p => {
|
||||
let activePokemon: (PlayerPokemon | EnemyPokemon)[] = this.getPlayerParty();
|
||||
activePokemon = activePokemon.concat(this.getEnemyParty());
|
||||
activePokemon.forEach((p) => {
|
||||
keys.push(p.getSpriteKey(true));
|
||||
keys.push(p.getBattleSpriteKey(true, true));
|
||||
keys.push("cry/" + p.species.getCryKey(p.formIndex));
|
||||
if (p.fusionSpecies) {
|
||||
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
||||
if (p instanceof PlayerPokemon) {
|
||||
keys.push(p.getBattleSpriteKey(true, true));
|
||||
}
|
||||
});
|
||||
// enemyParty has to be operated on separately from playerParty because playerPokemon =/= enemyPokemon
|
||||
const enemyParty = this.getEnemyParty();
|
||||
enemyParty.forEach(p => {
|
||||
keys.push(p.getSpriteKey(true));
|
||||
keys.push("cry/" + p.species.getCryKey(p.formIndex));
|
||||
keys.push(p.species.getCryKey(p.formIndex));
|
||||
if (p.fusionSpecies) {
|
||||
keys.push("cry/" + p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
||||
keys.push(p.fusionSpecies.getCryKey(p.fusionFormIndex));
|
||||
}
|
||||
});
|
||||
return keys;
|
||||
|
|
286
src/battle.ts
286
src/battle.ts
|
@ -6,11 +6,13 @@ import { GameMode } from "./game-mode";
|
|||
import { MoneyMultiplierModifier, PokemonHeldItemModifier } from "./modifier/modifier";
|
||||
import { PokeballType } from "./data/pokeball";
|
||||
import { trainerConfigs } from "#app/data/trainer-config";
|
||||
import { SpeciesFormKey } from "#enums/species-form-key";
|
||||
import Pokemon, { EnemyPokemon, PlayerPokemon, QueuedMove } from "#app/field/pokemon";
|
||||
import { ArenaTagType } from "#enums/arena-tag-type";
|
||||
import { BattleSpec } from "#enums/battle-spec";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { MusicPreference } from "#app/system/settings/settings";
|
||||
import { Species } from "#enums/species";
|
||||
import { TrainerType } from "#enums/trainer-type";
|
||||
import i18next from "#app/plugins/i18n";
|
||||
|
@ -212,7 +214,6 @@ export default class Battle {
|
|||
}
|
||||
|
||||
getBgmOverride(scene: BattleScene): string | null {
|
||||
const battlers = this.enemyParty.slice(0, this.getBattlerCount());
|
||||
if (this.isBattleMysteryEncounter() && this.mysteryEncounter?.encounterMode === MysteryEncounterMode.DEFAULT) {
|
||||
// Music is overridden for MEs during ME onInit()
|
||||
// Should not use any BGM overrides before swapping from DEFAULT mode
|
||||
|
@ -221,7 +222,7 @@ export default class Battle {
|
|||
if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) {
|
||||
return `encounter_${this.trainer?.getEncounterBgm()}`;
|
||||
}
|
||||
if (scene.musicPreference === 0) {
|
||||
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||
return this.trainer?.getBattleBgm() ?? null;
|
||||
} else {
|
||||
return this.trainer?.getMixedBattleBgm() ?? null;
|
||||
|
@ -229,147 +230,168 @@ export default class Battle {
|
|||
} else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) {
|
||||
return "end_summit";
|
||||
}
|
||||
for (const pokemon of battlers) {
|
||||
const wildOpponents = scene.getEnemyParty();
|
||||
for (const pokemon of wildOpponents) {
|
||||
if (this.battleSpec === BattleSpec.FINAL_BOSS) {
|
||||
if (pokemon.formIndex) {
|
||||
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ETERNAMAX) {
|
||||
return "battle_final";
|
||||
}
|
||||
return "battle_final_encounter";
|
||||
}
|
||||
if (pokemon.species.legendary || pokemon.species.subLegendary || pokemon.species.mythical) {
|
||||
if (scene.musicPreference === 0) {
|
||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
||||
return "battle_legendary_regis_g5";
|
||||
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.REGIROCK:
|
||||
case Species.REGICE:
|
||||
case Species.REGISTEEL:
|
||||
case Species.REGIGIGAS:
|
||||
case Species.REGIDRAGO:
|
||||
case Species.REGIELEKI:
|
||||
return "battle_legendary_regis_g5";
|
||||
case Species.KYUREM:
|
||||
return "battle_legendary_kyurem";
|
||||
default:
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
||||
return "battle_legendary_kyurem";
|
||||
}
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
} else {
|
||||
if (pokemon.species.speciesId === Species.ARTICUNO || pokemon.species.speciesId === Species.ZAPDOS || pokemon.species.speciesId === Species.MOLTRES || pokemon.species.speciesId === Species.MEWTWO || pokemon.species.speciesId === Species.MEW) {
|
||||
return "battle_legendary_kanto";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.RAIKOU) {
|
||||
return "battle_legendary_raikou";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ENTEI) {
|
||||
return "battle_legendary_entei";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.SUICUNE) {
|
||||
return "battle_legendary_suicune";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.LUGIA) {
|
||||
return "battle_legendary_lugia";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.HO_OH) {
|
||||
return "battle_legendary_ho_oh";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.REGIROCK || pokemon.species.speciesId === Species.REGICE || pokemon.species.speciesId === Species.REGISTEEL || pokemon.species.speciesId === Species.REGIGIGAS || pokemon.species.speciesId === Species.REGIELEKI || pokemon.species.speciesId === Species.REGIDRAGO) {
|
||||
return "battle_legendary_regis_g6";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GROUDON || pokemon.species.speciesId === Species.KYOGRE) {
|
||||
return "battle_legendary_gro_kyo";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.RAYQUAZA) {
|
||||
return "battle_legendary_rayquaza";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.DEOXYS) {
|
||||
return "battle_legendary_deoxys";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.UXIE || pokemon.species.speciesId === Species.MESPRIT || pokemon.species.speciesId === Species.AZELF) {
|
||||
return "battle_legendary_lake_trio";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.HEATRAN || pokemon.species.speciesId === Species.CRESSELIA || pokemon.species.speciesId === Species.DARKRAI || pokemon.species.speciesId === Species.SHAYMIN) {
|
||||
return "battle_legendary_sinnoh";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.DIALGA || pokemon.species.speciesId === Species.PALKIA) {
|
||||
if (pokemon.getFormKey() === "") {
|
||||
} else if (scene.musicPreference === MusicPreference.MIXED) {
|
||||
switch (pokemon.species.speciesId) {
|
||||
case Species.ARTICUNO:
|
||||
case Species.ZAPDOS:
|
||||
case Species.MOLTRES:
|
||||
case Species.MEWTWO:
|
||||
case Species.MEW:
|
||||
return "battle_legendary_kanto";
|
||||
case Species.RAIKOU:
|
||||
return "battle_legendary_raikou";
|
||||
case Species.ENTEI:
|
||||
return "battle_legendary_entei";
|
||||
case Species.SUICUNE:
|
||||
return "battle_legendary_suicune";
|
||||
case Species.LUGIA:
|
||||
return "battle_legendary_lugia";
|
||||
case Species.HO_OH:
|
||||
return "battle_legendary_ho_oh";
|
||||
case Species.REGIROCK:
|
||||
case Species.REGICE:
|
||||
case Species.REGISTEEL:
|
||||
case Species.REGIGIGAS:
|
||||
case Species.REGIDRAGO:
|
||||
case Species.REGIELEKI:
|
||||
return "battle_legendary_regis_g6";
|
||||
case Species.GROUDON:
|
||||
case Species.KYOGRE:
|
||||
return "battle_legendary_gro_kyo";
|
||||
case Species.RAYQUAZA:
|
||||
return "battle_legendary_rayquaza";
|
||||
case Species.DEOXYS:
|
||||
return "battle_legendary_deoxys";
|
||||
case Species.UXIE:
|
||||
case Species.MESPRIT:
|
||||
case Species.AZELF:
|
||||
return "battle_legendary_lake_trio";
|
||||
case Species.HEATRAN:
|
||||
case Species.CRESSELIA:
|
||||
case Species.DARKRAI:
|
||||
case Species.SHAYMIN:
|
||||
return "battle_legendary_sinnoh";
|
||||
case Species.DIALGA:
|
||||
case Species.PALKIA:
|
||||
if (pokemon.species.getFormSpriteKey(pokemon.formIndex) === SpeciesFormKey.ORIGIN) {
|
||||
return "battle_legendary_origin_forme";
|
||||
}
|
||||
return "battle_legendary_dia_pal";
|
||||
}
|
||||
if (pokemon.getFormKey() === "origin") {
|
||||
return "battle_legendary_origin_forme";
|
||||
}
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GIRATINA) {
|
||||
return "battle_legendary_giratina";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ARCEUS) {
|
||||
return "battle_legendary_arceus";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.COBALION || pokemon.species.speciesId === Species.TERRAKION || pokemon.species.speciesId === Species.VIRIZION || pokemon.species.speciesId === Species.TORNADUS || pokemon.species.speciesId === Species.THUNDURUS || pokemon.species.speciesId === Species.LANDORUS || pokemon.species.speciesId === Species.KELDEO || pokemon.species.speciesId === Species.MELOETTA || pokemon.species.speciesId === Species.GENESECT) {
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KYUREM) {
|
||||
return "battle_legendary_kyurem";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.XERNEAS || pokemon.species.speciesId === Species.YVELTAL || pokemon.species.speciesId === Species.ZYGARDE) {
|
||||
return "battle_legendary_xern_yvel";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.TAPU_KOKO || pokemon.species.speciesId === Species.TAPU_LELE || pokemon.species.speciesId === Species.TAPU_BULU || pokemon.species.speciesId === Species.TAPU_FINI) {
|
||||
return "battle_legendary_tapu";
|
||||
}
|
||||
if ([ Species.COSMOG, Species.COSMOEM, Species.SOLGALEO, Species.LUNALA ].includes(pokemon.species.speciesId)) {
|
||||
return "battle_legendary_sol_lun";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.NECROZMA) {
|
||||
if (pokemon.getFormKey() === "") {
|
||||
case Species.GIRATINA:
|
||||
return "battle_legendary_giratina";
|
||||
case Species.ARCEUS:
|
||||
return "battle_legendary_arceus";
|
||||
case Species.COBALION:
|
||||
case Species.TERRAKION:
|
||||
case Species.VIRIZION:
|
||||
case Species.KELDEO:
|
||||
case Species.TORNADUS:
|
||||
case Species.LANDORUS:
|
||||
case Species.THUNDURUS:
|
||||
case Species.MELOETTA:
|
||||
case Species.GENESECT:
|
||||
return "battle_legendary_unova";
|
||||
case Species.KYUREM:
|
||||
return "battle_legendary_kyurem";
|
||||
case Species.XERNEAS:
|
||||
case Species.YVELTAL:
|
||||
case Species.ZYGARDE:
|
||||
return "battle_legendary_xern_yvel";
|
||||
case Species.TAPU_KOKO:
|
||||
case Species.TAPU_LELE:
|
||||
case Species.TAPU_BULU:
|
||||
case Species.TAPU_FINI:
|
||||
return "battle_legendary_tapu";
|
||||
case Species.SOLGALEO:
|
||||
case Species.LUNALA:
|
||||
return "battle_legendary_sol_lun";
|
||||
}
|
||||
if (pokemon.getFormKey() === "dusk-mane" || pokemon.getFormKey() === "dawn-wings") {
|
||||
return "battle_legendary_dusk_dawn";
|
||||
}
|
||||
if (pokemon.getFormKey() === "ultra") {
|
||||
return "battle_legendary_ultra_nec";
|
||||
}
|
||||
}
|
||||
if ([ Species.NIHILEGO, Species.BUZZWOLE, Species.PHEROMOSA, Species.XURKITREE, Species.CELESTEELA, Species.KARTANA, Species.GUZZLORD, Species.POIPOLE, Species.NAGANADEL, Species.STAKATAKA, Species.BLACEPHALON ].includes(pokemon.species.speciesId)) {
|
||||
return "battle_legendary_ub";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.ZACIAN || pokemon.species.speciesId === Species.ZAMAZENTA) {
|
||||
return "battle_legendary_zac_zam";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GLASTRIER || pokemon.species.speciesId === Species.SPECTRIER) {
|
||||
return "battle_legendary_glas_spec";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.CALYREX) {
|
||||
if (pokemon.getFormKey() === "") {
|
||||
case Species.NECROZMA:
|
||||
switch (pokemon.getFormKey()) {
|
||||
case "dusk-mane":
|
||||
case "dawn-wings":
|
||||
return "battle_legendary_dusk_dawn";
|
||||
case "ultra":
|
||||
return "battle_legendary_ultra_nec";
|
||||
default:
|
||||
return "battle_legendary_sol_lun";
|
||||
}
|
||||
case Species.NIHILEGO:
|
||||
case Species.PHEROMOSA:
|
||||
case Species.BUZZWOLE:
|
||||
case Species.XURKITREE:
|
||||
case Species.CELESTEELA:
|
||||
case Species.KARTANA:
|
||||
case Species.GUZZLORD:
|
||||
case Species.POIPOLE:
|
||||
case Species.NAGANADEL:
|
||||
case Species.STAKATAKA:
|
||||
case Species.BLACEPHALON:
|
||||
return "battle_legendary_ub";
|
||||
case Species.ZACIAN:
|
||||
case Species.ZAMAZENTA:
|
||||
return "battle_legendary_zac_zam";
|
||||
case Species.GLASTRIER:
|
||||
case Species.SPECTRIER:
|
||||
return "battle_legendary_glas_spec";
|
||||
case Species.CALYREX:
|
||||
if (pokemon.getFormKey() === "ice" || pokemon.getFormKey() === "shadow") {
|
||||
return "battle_legendary_riders";
|
||||
}
|
||||
return "battle_legendary_calyrex";
|
||||
}
|
||||
if (pokemon.getFormKey() === "ice" || pokemon.getFormKey() === "shadow") {
|
||||
return "battle_legendary_riders";
|
||||
}
|
||||
case Species.GALAR_ARTICUNO:
|
||||
case Species.GALAR_ZAPDOS:
|
||||
case Species.GALAR_MOLTRES:
|
||||
return "battle_legendary_birds_galar";
|
||||
case Species.WO_CHIEN:
|
||||
case Species.CHIEN_PAO:
|
||||
case Species.TING_LU:
|
||||
case Species.CHI_YU:
|
||||
return "battle_legendary_ruinous";
|
||||
case Species.KORAIDON:
|
||||
case Species.MIRAIDON:
|
||||
return "battle_legendary_kor_mir";
|
||||
case Species.OKIDOGI:
|
||||
case Species.MUNKIDORI:
|
||||
case Species.FEZANDIPITI:
|
||||
return "battle_legendary_loyal_three";
|
||||
case Species.OGERPON:
|
||||
return "battle_legendary_ogerpon";
|
||||
case Species.TERAPAGOS:
|
||||
return "battle_legendary_terapagos";
|
||||
case Species.PECHARUNT:
|
||||
return "battle_legendary_pecharunt";
|
||||
default:
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.GALAR_ARTICUNO || pokemon.species.speciesId === Species.GALAR_ZAPDOS || pokemon.species.speciesId === Species.GALAR_MOLTRES) {
|
||||
return "battle_legendary_birds_galar";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.WO_CHIEN || pokemon.species.speciesId === Species.CHIEN_PAO || pokemon.species.speciesId === Species.TING_LU || pokemon.species.speciesId === Species.CHI_YU) {
|
||||
return "battle_legendary_ruinous";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.KORAIDON || pokemon.species.speciesId === Species.MIRAIDON) {
|
||||
return "battle_legendary_kor_mir";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.OKIDOGI || pokemon.species.speciesId === Species.MUNKIDORI || pokemon.species.speciesId === Species.FEZANDIPITI) {
|
||||
return "battle_legendary_loyal_three";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.OGERPON) {
|
||||
return "battle_legendary_ogerpon";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.TERAPAGOS) {
|
||||
return "battle_legendary_terapagos";
|
||||
}
|
||||
if (pokemon.species.speciesId === Species.PECHARUNT) {
|
||||
return "battle_legendary_pecharunt";
|
||||
}
|
||||
if (pokemon.species.legendary) {
|
||||
return "battle_legendary_res_zek";
|
||||
}
|
||||
return "battle_legendary_unova";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -313,8 +313,8 @@ export class ConditionalProtectTag extends ArenaTag {
|
|||
* protection effect.
|
||||
* @param arena {@linkcode Arena} The arena containing the protection effect
|
||||
* @param moveId {@linkcode Moves} The move to check against this condition
|
||||
* @returns `true` if the incoming move's priority is greater than 0. This includes
|
||||
* moves with modified priorities from abilities (e.g. Prankster)
|
||||
* @returns `true` if the incoming move's priority is greater than 0.
|
||||
* This includes moves with modified priorities from abilities (e.g. Prankster)
|
||||
*/
|
||||
const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
||||
const move = allMoves[moveId];
|
||||
|
@ -322,9 +322,11 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (arena, moveId) => {
|
|||
const effectPhase = arena.scene.getCurrentPhase();
|
||||
|
||||
if (effectPhase instanceof MoveEffectPhase) {
|
||||
const attacker = effectPhase.getUserPokemon()!;
|
||||
const attacker = effectPhase.getUserPokemon();
|
||||
applyMoveAttrs(IncrementMovePriorityAttr, attacker, null, move, priority);
|
||||
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
|
||||
if (attacker) {
|
||||
applyAbAttrs(ChangeMovePriorityAbAttr, attacker, null, false, move, priority);
|
||||
}
|
||||
}
|
||||
return priority.value > 0;
|
||||
};
|
||||
|
|
|
@ -2496,7 +2496,10 @@ export class SubstituteTag extends BattlerTag {
|
|||
onHit(pokemon: Pokemon): void {
|
||||
const moveEffectPhase = pokemon.scene.getCurrentPhase();
|
||||
if (moveEffectPhase instanceof MoveEffectPhase) {
|
||||
const attacker = moveEffectPhase.getUserPokemon()!;
|
||||
const attacker = moveEffectPhase.getUserPokemon();
|
||||
if (!attacker) {
|
||||
return;
|
||||
}
|
||||
const move = moveEffectPhase.move.getMove();
|
||||
const firstHit = (attacker.turnData.hitCount === attacker.turnData.hitsLeft);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { leaveEncounterWithoutBattle, selectPokemonForOption, setEncounterRewards } from "#app/data/mystery-encounters/utils/encounter-phase-utils";
|
||||
import { TrainerSlot, } from "#app/data/trainer-config";
|
||||
import { ModifierTier } from "#app/modifier/modifier-tier";
|
||||
import { MusicPreference } from "#app/system/settings/settings";
|
||||
import { getPlayerModifierTypeOptions, ModifierPoolType, ModifierTypeOption, regenerateModifierPoolThresholds } from "#app/modifier/modifier-type";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
|
@ -105,7 +106,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter =
|
|||
|
||||
// Load bgm
|
||||
let bgmKey: string;
|
||||
if (scene.musicPreference === 0) {
|
||||
if (scene.musicPreference === MusicPreference.CONSISTENT) {
|
||||
bgmKey = "mystery_encounter_gen_5_gts";
|
||||
scene.loadBgm(bgmKey, `${bgmKey}.mp3`);
|
||||
} else {
|
||||
|
|
|
@ -460,7 +460,7 @@ export abstract class PokemonSpeciesForm {
|
|||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
return `cry/${ret}`;
|
||||
}
|
||||
|
||||
validateStarterMoveset(moveset: StarterMoveset, eggMoves: number): boolean {
|
||||
|
@ -488,7 +488,7 @@ export abstract class PokemonSpeciesForm {
|
|||
return new Promise(resolve => {
|
||||
const spriteKey = this.getSpriteKey(female, formIndex, shiny, variant);
|
||||
scene.loadPokemonAtlas(spriteKey, this.getSpriteAtlasPath(female, formIndex, shiny, variant));
|
||||
scene.load.audio(`cry/${this.getCryKey(formIndex)}`, `audio/cry/${this.getCryKey(formIndex)}.m4a`);
|
||||
scene.load.audio(`${this.getCryKey(formIndex)}`, `audio/${this.getCryKey(formIndex)}.m4a`);
|
||||
scene.load.once(Phaser.Loader.Events.COMPLETE, () => {
|
||||
const originalWarn = console.warn;
|
||||
// Ignore warnings for missing frames, because there will be a lot
|
||||
|
@ -546,7 +546,7 @@ export abstract class PokemonSpeciesForm {
|
|||
if (cry?.pendingRemove) {
|
||||
cry = null;
|
||||
}
|
||||
cry = scene.playSound(`cry/${(cry ?? cryKey)}`, soundConfig);
|
||||
cry = scene.playSound(cry ?? cryKey, soundConfig);
|
||||
if (ignorePlay) {
|
||||
cry.stop();
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
if (this.variant === undefined) {
|
||||
this.variant = this.shiny ? this.generateVariant() : 0;
|
||||
this.variant = this.shiny ? this.generateShinyVariant() : 0;
|
||||
}
|
||||
|
||||
this.customPokemonData = new CustomPokemonData();
|
||||
|
@ -1199,7 +1199,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* @returns an array of {@linkcode Moves}, the length of which is determined
|
||||
* by how many learnable moves there are for the {@linkcode Pokemon}.
|
||||
*/
|
||||
getLearnableLevelMoves(): Moves[] {
|
||||
public getLearnableLevelMoves(): Moves[] {
|
||||
let levelMoves = this.getLevelMoves(1, true, false, true).map(lm => lm[1]);
|
||||
if (this.metBiome === -1 && !this.scene.gameMode.isFreshStartChallenge() && !this.scene.gameMode.isDaily) {
|
||||
levelMoves = this.getUnlockedEggMoves().concat(levelMoves);
|
||||
|
@ -1213,13 +1213,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
/**
|
||||
* Gets the types of a pokemon
|
||||
* @param includeTeraType boolean to include tera-formed type, default false
|
||||
* @param forDefend boolean if the pokemon is defending from an attack
|
||||
* @param ignoreOverride boolean if true, ignore ability changing effects
|
||||
* @param includeTeraType - `true` to include tera-formed type; Default: `false`
|
||||
* @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
|
||||
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||
* @returns array of {@linkcode Type}
|
||||
*/
|
||||
getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean): Type[] {
|
||||
const types : Type[] = [];
|
||||
public getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride: boolean = false): Type[] {
|
||||
const types: Type[] = [];
|
||||
|
||||
if (includeTeraType) {
|
||||
const teraType = this.getTeraType();
|
||||
|
@ -1284,14 +1284,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
// this.scene potentially can be undefined for a fainted pokemon in doubles
|
||||
// use optional chaining to avoid runtime errors
|
||||
|
||||
if (!types.length) { // become UNKNOWN if no types are present
|
||||
// become UNKNOWN if no types are present
|
||||
if (!types.length) {
|
||||
types.push(Type.UNKNOWN);
|
||||
}
|
||||
|
||||
if (types.length > 1 && types.includes(Type.UNKNOWN)) { // remove UNKNOWN if other types are present
|
||||
// remove UNKNOWN if other types are present
|
||||
if (types.length > 1 && types.includes(Type.UNKNOWN)) {
|
||||
const index = types.indexOf(Type.UNKNOWN);
|
||||
if (index !== -1) {
|
||||
types.splice(index, 1);
|
||||
|
@ -1311,19 +1310,27 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return types;
|
||||
}
|
||||
|
||||
isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride?: boolean): boolean {
|
||||
return !!this.getTypes(includeTeraType, forDefend, ignoreOverride).some(t => t === type);
|
||||
/**
|
||||
* Checks if the pokemon's typing includes the specified type
|
||||
* @param type - {@linkcode Type} to check
|
||||
* @param includeTeraType - `true` to include tera-formed type; Default: `true`
|
||||
* @param forDefend - `true` if the pokemon is defending from an attack; Default: `false`
|
||||
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||
* @returns `true` if the Pokemon's type matches
|
||||
*/
|
||||
public isOfType(type: Type, includeTeraType: boolean = true, forDefend: boolean = false, ignoreOverride: boolean = false): boolean {
|
||||
return this.getTypes(includeTeraType, forDefend, ignoreOverride).some((t) => t === type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the non-passive ability of the pokemon. This accounts for fusions and ability changing effects.
|
||||
* This should rarely be called, most of the time {@link hasAbility} or {@link hasAbilityWithAttr} are better used as
|
||||
* This should rarely be called, most of the time {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as
|
||||
* those check both the passive and non-passive abilities and account for ability suppression.
|
||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @param {boolean} ignoreOverride If true, ignore ability changing effects
|
||||
* @returns {Ability} The non-passive ability of the pokemon
|
||||
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @param ignoreOverride - If `true`, ignore ability changing effects; Default: `false`
|
||||
* @returns The non-passive {@linkcode Ability} of the pokemon
|
||||
*/
|
||||
getAbility(ignoreOverride?: boolean): Ability {
|
||||
public getAbility(ignoreOverride: boolean = false): Ability {
|
||||
if (!ignoreOverride && this.summonData?.ability) {
|
||||
return allAbilities[this.summonData.ability];
|
||||
}
|
||||
|
@ -1352,12 +1359,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
/**
|
||||
* Gets the passive ability of the pokemon. This should rarely be called, most of the time
|
||||
* {@link hasAbility} or {@link hasAbilityWithAttr} are better used as those check both the passive and
|
||||
* {@linkcode hasAbility} or {@linkcode hasAbilityWithAttr} are better used as those check both the passive and
|
||||
* non-passive abilities and account for ability suppression.
|
||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @returns {Ability} The passive ability of the pokemon
|
||||
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @returns The passive {@linkcode Ability} of the pokemon
|
||||
*/
|
||||
getPassiveAbility(): Ability {
|
||||
public getPassiveAbility(): Ability {
|
||||
if (Overrides.PASSIVE_ABILITY_OVERRIDE && this.isPlayer()) {
|
||||
return allAbilities[Overrides.PASSIVE_ABILITY_OVERRIDE];
|
||||
}
|
||||
|
@ -1379,12 +1386,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* Gets a list of all instances of a given ability attribute among abilities this pokemon has.
|
||||
* Accounts for all the various effects which can affect whether an ability will be present or
|
||||
* in effect, and both passive and non-passive.
|
||||
* @param attrType {@linkcode AbAttr} The ability attribute to check for.
|
||||
* @param canApply {@linkcode Boolean} If false, it doesn't check whether the ability is currently active
|
||||
* @param ignoreOverride {@linkcode Boolean} If true, it ignores ability changing effects
|
||||
* @returns A list of all the ability attributes on this ability.
|
||||
* @param attrType - {@linkcode AbAttr} The ability attribute to check for.
|
||||
* @param canApply - If `false`, it doesn't check whether the ability is currently active; Default `true`
|
||||
* @param ignoreOverride - If `true`, it ignores ability changing effects; Default `false`
|
||||
* @returns An array of all the ability attributes on this ability.
|
||||
*/
|
||||
getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride?: boolean): T[] {
|
||||
public getAbilityAttrs<T extends AbAttr = AbAttr>(attrType: { new(...args: any[]): T }, canApply: boolean = true, ignoreOverride: boolean = false): T[] {
|
||||
const abilityAttrs: T[] = [];
|
||||
|
||||
if (!canApply || this.canApplyAbility()) {
|
||||
|
@ -1403,12 +1410,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* - bought with starter candy
|
||||
* - set by override
|
||||
* - is a boss pokemon
|
||||
* @returns whether or not a pokemon should have a passive
|
||||
* @returns `true` if the Pokemon has a passive
|
||||
*/
|
||||
hasPassive(): boolean {
|
||||
public hasPassive(): boolean {
|
||||
// returns override if valid for current case
|
||||
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer()) ||
|
||||
(Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
|
||||
if ((Overrides.PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && this.isPlayer())
|
||||
|| (Overrides.OPP_PASSIVE_ABILITY_OVERRIDE !== Abilities.NONE && !this.isPlayer())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1427,12 +1434,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
|
||||
/**
|
||||
* Checks whether an ability of a pokemon can be currently applied. This should rarely be
|
||||
* directly called, as {@link hasAbility} and {@link hasAbilityWithAttr} already call this.
|
||||
* @see {@link hasAbility} {@link hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @param {boolean} passive If true, check if passive can be applied instead of non-passive
|
||||
* @returns {Ability} The passive ability of the pokemon
|
||||
* directly called, as {@linkcode hasAbility} and {@linkcode hasAbilityWithAttr} already call this.
|
||||
* @see {@linkcode hasAbility} {@linkcode hasAbilityWithAttr} Intended ways to check abilities in most cases
|
||||
* @param passive If true, check if passive can be applied instead of non-passive
|
||||
* @returns `true` if the ability can be applied
|
||||
*/
|
||||
canApplyAbility(passive: boolean = false): boolean {
|
||||
public canApplyAbility(passive: boolean = false): boolean {
|
||||
if (passive && !this.hasPassive()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1461,7 +1468,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return (!!this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
||||
return (this.hp > 0 || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1473,7 +1480,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
||||
* @returns {boolean} Whether the ability is present and active
|
||||
*/
|
||||
hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
public hasAbility(ability: Abilities, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
if (this.getAbility(ignoreOverride).id === ability && (!canApply || this.canApplyAbility())) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1493,7 +1500,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* @param {boolean} ignoreOverride If true, it ignores ability changing effects
|
||||
* @returns {boolean} Whether an ability with that attribute is present and active
|
||||
*/
|
||||
hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
public hasAbilityWithAttr(attrType: Constructor<AbAttr>, canApply: boolean = true, ignoreOverride?: boolean): boolean {
|
||||
if ((!canApply || this.canApplyAbility()) && this.getAbility(ignoreOverride).hasAttr(attrType)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1508,7 +1515,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* and then multiplicative modifiers happening after (Heavy Metal and Light Metal)
|
||||
* @returns the kg of the Pokemon (minimum of 0.1)
|
||||
*/
|
||||
getWeight(): number {
|
||||
public getWeight(): number {
|
||||
const autotomizedTag = this.getTag(AutotomizedTag);
|
||||
let weightRemoved = 0;
|
||||
if (!Utils.isNullOrUndefined(autotomizedTag)) {
|
||||
|
@ -1523,10 +1530,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the tera-formed type of the pokemon, or UNKNOWN if not present
|
||||
* @returns the {@linkcode Type}
|
||||
* @returns The tera-formed type of the pokemon, or {@linkcode Type.UNKNOWN} if not present
|
||||
*/
|
||||
getTeraType(): Type {
|
||||
public getTeraType(): Type {
|
||||
// this.scene can be undefined for a fainted mon in doubles
|
||||
if (this.scene !== undefined) {
|
||||
const teraModifier = this.scene.findModifier(m => m instanceof TerastallizeModifier
|
||||
|
@ -1540,23 +1546,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return Type.UNKNOWN;
|
||||
}
|
||||
|
||||
isTerastallized(): boolean {
|
||||
public isTerastallized(): boolean {
|
||||
return this.getTeraType() !== Type.UNKNOWN;
|
||||
}
|
||||
|
||||
isGrounded(): boolean {
|
||||
public isGrounded(): boolean {
|
||||
return !!this.getTag(GroundedTag) || (!this.isOfType(Type.FLYING, true, true) && !this.hasAbility(Abilities.LEVITATE) && !this.getTag(BattlerTagType.FLOATING) && !this.getTag(SemiInvulnerableTag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether this Pokemon is prevented from running or switching due
|
||||
* to effects from moves and/or abilities.
|
||||
* @param trappedAbMessages `string[]` If defined, ability trigger messages
|
||||
* @param trappedAbMessages - If defined, ability trigger messages
|
||||
* (e.g. from Shadow Tag) are forwarded through this array.
|
||||
* @param simulated `boolean` if `true`, applies abilities via simulated calls.
|
||||
* @returns
|
||||
* @param simulated - If `true`, applies abilities via simulated calls.
|
||||
* @returns `true` if the pokemon is trapped
|
||||
*/
|
||||
isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean {
|
||||
public isTrapped(trappedAbMessages: string[] = [], simulated: boolean = true): boolean {
|
||||
if (this.isOfType(Type.GHOST)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1564,7 +1570,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
const trappedByAbility = new Utils.BooleanHolder(false);
|
||||
const opposingField = this.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField();
|
||||
|
||||
opposingField.forEach(opponent =>
|
||||
opposingField.forEach((opponent) =>
|
||||
applyCheckTrappedAbAttrs(CheckTrappedAbAttr, opponent, trappedByAbility, this, trappedAbMessages, simulated)
|
||||
);
|
||||
|
||||
|
@ -1575,11 +1581,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
/**
|
||||
* Calculates the type of a move when used by this Pokemon after
|
||||
* type-changing move and ability attributes have applied.
|
||||
* @param move {@linkcode Move} The move being used.
|
||||
* @param simulated If `true`, prevents showing abilities applied in this calculation.
|
||||
* @returns the {@linkcode Type} of the move after attributes are applied
|
||||
* @param move - {@linkcode Move} The move being used.
|
||||
* @param simulated - If `true`, prevents showing abilities applied in this calculation.
|
||||
* @returns The {@linkcode Type} of the move after attributes are applied
|
||||
*/
|
||||
getMoveType(move: Move, simulated: boolean = true): Type {
|
||||
public getMoveType(move: Move, simulated: boolean = true): Type {
|
||||
const moveTypeHolder = new Utils.NumberHolder(move.type);
|
||||
|
||||
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
|
||||
|
@ -1944,13 +1950,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
* Function that tries to set a Pokemon shiny based on seed.
|
||||
* For manual use only, usually to roll a Pokemon's shiny chance a second time.
|
||||
*
|
||||
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / 65536
|
||||
* @param thresholdOverride number that is divided by 2^16 (65536) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
||||
* The base shiny odds are {@linkcode BASE_SHINY_CHANCE} / `65536`
|
||||
* @param thresholdOverride number that is divided by `2^16` (`65536`) to get the shiny chance, overrides {@linkcode shinyThreshold} if set (bypassing shiny rate modifiers such as Shiny Charm)
|
||||
* @param applyModifiersToOverride If {@linkcode thresholdOverride} is set and this is true, will apply Shiny Charm and event modifiers to {@linkcode thresholdOverride}
|
||||
* @returns true if the Pokemon has been set as a shiny, false otherwise
|
||||
* @returns `true` if the Pokemon has been set as a shiny, `false` otherwise
|
||||
*/
|
||||
trySetShinySeed(thresholdOverride?: integer, applyModifiersToOverride?: boolean): boolean {
|
||||
const shinyThreshold = new Utils.IntegerHolder(BASE_SHINY_CHANCE);
|
||||
public trySetShinySeed(thresholdOverride?: number, applyModifiersToOverride?: boolean): boolean {
|
||||
const shinyThreshold = new Utils.NumberHolder(BASE_SHINY_CHANCE);
|
||||
if (thresholdOverride === undefined || applyModifiersToOverride) {
|
||||
if (thresholdOverride !== undefined && applyModifiersToOverride) {
|
||||
shinyThreshold.value = thresholdOverride;
|
||||
|
@ -1975,13 +1981,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates a variant
|
||||
* Has a 10% of returning 2 (epic variant)
|
||||
* And a 30% of returning 1 (rare variant)
|
||||
* Returns 0 (basic shiny) if there is no variant or 60% of the time otherwise
|
||||
* @returns the shiny variant
|
||||
* Generates a shiny variant
|
||||
* @returns `0-2`, with the following probabilities:
|
||||
* - Has a 10% chance of returning `2` (epic variant)
|
||||
* - Has a 30% chance of returning `1` (rare variant)
|
||||
* - Has a 60% chance of returning `0` (basic shiny)
|
||||
*/
|
||||
generateVariant(): Variant {
|
||||
protected generateShinyVariant(): Variant {
|
||||
const formIndex: number = this.formIndex;
|
||||
let variantDataIndex: string | number = this.species.speciesId;
|
||||
if (this.species.forms.length > 0) {
|
||||
|
@ -2007,8 +2013,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
generateFusionSpecies(forStarter?: boolean): void {
|
||||
const hiddenAbilityChance = new Utils.IntegerHolder(BASE_HIDDEN_ABILITY_CHANCE);
|
||||
public generateFusionSpecies(forStarter?: boolean): void {
|
||||
const hiddenAbilityChance = new Utils.NumberHolder(BASE_HIDDEN_ABILITY_CHANCE);
|
||||
if (!this.hasTrainer()) {
|
||||
this.scene.applyModifiers(HiddenAbilityRateBoosterModifier, true, hiddenAbilityChance);
|
||||
}
|
||||
|
@ -2057,7 +2063,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.generateName();
|
||||
}
|
||||
|
||||
clearFusionSpecies(): void {
|
||||
public clearFusionSpecies(): void {
|
||||
this.fusionSpecies = null;
|
||||
this.fusionFormIndex = 0;
|
||||
this.fusionAbilityIndex = 0;
|
||||
|
@ -2071,12 +2077,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
this.calculateStats();
|
||||
}
|
||||
|
||||
generateAndPopulateMoveset(): void {
|
||||
/** Generates a semi-random moveset for a Pokemon */
|
||||
public generateAndPopulateMoveset(): void {
|
||||
this.moveset = [];
|
||||
let movePool: [Moves, number][] = [];
|
||||
const allLevelMoves = this.getLevelMoves(1, true, true);
|
||||
if (!allLevelMoves) {
|
||||
console.log(this.species.speciesId, "ERROR");
|
||||
console.warn("Error encountered trying to generate moveset for:", this.species.name);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2086,15 +2093,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
break;
|
||||
}
|
||||
let weight = levelMove[0];
|
||||
if (weight === 0) { // Evo Moves
|
||||
// Evolution Moves
|
||||
if (weight === 0) {
|
||||
weight = 50;
|
||||
}
|
||||
if (weight === 1 && allMoves[levelMove[1]].power >= 80) { // Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
|
||||
// Assume level 1 moves with 80+ BP are "move reminder" moves and bump their weight
|
||||
if (weight === 1 && allMoves[levelMove[1]].power >= 80) {
|
||||
weight = 40;
|
||||
}
|
||||
if (allMoves[levelMove[1]].name.endsWith(" (N)")) {
|
||||
weight /= 100;
|
||||
} // Unimplemented level up moves are possible to generate, but 1% of their normal chance.
|
||||
if (!movePool.some(m => m[0] === levelMove[1])) {
|
||||
movePool.push([ levelMove[1], weight ]);
|
||||
}
|
||||
|
@ -2127,7 +2133,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.level >= 60) { // No egg moves below level 60
|
||||
// No egg moves below level 60
|
||||
if (this.level >= 60) {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][i];
|
||||
if (!movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)")) {
|
||||
|
@ -2135,7 +2142,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
const moveId = speciesEggMoves[this.species.getRootSpeciesId()][3];
|
||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) { // No rare egg moves before e4
|
||||
// No rare egg moves before e4
|
||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
|
||||
movePool.push([ moveId, 30 ]);
|
||||
}
|
||||
if (this.fusionSpecies) {
|
||||
|
@ -2146,14 +2154,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
const moveId = speciesEggMoves[this.fusionSpecies.getRootSpeciesId()][3];
|
||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {// No rare egg moves before e4
|
||||
// No rare egg moves before e4
|
||||
if (this.level >= 170 && !movePool.some(m => m[0] === moveId) && !allMoves[moveId].name.endsWith(" (N)") && !this.isBoss()) {
|
||||
movePool.push([ moveId, 30 ]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isBoss()) { // Bosses never get self ko moves
|
||||
// Bosses never get self ko moves
|
||||
if (this.isBoss()) {
|
||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttr));
|
||||
}
|
||||
movePool = movePool.filter(m => !allMoves[m[0]].hasAttr(SacrificialAttrOnHit));
|
||||
|
@ -2182,7 +2192,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
const statRatio = worseCategory === MoveCategory.PHYSICAL ? atk / spAtk : spAtk / atk;
|
||||
movePool = movePool.map(m => [ m[0], m[1] * (allMoves[m[0]].category === worseCategory ? statRatio : 1) ]);
|
||||
|
||||
let weightMultiplier = 0.9; // The higher this is the more the game weights towards higher level moves. At 0 all moves are equal weight.
|
||||
/** The higher this is the more the game weights towards higher level moves. At `0` all moves are equal weight. */
|
||||
let weightMultiplier = 0.9;
|
||||
if (this.hasTrainer()) {
|
||||
weightMultiplier += 0.7;
|
||||
}
|
||||
|
@ -2191,7 +2202,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
const baseWeights: [Moves, number][] = movePool.map(m => [ m[0], Math.ceil(Math.pow(m[1], weightMultiplier) * 100) ]);
|
||||
|
||||
if (this.hasTrainer() || this.isBoss()) { // Trainers and bosses always force a stab move
|
||||
// Trainers and bosses always force a stab move
|
||||
if (this.hasTrainer() || this.isBoss()) {
|
||||
const stabMovePool = baseWeights.filter(m => allMoves[m[0]].category !== MoveCategory.STATUS && this.isOfType(allMoves[m[0]].type));
|
||||
|
||||
if (stabMovePool.length) {
|
||||
|
@ -2221,8 +2233,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
// Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier.
|
||||
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB.
|
||||
// Status moves remain unchanged on weight, this encourages 1-2
|
||||
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [ m[0], this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power!) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1] ]); // TODO: is this bang correct?
|
||||
} else { // Non-trainer pokemon just use normal weights
|
||||
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map((m) => {
|
||||
let ret: number;
|
||||
if (this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type)) {
|
||||
ret = Math.ceil(Math.sqrt(m[1]));
|
||||
} else if (allMoves[m[0]].category !== MoveCategory.STATUS) {
|
||||
ret = Math.ceil(m[1] / Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power ?? 0) > 1).length) / 8, 0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1));
|
||||
} else {
|
||||
ret = m[1];
|
||||
}
|
||||
return [ m[0], ret ];
|
||||
});
|
||||
} else {
|
||||
// Non-trainer pokemon just use normal weights
|
||||
movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId));
|
||||
}
|
||||
const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
|
||||
|
@ -2240,11 +2263,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
}
|
||||
|
||||
trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||
public trySelectMove(moveIndex: integer, ignorePp?: boolean): boolean {
|
||||
const move = this.getMoveset().length > moveIndex
|
||||
? this.getMoveset()[moveIndex]
|
||||
: null;
|
||||
return move?.isUsable(this, ignorePp)!; // TODO: is this bang correct?
|
||||
return move?.isUsable(this, ignorePp) ?? false;
|
||||
}
|
||||
|
||||
showInfo(): void {
|
||||
|
@ -3243,7 +3266,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
try {
|
||||
SoundFade.fadeOut(scene, cry, Utils.fixedInt(Math.ceil(duration * 0.2)));
|
||||
fusionCry = this.getFusionSpeciesForm().cry(scene, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0) }, soundConfig));
|
||||
SoundFade.fadeIn(scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.seVolume, 0);
|
||||
SoundFade.fadeIn(scene, fusionCry, Utils.fixedInt(Math.ceil(duration * 0.2)), scene.masterVolume * scene.fieldVolume, 0);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
@ -3258,11 +3281,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
return this.fusionFaintCry(callback);
|
||||
}
|
||||
|
||||
const key = `cry/${this.species.getCryKey(this.formIndex)}`;
|
||||
//eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
let i = 0;
|
||||
const key = this.species.getCryKey(this.formIndex);
|
||||
let rate = 0.85;
|
||||
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
||||
if (!cry || this.scene.fieldVolume === 0) {
|
||||
return callback();
|
||||
}
|
||||
const sprite = this.getSprite();
|
||||
const tintSprite = this.getTintSprite();
|
||||
const delay = Math.max(this.scene.sound.get(key).totalDuration * 50, 25);
|
||||
|
@ -3277,7 +3301,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
delay: Utils.fixedInt(delay),
|
||||
repeat: -1,
|
||||
callback: () => {
|
||||
++i;
|
||||
frameThreshold = sprite.anims.msPerFrame / rate;
|
||||
frameProgress += delay;
|
||||
while (frameProgress > frameThreshold) {
|
||||
|
@ -3316,7 +3339,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
|
||||
private fusionFaintCry(callback: Function): void {
|
||||
const key = `cry/${this.species.getCryKey(this.formIndex)}`;
|
||||
const key = this.species.getCryKey(this.formIndex);
|
||||
let i = 0;
|
||||
let rate = 0.85;
|
||||
const cry = this.scene.playSound(key, { rate: rate }) as AnySound;
|
||||
|
@ -3324,12 +3347,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
const tintSprite = this.getTintSprite();
|
||||
let duration = cry.totalDuration * 1000;
|
||||
|
||||
const fusionCryKey = `cry/${this.fusionSpecies?.getCryKey(this.fusionFormIndex)}`;
|
||||
const fusionCryKey = this.fusionSpecies!.getCryKey(this.fusionFormIndex);
|
||||
let fusionCry = this.scene.playSound(fusionCryKey, { rate: rate }) as AnySound;
|
||||
if (!cry || !fusionCry || this.scene.fieldVolume === 0) {
|
||||
return callback();
|
||||
}
|
||||
fusionCry.stop();
|
||||
duration = Math.min(duration, fusionCry.totalDuration * 1000);
|
||||
fusionCry.destroy();
|
||||
|
||||
const delay = Math.max(duration * 0.05, 25);
|
||||
|
||||
let transitionIndex = 0;
|
||||
|
@ -3367,10 +3392,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
|||
}
|
||||
frameProgress -= frameThreshold;
|
||||
}
|
||||
if (i === transitionIndex) {
|
||||
if (i === transitionIndex && fusionCryKey) {
|
||||
SoundFade.fadeOut(this.scene, cry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)));
|
||||
fusionCry = this.scene.playSound(fusionCryKey, Object.assign({ seek: Math.max(fusionCry.totalDuration * 0.4, 0), rate: rate }));
|
||||
SoundFade.fadeIn(this.scene, fusionCry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), this.scene.masterVolume * this.scene.seVolume, 0);
|
||||
SoundFade.fadeIn(this.scene, fusionCry, Utils.fixedInt(Math.ceil((duration / rate) * 0.2)), this.scene.masterVolume * this.scene.fieldVolume, 0);
|
||||
}
|
||||
rate *= 0.99;
|
||||
if (cry && !cry.pendingRemove) {
|
||||
|
@ -4576,7 +4601,7 @@ export class EnemyPokemon extends Pokemon {
|
|||
}
|
||||
|
||||
if (this.shiny) {
|
||||
this.variant = this.generateVariant();
|
||||
this.variant = this.generateShinyVariant();
|
||||
if (Overrides.OPP_VARIANT_OVERRIDE !== null) {
|
||||
this.variant = Overrides.OPP_VARIANT_OVERRIDE;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ export class LoadingScene extends SceneBase {
|
|||
// Get current lang and load the types atlas for it. English will only load types while all other languages will load types and types_<lang>
|
||||
const lang = i18next.resolvedLanguage;
|
||||
if (lang !== "en") {
|
||||
if (Utils.verifyLang(lang)) {
|
||||
if (Utils.hasAllLocalizedSprites(lang)) {
|
||||
this.loadAtlas(`statuses_${lang}`, "");
|
||||
this.loadAtlas(`types_${lang}`, "");
|
||||
} else {
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
import { type PokeballCounts } from "#app/battle-scene";
|
||||
import { Gender } from "#app/data/gender";
|
||||
import { Variant } from "#app/data/variant";
|
||||
import { type ModifierOverride } from "#app/modifier/modifier-type";
|
||||
import { Unlockables } from "#app/system/unlockables";
|
||||
import { Abilities } from "#enums/abilities";
|
||||
import { Biome } from "#enums/biome";
|
||||
import { EggTier } from "#enums/egg-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { PokeballType } from "#enums/pokeball";
|
||||
import { Species } from "#enums/species";
|
||||
import { StatusEffect } from "#enums/status-effect";
|
||||
import { TimeOfDay } from "#enums/time-of-day";
|
||||
import { VariantTier } from "#enums/variant-tier";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import { type PokeballCounts } from "./battle-scene";
|
||||
import { Gender } from "./data/gender";
|
||||
import { Variant } from "./data/variant";
|
||||
import { type ModifierOverride } from "./modifier/modifier-type";
|
||||
import { Unlockables } from "./system/unlockables";
|
||||
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
|
||||
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
|
||||
|
||||
/**
|
||||
* Overrides that are using when testing different in game situations
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import SoundFade from "phaser3-rex-plugins/plugins/soundfade";
|
||||
import { Phase } from "#app/phase";
|
||||
import BattleScene from "#app/battle-scene";
|
||||
import BattleScene, { AnySound } from "#app/battle-scene";
|
||||
import { SpeciesFormEvolution } from "#app/data/balance/pokemon-evolutions";
|
||||
import EvolutionSceneHandler from "#app/ui/evolution-scene-handler";
|
||||
import * as Utils from "#app/utils";
|
||||
import { Mode } from "#app/ui/ui";
|
||||
import { cos, sin } from "#app/field/anims";
|
||||
import { PlayerPokemon } from "#app/field/pokemon";
|
||||
import Pokemon, { PlayerPokemon } from "#app/field/pokemon";
|
||||
import { getTypeRgb } from "#app/data/type";
|
||||
import i18next from "i18next";
|
||||
import { getPokemonNameWithAffix } from "#app/messages";
|
||||
|
@ -17,7 +17,11 @@ export class EvolutionPhase extends Phase {
|
|||
protected pokemon: PlayerPokemon;
|
||||
protected lastLevel: integer;
|
||||
|
||||
private preEvolvedPokemonName: string;
|
||||
|
||||
private evolution: SpeciesFormEvolution | null;
|
||||
private evolutionBgm: AnySound;
|
||||
private evolutionHandler: EvolutionSceneHandler;
|
||||
|
||||
protected evolutionContainer: Phaser.GameObjects.Container;
|
||||
protected evolutionBaseBg: Phaser.GameObjects.Image;
|
||||
|
@ -35,6 +39,8 @@ export class EvolutionPhase extends Phase {
|
|||
this.pokemon = pokemon;
|
||||
this.evolution = evolution;
|
||||
this.lastLevel = lastLevel;
|
||||
this.evolutionBgm = this.scene.playSoundWithoutBgm("evolution");
|
||||
this.preEvolvedPokemonName = getPokemonNameWithAffix(this.pokemon);
|
||||
}
|
||||
|
||||
validate(): boolean {
|
||||
|
@ -117,10 +123,9 @@ export class EvolutionPhase extends Phase {
|
|||
}
|
||||
|
||||
doEvolution(): void {
|
||||
const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
||||
const preName = getPokemonNameWithAffix(this.pokemon);
|
||||
this.evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
||||
|
||||
this.scene.ui.showText(i18next.t("menu:evolving", { pokemonName: preName }), null, () => {
|
||||
this.scene.ui.showText(i18next.t("menu:evolving", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||
this.pokemon.cry();
|
||||
|
||||
this.pokemon.getPossibleEvolution(this.evolution).then(evolvedPokemon => {
|
||||
|
@ -140,7 +145,6 @@ export class EvolutionPhase extends Phase {
|
|||
});
|
||||
|
||||
this.scene.time.delayedCall(1000, () => {
|
||||
const evolutionBgm = this.scene.playSoundWithoutBgm("evolution");
|
||||
this.scene.tweens.add({
|
||||
targets: this.evolutionBgOverlay,
|
||||
alpha: 1,
|
||||
|
@ -174,100 +178,13 @@ export class EvolutionPhase extends Phase {
|
|||
this.scene.time.delayedCall(1500, () => {
|
||||
this.pokemonEvoTintSprite.setScale(0.25);
|
||||
this.pokemonEvoTintSprite.setVisible(true);
|
||||
evolutionHandler.canCancel = true;
|
||||
this.evolutionHandler.canCancel = true;
|
||||
this.doCycle(1).then(success => {
|
||||
if (!success) {
|
||||
|
||||
this.pokemonSprite.setVisible(true);
|
||||
this.pokemonTintSprite.setScale(1);
|
||||
this.scene.tweens.add({
|
||||
targets: [ this.evolutionBg, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite ],
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
onComplete: () => {
|
||||
this.evolutionBg.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
SoundFade.fadeOut(this.scene, evolutionBgm, 100);
|
||||
|
||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||
|
||||
this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: preName }), null, () => {
|
||||
this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => {
|
||||
const end = () => {
|
||||
this.scene.ui.showText("", 0);
|
||||
this.scene.playBgm();
|
||||
evolvedPokemon.destroy();
|
||||
this.end();
|
||||
};
|
||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.pokemon.pauseEvolutions = true;
|
||||
this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: preName }), null, end, 3000);
|
||||
}, () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.scene.time.delayedCall(3000, end);
|
||||
});
|
||||
});
|
||||
}, null, true);
|
||||
return;
|
||||
if (success) {
|
||||
this.handleSuccessEvolution(evolvedPokemon);
|
||||
} else {
|
||||
this.handleFailedEvolution(evolvedPokemon);
|
||||
}
|
||||
|
||||
this.scene.playSound("se/sparkle");
|
||||
this.pokemonEvoSprite.setVisible(true);
|
||||
this.doCircleInward();
|
||||
this.scene.time.delayedCall(900, () => {
|
||||
evolutionHandler.canCancel = false;
|
||||
|
||||
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
||||
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
|
||||
for (const lm of levelMoves) {
|
||||
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getPlayerParty().indexOf(this.pokemon), lm[1]));
|
||||
}
|
||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||
|
||||
this.scene.playSound("se/shine");
|
||||
this.doSpray();
|
||||
this.scene.tweens.add({
|
||||
targets: this.evolutionOverlay,
|
||||
alpha: 1,
|
||||
duration: 250,
|
||||
easing: "Sine.easeIn",
|
||||
onComplete: () => {
|
||||
this.evolutionBgOverlay.setAlpha(1);
|
||||
this.evolutionBg.setVisible(false);
|
||||
this.scene.tweens.add({
|
||||
targets: [ this.evolutionOverlay, this.pokemonEvoTintSprite ],
|
||||
alpha: 0,
|
||||
duration: 2000,
|
||||
delay: 150,
|
||||
easing: "Sine.easeIn",
|
||||
onComplete: () => {
|
||||
this.scene.tweens.add({
|
||||
targets: this.evolutionBgOverlay,
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
onComplete: () => {
|
||||
SoundFade.fadeOut(this.scene, evolutionBgm, 100);
|
||||
this.scene.time.delayedCall(250, () => {
|
||||
this.pokemon.cry();
|
||||
this.scene.time.delayedCall(1250, () => {
|
||||
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
||||
|
||||
evolvedPokemon.destroy();
|
||||
this.scene.ui.showText(i18next.t("menu:evolutionDone", { pokemonName: preName, evolvedPokemonName: this.pokemon.name }), null, () => this.end(), null, true, Utils.fixedInt(4000));
|
||||
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -280,6 +197,110 @@ export class EvolutionPhase extends Phase {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a failed/stopped evolution
|
||||
* @param evolvedPokemon - The evolved Pokemon
|
||||
*/
|
||||
private handleFailedEvolution(evolvedPokemon: Pokemon): void {
|
||||
this.pokemonSprite.setVisible(true);
|
||||
this.pokemonTintSprite.setScale(1);
|
||||
this.scene.tweens.add({
|
||||
targets: [ this.evolutionBg, this.pokemonTintSprite, this.pokemonEvoSprite, this.pokemonEvoTintSprite ],
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
onComplete: () => {
|
||||
this.evolutionBg.setVisible(false);
|
||||
}
|
||||
});
|
||||
|
||||
SoundFade.fadeOut(this.scene, this.evolutionBgm, 100);
|
||||
|
||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||
|
||||
this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||
this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: this.preEvolvedPokemonName }), null, () => {
|
||||
const end = () => {
|
||||
this.scene.ui.showText("", 0);
|
||||
this.scene.playBgm();
|
||||
evolvedPokemon.destroy();
|
||||
this.end();
|
||||
};
|
||||
this.scene.ui.setOverlayMode(Mode.CONFIRM, () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.pokemon.pauseEvolutions = true;
|
||||
this.scene.ui.showText(i18next.t("menu:evolutionsPaused", { pokemonName: this.preEvolvedPokemonName }), null, end, 3000);
|
||||
}, () => {
|
||||
this.scene.ui.revertMode();
|
||||
this.scene.time.delayedCall(3000, end);
|
||||
});
|
||||
});
|
||||
}, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a successful evolution
|
||||
* @param evolvedPokemon - The evolved Pokemon
|
||||
*/
|
||||
private handleSuccessEvolution(evolvedPokemon: Pokemon): void {
|
||||
this.scene.playSound("se/sparkle");
|
||||
this.pokemonEvoSprite.setVisible(true);
|
||||
this.doCircleInward();
|
||||
|
||||
const onEvolutionComplete = () => {
|
||||
SoundFade.fadeOut(this.scene, this.evolutionBgm, 100);
|
||||
this.scene.time.delayedCall(250, () => {
|
||||
this.pokemon.cry();
|
||||
this.scene.time.delayedCall(1250, () => {
|
||||
this.scene.playSoundWithoutBgm("evolution_fanfare");
|
||||
|
||||
evolvedPokemon.destroy();
|
||||
this.scene.ui.showText(i18next.t("menu:evolutionDone", { pokemonName: this.preEvolvedPokemonName, evolvedPokemonName: this.pokemon.name }), null, () => this.end(), null, true, Utils.fixedInt(4000));
|
||||
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
this.scene.time.delayedCall(900, () => {
|
||||
this.evolutionHandler.canCancel = false;
|
||||
|
||||
this.pokemon.evolve(this.evolution, this.pokemon.species).then(() => {
|
||||
const levelMoves = this.pokemon.getLevelMoves(this.lastLevel + 1, true);
|
||||
for (const lm of levelMoves) {
|
||||
this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.scene.getPlayerParty().indexOf(this.pokemon), lm[1]));
|
||||
}
|
||||
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
|
||||
|
||||
this.scene.playSound("se/shine");
|
||||
this.doSpray();
|
||||
this.scene.tweens.add({
|
||||
targets: this.evolutionOverlay,
|
||||
alpha: 1,
|
||||
duration: 250,
|
||||
easing: "Sine.easeIn",
|
||||
onComplete: () => {
|
||||
this.evolutionBgOverlay.setAlpha(1);
|
||||
this.evolutionBg.setVisible(false);
|
||||
this.scene.tweens.add({
|
||||
targets: [ this.evolutionOverlay, this.pokemonEvoTintSprite ],
|
||||
alpha: 0,
|
||||
duration: 2000,
|
||||
delay: 150,
|
||||
easing: "Sine.easeIn",
|
||||
onComplete: () => {
|
||||
this.scene.tweens.add({
|
||||
targets: this.evolutionBgOverlay,
|
||||
alpha: 0,
|
||||
duration: 250,
|
||||
onComplete: onEvolutionComplete
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
doSpiralUpward() {
|
||||
let f = 0;
|
||||
|
||||
|
@ -320,7 +341,6 @@ export class EvolutionPhase extends Phase {
|
|||
|
||||
doCycle(l: number, lastCycle: integer = 15): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler;
|
||||
const isLastCycle = l === lastCycle;
|
||||
this.scene.tweens.add({
|
||||
targets: this.pokemonTintSprite,
|
||||
|
@ -336,7 +356,7 @@ export class EvolutionPhase extends Phase {
|
|||
duration: 500 / l,
|
||||
yoyo: !isLastCycle,
|
||||
onComplete: () => {
|
||||
if (evolutionHandler.cancelled) {
|
||||
if (this.evolutionHandler.cancelled) {
|
||||
return resolve(false);
|
||||
}
|
||||
if (l < lastCycle) {
|
||||
|
|
|
@ -52,11 +52,11 @@ import {
|
|||
HitHealModifier,
|
||||
PokemonMultiHitModifier,
|
||||
} from "#app/modifier/modifier";
|
||||
import { PokemonPhase } from "#app/phases/pokemon-phase";
|
||||
import { BooleanHolder, executeIf, NumberHolder } from "#app/utils";
|
||||
import { BattlerTagType } from "#enums/battler-tag-type";
|
||||
import { Moves } from "#enums/moves";
|
||||
import i18next from "i18next";
|
||||
import { PokemonPhase } from "./pokemon-phase";
|
||||
|
||||
export class MoveEffectPhase extends PokemonPhase {
|
||||
public move: PokemonMove;
|
||||
|
@ -517,7 +517,11 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
return true;
|
||||
}
|
||||
|
||||
const user = this.getUserPokemon()!; // TODO: is this bang correct?
|
||||
const user = this.getUserPokemon();
|
||||
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits.
|
||||
// However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal
|
||||
|
@ -566,9 +570,9 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
}
|
||||
|
||||
/** @returns The {@linkcode Pokemon} using this phase's invoked move */
|
||||
public getUserPokemon(): Pokemon | undefined {
|
||||
public getUserPokemon(): Pokemon | null {
|
||||
if (this.battlerIndex > BattlerIndex.ENEMY_2) {
|
||||
return this.scene.getPokemonById(this.battlerIndex) ?? undefined;
|
||||
return this.scene.getPokemonById(this.battlerIndex);
|
||||
}
|
||||
return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex];
|
||||
}
|
||||
|
@ -596,8 +600,7 @@ export class MoveEffectPhase extends PokemonPhase {
|
|||
|
||||
/**
|
||||
* Prevents subsequent strikes of this phase's invoked move from occurring
|
||||
* @param target {@linkcode Pokemon} if defined, only stop subsequent
|
||||
* strikes against this Pokemon
|
||||
* @param target - If defined, only stop subsequent strikes against this {@linkcode Pokemon}
|
||||
*/
|
||||
public stopMultiHit(target?: Pokemon): void {
|
||||
// If given a specific target, remove the target from subsequent strikes
|
||||
|
|
|
@ -163,6 +163,11 @@ export const SettingKeys = {
|
|||
Shop_Overlay_Opacity: "SHOP_OVERLAY_OPACITY"
|
||||
};
|
||||
|
||||
export enum MusicPreference {
|
||||
CONSISTENT,
|
||||
MIXED
|
||||
}
|
||||
|
||||
/**
|
||||
* All Settings not related to controls
|
||||
*/
|
||||
|
@ -634,7 +639,7 @@ export const Setting: Array<Setting> = [
|
|||
label: i18next.t("settings:mixed")
|
||||
}
|
||||
],
|
||||
default: 0,
|
||||
default: MusicPreference.MIXED,
|
||||
type: SettingType.AUDIO,
|
||||
requireReload: true
|
||||
},
|
||||
|
|
|
@ -216,7 +216,7 @@ describe("Abilities - Unburden", () => {
|
|||
.moveset([ Moves.SPLASH ]);
|
||||
await game.classicMode.startBattle([ Species.TREECKO, Species.MEOWTH, Species.WEEZING ]);
|
||||
|
||||
const playerPokemon = game.scene.getParty();
|
||||
const playerPokemon = game.scene.getPlayerParty();
|
||||
const treecko = playerPokemon[0];
|
||||
const weezing = playerPokemon[2];
|
||||
treecko.abilityIndex = 2;
|
||||
|
|
|
@ -156,6 +156,7 @@ export default class GameManager {
|
|||
this.scene.enableTutorials = false;
|
||||
this.scene.gameData.gender = PlayerGender.MALE; // set initial player gender
|
||||
this.scene.battleStyle = this.settings.battleStyle;
|
||||
this.scene.fieldVolume = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
41
src/utils.ts
41
src/utils.ts
|
@ -121,18 +121,6 @@ export function randSeedWeightedItem<T>(items: T[]): T {
|
|||
: Phaser.Math.RND.weightedPick(items);
|
||||
}
|
||||
|
||||
export function randSeedEasedWeightedItem<T>(items: T[], easingFunction: string = "Sine.easeIn"): T | null {
|
||||
if (!items.length) {
|
||||
return null;
|
||||
}
|
||||
if (items.length === 1) {
|
||||
return items[0];
|
||||
}
|
||||
const value = Phaser.Math.RND.realInRange(0, 1);
|
||||
const easedValue = Phaser.Tweens.Builders.GetEaseFunction(easingFunction)(value);
|
||||
return items[Math.floor(easedValue * items.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffle a list using the seeded rng. Utilises the Fisher-Yates algorithm.
|
||||
* @param {Array} items An array of items.
|
||||
|
@ -380,18 +368,21 @@ export class NumberHolder {
|
|||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use {@linkcode NumberHolder} */
|
||||
export class IntegerHolder extends NumberHolder {
|
||||
constructor(value: integer) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use {@linkcode NumberHolder}*/
|
||||
export class FixedInt extends IntegerHolder {
|
||||
constructor(value: integer) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
export function fixedInt(value: integer): integer {
|
||||
return new FixedInt(value) as unknown as integer;
|
||||
}
|
||||
|
@ -474,14 +465,15 @@ export function hslToHex(h: number, s: number, l: number): string {
|
|||
return `#${f(0)}${f(8)}${f(4)}`;
|
||||
}
|
||||
|
||||
/*This function returns true if the current lang is available for some functions
|
||||
If the lang is not in the function, it usually means that lang is going to use the default english version
|
||||
This function is used in:
|
||||
- summary-ui-handler.ts: If the lang is not available, it'll use types.json (english)
|
||||
English itself counts as not available
|
||||
/**
|
||||
* This function returns `true` if all localized images used by the game have been added for the given language.
|
||||
*
|
||||
* If the lang is not in the function, it usually means that lang is going to use the default english version
|
||||
*
|
||||
* English itself counts as not available
|
||||
*/
|
||||
export function verifyLang(lang?: string): boolean {
|
||||
//IMPORTANT - ONLY ADD YOUR LANG HERE IF YOU'VE ALREADY ADDED ALL THE NECESSARY IMAGES
|
||||
export function hasAllLocalizedSprites(lang?: string): boolean {
|
||||
// IMPORTANT - ONLY ADD YOUR LANG HERE IF YOU'VE ALREADY ADDED ALL THE NECESSARY IMAGES
|
||||
if (!lang) {
|
||||
lang = i18next.resolvedLanguage;
|
||||
}
|
||||
|
@ -503,7 +495,7 @@ export function verifyLang(lang?: string): boolean {
|
|||
}
|
||||
|
||||
/**
|
||||
* Prints the type and name of all game objects in a container for debuggin purposes
|
||||
* Prints the type and name of all game objects in a container for debugging purposes
|
||||
* @param container container with game objects inside it
|
||||
*/
|
||||
export function printContainerList(container: Phaser.GameObjects.Container): void {
|
||||
|
@ -578,17 +570,12 @@ export function capitalizeString(str: string, sep: string, lowerFirstChar: boole
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if an object is null or undefined
|
||||
* @param object
|
||||
*/
|
||||
export function isNullOrUndefined(object: any): object is undefined | null {
|
||||
return null === object || undefined === object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalizes the first letter of a string
|
||||
* @param str
|
||||
*/
|
||||
export function capitalizeFirstLetter(str: string) {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
|
@ -614,7 +601,7 @@ export function toDmgValue(value: number, minValue: number = 1) {
|
|||
* @returns the localized sprite key
|
||||
*/
|
||||
export function getLocalizedSpriteKey(baseKey: string) {
|
||||
return `${baseKey}${verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`;
|
||||
return `${baseKey}${hasAllLocalizedSprites(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -622,7 +609,7 @@ export function getLocalizedSpriteKey(baseKey: string) {
|
|||
* @param num the number to check
|
||||
* @param min the minimum value (included)
|
||||
* @param max the maximum value (included)
|
||||
* @returns true if number is **inclusive** between min and max
|
||||
* @returns `true` if number is **inclusive** between min and max
|
||||
*/
|
||||
export function isBetween(num: number, min: number, max: number): boolean {
|
||||
return num >= min && num <= max;
|
||||
|
|
Loading…
Reference in New Issue