[Refactor] Moveset arrays can no longer hold null values (#4919)

* Pokemon movesets no longer allow `null` values

* Clean up all the bangs caused by movesets allowing `null`

* Pokemon movesets no longer allow `null` values

* Clean up all the bangs caused by movesets allowing `null`

* Fix merge issues

* Remove various unnecessary `?`

* Apply biome

* Fix `global-trade-system-encounter.ts`

* Fix merge issue

* Remove unnecessary parentheses

* Remove missed `?`

* Match formatting of `pokemon.ts`
This commit is contained in:
NightKev 2025-03-26 19:12:54 -07:00 committed by GitHub
parent 0b1f324f97
commit db850c79cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 206 additions and 205 deletions

View File

@ -1371,6 +1371,7 @@ export default class BattleScene extends SceneBase {
return Math.max(doubleChance.value, 1); return Math.max(doubleChance.value, 1);
} }
// TODO: ...this never actually returns `null`, right?
newBattle( newBattle(
waveIndex?: number, waveIndex?: number,
battleType?: BattleType, battleType?: BattleType,

View File

@ -179,7 +179,7 @@ class TimeOfDayEvolutionCondition extends SpeciesEvolutionCondition {
class MoveEvolutionCondition extends SpeciesEvolutionCondition { class MoveEvolutionCondition extends SpeciesEvolutionCondition {
public move: Moves; public move: Moves;
constructor(move: Moves) { constructor(move: Moves) {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0); super(p => p.moveset.filter(m => m.moveId === move).length > 0);
this.move = move; this.move = move;
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join(""); const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) }); this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) });
@ -282,7 +282,7 @@ class TyrogueEvolutionCondition extends SpeciesEvolutionCondition {
public move: Moves; public move: Moves;
constructor(move: Moves) { constructor(move: Moves) {
super(p => super(p =>
p.getMoveset(true).find(m => m && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(m?.moveId))?.moveId === move); p.getMoveset(true).find(m => m && [ Moves.LOW_SWEEP, Moves.MACH_PUNCH, Moves.RAPID_SPIN ].includes(m.moveId))?.moveId === move);
this.move = move; this.move = move;
const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join(""); const moveKey = Moves[this.move].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("");
this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) }); this.description = i18next.t("pokemonEvolutions:move", { move: i18next.t(`move:${moveKey}.name`) });
@ -303,11 +303,11 @@ class MoveTimeOfDayEvolutionCondition extends SpeciesEvolutionCondition {
public timesOfDay: TimeOfDay[]; public timesOfDay: TimeOfDay[];
constructor(move: Moves, tod: "day" | "night") { constructor(move: Moves, tod: "day" | "night") {
if (tod === "day") { if (tod === "day") {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY)); super(p => p.moveset.filter(m => m.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DAWN || globalScene.arena.getTimeOfDay() === TimeOfDay.DAY));
this.move = move; this.move = move;
this.timesOfDay = [ TimeOfDay.DAWN, TimeOfDay.DAY ]; this.timesOfDay = [ TimeOfDay.DAWN, TimeOfDay.DAY ];
} else if (tod === "night") { } else if (tod === "night") {
super(p => p.moveset.filter(m => m?.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT)); super(p => p.moveset.filter(m => m.moveId === move).length > 0 && (globalScene.arena.getTimeOfDay() === TimeOfDay.DUSK || globalScene.arena.getTimeOfDay() === TimeOfDay.NIGHT));
this.move = move; this.move = move;
this.timesOfDay = [ TimeOfDay.DUSK, TimeOfDay.NIGHT ]; this.timesOfDay = [ TimeOfDay.DUSK, TimeOfDay.NIGHT ];
} else { } else {
@ -332,7 +332,7 @@ class DunsparceEvolutionCondition extends SpeciesEvolutionCondition {
constructor() { constructor() {
super(p => { super(p => {
let ret = false; let ret = false;
if (p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0) { if (p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0) {
globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); globalScene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id);
} }
return ret; return ret;
@ -1540,13 +1540,13 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.AIPOM]: [ [Species.AIPOM]: [
new SpeciesEvolution(Species.AMBIPOM, 32, null, new MoveEvolutionCondition(Moves.DOUBLE_HIT), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.AMBIPOM, 32, null, new MoveEvolutionCondition(Moves.DOUBLE_HIT), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.SUNKERN]: [ [Species.SUNKERN]: [
new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.YANMA]: [ [Species.YANMA]: [
new SpeciesEvolution(Species.YANMEGA, 33, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.YANMEGA, 33, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.MURKROW]: [ [Species.MURKROW]: [
new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1555,11 +1555,11 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.GIRAFARIG]: [ [Species.GIRAFARIG]: [
new SpeciesEvolution(Species.FARIGIRAF, 32, null, new MoveEvolutionCondition(Moves.TWIN_BEAM), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.FARIGIRAF, 32, null, new MoveEvolutionCondition(Moves.TWIN_BEAM), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.DUNSPARCE]: [ [Species.DUNSPARCE]: [
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new DunsparceEvolutionCondition(), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new DunsparceEvolutionCondition(), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "two-segment", 32, null, new MoveEvolutionCondition(Moves.HYPER_DRILL), SpeciesWildEvolutionDelay.LONG) new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "two-segment", 32, null, new MoveEvolutionCondition(Moves.HYPER_DRILL), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.GLIGAR]: [ [Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new TimeOfDayEvolutionCondition("night") /* Razor fang at night*/, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new TimeOfDayEvolutionCondition("night") /* Razor fang at night*/, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1571,10 +1571,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
], ],
[Species.PILOSWINE]: [ [Species.PILOSWINE]: [
new SpeciesEvolution(Species.MAMOSWINE, 1, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.MAMOSWINE, 1, null, new MoveEvolutionCondition(Moves.ANCIENT_POWER), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.STANTLER]: [ [Species.STANTLER]: [
new SpeciesEvolution(Species.WYRDEER, 25, null, new MoveEvolutionCondition(Moves.PSYSHIELD_BASH), SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.WYRDEER, 25, null, new MoveEvolutionCondition(Moves.PSYSHIELD_BASH), SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.LOMBRE]: [ [Species.LOMBRE]: [
new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1592,7 +1592,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG)
], ],
[Species.BONSLY]: [ [Species.BONSLY]: [
new SpeciesEvolution(Species.SUDOWOODO, 1, null, new MoveEvolutionCondition(Moves.MIMIC), SpeciesWildEvolutionDelay.MEDIUM) new SpeciesEvolution(Species.SUDOWOODO, 1, null, new MoveEvolutionCondition(Moves.MIMIC), SpeciesWildEvolutionDelay.MEDIUM)
], ],
[Species.MIME_JR]: [ [Species.MIME_JR]: [
new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new MoveTimeOfDayEvolutionCondition(Moves.MIMIC, "night"), SpeciesWildEvolutionDelay.MEDIUM), new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new MoveTimeOfDayEvolutionCondition(Moves.MIMIC, "night"), SpeciesWildEvolutionDelay.MEDIUM),
@ -1651,10 +1651,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new TimeOfDayEvolutionCondition("night")) new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new TimeOfDayEvolutionCondition("night"))
], ],
[Species.STEENEE]: [ [Species.STEENEE]: [
new SpeciesEvolution(Species.TSAREENA, 28, null, new MoveEvolutionCondition(Moves.STOMP), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.TSAREENA, 28, null, new MoveEvolutionCondition(Moves.STOMP), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.POIPOLE]: [ [Species.POIPOLE]: [
new SpeciesEvolution(Species.NAGANADEL, 1, null, new MoveEvolutionCondition(Moves.DRAGON_PULSE), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.NAGANADEL, 1, null, new MoveEvolutionCondition(Moves.DRAGON_PULSE), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.ALOLA_SANDSHREW]: [ [Species.ALOLA_SANDSHREW]: [
new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1720,7 +1720,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_QWILFISH]: [ [Species.HISUI_QWILFISH]: [
new SpeciesEvolution(Species.OVERQWIL, 28, null, new MoveEvolutionCondition(Moves.BARB_BARRAGE), SpeciesWildEvolutionDelay.LONG) new SpeciesEvolution(Species.OVERQWIL, 28, null, new MoveEvolutionCondition(Moves.BARB_BARRAGE), SpeciesWildEvolutionDelay.LONG)
], ],
[Species.HISUI_SNEASEL]: [ [Species.HISUI_SNEASEL]: [
new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new TimeOfDayEvolutionCondition("day") /* Razor claw at day*/, SpeciesWildEvolutionDelay.VERY_LONG) new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new TimeOfDayEvolutionCondition("day") /* Razor claw at day*/, SpeciesWildEvolutionDelay.VERY_LONG)

View File

@ -1174,13 +1174,13 @@ export class EncoreTag extends MoveRestrictionBattlerTag {
const movePhase = globalScene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon); const movePhase = globalScene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon);
if (movePhase) { if (movePhase) {
const movesetMove = pokemon.getMoveset().find(m => m!.moveId === this.moveId); // TODO: is this bang correct? const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId);
if (movesetMove) { if (movesetMove) {
const lastMove = pokemon.getLastXMoves(1)[0]; const lastMove = pokemon.getLastXMoves(1)[0];
globalScene.tryReplacePhase( globalScene.tryReplacePhase(
m => m instanceof MovePhase && m.pokemon === pokemon, m => m instanceof MovePhase && m.pokemon === pokemon,
new MovePhase(pokemon, lastMove.targets!, movesetMove), new MovePhase(pokemon, lastMove.targets ?? [], movesetMove),
); // TODO: is this bang correct? );
} }
} }
} }
@ -1191,7 +1191,7 @@ export class EncoreTag extends MoveRestrictionBattlerTag {
*/ */
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.CUSTOM) { if (lapseType === BattlerTagLapseType.CUSTOM) {
const encoredMove = pokemon.getMoveset().find(m => m?.moveId === this.moveId); const encoredMove = pokemon.getMoveset().find(m => m.moveId === this.moveId);
if (encoredMove && encoredMove?.getPpRatio() > 0) { if (encoredMove && encoredMove?.getPpRatio() > 0) {
return true; return true;
} }
@ -3184,7 +3184,7 @@ export class ImprisonTag extends MoveRestrictionBattlerTag {
public override isMoveRestricted(move: Moves, _user: Pokemon): boolean { public override isMoveRestricted(move: Moves, _user: Pokemon): boolean {
const source = this.getSourcePokemon(); const source = this.getSourcePokemon();
if (source) { if (source) {
const sourceMoveset = source.getMoveset().map(m => m!.moveId); const sourceMoveset = source.getMoveset().map(m => m.moveId);
return sourceMoveset?.includes(move) && source.isActive(true); return sourceMoveset?.includes(move) && source.isActive(true);
} }
return false; return false;
@ -3354,7 +3354,7 @@ export class GrudgeTag extends BattlerTag {
if (lapseType === BattlerTagLapseType.CUSTOM && sourcePokemon) { if (lapseType === BattlerTagLapseType.CUSTOM && sourcePokemon) {
if (sourcePokemon.isActive() && pokemon.isOpponent(sourcePokemon)) { if (sourcePokemon.isActive() && pokemon.isOpponent(sourcePokemon)) {
const lastMove = pokemon.turnData.attacksReceived[0]; const lastMove = pokemon.turnData.attacksReceived[0];
const lastMoveData = sourcePokemon.getMoveset().find(m => m?.moveId === lastMove.move); const lastMoveData = sourcePokemon.getMoveset().find(m => m.moveId === lastMove.move);
if (lastMoveData && lastMove.move !== Moves.STRUGGLE) { if (lastMoveData && lastMove.move !== Moves.STRUGGLE) {
lastMoveData.ppUsed = lastMoveData.getMovePp(); lastMoveData.ppUsed = lastMoveData.getMovePp();
globalScene.queueMessage( globalScene.queueMessage(

View File

@ -65,7 +65,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate {
return (pokemon: Pokemon) => { return (pokemon: Pokemon) => {
const threshold = new Utils.NumberHolder(0.25); const threshold = new Utils.NumberHolder(0.25);
applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, false, threshold);
return !!pokemon.getMoveset().find(m => !m?.getPpRatio()); return !!pokemon.getMoveset().find(m => !m.getPpRatio());
}; };
} }
} }
@ -147,9 +147,9 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc {
if (pokemon.battleData) { if (pokemon.battleData) {
pokemon.battleData.berriesEaten.push(berryType); pokemon.battleData.berriesEaten.push(berryType);
} }
const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio())
? pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m.getPpRatio())
: pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct? : pokemon.getMoveset().find(m => m.getPpRatio() < 1);
if (ppRestoreMove !== undefined) { if (ppRestoreMove !== undefined) {
ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0); ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0);
globalScene.queueMessage( globalScene.queueMessage(

View File

@ -450,7 +450,7 @@ export class SingleGenerationChallenge extends Challenge {
applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean {
const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation; const baseGeneration = getPokemonSpecies(pokemon.species.speciesId).generation;
const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; // TODO: is the bang on fusionSpecies correct? const fusionGeneration = pokemon.isFusion() ? getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0;
if ( if (
pokemon.isPlayer() && pokemon.isPlayer() &&
(baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value)) (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))

View File

@ -1881,14 +1881,14 @@ export class HealAttr extends MoveEffectAttr {
*/ */
export class PartyStatusCureAttr extends MoveEffectAttr { export class PartyStatusCureAttr extends MoveEffectAttr {
/** Message to display after using move */ /** Message to display after using move */
private message: string; private message: string | null;
/** Skips mons with this ability, ie. Soundproof */ /** Skips mons with this ability, ie. Soundproof */
private abilityCondition: Abilities; private abilityCondition: Abilities;
constructor(message: string | null, abilityCondition: Abilities) { constructor(message: string | null, abilityCondition: Abilities) {
super(); super();
this.message = message!; // TODO: is this bang correct? this.message = message;
this.abilityCondition = abilityCondition; this.abilityCondition = abilityCondition;
} }
@ -2103,10 +2103,10 @@ export class BoostHealAttr extends HealAttr {
/** The lambda expression to check against when boosting the healing value */ /** The lambda expression to check against when boosting the healing value */
private condition?: MoveConditionFunc; private condition?: MoveConditionFunc;
constructor(normalHealRatio?: number, boostedHealRatio?: number, showAnim?: boolean, selfTarget?: boolean, condition?: MoveConditionFunc) { constructor(normalHealRatio: number = 0.5, boostedHealRatio: number = 2 / 3, showAnim?: boolean, selfTarget?: boolean, condition?: MoveConditionFunc) {
super(normalHealRatio, showAnim, selfTarget); super(normalHealRatio, showAnim, selfTarget);
this.normalHealRatio = normalHealRatio!; // TODO: is this bang correct? this.normalHealRatio = normalHealRatio;
this.boostedHealRatio = boostedHealRatio!; // TODO: is this bang correct? this.boostedHealRatio = boostedHealRatio;
this.condition = condition; this.condition = condition;
} }
@ -3374,14 +3374,14 @@ export class SecretPowerAttr extends MoveEffectAttr {
export class PostVictoryStatStageChangeAttr extends MoveAttr { export class PostVictoryStatStageChangeAttr extends MoveAttr {
private stats: BattleStat[]; private stats: BattleStat[];
private stages: number; private stages: number;
private condition: MoveConditionFunc | null; private condition?: MoveConditionFunc;
private showMessage: boolean; private showMessage: boolean;
constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { constructor(stats: BattleStat[], stages: number, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) {
super(); super();
this.stats = stats; this.stats = stats;
this.stages = stages; this.stages = stages;
this.condition = condition!; // TODO: is this bang correct? this.condition = condition;
this.showMessage = showMessage; this.showMessage = showMessage;
} }
applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void { applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void {
@ -3653,7 +3653,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr {
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const ppMax = move.pp; const ppMax = move.pp;
const ppUsed = user.moveset.find((m) => m?.moveId === move.id)?.ppUsed!; // TODO: is the bang correct? const ppUsed = user.moveset.find((m) => m.moveId === move.id)?.ppUsed ?? 0;
let ppRemains = ppMax - ppUsed; let ppRemains = ppMax - ppUsed;
/** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */ /** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */
@ -3779,7 +3779,13 @@ export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultipl
let count = 0; let count = 0;
let turnMove: TurnMove | undefined; let turnMove: TurnMove | undefined;
while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.includes(turnMove?.move!))) && (!resetOnFail || turnMove?.result === MoveResult.SUCCESS)) { // TODO: is this bang correct? while (
(
(turnMove = moveHistory.shift())?.move === move.id
|| (comboMoves.length && comboMoves.includes(turnMove?.move ?? Moves.NONE))
)
&& (!resetOnFail || turnMove?.result === MoveResult.SUCCESS)
) {
if (count < (limit - 1)) { if (count < (limit - 1)) {
count++; count++;
} else if (resetOnLimit) { } else if (resetOnLimit) {
@ -4365,8 +4371,8 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr {
for (const p of pokemonActed) { for (const p of pokemonActed) {
const [ lastMove ] = p.getLastXMoves(1); const [ lastMove ] = p.getLastXMoves(1);
if (lastMove?.result !== MoveResult.FAIL) { if (lastMove.result !== MoveResult.FAIL) {
if ((lastMove?.result === MoveResult.SUCCESS) && (lastMove?.move === this.move)) { if ((lastMove.result === MoveResult.SUCCESS) && (lastMove.move === this.move)) {
power.value *= 2; power.value *= 2;
return true; return true;
} else { } else {
@ -5461,7 +5467,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
: null; : null;
} }
getTagTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number | void { getTagTargetBenefitScore(): number {
switch (this.tagType) { switch (this.tagType) {
case BattlerTagType.RECHARGING: case BattlerTagType.RECHARGING:
case BattlerTagType.PERISH_SONG: case BattlerTagType.PERISH_SONG:
@ -5506,6 +5512,9 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
case BattlerTagType.CRIT_BOOST: case BattlerTagType.CRIT_BOOST:
case BattlerTagType.ALWAYS_CRIT: case BattlerTagType.ALWAYS_CRIT:
return 5; return 5;
default:
console.warn(`BattlerTag ${BattlerTagType[this.tagType]} is missing a score!`);
return 0;
} }
} }
@ -5514,7 +5523,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr {
if (moveChance < 0) { if (moveChance < 0) {
moveChance = 100; moveChance = 100;
} }
return Math.floor(this.getTagTargetBenefitScore(user, target, move)! * (moveChance / 100)); // TODO: is the bang correct? return Math.floor(this.getTagTargetBenefitScore() * (moveChance / 100));
} }
} }
@ -5769,7 +5778,7 @@ export class ProtectAttr extends AddBattlerTagAttr {
while (moveHistory.length) { while (moveHistory.length) {
turnMove = moveHistory.shift(); turnMove = moveHistory.shift();
if (!allMoves[turnMove?.move!].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) { // TODO: is the bang correct? if (!allMoves[turnMove?.move ?? Moves.NONE].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) {
break; break;
} }
timesUsed++; timesUsed++;
@ -6688,7 +6697,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr {
return false; return false;
} }
const firstMoveType = target.getMoveset()[0]?.getMove().type!; // TODO: is this bang correct? const firstMoveType = target.getMoveset()[0].getMove().type;
user.summonData.types = [ firstMoveType ]; user.summonData.types = [ firstMoveType ];
globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${PokemonType[firstMoveType]}`) })); globalScene.queueMessage(i18next.t("battle:transformedIntoType", { pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${PokemonType[firstMoveType]}`) }));
@ -7005,7 +7014,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// get the last move used (excluding status based failures) as well as the corresponding moveset slot // get the last move used (excluding status based failures) as well as the corresponding moveset slot
const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE)!; const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE)!;
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move)!; const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move)!;
// If the last move used can hit more than one target or has variable targets, // If the last move used can hit more than one target or has variable targets,
// re-compute the targets for the attack // re-compute the targets for the attack
// (mainly for alternating double/single battle shenanigans) // (mainly for alternating double/single battle shenanigans)
@ -7039,7 +7048,7 @@ export class RepeatMoveAttr extends MoveEffectAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => { return (user, target, move) => {
const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE); const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE);
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove?.move);
const uninstructableMoves = [ const uninstructableMoves = [
// Locking/Continually Executed moves // Locking/Continually Executed moves
Moves.OUTRAGE, Moves.OUTRAGE,
@ -7134,19 +7143,19 @@ export class ReducePpMoveAttr extends MoveEffectAttr {
* *
* @param user {@linkcode Pokemon} that used the attack * @param user {@linkcode Pokemon} that used the attack
* @param target {@linkcode Pokemon} targeted by the attack * @param target {@linkcode Pokemon} targeted by the attack
* @param move {@linkcode Move} being used * @param move N/A
* @param args N/A * @param args N/A
* @returns {boolean} true * @returns `true`
*/ */
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
// Null checks can be skipped due to condition function // Null checks can be skipped due to condition function
const lastMove = target.getLastXMoves().find(() => true); const lastMove = target.getLastXMoves()[0];
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move)!;
const lastPpUsed = movesetMove?.ppUsed!; // TODO: is the bang correct? const lastPpUsed = movesetMove.ppUsed;
movesetMove!.ppUsed = Math.min((movesetMove?.ppUsed!) + this.reduction, movesetMove?.getMovePp()!); // TODO: is the bang correct? movesetMove.ppUsed = Math.min((lastPpUsed) + this.reduction, movesetMove.getMovePp());
const message = i18next.t("battle:ppReduced", { targetName: getPokemonNameWithAffix(target), moveName: movesetMove?.getName(), reduction: (movesetMove?.ppUsed!) - lastPpUsed }); // TODO: is the bang correct? const message = i18next.t("battle:ppReduced", { targetName: getPokemonNameWithAffix(target), moveName: movesetMove.getName(), reduction: (movesetMove.ppUsed) - lastPpUsed });
globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(target?.id, movesetMove?.getMove()!, movesetMove?.ppUsed!)); // TODO: are these bangs correct? globalScene.eventTarget.dispatchEvent(new MoveUsedEvent(target.id, movesetMove.getMove(), movesetMove.ppUsed));
globalScene.queueMessage(message); globalScene.queueMessage(message);
return true; return true;
@ -7154,9 +7163,9 @@ export class ReducePpMoveAttr extends MoveEffectAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user, target, move) => { return (user, target, move) => {
const lastMove = target.getLastXMoves().find(() => true); const lastMove = target.getLastXMoves()[0];
if (lastMove) { if (lastMove) {
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move);
return !!movesetMove?.getPpRatio(); return !!movesetMove?.getPpRatio();
} }
return false; return false;
@ -7164,9 +7173,9 @@ export class ReducePpMoveAttr extends MoveEffectAttr {
} }
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
const lastMove = target.getLastXMoves().find(() => true); const lastMove = target.getLastXMoves()[0];
if (lastMove) { if (lastMove) {
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move);
if (movesetMove) { if (movesetMove) {
const maxPp = movesetMove.getMovePp(); const maxPp = movesetMove.getMovePp();
const ppLeft = maxPp - movesetMove.ppUsed; const ppLeft = maxPp - movesetMove.ppUsed;
@ -7203,7 +7212,7 @@ export class AttackReducePpMoveAttr extends ReducePpMoveAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const lastMove = target.getLastXMoves().find(() => true); const lastMove = target.getLastXMoves().find(() => true);
if (lastMove) { if (lastMove) {
const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move);
if (Boolean(movesetMove?.getPpRatio())) { if (Boolean(movesetMove?.getPpRatio())) {
super.apply(user, target, move, args); super.apply(user, target, move, args);
} }
@ -7249,7 +7258,7 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr {
const copiedMove = allMoves[targetMoves[0].move]; const copiedMove = allMoves[targetMoves[0].move];
const thisMoveIndex = user.getMoveset().findIndex(m => m?.moveId === move.id); const thisMoveIndex = user.getMoveset().findIndex(m => m.moveId === move.id);
if (thisMoveIndex === -1) { if (thisMoveIndex === -1) {
return false; return false;
@ -7301,7 +7310,7 @@ export class SketchAttr extends MoveEffectAttr {
} }
const sketchedMove = allMoves[targetMove.move]; const sketchedMove = allMoves[targetMove.move];
const sketchIndex = user.getMoveset().findIndex(m => m?.moveId === move.id); const sketchIndex = user.getMoveset().findIndex(m => m.moveId === move.id);
if (sketchIndex === -1) { if (sketchIndex === -1) {
return false; return false;
} }
@ -7340,7 +7349,7 @@ export class SketchAttr extends MoveEffectAttr {
return false; return false;
} }
if (user.getMoveset().find(m => m?.moveId === targetMove.move)) { if (user.getMoveset().find(m => m.moveId === targetMove.move)) {
return false; return false;
} }
@ -7790,7 +7799,7 @@ export class LastResortAttr extends MoveAttr {
getCondition(): MoveConditionFunc { getCondition(): MoveConditionFunc {
return (user: Pokemon, target: Pokemon, move: Move) => { return (user: Pokemon, target: Pokemon, move: Move) => {
const uniqueUsedMoveIds = new Set<Moves>(); const uniqueUsedMoveIds = new Set<Moves>();
const movesetMoveIds = user.getMoveset().map(m => m?.moveId); const movesetMoveIds = user.getMoveset().map(m => m.moveId);
user.getMoveHistory().map(m => { user.getMoveHistory().map(m => {
if (m.move !== move.id && movesetMoveIds.find(mm => mm === m.move)) { if (m.move !== move.id && movesetMoveIds.find(mm => mm === m.move)) {
uniqueUsedMoveIds.add(m.move); uniqueUsedMoveIds.add(m.move);
@ -9186,7 +9195,17 @@ export function initMoves() {
.attr(FlinchAttr), .attr(FlinchAttr),
new AttackMove(Moves.WEATHER_BALL, PokemonType.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 3) new AttackMove(Moves.WEATHER_BALL, PokemonType.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 3)
.attr(WeatherBallTypeAttr) .attr(WeatherBallTypeAttr)
.attr(MovePowerMultiplierAttr, (user, target, move) => [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN ].includes(globalScene.arena.weather?.weatherType!) && !globalScene.arena.weather?.isEffectSuppressed() ? 2 : 1) // TODO: is this bang correct? .attr(MovePowerMultiplierAttr, (user, target, move) => {
const weather = globalScene.arena.weather;
if (!weather) {
return 1;
}
const weatherTypes = [ WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN ];
if (weatherTypes.includes(weather.weatherType) && !weather.isEffectSuppressed()) {
return 2;
}
return 1;
})
.ballBombMove(), .ballBombMove(),
new StatusMove(Moves.AROMATHERAPY, PokemonType.GRASS, -1, 5, -1, 0, 3) new StatusMove(Moves.AROMATHERAPY, PokemonType.GRASS, -1, 5, -1, 0, 3)
.attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), Abilities.SAP_SIPPER) .attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), Abilities.SAP_SIPPER)
@ -9426,7 +9445,13 @@ export function initMoves() {
.attr(AbilityChangeAttr, Abilities.INSOMNIA) .attr(AbilityChangeAttr, Abilities.INSOMNIA)
.reflectable(), .reflectable(),
new AttackMove(Moves.SUCKER_PUNCH, PokemonType.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4) new AttackMove(Moves.SUCKER_PUNCH, PokemonType.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4)
.condition((user, target, move) => globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? .condition((user, target, move) => {
const turnCommand = globalScene.currentBattle.turnCommands[target.getBattlerIndex()];
if (!turnCommand || !turnCommand.move) {
return false;
}
return (turnCommand.command === Command.FIGHT && !target.turnData.acted && allMoves[turnCommand.move.move].category !== MoveCategory.STATUS);
}),
new StatusMove(Moves.TOXIC_SPIKES, PokemonType.POISON, -1, 20, -1, 0, 4) new StatusMove(Moves.TOXIC_SPIKES, PokemonType.POISON, -1, 20, -1, 0, 4)
.attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES)
.target(MoveTarget.ENEMY_SIDE) .target(MoveTarget.ENEMY_SIDE)
@ -10321,8 +10346,12 @@ export function initMoves() {
.ignoresSubstitute(), .ignoresSubstitute(),
new AttackMove(Moves.SMART_STRIKE, PokemonType.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7), new AttackMove(Moves.SMART_STRIKE, PokemonType.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7),
new StatusMove(Moves.PURIFY, PokemonType.POISON, -1, 20, -1, 0, 7) new StatusMove(Moves.PURIFY, PokemonType.POISON, -1, 20, -1, 0, 7)
.condition( .condition((user, target, move) => {
(user: Pokemon, target: Pokemon, move: Move) => isNonVolatileStatusEffect(target.status?.effect!)) // TODO: is this bang correct? if (!target.status) {
return false;
}
return isNonVolatileStatusEffect(target.status.effect);
})
.attr(HealAttr, 0.5) .attr(HealAttr, 0.5)
.attr(HealStatusEffectAttr, false, getNonVolatileStatusEffects()) .attr(HealStatusEffectAttr, false, getNonVolatileStatusEffects())
.triageMove() .triageMove()
@ -11041,7 +11070,13 @@ export function initMoves() {
.slicingMove(), .slicingMove(),
new AttackMove(Moves.HYDRO_STEAM, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) new AttackMove(Moves.HYDRO_STEAM, PokemonType.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9)
.attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY) .attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY)
.attr(MovePowerMultiplierAttr, (user, target, move) => [ WeatherType.SUNNY, WeatherType.HARSH_SUN ].includes(globalScene.arena.weather?.weatherType!) && !globalScene.arena.weather?.isEffectSuppressed() ? 1.5 : 1), // TODO: is this bang correct? .attr(MovePowerMultiplierAttr, (user, target, move) => {
const weather = globalScene.arena.weather;
if (!weather) {
return 1;
}
return [ WeatherType.SUNNY, WeatherType.HARSH_SUN ].includes(weather.weatherType) && !weather.isEffectSuppressed() ? 1.5 : 1;
}),
new AttackMove(Moves.RUINATION, PokemonType.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) new AttackMove(Moves.RUINATION, PokemonType.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9)
.attr(TargetHalfHpDamageAttr), .attr(TargetHalfHpDamageAttr),
new AttackMove(Moves.COLLISION_COURSE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) new AttackMove(Moves.COLLISION_COURSE, PokemonType.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)
@ -11156,7 +11191,13 @@ export function initMoves() {
.attr(ProtectAttr, BattlerTagType.BURNING_BULWARK) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK)
.condition(failIfLastCondition), .condition(failIfLastCondition),
new AttackMove(Moves.THUNDERCLAP, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) new AttackMove(Moves.THUNDERCLAP, PokemonType.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9)
.condition((user, target, move) => globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[globalScene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? .condition((user, target, move) => {
const turnCommand = globalScene.currentBattle.turnCommands[target.getBattlerIndex()];
if (!turnCommand || !turnCommand.move) {
return false;
}
return (turnCommand.command === Command.FIGHT && !target.turnData.acted && allMoves[turnCommand.move.move].category !== MoveCategory.STATUS);
}),
new AttackMove(Moves.MIGHTY_CLEAVE, PokemonType.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) new AttackMove(Moves.MIGHTY_CLEAVE, PokemonType.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9)
.slicingMove() .slicingMove()
.ignoresProtect(), .ignoresProtect(),

View File

@ -306,7 +306,7 @@ export const GlobalTradeSystemEncounter: MysteryEncounter = MysteryEncounterBuil
if (eggMoves) { if (eggMoves) {
// Cannot gen the rare egg move, only 1 of the first 3 common moves // Cannot gen the rare egg move, only 1 of the first 3 common moves
const eggMove = eggMoves[randSeedInt(3)]; const eggMove = eggMoves[randSeedInt(3)];
if (!tradePokemon.moveset.some(m => m?.moveId === eggMove)) { if (!tradePokemon.moveset.some(m => m.moveId === eggMove)) {
if (tradePokemon.moveset.length < 4) { if (tradePokemon.moveset.length < 4) {
tradePokemon.moveset.push(new PokemonMove(eggMove)); tradePokemon.moveset.push(new PokemonMove(eggMove));
} else { } else {

View File

@ -781,7 +781,7 @@ async function addEggMoveToNewPokemonMoveset(
let randomEggMoveIndex = eggMoveIndices.pop(); let randomEggMoveIndex = eggMoveIndices.pop();
let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; let randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
let retries = 0; let retries = 0;
while (retries < 3 && (!randomEggMove || newPokemon.moveset.some(m => m?.moveId === randomEggMove))) { while (retries < 3 && (!randomEggMove || newPokemon.moveset.some(m => m.moveId === randomEggMove))) {
// If Pokemon already knows this move, roll for another egg move // If Pokemon already knows this move, roll for another egg move
randomEggMoveIndex = eggMoveIndices.pop(); randomEggMoveIndex = eggMoveIndices.pop();
randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null; randomEggMove = !isNullOrUndefined(randomEggMoveIndex) ? eggMoves[randomEggMoveIndex] : null;
@ -789,7 +789,7 @@ async function addEggMoveToNewPokemonMoveset(
} }
if (randomEggMove) { if (randomEggMove) {
if (!newPokemon.moveset.some(m => m?.moveId === randomEggMove)) { if (!newPokemon.moveset.some(m => m.moveId === randomEggMove)) {
if (newPokemon.moveset.length < 4) { if (newPokemon.moveset.length < 4) {
newPokemon.moveset.push(new PokemonMove(randomEggMove)); newPokemon.moveset.push(new PokemonMove(randomEggMove));
} else { } else {
@ -820,16 +820,13 @@ async function addEggMoveToNewPokemonMoveset(
*/ */
function addFavoredMoveToNewPokemonMoveset( function addFavoredMoveToNewPokemonMoveset(
newPokemon: PlayerPokemon, newPokemon: PlayerPokemon,
newPokemonGeneratedMoveset: (PokemonMove | null)[], newPokemonGeneratedMoveset: PokemonMove[],
newEggMoveIndex: number | null, newEggMoveIndex: number | null,
) { ) {
let favoredMove: PokemonMove | null = null; let favoredMove: PokemonMove | null = null;
for (const move of newPokemonGeneratedMoveset) { for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced // Needs to match first type, second type will be replaced
if ( if (move?.getMove().type === newPokemon.getTypes()[0] && !newPokemon.moveset.some(m => m.moveId === move.moveId)) {
move?.getMove().type === newPokemon.getTypes()[0] &&
!newPokemon.moveset.some(m => m?.moveId === move?.moveId)
) {
favoredMove = move; favoredMove = move;
break; break;
} }
@ -839,7 +836,7 @@ function addFavoredMoveToNewPokemonMoveset(
if (!favoredMove) { if (!favoredMove) {
for (const move of newPokemonGeneratedMoveset) { for (const move of newPokemonGeneratedMoveset) {
// Needs to match first type, second type will be replaced // Needs to match first type, second type will be replaced
if (!newPokemon.moveset.some(m => m?.moveId === move?.moveId)) { if (!newPokemon.moveset.some(m => m.moveId === move.moveId)) {
favoredMove = move; favoredMove = move;
break; break;
} }

View File

@ -576,19 +576,19 @@ export class MoveRequirement extends EncounterPokemonRequirement {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) && (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
pokemon.moveset.some(move => move?.moveId && this.requiredMoves.includes(move.moveId)), pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
); );
} }
// for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves // for an inverted query, we only want to get the pokemon that don't have ANY of the listed moves
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
(!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) && (!this.excludeDisallowedPokemon || pokemon.isAllowedInBattle()) &&
!pokemon.moveset.some(move => move?.moveId && this.requiredMoves.includes(move.moveId)), !pokemon.moveset.some(move => move.moveId && this.requiredMoves.includes(move.moveId)),
); );
} }
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const includedMoves = pokemon?.moveset.filter(move => move?.moveId && this.requiredMoves.includes(move.moveId)); const includedMoves = pokemon?.moveset.filter(move => move.moveId && this.requiredMoves.includes(move.moveId));
if (includedMoves && includedMoves.length > 0 && includedMoves[0]) { if (includedMoves && includedMoves.length > 0 && includedMoves[0]) {
return ["move", includedMoves[0].getName()]; return ["move", includedMoves[0].getName()];
} }
@ -626,7 +626,7 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
this.requiredMoves.filter(learnableMove => this.requiredMoves.filter(learnableMove =>
pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(learnableMove), pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m.moveId === tm)).includes(learnableMove),
).length > 0, ).length > 0,
); );
} }
@ -634,14 +634,14 @@ export class CompatibleMoveRequirement extends EncounterPokemonRequirement {
return partyPokemon.filter( return partyPokemon.filter(
pokemon => pokemon =>
this.requiredMoves.filter(learnableMove => this.requiredMoves.filter(learnableMove =>
pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(learnableMove), pokemon.compatibleTms.filter(tm => !pokemon.moveset.find(m => m.moveId === tm)).includes(learnableMove),
).length === 0, ).length === 0,
); );
} }
override getDialogueToken(pokemon?: PlayerPokemon): [string, string] { override getDialogueToken(pokemon?: PlayerPokemon): [string, string] {
const includedCompatMoves = this.requiredMoves.filter(reqMove => const includedCompatMoves = this.requiredMoves.filter(reqMove =>
pokemon?.compatibleTms.filter(tm => !pokemon.moveset.find(m => m?.moveId === tm)).includes(reqMove), pokemon?.compatibleTms.filter(tm => !pokemon.moveset.find(m => m.moveId === tm)).includes(reqMove),
); );
if (includedCompatMoves.length > 0) { if (includedCompatMoves.length > 0) {
return ["compatibleMove", Moves[includedCompatMoves[0]]]; return ["compatibleMove", Moves[includedCompatMoves[0]]];

View File

@ -363,7 +363,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
} }
canChange(pokemon: Pokemon): boolean { canChange(pokemon: Pokemon): boolean {
return !!pokemon.moveset.filter(m => m?.moveId === this.move).length === this.known; return !!pokemon.moveset.filter(m => m.moveId === this.move).length === this.known;
} }
} }

View File

@ -296,7 +296,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public stats: number[]; public stats: number[];
public ivs: number[]; public ivs: number[];
public nature: Nature; public nature: Nature;
public moveset: (PokemonMove | null)[]; public moveset: PokemonMove[];
public status: Status | null; public status: Status | null;
public friendship: number; public friendship: number;
public metLevel: number; public metLevel: number;
@ -694,7 +694,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
loadAssets(ignoreOverride = true): Promise<void> { loadAssets(ignoreOverride = true): Promise<void> {
return new Promise(resolve => { return new Promise(resolve => {
const moveIds = this.getMoveset().map(m => m!.getMove().id); // TODO: is this bang correct? const moveIds = this.getMoveset().map(m => m.getMove().id);
Promise.allSettled(moveIds.map(m => initMoveAnim(m))).then(() => { Promise.allSettled(moveIds.map(m => initMoveAnim(m))).then(() => {
loadMoveAnimAssets(moveIds); loadMoveAnimAssets(moveIds);
this.getSpeciesForm().loadAssets( this.getSpeciesForm().loadAssets(
@ -1751,7 +1751,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
abstract isBoss(): boolean; abstract isBoss(): boolean;
getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] { getMoveset(ignoreOverride?: boolean): PokemonMove[] {
const ret = const ret =
!ignoreOverride && this.summonData?.moveset !ignoreOverride && this.summonData?.moveset
? this.summonData.moveset ? this.summonData.moveset
@ -1826,9 +1826,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
.filter(m => !levelMoves.includes(m)) .filter(m => !levelMoves.includes(m))
.concat(levelMoves); .concat(levelMoves);
} }
levelMoves = levelMoves.filter( levelMoves = levelMoves.filter(lm => !this.moveset.some(m => m.moveId === lm));
lm => !this.moveset.some(m => m?.moveId === lm),
);
return levelMoves; return levelMoves;
} }
@ -2942,7 +2940,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
setMove(moveIndex: number, moveId: Moves): void { setMove(moveIndex: number, moveId: Moves): void {
const move = moveId ? new PokemonMove(moveId) : null; if (moveId === Moves.NONE) {
return;
}
const move = new PokemonMove(moveId);
this.moveset[moveIndex] = move; this.moveset[moveIndex] = move;
if (this.summonData?.moveset) { if (this.summonData?.moveset) {
this.summonData.moveset[moveIndex] = move; this.summonData.moveset[moveIndex] = move;
@ -3487,14 +3488,14 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights get 20x if STAB. // Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights get 20x if STAB.
// Status moves remain unchanged on weight, this encourages 1-2 // Status moves remain unchanged on weight, this encourages 1-2
movePool = baseWeights movePool = baseWeights
.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)) .filter(m => !this.moveset.some(mo => m[0] === mo.moveId))
.map(m => { .map(m => {
let ret: number; let ret: number;
if ( if (
this.moveset.some( this.moveset.some(
mo => mo =>
mo?.getMove().category !== MoveCategory.STATUS && mo.getMove().category !== MoveCategory.STATUS &&
mo?.getMove().type === allMoves[m[0]].type, mo.getMove().type === allMoves[m[0]].type,
) )
) { ) {
ret = Math.ceil(Math.sqrt(m[1])); ret = Math.ceil(Math.sqrt(m[1]));
@ -3504,7 +3505,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
Math.max( Math.max(
Math.pow( Math.pow(
4, 4,
this.moveset.filter(mo => (mo?.getMove().power ?? 0) > 1) this.moveset.filter(mo => (mo.getMove().power ?? 0) > 1)
.length, .length,
) / 8, ) / 8,
0.5, 0.5,
@ -3518,9 +3519,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}); });
} else { } else {
// Non-trainer pokemon just use normal weights // Non-trainer pokemon just use normal weights
movePool = baseWeights.filter( movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId));
m => !this.moveset.some(mo => m[0] === mo?.moveId),
);
} }
const totalWeight = movePool.reduce((v, m) => v + m[1], 0); const totalWeight = movePool.reduce((v, m) => v + m[1], 0);
let rand = Utils.randSeedInt(totalWeight); let rand = Utils.randSeedInt(totalWeight);
@ -6852,18 +6851,7 @@ export class PlayerPokemon extends Pokemon {
copyMoveset(): PokemonMove[] { copyMoveset(): PokemonMove[] {
const newMoveset: PokemonMove[] = []; const newMoveset: PokemonMove[] = [];
this.moveset.forEach(move => { this.moveset.forEach(move => {
// TODO: refactor `moveset` to not accept `null`s newMoveset.push(new PokemonMove(move.moveId, 0, move.ppUp, move.virtual, move.maxPpOverride));
if (move) {
newMoveset.push(
new PokemonMove(
move.moveId,
0,
move.ppUp,
move.virtual,
move.maxPpOverride,
),
);
}
}); });
return newMoveset; return newMoveset;
@ -7050,17 +7038,8 @@ export class EnemyPokemon extends Pokemon {
if (moveQueue.length !== 0) { if (moveQueue.length !== 0) {
const queuedMove = moveQueue[0]; const queuedMove = moveQueue[0];
if (queuedMove) { if (queuedMove) {
const moveIndex = this.getMoveset().findIndex( const moveIndex = this.getMoveset().findIndex(m => m.moveId === queuedMove.move);
m => m?.moveId === queuedMove.move, if ((moveIndex > -1 && this.getMoveset()[moveIndex].isUsable(this, queuedMove.ignorePP)) || queuedMove.virtual) {
);
if (
(moveIndex > -1 &&
this.getMoveset()[moveIndex]!.isUsable(
this,
queuedMove.ignorePP,
)) ||
queuedMove.virtual
) {
return queuedMove; return queuedMove;
} else { } else {
this.getMoveQueue().shift(); this.getMoveQueue().shift();
@ -7070,20 +7049,17 @@ export class EnemyPokemon extends Pokemon {
} }
// Filter out any moves this Pokemon cannot use // Filter out any moves this Pokemon cannot use
let movePool = this.getMoveset().filter(m => m?.isUsable(this)); let movePool = this.getMoveset().filter(m => m.isUsable(this));
// If no moves are left, use Struggle. Otherwise, continue with move selection // If no moves are left, use Struggle. Otherwise, continue with move selection
if (movePool.length) { if (movePool.length) {
// If there's only 1 move in the move pool, use it. // If there's only 1 move in the move pool, use it.
if (movePool.length === 1) { if (movePool.length === 1) {
return { return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) };
move: movePool[0]!.moveId,
targets: this.getNextTargets(movePool[0]!.moveId),
}; // TODO: are the bangs correct?
} }
// If a move is forced because of Encore, use it. // If a move is forced because of Encore, use it.
const encoreTag = this.getTag(EncoreTag) as EncoreTag; const encoreTag = this.getTag(EncoreTag) as EncoreTag;
if (encoreTag) { if (encoreTag) {
const encoreMove = movePool.find(m => m?.moveId === encoreTag.moveId); const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId);
if (encoreMove) { if (encoreMove) {
return { return {
move: encoreMove.moveId, move: encoreMove.moveId,
@ -7093,8 +7069,7 @@ export class EnemyPokemon extends Pokemon {
} }
switch (this.aiType) { switch (this.aiType) {
case AiType.RANDOM: // No enemy should spawn with this AI type in-game case AiType.RANDOM: // No enemy should spawn with this AI type in-game
const moveId = const moveId = movePool[globalScene.randBattleSeedInt(movePool.length)].moveId;
movePool[globalScene.randBattleSeedInt(movePool.length)]!.moveId; // TODO: is the bang correct?
return { move: moveId, targets: this.getNextTargets(moveId) }; return { move: moveId, targets: this.getNextTargets(moveId) };
case AiType.SMART_RANDOM: case AiType.SMART_RANDOM:
case AiType.SMART: case AiType.SMART:
@ -7156,11 +7131,9 @@ export class EnemyPokemon extends Pokemon {
* For more information on how benefit scores are calculated, see `docs/enemy-ai.md`. * For more information on how benefit scores are calculated, see `docs/enemy-ai.md`.
*/ */
const moveScores = movePool.map(() => 0); const moveScores = movePool.map(() => 0);
const moveTargets = Object.fromEntries( const moveTargets = Object.fromEntries(movePool.map(m => [ m.moveId, this.getNextTargets(m.moveId) ]));
movePool.map(m => [m!.moveId, this.getNextTargets(m!.moveId)]),
); // TODO: are those bangs correct?
for (const m in movePool) { for (const m in movePool) {
const pokemonMove = movePool[m]!; // TODO: is the bang correct? const pokemonMove = movePool[m];
const move = pokemonMove.getMove(); const move = pokemonMove.getMove();
let moveScore = moveScores[m]; let moveScore = moveScores[m];
@ -7269,16 +7242,8 @@ export class EnemyPokemon extends Pokemon {
r++; r++;
} }
} }
console.log( console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName()));
movePool.map(m => m!.getName()), return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] };
moveScores,
r,
sortedMovePool.map(m => m!.getName()),
); // TODO: are those bangs correct?
return {
move: sortedMovePool[r]!.moveId,
targets: moveTargets[sortedMovePool[r]!.moveId],
};
} }
} }
@ -7637,7 +7602,7 @@ export class PokemonSummonData {
public gender: Gender; public gender: Gender;
public fusionGender: Gender; public fusionGender: Gender;
public stats: number[] = [0, 0, 0, 0, 0, 0]; public stats: number[] = [0, 0, 0, 0, 0, 0];
public moveset: (PokemonMove | null)[]; public moveset: PokemonMove[];
// If not initialized this value will not be populated from save data. // If not initialized this value will not be populated from save data.
public types: PokemonType[] = []; public types: PokemonType[] = [];
public addedType: PokemonType | null = null; public addedType: PokemonType | null = null;

View File

@ -627,7 +627,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType {
iconImage, iconImage,
(_type, args) => new PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints), (_type, args) => new PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints),
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if (!pokemon.getMoveset().filter(m => m?.ppUsed).length) { if (!pokemon.getMoveset().filter(m => m.ppUsed).length) {
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
} }
return null; return null;
@ -1170,7 +1170,7 @@ export class TmModifierType extends PokemonModifierType {
(pokemon: PlayerPokemon) => { (pokemon: PlayerPokemon) => {
if ( if (
pokemon.compatibleTms.indexOf(moveId) === -1 || pokemon.compatibleTms.indexOf(moveId) === -1 ||
pokemon.getMoveset().filter(m => m?.moveId === moveId).length pokemon.getMoveset().filter(m => m.moveId === moveId).length
) { ) {
return PartyUiHandler.NoEffectMessage; return PartyUiHandler.NoEffectMessage;
} }
@ -1333,7 +1333,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
const attackMoveTypes = party.flatMap(p => const attackMoveTypes = party.flatMap(p =>
p p
.getMoveset() .getMoveset()
.map(m => m?.getMove()) .map(m => m.getMove())
.filter(m => m instanceof AttackMove) .filter(m => m instanceof AttackMove)
.map(m => m.type), .map(m => m.type),
); );
@ -1468,7 +1468,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator {
const speciesId = p.getSpeciesForm(true).speciesId; const speciesId = p.getSpeciesForm(true).speciesId;
const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null; const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null;
// TODO: Use commented boolean when Fling is implemented // TODO: Use commented boolean when Fling is implemented
const hasFling = false; /* p.getMoveset(true).some(m => m?.moveId === Moves.FLING) */ const hasFling = false; /* p.getMoveset(true).some(m => m.moveId === Moves.FLING) */
for (const i in values) { for (const i in values) {
const checkedSpecies = values[i].species; const checkedSpecies = values[i].species;
@ -1529,7 +1529,7 @@ class TmModifierTypeGenerator extends ModifierTypeGenerator {
const partyMemberCompatibleTms = party.map(p => { const partyMemberCompatibleTms = party.map(p => {
const previousLevelMoves = p.getLearnableLevelMoves(); const previousLevelMoves = p.getLearnableLevelMoves();
return (p as PlayerPokemon).compatibleTms.filter( return (p as PlayerPokemon).compatibleTms.filter(
tm => !p.moveset.find(m => m?.moveId === tm) && !previousLevelMoves.find(lm => lm === tm), tm => !p.moveset.find(m => m.moveId === tm) && !previousLevelMoves.find(lm => lm === tm),
); );
}); });
const tierUniqueCompatibleTms = partyMemberCompatibleTms const tierUniqueCompatibleTms = partyMemberCompatibleTms
@ -2433,7 +2433,7 @@ const modifierPool: ModifierPool = {
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
p p
.getMoveset() .getMoveset()
.filter(m => m?.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2)) .filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
.length, .length,
).length, ).length,
3, 3,
@ -2452,7 +2452,7 @@ const modifierPool: ModifierPool = {
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
p p
.getMoveset() .getMoveset()
.filter(m => m?.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2)) .filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
.length, .length,
).length, ).length,
3, 3,
@ -2574,7 +2574,7 @@ const modifierPool: ModifierPool = {
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
p p
.getMoveset() .getMoveset()
.filter(m => m?.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2)) .filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
.length, .length,
).length, ).length,
3, 3,
@ -2593,7 +2593,7 @@ const modifierPool: ModifierPool = {
!p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) && !p.getHeldItems().some(m => m instanceof BerryModifier && m.berryType === BerryType.LEPPA) &&
p p
.getMoveset() .getMoveset()
.filter(m => m?.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2)) .filter(m => m.ppUsed && m.getMovePp() - m.ppUsed <= 5 && m.ppUsed > Math.floor(m.getMovePp() / 2))
.length, .length,
).length, ).length,
3, 3,

View File

@ -104,15 +104,14 @@ export class CommandPhase extends FieldPhase {
moveQueue[0] && moveQueue[0] &&
moveQueue[0].move && moveQueue[0].move &&
!moveQueue[0].virtual && !moveQueue[0].virtual &&
(!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) || (!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) ||
!playerPokemon !playerPokemon
.getMoveset() .getMoveset()
[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable( [playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(
playerPokemon, playerPokemon,
moveQueue[0].ignorePP, moveQueue[0].ignorePP,
)) ))
) { ) {
// TODO: is the bang correct?
moveQueue.shift(); moveQueue.shift();
} }
@ -121,12 +120,11 @@ export class CommandPhase extends FieldPhase {
if (!queuedMove.move) { if (!queuedMove.move) {
this.handleCommand(Command.FIGHT, -1); this.handleCommand(Command.FIGHT, -1);
} else { } else {
const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move); const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move);
if ( if (
(moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) || (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, queuedMove.ignorePP)) ||
queuedMove.virtual queuedMove.virtual
) { ) {
// TODO: is the bang correct?
this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, queuedMove); this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, queuedMove);
} else { } else {
globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex); globalScene.ui.setMode(Mode.COMMAND, this.fieldIndex);
@ -157,7 +155,7 @@ export class CommandPhase extends FieldPhase {
if ( if (
cursor === -1 || cursor === -1 ||
playerPokemon.trySelectMove(cursor, args[0] as boolean) || playerPokemon.trySelectMove(cursor, args[0] as boolean) ||
(useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length) (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)
) { ) {
let moveId: Moves; let moveId: Moves;
if (useStruggle) { if (useStruggle) {
@ -165,7 +163,7 @@ export class CommandPhase extends FieldPhase {
} else if (turnMove !== undefined) { } else if (turnMove !== undefined) {
moveId = turnMove.move; moveId = turnMove.move;
} else if (cursor > -1) { } else if (cursor > -1) {
moveId = playerPokemon.getMoveset()[cursor]!.moveId; moveId = playerPokemon.getMoveset()[cursor].moveId;
} else { } else {
moveId = Moves.NONE; moveId = Moves.NONE;
} }
@ -195,10 +193,14 @@ export class CommandPhase extends FieldPhase {
if (moveTargets.targets.length > 1 && moveTargets.multiple) { if (moveTargets.targets.length > 1 && moveTargets.multiple) {
globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
} }
if (moveTargets.targets.length <= 1 || moveTargets.multiple) { if (turnCommand.move && (moveTargets.targets.length <= 1 || moveTargets.multiple)) {
turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? turnCommand.move.targets = moveTargets.targets;
} else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { } else if (
turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? turnCommand.move &&
playerPokemon.getTag(BattlerTagType.CHARGING) &&
playerPokemon.getMoveQueue().length >= 1
) {
turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets;
} else { } else {
globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex)); globalScene.unshiftPhase(new SelectTargetPhase(this.fieldIndex));
} }
@ -206,7 +208,7 @@ export class CommandPhase extends FieldPhase {
globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; globalScene.currentBattle.turnCommands[this.fieldIndex] = turnCommand;
success = true; success = true;
} else if (cursor < playerPokemon.getMoveset().length) { } else if (cursor < playerPokemon.getMoveset().length) {
const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? const move = playerPokemon.getMoveset()[cursor];
globalScene.ui.setMode(Mode.MESSAGE); globalScene.ui.setMode(Mode.MESSAGE);
// Decides between a Disabled, Not Implemented, or No PP translation message // Decides between a Disabled, Not Implemented, or No PP translation message

View File

@ -49,7 +49,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase {
const currentMoveset = pokemon.getMoveset(); const currentMoveset = pokemon.getMoveset();
// The game first checks if the Pokemon already has the move and ends the phase if it does. // The game first checks if the Pokemon already has the move and ends the phase if it does.
const hasMoveAlready = currentMoveset.some(m => m?.moveId === move.id) && this.moveId !== Moves.SKETCH; const hasMoveAlready = currentMoveset.some(m => m.moveId === move.id) && this.moveId !== Moves.SKETCH;
if (hasMoveAlready) { if (hasMoveAlready) {
return this.end(); return this.end();
} }

View File

@ -23,7 +23,7 @@ export class PartyHealPhase extends BattlePhase {
pokemon.hp = pokemon.getMaxHp(); pokemon.hp = pokemon.getMaxHp();
pokemon.resetStatus(); pokemon.resetStatus();
for (const move of pokemon.moveset) { for (const move of pokemon.moveset) {
move!.ppUsed = 0; // TODO: is this bang correct? move.ppUsed = 0;
} }
pokemon.updateInfo(true); pokemon.updateInfo(true);
} }

View File

@ -178,7 +178,7 @@ export class TurnStartPhase extends FieldPhase {
continue; continue;
} }
const move = const move =
pokemon.getMoveset().find(m => m?.moveId === queuedMove.move && m?.ppUsed < m?.getMovePp()) || pokemon.getMoveset().find(m => m.moveId === queuedMove.move && m.ppUsed < m.getMovePp()) ||
new PokemonMove(queuedMove.move); new PokemonMove(queuedMove.move);
if (move.getMove().hasAttr(MoveHeaderAttr)) { if (move.getMove().hasAttr(MoveHeaderAttr)) {
globalScene.unshiftPhase(new MoveHeaderPhase(pokemon, move)); globalScene.unshiftPhase(new MoveHeaderPhase(pokemon, move));

View File

@ -35,7 +35,7 @@ export default class PokemonData {
public stats: number[]; public stats: number[];
public ivs: number[]; public ivs: number[];
public nature: Nature; public nature: Nature;
public moveset: (PokemonMove | null)[]; public moveset: PokemonMove[];
public status: Status | null; public status: Status | null;
public friendship: number; public friendship: number;
public metLevel: number; public metLevel: number;

View File

@ -243,7 +243,7 @@ export default class FightUiHandler extends UiHandler implements InfoToggle {
const hasMove = cursor < moveset.length; const hasMove = cursor < moveset.length;
if (hasMove) { if (hasMove) {
const pokemonMove = moveset[cursor]!; // TODO: is the bang correct? const pokemonMove = moveset[cursor];
const moveType = pokemon.getMoveType(pokemonMove.getMove()); const moveType = pokemon.getMoveType(pokemonMove.getMove());
const textureKey = Utils.getLocalizedSpriteKey("types"); const textureKey = Utils.getLocalizedSpriteKey("types");
this.typeIcon.setTexture(textureKey, PokemonType[moveType].toLowerCase()).setScale(0.8); this.typeIcon.setTexture(textureKey, PokemonType[moveType].toLowerCase()).setScale(0.8);

View File

@ -478,7 +478,7 @@ export default class PartyUiHandler extends MessageUiHandler {
filterResult = this.FilterChallengeLegal(pokemon); filterResult = this.FilterChallengeLegal(pokemon);
} }
if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) {
filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]!); // TODO: is this bang correct? filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]);
} }
} else { } else {
filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)( filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)(
@ -1182,7 +1182,7 @@ export default class PartyUiHandler extends MessageUiHandler {
case PartyOption.MOVE_2: case PartyOption.MOVE_2:
case PartyOption.MOVE_3: case PartyOption.MOVE_3:
case PartyOption.MOVE_4: case PartyOption.MOVE_4:
const move = pokemon.moveset[option - PartyOption.MOVE_1]!; // TODO: is the bang correct? const move = pokemon.moveset[option - PartyOption.MOVE_1];
if (this.showMovePp) { if (this.showMovePp) {
const maxPP = move.getMovePp(); const maxPP = move.getMovePp();
const currPP = maxPP - move.ppUsed; const currPP = maxPP - move.ppUsed;
@ -1647,7 +1647,7 @@ class PartySlot extends Phaser.GameObjects.Container {
this.slotHpText.setVisible(false); this.slotHpText.setVisible(false);
let slotTmText: string; let slotTmText: string;
if (this.pokemon.getMoveset().filter(m => m?.moveId === tmMoveId).length > 0) { if (this.pokemon.getMoveset().filter(m => m.moveId === tmMoveId).length > 0) {
slotTmText = i18next.t("partyUiHandler:learned"); slotTmText = i18next.t("partyUiHandler:learned");
} else if (this.pokemon.compatibleTms.indexOf(tmMoveId) === -1) { } else if (this.pokemon.compatibleTms.indexOf(tmMoveId) === -1) {
slotTmText = i18next.t("partyUiHandler:notAble"); slotTmText = i18next.t("partyUiHandler:notAble");

View File

@ -1208,7 +1208,7 @@ export default class SummaryUiHandler extends UiHandler {
} }
if (this.moveCursor < 4 && this.pokemon && this.moveCursor < this.pokemon.moveset.length) { if (this.moveCursor < 4 && this.pokemon && this.moveCursor < this.pokemon.moveset.length) {
return this.pokemon.moveset[this.moveCursor]!.getMove(); // TODO: is this bang correct? return this.pokemon.moveset[this.moveCursor].getMove();
} }
if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.moveCursor === 4) { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.moveCursor === 4) {
return this.newMove; return this.newMove;

View File

@ -24,7 +24,7 @@ describe("Abilities - Lightningrod", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset([ Moves.SPLASH, Moves.SHOCK_WAVE ]) .moveset([Moves.SPLASH, Moves.SHOCK_WAVE])
.ability(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH)
.battleType("double") .battleType("double")
.disableCrits() .disableCrits()
@ -34,7 +34,7 @@ describe("Abilities - Lightningrod", () => {
}); });
it("should redirect electric type moves", async () => { it("should redirect electric type moves", async () => {
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -49,8 +49,8 @@ describe("Abilities - Lightningrod", () => {
}); });
it("should not redirect non-electric type moves", async () => { it("should not redirect non-electric type moves", async () => {
game.override.moveset([ Moves.SPLASH, Moves.AERIAL_ACE ]); game.override.moveset([Moves.SPLASH, Moves.AERIAL_ACE]);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -65,7 +65,7 @@ describe("Abilities - Lightningrod", () => {
}); });
it("should boost the user's spatk without damaging", async () => { it("should boost the user's spatk without damaging", async () => {
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -81,7 +81,7 @@ describe("Abilities - Lightningrod", () => {
it("should not redirect moves changed from electric type via ability", async () => { it("should not redirect moves changed from electric type via ability", async () => {
game.override.ability(Abilities.NORMALIZE); game.override.ability(Abilities.NORMALIZE);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -96,9 +96,8 @@ describe("Abilities - Lightningrod", () => {
}); });
it("should redirect moves changed to electric type via ability", async () => { it("should redirect moves changed to electric type via ability", async () => {
game.override.ability(Abilities.GALVANIZE) game.override.ability(Abilities.GALVANIZE).moveset(Moves.TACKLE);
.moveset(Moves.TACKLE); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];

View File

@ -176,11 +176,8 @@ describe("Abilities - Neutralizing Gas", () => {
}); });
it("should not activate abilities of pokemon no longer on the field", async () => { it("should not activate abilities of pokemon no longer on the field", async () => {
game.override game.override.battleType("single").ability(Abilities.NEUTRALIZING_GAS).enemyAbility(Abilities.DELTA_STREAM);
.battleType("single") await game.classicMode.startBattle([Species.MAGIKARP]);
.ability(Abilities.NEUTRALIZING_GAS)
.enemyAbility(Abilities.DELTA_STREAM);
await game.classicMode.startBattle([ Species.MAGIKARP ]);
const enemy = game.scene.getEnemyPokemon()!; const enemy = game.scene.getEnemyPokemon()!;
const weatherChangeAttr = enemy.getAbilityAttrs(PostSummonWeatherChangeAbAttr, false)[0]; const weatherChangeAttr = enemy.getAbilityAttrs(PostSummonWeatherChangeAbAttr, false)[0];

View File

@ -24,7 +24,7 @@ describe("Abilities - Storm Drain", () => {
beforeEach(() => { beforeEach(() => {
game = new GameManager(phaserGame); game = new GameManager(phaserGame);
game.override game.override
.moveset([ Moves.SPLASH, Moves.WATER_GUN ]) .moveset([Moves.SPLASH, Moves.WATER_GUN])
.ability(Abilities.BALL_FETCH) .ability(Abilities.BALL_FETCH)
.battleType("double") .battleType("double")
.disableCrits() .disableCrits()
@ -34,7 +34,7 @@ describe("Abilities - Storm Drain", () => {
}); });
it("should redirect water type moves", async () => { it("should redirect water type moves", async () => {
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -49,8 +49,8 @@ describe("Abilities - Storm Drain", () => {
}); });
it("should not redirect non-water type moves", async () => { it("should not redirect non-water type moves", async () => {
game.override.moveset([ Moves.SPLASH, Moves.AERIAL_ACE ]); game.override.moveset([Moves.SPLASH, Moves.AERIAL_ACE]);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -65,7 +65,7 @@ describe("Abilities - Storm Drain", () => {
}); });
it("should boost the user's spatk without damaging", async () => { it("should boost the user's spatk without damaging", async () => {
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -81,7 +81,7 @@ describe("Abilities - Storm Drain", () => {
it("should not redirect moves changed from water type via ability", async () => { it("should not redirect moves changed from water type via ability", async () => {
game.override.ability(Abilities.NORMALIZE); game.override.ability(Abilities.NORMALIZE);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];
@ -96,9 +96,8 @@ describe("Abilities - Storm Drain", () => {
}); });
it("should redirect moves changed to water type via ability", async () => { it("should redirect moves changed to water type via ability", async () => {
game.override.ability(Abilities.LIQUID_VOICE) game.override.ability(Abilities.LIQUID_VOICE).moveset(Moves.PSYCHIC_NOISE);
.moveset(Moves.PSYCHIC_NOISE); await game.classicMode.startBattle([Species.FEEBAS, Species.MAGIKARP]);
await game.classicMode.startBattle([ Species.FEEBAS, Species.MAGIKARP ]);
const enemy1 = game.scene.getEnemyField()[0]; const enemy1 = game.scene.getEnemyField()[0];
const enemy2 = game.scene.getEnemyField()[1]; const enemy2 = game.scene.getEnemyField()[1];

View File

@ -41,7 +41,7 @@ describe("Moves - Grudge", () => {
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
const playerMove = playerPokemon?.getMoveset().find(m => m?.moveId === Moves.EMBER); const playerMove = playerPokemon?.getMoveset().find(m => m.moveId === Moves.EMBER);
expect(playerMove?.getPpRatio()).toBe(0); expect(playerMove?.getPpRatio()).toBe(0);
}); });
@ -60,7 +60,7 @@ describe("Moves - Grudge", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("BerryPhase"); await game.phaseInterceptor.to("BerryPhase");
const playerMove = playerPokemon?.getMoveset().find(m => m?.moveId === Moves.EMBER); const playerMove = playerPokemon?.getMoveset().find(m => m.moveId === Moves.EMBER);
expect(playerMove?.getPpRatio()).toBe(0); expect(playerMove?.getPpRatio()).toBe(0);
}); });
@ -84,7 +84,7 @@ describe("Moves - Grudge", () => {
expect(enemyPokemon?.isFainted()).toBe(true); expect(enemyPokemon?.isFainted()).toBe(true);
const playerMove = playerPokemon?.getMoveset().find(m => m?.moveId === Moves.FALSE_SWIPE); const playerMove = playerPokemon?.getMoveset().find(m => m.moveId === Moves.FALSE_SWIPE);
expect(playerMove?.getPpRatio()).toBeGreaterThan(0); expect(playerMove?.getPpRatio()).toBeGreaterThan(0);
}); });
}); });

View File

@ -136,7 +136,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(scene.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name);
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(Species.GREEDENT); expect(enemyField[0].species.speciesId).toBe(Species.GREEDENT);
const moveset = enemyField[0].moveset.map(m => m?.moveId); const moveset = enemyField[0].moveset.map(m => m.moveId);
expect(moveset?.length).toBe(4); expect(moveset?.length).toBe(4);
expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH]); expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.CRUNCH]);
@ -259,7 +259,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
expect(partyCountBefore + 1).toBe(partyCountAfter); expect(partyCountBefore + 1).toBe(partyCountAfter);
const greedent = scene.getPlayerParty()[scene.getPlayerParty().length - 1]; const greedent = scene.getPlayerParty()[scene.getPlayerParty().length - 1];
expect(greedent.species.speciesId).toBe(Species.GREEDENT); expect(greedent.species.speciesId).toBe(Species.GREEDENT);
const moveset = greedent.moveset.map(m => m?.moveId); const moveset = greedent.moveset.map(m => m.moveId);
expect(moveset?.length).toBe(4); expect(moveset?.length).toBe(4);
expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]); expect(moveset).toEqual([Moves.THRASH, Moves.BODY_PRESS, Moves.STUFF_CHEEKS, Moves.SLACK_OFF]);
}); });

View File

@ -112,7 +112,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(Species.ORICORIO); expect(enemyField[0].species.speciesId).toBe(Species.ORICORIO);
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]); expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]);
const moveset = enemyField[0].moveset.map(m => m?.moveId); const moveset = enemyField[0].moveset.map(m => m.moveId);
expect(moveset.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy(); expect(moveset.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy();
const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]); const movePhases = phaseSpy.mock.calls.filter(p => p[0] instanceof MovePhase).map(p => p[0]);
@ -208,7 +208,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
expect(partyCountBefore + 1).toBe(partyCountAfter); expect(partyCountBefore + 1).toBe(partyCountAfter);
const oricorio = scene.getPlayerParty()[scene.getPlayerParty().length - 1]; const oricorio = scene.getPlayerParty()[scene.getPlayerParty().length - 1];
expect(oricorio.species.speciesId).toBe(Species.ORICORIO); expect(oricorio.species.speciesId).toBe(Species.ORICORIO);
const moveset = oricorio.moveset.map(m => m?.moveId); const moveset = oricorio.moveset.map(m => m.moveId);
expect(moveset?.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy(); expect(moveset?.some(m => m === Moves.REVELATION_DANCE)).toBeTruthy();
expect(moveset?.some(m => m === Moves.DRAGON_DANCE)).toBeTruthy(); expect(moveset?.some(m => m === Moves.DRAGON_DANCE)).toBeTruthy();
}); });

View File

@ -292,7 +292,7 @@ export default class GameManager {
const move = (this.scene.getCurrentPhase() as SelectTargetPhase) const move = (this.scene.getCurrentPhase() as SelectTargetPhase)
.getPokemon() .getPokemon()
.getMoveset() .getMoveset()
[movePosition]!.getMove(); // TODO: is the bang correct? [movePosition].getMove();
if (!move.isMultiTarget()) { if (!move.isMultiTarget()) {
handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY);
} }

View File

@ -99,7 +99,7 @@ export function waitUntil(truth): Promise<unknown> {
export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves): number { export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves): number {
const playerPokemon = scene.getPlayerField()[pokemonIndex]; const playerPokemon = scene.getPlayerField()[pokemonIndex];
const moveSet = playerPokemon.getMoveset(); const moveSet = playerPokemon.getMoveset();
const index = moveSet.findIndex(m => m?.moveId === move && m?.ppUsed < m?.getMovePp()); const index = moveSet.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp());
console.log(`Move position for ${Moves[move]} (=${move}):`, index); console.log(`Move position for ${Moves[move]} (=${move}):`, index);
return index; return index;
} }