refactor: apply Modifiers type inference (pattern)

Mirror from #1747

Co-authored-by: Dmitriy <kagno.dmitriy@gmail.com>
This commit is contained in:
flx-sta 2024-09-17 08:48:05 -07:00
parent 2221afca81
commit 919d2352f7
3 changed files with 800 additions and 381 deletions

View File

@ -2415,7 +2415,10 @@ export default class BattleScene extends SceneBase {
} }
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) { if ((modifier as PersistentModifier).add(this.modifiers, !!virtual, this)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
success = modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
success = modifier.apply(pokemon, true);
}
} }
if (playSound && !this.sound.get(soundName)) { if (playSound && !this.sound.get(soundName)) {
this.playSound(soundName); this.playSound(soundName);
@ -2442,7 +2445,7 @@ export default class BattleScene extends SceneBase {
for (const p in this.party) { for (const p in this.party) {
const pokemon = this.party[p]; const pokemon = this.party[p];
const args: any[] = [ pokemon ]; const args: unknown[] = [];
if (modifier instanceof PokemonHpRestoreModifier) { if (modifier instanceof PokemonHpRestoreModifier) {
if (!(modifier as PokemonHpRestoreModifier).fainted) { if (!(modifier as PokemonHpRestoreModifier).fainted) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1); const hpRestoreMultiplier = new Utils.IntegerHolder(1);
@ -2455,8 +2458,8 @@ export default class BattleScene extends SceneBase {
args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon); args.push(this.getPokemonById(modifier.fusePokemonId) as PlayerPokemon);
} }
if (modifier.shouldApply(args)) { if (modifier.shouldApply(pokemon, ...args)) {
const result = modifier.apply(args); const result = modifier.apply(pokemon, ...args);
if (result instanceof Promise) { if (result instanceof Promise) {
modifierPromises.push(result.then(s => success ||= s)); modifierPromises.push(result.then(s => success ||= s));
} else { } else {
@ -2468,8 +2471,8 @@ export default class BattleScene extends SceneBase {
return Promise.allSettled([this.party.map(p => p.updateInfo(instant)), ...modifierPromises]).then(() => resolve(success)); return Promise.allSettled([this.party.map(p => p.updateInfo(instant)), ...modifierPromises]).then(() => resolve(success));
} else { } else {
const args = [ this ]; const args = [ this ];
if (modifier.shouldApply(args)) { if (modifier.shouldApply(...args)) {
const result = modifier.apply(args); const result = modifier.apply(...args);
if (result instanceof Promise) { if (result instanceof Promise) {
return result.then(success => resolve(success)); return result.then(success => resolve(success));
} else { } else {
@ -2491,7 +2494,10 @@ export default class BattleScene extends SceneBase {
} }
if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) { if ((modifier as PersistentModifier).add(this.enemyModifiers, false, this)) {
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
modifier.apply([ this.getPokemonById(modifier.pokemonId), true ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, true);
}
} }
for (const rm of modifiersToRemove) { for (const rm of modifiersToRemove) {
this.removeModifier(rm, true); this.removeModifier(rm, true);
@ -2727,7 +2733,10 @@ export default class BattleScene extends SceneBase {
if (modifierIndex > -1) { if (modifierIndex > -1) {
modifiers.splice(modifierIndex, 1); modifiers.splice(modifierIndex, 1);
if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) { if (modifier instanceof PokemonFormChangeItemModifier || modifier instanceof TerastallizeModifier) {
modifier.apply([ this.getPokemonById(modifier.pokemonId), false ]); const pokemon = this.getPokemonById(modifier.pokemonId);
if (pokemon) {
modifier.apply(pokemon, false);
}
} }
return true; return true;
} }
@ -2745,16 +2754,36 @@ export default class BattleScene extends SceneBase {
return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType); return (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType);
} }
/**
* Get all of the modifiers that pass the `modifierFilter` function
* @param modifierFilter The function used to filter a target's modifiers
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @returns the list of all modifiers that passed the `modifierFilter` function
*/
findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] { findModifiers(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier[] {
return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m)); return (player ? this.modifiers : this.enemyModifiers).filter(modifierFilter);
} }
/**
* Find the first modifier that pass the `modifierFilter` function
* @param modifierFilter The function used to filter a target's modifiers
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @returns the first modifier that passed the `modifierFilter` function; `undefined` if none passed
*/
findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined { findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined {
return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m)); return (player ? this.modifiers : this.enemyModifiers).find(modifierFilter);
} }
applyShuffledModifiers(scene: BattleScene, modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] { /**
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply all modifiers that match `modifierType` in a random order
* @param scene {@linkcode BattleScene} used to randomize the order of modifiers
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyShuffledModifiers<T extends PersistentModifier>(scene: BattleScene, modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
let modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
scene.executeWithSeedOffset(() => { scene.executeWithSeedOffset(() => {
const shuffleModifiers = mods => { const shuffleModifiers = mods => {
if (mods.length < 1) { if (mods.length < 1) {
@ -2768,15 +2797,23 @@ export default class BattleScene extends SceneBase {
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
applyModifiers(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier[] { /**
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply all modifiers that match `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the list of all modifiers that matched `modifierType` and were applied.
*/
applyModifiers<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T[] {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
return this.applyModifiersInternal(modifiers, player, args); return this.applyModifiersInternal(modifiers, player, args);
} }
applyModifiersInternal(modifiers: PersistentModifier[], player: boolean, args: any[]): PersistentModifier[] { /** Helper function to apply all passed modifiers */
const appliedModifiers: PersistentModifier[] = []; applyModifiersInternal<T extends PersistentModifier>(modifiers: T[], player: boolean, args: Parameters<T["apply"]>): T[] {
const appliedModifiers: T[] = [];
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : ""); console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
appliedModifiers.push(modifier); appliedModifiers.push(modifier);
} }
@ -2785,10 +2822,17 @@ export default class BattleScene extends SceneBase {
return appliedModifiers; return appliedModifiers;
} }
applyModifier(modifierType: Constructor<Modifier>, player: boolean = true, ...args: any[]): PersistentModifier | null { /**
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); * Apply the first modifier that matches `modifierType`
* @param modifierType The type of modifier to apply; must extend {@linkcode PersistentModifier}
* @param player Whether to search the player (`true`) or the enemy (`false`); Defaults to `true`
* @param ...args The list of arguments needed to invoke `modifierType.apply`
* @returns the first modifier that matches `modifierType` and was applied; return `null` if none matched
*/
applyModifier<T extends PersistentModifier>(modifierType: Constructor<T>, player: boolean = true, ...args: Parameters<T["apply"]>): T | null {
const modifiers = (player ? this.modifiers : this.enemyModifiers).filter((m): m is T => m instanceof modifierType && m.shouldApply(...args));
for (const modifier of modifiers) { for (const modifier of modifiers) {
if (modifier.apply(args)) { if (modifier.apply(...args)) {
console.log("Applied", modifier.type.name, !player ? "(enemy)" : ""); console.log("Applied", modifier.type.name, !player ? "(enemy)" : "");
return modifier; return modifier;
} }
@ -2854,7 +2898,7 @@ export default class BattleScene extends SceneBase {
} }
} }
validateAchv(achv: Achv, args?: any[]): boolean { validateAchv(achv: Achv, args?: unknown[]): boolean {
if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) { if (!this.gameData.achvUnlocks.hasOwnProperty(achv.id) && achv.validate(this, args)) {
this.gameData.achvUnlocks[achv.id] = new Date().getTime(); this.gameData.achvUnlocks[achv.id] = new Date().getTime();
this.ui.achvBar.showAchv(achv); this.ui.achvBar.showAchv(achv);
@ -2867,7 +2911,7 @@ export default class BattleScene extends SceneBase {
return false; return false;
} }
validateVoucher(voucher: Voucher, args?: any[]): boolean { validateVoucher(voucher: Voucher, args?: unknown[]): boolean {
if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) { if (!this.gameData.voucherUnlocks.hasOwnProperty(voucher.id) && voucher.validate(this, args)) {
this.gameData.voucherUnlocks[voucher.id] = new Date().getTime(); this.gameData.voucherUnlocks[voucher.id] = new Date().getTime();
this.ui.achvBar.showAchv(voucher); this.ui.achvBar.showAchv(voucher);

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ export class BerryPhase extends FieldPhase {
this.executeForAll((pokemon) => { this.executeForAll((pokemon) => {
const hasUsableBerry = !!this.scene.findModifier((m) => { const hasUsableBerry = !!this.scene.findModifier((m) => {
return m instanceof BerryModifier && m.shouldApply([pokemon]); return m instanceof BerryModifier && m.shouldApply(pokemon);
}, pokemon.isPlayer()); }, pokemon.isPlayer());
if (hasUsableBerry) { if (hasUsableBerry) {
@ -29,7 +29,7 @@ export class BerryPhase extends FieldPhase {
new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM) new CommonAnimPhase(this.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.USE_ITEM)
); );
for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon) as BerryModifier[]) { for (const berryModifier of this.scene.applyModifiers(BerryModifier, pokemon.isPlayer(), pokemon)) {
if (berryModifier.consumed) { if (berryModifier.consumed) {
if (!--berryModifier.stackCount) { if (!--berryModifier.stackCount) {
this.scene.removeModifier(berryModifier); this.scene.removeModifier(berryModifier);