diff --git a/src/@types/common.ts b/src/@types/common.ts new file mode 100644 index 00000000000..6868d766008 --- /dev/null +++ b/src/@types/common.ts @@ -0,0 +1,3 @@ +import BattleScene from "#app/battle-scene.js"; + +export type ConditionFn = (scene: BattleScene, args?: any[]) => boolean; diff --git a/src/account.ts b/src/account.ts index 7fd1d208496..6682e3e0b7c 100644 --- a/src/account.ts +++ b/src/account.ts @@ -8,7 +8,7 @@ export interface UserInfo { googleId: string; } -export let loggedInUser: UserInfo = null; +export let loggedInUser: UserInfo | null = null; // This is a random string that is used to identify the client session - unique per session (tab or window) so that the game will only save on the one that the server is expecting export const clientSessionId = Utils.randomString(32); @@ -30,11 +30,13 @@ export function updateUserInfo(): Promise<[boolean, integer]> { loggedInUser.lastSessionSlot = lastSessionSlot; // Migrate old data from before the username was appended [ "data", "sessionData", "sessionData1", "sessionData2", "sessionData3", "sessionData4" ].map(d => { - if (localStorage.hasOwnProperty(d)) { - if (localStorage.hasOwnProperty(`${d}_${loggedInUser.username}`)) { - localStorage.setItem(`${d}_${loggedInUser.username}_bak`, localStorage.getItem(`${d}_${loggedInUser.username}`)); + const lsItem = localStorage.getItem(d); + if (lsItem && !!loggedInUser?.username) { + const lsUserItem = localStorage.getItem(`${d}_${loggedInUser.username}`); + if (lsUserItem) { + localStorage.setItem(`${d}_${loggedInUser.username}_bak`, lsUserItem); } - localStorage.setItem(`${d}_${loggedInUser.username}`, localStorage.getItem(d)); + localStorage.setItem(`${d}_${loggedInUser.username}`, lsItem); localStorage.removeItem(d); } }); diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f54ff92e324..4269edecfaf 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -106,8 +106,8 @@ export default class BattleScene extends SceneBase { public inputController: InputsController; public uiInputs: UiInputs; - public sessionPlayTime: integer = null; - public lastSavePlayTime: integer = null; + public sessionPlayTime: integer | null = null; + public lastSavePlayTime: integer | null = null; public masterVolume: number = 0.5; public bgmVolume: number = 1; public seVolume: number = 1; @@ -192,8 +192,8 @@ export default class BattleScene extends SceneBase { private phaseQueuePrependSpliceIndex: integer; private nextCommandPhaseQueue: Phase[]; - private currentPhase: Phase; - private standbyPhase: Phase; + private currentPhase: Phase | null; + private standbyPhase: Phase | null; public field: Phaser.GameObjects.Container; public fieldUI: Phaser.GameObjects.Container; public charSprite: CharSprite; @@ -213,7 +213,7 @@ export default class BattleScene extends SceneBase { public score: integer; public lockModifierTiers: boolean; public trainer: Phaser.GameObjects.Sprite; - public lastEnemyTrainer: Trainer; + public lastEnemyTrainer: Trainer | null; public currentBattle: Battle; public pokeballCounts: PokeballCounts; public money: integer; @@ -251,7 +251,7 @@ export default class BattleScene extends SceneBase { public spritePipeline: SpritePipeline; private bgm: AnySound; - private bgmResumeTimer: Phaser.Time.TimerEvent; + private bgmResumeTimer: Phaser.Time.TimerEvent | null; private bgmCache: Set = new Set(); private playTimeTimer: Phaser.Time.TimerEvent; @@ -700,7 +700,11 @@ export default class BattleScene extends SceneBase { hasExpSprite(key: string): boolean { const keyMatch = /^pkmn__?(back__)?(shiny__)?(female__)?(\d+)(\-.*?)?(?:_[1-3])?$/g.exec(key); - let k = keyMatch[4]; + if (!keyMatch) { + return false; + } + + let k = keyMatch[4]!; if (keyMatch[2]) { k += "s"; } @@ -723,7 +727,7 @@ export default class BattleScene extends SceneBase { return this.party; } - getPlayerPokemon(): PlayerPokemon { + getPlayerPokemon(): PlayerPokemon | undefined { return this.getPlayerField().find(p => p.isActive()); } @@ -740,7 +744,7 @@ export default class BattleScene extends SceneBase { return this.currentBattle?.enemyParty || []; } - getEnemyPokemon(): EnemyPokemon { + getEnemyPokemon(): EnemyPokemon | undefined { return this.getEnemyField().find(p => p.isActive()); } @@ -782,12 +786,12 @@ export default class BattleScene extends SceneBase { return activeOnly ? this.infoToggles.filter(t => t?.isActive()) : this.infoToggles; } - getPokemonById(pokemonId: integer): Pokemon { + getPokemonById(pokemonId: integer): Pokemon | null { const findInParty = (party: Pokemon[]) => party.find(p => p.id === pokemonId); - return findInParty(this.getParty()) || findInParty(this.getEnemyParty()); + return (findInParty(this.getParty()) || findInParty(this.getEnemyParty())) ?? null; } - addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { + addPlayerPokemon(species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData, postProcess?: (playerPokemon: PlayerPokemon) => void): PlayerPokemon { const pokemon = new PlayerPokemon(this, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); if (postProcess) { postProcess(pokemon); @@ -957,7 +961,8 @@ export default class BattleScene extends SceneBase { p.destroy(); } - this.currentBattle = null; + //@ts-ignore - allowing `null` for currentBattle causes a lot of trouble + this.currentBattle = null; // TODO: resolve ts-ignore this.biomeWaveText.setText(startingWave.toString()); this.biomeWaveText.setVisible(false); @@ -1021,14 +1026,14 @@ export default class BattleScene extends SceneBase { } } - newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle { + newBattle(waveIndex?: integer, battleType?: BattleType, trainerData?: TrainerData, double?: boolean): Battle | null { const _startingWave = Overrides.STARTING_WAVE_OVERRIDE || startingWave; const newWaveIndex = waveIndex || ((this.currentBattle?.waveIndex || (_startingWave - 1)) + 1); - let newDouble: boolean; + let newDouble: boolean | undefined; let newBattleType: BattleType; - let newTrainer: Trainer; + let newTrainer: Trainer | undefined; - let battleConfig: FixedBattleConfig = null; + let battleConfig: FixedBattleConfig | null = null; this.resetSeed(newWaveIndex); @@ -1038,7 +1043,7 @@ export default class BattleScene extends SceneBase { battleConfig = this.gameMode.getFixedBattle(newWaveIndex); newDouble = battleConfig.double; newBattleType = battleConfig.battleType; - this.executeWithSeedOffset(() => newTrainer = battleConfig.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); + this.executeWithSeedOffset(() => newTrainer = battleConfig?.getTrainer(this), (battleConfig.seedOffsetWaveIndex || newWaveIndex) << 8); if (newTrainer) { this.field.add(newTrainer); } @@ -1078,7 +1083,7 @@ export default class BattleScene extends SceneBase { playerField.forEach(p => applyAbAttrs(DoubleBattleChanceAbAttr, p, null, doubleChance)); newDouble = !Utils.randSeedInt(doubleChance.value); } else if (newBattleType === BattleType.TRAINER) { - newDouble = newTrainer.variant === TrainerVariant.DOUBLE; + newDouble = newTrainer?.variant === TrainerVariant.DOUBLE; } } else if (!battleConfig) { newDouble = !!double; @@ -1309,7 +1314,7 @@ export default class BattleScene extends SceneBase { return 5; } - let isBoss: boolean; + let isBoss: boolean | undefined; if (forceBoss || (species && (species.subLegendary || species.legendary || species.mythical))) { isBoss = true; } else { @@ -1608,7 +1613,7 @@ export default class BattleScene extends SceneBase { randomSpecies(waveIndex: integer, level: integer, fromArenaPool?: boolean, speciesFilter?: PokemonSpeciesFilter, filterAllEvolutions?: boolean): PokemonSpecies { if (fromArenaPool) { - return this.arena.randomSpecies(waveIndex, level,null , getPartyLuckValue(this.party)); + return this.arena.randomSpecies(waveIndex, level, undefined , getPartyLuckValue(this.party)); } const filteredSpecies = speciesFilter ? [...new Set(allSpecies.filter(s => s.isCatchable()).filter(speciesFilter).map(s => { if (!filterAllEvolutions) { @@ -1966,11 +1971,11 @@ export default class BattleScene extends SceneBase { } /* Phase Functions */ - getCurrentPhase(): Phase { + getCurrentPhase(): Phase | null { return this.currentPhase; } - getStandbyPhase(): Phase { + getStandbyPhase(): Phase | null { return this.standbyPhase; } @@ -2048,7 +2053,10 @@ export default class BattleScene extends SceneBase { } if (this.phaseQueuePrepend.length) { while (this.phaseQueuePrepend.length) { - this.phaseQueue.unshift(this.phaseQueuePrepend.pop()); + const poppedPhase = this.phaseQueuePrepend.pop(); + if (poppedPhase) { + this.phaseQueue.unshift(poppedPhase); + } } } if (!this.phaseQueue.length) { @@ -2056,23 +2064,26 @@ export default class BattleScene extends SceneBase { // Clear the conditionalQueue if there are no phases left in the phaseQueue this.conditionalQueue = []; } - this.currentPhase = this.phaseQueue.shift(); + + this.currentPhase = this.phaseQueue.shift() ?? null; // Check if there are any conditional phases queued if (this.conditionalQueue?.length) { // Retrieve the first conditional phase from the queue const conditionalPhase = this.conditionalQueue.shift(); // Evaluate the condition associated with the phase - if (conditionalPhase[0]()) { + if (conditionalPhase?.[0]()) { // If the condition is met, add the phase to the phase queue this.pushPhase(conditionalPhase[1]); - } else { + } else if (conditionalPhase) { // If the condition is not met, re-add the phase back to the front of the conditional queue this.conditionalQueue.unshift(conditionalPhase); + } else { + console.warn("condition phase is undefined/null!", conditionalPhase); } } - this.currentPhase.start(); + this.currentPhase?.start(); } overridePhase(phase: Phase): boolean { @@ -2087,7 +2098,7 @@ export default class BattleScene extends SceneBase { return true; } - findPhase(phaseFilter: (phase: Phase) => boolean): Phase { + findPhase(phaseFilter: (phase: Phase) => boolean): Phase | undefined { return this.phaseQueue.find(phaseFilter); } @@ -2146,7 +2157,7 @@ export default class BattleScene extends SceneBase { * @param promptDelay optional param for MessagePhase constructor * @param defer boolean for which queue to add it to, false -> add to PhaseQueuePrepend, true -> nextCommandPhaseQueue */ - queueMessage(message: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer, defer?: boolean) { + queueMessage(message: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null, defer?: boolean | null) { const phase = new MessagePhase(this, message, callbackDelay, prompt, promptDelay); if (!defer) { // adds to the end of PhaseQueuePrepend @@ -2182,7 +2193,10 @@ export default class BattleScene extends SceneBase { return Math.floor(moneyValue / 10) * 10; } - addModifier(modifier: Modifier, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise { + addModifier(modifier: Modifier | null, ignoreUpdate?: boolean, playSound?: boolean, virtual?: boolean, instant?: boolean): Promise { + if (!modifier) { + return Promise.resolve(false); + } return new Promise(resolve => { let success = false; const soundName = modifier.type.soundName; @@ -2202,7 +2216,7 @@ export default class BattleScene extends SceneBase { } } else if (!virtual) { const defaultModifierType = getDefaultModifierTypeForTier(modifier.type.tier); - this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, null, true); + this.queueMessage(`The stack for this item is full.\n You will receive ${defaultModifierType.name} instead.`, undefined, true); return this.addModifier(defaultModifierType.newModifier(), ignoreUpdate, playSound, false, instant).then(success => resolve(success)); } @@ -2302,7 +2316,7 @@ export default class BattleScene extends SceneBase { return new Promise(resolve => { const source = itemModifier.pokemonId ? itemModifier.getPokemon(target.scene) : null; const cancelled = new Utils.BooleanHolder(false); - Utils.executeIf(source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source, cancelled)).then(() => { + Utils.executeIf(!!source && source.isPlayer() !== target.isPlayer(), () => applyAbAttrs(BlockItemTheftAbAttr, source! /* checked in condition*/, cancelled)).then(() => { if (cancelled.value) { return resolve(false); } @@ -2383,7 +2397,7 @@ export default class BattleScene extends SceneBase { } party.forEach((enemyPokemon: EnemyPokemon, i: integer) => { - const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer.config.isBoss); + const isBoss = enemyPokemon.isBoss() || (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer?.config.isBoss); let upgradeChance = 32; if (isBoss) { upgradeChance /= 2; @@ -2391,9 +2405,9 @@ export default class BattleScene extends SceneBase { if (isFinalBoss) { upgradeChance /= 8; } - const modifierChance = this.gameMode.getEnemyModifierChance(isBoss); + const modifierChance = this.gameMode.getEnemyModifierChance(isBoss!); // TODO: is this bang correct? let pokemonModifierChance = modifierChance; - if (this.currentBattle.battleType === BattleType.TRAINER) + if (this.currentBattle.battleType === BattleType.TRAINER && this.currentBattle.trainer) pokemonModifierChance = Math.ceil(pokemonModifierChance * this.currentBattle.trainer.getPartyMemberModifierChanceMultiplier(i)); // eslint-disable-line let count = 0; for (let c = 0; c < chances; c++) { @@ -2512,7 +2526,7 @@ export default class BattleScene extends SceneBase { return (player ? this.modifiers : this.enemyModifiers).filter(m => (modifierFilter as ModifierPredicate)(m)); } - findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier { + findModifier(modifierFilter: ModifierPredicate, player: boolean = true): PersistentModifier | undefined { return (player ? this.modifiers : this.enemyModifiers).find(m => (modifierFilter as ModifierPredicate)(m)); } @@ -2548,7 +2562,7 @@ export default class BattleScene extends SceneBase { return appliedModifiers; } - applyModifier(modifierType: Constructor, player: boolean = true, ...args: any[]): PersistentModifier { + applyModifier(modifierType: Constructor, player: boolean = true, ...args: any[]): PersistentModifier | null { const modifiers = (player ? this.modifiers : this.enemyModifiers).filter(m => m instanceof modifierType && m.shouldApply(args)); for (const modifier of modifiers) { if (modifier.apply(args)) { @@ -2635,7 +2649,7 @@ export default class BattleScene extends SceneBase { initFinalBossPhaseTwo(pokemon: Pokemon): void { if (pokemon instanceof EnemyPokemon && pokemon.isBoss() && !pokemon.formIndex && pokemon.bossSegmentIndex < 1) { this.fadeOutBgm(Utils.fixedInt(2000), false); - this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, null, () => { + this.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].firstStageWin, pokemon.species.name, undefined, () => { this.addEnemyModifier(getModifierType(modifierTypes.MINI_BLACK_HOLE).newModifier(pokemon) as PersistentModifier, false, true); pokemon.generateAndPopulateMoveset(1); this.setFieldScale(0.75); diff --git a/src/battle.ts b/src/battle.ts index 8a1309938f7..0b3b256f3df 100644 --- a/src/battle.ts +++ b/src/battle.ts @@ -39,7 +39,7 @@ export interface TurnCommand { } interface TurnCommands { - [key: integer]: TurnCommand + [key: integer]: TurnCommand | null } export default class Battle { @@ -47,8 +47,8 @@ export default class Battle { public waveIndex: integer; public battleType: BattleType; public battleSpec: BattleSpec; - public trainer: Trainer; - public enemyLevels: integer[]; + public trainer: Trainer | null; + public enemyLevels: integer[] | undefined; public enemyParty: EnemyPokemon[]; public seenEnemyPartyMemberIds: Set; public double: boolean; @@ -62,26 +62,26 @@ export default class Battle { public escapeAttempts: integer; public lastMove: Moves; public battleSeed: string; - private battleSeedState: string; + private battleSeedState: string | null; public moneyScattered: number; - public lastUsedPokeball: PokeballType; + public lastUsedPokeball: PokeballType | null; public playerFaints: number; // The amount of times pokemon on the players side have fainted public enemyFaints: number; // The amount of times pokemon on the enemies side have fainted private rngCounter: integer = 0; - constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer: Trainer, double: boolean) { + constructor(gameMode: GameMode, waveIndex: integer, battleType: BattleType, trainer?: Trainer, double?: boolean) { this.gameMode = gameMode; this.waveIndex = waveIndex; this.battleType = battleType; - this.trainer = trainer; + this.trainer = trainer!; //TODO: is this bang correct? this.initBattleSpec(); this.enemyLevels = battleType !== BattleType.TRAINER ? new Array(double ? 2 : 1).fill(null).map(() => this.getLevelForWave()) - : trainer.getPartyLevels(this.waveIndex); + : trainer?.getPartyLevels(this.waveIndex); this.enemyParty = []; this.seenEnemyPartyMemberIds = new Set(); - this.double = double; + this.double = double!; //TODO: is this bang correct? this.enemySwitchCounter = 0; this.turn = 0; this.playerParticipantIds = new Set(); @@ -159,6 +159,7 @@ export default class Battle { addPostBattleLoot(enemyPokemon: EnemyPokemon): void { this.postBattleLoot.push(...enemyPokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === enemyPokemon.id && m.isTransferrable, false).map(i => { const ret = i as PokemonHeldItemModifier; + //@ts-ignore - this is awful to fix/change ret.pokemonId = null; return ret; })); @@ -177,7 +178,7 @@ export default class Battle { const userLocale = navigator.language || "en-US"; const formattedMoneyAmount = moneyAmount.value.toLocaleString(userLocale); const message = i18next.t("battle:moneyPickedUp", { moneyAmount: formattedMoneyAmount }); - scene.queueMessage(message, null, true); + scene.queueMessage(message, undefined, true); scene.currentBattle.moneyScattered = 0; } @@ -200,16 +201,16 @@ export default class Battle { scene.updateScoreText(); } - getBgmOverride(scene: BattleScene): string { + getBgmOverride(scene: BattleScene): string | null { const battlers = this.enemyParty.slice(0, this.getBattlerCount()); if (this.battleType === BattleType.TRAINER) { - if (!this.started && this.trainer.config.encounterBgm && this.trainer.getEncounterMessages()?.length) { - return `encounter_${this.trainer.getEncounterBgm()}`; + if (!this.started && this.trainer?.config.encounterBgm && this.trainer?.getEncounterMessages()?.length) { + return `encounter_${this.trainer?.getEncounterBgm()}`; } if (scene.musicPreference === 0) { - return this.trainer.getBattleBgm(); + return this.trainer?.getBattleBgm()!; // TODO: is this bang correct? } else { - return this.trainer.getMixedBattleBgm(); + return this.trainer?.getMixedBattleBgm()!; // TODO: is this bang correct? } } else if (this.gameMode.isClassic && this.waveIndex > 195 && this.battleSpec !== BattleSpec.FINAL_BOSS) { return "end_summit"; @@ -382,7 +383,7 @@ export default class Battle { export class FixedBattle extends Battle { constructor(scene: BattleScene, waveIndex: integer, config: FixedBattleConfig) { - super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : null, config.double); + super(scene.gameMode, waveIndex, config.battleType, config.battleType === BattleType.TRAINER ? config.getTrainer(scene) : undefined, config.double); if (config.getEnemyParty) { this.enemyParty = config.getEnemyParty(scene); } diff --git a/src/configs/inputs/configHandler.ts b/src/configs/inputs/configHandler.ts index a67c45fd413..45fb033e9fa 100644 --- a/src/configs/inputs/configHandler.ts +++ b/src/configs/inputs/configHandler.ts @@ -20,7 +20,7 @@ export function getKeyWithKeycode(config, keycode) { */ export function getSettingNameWithKeycode(config, keycode) { const key = getKeyWithKeycode(config, keycode); - return config.custom[key]; + return key ? config.custom[key] : null; } /** @@ -32,7 +32,7 @@ export function getSettingNameWithKeycode(config, keycode) { */ export function getIconWithKeycode(config, keycode) { const key = getKeyWithKeycode(config, keycode); - return config.icons[key]; + return key ? config.icons[key] : null; } /** @@ -122,15 +122,21 @@ export function assign(config, settingNameTarget, keycode): boolean { // if it was already bound, we delete the bind if (previousSettingName) { const previousKey = getKeyWithSettingName(config, previousSettingName); - config.custom[previousKey] = -1; + if (previousKey) { + config.custom[previousKey] = -1; + } } // then, we need to delete the current key for this settingName const currentKey = getKeyWithSettingName(config, settingNameTarget); - config.custom[currentKey] = -1; + if (currentKey) { + config.custom[currentKey] = -1; + } // then, the new key is assigned to the new settingName const newKey = getKeyWithKeycode(config, keycode); - config.custom[newKey] = settingNameTarget; + if (newKey) { + config.custom[newKey] = settingNameTarget; + } return true; } @@ -145,8 +151,12 @@ export function swap(config, settingNameTarget, keycode) { const new_key = getKeyWithKeycode(config, keycode); const new_settingName = getSettingNameWithKey(config, new_key); - config.custom[prev_key] = new_settingName; - config.custom[new_key] = prev_settingName; + if (prev_key) { + config.custom[prev_key] = new_settingName; + } + if (new_key) { + config.custom[new_key] = prev_settingName; + } return true; } @@ -161,7 +171,9 @@ export function deleteBind(config, settingName) { if (config.blacklist.includes(key)) { return false; } - config.custom[key] = -1; + if (key) { + config.custom[key] = -1; + } return true; } diff --git a/src/data/ability.ts b/src/data/ability.ts index b483998612e..f4701d3a4e1 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -120,7 +120,7 @@ export class Ability implements Localizable { type AbAttrApplyFunc = (attr: TAttr, passive: boolean) => boolean | Promise; type AbAttrCondition = (pokemon: Pokemon) => boolean; -type PokemonAttackCondition = (user: Pokemon, target: Pokemon, move: Move) => boolean; +type PokemonAttackCondition = (user: Pokemon | null, target: Pokemon | null, move: Move) => boolean; type PokemonDefendCondition = (target: Pokemon, user: Pokemon, move: Move) => boolean; type PokemonStatChangeCondition = (target: Pokemon, statsChanged: BattleStat[], levels: integer) => boolean; @@ -132,11 +132,11 @@ export abstract class AbAttr { this.showAbility = showAbility; } - apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } - getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string { + getTriggerMessage(_pokemon: Pokemon, _abilityName: string, ..._args: any[]): string | null { return null; } @@ -226,7 +226,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { } for (const statChangePhase of statChangePhases) { - if (!this.selfTarget && !statChangePhase.getPokemon().summonData) { + if (!this.selfTarget && !statChangePhase.getPokemon()?.summonData) { pokemon.scene.pushPhase(statChangePhase); } else { // TODO: This causes the ability bar to be shown at the wrong time pokemon.scene.unshiftPhase(statChangePhase); @@ -240,7 +240,7 @@ export class PostBattleInitStatChangeAbAttr extends PostBattleInitAbAttr { type PreDefendAbAttrCondition = (pokemon: Pokemon, attacker: Pokemon, move: Move) => boolean; export class PreDefendAbAttr extends AbAttr { - applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, args: any[]): boolean | Promise { return false; } } @@ -352,14 +352,14 @@ export class PreDefendMoveDamageToOneAbAttr extends ReceivedMoveDamageMultiplier * @see {@linkcode getCondition} */ export class TypeImmunityAbAttr extends PreDefendAbAttr { - private immuneType: Type; - private condition: AbAttrCondition; + private immuneType: Type | null; + private condition: AbAttrCondition | null; - constructor(immuneType: Type, condition?: AbAttrCondition) { + constructor(immuneType: Type | null, condition?: AbAttrCondition) { super(); this.immuneType = immuneType; - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } /** @@ -386,7 +386,7 @@ export class TypeImmunityAbAttr extends PreDefendAbAttr { return false; } - getCondition(): AbAttrCondition { + override getCondition(): AbAttrCondition | null { return this.condition; } } @@ -491,7 +491,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr { } export class PostDefendAbAttr extends AbAttr { - applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } @@ -881,7 +881,7 @@ export class EffectSporeAbAttr extends PostDefendContactApplyStatusEffectAbAttr export class PostDefendContactApplyTagChanceAbAttr extends PostDefendAbAttr { private chance: integer; private tagType: BattlerTagType; - private turnCount: integer; + private turnCount: integer | undefined; constructor(chance: integer, tagType: BattlerTagType, turnCount?: integer) { super(); @@ -918,7 +918,7 @@ export class PostDefendCritStatChangeAbAttr extends PostDefendAbAttr { } getCondition(): AbAttrCondition { - return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; + return (pokemon: Pokemon) => pokemon.turnData.attacksReceived.length !== 0 && pokemon.turnData.attacksReceived[pokemon.turnData.attacksReceived.length - 1].critical; } } @@ -1109,7 +1109,7 @@ export class PostStatChangeStatChangeAbAttr extends PostStatChangeAbAttr { } export class PreAttackAbAttr extends AbAttr { - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean | Promise { + applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean | Promise { return false; } } @@ -1122,7 +1122,7 @@ export class PreAttackAbAttr extends AbAttr { export class MoveEffectChanceMultiplierAbAttr extends AbAttr { private chanceMultiplier: number; - constructor(chanceMultiplier?: number) { + constructor(chanceMultiplier: number) { super(true); this.chanceMultiplier = chanceMultiplier; } @@ -1503,7 +1503,7 @@ export class FieldMovePowerBoostAbAttr extends AbAttr { this.powerMultiplier = powerMultiplier; } - applyPreAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, args: any[]): boolean { + applyPreAttack(pokemon: Pokemon | null, passive: boolean | null, defender: Pokemon | null, move: Move, args: any[]): boolean { if (this.condition(pokemon, defender, move)) { (args[0] as Utils.NumberHolder).value *= this.powerMultiplier; @@ -1557,14 +1557,14 @@ export class AllyMoveCategoryPowerBoostAbAttr extends FieldMovePowerBoostAbAttr export class BattleStatMultiplierAbAttr extends AbAttr { private battleStat: BattleStat; private multiplier: number; - private condition: PokemonAttackCondition; + private condition: PokemonAttackCondition | null; constructor(battleStat: BattleStat, multiplier: number, condition?: PokemonAttackCondition) { super(false); this.battleStat = battleStat; this.multiplier = multiplier; - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } applyBattleStat(pokemon: Pokemon, passive: boolean, battleStat: BattleStat, statValue: Utils.NumberHolder, args: any[]): boolean | Promise { @@ -1593,7 +1593,7 @@ export class PostAttackAbAttr extends AbAttr { * applying the effect of any inherited class. This can be changed by providing a different {@link attackCondition} to the constructor. See {@link ConfusionOnStatusEffectAbAttr} * for an example of an effect that does not require a damaging move. */ - applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostAttack(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { // When attackRequired is true, we require the move to be an attack move and to deal damage before checking secondary requirements. // If attackRequired is false, we always defer to the secondary requirements. if (this.attackCondition(pokemon, defender, move)) { @@ -1606,18 +1606,18 @@ export class PostAttackAbAttr extends AbAttr { /** * This method is only called after {@link applyPostAttack} has already been applied. Use this for handling checks specific to the ability in question. */ - applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { + applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult | null, args: any[]): boolean | Promise { return false; } } export class PostAttackStealHeldItemAbAttr extends PostAttackAbAttr { - private stealCondition: PokemonAttackCondition; + private stealCondition: PokemonAttackCondition | null; constructor(stealCondition?: PokemonAttackCondition) { super(); - this.stealCondition = stealCondition; + this.stealCondition = stealCondition!; // TODO: is this bang correct? } applyPostAttackAfterMoveTypeCheck(pokemon: Pokemon, passive: boolean, defender: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { @@ -1701,12 +1701,12 @@ export class PostAttackApplyBattlerTagAbAttr extends PostAttackAbAttr { } export class PostDefendStealHeldItemAbAttr extends PostDefendAbAttr { - private condition: PokemonDefendCondition; + private condition: PokemonDefendCondition | null; constructor(condition?: PokemonDefendCondition) { super(); - this.condition = condition; + this.condition = condition!; // TODO: is this bang correct? } applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): Promise { @@ -2197,17 +2197,17 @@ export class PostSummonCopyAbilityAbAttr extends PostSummonAbAttr { } if ( - target.getAbility().hasAttr(UncopiableAbilityAbAttr) && + target!.getAbility().hasAttr(UncopiableAbilityAbAttr) && // Wonder Guard is normally uncopiable so has the attribute, but Trace specifically can copy it - !(pokemon.hasAbility(Abilities.TRACE) && target.getAbility().id === Abilities.WONDER_GUARD) + !(pokemon.hasAbility(Abilities.TRACE) && target!.getAbility().id === Abilities.WONDER_GUARD) ) { return false; } - this.target = target; - this.targetAbilityName = allAbilities[target.getAbility().id].name; - pokemon.summonData.ability = target.getAbility().id; - setAbilityRevealed(target); + this.target = target!; + this.targetAbilityName = allAbilities[target!.getAbility().id].name; + pokemon.summonData.ability = target!.getAbility().id; + setAbilityRevealed(target!); pokemon.updateInfo(); return true; @@ -2254,7 +2254,7 @@ export class PostSummonUserFieldRemoveStatusEffectAbAttr extends PostSummonAbAtt } for (const pokemon of allowedParty) { - if (this.statusEffect.includes(pokemon.status?.effect)) { + if (pokemon.status && this.statusEffect.includes(pokemon.status.effect)) { pokemon.scene.queueMessage(getStatusEffectHealText(pokemon.status.effect, getPokemonNameWithAffix(pokemon))); pokemon.resetStatus(false); pokemon.updateInfo(); @@ -2310,6 +2310,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { target = targets[0]; } + target = target!; // compiler doesn't know its guranteed to be defined pokemon.summonData.speciesForm = target.getSpeciesForm(); pokemon.summonData.fusionSpeciesForm = target.getFusionSpeciesForm(); pokemon.summonData.ability = target.getAbility().id; @@ -2317,7 +2318,7 @@ export class PostSummonTransformAbAttr extends PostSummonAbAttr { pokemon.summonData.fusionGender = target.getFusionGender(); pokemon.summonData.stats = [ pokemon.stats[Stat.HP] ].concat(target.stats.slice(1)); pokemon.summonData.battleStats = target.summonData.battleStats.slice(0); - pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); + pokemon.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m!.moveId, m!.ppUsed, m!.ppUp)); // TODO: are those bangs correct? pokemon.summonData.types = target.getTypes(); pokemon.scene.playSound("PRSFX- Transform"); @@ -2364,7 +2365,7 @@ export class PreSwitchOutClearWeatherAbAttr extends PreSwitchOutAbAttr { * @returns {boolean} Returns true if the weather clears, otherwise false. */ applyPreSwitchOut(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - const weatherType = pokemon.scene.arena.weather.weatherType; + const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. @@ -2445,18 +2446,18 @@ export class PreSwitchOutFormChangeAbAttr extends PreSwitchOutAbAttr { } export class PreStatChangeAbAttr extends AbAttr { - applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreStatChange(pokemon: Pokemon | null, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } export class ProtectStatAbAttr extends PreStatChangeAbAttr { - private protectedStat: BattleStat; + private protectedStat: BattleStat | null; constructor(protectedStat?: BattleStat) { super(); - this.protectedStat = protectedStat; + this.protectedStat = protectedStat!; // TODO: is this bang correct? } applyPreStatChange(pokemon: Pokemon, passive: boolean, stat: BattleStat, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -2472,7 +2473,7 @@ export class ProtectStatAbAttr extends PreStatChangeAbAttr { return i18next.t("abilityTriggers:protectStat", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), abilityName, - statName: this.protectedStat !== undefined ? getBattleStatName(this.protectedStat) : i18next.t("battle:stats") + statName: this.protectedStat ? getBattleStatName(this.protectedStat) : i18next.t("battle:stats") }); } } @@ -2512,7 +2513,7 @@ export class ConfusionOnStatusEffectAbAttr extends PostAttackAbAttr { } export class PreSetStatusAbAttr extends AbAttr { - applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreSetStatus(pokemon: Pokemon, passive: boolean, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2723,7 +2724,7 @@ export class BlockStatusDamageAbAttr extends AbAttr { * @returns Returns true if status damage is blocked */ apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean { - if (this.effects.includes(pokemon.status?.effect)) { + if (pokemon.status && this.effects.includes(pokemon.status.effect)) { cancelled.value = true; return true; } @@ -2762,7 +2763,7 @@ export class IncrementMovePriorityAbAttr extends AbAttr { export class IgnoreContactAbAttr extends AbAttr { } export class PreWeatherEffectAbAttr extends AbAttr { - applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { + applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather | null, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise { return false; } } @@ -2793,7 +2794,7 @@ export class SuppressWeatherEffectAbAttr extends PreWeatherEffectAbAttr { constructor(affectsImmutable?: boolean) { super(); - this.affectsImmutable = affectsImmutable; + this.affectsImmutable = affectsImmutable!; // TODO: is this bang correct? } applyPreWeatherEffect(pokemon: Pokemon, passive: boolean, weather: Weather, cancelled: Utils.BooleanHolder, args: any[]): boolean { @@ -2844,7 +2845,7 @@ function getWeatherCondition(...weatherTypes: WeatherType[]): AbAttrCondition { return false; } const weatherType = pokemon.scene.arena.weather?.weatherType; - return weatherType && weatherTypes.indexOf(weatherType) > -1; + return !!weatherType && weatherTypes.indexOf(weatherType) > -1; }; } @@ -2853,15 +2854,15 @@ function getAnticipationCondition(): AbAttrCondition { for (const opponent of pokemon.getOpponents()) { for (const move of opponent.moveset) { // move is super effective - if (move.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move.getMove().type, opponent, true) >= 2) { + if (move!.getMove() instanceof AttackMove && pokemon.getAttackTypeEffectiveness(move!.getMove().type, opponent, true) >= 2) { // TODO: is this bang correct? return true; } // move is a OHKO - if (move.getMove().hasAttr(OneHitKOAttr)) { + if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct? return true; } // edge case for hidden power, type is computed - if (move.getMove().id === Moves.HIDDEN_POWER) { + if (move!.getMove().id === Moves.HIDDEN_POWER) { // TODO: is this bang correct? const iv_val = Math.floor(((opponent.ivs[Stat.HP] & 1) +(opponent.ivs[Stat.ATK] & 1) * 2 +(opponent.ivs[Stat.DEF] & 1) * 4 @@ -2909,21 +2910,21 @@ export class ForewarnAbAttr extends PostSummonAbAttr { let movePower = 0; for (const opponent of pokemon.getOpponents()) { for (const move of opponent.moveset) { - if (move.getMove() instanceof StatusMove) { + if (move!.getMove() instanceof StatusMove) { // TODO: is this bang correct? movePower = 1; - } else if (move.getMove().hasAttr(OneHitKOAttr)) { + } else if (move!.getMove().hasAttr(OneHitKOAttr)) { // TODO: is this bang correct? movePower = 150; - } else if (move.getMove().id === Moves.COUNTER || move.getMove().id === Moves.MIRROR_COAT || move.getMove().id === Moves.METAL_BURST) { + } else if (move!.getMove().id === Moves.COUNTER || move!.getMove().id === Moves.MIRROR_COAT || move!.getMove().id === Moves.METAL_BURST) { // TODO: are those bangs correct? movePower = 120; - } else if (move.getMove().power === -1) { + } else if (move!.getMove().power === -1) { // TODO: is this bang correct? movePower = 80; } else { - movePower = move.getMove().power; + movePower = move!.getMove().power; // TODO: is this bang correct? } if (movePower > maxPowerSeen) { maxPowerSeen = movePower; - maxMove = move.getName(); + maxMove = move!.getName(); // TODO: is this bang correct? } } } @@ -2984,7 +2985,7 @@ export class PostWeatherLapseAbAttr extends AbAttr { this.weatherTypes = weatherTypes; } - applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather, args: any[]): boolean | Promise { + applyPostWeatherLapse(pokemon: Pokemon, passive: boolean, weather: Weather | null, args: any[]): boolean | Promise { return false; } @@ -3067,7 +3068,7 @@ export class PostTerrainChangeAddBattlerTagAttr extends PostTerrainChangeAbAttr function getTerrainCondition(...terrainTypes: TerrainType[]): AbAttrCondition { return (pokemon: Pokemon) => { const terrainType = pokemon.scene.arena.terrain?.terrainType; - return terrainType && terrainTypes.indexOf(terrainType) > -1; + return !!terrainType && terrainTypes.indexOf(terrainType) > -1; }; } @@ -3099,7 +3100,7 @@ export class PostTurnStatusHealAbAttr extends PostTurnAbAttr { * @returns Returns true if healed from status, false if not */ applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean | Promise { - if (this.effects.includes(pokemon.status?.effect)) { + if (pokemon.status && this.effects.includes(pokemon.status.effect)) { if (!pokemon.isFullHp()) { const scene = pokemon.scene; const abilityName = (!passive ? pokemon.getAbility() : pokemon.getPassiveAbility()).name; @@ -3335,7 +3336,7 @@ export class FetchBallAbAttr extends PostTurnAbAttr { */ applyPostTurn(pokemon: Pokemon, passive: boolean, args: any[]): boolean { const lastUsed = pokemon.scene.currentBattle.lastUsedPokeball; - if (lastUsed !== null && pokemon.isPlayer) { + if (lastUsed !== null && !!pokemon.isPlayer) { pokemon.scene.pokeballCounts[lastUsed]++; pokemon.scene.currentBattle.lastUsedPokeball = null; pokemon.scene.queueMessage(i18next.t("abilityTriggers:fetchBall", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), pokeballName: getPokeballName(lastUsed) })); @@ -3616,7 +3617,8 @@ export class PostBattleLootAbAttr extends PostBattleAbAttr { const postBattleLoot = pokemon.scene.currentBattle.postBattleLoot; if (postBattleLoot.length) { const randItem = Utils.randSeedItem(postBattleLoot); - if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { + //@ts-ignore - TODO see below + if (pokemon.scene.tryTransferHeldItemModifier(randItem, pokemon, true, 1, true)) { // TODO: fix. This is a promise!? postBattleLoot.splice(postBattleLoot.indexOf(randItem), 1); pokemon.scene.queueMessage(i18next.t("abilityTriggers:postBattleLoot", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), itemName: randItem.type.name })); return true; @@ -3648,7 +3650,7 @@ export class PostFaintClearWeatherAbAttr extends PostFaintAbAttr { * @returns {boolean} Returns true if the weather clears, otherwise false. */ applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean { - const weatherType = pokemon.scene.arena.weather.weatherType; + const weatherType = pokemon.scene.arena.weather?.weatherType; let turnOffWeather = false; // Clear weather only if user's ability matches the weather and no other pokemon has the ability. @@ -4120,7 +4122,7 @@ export class BypassSpeedChanceAbAttr extends AbAttr { const turnCommand = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; const isCommandFight = turnCommand?.command === Command.FIGHT; - const move = allMoves[turnCommand.move?.move]; + const move = turnCommand?.move?.move ?allMoves[turnCommand.move.move] : null; const isDamageMove = move?.category === MoveCategory.PHYSICAL || move?.category === MoveCategory.SPECIAL; if (isCommandFight && isDamageMove) { @@ -4139,14 +4141,14 @@ export class BypassSpeedChanceAbAttr extends AbAttr { async function applyAbAttrsInternal( attrType: Constructor, - pokemon: Pokemon, + pokemon: Pokemon | null, applyFunc: AbAttrApplyFunc, args: any[], showAbilityInstant: boolean = false, quiet: boolean = false, ) { for (const passive of [false, true]) { - if (!pokemon.canApplyAbility(passive)) { + if (!pokemon?.canApplyAbility(passive)) { continue; } @@ -4194,7 +4196,7 @@ async function applyAbAttrsInternal( } } -export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { +export function applyAbAttrs(attrType: Constructor, pokemon: Pokemon, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.apply(pokemon, passive, cancelled, args), args); } @@ -4204,13 +4206,13 @@ export function applyPostBattleInitAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, attacker: Pokemon, move: Move | null, cancelled: Utils.BooleanHolder | null, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreDefend(pokemon, passive, attacker, move, cancelled, args), args, false, simulated); } export function applyPostDefendAbAttrs(attrType: Constructor, - pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { + pokemon: Pokemon, attacker: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostDefend(pokemon, passive, attacker, move, hitResult, args), args); } @@ -4240,12 +4242,12 @@ export function applyFieldBattleStatMultiplierAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, ...args: any[]): Promise { + pokemon: Pokemon, defender: Pokemon | null, move: Move, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreAttack(pokemon, passive, defender, move, args), args); } export function applyPostAttackAbAttrs(attrType: Constructor, - pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult, ...args: any[]): Promise { + pokemon: Pokemon, defender: Pokemon, move: Move, hitResult: HitResult | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostAttack(pokemon, passive, defender, move, hitResult, args), args); } @@ -4270,7 +4272,7 @@ export function applyPreSwitchOutAbAttrs(attrType: Constructor, - pokemon: Pokemon, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon | null, stat: BattleStat, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreStatChange(pokemon, passive, stat, cancelled, args), args); } @@ -4280,7 +4282,7 @@ export function applyPostStatChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, effect: StatusEffect, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, effect: StatusEffect | undefined, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { const simulated = args.length > 1 && args[1]; return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreSetStatus(pokemon, passive, effect, cancelled, args), args, false, !simulated); } @@ -4291,7 +4293,7 @@ export function applyPreApplyBattlerTagAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { + pokemon: Pokemon, weather: Weather | null, cancelled: Utils.BooleanHolder, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPreWeatherEffect(pokemon, passive, weather, cancelled, args), args, true); } @@ -4306,7 +4308,7 @@ export function applyPostWeatherChangeAbAttrs(attrType: Constructor, - pokemon: Pokemon, weather: Weather, ...args: any[]): Promise { + pokemon: Pokemon, weather: Weather | null, ...args: any[]): Promise { return applyAbAttrsInternal(attrType, pokemon, (attr, passive) => attr.applyPostWeatherLapse(pokemon, passive, weather, args), args); } @@ -4595,8 +4597,8 @@ export function initAbilities() { .attr(TypeImmunityStatChangeAbAttr, Type.ELECTRIC, BattleStat.SPD, 1) .ignorable(), new Ability(Abilities.RIVALRY, 4) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender === target.gender, 1.25, true) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.gender !== Gender.GENDERLESS && target.gender !== Gender.GENDERLESS && user.gender !== target.gender, 0.75), + .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender === target?.gender, 1.25, true) + .attr(MovePowerBoostAbAttr, (user, target, move) => user?.gender !== Gender.GENDERLESS && target?.gender !== Gender.GENDERLESS && user?.gender !== target?.gender, 0.75), new Ability(Abilities.STEADFAST, 4) .attr(FlinchStatChangeAbAttr, BattleStat.SPD, 1), new Ability(Abilities.SNOW_CLOAK, 4) @@ -4686,7 +4688,8 @@ export function initAbilities() { .attr(IgnoreOpponentStatChangesAbAttr) .ignorable(), new Ability(Abilities.TINTED_LENS, 4) - .attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), + //@ts-ignore + .attr(DamageBoostAbAttr, 2, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) <= 0.5), // TODO: fix TS issues new Ability(Abilities.FILTER, 4) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75) .ignorable(), @@ -4768,9 +4771,9 @@ export function initAbilities() { .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.isFullHp(), 0.5) .ignorable(), new Ability(Abilities.TOXIC_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.PHYSICAL && (user.status?.effect === StatusEffect.POISON || user.status?.effect === StatusEffect.TOXIC), 1.5), + .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.PHYSICAL && (user?.status?.effect === StatusEffect.POISON || user?.status?.effect === StatusEffect.TOXIC), 1.5), new Ability(Abilities.FLARE_BOOST, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user.status?.effect === StatusEffect.BURN, 1.5), + .attr(MovePowerBoostAbAttr, (user, target, move) => move.category === MoveCategory.SPECIAL && user?.status?.effect === StatusEffect.BURN, 1.5), new Ability(Abilities.HARVEST, 5) .attr( PostTurnLootAbAttr, @@ -4803,7 +4806,8 @@ export function initAbilities() { .attr(WonderSkinAbAttr) .ignorable(), new Ability(Abilities.ANALYTIC, 5) - .attr(MovePowerBoostAbAttr, (user, target, move) => !!target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.getLastXMoves(1).find(m => m.turn === target?.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command !== Command.FIGHT, 1.3), // TODO fix TS issues new Ability(Abilities.ILLUSION, 5) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) @@ -4953,7 +4957,7 @@ export function initAbilities() { new Ability(Abilities.WATER_COMPACTION, 7) .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.type === Type.WATER && move.category !== MoveCategory.STATUS, BattleStat.DEF, 2), new Ability(Abilities.MERCILESS, 7) - .attr(ConditionalCritAbAttr, (user, target, move) => target.status?.effect === StatusEffect.TOXIC || target.status?.effect === StatusEffect.POISON), + .attr(ConditionalCritAbAttr, (user, target, move) => target?.status?.effect === StatusEffect.TOXIC || target?.status?.effect === StatusEffect.POISON), new Ability(Abilities.SHIELDS_DOWN, 7) .attr(PostBattleInitFormChangeAbAttr, () => 0) .attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0)) @@ -4965,7 +4969,8 @@ export function initAbilities() { .bypassFaint() .partial(), new Ability(Abilities.STAKEOUT, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.POKEMON, 2), // TODO: fix TS issues new Ability(Abilities.WATER_BUBBLE, 7) .attr(ReceivedTypeDamageMultiplierAbAttr, Type.FIRE, 0.5) .attr(MoveTypePowerBoostAbAttr, Type.WATER, 2) @@ -5105,7 +5110,8 @@ export function initAbilities() { new Ability(Abilities.PRISM_ARMOR, 7) .attr(ReceivedMoveDamageMultiplierAbAttr,(target, user, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 0.75), new Ability(Abilities.NEUROFORCE, 7) - .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), + //@ts-ignore + .attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type, user) >= 2, 1.25), // TODO: fix TS issues new Ability(Abilities.INTREPID_SWORD, 8) .attr(PostSummonStatChangeAbAttr, BattleStat.ATK, 1, true) .condition(getOncePerBattleCondition(Abilities.INTREPID_SWORD)), @@ -5192,8 +5198,10 @@ export function initAbilities() { .attr(UserFieldStatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .ignorable(), new Ability(Abilities.HUNGER_SWITCH, 8) - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) - .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) + //@ts-ignore + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 0 : 1) // TODO: fix ts-ignore + //@ts-ignore + .attr(PostTurnFormChangeAbAttr, p => p.getFormKey ? 1 : 0) // TODO: fix ts-ignore .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) .attr(NoTransformAbilityAbAttr) diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 9c846d2f3cb..7c67271b0dc 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -25,12 +25,12 @@ export enum ArenaTagSide { export abstract class ArenaTag { public tagType: ArenaTagType; public turnCount: integer; - public sourceMove: Moves; - public sourceId: integer; + public sourceMove?: Moves; + public sourceId?: integer; public side: ArenaTagSide; - constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { + constructor(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId?: integer, side: ArenaTagSide = ArenaTagSide.BOTH) { this.tagType = tagType; this.turnCount = turnCount; this.sourceMove = sourceMove; @@ -56,7 +56,7 @@ export abstract class ArenaTag { return this.turnCount < 1 || !!(--this.turnCount); } - getMoveName(): string { + getMoveName(): string | null { return this.sourceMove ? allMoves[this.sourceMove].name : null; @@ -75,9 +75,14 @@ export class MistTag extends ArenaTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { - arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + if (this.sourceId) { + const source = arena.scene.getPokemonById(this.sourceId); + + if (!quiet && source) { + arena.scene.queueMessage(i18next.t("arenaTag:mistOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } else if (!quiet) { + console.warn("Failed to get source for MistTag onAdd"); + } } } @@ -280,8 +285,14 @@ class MatBlockTag extends ConditionalProtectTag { } onAdd(arena: Arena) { - const source = arena.scene.getPokemonById(this.sourceId); - arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + if (this.sourceId) { + const source = arena.scene.getPokemonById(this.sourceId); + if (source) { + arena.scene.queueMessage(i18next.t("arenaTag:matBlockOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } else { + console.warn("Failed to get source for MatBlockTag onAdd"); + } + } } } @@ -328,9 +339,9 @@ export class NoCritTag extends ArenaTag { /** Queues a message upon removing this effect from the field */ onRemove(arena: Arena): void { - const source = arena.scene.getPokemonById(this.sourceId); + const source = arena.scene.getPokemonById(this.sourceId!); // TODO: is this bang correct? arena.scene.queueMessage(i18next.t("arenaTag:noCritOnRemove", { - pokemonNameWithAffix: getPokemonNameWithAffix(source), + pokemonNameWithAffix: getPokemonNameWithAffix(source ?? undefined), moveName: this.getMoveName() })); } @@ -350,10 +361,16 @@ class WishTag extends ArenaTag { } onAdd(arena: Arena): void { - const user = arena.scene.getPokemonById(this.sourceId); - this.battlerIndex = user.getBattlerIndex(); - this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); - this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + if (this.sourceId) { + const user = arena.scene.getPokemonById(this.sourceId); + if (user) { + this.battlerIndex = user.getBattlerIndex(); + this.triggerMessage = i18next.t("arenaTag:wishTagOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(user) }); + this.healHp = Math.max(Math.floor(user.getMaxHp() / 2), 1); + } else { + console.warn("Failed to get source for WishTag onAdd"); + } + } } onRemove(arena: Arena): void { @@ -494,8 +511,8 @@ class SpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:spikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -539,8 +556,8 @@ class ToxicSpikesTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:toxicSpikesOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -589,7 +606,7 @@ class ToxicSpikesTag extends ArenaTrapTag { class DelayedAttackTag extends ArenaTag { public targetIndex: BattlerIndex; - constructor(tagType: ArenaTagType, sourceMove: Moves, sourceId: integer, targetIndex: BattlerIndex) { + constructor(tagType: ArenaTagType, sourceMove: Moves | undefined, sourceId: integer, targetIndex: BattlerIndex) { super(tagType, 3, sourceMove, sourceId); this.targetIndex = targetIndex; @@ -599,7 +616,7 @@ class DelayedAttackTag extends ArenaTag { const ret = super.lapse(arena); if (!ret) { - arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId, [ this.targetIndex ], new PokemonMove(this.sourceMove, 0, 0, true))); + arena.scene.unshiftPhase(new MoveEffectPhase(arena.scene, this.sourceId!, [ this.targetIndex ], new PokemonMove(this.sourceMove!, 0, 0, true))); // TODO: are those bangs correct? } return ret; @@ -621,8 +638,8 @@ class StealthRockTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:stealthRockOnAdd", { opponentDesc: source.getOpponentDescriptor() })); } } @@ -630,7 +647,7 @@ class StealthRockTag extends ArenaTrapTag { getDamageHpRatio(pokemon: Pokemon): number { const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK, undefined, true); - let damageHpRatio: number; + let damageHpRatio: number = 0; switch (effectiveness) { case 0: @@ -696,8 +713,8 @@ class StickyWebTag extends ArenaTrapTag { onAdd(arena: Arena, quiet: boolean = false): void { super.onAdd(arena); - const source = arena.scene.getPokemonById(this.sourceId); - if (!quiet) { + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (!quiet && source) { arena.scene.queueMessage(i18next.t("arenaTag:stickyWebOnAdd", { moveName: this.getMoveName(), opponentDesc: source.getOpponentDescriptor() })); } } @@ -735,7 +752,10 @@ export class TrickRoomTag extends ArenaTag { } onAdd(arena: Arena): void { - arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(arena.scene.getPokemonById(this.sourceId)) })); + const source = this.sourceId ? arena.scene.getPokemonById(this.sourceId) : null; + if (source) { + arena.scene.queueMessage(i18next.t("arenaTag:trickRoomOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(source) })); + } } onRemove(arena: Arena): void { @@ -782,8 +802,8 @@ class TailwindTag extends ArenaTag { arena.scene.queueMessage(i18next.t(`arenaTag:tailwindOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`)); } - const source = arena.scene.getPokemonById(this.sourceId); - const party = source.isPlayer() ? source.scene.getPlayerField() : source.scene.getEnemyField(); + const source = arena.scene.getPokemonById(this.sourceId!); //TODO: this bang is questionable! + const party = (source?.isPlayer() ? source.scene.getPlayerField() : source?.scene.getEnemyField()) ?? []; for (const pokemon of party) { // Apply the CHARGED tag to party members with the WIND_POWER ability @@ -824,7 +844,7 @@ class HappyHourTag extends ArenaTag { } } -export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag { +export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null { switch (tagType) { case ArenaTagType.MIST: return new MistTag(turnCount, sourceId, side); @@ -837,7 +857,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov case ArenaTagType.CRAFTY_SHIELD: return new CraftyShieldTag(sourceId, side); case ArenaTagType.NO_CRIT: - return new NoCritTag(turnCount, sourceMove, sourceId, side); + return new NoCritTag(turnCount, sourceMove!, sourceId, side); // TODO: is this bang correct? case ArenaTagType.MUD_SPORT: return new MudSportTag(turnCount, sourceId); case ArenaTagType.WATER_SPORT: @@ -848,7 +868,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new ToxicSpikesTag(sourceId, side); case ArenaTagType.FUTURE_SIGHT: case ArenaTagType.DOOM_DESIRE: - return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex); + return new DelayedAttackTag(tagType, sourceMove, sourceId, targetIndex!); // TODO:questionable bang case ArenaTagType.WISH: return new WishTag(turnCount, sourceId, side); case ArenaTagType.STEALTH_ROCK: @@ -869,5 +889,7 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov return new TailwindTag(turnCount, sourceId, side); case ArenaTagType.HAPPY_HOUR: return new HappyHourTag(turnCount, sourceId, side); + default: + return null; } } diff --git a/src/data/battle-anims.ts b/src/data/battle-anims.ts index d4dbb8ec350..d9fc87c67c7 100644 --- a/src/data/battle-anims.ts +++ b/src/data/battle-anims.ts @@ -128,7 +128,7 @@ export class AnimConfig { for (const fte of Object.keys(frameTimedEvents)) { const timedEvents: AnimTimedEvent[] = []; for (const te of frameTimedEvents[fte]) { - let timedEvent: AnimTimedEvent; + let timedEvent: AnimTimedEvent | undefined; switch (te.eventType) { case "AnimTimedSoundEvent": timedEvent = new AnimTimedSoundEvent(te.frameIndex, te.resourceName, te); @@ -140,7 +140,8 @@ export class AnimConfig { timedEvent = new AnimTimedUpdateBgEvent(te.frameIndex, te.resourceName, te); break; } - timedEvents.push(timedEvent); + + timedEvent && timedEvents.push(timedEvent); } this.frameTimedEvents.set(parseInt(fte), timedEvents); } @@ -330,7 +331,7 @@ class AnimTimedSoundEvent extends AnimTimedEvent { } return Math.ceil((scene.sound.get(this.resourceName).totalDuration * 1000) / 33.33); } else { - return Math.ceil((battleAnim.user.cry(soundConfig).totalDuration * 1000) / 33.33); + return Math.ceil((battleAnim.user!.cry(soundConfig).totalDuration * 1000) / 33.33); // TODO: is the bang behind user correct? } } @@ -441,15 +442,15 @@ class AnimTimedAddBgEvent extends AnimTimedBgEvent { } } -export const moveAnims = new Map(); -export const chargeAnims = new Map(); +export const moveAnims = new Map(); +export const chargeAnims = new Map(); export const commonAnims = new Map(); export function initCommonAnims(scene: BattleScene): Promise { return new Promise(resolve => { const commonAnimNames = Utils.getEnumKeys(CommonAnim); const commonAnimIds = Utils.getEnumValues(CommonAnim); - const commonAnimFetches = []; + const commonAnimFetches: Promise>[] = []; for (let ca = 0; ca < commonAnimIds.length; ca++) { const commonAnimId = commonAnimIds[ca]; commonAnimFetches.push(scene.cachedFetch(`./battle-anims/common-${commonAnimNames[ca].toLowerCase().replace(/\_/g, "-")}.json`) @@ -572,7 +573,7 @@ export function loadMoveAnimAssets(scene: BattleScene, moveIds: Moves[], startLo const chargeAttr = allMoves[moveId].getAttrs(ChargeAttr)[0] || allMoves[moveId].getAttrs(DelayedAttackAttr)[0]; if (chargeAttr) { const moveChargeAnims = chargeAnims.get(chargeAttr.chargeAnim); - moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims[0]); + moveAnimations.push(moveChargeAnims instanceof AnimConfig ? moveChargeAnims : moveChargeAnims![0]); // TODO: is the bang correct? if (Array.isArray(moveChargeAnims)) { moveAnimations.push(moveChargeAnims[1]); } @@ -668,21 +669,21 @@ interface SpriteCache { } export abstract class BattleAnim { - public user: Pokemon; - public target: Pokemon; + public user: Pokemon | null; + public target: Pokemon | null; public sprites: Phaser.GameObjects.Sprite[]; public bgSprite: Phaser.GameObjects.TileSprite | Phaser.GameObjects.Rectangle; private srcLine: number[]; private dstLine: number[]; - constructor(user: Pokemon, target: Pokemon) { - this.user = user; - this.target = target; + constructor(user?: Pokemon, target?: Pokemon) { + this.user = user!; // TODO: is this bang correct? + this.target = target!; // TODO: is this bang correct? this.sprites = []; } - abstract getAnim(): AnimConfig; + abstract getAnim(): AnimConfig | null; abstract isOppAnim(): boolean; @@ -705,12 +706,12 @@ export abstract class BattleAnim { const user = !isOppAnim ? this.user : this.target; const target = !isOppAnim ? this.target : this.user; - const userInitialX = user.x; - const userInitialY = user.y; - const userHalfHeight = user.getSprite().displayHeight / 2; - const targetInitialX = target.x; - const targetInitialY = target.y; - const targetHalfHeight = target.getSprite().displayHeight / 2; + const userInitialX = user!.x; // TODO: is this bang correct? + const userInitialY = user!.y; // TODO: is this bang correct? + const userHalfHeight = user!.getSprite().displayHeight! / 2; // TODO: is this bang correct? + const targetInitialX = target!.x; // TODO: is this bang correct? + const targetInitialY = target!.y; // TODO: is this bang correct? + const targetHalfHeight = target!.getSprite().displayHeight! / 2; // TODO: is this bang correct? let g = 0; let u = 0; @@ -742,7 +743,7 @@ export abstract class BattleAnim { } const angle = -frame.angle; const key = frame.target === AnimFrameTarget.GRAPHIC ? g++ : frame.target === AnimFrameTarget.USER ? u++ : t++; - ret.get(frame.target).set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); + ret.get(frame.target)!.set(key, { x: x, y: y, scaleX: scaleX, scaleY: scaleY, angle: angle }); // TODO: is the bang correct? } return ret; @@ -750,10 +751,10 @@ export abstract class BattleAnim { play(scene: BattleScene, callback?: Function) { const isOppAnim = this.isOppAnim(); - const user = !isOppAnim ? this.user : this.target; + const user = !isOppAnim ? this.user! : this.target!; // TODO: are those bangs correct? const target = !isOppAnim ? this.target : this.user; - if (!target.isOnField()) { + if (!target?.isOnField()) { if (callback) { callback(); } @@ -781,7 +782,7 @@ export abstract class BattleAnim { targetSprite.setAlpha(1); targetSprite.pipelineData["tone"] = [ 0.0, 0.0, 0.0, 0.0 ]; targetSprite.setAngle(0); - if (!this.isHideUser()) { + if (!this.isHideUser() && userSprite) { userSprite.setVisible(true); } if (!this.isHideTarget() && (targetSprite !== userSprite || !this.isHideUser())) { @@ -814,20 +815,20 @@ export abstract class BattleAnim { this.srcLine = [ userFocusX, userFocusY, targetFocusX, targetFocusY ]; this.dstLine = [ userInitialX, userInitialY, targetInitialX, targetInitialY ]; - let r = anim.frames.length; + let r = anim!.frames.length; // TODO: is this bang correct? let f = 0; scene.tweens.addCounter({ duration: Utils.getFrameMs(3), - repeat: anim.frames.length, + repeat: anim!.frames.length, // TODO: is this bang correct? onRepeat: () => { if (!f) { userSprite.setVisible(false); targetSprite.setVisible(false); } - const spriteFrames = anim.frames[f]; - const frameData = this.getGraphicFrameData(scene, anim.frames[f]); + const spriteFrames = anim!.frames[f]; // TODO: is the bang correcT? + const frameData = this.getGraphicFrameData(scene, anim!.frames[f]); // TODO: is the bang correct? let u = 0; let t = 0; let g = 0; @@ -840,9 +841,9 @@ export abstract class BattleAnim { const sprites = spriteCache[isUser ? AnimFrameTarget.USER : AnimFrameTarget.TARGET]; const spriteSource = isUser ? userSprite : targetSprite; if ((isUser ? u : t) === sprites.length) { - const sprite = scene.addPokemonSprite(isUser ? user : target, 0, 0, spriteSource.texture, spriteSource.frame.name, true); - [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user : target).getSprite().pipelineData[k]); - sprite.setPipelineData("spriteKey", (isUser ? user : target).getBattleSpriteKey()); + const sprite = scene.addPokemonSprite(isUser ? user! : target, 0, 0, spriteSource!.texture, spriteSource!.frame.name, true); // TODO: are those bangs correct? + [ "spriteColors", "fusionSpriteColors" ].map(k => sprite.pipelineData[k] = (isUser ? user! : target).getSprite().pipelineData[k]); // TODO: are those bangs correct? + sprite.setPipelineData("spriteKey", (isUser ? user! : target).getBattleSpriteKey()); sprite.setPipelineData("shiny", (isUser ? user : target).shiny); sprite.setPipelineData("variant", (isUser ? user : target).variant); sprite.setPipelineData("ignoreFieldPos", true); @@ -853,7 +854,7 @@ export abstract class BattleAnim { const spriteIndex = isUser ? u++ : t++; const pokemonSprite = sprites[spriteIndex]; - const graphicFrameData = frameData.get(frame.target).get(spriteIndex); + const graphicFrameData = frameData.get(frame.target)!.get(spriteIndex)!; // TODO: are the bangs correct? pokemonSprite.setPosition(graphicFrameData.x, graphicFrameData.y - ((spriteSource.height / 2) * (spriteSource.parentContainer.scale - 1))); pokemonSprite.setAngle(graphicFrameData.angle); @@ -868,7 +869,7 @@ export abstract class BattleAnim { } else { const sprites = spriteCache[AnimFrameTarget.GRAPHIC]; if (g === sprites.length) { - const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim.graphic, 1); + const newSprite: Phaser.GameObjects.Sprite = scene.addFieldSprite(0, 0, anim!.graphic, 1); // TODO: is the bang correct? sprites.push(newSprite); scene.field.add(newSprite); spritePriorities.push(1); @@ -881,7 +882,7 @@ export abstract class BattleAnim { const setSpritePriority = (priority: integer) => { switch (priority) { case 0: - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, scene.getEnemyPokemon() || scene.getPlayerPokemon()!); // TODO: is this bang correct? break; case 1: scene.field.moveTo(moveSprite, scene.field.getAll().length - 1); @@ -892,11 +893,11 @@ export abstract class BattleAnim { if (this.bgSprite) { scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.bgSprite); } else { - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? } break; case AnimFocus.TARGET: - scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target); + scene.field.moveBelow(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? break; default: setSpritePriority(1); @@ -906,10 +907,10 @@ export abstract class BattleAnim { case 3: switch (frame.focus) { case AnimFocus.USER: - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user); + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.user!); // TODO: is this bang correct? break; case AnimFocus.TARGET: - scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target); + scene.field.moveAbove(moveSprite as Phaser.GameObjects.GameObject, this.target!); // TODO: is this bang correct? break; default: setSpritePriority(1); @@ -925,7 +926,7 @@ export abstract class BattleAnim { moveSprite.setFrame(frame.graphicFrame); //console.log(AnimFocus[frame.focus]); - const graphicFrameData = frameData.get(frame.target).get(graphicIndex); + const graphicFrameData = frameData.get(frame.target)!.get(graphicIndex)!; // TODO: are those bangs correct? moveSprite.setPosition(graphicFrameData.x, graphicFrameData.y); moveSprite.setAngle(graphicFrameData.angle); moveSprite.setScale(graphicFrameData.scaleX, graphicFrameData.scaleY); @@ -935,8 +936,8 @@ export abstract class BattleAnim { moveSprite.setBlendMode(frame.blendType === AnimBlendType.NORMAL ? Phaser.BlendModes.NORMAL : frame.blendType === AnimBlendType.ADD ? Phaser.BlendModes.ADD : Phaser.BlendModes.DIFFERENCE); } } - if (anim.frameTimedEvents.has(f)) { - for (const event of anim.frameTimedEvents.get(f)) { + if (anim?.frameTimedEvents.has(f)) { + for (const event of anim.frameTimedEvents.get(f)!) { // TODO: is this bang correct? r = Math.max((anim.frames.length - f) + event.execute(scene, this), r); } } @@ -980,16 +981,16 @@ export abstract class BattleAnim { } export class CommonBattleAnim extends BattleAnim { - public commonAnim: CommonAnim; + public commonAnim: CommonAnim | null; - constructor(commonAnim: CommonAnim, user: Pokemon, target?: Pokemon) { + constructor(commonAnim: CommonAnim | null, user: Pokemon, target?: Pokemon) { super(user, target || user); this.commonAnim = commonAnim; } - getAnim(): AnimConfig { - return commonAnims.get(this.commonAnim); + getAnim(): AnimConfig | null { + return this.commonAnim ? commonAnims.get(this.commonAnim)! : null; // TODO: is this bang correct? } isOppAnim(): boolean { @@ -1009,11 +1010,11 @@ export class MoveAnim extends BattleAnim { getAnim(): AnimConfig { return moveAnims.get(this.move) instanceof AnimConfig ? moveAnims.get(this.move) as AnimConfig - : moveAnims.get(this.move)[this.user.isPlayer() ? 0 : 1] as AnimConfig; + : moveAnims.get(this.move)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct? } isOppAnim(): boolean { - return !this.user.isPlayer() && Array.isArray(moveAnims.get(this.move)); + return !this.user?.isPlayer() && Array.isArray(moveAnims.get(this.move)); } protected isHideUser(): boolean { @@ -1035,13 +1036,13 @@ export class MoveChargeAnim extends MoveAnim { } isOppAnim(): boolean { - return !this.user.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim)); + return !this.user?.isPlayer() && Array.isArray(chargeAnims.get(this.chargeAnim)); } getAnim(): AnimConfig { return chargeAnims.get(this.chargeAnim) instanceof AnimConfig ? chargeAnims.get(this.chargeAnim) as AnimConfig - : chargeAnims.get(this.chargeAnim)[this.user.isPlayer() ? 0 : 1] as AnimConfig; + : chargeAnims.get(this.chargeAnim)![this.user?.isPlayer() ? 0 : 1] as AnimConfig; // TODO: is this bang correct? } } @@ -1059,19 +1060,19 @@ export async function populateAnims() { moveNameToId[moveName] = move; } - const seNames = [];//(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString()); + const seNames: string[] = [];//(await fs.readdir('./public/audio/se/battle_anims/')).map(se => se.toString()); - const animsData = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1); + const animsData : any[] = [];//battleAnimRawData.split('!ruby/array:PBAnimation').slice(1); // TODO: add a proper type for (let a = 0; a < animsData.length; a++) { const fields = animsData[a].split("@").slice(1); const nameField = fields.find(f => f.startsWith("name: ")); - let isOppMove: boolean; - let commonAnimId: CommonAnim; - let chargeAnimId: ChargeAnim; + let isOppMove: boolean | undefined; + let commonAnimId: CommonAnim | undefined; + let chargeAnimId: ChargeAnim | undefined; if (!nameField.startsWith("name: Move:") && !(isOppMove = nameField.startsWith("name: OppMove:"))) { - const nameMatch = commonNamePattern.exec(nameField); + const nameMatch = commonNamePattern.exec(nameField)!; // TODO: is this bang correct? const name = nameMatch[2].toLowerCase(); if (commonAnimMatchNames.indexOf(name) > -1) { commonAnimId = commonAnimIds[commonAnimMatchNames.indexOf(name)]; @@ -1128,14 +1129,14 @@ export async function populateAnims() { for (let t = 0; t < timingEntries.length; t++) { const timingData = timingEntries[t].replace(/\n/g, " ").replace(/[ ]{2,}/g, " ").replace(/[a-z]+: ! '', /ig, "").replace(/name: (.*?),/, "name: \"$1\",") .replace(/flashColor: !ruby\/object:Color { alpha: ([\d\.]+), blue: ([\d\.]+), green: ([\d\.]+), red: ([\d\.]+)}/, "flashRed: $4, flashGreen: $3, flashBlue: $2, flashAlpha: $1"); - const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)[1]); - let resourceName = /name: "(.*?)"/.exec(timingData)[1].replace("''", ""); - const timingType = parseInt(/timingType: (\d)/.exec(timingData)[1]); - let timedEvent: AnimTimedEvent; + const frameIndex = parseInt(/frame: (\d+)/.exec(timingData)![1]); // TODO: is the bang correct? + let resourceName = /name: "(.*?)"/.exec(timingData)![1].replace("''", ""); // TODO: is the bang correct? + const timingType = parseInt(/timingType: (\d)/.exec(timingData)![1]); // TODO: is the bang correct? + let timedEvent: AnimTimedEvent | undefined; switch (timingType) { case 0: if (resourceName && resourceName.indexOf(".") === -1) { - let ext: string; + let ext: string | undefined; [ "wav", "mp3", "m4a" ].every(e => { if (seNames.indexOf(`${resourceName}.${e}`) > -1) { ext = e; @@ -1162,7 +1163,7 @@ export async function populateAnims() { } const propPattern = /([a-z]+): (.*?)(?:,|\})/ig; let propMatch: RegExpExecArray; - while ((propMatch = propPattern.exec(timingData))) { + while ((propMatch = propPattern.exec(timingData)!)) { // TODO: is this bang correct? const prop = propMatch[1]; let value: any = propMatch[2]; switch (prop) { @@ -1194,7 +1195,7 @@ export async function populateAnims() { if (!anim.frameTimedEvents.has(frameIndex)) { anim.frameTimedEvents.set(frameIndex, []); } - anim.frameTimedEvents.get(frameIndex).push(timedEvent); + anim.frameTimedEvents.get(frameIndex)!.push(timedEvent); // TODO: is this bang correct? } break; case "position": diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index dd5ab8938ae..c25407d0599 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -36,11 +36,11 @@ export class BattlerTag { public sourceMove: Moves; public sourceId?: number; - constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove: Moves, sourceId?: number) { + constructor(tagType: BattlerTagType, lapseType: BattlerTagLapseType | BattlerTagLapseType[], turnCount: number, sourceMove?: Moves, sourceId?: number) { this.tagType = tagType; this.lapseTypes = Array.isArray(lapseType) ? lapseType : [ lapseType ]; this.turnCount = turnCount; - this.sourceMove = sourceMove; + this.sourceMove = sourceMove!; // TODO: is this bang correct? this.sourceId = sourceId; } @@ -66,7 +66,7 @@ export class BattlerTag { return false; } - getMoveName(): string { + getMoveName(): string | null { return this.sourceMove ? allMoves[this.sourceMove].name : null; @@ -299,12 +299,12 @@ export class DestinyBondTag extends BattlerTag { if (lapseType !== BattlerTagLapseType.CUSTOM) { return super.lapse(pokemon, lapseType); } - const source = pokemon.scene.getPokemonById(this.sourceId); - if (!source.isFainted()) { + const source = this.sourceId ? pokemon.scene.getPokemonById(this.sourceId) : null; + if (!source?.isFainted()) { return true; } - if (source.getAlly() === pokemon) { + if (source?.getAlly() === pokemon) { return false; } @@ -330,7 +330,19 @@ export class InfatuatedTag extends BattlerTag { } canAdd(pokemon: Pokemon): boolean { - return pokemon.isOppositeGender(pokemon.scene.getPokemonById(this.sourceId)); + if (this.sourceId) { + const pkm = pokemon.scene.getPokemonById(this.sourceId); + + if (pkm) { + return pokemon.isOppositeGender(pkm); + } else { + console.warn("canAdd: this.sourceId is not a valid pokemon id!", this.sourceId); + return false; + } + } else { + console.warn("canAdd: this.sourceId is undefined"); + return false; + } } onAdd(pokemon: Pokemon): void { @@ -339,7 +351,7 @@ export class InfatuatedTag extends BattlerTag { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsInfatuatedOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined) // TODO: is that bang correct? }) ); } @@ -357,7 +369,7 @@ export class InfatuatedTag extends BattlerTag { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsInfatuatedLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined) // TODO: is that bang correct? }) ); pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), undefined, CommonAnim.ATTRACT)); @@ -410,7 +422,7 @@ export class SeedTag extends BattlerTag { super.onAdd(pokemon); pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSeededOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -556,11 +568,11 @@ export class EncoreTag extends BattlerTag { const movePhase = pokemon.scene.findPhase(m => m instanceof MovePhase && m.pokemon === pokemon); if (movePhase) { - const movesetMove = pokemon.getMoveset().find(m => m.moveId === this.moveId); + const movesetMove = pokemon.getMoveset().find(m => m!.moveId === this.moveId); // TODO: is this bang correct? if (movesetMove) { const lastMove = pokemon.getLastXMoves(1)[0]; pokemon.scene.tryReplacePhase((m => m instanceof MovePhase && m.pokemon === pokemon), - new MovePhase(pokemon.scene, pokemon, lastMove.targets, movesetMove)); + new MovePhase(pokemon.scene, pokemon, lastMove.targets!, movesetMove)); // TODO: is this bang correct? } } } @@ -580,7 +592,7 @@ export class HelpingHandTag extends BattlerTag { onAdd(pokemon: Pokemon): void { pokemon.scene.queueMessage( i18next.t("battle:battlerTagsHelpingHandOnAdd", { - pokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + pokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? pokemonName: getPokemonNameWithAffix(pokemon) }) ); @@ -800,7 +812,7 @@ export class BindTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsBindOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? moveName: this.getMoveName() }); } @@ -814,7 +826,7 @@ export class WrapTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsWrapOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonName: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -848,7 +860,7 @@ export class ClampTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsClampOnTrap", { - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)), + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? pokemonName: getPokemonNameWithAffix(pokemon), }); } @@ -895,7 +907,7 @@ export class ThunderCageTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsThunderCageOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -908,7 +920,7 @@ export class InfestationTag extends DamagingTrapTag { getTrapMessage(pokemon: Pokemon): string { return i18next.t("battle:battlerTagsInfestationOnTrap", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), - sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId)) + sourcePokemonNameWithAffix: getPokemonNameWithAffix(pokemon.scene.getPokemonById(this.sourceId!) ?? undefined), // TODO: is that bang correct? }); } } @@ -1242,6 +1254,7 @@ export class HighestStatBoostTag extends AbilityBattlerTag { return highestValue; }, 0); + highestStat = highestStat!; // tell TS compiler it's defined! this.stat = highestStat; switch (this.stat) { @@ -1427,7 +1440,7 @@ export class SaltCuredTag extends BattlerTag { super.onAdd(pokemon); pokemon.scene.queueMessage(i18next.t("battle:battlerTagsSaltCuredOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) })); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { @@ -1474,7 +1487,7 @@ export class CursedTag extends BattlerTag { onAdd(pokemon: Pokemon): void { super.onAdd(pokemon); - this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId).getBattlerIndex(); + this.sourceIndex = pokemon.scene.getPokemonById(this.sourceId!)!.getBattlerIndex(); // TODO: are those bangs correct? } lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { diff --git a/src/data/berry.ts b/src/data/berry.ts index 5ca64544544..30b89848452 100644 --- a/src/data/berry.ts +++ b/src/data/berry.ts @@ -54,7 +54,7 @@ export function getBerryPredicate(berryType: BerryType): BerryPredicate { return (pokemon: Pokemon) => { const threshold = new Utils.NumberHolder(0.25); applyAbAttrs(ReduceBerryUseThresholdAbAttr, pokemon, null, threshold); - return !!pokemon.getMoveset().find(m => !m.getPpRatio()); + return !!pokemon.getMoveset().find(m => !m?.getPpRatio()); }; } } @@ -120,10 +120,10 @@ export function getBerryEffectFunc(berryType: BerryType): BerryEffectFunc { if (pokemon.battleData) { pokemon.battleData.berriesEaten.push(berryType); } - const ppRestoreMove = pokemon.getMoveset().find(m => !m.getPpRatio()) ? pokemon.getMoveset().find(m => !m.getPpRatio()) : pokemon.getMoveset().find(m => m.getPpRatio() < 1); + const ppRestoreMove = pokemon.getMoveset().find(m => !m?.getPpRatio()) ? pokemon.getMoveset().find(m => !m?.getPpRatio()) : pokemon.getMoveset().find(m => m!.getPpRatio() < 1); // TODO: is this bang correct? if (ppRestoreMove !== undefined) { - ppRestoreMove.ppUsed = Math.max(ppRestoreMove.ppUsed - 10, 0); - pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove.getName(), berryName: getBerryName(berryType) })); + ppRestoreMove!.ppUsed = Math.max(ppRestoreMove!.ppUsed - 10, 0); + pokemon.scene.queueMessage(i18next.t("battle:ppHealBerry", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon), moveName: ppRestoreMove!.getName(), berryName: getBerryName(berryType) })); } }; } diff --git a/src/data/biomes.ts b/src/data/biomes.ts index 479e0994cb6..ed579112249 100644 --- a/src/data/biomes.ts +++ b/src/data/biomes.ts @@ -7705,7 +7705,7 @@ export function initBiomes() { ? pokemonEvolutions[speciesId] : []; - if (!biomeEntries.filter(b => b[0] !== Biome.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId))[3] as any[]).filter(b => b[0] !== Biome.END).length).length) { + if (!biomeEntries.filter(b => b[0] !== Biome.END).length && !speciesEvolutions.filter(es => !!((pokemonBiomes.find(p => p[0] === es.speciesId)!)[3] as any[]).filter(b => b[0] !== Biome.END).length).length) { // TODO: is the bang on the `find()` correct? uncatchableSpecies.push(speciesId); } diff --git a/src/data/challenge.ts b/src/data/challenge.ts index 7bea68e36c7..5f17fdedb78 100644 --- a/src/data/challenge.ts +++ b/src/data/challenge.ts @@ -412,7 +412,7 @@ export class SingleGenerationChallenge extends Challenge { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); generations.push(getPokemonSpecies(e.speciesId).generation); @@ -430,7 +430,7 @@ export class SingleGenerationChallenge extends Challenge { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { const baseGeneration = pokemon.species.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.species.speciesId).generation; - const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies.speciesId).generation : 0; + const fusionGeneration = pokemon.isFusion() ? pokemon.fusionSpecies?.speciesId === Species.VICTINI ? 5 : getPokemonSpecies(pokemon.fusionSpecies!.speciesId).generation : 0; // TODO: is the bang on fusionSpecies correct? if (pokemon.isPlayer() && (baseGeneration !== this.value || (pokemon.isFusion() && fusionGeneration !== this.value))) { valid.value = false; return true; @@ -542,13 +542,13 @@ export class SingleTypeChallenge extends Challenge { const speciesToCheck = [pokemon.speciesId]; while (speciesToCheck.length) { const checking = speciesToCheck.pop(); - if (pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { + if (checking && pokemonEvolutions.hasOwnProperty(checking) && checkPokemonEvolutions) { pokemonEvolutions[checking].forEach(e => { speciesToCheck.push(e.speciesId); types.push(getPokemonSpecies(e.speciesId).type1, getPokemonSpecies(e.speciesId).type2); }); } - if (pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { + if (checking && pokemonFormChanges.hasOwnProperty(checking) && checkPokemonForms) { pokemonFormChanges[checking].forEach(f1 => { getPokemonSpecies(checking).forms.forEach(f2 => { if (f1.formKey === f2.formKey) { @@ -568,10 +568,11 @@ export class SingleTypeChallenge extends Challenge { applyPokemonInBattle(pokemon: Pokemon, valid: Utils.BooleanHolder): boolean { if (pokemon.isPlayer() && !pokemon.isOfType(this.value - 1, false, false, true) - && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies : pokemon.species).speciesId === o.species)) { + && !SingleTypeChallenge.TYPE_OVERRIDES.some(o => o.type === (this.value - 1) && (pokemon.isFusion() && o.fusion ? pokemon.fusionSpecies! : pokemon.species).speciesId === o.species)) { // TODO: is the bang on fusionSpecies correct? valid.value = false; return true; } + return false; } /** diff --git a/src/data/daily-run.ts b/src/data/daily-run.ts index 4b741b6f473..b7e03b4b189 100644 --- a/src/data/daily-run.ts +++ b/src/data/daily-run.ts @@ -11,15 +11,15 @@ export interface DailyRunConfig { starters: Starter; } -export function fetchDailyRunSeed(): Promise { - return new Promise((resolve, reject) => { +export function fetchDailyRunSeed(): Promise { + return new Promise((resolve, reject) => { Utils.apiFetch("daily/seed").then(response => { if (!response.ok) { resolve(null); return; } return response.text(); - }).then(seed => resolve(seed)) + }).then(seed => resolve(seed!)) // TODO: is this bang correct? .catch(err => reject(err)); }); } diff --git a/src/data/egg.ts b/src/data/egg.ts index cb889951488..9c76591f01b 100644 --- a/src/data/egg.ts +++ b/src/data/egg.ts @@ -140,30 +140,30 @@ export class Egg { constructor(eggOptions?: IEggOptions) { //if (eggOptions.tier && eggOptions.species) throw Error("Error egg can't have species and tier as option. only choose one of them.") - this._sourceType = eggOptions.sourceType ?? undefined; + this._sourceType = eggOptions?.sourceType!; // TODO: is this bang correct? // Ensure _sourceType is defined before invoking rollEggTier(), as it is referenced - this._tier = eggOptions.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); + this._tier = eggOptions?.tier ?? (Overrides.EGG_TIER_OVERRIDE ?? this.rollEggTier()); // If egg was pulled, check if egg pity needs to override the egg tier - if (eggOptions.pulled) { + if (eggOptions?.pulled) { // Needs this._tier and this._sourceType to work - this.checkForPityTierOverrides(eggOptions.scene); + this.checkForPityTierOverrides(eggOptions.scene!); // TODO: is this bang correct? } - this._id = eggOptions.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); + this._id = eggOptions?.id ?? Utils.randInt(EGG_SEED, EGG_SEED * this._tier); - this._sourceType = eggOptions.sourceType ?? undefined; - this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); - this._timestamp = eggOptions.timestamp ?? new Date().getTime(); + this._sourceType = eggOptions?.sourceType ?? undefined; + this._hatchWaves = eggOptions?.hatchWaves ?? this.getEggTierDefaultHatchWaves(); + this._timestamp = eggOptions?.timestamp ?? new Date().getTime(); // First roll shiny and variant so we can filter if species with an variant exist - this._isShiny = eggOptions.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); - this._variantTier = eggOptions.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); - this._species = eggOptions.species ?? this.rollSpecies(eggOptions.scene); + this._isShiny = eggOptions?.isShiny ?? (Overrides.EGG_SHINY_OVERRIDE || this.rollShiny()); + this._variantTier = eggOptions?.variantTier ?? (Overrides.EGG_VARIANT_OVERRIDE ?? this.rollVariant()); + this._species = eggOptions?.species ?? this.rollSpecies(eggOptions!.scene!)!; // TODO: Are those bangs correct? - this._overrideHiddenAbility = eggOptions.overrideHiddenAbility ?? false; + this._overrideHiddenAbility = eggOptions?.overrideHiddenAbility ?? false; // Override egg tier and hatchwaves if species was given - if (eggOptions.species) { + if (eggOptions?.species) { this._tier = this.getEggTierFromSpeciesStarterValue(); this._hatchWaves = eggOptions.hatchWaves ?? this.getEggTierDefaultHatchWaves(); } @@ -174,10 +174,10 @@ export class Egg { this._variantTier = VariantTier.COMMON; } // Needs this._tier so it needs to be generated afer the tier override if bought from same species - this._eggMoveIndex = eggOptions.eggMoveIndex ?? this.rollEggMoveIndex(); - if (eggOptions.pulled) { - this.increasePullStatistic(eggOptions.scene); - this.addEggToGameData(eggOptions.scene); + this._eggMoveIndex = eggOptions?.eggMoveIndex ?? this.rollEggMoveIndex(); + if (eggOptions?.pulled) { + this.increasePullStatistic(eggOptions.scene!); // TODO: is this bang correct? + this.addEggToGameData(eggOptions.scene!); // TODO: is this bang correct? } } @@ -202,7 +202,7 @@ export class Egg { // Legacy egg wants to hatch. Generate missing properties if (!this._species) { this._isShiny = this.rollShiny(); - this._species = this.rollSpecies(scene); + this._species = this.rollSpecies(scene!)!; // TODO: are these bangs correct? } let pokemonSpecies = getPokemonSpecies(this._species); @@ -213,7 +213,7 @@ export class Egg { // Sets the hidden ability if a hidden ability exists and the override is set // or if the same species egg hits the chance - let abilityIndex = undefined; + let abilityIndex: number | undefined = undefined; if (pokemonSpecies.abilityHidden && (this._overrideHiddenAbility || (this._sourceType === EggSourceType.SAME_SPECIES_EGG && !Utils.randSeedInt(SAME_SPECIES_EGG_HA_RATE)))) { abilityIndex = 2; @@ -277,6 +277,9 @@ export class Egg { return i18next.t("egg:gachaTypeShiny"); case EggSourceType.GACHA_MOVE: return i18next.t("egg:gachaTypeMove"); + default: + console.warn("getEggTypeDescriptor case not defined. Returning default empty string"); + return ""; } } @@ -326,9 +329,9 @@ export class Egg { return tierValue >= 52 + tierValueOffset ? EggTier.COMMON : tierValue >= 8 + tierValueOffset ? EggTier.GREAT : tierValue >= 1 + tierValueOffset ? EggTier.ULTRA : EggTier.MASTER; } - private rollSpecies(scene: BattleScene): Species { + private rollSpecies(scene: BattleScene): Species | null { if (!scene) { - return undefined; + return null; } /** * Manaphy eggs have a 1/8 chance of being Manaphy and 7/8 chance of being Phione @@ -400,7 +403,7 @@ export class Egg { * and being the same each time */ let totalWeight = 0; - const speciesWeights = []; + const speciesWeights : number[] = []; for (const speciesId of speciesPool) { let weight = Math.floor((((maxStarterValue - speciesStarters[speciesId]) / ((maxStarterValue - minStarterValue) + 1)) * 1.5 + 1) * 100); const species = getPokemonSpecies(speciesId); @@ -420,6 +423,7 @@ export class Egg { break; } } + species = species!; // tell TS compiled it's defined now! if (!!scene.gameData.dexData[species].caughtAttr || scene.gameData.eggs.some(e => e.species === species)) { scene.gameData.unlockPity[this.tier] = Math.min(scene.gameData.unlockPity[this.tier] + 1, 10); @@ -517,6 +521,8 @@ export class Egg { if (speciesStartValue >= 8) { return EggTier.MASTER; } + + return EggTier.COMMON; } //// @@ -541,6 +547,7 @@ export function getLegendaryGachaSpeciesForTimestamp(scene: BattleScene, timesta scene.executeWithSeedOffset(() => { ret = Phaser.Math.RND.shuffle(legendarySpecies)[index]; }, offset, EGG_SEED.toString()); + ret = ret!; // tell TS compiler it's return ret; } diff --git a/src/data/move.ts b/src/data/move.ts index 589c169805d..b5ca217a73e 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -180,7 +180,7 @@ export default class Move implements Localizable { * @returns the first {@linkcode MoveAttr} element in attrs that makes the input function return true */ findAttr(attrPredicate: (attr: MoveAttr) => boolean): MoveAttr { - return this.attrs.find(attrPredicate); + return this.attrs.find(attrPredicate)!; // TODO: is the bang correct? } /** @@ -361,7 +361,7 @@ export default class Move implements Localizable { * @param makesContact The value (boolean) to set the flag to * @returns The {@linkcode Move} that called this function */ - makesContact(makesContact?: boolean): this { + makesContact(makesContact: boolean = true): this { // TODO: is true the correct default? this.setFlag(MoveFlags.MAKES_CONTACT, makesContact); return this; } @@ -372,7 +372,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.CURSE} * @returns The {@linkcode Move} that called this function */ - ignoresProtect(ignoresProtect?: boolean): this { + ignoresProtect(ignoresProtect: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_PROTECT, ignoresProtect); return this; } @@ -383,7 +383,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.NATURE_POWER} * @returns The {@linkcode Move} that called this function */ - ignoresVirtual(ignoresVirtual?: boolean): this { + ignoresVirtual(ignoresVirtual: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_VIRTUAL, ignoresVirtual); return this; } @@ -394,7 +394,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.UPROAR} * @returns The {@linkcode Move} that called this function */ - soundBased(soundBased?: boolean): this { + soundBased(soundBased: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.SOUND_BASED, soundBased); return this; } @@ -405,7 +405,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TELEPORT} * @returns The {@linkcode Move} that called this function */ - hidesUser(hidesUser?: boolean): this { + hidesUser(hidesUser: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.HIDE_USER, hidesUser); return this; } @@ -416,7 +416,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WHIRLWIND} * @returns The {@linkcode Move} that called this function */ - hidesTarget(hidesTarget?: boolean): this { + hidesTarget(hidesTarget: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.HIDE_TARGET, hidesTarget); return this; } @@ -427,7 +427,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.BITE} * @returns The {@linkcode Move} that called this function */ - bitingMove(bitingMove?: boolean): this { + bitingMove(bitingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.BITING_MOVE, bitingMove); return this; } @@ -438,7 +438,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.WATER_PULSE} * @returns The {@linkcode Move} that called this function */ - pulseMove(pulseMove?: boolean): this { + pulseMove(pulseMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.PULSE_MOVE, pulseMove); return this; } @@ -449,7 +449,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.DRAIN_PUNCH} * @returns The {@linkcode Move} that called this function */ - punchingMove(punchingMove?: boolean): this { + punchingMove(punchingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.PUNCHING_MOVE, punchingMove); return this; } @@ -460,7 +460,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.X_SCISSOR} * @returns The {@linkcode Move} that called this function */ - slicingMove(slicingMove?: boolean): this { + slicingMove(slicingMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.SLICING_MOVE, slicingMove); return this; } @@ -471,7 +471,7 @@ export default class Move implements Localizable { * @param recklessMove The value to set the flag to * @returns The {@linkcode Move} that called this function */ - recklessMove(recklessMove?: boolean): this { + recklessMove(recklessMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.RECKLESS_MOVE, recklessMove); return this; } @@ -482,7 +482,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ELECTRO_BALL} * @returns The {@linkcode Move} that called this function */ - ballBombMove(ballBombMove?: boolean): this { + ballBombMove(ballBombMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.BALLBOMB_MOVE, ballBombMove); return this; } @@ -493,7 +493,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.STUN_SPORE} * @returns The {@linkcode Move} that called this function */ - powderMove(powderMove?: boolean): this { + powderMove(powderMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.POWDER_MOVE, powderMove); return this; } @@ -504,7 +504,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.PETAL_DANCE} * @returns The {@linkcode Move} that called this function */ - danceMove(danceMove?: boolean): this { + danceMove(danceMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.DANCE_MOVE, danceMove); return this; } @@ -515,7 +515,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.HURRICANE} * @returns The {@linkcode Move} that called this function */ - windMove(windMove?: boolean): this { + windMove(windMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.WIND_MOVE, windMove); return this; } @@ -526,7 +526,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.ABSORB} * @returns The {@linkcode Move} that called this function */ - triageMove(triageMove?: boolean): this { + triageMove(triageMove: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.TRIAGE_MOVE, triageMove); return this; } @@ -537,7 +537,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.SUNSTEEL_STRIKE} * @returns The {@linkcode Move} that called this function */ - ignoresAbilities(ignoresAbilities?: boolean): this { + ignoresAbilities(ignoresAbilities: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.IGNORE_ABILITIES, ignoresAbilities); return this; } @@ -548,7 +548,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.TRIPLE_AXEL} * @returns The {@linkcode Move} that called this function */ - checkAllHits(checkAllHits?: boolean): this { + checkAllHits(checkAllHits: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.CHECK_ALL_HITS, checkAllHits); return this; } @@ -559,7 +559,7 @@ export default class Move implements Localizable { * example: @see {@linkcode Moves.METAL_BURST} * @returns The {@linkcode Move} that called this function */ - redirectCounter(redirectCounter?: boolean): this { + redirectCounter(redirectCounter: boolean = true): this { // TODO: is `true` the correct default? this.setFlag(MoveFlags.REDIRECT_COUNTER, redirectCounter); return this; } @@ -571,7 +571,7 @@ export default class Move implements Localizable { * @param target {@linkcode Pokemon} the Pokemon receiving the move * @returns boolean */ - checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon): boolean { + checkFlag(flag: MoveFlags, user: Pokemon, target: Pokemon | null): boolean { // special cases below, eg: if the move flag is MAKES_CONTACT, and the user pokemon has an ability that ignores contact (like "Long Reach"), then overrides and move does not make contact switch (flag) { case MoveFlags.MAKES_CONTACT: @@ -870,7 +870,7 @@ export abstract class MoveAttr { * @param args Set of unique arguments needed by this attribute * @returns true if application of the ability succeeds */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): boolean | Promise { return true; } @@ -878,7 +878,7 @@ export abstract class MoveAttr { * @virtual * @returns the {@linkcode MoveCondition} or {@linkcode MoveConditionFunc} for this {@linkcode Move} */ - getCondition(): MoveCondition | MoveConditionFunc { + getCondition(): MoveCondition | MoveConditionFunc | null { return null; } @@ -954,14 +954,14 @@ export class MoveEffectAttr extends MoveAttr { * @param args Set of unique arguments needed by this attribute * @returns true if basic application of the ability attribute should be possible */ - canApply(user: Pokemon, target: Pokemon, move: Move, args: any[]) { + canApply(user: Pokemon, target: Pokemon, move: Move, args?: any[]) { return !! (this.selfTarget ? user.hp && !user.getTag(BattlerTagType.FRENZY) : target.hp) && (this.selfTarget || !target.getTag(BattlerTagType.PROTECTED) || move.checkFlag(MoveFlags.IGNORE_PROTECT, user, target)); } /** Applies move effects so long as they are able based on {@linkcode canApply} */ - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise { return this.canApply(user, target, move, args); } @@ -1408,10 +1408,10 @@ export class PartyStatusCureAttr extends MoveEffectAttr { /** Skips mons with this ability, ie. Soundproof */ private abilityCondition: Abilities; - constructor(message: string, abilityCondition: Abilities) { + constructor(message: string | null, abilityCondition: Abilities) { super(); - this.message = message; + this.message = message!; // TODO: is this bang correct? this.abilityCondition = abilityCondition; } @@ -1428,6 +1428,7 @@ export class PartyStatusCureAttr extends MoveEffectAttr { return false; } this.addPartyCurePhase(user); + return true; } addPartyCurePhase(user: Pokemon) { @@ -1583,16 +1584,16 @@ export class SandHealAttr extends WeatherHealAttr { */ export class BoostHealAttr extends HealAttr { /** Healing received when {@linkcode condition} is false */ - private normalHealRatio?: number; + private normalHealRatio: number; /** Healing received when {@linkcode condition} is true */ - private boostedHealRatio?: number; + private boostedHealRatio: number; /** The lambda expression to check against when boosting the healing value */ private condition?: MoveConditionFunc; constructor(normalHealRatio?: number, boostedHealRatio?: number, showAnim?: boolean, selfTarget?: boolean, condition?: MoveConditionFunc) { super(normalHealRatio, showAnim, selfTarget); - this.normalHealRatio = normalHealRatio; - this.boostedHealRatio = boostedHealRatio; + this.normalHealRatio = normalHealRatio!; // TODO: is this bang correct? + this.boostedHealRatio = boostedHealRatio!; // TODO: is this bang correct? this.condition = condition; } @@ -1604,7 +1605,7 @@ export class BoostHealAttr extends HealAttr { * @returns true if the move was successful */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const healRatio = this.condition(user, target, move) ? this.boostedHealRatio : this.normalHealRatio; + const healRatio: number = (this.condition ? this.condition(user, target, move) : false) ? this.boostedHealRatio : this.normalHealRatio; this.addHealPhase(target, healRatio); return true; } @@ -1643,13 +1644,13 @@ export class HealOnAllyAttr extends HealAttr { export class HitHealAttr extends MoveEffectAttr { private healRatio: number; private message: string; - private healStat: Stat; + private healStat: Stat | null; - constructor(healRatio?: number, healStat?: Stat) { + constructor(healRatio?: number | null, healStat?: Stat) { super(true, MoveEffectTrigger.HIT); - this.healRatio = healRatio || 0.5; - this.healStat = healStat || null; + this.healRatio = healRatio!; // TODO: is this bang correct? + this.healStat = healStat!; // TODO: is this bang correct? } /** * Heals the user the determined amount and possibly displays a message about regaining health. @@ -1665,7 +1666,7 @@ export class HitHealAttr extends MoveEffectAttr { let healAmount = 0; let message = ""; const reverseDrain = target.hasAbilityWithAttr(ReverseDrainAbAttr, false); - if (this.healStat) { + if (this.healStat !== null) { // Strength Sap formula healAmount = target.getBattleStat(this.healStat); message = i18next.t("battle:drainMessage", {pokemonName: getPokemonNameWithAffix(target)}); @@ -1677,11 +1678,11 @@ export class HitHealAttr extends MoveEffectAttr { if (reverseDrain) { if (user.hasAbilityWithAttr(BlockNonDirectDamageAbAttr)) { healAmount = 0; - message = null; + message = ""; } else { user.turnData.damageTaken += healAmount; healAmount = healAmount * -1; - message = null; + message = ""; } } user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), healAmount, message, false, true)); @@ -1850,14 +1851,14 @@ export class WaterShurikenMultiHitTypeAttr extends ChangeMultiHitTypeAttr { export class StatusEffectAttr extends MoveEffectAttr { public effect: StatusEffect; - public cureTurn: integer; + public cureTurn: integer | null; public overrideStatus: boolean; constructor(effect: StatusEffect, selfTarget?: boolean, cureTurn?: integer, overrideStatus?: boolean) { super(selfTarget, MoveEffectTrigger.HIT); this.effect = effect; - this.cureTurn = cureTurn; + this.cureTurn = cureTurn!; // TODO: is this bang correct? this.overrideStatus = !!overrideStatus; } @@ -1875,7 +1876,7 @@ export class StatusEffectAttr extends MoveEffectAttr { } if ((!pokemon.status || (pokemon.status.effect === this.effect && moveChance < 0)) && pokemon.trySetStatus(this.effect, true, user, this.cureTurn)) { - applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null,this.effect); + applyPostAttackAbAttrs(ConfusionOnStatusEffectAbAttr, user, target, move, null, this.effect); return true; } } @@ -1914,12 +1915,13 @@ export class PsychoShiftEffectAttr extends MoveEffectAttr { } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { - const statusToApply: StatusEffect = user.status?.effect ?? (user.hasAbility(Abilities.COMATOSE) ? StatusEffect.SLEEP : undefined); + const statusToApply: StatusEffect | undefined = user.status?.effect ?? (user.hasAbility(Abilities.COMATOSE) ? StatusEffect.SLEEP : undefined); if (target.status) { return false; } - if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { + //@ts-ignore - how can target.status.effect be checked when we return `false` before when it's defined? + if (!target.status || (target.status.effect === statusToApply && move.chance < 0)) { // TODO: resolve ts-ignore const statusAfflictResult = target.trySetStatus(statusToApply, true, user); if (statusAfflictResult) { if (user.status) { @@ -1960,7 +1962,7 @@ export class StealHeldItemChanceAttr extends MoveEffectAttr { const heldItems = this.getTargetHeldItems(target).filter(i => i.isTransferrable); if (heldItems.length) { const poolType = target.isPlayer() ? ModifierPoolType.PLAYER : target.hasTrainer() ? ModifierPoolType.TRAINER : ModifierPoolType.WILD; - const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0); + const highestItemTier = heldItems.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is the bang after tier correct? const tierHeldItems = heldItems.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); const stolenItem = tierHeldItems[user.randSeedInt(tierHeldItems.length)]; user.scene.tryTransferHeldItemModifier(stolenItem, user, false).then(success => { @@ -2073,10 +2075,9 @@ export class RemoveHeldItemAttr extends MoveEffectAttr { * Attribute that causes targets of the move to eat a berry. Used for Teatime, Stuff Cheeks */ export class EatBerryAttr extends MoveEffectAttr { - protected chosenBerry: BerryModifier; + protected chosenBerry: BerryModifier | undefined; constructor() { super(true, MoveEffectTrigger.HIT); - this.chosenBerry = undefined; } /** * Causes the target to eat a berry. @@ -2111,16 +2112,16 @@ export class EatBerryAttr extends MoveEffectAttr { } reduceBerryModifier(target: Pokemon) { - if (this.chosenBerry.stackCount === 1) { + if (this.chosenBerry?.stackCount === 1) { target.scene.removeModifier(this.chosenBerry, !target.isPlayer()); - } else { + } else if (this.chosenBerry !== undefined && this.chosenBerry.stackCount > 1) { this.chosenBerry.stackCount--; } target.scene.updateModifiers(target.isPlayer()); } eatBerry(consumer: Pokemon) { - getBerryEffectFunc(this.chosenBerry.berryType)(consumer); // consumer eats the berry + getBerryEffectFunc(this.chosenBerry!.berryType)(consumer); // consumer eats the berry applyAbAttrs(HealFromBerryUseAbAttr, consumer, new Utils.BooleanHolder(false)); } } @@ -2351,20 +2352,20 @@ export class OverrideMoveEffectAttr extends MoveAttr { export class ChargeAttr extends OverrideMoveEffectAttr { public chargeAnim: ChargeAnim; private chargeText: string; - private tagType: BattlerTagType; + private tagType: BattlerTagType | null; private chargeEffect: boolean; public sameTurn: boolean; - public followUpPriority: integer; + public followUpPriority: integer | null; - constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType, chargeEffect: boolean = false, sameTurn: boolean = false, followUpPriority?: integer) { + constructor(chargeAnim: ChargeAnim, chargeText: string, tagType?: BattlerTagType | null, chargeEffect: boolean = false, sameTurn: boolean = false, followUpPriority?: integer) { super(); this.chargeAnim = chargeAnim; this.chargeText = chargeText; - this.tagType = tagType; + this.tagType = tagType!; // TODO: is this bang correct? this.chargeEffect = chargeEffect; this.sameTurn = sameTurn; - this.followUpPriority = followUpPriority; + this.followUpPriority = followUpPriority!; // TODO: is this bang correct? } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { @@ -2383,11 +2384,11 @@ export class ChargeAttr extends OverrideMoveEffectAttr { user.pushMoveHistory({ move: move.id, targets: [ target.getBattlerIndex() ], result: MoveResult.OTHER }); user.getMoveQueue().push({ move: move.id, targets: [ target.getBattlerIndex() ], ignorePP: true }); if (this.sameTurn) { - let movesetMove = user.moveset.find(m => m.moveId === move.id); + let movesetMove = user.moveset.find(m => m?.moveId === move.id); if (!movesetMove) { // account for any move that calls a ChargeAttr move when the ChargeAttr move does not exist in moveset movesetMove = new PokemonMove(move.id, 0, 0, true); } - user.scene.pushMovePhase(new MovePhase(user.scene, user, [ target.getBattlerIndex() ], movesetMove, true), this.followUpPriority); + user.scene.pushMovePhase(new MovePhase(user.scene, user, [ target.getBattlerIndex() ], movesetMove, true), this.followUpPriority!); // TODO: is this bang correct? } user.addTag(BattlerTagType.CHARGING, 1, move.id, user.id); resolve(true); @@ -2399,7 +2400,7 @@ export class ChargeAttr extends OverrideMoveEffectAttr { }); } - usedChargeEffect(user: Pokemon, target: Pokemon, move: Move): boolean { + usedChargeEffect(user: Pokemon, target: Pokemon | null, move: Move): boolean { if (!this.chargeEffect) { return false; } @@ -2488,7 +2489,7 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { resolve(true); }); } else { - user.scene.ui.showText(i18next.t("moveTriggers:tookMoveAttack", {pokemonName: getPokemonNameWithAffix(user.scene.getPokemonById(target.id)), moveName: move.name}), null, () => resolve(true)); + user.scene.ui.showText(i18next.t("moveTriggers:tookMoveAttack", {pokemonName: getPokemonNameWithAffix(user.scene.getPokemonById(target.id) ?? undefined), moveName: move.name}), null, () => resolve(true)); } }); } @@ -2497,20 +2498,20 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr { export class StatChangeAttr extends MoveEffectAttr { public stats: BattleStat[]; public levels: integer; - private condition: MoveConditionFunc; + private condition: MoveConditionFunc | null; private showMessage: boolean; - constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { + constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc | null, showMessage: boolean = true, firstHitOnly: boolean = false, moveEffectTrigger: MoveEffectTrigger = MoveEffectTrigger.HIT, firstTargetOnly: boolean = false) { super(selfTarget, moveEffectTrigger, firstHitOnly, false, firstTargetOnly); this.stats = typeof(stats) === "number" ? [ stats as BattleStat ] : stats as BattleStat[]; this.levels = levels; - this.condition = condition || null; + this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } - apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean | Promise { + apply(user: Pokemon, target: Pokemon, move: Move, args?: any[]): boolean | Promise { if (!super.apply(user, target, move, args) || (this.condition && !this.condition(user, target, move))) { return false; } @@ -2574,7 +2575,7 @@ export class StatChangeAttr extends MoveEffectAttr { export class PostVictoryStatChangeAttr extends MoveAttr { private stats: BattleStat[]; private levels: integer; - private condition: MoveConditionFunc; + private condition: MoveConditionFunc | null; private showMessage: boolean; constructor(stats: BattleStat | BattleStat[], levels: integer, selfTarget?: boolean, condition?: MoveConditionFunc, showMessage: boolean = true, firstHitOnly: boolean = false) { @@ -2583,7 +2584,7 @@ export class PostVictoryStatChangeAttr extends MoveAttr { ? [ stats as BattleStat ] : stats as BattleStat[]; this.levels = levels; - this.condition = condition || null; + this.condition = condition!; // TODO: is this bang correct? this.showMessage = showMessage; } applyPostVictory(user: Pokemon, target: Pokemon, move: Move): void { @@ -2591,7 +2592,7 @@ export class PostVictoryStatChangeAttr extends MoveAttr { return; } const statChangeAttr = new StatChangeAttr(this.stats, this.levels, this.showMessage); - statChangeAttr.apply(user, target, move, undefined); + statChangeAttr.apply(user, target, move); } } @@ -2751,7 +2752,7 @@ export class HpSplitAttr extends MoveEffectAttr { return resolve(false); } - const infoUpdates = []; + const infoUpdates: Promise[] = []; const hpValue = Math.floor((target.hp + user.hp) / 2); if (user.hp < hpValue) { @@ -2803,7 +2804,7 @@ export class LessPPMorePowerAttr extends VariablePowerAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const ppMax = move.pp; - const ppUsed = user.moveset.find((m) => m.moveId === move.id).ppUsed; + const ppUsed = user.moveset.find((m) => m?.moveId === move.id)?.ppUsed!; // TODO: is the bang correct? let ppRemains = ppMax - ppUsed; /** Reduce to 0 to avoid negative numbers if user has 1PP before attack and target has Ability.PRESSURE */ @@ -2870,6 +2871,7 @@ const beatUpFunc = (user: Pokemon, allyIndex: number): number => { } return (pokemon.species.getBaseStat(Stat.ATK) / 10) + 5; } + return 0; }; export class BeatUpAttr extends VariablePowerAttr { @@ -2896,7 +2898,7 @@ export class BeatUpAttr extends VariablePowerAttr { } const doublePowerChanceMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { - let message: string = null; + let message: string = ""; user.scene.executeWithSeedOffset(() => { const rand = Utils.randSeedInt(100); if (rand < move.chance) { @@ -2910,7 +2912,7 @@ export class DoublePowerChanceAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { let rand: integer; user.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(100), user.scene.currentBattle.turn << 6, user.scene.waveSeed); - if (rand < move.chance) { + if (rand! < move.chance) { const power = args[0] as Utils.NumberHolder; power.value *= 2; return true; @@ -2926,9 +2928,9 @@ export abstract class ConsecutiveUsePowerMultiplierAttr extends MovePowerMultipl const moveHistory = user.getLastXMoves(limit + 1).slice(1); let count = 0; - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; - while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.includes(turnMove?.move))) && (!resetOnFail || turnMove.result === MoveResult.SUCCESS)) { + while (((turnMove = moveHistory.shift())?.move === move.id || (comboMoves.length && comboMoves.includes(turnMove?.move!))) && (!resetOnFail || turnMove?.result === MoveResult.SUCCESS)) { // TODO: is this bang correct? if (count < (limit - 1)) { count++; } else if (resetOnLimit) { @@ -3181,7 +3183,7 @@ const magnitudeMessageFunc = (user: Pokemon, target: Pokemon, move: Move) => { message = i18next.t("moveTriggers:magnitudeMessage", {magnitude: m + 4}); }, user.scene.currentBattle.turn << 6, user.scene.waveSeed); - return message; + return message!; }; export class MagnitudePowerAttr extends VariablePowerAttr { @@ -3197,7 +3199,7 @@ export class MagnitudePowerAttr extends VariablePowerAttr { let m = 0; for (; m < magnitudeThresholds.length; m++) { - if (rand < magnitudeThresholds[m]) { + if (rand! < magnitudeThresholds[m]) { break; } } @@ -3360,7 +3362,7 @@ export class SpitUpPowerAttr extends VariablePowerAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag?.stockpiledCount > 0) { + if (stockpilingTag !== null && stockpilingTag.stockpiledCount > 0) { const power = args[0] as Utils.IntegerHolder; power.value = this.multiplier * stockpilingTag.stockpiledCount; return true; @@ -3378,7 +3380,7 @@ export class SwallowHealAttr extends HealAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const stockpilingTag = user.getTag(StockpilingTag); - if (stockpilingTag?.stockpiledCount > 0) { + if (stockpilingTag !== null && stockpilingTag?.stockpiledCount > 0) { const stockpiled = stockpilingTag.stockpiledCount; let healRatio: number; @@ -3400,7 +3402,10 @@ export class SwallowHealAttr extends HealAttr { } } -const hasStockpileStacksCondition: MoveConditionFunc = (user) => user.getTag(StockpilingTag)?.stockpiledCount > 0; +const hasStockpileStacksCondition: MoveConditionFunc = (user) => { + const hasStockpilingTag = user.getTag(StockpilingTag); + return !!hasStockpilingTag && hasStockpilingTag.stockpiledCount > 0; +}; /** * Attribute used for multi-hit moves that increase power in increments of the @@ -3471,13 +3476,13 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr { const enemy = user.getOpponent(0); const pokemonActed: Pokemon[] = []; - if (enemy.turnData.acted) { + if (enemy?.turnData.acted) { pokemonActed.push(enemy); } if (user.scene.currentBattle.double) { const userAlly = user.getAlly(); - const enemyAlly = enemy.getAlly(); + const enemyAlly = enemy?.getAlly(); if (userAlly && userAlly.turnData.acted) { pokemonActed.push(userAlly); @@ -3751,7 +3756,7 @@ export class VariableMoveTypeAttr extends MoveAttr { export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.ARCEUS) || [user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.SILVALLY)) { - const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.ARCEUS || user.species.speciesId === Species.SILVALLY ? user.formIndex : user.fusionSpecies?.formIndex!; // TODO: is this bang correct? move.type = Type[Type[form]]; return true; @@ -3764,7 +3769,7 @@ export class FormChangeItemTypeAttr extends VariableMoveTypeAttr { export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.GENESECT)) { - const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.GENESECT ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Shock Drive @@ -3793,7 +3798,7 @@ export class TechnoBlastTypeAttr extends VariableMoveTypeAttr { export class AuraWheelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.MORPEKO)) { - const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.MORPEKO ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Hangry Mode @@ -3813,7 +3818,7 @@ export class AuraWheelTypeAttr extends VariableMoveTypeAttr { export class RagingBullTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.PALDEA_TAUROS)) { - const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.PALDEA_TAUROS ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Blaze breed @@ -3836,7 +3841,7 @@ export class RagingBullTypeAttr extends VariableMoveTypeAttr { export class IvyCudgelTypeAttr extends VariableMoveTypeAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if ([user.species.speciesId, user.fusionSpecies?.speciesId].includes(Species.OGERPON)) { - const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies.formIndex; + const form = user.species.speciesId === Species.OGERPON ? user.formIndex : user.fusionSpecies?.formIndex; switch (form) { case 1: // Wellspring Mask @@ -4125,23 +4130,23 @@ export class DisableMoveAttr extends MoveEffectAttr { } const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveQueue.length) { turnMove = moveQueue.shift(); - if (turnMove.virtual) { + if (turnMove?.virtual) { continue; } - const moveIndex = target.getMoveset().findIndex(m => m.moveId === turnMove.move); + const moveIndex = target.getMoveset().findIndex(m => m?.moveId === turnMove?.move); if (moveIndex === -1) { return false; } const disabledMove = target.getMoveset()[moveIndex]; - target.summonData.disabledMove = disabledMove.moveId; + target.summonData.disabledMove = disabledMove?.moveId!; // TODO: is this bang correct? target.summonData.disabledTurns = 4; - user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove.getName()})); + user.scene.queueMessage(i18next.t("abilityTriggers:postDefendMoveDisable", { pokemonNameWithAffix: getPokemonNameWithAffix(target), moveName: disabledMove?.getName()})); return true; } @@ -4150,26 +4155,28 @@ export class DisableMoveAttr extends MoveEffectAttr { } getCondition(): MoveConditionFunc { - return (user, target, move) => { + return (user, target, move): boolean => { // TODO: Not sure what to do here if (target.summonData.disabledMove || target.isMax()) { return false; } const moveQueue = target.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveQueue.length) { turnMove = moveQueue.shift(); - if (turnMove.virtual) { + if (turnMove?.virtual) { continue; } - const move = target.getMoveset().find(m => m.moveId === turnMove.move); + const move = target.getMoveset().find(m => m?.moveId === turnMove?.move); if (!move) { continue; } return true; } + + return false; }; } @@ -4242,13 +4249,13 @@ export class AddBattlerTagAttr extends MoveEffectAttr { return false; } - getCondition(): MoveConditionFunc { + getCondition(): MoveConditionFunc | null { return this.failOnOverlap ? (user, target, move) => !(this.selfTarget ? user : target).getTag(this.tagType) : null; } - getTagTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { + getTagTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer | void { switch (this.tagType) { case BattlerTagType.RECHARGING: case BattlerTagType.PERISH_SONG: @@ -4296,7 +4303,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { if (moveChance < 0) { moveChance = 100; } - return Math.floor(this.getTagTargetBenefitScore(user, target, move) * (moveChance / 100)); + return Math.floor(this.getTagTargetBenefitScore(user, target, move)! * (moveChance / 100)); // TODO: is the bang correct? } } @@ -4444,11 +4451,11 @@ export class ProtectAttr extends AddBattlerTagAttr { return ((user, target, move): boolean => { let timesUsed = 0; const moveHistory = user.getLastXMoves(); - let turnMove: TurnMove; + let turnMove: TurnMove | undefined; while (moveHistory.length) { turnMove = moveHistory.shift(); - if (!allMoves[turnMove.move].hasAttr(ProtectAttr) || turnMove.result !== MoveResult.SUCCESS) { + if (!allMoves[turnMove?.move!].hasAttr(ProtectAttr) || turnMove?.result !== MoveResult.SUCCESS) { // TODO: is the bang correct? break; } timesUsed++; @@ -4521,11 +4528,11 @@ export class AddArenaTagAttr extends MoveEffectAttr { private failOnOverlap: boolean; public selfSideTarget: boolean; - constructor(tagType: ArenaTagType, turnCount?: integer, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { + constructor(tagType: ArenaTagType, turnCount?: integer | null, failOnOverlap: boolean = false, selfSideTarget: boolean = false) { super(true, MoveEffectTrigger.POST_APPLY); this.tagType = tagType; - this.turnCount = turnCount; + this.turnCount = turnCount!; // TODO: is the bang correct? this.failOnOverlap = failOnOverlap; this.selfSideTarget = selfSideTarget; } @@ -4543,7 +4550,7 @@ export class AddArenaTagAttr extends MoveEffectAttr { return false; } - getCondition(): MoveConditionFunc { + getCondition(): MoveConditionFunc | null { return this.failOnOverlap ? (user, target, move) => !user.scene.arena.getTagOnSide(this.tagType, target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY) : null; @@ -4716,13 +4723,13 @@ export class SwapArenaTagsAttr extends MoveEffectAttr { if (tagPlayerTemp) { for (const swapTagsType of tagPlayerTemp) { user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.PLAYER, true); - user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.ENEMY, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.ENEMY, true); // TODO: is the bang correct? } } if (tagEnemyTemp) { for (const swapTagsType of tagEnemyTemp) { user.scene.arena.removeTagOnSide(swapTagsType.tagType, ArenaTagSide.ENEMY, true); - user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId, ArenaTagSide.PLAYER, true); + user.scene.arena.addTag(swapTagsType.tagType, swapTagsType.turnCount, swapTagsType.sourceMove, swapTagsType.sourceId!, ArenaTagSide.PLAYER, true); // TODO: is the bang correct? } } @@ -4836,7 +4843,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { if (switchOutTarget.hp > 0) { // for opponent switching out - user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot), false, this.batonPass, false), MoveEndPhase); + user.scene.prependToPhase(new SwitchSummonPhase(user.scene, switchOutTarget.getFieldIndex(), (user.scene.currentBattle.trainer ? user.scene.currentBattle.trainer.getNextSummonIndex((switchOutTarget as EnemyPokemon).trainerSlot) : 0), false, this.batonPass, false), MoveEndPhase); } } else { // Switch out logic for everything else @@ -4926,7 +4933,7 @@ export class RemoveTypeAttr extends MoveEffectAttr { return false; } - if (user.isTerastallized && user.getTeraType() === this.removedType) { // active tera types cannot be removed + if (user.isTerastallized() && user.getTeraType() === this.removedType) { // active tera types cannot be removed return false; } @@ -5048,7 +5055,7 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { return false; } - const firstMoveType = target.getMoveset()[0].getMove().type; + const firstMoveType = target.getMoveset()[0]?.getMove().type!; // TODO: is this bang correct? user.summonData.types = [ firstMoveType ]; user.scene.queueMessage(i18next.t("battle:transformedIntoType", {pokemonName: getPokemonNameWithAffix(user), type: i18next.t(`pokemonInfo:Type.${Type[firstMoveType]}`)})); @@ -5057,21 +5064,21 @@ export class FirstMoveTypeAttr extends MoveEffectAttr { } export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { - private enemyMoveset: boolean; + private enemyMoveset: boolean | null; constructor(enemyMoveset?: boolean) { super(); - this.enemyMoveset = enemyMoveset; + this.enemyMoveset = enemyMoveset!; // TODO: is this bang correct? } apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const moveset = (!this.enemyMoveset ? user : target).getMoveset(); - const moves = moveset.filter(m => !m.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); + const moves = moveset.filter(m => !m?.getMove().hasFlag(MoveFlags.IGNORE_VIRTUAL)); if (moves.length) { const move = moves[user.randSeedInt(moves.length)]; - const moveIndex = moveset.findIndex(m => m.moveId === move.moveId); - const moveTargets = getMoveTargets(user, move.moveId); + const moveIndex = moveset.findIndex(m => m?.moveId === move?.moveId); + const moveTargets = getMoveTargets(user, move?.moveId!); // TODO: is this bang correct? if (!moveTargets.targets.length) { return false; } @@ -5092,8 +5099,8 @@ export class RandomMovesetMoveAttr extends OverrideMoveEffectAttr { } } const targets = selectTargets; - user.getMoveQueue().push({ move: move.moveId, targets: targets, ignorePP: true }); - user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex], true)); + user.getMoveQueue().push({ move: move?.moveId!, targets: targets, ignorePP: true }); // TODO: is this bang correct? + user.scene.unshiftPhase(new MovePhase(user.scene, user, targets, moveset[moveIndex]!, true)); // There's a PR to re-do the move(s) that use this Attr, gonna put `!` for now return true; } @@ -5335,12 +5342,12 @@ export class ReducePpMoveAttr extends MoveEffectAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { // Null checks can be skipped due to condition function const lastMove = target.getLastXMoves().find(() => true); - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); - const lastPpUsed = movesetMove.ppUsed; - movesetMove.ppUsed = Math.min(movesetMove.ppUsed + this.reduction, movesetMove.getMovePp()); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move); + const lastPpUsed = movesetMove?.ppUsed!; // TODO: is the bang correct? + movesetMove!.ppUsed = Math.min((movesetMove?.ppUsed!) + this.reduction, movesetMove?.getMovePp()!); // TODO: is the bang correct? - const message = i18next.t("battle:ppReduced", {targetName: getPokemonNameWithAffix(target), moveName: movesetMove.getName(), reduction: movesetMove.ppUsed - lastPpUsed}); - user.scene.eventTarget.dispatchEvent(new MoveUsedEvent(target?.id, movesetMove.getMove(), movesetMove.ppUsed)); + const message = i18next.t("battle:ppReduced", {targetName: getPokemonNameWithAffix(target), moveName: movesetMove?.getName(), reduction: (movesetMove?.ppUsed!) - lastPpUsed}); // TODO: is the bang correct? + user.scene.eventTarget.dispatchEvent(new MoveUsedEvent(target?.id, movesetMove?.getMove()!, movesetMove?.ppUsed!)); // TODO: are these bangs correct? user.scene.queueMessage(message); return true; @@ -5350,7 +5357,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { return (user, target, move) => { const lastMove = target.getLastXMoves().find(() => true); 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 false; @@ -5360,7 +5367,7 @@ export class ReducePpMoveAttr extends MoveEffectAttr { getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number { const lastMove = target.getLastXMoves().find(() => true); if (lastMove) { - const movesetMove = target.getMoveset().find(m => m.moveId === lastMove.move); + const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move); if (movesetMove) { const maxPp = movesetMove.getMovePp(); const ppLeft = maxPp - movesetMove.ppUsed; @@ -5397,7 +5404,7 @@ export class AttackReducePpMoveAttr extends ReducePpMoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { const lastMove = target.getLastXMoves().find(() => true); 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())) { super.apply(user, target, move, args); } @@ -5443,7 +5450,7 @@ export class MovesetCopyMoveAttr extends OverrideMoveEffectAttr { 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) { return false; @@ -5494,7 +5501,7 @@ export class SketchAttr extends MoveEffectAttr { } 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) { return false; } @@ -5533,7 +5540,7 @@ export class SketchAttr extends MoveEffectAttr { return false; } - if (user.getMoveset().find(m => m.moveId === targetMove.move)) { + if (user.getMoveset().find(m => m?.moveId === targetMove.move)) { return false; } @@ -5722,7 +5729,7 @@ export class TransformAttr extends MoveEffectAttr { user.summonData.fusionGender = target.getFusionGender(); user.summonData.stats = [ user.stats[Stat.HP] ].concat(target.stats.slice(1)); user.summonData.battleStats = target.summonData.battleStats.slice(0); - user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m.moveId, m.ppUsed, m.ppUp)); + user.summonData.moveset = target.getMoveset().map(m => new PokemonMove(m?.moveId!, m?.ppUsed, m?.ppUp)); // TODO: is this bang correct? user.summonData.types = target.getTypes(); user.scene.queueMessage(i18next.t("moveTriggers:transformedIntoTarget", {pokemonName: getPokemonNameWithAffix(user), targetName: getPokemonNameWithAffix(target)})); @@ -5790,7 +5797,7 @@ export class LastResortAttr extends MoveAttr { getCondition(): MoveConditionFunc { return (user: Pokemon, target: Pokemon, move: Move) => { const uniqueUsedMoveIds = new Set(); - const movesetMoveIds = user.getMoveset().map(m => m.moveId); + const movesetMoveIds = user.getMoveset().map(m => m?.moveId); user.getMoveHistory().map(m => { if (m.move !== move.id && movesetMoveIds.find(mm => mm === m.move)) { uniqueUsedMoveIds.add(m.move); @@ -5864,7 +5871,7 @@ const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target export type MoveAttrFilter = (attr: MoveAttr) => boolean; -function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, args: any[]): Promise { +function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon | null, target: Pokemon | null, move: Move, args: any[]): Promise { return new Promise(resolve => { const attrPromises: Promise[] = []; const moveAttrs = move.attrs.filter(a => attrFilter(a)); @@ -5878,11 +5885,11 @@ function applyMoveAttrsInternal(attrFilter: MoveAttrFilter, user: Pokemon, targe }); } -export function applyMoveAttrs(attrType: Constructor, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise { +export function applyMoveAttrs(attrType: Constructor, user: Pokemon | null, target: Pokemon | null, move: Move, ...args: any[]): Promise { return applyMoveAttrsInternal((attr: MoveAttr) => attr instanceof attrType, user, target, move, args); } -export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon, move: Move, ...args: any[]): Promise { +export function applyFilteredMoveAttrs(attrFilter: MoveAttrFilter, user: Pokemon, target: Pokemon | null, move: Move, ...args: any[]): Promise { return applyMoveAttrsInternal(attrFilter, user, target, move, args); } @@ -6889,7 +6896,7 @@ export function initMoves() { .unimplemented(), new SelfStatusMove(Moves.REFRESH, Type.NORMAL, -1, 20, -1, 0, 3) .attr(HealStatusEffectAttr, true, StatusEffect.PARALYSIS, StatusEffect.POISON, StatusEffect.TOXIC, StatusEffect.BURN) - .condition((user, target, move) => user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), + .condition((user, target, move) => !!user.status && (user.status.effect === StatusEffect.PARALYSIS || user.status.effect === StatusEffect.POISON || user.status.effect === StatusEffect.TOXIC || user.status.effect === StatusEffect.BURN)), new SelfStatusMove(Moves.GRUDGE, Type.GHOST, -1, 5, -1, 0, 3) .unimplemented(), new SelfStatusMove(Moves.SNATCH, Type.DARK, -1, 10, -1, 4, 3) @@ -6952,7 +6959,7 @@ export function initMoves() { .attr(FlinchAttr), new AttackMove(Moves.WEATHER_BALL, Type.NORMAL, MoveCategory.SPECIAL, 50, 100, 10, -1, 0, 3) .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(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) + .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.RAIN, WeatherType.SANDSTORM, WeatherType.HAIL, WeatherType.SNOW, WeatherType.FOG, WeatherType.HEAVY_RAIN, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType!) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 2 : 1) // TODO: is this bang correct? .ballBombMove(), new StatusMove(Moves.AROMATHERAPY, Type.GRASS, -1, 5, -1, 0, 3) .attr(PartyStatusCureAttr, i18next.t("moveTriggers:soothingAromaWaftedThroughArea"), Abilities.SAP_SIPPER) @@ -7120,7 +7127,7 @@ export function initMoves() { new AttackMove(Moves.CLOSE_COMBAT, Type.FIGHTING, MoveCategory.PHYSICAL, 120, 100, 5, -1, 0, 4) .attr(StatChangeAttr, [ BattleStat.DEF, BattleStat.SPDEF ], -1, true), new AttackMove(Moves.PAYBACK, Type.DARK, MoveCategory.PHYSICAL, 50, 100, 10, -1, 0, 4) - .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.BALL ? 2 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => target.getLastXMoves(1).find(m => m.turn === target.scene.currentBattle.turn) || user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.BALL ? 2 : 1), new AttackMove(Moves.ASSURANCE, Type.DARK, MoveCategory.PHYSICAL, 60, 100, 10, -1, 0, 4) .attr(MovePowerMultiplierAttr, (user, target, move) => target.turnData.damageTaken > 0 ? 2 : 1), new StatusMove(Moves.EMBARGO, Type.DARK, 100, 15, -1, 0, 4) @@ -7135,7 +7142,7 @@ export function initMoves() { if (user.status?.effect && isNonVolatileStatusEffect(user.status.effect)) { statusToApply = user.status.effect; } - return statusToApply && target.canSetStatus(statusToApply, false, false, user); + return !!statusToApply && target.canSetStatus(statusToApply, false, false, user); } ), new AttackMove(Moves.TRUMP_CARD, Type.NORMAL, MoveCategory.SPECIAL, -1, -1, 5, -1, 0, 4) @@ -7173,7 +7180,7 @@ export function initMoves() { new StatusMove(Moves.WORRY_SEED, Type.GRASS, 100, 10, -1, 0, 4) .attr(AbilityChangeAttr, Abilities.INSOMNIA), new AttackMove(Moves.SUCKER_PUNCH, Type.DARK, MoveCategory.PHYSICAL, 70, 100, 5, -1, 1, 4) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? new StatusMove(Moves.TOXIC_SPIKES, Type.POISON, -1, 20, -1, 0, 4) .attr(AddArenaTrapTagAttr, ArenaTagType.TOXIC_SPIKES) .target(MoveTarget.ENEMY_SIDE), @@ -8016,7 +8023,7 @@ export function initMoves() { new AttackMove(Moves.SMART_STRIKE, Type.STEEL, MoveCategory.PHYSICAL, 70, -1, 10, -1, 0, 7), new StatusMove(Moves.PURIFY, Type.POISON, -1, 20, -1, 0, 7) .condition( - (user: Pokemon, target: Pokemon, move: Move) => isNonVolatileStatusEffect(target.status?.effect)) + (user: Pokemon, target: Pokemon, move: Move) => isNonVolatileStatusEffect(target.status?.effect!)) // TODO: is this bang correct? .attr(HealAttr, 0.5) .attr(HealStatusEffectAttr, false, ...getNonVolatileStatusEffects()) .triageMove(), @@ -8738,7 +8745,7 @@ export function initMoves() { .slicingMove(), new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) .attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY) - .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 1.5 : 1), + .attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType!) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 1.5 : 1), // TODO: is this bang correct? new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) .attr(TargetHalfHpDamageAttr), new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) @@ -8846,7 +8853,7 @@ export function initMoves() { new SelfStatusMove(Moves.BURNING_BULWARK, Type.FIRE, -1, 10, -1, 4, 9) .attr(ProtectAttr, BattlerTagType.BURNING_BULWARK), new AttackMove(Moves.THUNDERCLAP, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 5, -1, 1, 9) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS), + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS), // TODO: is this bang correct? new AttackMove(Moves.MIGHTY_CLEAVE, Type.ROCK, MoveCategory.PHYSICAL, 95, 100, 5, -1, 0, 9) .slicingMove() .ignoresProtect(), @@ -8873,7 +8880,7 @@ export function initMoves() { .partial(), new AttackMove(Moves.UPPER_HAND, Type.FIGHTING, MoveCategory.PHYSICAL, 65, 100, 15, 100, 3, 9) .attr(FlinchAttr) - .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()].command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()].move.move].priority > 0 ) + .condition((user, target, move) => user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.command === Command.FIGHT && !target.turnData.acted && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].category !== MoveCategory.STATUS && allMoves[user.scene.currentBattle.turnCommands[target.getBattlerIndex()]?.move?.move!].priority > 0 ) // TODO: is this bang correct? //TODO: Should also apply when target move priority increased by ability ex. gale wings .partial(), new AttackMove(Moves.MALIGNANT_CHAIN, Type.POISON, MoveCategory.SPECIAL, 100, 100, 5, 50, 0, 9) diff --git a/src/data/nature.ts b/src/data/nature.ts index 1ae3b76a6b6..72e5bb7863c 100644 --- a/src/data/nature.ts +++ b/src/data/nature.ts @@ -15,8 +15,8 @@ export function getNatureName(nature: Nature, includeStatEffects: boolean = fals } if (includeStatEffects) { const stats = Utils.getEnumValues(Stat).slice(1); - let increasedStat: Stat = null; - let decreasedStat: Stat = null; + let increasedStat: Stat | null = null; + let decreasedStat: Stat | null = null; for (const stat of stats) { const multiplier = getNatureStatMultiplier(nature, stat); if (multiplier > 1) { diff --git a/src/data/pokemon-evolutions.ts b/src/data/pokemon-evolutions.ts index 236d174492f..e7baf608b97 100644 --- a/src/data/pokemon-evolutions.ts +++ b/src/data/pokemon-evolutions.ts @@ -59,26 +59,26 @@ export type EvolutionConditionEnforceFunc = (p: Pokemon) => void; export class SpeciesFormEvolution { public speciesId: Species; - public preFormKey: string; - public evoFormKey: string; + public preFormKey: string | null; + public evoFormKey: string | null; public level: integer; - public item: EvolutionItem; - public condition: SpeciesEvolutionCondition; - public wildDelay: SpeciesWildEvolutionDelay; + public item: EvolutionItem | null; + public condition: SpeciesEvolutionCondition | null; + public wildDelay: SpeciesWildEvolutionDelay | null; - constructor(speciesId: Species, preFormKey: string, evoFormKey: string, level: integer, item: EvolutionItem, condition: SpeciesEvolutionCondition, wildDelay?: SpeciesWildEvolutionDelay) { + constructor(speciesId: Species, preFormKey: string | null, evoFormKey: string | null, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay | null) { this.speciesId = speciesId; this.preFormKey = preFormKey; this.evoFormKey = evoFormKey; this.level = level; this.item = item || EvolutionItem.NONE; this.condition = condition; - this.wildDelay = wildDelay || SpeciesWildEvolutionDelay.NONE; + this.wildDelay = wildDelay!; // TODO: is this bang correct? } } export class SpeciesEvolution extends SpeciesFormEvolution { - constructor(speciesId: Species, level: integer, item: EvolutionItem, condition: SpeciesEvolutionCondition, wildDelay?: SpeciesWildEvolutionDelay) { + constructor(speciesId: Species, level: integer, item: EvolutionItem | null, condition: SpeciesEvolutionCondition | null, wildDelay?: SpeciesWildEvolutionDelay | null) { super(speciesId, null, null, level, item, condition, wildDelay); } } @@ -95,7 +95,7 @@ export class FusionSpeciesFormEvolution extends SpeciesFormEvolution { export class SpeciesEvolutionCondition { public predicate: EvolutionConditionPredicate; - public enforceFunc: EvolutionConditionEnforceFunc; + public enforceFunc: EvolutionConditionEnforceFunc | undefined; constructor(predicate: EvolutionConditionPredicate, enforceFunc?: EvolutionConditionEnforceFunc) { this.predicate = predicate; @@ -1264,17 +1264,17 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.TANGELA]: [ - new SpeciesEvolution(Species.TANGROWTH, 34, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.TANGROWTH, 34, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.LICKITUNG]: [ - new SpeciesEvolution(Species.LICKILICKY, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ROLLOUT).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.LICKILICKY, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ROLLOUT).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.STARYU]: [ new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.EEVEE]: [ - new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), - new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), + new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG), new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG), @@ -1294,13 +1294,13 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.TOGEKISS, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.AIPOM]: [ - new SpeciesEvolution(Species.AMBIPOM, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DOUBLE_HIT).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.AMBIPOM, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DOUBLE_HIT).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.SUNKERN]: [ new SpeciesEvolution(Species.SUNFLORA, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.YANMA]: [ - new SpeciesEvolution(Species.YANMEGA, 33, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.YANMEGA, 33, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.MURKROW]: [ new SpeciesEvolution(Species.HONCHKROW, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) @@ -1309,17 +1309,17 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.MISMAGIUS, 1, EvolutionItem.DUSK_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.GIRAFARIG]: [ - new SpeciesEvolution(Species.FARIGIRAF, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.TWIN_BEAM).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.FARIGIRAF, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TWIN_BEAM).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.DUNSPARCE]: [ new SpeciesFormEvolution(Species.DUDUNSPARCE, "", "three-segment", 32, null, new SpeciesEvolutionCondition(p => { 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) { p.scene.executeWithSeedOffset(() => ret = !Utils.randSeedInt(4), p.id); } return ret; }), SpeciesWildEvolutionDelay.LONG), - new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.GLIGAR]: [ new SpeciesEvolution(Species.GLISCOR, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.LONG) @@ -1331,10 +1331,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna ], [Species.PILOSWINE]: [ - new SpeciesEvolution(Species.MAMOSWINE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.MAMOSWINE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.ANCIENT_POWER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.STANTLER]: [ - new SpeciesEvolution(Species.WYRDEER, 25, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.PSYSHIELD_BASH).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.WYRDEER, 25, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.PSYSHIELD_BASH).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.LOMBRE]: [ new SpeciesEvolution(Species.LUDICOLO, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1352,11 +1352,11 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ROSERADE, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.BONSLY]: [ - new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.MIME_JR]: [ - new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM), - new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM), + new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.PANSAGE]: [ new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1411,10 +1411,10 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesFormEvolution(Species.LYCANROC, "", "midnight", 25, null, new SpeciesEvolutionCondition(p => (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT) && (p.formIndex === 0)), null) ], [Species.STEENEE]: [ - new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.TSAREENA, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.STOMP).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.POIPOLE]: [ - new SpeciesEvolution(Species.NAGANADEL, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DRAGON_PULSE).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.NAGANADEL, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_PULSE).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.ALOLA_SANDSHREW]: [ new SpeciesEvolution(Species.ALOLA_SANDSLASH, 1, EvolutionItem.ICE_STONE, null, SpeciesWildEvolutionDelay.LONG) @@ -1428,7 +1428,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.CLOBBOPUS]: [ - new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM) + new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM) ], [Species.SINISTEA]: [ new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG), @@ -1462,7 +1462,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.HISUI_ELECTRODE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG) ], [Species.HISUI_QWILFISH]: [ - new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) + new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG) ], [Species.HISUI_SNEASEL]: [ new SpeciesEvolution(Species.SNEASLER, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.LONG) @@ -1485,7 +1485,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesFormEvolution(Species.SINISTCHA, "artisan", "masterpiece", 1, EvolutionItem.MASTERPIECE_TEACUP, null, SpeciesWildEvolutionDelay.LONG) ], [Species.DIPPLIN]: [ - new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.DRAGON_CHEER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.HYDRAPPLE, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.DRAGON_CHEER).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.KADABRA]: [ new SpeciesEvolution(Species.ALAKAZAM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) @@ -1501,7 +1501,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.ONIX]: [ new SpeciesEvolution(Species.STEELIX, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( - p => p.moveset.filter(m => m.getMove().type === Type.STEEL).length > 0), + p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.RHYDON]: [ @@ -1512,7 +1512,7 @@ export const pokemonEvolutions: PokemonEvolutions = { ], [Species.SCYTHER]: [ new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition( - p => p.moveset.filter(m => m.getMove().type === Type.STEEL).length > 0), + p => p.moveset.filter(m => m?.getMove().type === Type.STEEL).length > 0), SpeciesWildEvolutionDelay.VERY_LONG), new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG) ], @@ -1566,7 +1566,7 @@ export const pokemonEvolutions: PokemonEvolutions = { new SpeciesEvolution(Species.ALOLA_GOLEM, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.PRIMEAPE]: [ - new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) + new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG) ], [Species.GOLBAT]: [ new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG) diff --git a/src/data/pokemon-forms.ts b/src/data/pokemon-forms.ts index 7e29ea994e9..a55b9186839 100644 --- a/src/data/pokemon-forms.ts +++ b/src/data/pokemon-forms.ts @@ -181,7 +181,7 @@ export class SpeciesFormChange { return true; } - findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger { + findTrigger(triggerType: Constructor): SpeciesFormChangeTrigger | null { if (!this.trigger.hasTriggerType(triggerType)) { return null; } @@ -189,7 +189,7 @@ export class SpeciesFormChange { const trigger = this.trigger; if (trigger instanceof SpeciesFormChangeCompoundTrigger) { - return trigger.triggers.find(t => t.hasTriggerType(triggerType)); + return trigger.triggers.find(t => t.hasTriggerType(triggerType))!; // TODO: is this bang correct? } return trigger; @@ -198,11 +198,11 @@ export class SpeciesFormChange { export class SpeciesFormChangeCondition { public predicate: SpeciesFormChangeConditionPredicate; - public enforceFunc: SpeciesFormChangeConditionEnforceFunc; + public enforceFunc: SpeciesFormChangeConditionEnforceFunc | null; constructor(predicate: SpeciesFormChangeConditionPredicate, enforceFunc?: SpeciesFormChangeConditionEnforceFunc) { this.predicate = predicate; - this.enforceFunc = enforceFunc; + this.enforceFunc = enforceFunc!; // TODO: is this bang correct? } } @@ -314,7 +314,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge } 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; } } @@ -332,7 +332,7 @@ export abstract class SpeciesFormChangeMoveTrigger extends SpeciesFormChangeTrig export class SpeciesFormChangePreMoveTrigger extends SpeciesFormChangeMoveTrigger { canChange(pokemon: Pokemon): boolean { const command = pokemon.scene.currentBattle.turnCommands[pokemon.getBattlerIndex()]; - return command?.move && this.movePredicate(command.move.move) === this.used; + return !!command?.move && this.movePredicate(command.move.move) === this.used; } } diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index fa6587145ad..837cad4a0df 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -28,21 +28,21 @@ export enum Region { PALDEA } -export function getPokemonSpecies(species: Species): PokemonSpecies { +export function getPokemonSpecies(species: Species | Species[]): PokemonSpecies { // If a special pool (named trainers) is used here it CAN happen that they have a array as species (which means choose one of those two). So we catch that with this code block if (Array.isArray(species)) { // Pick a random species from the list species = species[Math.floor(Math.random() * species.length)]; } if (species >= 2000) { - return allSpecies.find(s => s.speciesId === species); + return allSpecies.find(s => s.speciesId === species)!; // TODO: is this bang correct? } return allSpecies[species - 1]; } export function getPokemonSpeciesForm(species: Species, formIndex: integer): PokemonSpeciesForm { const retSpecies: PokemonSpecies = species >= 2000 - ? allSpecies.find(s => s.speciesId === species) + ? allSpecies.find(s => s.speciesId === species)! // TODO: is the bang correct? : allSpecies[species - 1]; if (formIndex < retSpecies.forms?.length) { return retSpecies.forms[formIndex]; @@ -97,7 +97,7 @@ export function getFusedSpeciesName(speciesAName: string, speciesBName: string): fragB = fragB.slice(1); } else { const newCharMatch = new RegExp(`[^${lastCharA}]`).exec(fragB); - if (newCharMatch?.index > 0) { + if (newCharMatch?.index !== undefined && newCharMatch.index > 0) { fragB = fragB.slice(newCharMatch.index); } } @@ -125,7 +125,7 @@ export abstract class PokemonSpeciesForm { public formIndex: integer; public generation: integer; public type1: Type; - public type2: Type; + public type2: Type | null; public height: number; public weight: number; public ability1: Abilities; @@ -139,7 +139,7 @@ export abstract class PokemonSpeciesForm { public genderDiffs: boolean; public isStarterSelectable: boolean; - constructor(type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + constructor(type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs: boolean, isStarterSelectable: boolean) { this.type1 = type1; @@ -267,7 +267,7 @@ export abstract class PokemonSpeciesForm { return `${/_[1-3]$/.test(spriteId) ? "variant/" : ""}${spriteId}`; } - getSpriteId(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer, back?: boolean): string { + getSpriteId(female: boolean, formIndex?: integer, shiny?: boolean, variant: integer = 0, back?: boolean): string { if (formIndex === undefined || this instanceof PokemonForm) { formIndex = this.formIndex; } @@ -281,7 +281,7 @@ export abstract class PokemonSpeciesForm { `${back ? "back__" : ""}${baseSpriteKey}`.split("__").map(p => config ? config = config[p] : null); const variantSet = config as VariantSet; - return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant || 0] === 2 ? `_${variant + 1}` : ""}`; + return `${back ? "back__" : ""}${shiny && (!variantSet || (!variant && !variantSet[variant || 0])) ? "shiny__" : ""}${baseSpriteKey}${shiny && variantSet && variantSet[variant] === 2 ? `_${variant + 1}` : ""}`; } getSpriteKey(female: boolean, formIndex?: integer, shiny?: boolean, variant?: integer): string { @@ -297,10 +297,10 @@ export abstract class PokemonSpeciesForm { * @returns species id if no additional forms, index with formkey if a pokemon with a form */ getVariantDataIndex(formIndex?: integer) { - let formkey = null; - let variantDataIndex: integer|string = this.speciesId; + let formkey: string | null = null; + let variantDataIndex: integer | string = this.speciesId; const species = getPokemonSpecies(this.speciesId); - if (species.forms.length > 0) { + if (species.forms.length > 0 && formIndex !== undefined) { formkey = species.forms[formIndex]?.formSpriteKey; if (formkey) { variantDataIndex = `${this.speciesId}-${formkey}`; @@ -311,7 +311,7 @@ export abstract class PokemonSpeciesForm { getIconAtlasKey(formIndex?: integer, shiny?: boolean, variant?: integer): string { const variantDataIndex = this.getVariantDataIndex(formIndex); - const isVariant = shiny && variantData[variantDataIndex] && variantData[variantDataIndex][variant]; + const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]); return `pokemon_icons_${this.generation}${isVariant ? "v" : ""}`; } @@ -324,7 +324,7 @@ export abstract class PokemonSpeciesForm { let ret = this.speciesId.toString(); - const isVariant = shiny && variantData[variantDataIndex] && variantData[variantDataIndex][variant]; + const isVariant = shiny && variantData[variantDataIndex] && (variant !== undefined && variantData[variantDataIndex][variant]); if (shiny && !isVariant) { ret += "s"; @@ -382,7 +382,7 @@ export abstract class PokemonSpeciesForm { let ret = speciesId.toString(); const forms = getPokemonSpecies(speciesId).forms; if (forms.length) { - if (formIndex >= forms.length) { + if (formIndex !== undefined && formIndex >= forms.length) { console.warn(`Attempted accessing form with index ${formIndex} of species ${getPokemonSpecies(speciesId).getName()} with only ${forms.length || 0} forms`); formIndex = Math.min(formIndex, forms.length - 1); } @@ -478,7 +478,7 @@ export abstract class PokemonSpeciesForm { let config = variantData; spritePath.split("/").map(p => config ? config = config[p] : null); const variantSet = config as VariantSet; - if (variantSet && variantSet[variant] === 1) { + if (variantSet && (variant !== undefined && variantSet[variant] === 1)) { const populateVariantColors = (key: string): Promise => { return new Promise(resolve => { if (variantColorCache.hasOwnProperty(key)) { @@ -507,7 +507,7 @@ export abstract class PokemonSpeciesForm { cry(scene: BattleScene, soundConfig?: Phaser.Types.Sound.SoundConfig, ignorePlay?: boolean): AnySound { const cryKey = this.getCryKey(this.formIndex); - let cry = scene.sound.get(cryKey) as AnySound; + let cry: AnySound | null = scene.sound.get(cryKey) as AnySound; if (cry?.pendingRemove) { cry = null; } @@ -532,30 +532,32 @@ export abstract class PokemonSpeciesForm { const frame = sourceFrame; canvas.width = frame.width; canvas.height = frame.height; - context.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); - const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); - const pixelData = imageData.data; + context?.drawImage(sourceImage, frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); + const imageData = context?.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + const pixelData = imageData?.data; + const pixelColors: number[] = []; - for (let i = 0; i < pixelData.length; i += 4) { - if (pixelData[i + 3]) { - const pixel = pixelData.slice(i, i + 4); - const [ r, g, b, a ] = pixel; - if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { - spriteColors.push([ r, g, b, a ]); + if (pixelData?.length !== undefined) { + for (let i = 0; i < pixelData.length; i += 4) { + if (pixelData[i + 3]) { + const pixel = pixelData.slice(i, i + 4); + const [ r, g, b, a ] = pixel; + if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { + spriteColors.push([ r, g, b, a ]); + } } } - } - const pixelColors = []; - for (let i = 0; i < pixelData.length; i += 4) { - const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); - if (!total) { - continue; + for (let i = 0; i < pixelData.length; i += 4) { + const total = pixelData.slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); + if (!total) { + continue; + } + pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] })); } - pixelColors.push(argbFromRgba({ r: pixelData[i], g: pixelData[i + 1], b: pixelData[i + 2], a: pixelData[i + 3] })); } - let paletteColors: Map; + let paletteColors: Map = new Map(); const originalRandom = Math.random; Math.random = () => Phaser.Math.RND.realInRange(0, 1); @@ -577,15 +579,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali public mythical: boolean; public species: string; public growthRate: GrowthRate; - public malePercent: number; + public malePercent: number | null; public genderDiffs: boolean; public canChangeForm: boolean; public forms: PokemonForm[]; constructor(id: Species, generation: integer, subLegendary: boolean, legendary: boolean, mythical: boolean, species: string, - type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, - catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number, + catchRate: integer, baseFriendship: integer, baseExp: integer, growthRate: GrowthRate, malePercent: number | null, genderDiffs: boolean, canChangeForm?: boolean, ...forms: PokemonForm[]) { super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd, catchRate, baseFriendship, baseExp, genderDiffs, false); @@ -614,7 +616,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali getName(formIndex?: integer): string { if (formIndex !== undefined && this.forms.length) { const form = this.forms[formIndex]; - let key: string; + let key: string | null; switch (form.formKey) { case SpeciesFormKey.MEGA: case SpeciesFormKey.PRIMAL: @@ -626,6 +628,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali default: if (form.formKey.indexOf(SpeciesFormKey.GIGANTAMAX) > -1) { key = "gigantamax"; + } else { + key = null; } } @@ -713,11 +717,11 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali evolutionChance = Math.min(minChance + easeInFunc(Math.min(level - ev.level, maxLevelDiff) / maxLevelDiff) * (1 - minChance), 1); } } else { - const preferredMinLevel = Math.max((ev.level - 1) + ev.wildDelay * this.getStrengthLevelDiff(strength), 1); + const preferredMinLevel = Math.max((ev.level - 1) + (ev.wildDelay!) * this.getStrengthLevelDiff(strength), 1); // TODO: is the bang correct? let evolutionLevel = Math.max(ev.level > 1 ? ev.level : Math.floor(preferredMinLevel / 2), 1); if (ev.level <= 1 && pokemonPrevolutions.hasOwnProperty(this.speciesId)) { - const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(ev => ev.speciesId === this.speciesId).level; + const prevolutionLevel = pokemonEvolutions[pokemonPrevolutions[this.speciesId]].find(ev => ev.speciesId === this.speciesId)!.level; // TODO: is the bang correct? if (prevolutionLevel > 1) { evolutionLevel = prevolutionLevel; } @@ -750,15 +754,15 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali for (const weight of evolutionPool.keys()) { if (randValue < weight) { - return getPokemonSpecies(evolutionPool.get(weight)).getSpeciesForLevel(level, true, forTrainer, strength); + return getPokemonSpecies(evolutionPool.get(weight)!).getSpeciesForLevel(level, true, forTrainer, strength); // TODO: is the bang correct? } } return this.speciesId; } - getEvolutionLevels() { - const evolutionLevels = []; + getEvolutionLevels(): [Species, integer][] { + const evolutionLevels: [Species, integer][] = []; //console.log(Species[this.speciesId], pokemonEvolutions[this.speciesId]) @@ -778,8 +782,8 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali return evolutionLevels; } - getPrevolutionLevels() { - const prevolutionLevels = []; + getPrevolutionLevels(): [Species, integer][] { + const prevolutionLevels: [Species, integer][] = []; const allEvolvingPokemon = Object.keys(pokemonEvolutions); for (const p of allEvolvingPokemon) { @@ -801,18 +805,18 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali // This could definitely be written better and more accurate to the getSpeciesForLevel logic, but it is only for generating movesets for evolved Pokemon getSimulatedEvolutionChain(currentLevel: integer, forTrainer: boolean = false, isBoss: boolean = false, player: boolean = false): [Species, integer][] { - const ret = []; + const ret: [Species, integer][] = []; if (pokemonPrevolutions.hasOwnProperty(this.speciesId)) { const prevolutionLevels = this.getPrevolutionLevels().reverse(); const levelDiff = player ? 0 : forTrainer || isBoss ? forTrainer && isBoss ? 2.5 : 5 : 10; ret.push([ prevolutionLevels[0][0], 1 ]); for (let l = 1; l < prevolutionLevels.length; l++) { const evolution = pokemonEvolutions[prevolutionLevels[l - 1][0]].find(e => e.speciesId === prevolutionLevels[l][0]); - ret.push([ prevolutionLevels[l][0], Math.min(Math.max(evolution.level + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution.wildDelay, 0.5) * 5) - 1, 2, evolution.level), currentLevel - 1) ]); + ret.push([ prevolutionLevels[l][0], Math.min(Math.max((evolution?.level!) + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max((evolution?.wildDelay!), 0.5) * 5) - 1, 2, (evolution?.level!)), currentLevel - 1) ]); // TODO: are those bangs correct? } const lastPrevolutionLevel = ret[prevolutionLevels.length - 1][1]; const evolution = pokemonEvolutions[prevolutionLevels[prevolutionLevels.length - 1][0]].find(e => e.speciesId === this.speciesId); - ret.push([ this.speciesId, Math.min(Math.max(lastPrevolutionLevel + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max(evolution.wildDelay, 0.5) * 5), lastPrevolutionLevel + 1, evolution.level), currentLevel) ]); + ret.push([ this.speciesId, Math.min(Math.max(lastPrevolutionLevel + Math.round(Utils.randSeedGauss(0.5, 1 + levelDiff * 0.2) * Math.max((evolution?.wildDelay!), 0.5) * 5), lastPrevolutionLevel + 1, (evolution?.level!)), currentLevel) ]); // TODO: are those bangs correct? } else { ret.push([ this.speciesId, 1 ]); } @@ -853,7 +857,7 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali } getFormSpriteKey(formIndex?: integer) { - if (this.forms.length && formIndex >= this.forms.length) { + if (this.forms.length && (formIndex !== undefined && formIndex >= this.forms.length)) { console.warn(`Attempted accessing form with index ${formIndex} of species ${this.getName()} with only ${this.forms.length || 0} forms`); formIndex = Math.min(formIndex, this.forms.length - 1); } @@ -866,14 +870,14 @@ export default class PokemonSpecies extends PokemonSpeciesForm implements Locali export class PokemonForm extends PokemonSpeciesForm { public formName: string; public formKey: string; - public formSpriteKey: string; + public formSpriteKey: string | null; // This is a collection of form keys that have in-run form changes, but should still be separately selectable from the start screen private starterSelectableKeys: string[] = ["10", "50", "10-pc", "50-pc", "red", "orange", "yellow", "green", "blue", "indigo", "violet"]; - constructor(formName: string, formKey: string, type1: Type, type2: Type, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, + constructor(formName: string, formKey: string, type1: Type, type2: Type | null, height: number, weight: number, ability1: Abilities, ability2: Abilities, abilityHidden: Abilities, baseTotal: integer, baseHp: integer, baseAtk: integer, baseDef: integer, baseSpatk: integer, baseSpdef: integer, baseSpd: integer, - catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs?: boolean, formSpriteKey?: string, isStarterSelectable?: boolean, ) { + catchRate: integer, baseFriendship: integer, baseExp: integer, genderDiffs?: boolean, formSpriteKey?: string | null, isStarterSelectable?: boolean, ) { super(type1, type2, height, weight, ability1, ability2, abilityHidden, baseTotal, baseHp, baseAtk, baseDef, baseSpatk, baseSpdef, baseSpd, catchRate, baseFriendship, baseExp, !!genderDiffs, (!!isStarterSelectable || !formKey)); this.formName = formName; diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index 65caf58ccb9..828c52cae13 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -7,12 +7,12 @@ export { StatusEffect }; export class Status { public effect: StatusEffect; public turnCount: integer; - public cureTurn: integer; + public cureTurn: integer | null; constructor(effect: StatusEffect, turnCount: integer = 0, cureTurn?: integer) { this.effect = effect; this.turnCount = turnCount === undefined ? 0 : turnCount; - this.cureTurn = cureTurn; + this.cureTurn = cureTurn!; // TODO: is this bang correct? } incrementTurn(): void { @@ -24,7 +24,7 @@ export class Status { } } -function getStatusEffectMessageKey(statusEffect: StatusEffect): string { +function getStatusEffectMessageKey(statusEffect: StatusEffect | undefined): string { switch (statusEffect) { case StatusEffect.POISON: return "statusEffect:poison"; @@ -43,7 +43,7 @@ function getStatusEffectMessageKey(statusEffect: StatusEffect): string { } } -export function getStatusEffectObtainText(statusEffect: StatusEffect, pokemonNameWithAffix: string, sourceText?: string): string { +export function getStatusEffectObtainText(statusEffect: StatusEffect | undefined, pokemonNameWithAffix: string, sourceText?: string): string { if (!sourceText) { const i18nKey = `${getStatusEffectMessageKey(statusEffect)}.obtain`as ParseKeys; return i18next.t(i18nKey, { pokemonNameWithAffix: pokemonNameWithAffix }); diff --git a/src/data/trainer-config.ts b/src/data/trainer-config.ts index 87d1901eb01..5f47ce42a62 100644 --- a/src/data/trainer-config.ts +++ b/src/data/trainer-config.ts @@ -288,7 +288,7 @@ export class TrainerConfig { * @param trainerTypeToDeriveFrom - The trainer type to derive from. (If null, the this.trainerType property will be used.) * @returns {TrainerType} - The derived trainer type. */ - getDerivedType(trainerTypeToDeriveFrom: TrainerType = null): TrainerType { + getDerivedType(trainerTypeToDeriveFrom: TrainerType | null = null): TrainerType { let trainerType = trainerTypeToDeriveFrom ? trainerTypeToDeriveFrom : this.trainerType; switch (trainerType) { case TrainerType.RIVAL_2: @@ -361,7 +361,7 @@ export class TrainerConfig { this.nameFemale = i18next.t("trainerNames:rival_female"); } else { // Otherwise, assign the provided female name. - this.nameFemale = nameFemale; + this.nameFemale = nameFemale!; // TODO: is this bang correct? } // Indicate that this trainer configuration includes genders. @@ -587,6 +587,9 @@ export class TrainerConfig { }; } } + + console.warn(`Evil team admin for ${team} not found. Returning empty species pools.`); + return []; } /** @@ -715,7 +718,7 @@ export class TrainerConfig { this.setVictoryBgm("victory_gym"); this.setGenModifiersFunc(party => { const waveIndex = party[0].scene.currentBattle.waveIndex; - return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : null); + return getRandomTeraModifiers(party, waveIndex >= 100 ? 1 : 0, specialtyTypes.length ? specialtyTypes : undefined); }); return this; @@ -749,7 +752,7 @@ export class TrainerConfig { // Set species filter and specialty types if provided, otherwise filter by base total. if (specialtyTypes.length) { - this.setSpeciesFilter(p => specialtyTypes.find(t => p.isOfType(t)) && p.baseTotal >= 450); + this.setSpeciesFilter(p => specialtyTypes.some(t => p.isOfType(t)) && p.baseTotal >= 450); this.setSpecialtyTypes(...specialtyTypes); } else { this.setSpeciesFilter(p => p.baseTotal >= 450); @@ -772,7 +775,7 @@ export class TrainerConfig { this.setHasVoucher(true); this.setBattleBgm("battle_unova_elite"); this.setVictoryBgm("victory_gym"); - this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : null)); + this.setGenModifiersFunc(party => getRandomTeraModifiers(party, 2, specialtyTypes.length ? specialtyTypes : undefined)); return this; } @@ -897,7 +900,7 @@ export class TrainerConfig { start: 1, end: 128 }) - : null; + : ""; console.warn = originalWarn; if (!(scene.anims.exists(trainerKey))) { scene.anims.create({ @@ -983,7 +986,7 @@ function getRandomTeraModifiers(party: EnemyPokemon[], count: integer, types?: T for (let t = 0; t < Math.min(count, party.length); t++) { const randomIndex = Utils.randSeedItem(partyMemberIndexes); partyMemberIndexes.splice(partyMemberIndexes.indexOf(randomIndex), 1); - ret.push(modifierTypes.TERA_SHARD().generateType(null, [Utils.randSeedItem(types ? types : party[randomIndex].getTypes())]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); + ret.push(modifierTypes.TERA_SHARD().generateType([], [Utils.randSeedItem(types ? types : party[randomIndex].getTypes())])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(party[randomIndex]) as PersistentModifier); // TODO: is the bang correct? } return ret; } @@ -1594,7 +1597,7 @@ export const trainerConfigs: TrainerConfigs = { .setSpeciesFilter(species => species.baseTotal >= 540) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; // TODO: is the bang correct? }), [TrainerType.RIVAL_5]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.RIVAL).setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_5) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL], TrainerSlot.TRAINER, true, @@ -1610,7 +1613,7 @@ export const trainerConfigs: TrainerConfigs = { })) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; //TODO: is the bang correct? }), [TrainerType.RIVAL_6]: new TrainerConfig(++t).setName("Finn").setHasGenders("Ivy").setHasCharSprite().setTitle("Rival").setBoss().setStaticParty().setMoneyMultiplier(3).setEncounterBgm("final").setBattleBgm("battle_rival_3").setMixedBattleBgm("battle_rival_3").setPartyTemplates(trainerPartyTemplates.RIVAL_6) .setPartyMemberFunc(0, getRandomPartyMemberFunc([Species.VENUSAUR, Species.CHARIZARD, Species.BLASTOISE, Species.MEGANIUM, Species.TYPHLOSION, Species.FERALIGATR, Species.SCEPTILE, Species.BLAZIKEN, Species.SWAMPERT, Species.TORTERRA, Species.INFERNAPE, Species.EMPOLEON, Species.SERPERIOR, Species.EMBOAR, Species.SAMUROTT, Species.CHESNAUGHT, Species.DELPHOX, Species.GRENINJA, Species.DECIDUEYE, Species.INCINEROAR, Species.PRIMARINA, Species.RILLABOOM, Species.CINDERACE, Species.INTELEON, Species.MEOWSCARADA, Species.SKELEDIRGE, Species.QUAQUAVAL], TrainerSlot.TRAINER, true, @@ -1636,7 +1639,7 @@ export const trainerConfigs: TrainerConfigs = { })) .setGenModifiersFunc(party => { const starter = party[0]; - return [modifierTypes.TERA_SHARD().generateType(null, [starter.species.type1]).withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; + return [modifierTypes.TERA_SHARD().generateType([], [starter.species.type1])!.withIdFromFunc(modifierTypes.TERA_SHARD).newModifier(starter) as PersistentModifier]; // TODO: is the bang correct? }), [TrainerType.ROCKET_BOSS_GIOVANNI_1]: new TrainerConfig(t = TrainerType.ROCKET_BOSS_GIOVANNI_1).setName("Giovanni").initForEvilTeamLeader("Rocket Boss", []).setMixedBattleBgm("battle_rocket_boss").setVictoryBgm("victory_team_plasma") diff --git a/src/data/trainer-names.ts b/src/data/trainer-names.ts index b986378ee47..447f9fd4284 100644 --- a/src/data/trainer-names.ts +++ b/src/data/trainer-names.ts @@ -3,7 +3,7 @@ import * as Utils from "../utils"; class TrainerNameConfig { public urls: string[]; - public femaleUrls: string[]; + public femaleUrls: string[] | null; constructor(type: TrainerType, ...urls: string[]) { this.urls = urls.length ? urls : [ Utils.toReadableString(TrainerType[type]).replace(/ /g, "_") ]; @@ -136,8 +136,11 @@ function fetchAndPopulateTrainerNames(url: string, parser: DOMParser, trainerNam .then(html => { console.log(url); const htmlDoc = parser.parseFromString(html, "text/html"); - const trainerListHeader = htmlDoc.querySelector("#Trainer_list").parentElement; - const elements = [...trainerListHeader.parentElement.childNodes]; + const trainerListHeader = htmlDoc.querySelector("#Trainer_list")?.parentElement; + if (!trainerListHeader) { + return []; + } + const elements = [...(trainerListHeader?.parentElement?.childNodes ?? [])]; const startChildIndex = elements.indexOf(trainerListHeader); const endChildIndex = elements.findIndex(h => h.nodeName === "H2" && elements.indexOf(h) > startChildIndex); const tables = elements.filter(t => { @@ -152,6 +155,9 @@ function fetchAndPopulateTrainerNames(url: string, parser: DOMParser, trainerNam const trainerRows = [...table.querySelectorAll("tr:not(:first-child)")].filter(r => r.children.length === 9); for (const row of trainerRows) { const nameCell = row.firstElementChild; + if (!nameCell) { + continue; + } const content = nameCell.innerHTML; if (content.indexOf(" -1) { const female = /♀/.test(content); diff --git a/src/data/type.ts b/src/data/type.ts index 1330eb83f4b..7a9f7f3605e 100644 --- a/src/data/type.ts +++ b/src/data/type.ts @@ -499,6 +499,8 @@ export function getTypeDamageMultiplier(attackType: integer, defType: integer): case Type.STELLAR: return 1; } + + return 0; } /** diff --git a/src/data/weather.ts b/src/data/weather.ts index 901ad08d164..2421f719e6e 100644 --- a/src/data/weather.ts +++ b/src/data/weather.ts @@ -103,7 +103,7 @@ export class Weather { const field = scene.getField(true); for (const pokemon of field) { - let suppressWeatherEffectAbAttr = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; + let suppressWeatherEffectAbAttr: SuppressWeatherEffectAbAttr | null = pokemon.getAbility().getAttrs(SuppressWeatherEffectAbAttr)[0]; if (!suppressWeatherEffectAbAttr) { suppressWeatherEffectAbAttr = pokemon.hasPassive() ? pokemon.getPassiveAbility().getAttrs(SuppressWeatherEffectAbAttr)[0] : null; } @@ -116,7 +116,7 @@ export class Weather { } } -export function getWeatherStartMessage(weatherType: WeatherType): string { +export function getWeatherStartMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyStartMessage"); @@ -141,7 +141,7 @@ export function getWeatherStartMessage(weatherType: WeatherType): string { return null; } -export function getWeatherLapseMessage(weatherType: WeatherType): string { +export function getWeatherLapseMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyLapseMessage"); @@ -166,7 +166,7 @@ export function getWeatherLapseMessage(weatherType: WeatherType): string { return null; } -export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string { +export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokemon): string | null { switch (weatherType) { case WeatherType.SANDSTORM: return i18next.t("weather:sandstormDamageMessage", {pokemonNameWithAffix: getPokemonNameWithAffix(pokemon)}); @@ -177,7 +177,7 @@ export function getWeatherDamageMessage(weatherType: WeatherType, pokemon: Pokem return null; } -export function getWeatherClearMessage(weatherType: WeatherType): string { +export function getWeatherClearMessage(weatherType: WeatherType): string | null { switch (weatherType) { case WeatherType.SUNNY: return i18next.t("weather:sunnyClearMessage"); @@ -202,7 +202,7 @@ export function getWeatherClearMessage(weatherType: WeatherType): string { return null; } -export function getTerrainStartMessage(terrainType: TerrainType): string { +export function getTerrainStartMessage(terrainType: TerrainType): string | null { switch (terrainType) { case TerrainType.MISTY: return i18next.t("terrain:mistyStartMessage"); @@ -212,10 +212,13 @@ export function getTerrainStartMessage(terrainType: TerrainType): string { return i18next.t("terrain:grassyStartMessage"); case TerrainType.PSYCHIC: return i18next.t("terrain:psychicStartMessage"); + default: + console.warn("getTerrainStartMessage not defined. Using default null"); + return null; } } -export function getTerrainClearMessage(terrainType: TerrainType): string { +export function getTerrainClearMessage(terrainType: TerrainType): string | null { switch (terrainType) { case TerrainType.MISTY: return i18next.t("terrain:mistyClearMessage"); @@ -225,6 +228,9 @@ export function getTerrainClearMessage(terrainType: TerrainType): string { return i18next.t("terrain:grassyClearMessage"); case TerrainType.PSYCHIC: return i18next.t("terrain:psychicClearMessage"); + default: + console.warn("getTerrainClearMessage not defined. Using default null"); + return null; } } diff --git a/src/egg-hatch-phase.ts b/src/egg-hatch-phase.ts index 3965eae74f6..73c88cbde37 100644 --- a/src/egg-hatch-phase.ts +++ b/src/egg-hatch-phase.ts @@ -84,7 +84,7 @@ export class EggHatchPhase extends Phase { this.scene.gameData.eggs.splice(eggIndex, 1); - this.scene.fadeOutBgm(null, false); + this.scene.fadeOutBgm(undefined, false); this.eggHatchHandler = this.scene.ui.getHandler() as EggHatchSceneHandler; @@ -234,8 +234,8 @@ export class EggHatchPhase extends Phase { ease: "Sine.easeInOut", duration: 250, onComplete: () => { - count++; - if (count < repeatCount) { + count!++; + if (count! < repeatCount!) { // we know they are defined return this.doEggShake(intensity, repeatCount, count).then(() => resolve()); } this.scene.tweens.add({ @@ -347,7 +347,7 @@ export class EggHatchPhase extends Phase { this.scene.gameData.updateSpeciesDexIvs(this.pokemon.species.speciesId, this.pokemon.ivs); this.scene.gameData.setPokemonCaught(this.pokemon, true, true).then(() => { this.scene.gameData.setEggMoveUnlocked(this.pokemon.species, this.eggMoveIndex).then(() => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.end(); }); }); @@ -447,6 +447,6 @@ export class EggHatchPhase extends Phase { }, this.egg.id, EGG_SEED.toString()); - return ret; + return ret!; } } diff --git a/src/events/arena.ts b/src/events/arena.ts index 67b423f3b75..9fbbe572601 100644 --- a/src/events/arena.ts +++ b/src/events/arena.ts @@ -81,8 +81,8 @@ export class TagAddedEvent extends ArenaEvent { this.arenaTagType = arenaTagType; this.arenaTagSide = arenaTagSide; - this.arenaTagLayers = arenaTagLayers; - this.arenaTagMaxLayers = arenaTagMaxLayers; + this.arenaTagLayers = arenaTagLayers!; // TODO: is this bang correct? + this.arenaTagMaxLayers = arenaTagMaxLayers!; // TODO: is this bang correct? } } /** diff --git a/src/evolution-phase.ts b/src/evolution-phase.ts index 7633fbb3fdd..7b50a6368f6 100644 --- a/src/evolution-phase.ts +++ b/src/evolution-phase.ts @@ -16,7 +16,7 @@ export class EvolutionPhase extends Phase { protected pokemon: PlayerPokemon; protected lastLevel: integer; - private evolution: SpeciesFormEvolution; + private evolution: SpeciesFormEvolution | null; protected evolutionContainer: Phaser.GameObjects.Container; protected evolutionBaseBg: Phaser.GameObjects.Image; @@ -28,7 +28,7 @@ export class EvolutionPhase extends Phase { protected pokemonEvoSprite: Phaser.GameObjects.Sprite; protected pokemonEvoTintSprite: Phaser.GameObjects.Sprite; - constructor(scene: BattleScene, pokemon: PlayerPokemon, evolution: SpeciesFormEvolution, lastLevel: integer) { + constructor(scene: BattleScene, pokemon: PlayerPokemon, evolution: SpeciesFormEvolution | null, lastLevel: integer) { super(scene); this.pokemon = pokemon; @@ -53,7 +53,7 @@ export class EvolutionPhase extends Phase { return this.end(); } - this.scene.fadeOutBgm(null, false); + this.scene.fadeOutBgm(undefined, false); const evolutionHandler = this.scene.ui.getHandler() as EvolutionSceneHandler; @@ -195,7 +195,7 @@ export class EvolutionPhase extends Phase { this.scene.ui.showText(i18next.t("menu:stoppedEvolving", { pokemonName: preName }), null, () => { this.scene.ui.showText(i18next.t("menu:pauseEvolutionsQuestion", { pokemonName: preName }), null, () => { const end = () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.playBgm(); evolvedPokemon.destroy(); this.end(); diff --git a/src/field/arena.ts b/src/field/arena.ts index cb045cc76ac..923a0a4e286 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -25,8 +25,8 @@ import { TrainerType } from "#enums/trainer-type"; export class Arena { public scene: BattleScene; public biomeType: Biome; - public weather: Weather; - public terrain: Terrain; + public weather: Weather | null; + public terrain: Terrain | null; public tags: ArenaTag[]; public bgm: string; public ignoreAbilities: boolean; @@ -121,7 +121,7 @@ export class Arena { } } - ret = getPokemonSpecies(species); + ret = getPokemonSpecies(species!); if (ret.subLegendary || ret.legendary || ret.mythical) { switch (true) { @@ -292,7 +292,7 @@ export class Arena { trySetWeatherOverride(weather: WeatherType): boolean { this.weather = new Weather(weather, 0); this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); - this.scene.queueMessage(getWeatherStartMessage(weather)); + this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? return true; } @@ -314,13 +314,13 @@ export class Arena { const oldWeatherType = this.weather?.weatherType || WeatherType.NONE; this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null; - this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft)); + this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType!, this.weather?.turnsLeft!)); // TODO: is this bang correct? if (this.weather) { this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1))); - this.scene.queueMessage(getWeatherStartMessage(weather)); + this.scene.queueMessage(getWeatherStartMessage(weather)!); // TODO: is this bang correct? } else { - this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)); + this.scene.queueMessage(getWeatherClearMessage(oldWeatherType)!); // TODO: is this bang correct? } this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { @@ -339,15 +339,15 @@ export class Arena { const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE; this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null; - this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft)); + this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType!, this.terrain?.turnsLeft!)); // TODO: are those bangs correct? if (this.terrain) { if (!ignoreAnim) { this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.MISTY_TERRAIN + (terrain - 1))); } - this.scene.queueMessage(getTerrainStartMessage(terrain)); + this.scene.queueMessage(getTerrainStartMessage(terrain)!); // TODO: is this bang correct? } else { - this.scene.queueMessage(getTerrainClearMessage(oldTerrainType)); + this.scene.queueMessage(getTerrainClearMessage(oldTerrainType)!); // TODO: is this bang correct? } this.scene.getField(true).filter(p => p.isOnField()).map(pokemon => { @@ -554,7 +554,7 @@ export class Arena { this.applyTagsForSide(tagType, ArenaTagSide.BOTH, ...args); } - addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { + addTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, side: ArenaTagSide = ArenaTagSide.BOTH, quiet: boolean = false, targetIndex?: BattlerIndex): boolean { const existingTag = this.getTagOnSide(tagType, side); if (existingTag) { existingTag.onOverlap(this); @@ -568,21 +568,23 @@ export class Arena { } const newTag = getArenaTag(tagType, turnCount || 0, sourceMove, sourceId, targetIndex, side); - this.tags.push(newTag); - newTag.onAdd(this, quiet); + if (newTag) { + this.tags.push(newTag); + newTag.onAdd(this, quiet); - const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; + const { layers = 0, maxLayers = 0 } = newTag instanceof ArenaTrapTag ? newTag : {}; - this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers)); + this.eventTarget.dispatchEvent(new TagAddedEvent(newTag.tagType, newTag.side, newTag.turnCount, layers, maxLayers)); + } return true; } - getTag(tagType: ArenaTagType | Constructor): ArenaTag { + getTag(tagType: ArenaTagType | Constructor): ArenaTag | undefined { return this.getTagOnSide(tagType, ArenaTagSide.BOTH); } - getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag { + getTagOnSide(tagType: ArenaTagType | Constructor, side: ArenaTagSide): ArenaTag | undefined { return typeof(tagType) === "string" ? this.tags.find(t => t.tagType === tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)) : this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); @@ -724,6 +726,9 @@ export class Arena { return 0.000; case Biome.SNOWY_FOREST: return 3.047; + default: + console.warn(`missing bgm loop-point for biome "${Biome[this.biomeType]}" (=${this.biomeType})`); + return 0; } } } @@ -777,12 +782,12 @@ export class ArenaBase extends Phaser.GameObjects.Container { this.player = player; - this.base = scene.addFieldSprite(0, 0, "plains_a", null, 1); + this.base = scene.addFieldSprite(0, 0, "plains_a", undefined, 1); this.base.setOrigin(0, 0); this.props = !player ? new Array(3).fill(null).map(() => { - const ret = scene.addFieldSprite(0, 0, "plains_b", null, 1); + const ret = scene.addFieldSprite(0, 0, "plains_b", undefined, 1); ret.setOrigin(0, 0); ret.setVisible(false); return ret; diff --git a/src/field/damage-number-handler.ts b/src/field/damage-number-handler.ts index 4af219a60b9..ae0692da342 100644 --- a/src/field/damage-number-handler.ts +++ b/src/field/damage-number-handler.ts @@ -3,6 +3,8 @@ import Pokemon, { DamageResult, HitResult } from "./pokemon"; import * as Utils from "../utils"; import { BattlerIndex } from "../battle"; +type TextAndShadowArr = [ string | null, string | null ]; + export default class DamageNumberHandler { private damageNumbers: Map; @@ -24,7 +26,7 @@ export default class DamageNumberHandler { damageNumber.setOrigin(0.5, 1); damageNumber.setScale(baseScale); - let [ textColor, shadowColor ] = [ null, null ]; + let [ textColor, shadowColor ] : TextAndShadowArr = [ null, null ]; switch (result) { case HitResult.SUPER_EFFECTIVE: @@ -62,12 +64,12 @@ export default class DamageNumberHandler { this.damageNumbers.set(battlerIndex, []); } - const yOffset = this.damageNumbers.get(battlerIndex).length * -10; + const yOffset = this.damageNumbers.get(battlerIndex)!.length * -10; if (yOffset) { damageNumber.y += yOffset; } - this.damageNumbers.get(battlerIndex).push(damageNumber); + this.damageNumbers.get(battlerIndex)!.push(damageNumber); if (scene.damageNumbersMode === 1) { scene.tweens.add({ @@ -83,7 +85,7 @@ export default class DamageNumberHandler { alpha: 0, ease: "Sine.easeIn", onComplete: () => { - this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + this.damageNumbers.get(battlerIndex)!.splice(this.damageNumbers.get(battlerIndex)!.indexOf(damageNumber), 1); damageNumber.destroy(true); } }); @@ -167,7 +169,7 @@ export default class DamageNumberHandler { delay: Utils.fixedInt(500), alpha: 0, onComplete: () => { - this.damageNumbers.get(battlerIndex).splice(this.damageNumbers.get(battlerIndex).indexOf(damageNumber), 1); + this.damageNumbers.get(battlerIndex)!.splice(this.damageNumbers.get(battlerIndex)!.indexOf(damageNumber), 1); damageNumber.destroy(true); } } diff --git a/src/field/pokemon-sprite-sparkle-handler.ts b/src/field/pokemon-sprite-sparkle-handler.ts index 5312dd18727..ccf6a098667 100644 --- a/src/field/pokemon-sprite-sparkle-handler.ts +++ b/src/field/pokemon-sprite-sparkle-handler.ts @@ -35,7 +35,7 @@ export default class PokemonSpriteSparkleHandler { const ratioX = s.width / width; const ratioY = s.height / height; const pixel = texture.manager.getPixel(pixelX, pixelY, texture.key, "__BASE"); - if (pixel.alpha) { + if (pixel?.alpha) { const [ xOffset, yOffset ] = [ -s.originX * s.width, -s.originY * s.height]; const sparkle = (s.scene as BattleScene).addFieldSprite(((pokemon?.x || 0) + s.x + pixelX * ratioX + xOffset), ((pokemon?.y || 0) + s.y + pixelY * ratioY + yOffset), "tera_sparkle"); sparkle.pipelineData["ignoreTimeTint"] = s.pipelineData["ignoreTimeTint"]; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a53076bc856..84058cc656f 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -79,8 +79,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public ivs: integer[]; public nature: Nature; public natureOverride: Nature | -1; - public moveset: PokemonMove[]; - public status: Status; + public moveset: (PokemonMove | null)[]; + public status: Status | null; public friendship: integer; public metLevel: integer; public metBiome: Biome | -1; @@ -89,7 +89,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public pauseEvolutions: boolean; public pokerus: boolean; - public fusionSpecies: PokemonSpecies; + public fusionSpecies: PokemonSpecies | null; public fusionFormIndex: integer; public fusionAbilityIndex: integer; public fusionShiny: boolean; @@ -97,7 +97,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public fusionGender: Gender; public fusionLuck: integer; - private summonDataPrimer: PokemonSummonData; + private summonDataPrimer: PokemonSummonData | null; public summonData: PokemonSummonData; public battleData: PokemonBattleData; @@ -107,7 +107,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { public fieldPosition: FieldPosition; public maskEnabled: boolean; - public maskSprite: Phaser.GameObjects.Sprite; + public maskSprite: Phaser.GameObjects.Sprite | null; private shinySparkle: Phaser.GameObjects.Sprite; @@ -169,7 +169,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.nickname = dataSource.nickname; this.natureOverride = dataSource.natureOverride !== undefined ? dataSource.natureOverride : -1; this.moveset = dataSource.moveset; - this.status = dataSource.status; + this.status = dataSource.status!; // TODO: is this bang correct? this.friendship = dataSource.friendship !== undefined ? dataSource.friendship : this.species.baseFriendship; this.metLevel = dataSource.metLevel || 5; this.luck = dataSource.luck; @@ -177,7 +177,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.metSpecies = dataSource.metSpecies ?? (this.metBiome !== -1 ? this.species.speciesId : this.species.getRootSpeciesId(true)); this.pauseEvolutions = dataSource.pauseEvolutions; this.pokerus = !!dataSource.pokerus; - this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : getPokemonSpecies(dataSource.fusionSpecies); + this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : dataSource.fusionSpecies ? getPokemonSpecies(dataSource.fusionSpecies) : null; this.fusionFormIndex = dataSource.fusionFormIndex; this.fusionAbilityIndex = dataSource.fusionAbilityIndex; this.fusionShiny = dataSource.fusionShiny; @@ -350,7 +350,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { loadAssets(ignoreOverride: boolean = true): Promise { return new Promise(resolve => { - const moveIds = this.getMoveset().map(m => m.getMove().id); + const moveIds = this.getMoveset().map(m => m!.getMove().id); // TODO: is this bang correct? Promise.allSettled(moveIds.map(m => initMoveAnim(this.scene, m))) .then(() => { loadMoveAnimAssets(this.scene, moveIds); @@ -438,7 +438,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.species.forms[this.formIndex].formKey; } - getFusionFormKey(): string { + getFusionFormKey(): string | null { if (!this.fusionSpecies) { return null; } @@ -527,7 +527,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.summonData.fusionSpeciesForm; } if (!this.fusionSpecies?.forms?.length || this.fusionFormIndex >= this.fusionSpecies?.forms.length) { - return this.fusionSpecies; + //@ts-ignore + return this.fusionSpecies; // TODO: I don't even know how to fix this... A complete cluster of classes involved + null } return this.fusionSpecies?.forms[this.fusionFormIndex]; } @@ -536,7 +537,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getAt(0) as Phaser.GameObjects.Sprite; } - getTintSprite(): Phaser.GameObjects.Sprite { + getTintSprite(): Phaser.GameObjects.Sprite | null { return !this.maskEnabled ? this.getAt(1) as Phaser.GameObjects.Sprite : this.maskSprite; @@ -562,7 +563,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } updateSpritePipelineData(): void { - [ this.getSprite(), this.getTintSprite() ].map(s => s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType())); + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => s.pipelineData["teraColor"] = getTypeRgb(this.getTeraType())); this.updateInfo(true); } @@ -610,7 +611,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } playAnim(): void { - this.tryPlaySprite(this.getSprite(), this.getTintSprite(), this.getBattleSpriteKey()); + this.tryPlaySprite(this.getSprite(), this.getTintSprite()!, this.getBattleSpriteKey()); // TODO: is the bag correct? } getFieldPositionOffset(): [ number, number ] { @@ -870,7 +871,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { abstract isBoss(): boolean; - getMoveset(ignoreOverride?: boolean): PokemonMove[] { + getMoveset(ignoreOverride?: boolean): (PokemonMove | null)[] { const ret = !ignoreOverride && this.summonData?.moveset ? this.summonData.moveset : this.moveset; @@ -921,7 +922,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (this.metBiome === -1) { levelMoves = this.getUnlockedEggMoves().concat(levelMoves); } - return levelMoves.filter(lm => !this.moveset.some(m => m.moveId === lm)); + return levelMoves.filter(lm => !this.moveset.some(m => m?.moveId === lm)); } /** @@ -932,7 +933,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns array of {@linkcode Type} */ getTypes(includeTeraType = false, forDefend: boolean = false, ignoreOverride?: boolean): Type[] { - const types = []; + const types : Type[] = []; if (includeTeraType) { const teraType = this.getTeraType(); @@ -942,7 +943,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (!types.length || !includeTeraType) { - if (!ignoreOverride && this.summonData?.types) { + if (!ignoreOverride && this.summonData?.types && this.summonData.types.length !== 0) { this.summonData.types.forEach(t => types.push(t)); } else { const speciesForm = this.getSpeciesForm(ignoreOverride); @@ -1115,7 +1116,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } } - return (this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); + return (!!this.hp || ability.isBypassFaint) && !ability.conditions.find(condition => !condition(this)); } /** @@ -1242,7 +1243,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ? moveOrType : undefined; const moveType = (moveOrType instanceof Move) - ? move.type + ? move!.type // TODO: is this bang correct? : moveOrType; if (moveType === Type.STELLAR) { @@ -1285,7 +1286,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - return multiplier; + return multiplier as TypeDamageMultiplier; } /** @@ -1327,7 +1328,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return (atkScore + defScore) * hpDiffRatio; } - getEvolution(): SpeciesFormEvolution { + getEvolution(): SpeciesFormEvolution | null { if (pokemonEvolutions.hasOwnProperty(this.species.speciesId)) { const evolutions = pokemonEvolutions[this.species.speciesId]; for (const e of evolutions) { @@ -1339,7 +1340,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - if (this.isFusion() && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { + if (this.isFusion() && this.fusionSpecies && pokemonEvolutions.hasOwnProperty(this.fusionSpecies.speciesId)) { const fusionEvolutions = pokemonEvolutions[this.fusionSpecies.speciesId].map(e => new FusionSpeciesFormEvolution(this.species.speciesId, e)); for (const fe of fusionEvolutions) { if (!fe.item && this.level >= fe.level && (!fe.preFormKey || this.getFusionFormKey() === fe.preFormKey)) { @@ -1549,7 +1550,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } clearFusionSpecies(): void { - this.fusionSpecies = undefined; + this.fusionSpecies = null; this.fusionFormIndex = 0; this.fusionAbilityIndex = 0; this.fusionShiny = false; @@ -1709,9 +1710,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { // Sqrt the weight of any damaging moves with overlapping types. This is about a 0.05 - 0.1 multiplier. // Other damaging moves 2x weight if 0-1 damaging moves, 0.5x if 2, 0.125x if 3. These weights double if STAB. // Status moves remain unchanged on weight, this encourages 1-2 - movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)).map(m => [m[0], this.moveset.some(mo => mo.getMove().category !== MoveCategory.STATUS && mo.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1]/Math.max(Math.pow(4, this.moveset.filter(mo => mo.getMove().power > 1).length)/8,0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1]]); + movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)).map(m => [m[0], this.moveset.some(mo => mo?.getMove().category !== MoveCategory.STATUS && mo?.getMove().type === allMoves[m[0]].type) ? Math.ceil(Math.sqrt(m[1])) : allMoves[m[0]].category !== MoveCategory.STATUS ? Math.ceil(m[1]/Math.max(Math.pow(4, this.moveset.filter(mo => (mo?.getMove().power!) > 1).length)/8,0.5) * (this.isOfType(allMoves[m[0]].type) ? 2 : 1)) : m[1]]); // TODO: is this bang correct? } else { // Non-trainer pokemon just use normal weights - movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo.moveId)); + movePool = baseWeights.filter(m => !this.moveset.some(mo => m[0] === mo?.moveId)); } const totalWeight = movePool.reduce((v, m) => v + m[1], 0); let rand = Utils.randSeedInt(totalWeight); @@ -1729,7 +1730,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const move = this.getMoveset().length > moveIndex ? this.getMoveset()[moveIndex] : null; - return move?.isUsable(this, ignorePp); + return move?.isUsable(this, ignorePp)!; // TODO: is this bang correct? } showInfo(): void { @@ -1812,7 +1813,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.levelExp = this.exp - getLevelTotalExp(this.level, this.species.growthRate); } - getOpponent(targetIndex: integer): Pokemon { + getOpponent(targetIndex: integer): Pokemon | null { const ret = this.getOpponents()[targetIndex]; if (ret.summonData) { return ret; @@ -1977,7 +1978,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(CritBoosterModifier, source.isPlayer(), source, critLevel); this.scene.applyModifiers(TempBattleStatBoosterModifier, source.isPlayer(), TempBattleStat.CRIT, critLevel); const bonusCrit = new Utils.BooleanHolder(false); - if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { + //@ts-ignore + if (applyAbAttrs(BonusCritAbAttr, source, null, bonusCrit)) { // TODO: resolve ts-ignore. This is a promise. Checking a promise is bogus. if (bonusCrit.value) { critLevel.value += 1; } @@ -2000,7 +2002,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false; } } - const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, null, isCritical)); + const sourceAtk = new Utils.IntegerHolder(source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical)); const targetDef = new Utils.IntegerHolder(this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical)); const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1); applyAbAttrs(MultCritAbAttr, source, null, criticalMultiplier); @@ -2088,6 +2090,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isCritical = false; result = HitResult.EFFECTIVE; } + result = result!; // telling TS compiler that result is defined! if (!result) { if (!typeMultiplier.value) { @@ -2192,7 +2195,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (damage) { - const attacker = this.scene.getPokemonById(source.id); + const attacker = this.scene.getPokemonById(source.id)!; // TODO: is this bang correct? destinyTag?.lapse(attacker, BattlerTagLapseType.CUSTOM); } } @@ -2292,7 +2295,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { isMax(): boolean { const maxForms = [SpeciesFormKey.GIGANTAMAX, SpeciesFormKey.GIGANTAMAX_RAPID, SpeciesFormKey.GIGANTAMAX_SINGLE, SpeciesFormKey.ETERNAMAX] as string[]; - return maxForms.includes(this.getFormKey()) || maxForms.includes(this.getFusionFormKey()); + return maxForms.includes(this.getFormKey()) || (!!this.getFusionFormKey() && maxForms.includes(this.getFusionFormKey()!)); } addTag(tagType: BattlerTagType, turnCount: integer = 0, sourceMove?: Moves, sourceId?: integer): boolean { @@ -2302,7 +2305,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return false; } - const newTag = getBattlerTag(tagType, turnCount, sourceMove, sourceId); + const newTag = getBattlerTag(tagType, turnCount, sourceMove!, sourceId!); // TODO: are the bangs correct? const cancelled = new Utils.BooleanHolder(false); applyPreApplyBattlerTagAbAttrs(BattlerTagImmunityAbAttr, this, newTag, cancelled); @@ -2321,18 +2324,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } /** @overload */ - getTag(tagType: BattlerTagType): BattlerTag; + getTag(tagType: BattlerTagType): BattlerTag | null; /** @overload */ - getTag(tagType: Constructor): T; + getTag(tagType: Constructor): T | null; - getTag(tagType: BattlerTagType | Constructor): BattlerTag { + getTag(tagType: BattlerTagType | Constructor): BattlerTag | null { if (!this.summonData) { return null; } - return tagType instanceof Function + return (tagType instanceof Function ? this.summonData.tags.find(t => t instanceof tagType) - : this.summonData.tags.find(t => t.tagType === tagType); + : this.summonData.tags.find(t => t.tagType === tagType) + )!; // TODO: is this bang correct? } findTag(tagFilter: ((tag: BattlerTag) => boolean)) { @@ -2436,7 +2440,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.getMoveHistory().push(turnMove); } - getLastXMoves(turnCount?: integer): TurnMove[] { + getLastXMoves(turnCount: integer = 0): TurnMove[] { const moveHistory = this.getMoveHistory(); return moveHistory.slice(turnCount >= 0 ? Math.max(moveHistory.length - (turnCount || 1), 0) : 0, moveHistory.length).reverse(); } @@ -2514,9 +2518,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let frameThreshold: number; sprite.anims.pause(); - tintSprite.anims.pause(); + tintSprite?.anims.pause(); - let faintCryTimer = this.scene.time.addEvent({ + let faintCryTimer : Phaser.Time.TimerEvent | null = this.scene.time.addEvent({ delay: Utils.fixedInt(delay), repeat: -1, callback: () => { @@ -2526,7 +2530,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { while (frameProgress > frameThreshold) { if (sprite.anims.duration) { sprite.anims.nextFrame(); - tintSprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } frameProgress -= frameThreshold; } @@ -2534,7 +2538,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { rate *= 0.99; cry.setRate(rate); } else { - faintCryTimer.destroy(); + faintCryTimer?.destroy(); faintCryTimer = null; if (callback) { callback(); @@ -2593,9 +2597,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { let frameThreshold: number; sprite.anims.pause(); - tintSprite.anims.pause(); + tintSprite?.anims.pause(); - let faintCryTimer = this.scene.time.addEvent({ + let faintCryTimer: Phaser.Time.TimerEvent | null = this.scene.time.addEvent({ delay: Utils.fixedInt(delay), repeat: -1, callback: () => { @@ -2605,7 +2609,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { while (frameProgress > frameThreshold) { if (sprite.anims.duration) { sprite.anims.nextFrame(); - tintSprite.anims.nextFrame(); + tintSprite?.anims.nextFrame(); } frameProgress -= frameThreshold; } @@ -2622,7 +2626,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { fusionCry.setRate(rate); } if ((!cry || cry.pendingRemove) && (!fusionCry || fusionCry.pendingRemove)) { - faintCryTimer.destroy(); + faintCryTimer?.destroy(); faintCryTimer = null; if (callback) { callback(); @@ -2653,7 +2657,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.gender !== Gender.GENDERLESS && pokemon.gender === (this.gender === Gender.MALE ? Gender.FEMALE : Gender.MALE); } - canSetStatus(effect: StatusEffect, quiet: boolean = false, overrideStatus: boolean = false, sourcePokemon: Pokemon = null): boolean { + canSetStatus(effect: StatusEffect | undefined, quiet: boolean = false, overrideStatus: boolean = false, sourcePokemon: Pokemon | null = null): boolean { if (effect !== StatusEffect.FAINT) { if (overrideStatus ? this.status?.effect === effect : this.status) { return false; @@ -2704,7 +2708,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } break; case StatusEffect.FREEZE: - if (this.isOfType(Type.ICE) || [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(this.scene?.arena.weather?.weatherType)) { + if (this.isOfType(Type.ICE) || (this.scene?.arena?.weather?.weatherType &&[WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(this.scene.arena.weather.weatherType))) { return false; } break; @@ -2728,7 +2732,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return true; } - trySetStatus(effect: StatusEffect, asPhase: boolean = false, sourcePokemon: Pokemon = null, cureTurn: integer = 0, sourceText: string = null): boolean { + trySetStatus(effect: StatusEffect | undefined, asPhase: boolean = false, sourcePokemon: Pokemon | null = null, cureTurn: integer | null = 0, sourceText: string | null = null): boolean { if (!this.canSetStatus(effect, asPhase, false, sourcePokemon)) { return false; } @@ -2742,7 +2746,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } if (asPhase) { - this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText, sourcePokemon)); + this.scene.unshiftPhase(new ObtainStatusEffectPhase(this.scene, this.getBattlerIndex(), effect, cureTurn, sourceText!, sourcePokemon!)); // TODO: are these bangs correct? return true; } @@ -2770,6 +2774,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } + statusCureTurn = statusCureTurn!; // tell TS compiler it's defined + effect = effect!; // If `effect` is undefined then `trySetStatus()` will have already returned early via the `canSetStatus()` call this.status = new Status(effect, 0, statusCureTurn?.value); if (effect !== StatusEffect.FAINT) { @@ -2790,7 +2796,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { if (!revive && lastStatus === StatusEffect.FAINT) { return; } - this.status = undefined; + this.status = null; if (lastStatus === StatusEffect.SLEEP) { this.setFrameRate(12); if (this.getTag(BattlerTagType.NIGHTMARE)) { @@ -2858,16 +2864,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { setFrameRate(frameRate: integer) { this.scene.anims.get(this.getBattleSpriteKey()).frameRate = frameRate; this.getSprite().play(this.getBattleSpriteKey()); - this.getTintSprite().play(this.getBattleSpriteKey()); + this.getTintSprite()?.play(this.getBattleSpriteKey()); } tint(color: number, alpha?: number, duration?: integer, ease?: string) { const tintSprite = this.getTintSprite(); - tintSprite.setTintFill(color); - tintSprite.setVisible(true); + tintSprite?.setTintFill(color); + tintSprite?.setVisible(true); if (duration) { - tintSprite.setAlpha(0); + tintSprite?.setAlpha(0); this.scene.tweens.add({ targets: tintSprite, @@ -2876,7 +2882,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { ease: ease || "Linear" }); } else { - tintSprite.setAlpha(alpha); + tintSprite?.setAlpha(alpha); } } @@ -2890,32 +2896,32 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { duration: duration, ease: ease || "Linear", onComplete: () => { - tintSprite.setVisible(false); - tintSprite.setAlpha(1); + tintSprite?.setVisible(false); + tintSprite?.setAlpha(1); } }); } else { - tintSprite.setVisible(false); - tintSprite.setAlpha(1); + tintSprite?.setVisible(false); + tintSprite?.setAlpha(1); } } enableMask() { if (!this.maskEnabled) { this.maskSprite = this.getTintSprite(); - this.maskSprite.setVisible(true); - this.maskSprite.setPosition(this.x * this.parentContainer.scale + this.parentContainer.x, + this.maskSprite?.setVisible(true); + this.maskSprite?.setPosition(this.x * this.parentContainer.scale + this.parentContainer.x, this.y * this.parentContainer.scale + this.parentContainer.y); - this.maskSprite.setScale(this.getSpriteScale() * this.parentContainer.scale); + this.maskSprite?.setScale(this.getSpriteScale() * this.parentContainer.scale); this.maskEnabled = true; } } disableMask() { if (this.maskEnabled) { - this.maskSprite.setVisible(false); - this.maskSprite.setPosition(0, 0); - this.maskSprite.setScale(this.getSpriteScale()); + this.maskSprite?.setVisible(false); + this.maskSprite?.setPosition(0, 0); + this.maskSprite?.setScale(this.getSpriteScale()); this.maskSprite = null; this.maskEnabled = false; } @@ -2930,7 +2936,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { updateFusionPalette(ignoreOveride?: boolean): void { if (!this.getFusionSpeciesForm(ignoreOveride)) { - [ this.getSprite(), this.getTintSprite() ].map(s => { + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => { s.pipelineData[`spriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = []; s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = []; }); @@ -2966,9 +2972,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const frame = [ sourceFrame, sourceBackFrame, fusionFrame, fusionBackFrame ][c]; canv.width = frame.width; canv.height = frame.height; - context.drawImage([ sourceImage, sourceBackImage, fusionImage, fusionBackImage ][c], frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); - const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); - pixelData.push(imageData.data); + + if (context) { + context.drawImage([ sourceImage, sourceBackImage, fusionImage, fusionBackImage ][c], frame.cutX, frame.cutY, frame.width, frame.height, 0, 0, frame.width, frame.height); + const imageData = context.getImageData(frame.cutX, frame.cutY, frame.width, frame.height); + pixelData.push(imageData.data); + } }); for (let f = 0; f < 2; f++) { @@ -2988,7 +2997,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const color = Utils.rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); - [ r, g, b, a ] = mappedPixel; + if (mappedPixel) { + [ r, g, b, a ] = mappedPixel; + } } } if (!spriteColors.find(c => c[0] === r && c[1] === g && c[2] === b)) { @@ -3000,7 +3011,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const fusionSpriteColors = JSON.parse(JSON.stringify(spriteColors)); - const pixelColors = []; + const pixelColors: number[] = []; for (let f = 0; f < 2; f++) { for (let i = 0; i < pixelData[f].length; i += 4) { const total = pixelData[f].slice(i, i + 3).reduce((total: integer, value: integer) => total + value, 0); @@ -3011,7 +3022,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - const fusionPixelColors = []; + const fusionPixelColors : number[] = []; for (let f = 0; f < 2; f++) { const variantColors = variantColorCache[!f ? fusionSpriteKey : fusionBackSpriteKey]; const variantColorSet = new Map(); @@ -3030,7 +3041,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { const color = Utils.rgbaToInt([r, g, b, a]); if (variantColorSet.has(color)) { const mappedPixel = variantColorSet.get(color); - [ r, g, b, a ] = mappedPixel; + if (mappedPixel) { + [ r, g, b, a ] = mappedPixel; + } } } fusionPixelColors.push(argbFromRgba({ r, g, b, a })); @@ -3050,9 +3063,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { Math.random = originalRandom; + paletteColors = paletteColors!; // tell TS compiler that paletteColors is defined! + fusionPaletteColors = fusionPaletteColors!; // TS compiler that fusionPaletteColors is defined! const [ palette, fusionPalette ] = [ paletteColors, fusionPaletteColors ] .map(paletteColors => { - let keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a) < paletteColors.get(b) ? 1 : -1); + let keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1); let rgbaColors: Map; let hsvColors: Map; @@ -3065,19 +3080,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { map.set(k, Object.values(rgbaFromArgb(k))); return map; }, new Map()); hsvColors = Array.from(rgbaColors.keys()).reduce((map: Map, k: number) => { - const rgb = rgbaColors.get(k).slice(0, 3); + const rgb = rgbaColors.get(k)!.slice(0, 3); map.set(k, Utils.rgbToHsv(rgb[0], rgb[1], rgb[2])); return map; }, new Map()); for (let c = keys.length - 1; c >= 0; c--) { - const hsv = hsvColors.get(keys[c]); + const hsv = hsvColors.get(keys[c])!; for (let c2 = 0; c2 < c; c2++) { - const hsv2 = hsvColors.get(keys[c2]); + const hsv2 = hsvColors.get(keys[c2])!; const diff = Math.abs(hsv[0] - hsv2[0]); if (diff < 30 || diff >= 330) { if (mappedColors.has(keys[c])) { - mappedColors.get(keys[c]).push(keys[c2]); + mappedColors.get(keys[c])!.push(keys[c2]); } else { mappedColors.set(keys[c], [ keys[c2] ]); } @@ -3087,10 +3102,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } mappedColors.forEach((values: integer[], key: integer) => { - const keyColor = rgbaColors.get(key); - const valueColors = values.map(v => rgbaColors.get(v)); + const keyColor = rgbaColors.get(key)!; + const valueColors = values.map(v => rgbaColors.get(v)!); const color = keyColor.slice(0); - let count = paletteColors.get(key); + let count = paletteColors.get(key)!; for (const value of values) { const valueCount = paletteColors.get(value); if (!valueCount) { @@ -3100,10 +3115,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } for (let c = 0; c < 3; c++) { - color[c] *= (paletteColors.get(key) / count); + color[c] *= (paletteColors.get(key)! / count); values.forEach((value: integer, i: integer) => { if (paletteColors.has(value)) { - const valueCount = paletteColors.get(value); + const valueCount = paletteColors.get(value)!; color[c] += valueColors[i][c] * (valueCount / count); } }); @@ -3121,7 +3136,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { paletteColors.set(argbFromRgba({ r: color[0], g: color[1], b: color[2], a: color[3] }), count); }); - keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a) < paletteColors.get(b) ? 1 : -1); + keys = Array.from(paletteColors.keys()).sort((a: integer, b: integer) => paletteColors.get(a)! < paletteColors.get(b)! ? 1 : -1); } while (mappedColors.size); return keys.map(c => Object.values(rgbaFromArgb(c))); @@ -3152,7 +3167,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } } - [ this.getSprite(), this.getTintSprite() ].map(s => { + [ this.getSprite(), this.getTintSprite() ].filter(s => !!s).map(s => { s.pipelineData[`spriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = spriteColors; s.pipelineData[`fusionSpriteColors${ignoreOveride && this.summonData?.speciesForm ? "Base" : ""}`] = fusionSpriteColors; }); @@ -3216,7 +3231,7 @@ export default interface Pokemon { export class PlayerPokemon extends Pokemon { public compatibleTms: Moves[]; - constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex: integer, formIndex: integer, gender: Gender, shiny: boolean, variant: Variant, ivs: integer[], nature: Nature, dataSource: Pokemon | PokemonData) { + constructor(scene: BattleScene, species: PokemonSpecies, level: integer, abilityIndex?: integer, formIndex?: integer, gender?: Gender, shiny?: boolean, variant?: Variant, ivs?: integer[], nature?: Nature, dataSource?: Pokemon | PokemonData) { super(scene, 106, 148, species, level, abilityIndex, formIndex, gender, shiny, variant, ivs, nature, dataSource); if (Overrides.STATUS_OVERRIDE) { @@ -3320,11 +3335,11 @@ export class PlayerPokemon extends Pokemon { addFriendship(friendship: integer): void { const starterSpeciesId = this.species.getRootSpeciesId(); - const fusionStarterSpeciesId = this.isFusion() ? this.fusionSpecies.getRootSpeciesId() : 0; + const fusionStarterSpeciesId = this.isFusion() && this.fusionSpecies ? this.fusionSpecies.getRootSpeciesId() : 0; const starterData = [ this.scene.gameData.starterData[starterSpeciesId], fusionStarterSpeciesId ? this.scene.gameData.starterData[fusionStarterSpeciesId] : null - ].filter(d => d); + ].filter(d => !!d); const amount = new Utils.IntegerHolder(friendship); const starterAmount = new Utils.IntegerHolder(Math.floor(friendship * (this.scene.gameMode.isClassic && friendship > 0 ? 2 : 1) / (fusionStarterSpeciesId ? 2 : 1))); if (amount.value > 0) { @@ -3388,7 +3403,10 @@ export class PlayerPokemon extends Pokemon { }); } - getPossibleEvolution(evolution: SpeciesFormEvolution): Promise { + getPossibleEvolution(evolution: SpeciesFormEvolution | null): Promise { + if (!evolution) { + return new Promise(resolve => resolve(this)); + } return new Promise(resolve => { const evolutionSpecies = getPokemonSpecies(evolution.speciesId); const isFusion = evolution instanceof FusionSpeciesFormEvolution; @@ -3409,7 +3427,10 @@ export class PlayerPokemon extends Pokemon { }); } - evolve(evolution: SpeciesFormEvolution, preEvolution: PokemonSpeciesForm): Promise { + evolve(evolution: SpeciesFormEvolution | null, preEvolution: PokemonSpeciesForm): Promise { + if (!evolution) { + return new Promise(resolve => resolve()); + } return new Promise(resolve => { this.pauseEvolutions = false; // Handles Nincada evolving into Ninjask + Shedinja @@ -3421,7 +3442,7 @@ export class PlayerPokemon extends Pokemon { this.fusionSpecies = getPokemonSpecies(evolution.speciesId); } if (evolution.preFormKey !== null) { - const formIndex = Math.max((!isFusion ? this.species : this.fusionSpecies).forms.findIndex(f => f.formKey === evolution.evoFormKey), 0); + const formIndex = Math.max((!isFusion || !this.fusionSpecies ? this.species : this.fusionSpecies).forms.findIndex(f => f.formKey === evolution.evoFormKey), 0); if (!isFusion) { this.formIndex = formIndex; } else { @@ -3477,10 +3498,10 @@ export class PlayerPokemon extends Pokemon { const isFusion = evolution instanceof FusionSpeciesFormEvolution; const evoSpecies = (!isFusion ? this.species : this.fusionSpecies); - if (evoSpecies.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) { + if (evoSpecies?.speciesId === Species.NINCADA && evolution.speciesId === Species.NINJASK) { const newEvolution = pokemonEvolutions[evoSpecies.speciesId][1]; - if (newEvolution.condition.predicate(this)) { + if (newEvolution.condition?.predicate(this)) { const newPokemon = this.scene.addPlayerPokemon(this.species, this.level, this.abilityIndex, this.formIndex, undefined, this.shiny, this.variant, this.ivs, this.nature); newPokemon.natureOverride = this.natureOverride; newPokemon.passive = this.passive; @@ -3576,7 +3597,7 @@ export class PlayerPokemon extends Pokemon { if (!this.isFainted()) { // If this Pokemon hasn't fainted, make sure the HP wasn't set over the new maximum this.hp = Math.min(this.hp, this.stats[Stat.HP]); - this.status = getRandomStatus(this.status, pokemon.status); // Get a random valid status between the two + this.status = getRandomStatus(this.status!, pokemon.status!); // Get a random valid status between the two // TODO: are the bangs correct? } else if (!pokemon.isFainted()) { // If this Pokemon fainted but the other hasn't, make sure the HP wasn't set to zero this.hp = Math.max(this.hp, 1); @@ -3601,7 +3622,7 @@ export class PlayerPokemon extends Pokemon { this.scene.removePartyMemberModifiers(fusedPartyMemberIndex); this.scene.getParty().splice(fusedPartyMemberIndex, 1)[0]; const newPartyMemberIndex = this.scene.getParty().indexOf(this); - pokemon.getMoveset(true).map(m => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m.getMove().id))); + pokemon.getMoveset(true).map(m => this.scene.unshiftPhase(new LearnMovePhase(this.scene, newPartyMemberIndex, m!.getMove().id))); // TODO: is the bang correct? pokemon.destroy(); this.updateFusionPalette(); resolve(); @@ -3621,9 +3642,9 @@ export class PlayerPokemon extends Pokemon { /** Returns a deep copy of this Pokemon's moveset array */ copyMoveset(): PokemonMove[] { - const newMoveset = []; + const newMoveset : PokemonMove[] = []; this.moveset.forEach(move => - newMoveset.push(new PokemonMove(move.moveId, 0, move.ppUp, move.virtual))); + newMoveset.push(new PokemonMove(move!.moveId, 0, move!.ppUp, move!.virtual))); // TODO: are those bangs correct? return newMoveset; } @@ -3637,9 +3658,9 @@ export class EnemyPokemon extends Pokemon { /** To indicate of the instance was populated with a dataSource -> e.g. loaded & populated from session data */ public readonly isPopulatedFromDataSource: boolean; - constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource: PokemonData) { + constructor(scene: BattleScene, species: PokemonSpecies, level: integer, trainerSlot: TrainerSlot, boss: boolean, dataSource?: PokemonData) { super(scene, 236, 84, species, level, dataSource?.abilityIndex, dataSource?.formIndex, - dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, null, dataSource ? dataSource.nature : undefined, dataSource); + dataSource?.gender, dataSource ? dataSource.shiny : false, dataSource ? dataSource.variant : undefined, undefined, dataSource ? dataSource.nature : undefined, dataSource); this.trainerSlot = trainerSlot; this.isPopulatedFromDataSource = !!dataSource; // if a dataSource is provided, then it was populated from dataSource @@ -3672,7 +3693,7 @@ export class EnemyPokemon extends Pokemon { let speciesId = species.speciesId; while ((prevolution = pokemonPrevolutions[speciesId])) { const evolution = pokemonEvolutions[prevolution].find(pe => pe.speciesId === speciesId && (!pe.evoFormKey || pe.evoFormKey === this.getFormKey())); - if (evolution.condition?.enforceFunc) { + if (evolution?.condition?.enforceFunc) { evolution.condition.enforceFunc(this); } speciesId = prevolution; @@ -3748,7 +3769,7 @@ export class EnemyPokemon extends Pokemon { getNextMove(): QueuedMove { // If this Pokemon has a move already queued, return it. const queuedMove = this.getMoveQueue().length - ? this.getMoveset().find(m => m.moveId === this.getMoveQueue()[0].move) + ? this.getMoveset().find(m => m?.moveId === this.getMoveQueue()[0].move) : null; if (queuedMove) { if (queuedMove.isUsable(this, this.getMoveQueue()[0].ignorePP)) { @@ -3760,24 +3781,24 @@ export class EnemyPokemon extends Pokemon { } // Filter out any moves this Pokemon cannot use - const movePool = this.getMoveset().filter(m => m.isUsable(this)); + const movePool = this.getMoveset().filter(m => m?.isUsable(this)); // If no moves are left, use Struggle. Otherwise, continue with move selection if (movePool.length) { // If there's only 1 move in the move pool, use it. if (movePool.length === 1) { - return { move: movePool[0].moveId, targets: this.getNextTargets(movePool[0].moveId) }; + return { 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. const encoreTag = this.getTag(EncoreTag) as EncoreTag; if (encoreTag) { - const encoreMove = movePool.find(m => m.moveId === encoreTag.moveId); + const encoreMove = movePool.find(m => m?.moveId === encoreTag.moveId); if (encoreMove) { return { move: encoreMove.moveId, targets: this.getNextTargets(encoreMove.moveId) }; } } switch (this.aiType) { case AiType.RANDOM: // No enemy should spawn with this AI type in-game - const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)].moveId; + const moveId = movePool[this.scene.randBattleSeedInt(movePool.length)]!.moveId; // TODO: is the bang correct? return { move: moveId, targets: this.getNextTargets(moveId) }; case AiType.SMART_RANDOM: case AiType.SMART: @@ -3787,9 +3808,9 @@ export class EnemyPokemon extends Pokemon { * For more information on how benefit scores are calculated, see `docs/enemy-ai.md`. */ const moveScores = movePool.map(() => 0); - const moveTargets = Object.fromEntries(movePool.map(m => [ m.moveId, this.getNextTargets(m.moveId) ])); + const moveTargets = Object.fromEntries(movePool.map(m => [ m!.moveId, this.getNextTargets(m!.moveId) ])); // TODO: are those bangs correct? for (const m in movePool) { - const pokemonMove = movePool[m]; + const pokemonMove = movePool[m]!; // TODO: is the bang correct? const move = pokemonMove.getMove(); let moveScore = moveScores[m]; @@ -3870,8 +3891,8 @@ export class EnemyPokemon extends Pokemon { r++; } } - console.log(movePool.map(m => m.getName()), moveScores, r, sortedMovePool.map(m => m.getName())); - return { move: sortedMovePool[r].moveId, targets: moveTargets[sortedMovePool[r].moveId] }; + console.log(movePool.map(m => m!.getName()), moveScores, r, sortedMovePool.map(m => m!.getName())); // TODO: are those bangs correct? + return { move: sortedMovePool[r]!.moveId, targets: moveTargets[sortedMovePool[r]!.moveId] }; } } @@ -3934,7 +3955,7 @@ export class EnemyPokemon extends Pokemon { } const thresholds: integer[] = []; - let totalWeight: integer; + let totalWeight: integer = 0; targetWeights.reduce((total: integer, w: integer) => { total += w; thresholds.push(total); @@ -3948,7 +3969,7 @@ export class EnemyPokemon extends Pokemon { * is greater than that random number. */ const randValue = this.scene.randBattleSeedInt(totalWeight); - let targetIndex: integer; + let targetIndex: integer = 0; thresholds.every((t, i) => { if (randValue >= t) { @@ -4124,7 +4145,7 @@ export class EnemyPokemon extends Pokemon { addToParty(pokeballType: PokeballType) { const party = this.scene.getParty(); - let ret: PlayerPokemon = null; + let ret: PlayerPokemon | null = null; if (party.length < 6) { this.pokeball = pokeballType; @@ -4173,15 +4194,15 @@ export class PokemonSummonData { public abilitySuppressed: boolean = false; public abilitiesApplied: Abilities[] = []; - public speciesForm: PokemonSpeciesForm; + public speciesForm: PokemonSpeciesForm | null; public fusionSpeciesForm: PokemonSpeciesForm; public ability: Abilities = Abilities.NONE; public gender: Gender; public fusionGender: Gender; public stats: integer[]; - public moveset: PokemonMove[]; + public moveset: (PokemonMove | null)[]; // If not initialized this value will not be populated from save data. - public types: Type[] = null; + public types: Type[] = []; } export class PokemonBattleData { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index 470e8d2267f..1348749d964 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -208,7 +208,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } getPartyLevels(waveIndex: integer): integer[] { - const ret = []; + const ret: number[] = []; const partyTemplate = this.getPartyTemplate(); const difficultyWaveIndex = this.scene.gameMode.getWaveForDifficulty(waveIndex); @@ -257,7 +257,7 @@ export default class Trainer extends Phaser.GameObjects.Container { genPartyMember(index: integer): EnemyPokemon { const battle = this.scene.currentBattle; - const level = battle.enemyLevels[index]; + const level = battle.enemyLevels?.[index]!; // TODO: is this bang correct? let ret: EnemyPokemon; @@ -290,7 +290,7 @@ export default class Trainer extends Phaser.GameObjects.Container { } // Create an empty species pool (which will be set to one of the species pools based on the index) - let newSpeciesPool = []; + let newSpeciesPool: Species[] = []; let useNewSpeciesPool = false; // If we are in a double battle of named trainers, we need to use alternate species pools (generate half the party from each trainer) @@ -315,7 +315,7 @@ export default class Trainer extends Phaser.GameObjects.Container { return !species.some(s => AlreadyUsedSpecies.includes(s)); } return !AlreadyUsedSpecies.includes(species); - }); + }).flat(); // Filter out the species that are already in the enemy party from the partner trainer species pool const speciesPoolPartnerFiltered = speciesPoolPartner.filter(species => { @@ -324,7 +324,7 @@ export default class Trainer extends Phaser.GameObjects.Container { return !species.some(s => AlreadyUsedSpecies.includes(s)); } return !AlreadyUsedSpecies.includes(species); - }); + }).flat(); // If the index is even, use the species pool for the main trainer (that way he only uses his own pokemon in battle) @@ -370,7 +370,7 @@ export default class Trainer extends Phaser.GameObjects.Container { ret = this.scene.addEnemyPokemon(species, level, !this.isDouble() || !(index % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); }, this.config.hasStaticParty ? this.config.getDerivedType() + ((index + 1) << 8) : this.scene.currentBattle.waveIndex + (this.config.getDerivedType() << 10) + (((!this.config.useSameSeedForAllMembers ? index : 0) + 1) << 8)); - return ret; + return ret!; // TODO: is this bang correct? } @@ -481,7 +481,7 @@ export default class Trainer extends Phaser.GameObjects.Container { if (maxScorePartyMemberIndexes.length > 1) { let rand: integer; this.scene.executeWithSeedOffset(() => rand = Utils.randSeedInt(maxScorePartyMemberIndexes.length), this.scene.currentBattle.turn << 2); - return maxScorePartyMemberIndexes[rand]; + return maxScorePartyMemberIndexes[rand!]; } return maxScorePartyMemberIndexes[0]; @@ -499,6 +499,9 @@ export default class Trainer extends Phaser.GameObjects.Container { return 0.45; case PartyMemberStrength.STRONGER: return 0.375; + default: + console.warn("getPartyMemberModifierChanceMultiplier not defined. Using default 0"); + return 0; } } diff --git a/src/game-mode.ts b/src/game-mode.ts index 010cae03e5e..2475cb5cfb4 100644 --- a/src/game-mode.ts +++ b/src/game-mode.ts @@ -167,7 +167,7 @@ export class GameMode implements GameModeConfig { } } - getOverrideSpecies(waveIndex: integer): PokemonSpecies { + getOverrideSpecies(waveIndex: integer): PokemonSpecies | null { if (this.isDaily && this.isWaveFinal(waveIndex)) { const allFinalBossSpecies = allSpecies.filter(s => (s.subLegendary || s.legendary || s.mythical) && s.baseTotal >= 600 && s.speciesId !== Species.ETERNATUS && s.speciesId !== Species.ARCEUS); @@ -210,7 +210,7 @@ export class GameMode implements GameModeConfig { * @returns true if waveIndex is a multiple of 50 in Endless */ isEndlessBoss(waveIndex: integer): boolean { - return waveIndex % 50 && + return !!(waveIndex % 50) && (this.modeId === GameModes.ENDLESS || this.modeId === GameModes.SPLICED_ENDLESS); } @@ -267,6 +267,8 @@ export class GameMode implements GameModeConfig { return 5000; case GameModes.DAILY: return 2500; + default: + return 0; } } diff --git a/src/inputs-controller.ts b/src/inputs-controller.ts index 834ca233942..331d491b807 100644 --- a/src/inputs-controller.ts +++ b/src/inputs-controller.ts @@ -154,7 +154,7 @@ export class InputsController { }); if (typeof this.scene.input.gamepad !== "undefined") { - this.scene.input.gamepad.on("connected", function (thisGamepad) { + this.scene.input.gamepad?.on("connected", function (thisGamepad) { if (!thisGamepad) { return; } @@ -163,23 +163,23 @@ export class InputsController { this.onReconnect(thisGamepad); }, this); - this.scene.input.gamepad.on("disconnected", function (thisGamepad) { + this.scene.input.gamepad?.on("disconnected", function (thisGamepad) { this.onDisconnect(thisGamepad); // when a gamepad is disconnected }, this); // Check to see if the gamepad has already been setup by the browser - this.scene.input.gamepad.refreshPads(); - if (this.scene.input.gamepad.total) { + this.scene.input.gamepad?.refreshPads(); + if (this.scene.input.gamepad?.total) { this.refreshGamepads(); for (const thisGamepad of this.gamepads) { this.scene.input.gamepad.emit("connected", thisGamepad); } } - this.scene.input.gamepad.on("down", this.gamepadButtonDown, this); - this.scene.input.gamepad.on("up", this.gamepadButtonUp, this); - this.scene.input.keyboard.on("keydown", this.keyboardKeyDown, this); - this.scene.input.keyboard.on("keyup", this.keyboardKeyUp, this); + this.scene.input.gamepad?.on("down", this.gamepadButtonDown, this); + this.scene.input.gamepad?.on("up", this.gamepadButtonUp, this); + this.scene.input.keyboard?.on("keydown", this.keyboardKeyDown, this); + this.scene.input.keyboard?.on("keyup", this.keyboardKeyUp, this); } this.touchControls = new TouchControl(this.scene); } @@ -338,9 +338,9 @@ export class InputsController { */ refreshGamepads(): void { // Sometimes, gamepads are undefined. For some reason. - this.gamepads = this.scene.input.gamepad.gamepads.filter(function (el) { + this.gamepads = this.scene.input.gamepad?.gamepads.filter(function (el) { return el !== null; - }); + })!; // TODO: is this bang correct? for (const [index, thisGamepad] of this.gamepads.entries()) { thisGamepad.index = index; // Overwrite the gamepad index, in case we had undefined gamepads earlier diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 5275411055e..c00112318c8 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -453,8 +453,8 @@ export class LoadingScene extends SceneBase { // videos do not need to be preloaded intro.loadURL("images/intro_dark.mp4", true); if (mobile) { - intro.video.setAttribute("webkit-playsinline", "webkit-playsinline"); - intro.video.setAttribute("playsinline", "playsinline"); + intro.video?.setAttribute("webkit-playsinline", "webkit-playsinline"); + intro.video?.setAttribute("playsinline", "playsinline"); } intro.play(); }); diff --git a/src/messages.ts b/src/messages.ts index 555a6f30ef1..b9bf94802f7 100644 --- a/src/messages.ts +++ b/src/messages.ts @@ -7,7 +7,10 @@ import i18next from "i18next"; * @param pokemon {@linkcode Pokemon} name and battle context will be retrieved from this instance * @returns {string} ex: "Wild Gengar", "Ectoplasma sauvage" */ -export function getPokemonNameWithAffix(pokemon: Pokemon): string { +export function getPokemonNameWithAffix(pokemon: Pokemon | undefined): string { + if (!pokemon) { + return "Missigno"; + } // TODO: little easter-egg, lol switch (pokemon.scene.currentBattle.battleSpec) { case BattleSpec.DEFAULT: return !pokemon.isPlayer() diff --git a/src/modifier/modifier-type.ts b/src/modifier/modifier-type.ts index 6123ecd3b80..a6117d09ffe 100644 --- a/src/modifier/modifier-type.ts +++ b/src/modifier/modifier-type.ts @@ -51,13 +51,13 @@ export class ModifierType { public group: string; public soundName: string; public tier: ModifierTier; - protected newModifierFunc: NewModifierFunc; + protected newModifierFunc: NewModifierFunc | null; - constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, group?: string, soundName?: string) { - this.localeKey = localeKey; - this.iconImage = iconImage; - this.group = group || ""; - this.soundName = soundName || "restore"; + constructor(localeKey: string | null, iconImage: string | null, newModifierFunc: NewModifierFunc | null, group?: string, soundName?: string) { + this.localeKey = localeKey!; // TODO: is this bang correct? + this.iconImage = iconImage!; // TODO: is this bang correct? + this.group = group!; // TODO: is this bang correct? + this.soundName = soundName ?? "restore"; this.newModifierFunc = newModifierFunc; } @@ -73,7 +73,7 @@ export class ModifierType { this.tier = tier; } - getOrInferTier(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierTier { + getOrInferTier(poolType: ModifierPoolType = ModifierPoolType.PLAYER): ModifierTier | null { if (this.tier) { return this.tier; } @@ -111,16 +111,16 @@ export class ModifierType { } withIdFromFunc(func: ModifierTypeFunc): ModifierType { - this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func); + this.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === func)!; // TODO: is this bang correct? return this; } - newModifier(...args: any[]): Modifier { - return this.newModifierFunc(this, args); + newModifier(...args: any[]): Modifier | null { + return this.newModifierFunc && this.newModifierFunc(this, args); } } -type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType; +type ModifierTypeGeneratorFunc = (party: Pokemon[], pregenArgs?: any[]) => ModifierType | null; export class ModifierTypeGenerator extends ModifierType { private genTypeFunc: ModifierTypeGeneratorFunc; @@ -197,7 +197,7 @@ class AddVoucherModifierType extends ModifierType { } export class PokemonModifierType extends ModifierType { - public selectFilter: PokemonSelectFilter; + public selectFilter: PokemonSelectFilter | undefined; constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, group?: string, soundName?: string) { super(localeKey, iconImage, newModifierFunc, group, soundName); @@ -298,7 +298,7 @@ export class PokemonStatusHealModifierType extends PokemonModifierType { } export abstract class PokemonMoveModifierType extends PokemonModifierType { - public moveSelectFilter: PokemonMoveSelectFilter; + public moveSelectFilter: PokemonMoveSelectFilter | undefined; constructor(localeKey: string, iconImage: string, newModifierFunc: NewModifierFunc, selectFilter?: PokemonSelectFilter, moveSelectFilter?: PokemonMoveSelectFilter, group?: string) { super(localeKey, iconImage, newModifierFunc, selectFilter, group); @@ -338,7 +338,7 @@ export class PokemonAllMovePpRestoreModifierType extends PokemonModifierType { constructor(localeKey: string, iconImage: string, restorePoints: integer) { super(localeKey, iconImage, (_type, args) => new Modifiers.PokemonAllMovePpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints), (pokemon: PlayerPokemon) => { - if (!pokemon.getMoveset().filter(m => m.ppUsed).length) { + if (!pokemon.getMoveset().filter(m => m?.ppUsed).length) { return PartyUiHandler.NoEffectMessage; } return null; @@ -518,7 +518,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i public boostPercent: integer; constructor(moveType: Type, boostPercent: integer) { - super("", `${getAttackTypeBoosterItemName(moveType).replace(/[ \-]/g, "_").toLowerCase()}`, + super("", `${getAttackTypeBoosterItemName(moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`, (_type, args) => new Modifiers.AttackTypeBoosterModifier(this, (args[0] as Pokemon).id, moveType, boostPercent)); this.moveType = moveType; @@ -526,7 +526,7 @@ export class AttackTypeBoosterModifierType extends PokemonHeldItemModifierType i } get name(): string { - return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType).replace(/[ \-]/g, "_").toLowerCase()}`); + return i18next.t(`modifierType:AttackTypeBoosterItem.${getAttackTypeBoosterItemName(this.moveType)?.replace(/[ \-]/g, "_").toLowerCase()}`); } getDescription(scene: BattleScene): string { @@ -638,7 +638,7 @@ class AllPokemonFullHpRestoreModifierType extends ModifierType { constructor(localeKey: string, iconImage: string, descriptionKey?: string, newModifierFunc?: NewModifierFunc) { super(localeKey, iconImage, newModifierFunc || ((_type, _args) => new Modifiers.PokemonHpRestoreModifier(this, -1, 0, 100, false))); - this.descriptionKey = descriptionKey; + this.descriptionKey = descriptionKey!; // TODO: is this bang correct? } getDescription(scene: BattleScene): string { @@ -773,7 +773,7 @@ export class EvolutionItemModifierType extends PokemonModifierType implements Ge if (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) && pokemonEvolutions[pokemon.species.speciesId].filter(e => e.item === this.evolutionItem && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFormKey() !== SpeciesFormKey.GIGANTAMAX)) { return null; - } else if (pokemon.isFusion() && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem + } else if (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId) && pokemonEvolutions[pokemon.fusionSpecies.speciesId].filter(e => e.item === this.evolutionItem && (!e.condition || e.condition.predicate(pokemon))).length && (pokemon.getFusionFormKey() !== SpeciesFormKey.GIGANTAMAX)) { return null; } @@ -859,7 +859,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { return new AttackTypeBoosterModifierType(pregenArgs[0] as Type, 20); } - const attackMoveTypes = party.map(p => p.getMoveset().map(m => m.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat(); + const attackMoveTypes = party.map(p => p.getMoveset().map(m => m?.getMove()).filter(m => m instanceof AttackMove).map(m => m.type)).flat(); if (!attackMoveTypes.length) { return null; } @@ -868,8 +868,8 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { let totalWeight = 0; for (const t of attackMoveTypes) { if (attackMoveTypeWeights.has(t)) { - if (attackMoveTypeWeights.get(t) < 3) { - attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t) + 1); + if (attackMoveTypeWeights.get(t)! < 3) { // attackMoveTypeWeights.has(t) was checked before + attackMoveTypeWeights.set(t, attackMoveTypeWeights.get(t)! + 1); } else { continue; } @@ -889,7 +889,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { let weight = 0; for (const t of attackMoveTypeWeights.keys()) { - const typeWeight = attackMoveTypeWeights.get(t); + const typeWeight = attackMoveTypeWeights.get(t)!; // guranteed to be defined if (randInt <= weight + typeWeight) { type = t; break; @@ -897,7 +897,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator { weight += typeWeight; } - return new AttackTypeBoosterModifierType(type, 20); + return new AttackTypeBoosterModifierType(type!, 20); }); } } @@ -930,7 +930,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { for (const p of party) { const speciesId = p.getSpeciesForm(true).speciesId; const fusionSpeciesId = p.isFusion() ? p.getFusionSpeciesForm(true).speciesId : null; - const hasFling = p.getMoveset(true).some(m => m.moveId === Moves.FLING); + const hasFling = p.getMoveset(true).some(m => m?.moveId === Moves.FLING); for (const i in values) { const checkedSpecies = values[i].species; @@ -980,7 +980,7 @@ class SpeciesStatBoosterModifierTypeGenerator extends ModifierTypeGenerator { class TmModifierTypeGenerator extends ModifierTypeGenerator { constructor(tier: ModifierTier) { super((party: Pokemon[]) => { - const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m.moveId === tm))); + const partyMemberCompatibleTms = party.map(p => (p as PlayerPokemon).compatibleTms.filter(tm => !p.moveset.find(m => m?.moveId === tm))); const tierUniqueCompatibleTms = partyMemberCompatibleTms.flat().filter(tm => tmPoolTiers[tm] === tier).filter(tm => !allMoves[tm].name.endsWith(" (N)")).filter((tm, i, array) => array.indexOf(tm) === i); if (!tierUniqueCompatibleTms.length) { return null; @@ -1003,17 +1003,17 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator { const evolutions = pokemonEvolutions[p.species.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat(), - party.filter(p => p.isFusion() && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => { - const evolutions = pokemonEvolutions[p.fusionSpecies.speciesId]; + party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => { + const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId]; return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFusionFormKey()) && (!e.condition || e.condition.predicate(p))); }).flat() - ].flat().flatMap(e => e.item).filter(i => (i > 50) === rare); + ].flat().flatMap(e => e.item).filter(i => (!!i && i > 50) === rare); if (!evolutionItemPool.length) { return null; } - return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length)]); + return new EvolutionItemModifierType(evolutionItemPool[Utils.randSeedInt(evolutionItemPool.length)]!); // TODO: is the bang correct? }); } } @@ -1156,7 +1156,7 @@ class WeightedModifierType { constructor(modifierTypeFunc: ModifierTypeFunc, weight: integer | WeightedModifierTypeWeightFunc, maxWeight?: integer) { this.modifierType = modifierTypeFunc(); - this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); + this.modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc)!; // TODO: is this bang correct? this.weight = weight; this.maxWeight = maxWeight || (!(weight instanceof Function) ? weight : 0); } @@ -1410,7 +1410,7 @@ export const modifierTypes = { VOUCHER_PLUS: () => new AddVoucherModifierType(VoucherType.PLUS, 1), VOUCHER_PREMIUM: () => new AddVoucherModifierType(VoucherType.PREMIUM, 1), - GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new Modifiers.ExtraModifierModifier(type), null, "pb_bounce_1"), + GOLDEN_POKEBALL: () => new ModifierType("modifierType:ModifierType.GOLDEN_POKEBALL", "pb_gold", (type, _args) => new Modifiers.ExtraModifierModifier(type), undefined, "pb_bounce_1"), ENEMY_DAMAGE_BOOSTER: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_BOOSTER", "wl_item_drop", (type, _args) => new Modifiers.EnemyDamageBoosterModifier(type, 5)), ENEMY_DAMAGE_REDUCTION: () => new ModifierType("modifierType:ModifierType.ENEMY_DAMAGE_REDUCTION", "wl_guard_spec", (type, _args) => new Modifiers.EnemyDamageReducerModifier(type, 2.5)), @@ -1451,11 +1451,11 @@ const modifierPool: ModifierPool = { return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount * 3; }, 9), new WeightedModifierType(modifierTypes.MAX_ETHER, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.LURE, 2), @@ -1471,7 +1471,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.FULL_HEAL, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => { if (i instanceof Modifiers.TurnStatusEffectModifier) { - return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect; + return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect; } return false; })).length, 3); @@ -1499,7 +1499,7 @@ const modifierPool: ModifierPool = { new WeightedModifierType(modifierTypes.FULL_RESTORE, (party: Pokemon[]) => { const statusEffectPartyMemberCount = Math.min(party.filter(p => p.hp && !!p.status && !p.getHeldItems().some(i => { if (i instanceof Modifiers.TurnStatusEffectModifier) { - return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status.effect; + return (i as Modifiers.TurnStatusEffectModifier).getStatusEffect() === p.status?.effect; } return false; })).length, 3); @@ -1507,11 +1507,11 @@ const modifierPool: ModifierPool = { return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount * 3; }, 9), new WeightedModifierType(modifierTypes.MAX_ELIXIR, (party: Pokemon[]) => { - const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); + const thresholdPartyMemberCount = Math.min(party.filter(p => p.hp && p.getMoveset().filter(m => m?.ppUsed && (m.getMovePp() - m.ppUsed) <= 5).length).length, 3); return thresholdPartyMemberCount; }, 3), new WeightedModifierType(modifierTypes.DIRE_HIT, 4), @@ -1555,19 +1555,19 @@ const modifierPool: ModifierPool = { const checkedAbilities = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.TOXIC_BOOST, Abilities.POISON_HEAL, Abilities.MAGIC_GUARD]; const checkedMoves = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT]; // If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear - return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => checkedMoves.includes(m.moveId)))) ? 10 : 0; + return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0; }, 10), new WeightedModifierType(modifierTypes.FLAME_ORB, (party: Pokemon[]) => { const checkedAbilities = [Abilities.QUICK_FEET, Abilities.GUTS, Abilities.MARVEL_SCALE, Abilities.FLARE_BOOST, Abilities.MAGIC_GUARD]; const checkedMoves = [Moves.FACADE, Moves.TRICK, Moves.FLING, Moves.SWITCHEROO, Moves.PSYCHO_SHIFT]; // If a party member doesn't already have one of these two orbs and has one of the above moves or abilities, the orb can appear - return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => checkedMoves.includes(m.moveId)))) ? 10 : 0; + return party.some(p => !p.getHeldItems().some(i => i instanceof Modifiers.TurnStatusEffectModifier) && (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && checkedMoves.includes(m.moveId)))) ? 10 : 0; }, 10), new WeightedModifierType(modifierTypes.WHITE_HERB, (party: Pokemon[]) => { const checkedAbilities = [Abilities.WEAK_ARMOR, Abilities.CONTRARY, Abilities.MOODY, Abilities.ANGER_SHELL, Abilities.COMPETITIVE, Abilities.DEFIANT]; const weightMultiplier = party.filter( p => !p.getHeldItems().some(i => i instanceof Modifiers.PokemonResetNegativeStatStageModifier && i.stackCount >= i.getMaxHeldItemCount(p)) && - (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => selfStatLowerMoves.includes(m.moveId)))).length; + (checkedAbilities.some(a => p.hasAbility(a, false, true)) || p.getMoveset(true).some(m => m && selfStatLowerMoves.includes(m.moveId)))).length; // If a party member has one of the above moves or abilities and doesn't have max herbs, the herb will appear more frequently return 0 * (weightMultiplier ? 2 : 1) + (weightMultiplier ? weightMultiplier * 0 : 0); }, 10), @@ -1722,10 +1722,10 @@ const enemyBuffModifierPool: ModifierPool = { ].map(m => { m.setTier(ModifierTier.ULTRA); return m; }), - [ModifierTier.ROGUE]: [ ].map(m => { + [ModifierTier.ROGUE]: [ ].map((m: WeightedModifierType) => { m.setTier(ModifierTier.ROGUE); return m; }), - [ModifierTier.MASTER]: [ ].map(m => { + [ModifierTier.MASTER]: [ ].map((m: WeightedModifierType) => { m.setTier(ModifierTier.MASTER); return m; }) }; @@ -1770,7 +1770,7 @@ const dailyStarterModifierPool: ModifierPool = { export function getModifierType(modifierTypeFunc: ModifierTypeFunc): ModifierType { const modifierType = modifierTypeFunc(); if (!modifierType.id) { - modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc); + modifierType.id = Object.keys(modifierTypes).find(k => modifierTypes[k] === modifierTypeFunc)!; // TODO: is this bang correct? } return modifierType; } @@ -1893,12 +1893,14 @@ export function getPlayerModifierTypeOptions(count: integer, party: PlayerPokemo const options: ModifierTypeOption[] = []; const retryCount = Math.min(count * 5, 50); new Array(count).fill(0).map((_, i) => { - let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers?.length > i ? modifierTiers[i] : undefined); + let candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, modifierTiers && modifierTiers.length > i ? modifierTiers[i] : undefined); let r = 0; - while (options.length && ++r < retryCount && options.filter(o => o.type.name === candidate.type.name || o.type.group === candidate.type.group).length) { - candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate.type.tier, candidate.upgradeCount); + while (options.length && ++r < retryCount && options.filter(o => o.type?.name === candidate?.type?.name || o.type?.group === candidate?.type?.group).length) { + candidate = getNewModifierTypeOption(party, ModifierPoolType.PLAYER, candidate?.type?.tier, candidate?.upgradeCount); + } + if (candidate) { + options.push(candidate); } - options.push(candidate); }); // OVERRIDE IF NECESSARY @@ -1950,21 +1952,21 @@ export function getPlayerShopModifierTypeOptionsForWave(waveIndex: integer, base export function getEnemyBuffModifierForWave(tier: ModifierTier, enemyModifiers: Modifiers.PersistentModifier[], scene: BattleScene): Modifiers.EnemyPersistentModifier { const tierStackCount = tier === ModifierTier.ULTRA ? 5 : tier === ModifierTier.GREAT ? 3 : 1; const retryCount = 50; - let candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + let candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); let r = 0; - let matchingModifier: Modifiers.PersistentModifier; - while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate.type.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { - candidate = getNewModifierTypeOption(null, ModifierPoolType.ENEMY_BUFF, tier); + let matchingModifier: Modifiers.PersistentModifier | undefined; + while (++r < retryCount && (matchingModifier = enemyModifiers.find(m => m.type.id === candidate?.type?.id)) && matchingModifier.getMaxStackCount(scene) < matchingModifier.stackCount + (r < 10 ? tierStackCount : 1)) { + candidate = getNewModifierTypeOption([], ModifierPoolType.ENEMY_BUFF, tier); } - const modifier = candidate.type.newModifier() as Modifiers.EnemyPersistentModifier; + const modifier = candidate?.type?.newModifier() as Modifiers.EnemyPersistentModifier; modifier.stackCount = tierStackCount; return modifier; } export function getEnemyModifierTypesForWave(waveIndex: integer, count: integer, party: EnemyPokemon[], poolType: ModifierPoolType.WILD | ModifierPoolType.TRAINER, upgradeChance: integer = 0): PokemonHeldItemModifierType[] { - const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0).type as PokemonHeldItemModifierType); + const ret = new Array(count).fill(0).map(() => getNewModifierTypeOption(party, poolType, undefined, upgradeChance && !Utils.randSeedInt(upgradeChance) ? 1 : 0)?.type as PokemonHeldItemModifierType); if (!(waveIndex % 1000)) { ret.push(getModifierType(modifierTypes.MINI_BLACK_HOLE) as PokemonHeldItemModifierType); } @@ -1977,7 +1979,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P for (let m = 0; m < 3; m++) { const tierValue = Utils.randSeedInt(64); const tier = tierValue > 25 ? ModifierTier.COMMON : tierValue > 12 ? ModifierTier.GREAT : tierValue > 4 ? ModifierTier.ULTRA : tierValue ? ModifierTier.ROGUE : ModifierTier.MASTER; - const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier).type.newModifier(p) as Modifiers.PokemonHeldItemModifier; + const modifier = getNewModifierTypeOption(party, ModifierPoolType.DAILY_STARTER, tier)?.type?.newModifier(p) as Modifiers.PokemonHeldItemModifier; ret.push(modifier); } } @@ -1985,7 +1987,7 @@ export function getDailyRunStarterModifiers(party: PlayerPokemon[]): Modifiers.P return ret; } -function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0): ModifierTypeOption { +function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, tier?: ModifierTier, upgradeCount?: integer, retryCount: integer = 0): ModifierTypeOption | null { const player = !poolType; const pool = getModifierPoolForType(poolType); let thresholds: object; @@ -2052,7 +2054,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, const tierThresholds = Object.keys(thresholds[tier]); const totalWeight = parseInt(tierThresholds[tierThresholds.length - 1]); const value = Utils.randSeedInt(totalWeight); - let index: integer; + let index: integer | undefined; for (const t of tierThresholds) { const threshold = parseInt(t); if (value < threshold) { @@ -2068,7 +2070,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, if (player) { console.log(index, ignoredPoolIndexes[tier].filter(i => i <= index).length, ignoredPoolIndexes[tier]); } - let modifierType: ModifierType = (pool[tier][index]).modifierType; + let modifierType: ModifierType | null = (pool[tier][index]).modifierType; if (modifierType instanceof ModifierTypeGenerator) { modifierType = (modifierType as ModifierTypeGenerator).generateType(party); if (modifierType === null) { @@ -2081,7 +2083,7 @@ function getNewModifierTypeOption(party: Pokemon[], poolType: ModifierPoolType, console.log(modifierType, !player ? "(enemy)" : ""); - return new ModifierTypeOption(modifierType as ModifierType, upgradeCount); + return new ModifierTypeOption(modifierType as ModifierType, upgradeCount!); // TODO: is this bang correct? } export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType { @@ -2093,7 +2095,7 @@ export function getDefaultModifierTypeForTier(tier: ModifierTier): ModifierType } export class ModifierTypeOption { - public type: ModifierType; + public type: ModifierType | null; public upgradeCount: integer; public cost: integer; diff --git a/src/modifier/modifier.ts b/src/modifier/modifier.ts index 91041dc7564..6ff2d3c9718 100644 --- a/src/modifier/modifier.ts +++ b/src/modifier/modifier.ts @@ -33,16 +33,16 @@ export type ModifierPredicate = (modifier: Modifier) => boolean; const iconOverflowIndex = 24; -export const modifierSortFunc = (a: Modifier, b: Modifier) => { +export const modifierSortFunc = (a: Modifier, b: Modifier): number => { const itemNameMatch = a.type.name.localeCompare(b.type.name); const typeNameMatch = a.constructor.name.localeCompare(b.constructor.name); - const aId = a instanceof PokemonHeldItemModifier ? a.pokemonId : 4294967295; - const bId = b instanceof PokemonHeldItemModifier ? b.pokemonId : 4294967295; + const aId = a instanceof PokemonHeldItemModifier && a.pokemonId ? a.pokemonId : 4294967295; + const bId = b instanceof PokemonHeldItemModifier && b.pokemonId ? b.pokemonId : 4294967295; //First sort by pokemonID if (aId < bId) { return 1; - } else if (aId>bId) { + } else if (aId > bId) { return -1; } else if (aId === bId) { //Then sort by item type @@ -52,6 +52,8 @@ export const modifierSortFunc = (a: Modifier, b: Modifier) => { } else { return typeNameMatch; } + } else { + return 0; } }; @@ -150,7 +152,7 @@ export abstract class PersistentModifier extends Modifier { public stackCount: integer; public virtualStackCount: integer; - constructor(type: ModifierType, stackCount: integer) { + constructor(type: ModifierType, stackCount?: integer) { super(type); this.stackCount = stackCount === undefined ? 1 : stackCount; this.virtualStackCount = 0; @@ -221,7 +223,7 @@ export abstract class PersistentModifier extends Modifier { return container; } - getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText { + getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.BitmapText | null { if (this.getMaxStackCount(scene) === 1 || (virtual && !this.virtualStackCount)) { return null; } @@ -295,7 +297,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier { constructor(type: ModifierTypes.ModifierType, battlesLeft?: integer, stackCount?: integer) { super(type, stackCount); - this.battlesLeft = battlesLeft; + this.battlesLeft = battlesLeft!; // TODO: is this bang correct? } lapse(args: any[]): boolean { @@ -306,7 +308,7 @@ export abstract class LapsingPersistentModifier extends PersistentModifier { const container = super.getIcon(scene); const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" }); - battleCountText.setShadow(0, 0, null); + battleCountText.setShadow(0, 0); battleCountText.setStroke("#984038", 16); battleCountText.setOrigin(1, 0); container.add(battleCountText); @@ -472,7 +474,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { public pokemonId: integer; readonly isTransferrable: boolean = true; - constructor(type: ModifierType, pokemonId: integer, stackCount: integer) { + constructor(type: ModifierType, pokemonId: integer, stackCount?: integer) { super(type, stackCount); this.pokemonId = pokemonId; @@ -489,11 +491,11 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { } shouldApply(args: any[]): boolean { - return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId); + return super.shouldApply(args) && args.length !== 0 && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId); } isIconVisible(scene: BattleScene): boolean { - return this.getPokemon(scene).isOnField(); + return !!(this.getPokemon(scene)?.isOnField()); } getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { @@ -501,9 +503,10 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { if (!forSummary) { const pokemon = this.getPokemon(scene); - const pokemonIcon = scene.addPokemonIcon(pokemon, -2, 10, 0, 0.5); - - container.add(pokemonIcon); + if (pokemon) { + const pokemonIcon = scene.addPokemonIcon(pokemon, -2, 10, 0, 0.5); + container.add(pokemonIcon); + } const item = scene.add.sprite(16, this.virtualStackCount ? 8 : 16, "items"); item.setScale(0.5); @@ -527,8 +530,8 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return container; } - getPokemon(scene: BattleScene): Pokemon { - return scene.getPokemonById(this.pokemonId); + getPokemon(scene: BattleScene): Pokemon | undefined { + return this.pokemonId ? scene.getPokemonById(this.pokemonId) ?? undefined : undefined; } getScoreMultiplier(): number { @@ -562,7 +565,7 @@ export abstract class PokemonHeldItemModifier extends PersistentModifier { return this.getMaxHeldItemCount(pokemon); } - abstract getMaxHeldItemCount(pokemon: Pokemon): integer; + abstract getMaxHeldItemCount(pokemon?: Pokemon): integer; } export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModifier { @@ -572,7 +575,7 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi constructor(type: ModifierTypes.ModifierType, pokemonId: integer, battlesLeft?: integer, stackCount?: integer) { super(type, pokemonId, stackCount); - this.battlesLeft = battlesLeft; + this.battlesLeft = battlesLeft!; // TODO: is this bang correct? } lapse(args: any[]): boolean { @@ -582,9 +585,9 @@ export abstract class LapsingPokemonHeldItemModifier extends PokemonHeldItemModi getIcon(scene: BattleScene, forSummary?: boolean): Phaser.GameObjects.Container { const container = super.getIcon(scene, forSummary); - if (this.getPokemon(scene).isPlayer()) { + if (this.getPokemon(scene)?.isPlayer()) { const battleCountText = addTextObject(scene, 27, 0, this.battlesLeft.toString(), TextStyle.PARTY, { fontSize: "66px", color: "#f89890" }); - battleCountText.setShadow(0, 0, null); + battleCountText.setShadow(0, 0); battleCountText.setStroke("#984038", 16); battleCountText.setOrigin(1, 0); container.add(battleCountText); @@ -1442,7 +1445,7 @@ export abstract class ConsumablePokemonModifier extends ConsumableModifier { } shouldApply(args: any[]): boolean { - return args.length && args[0] instanceof PlayerPokemon && (this.pokemonId === -1 || (args[0] as PlayerPokemon).id === this.pokemonId); + return args.length !== 0 && args[0] instanceof PlayerPokemon && (this.pokemonId === -1 || (args[0] as PlayerPokemon).id === this.pokemonId); } getPokemon(scene: BattleScene) { @@ -1519,7 +1522,7 @@ export class PokemonPpRestoreModifier extends ConsumablePokemonMoveModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - const move = pokemon.getMoveset()[this.moveIndex]; + const move = pokemon.getMoveset()[this.moveIndex]!; //TODO: is the bang correct? move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; return true; @@ -1538,7 +1541,7 @@ export class PokemonAllMovePpRestoreModifier extends ConsumablePokemonModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; for (const move of pokemon.getMoveset()) { - move.ppUsed = this.restorePoints > -1 ? Math.max(move.ppUsed - this.restorePoints, 0) : 0; + move!.ppUsed = this.restorePoints > -1 ? Math.max(move!.ppUsed - this.restorePoints, 0) : 0; // TODO: are those bangs correct? } return true; @@ -1556,7 +1559,7 @@ export class PokemonPpUpModifier extends ConsumablePokemonMoveModifier { apply(args: any[]): boolean { const pokemon = args[0] as Pokemon; - const move = pokemon.getMoveset()[this.moveIndex]; + const move = pokemon.getMoveset()[this.moveIndex]!; // TODO: is the bang correct? move.ppUp = Math.min(move.ppUp + this.upPoints, 3); return true; @@ -1658,7 +1661,7 @@ export class EvolutionItemModifier extends ConsumablePokemonModifier { : null; if (!matchingEvolution && pokemon.isFusion()) { - matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies.speciesId].find(e => e.item === (this.type as ModifierTypes.EvolutionItemModifierType).evolutionItem + matchingEvolution = pokemonEvolutions[pokemon.fusionSpecies!.speciesId].find(e => e.item === (this.type as ModifierTypes.EvolutionItemModifierType).evolutionItem // TODO: is the bang correct? && (e.evoFormKey === null || (e.preFormKey || "") === pokemon.getFusionFormKey()) && (!e.condition || e.condition.predicate(pokemon))); if (matchingEvolution) { @@ -2135,7 +2138,7 @@ export class MoneyInterestModifier extends PersistentModifier { const userLocale = navigator.language || "en-US"; const formattedMoneyAmount = interestAmount.toLocaleString(userLocale); const message = i18next.t("modifier:moneyInterestApply", { moneyAmount: formattedMoneyAmount, typeName: this.type.name }); - scene.queueMessage(message, null, true); + scene.queueMessage(message, undefined, true); return true; } @@ -2290,7 +2293,7 @@ export abstract class HeldItemTransferModifier extends PokemonHeldItemModifier { const transferredModifierTypes: ModifierTypes.ModifierType[] = []; const itemModifiers = pokemon.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.pokemonId === targetPokemon.id && m.isTransferrable, targetPokemon.isPlayer()) as PokemonHeldItemModifier[]; - let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier, highestTier), 0); + let highestItemTier = itemModifiers.map(m => m.type.getOrInferTier(poolType)).reduce((highestTier, tier) => Math.max(tier!, highestTier), 0); // TODO: is this bang correct? let tierItemModifiers = itemModifiers.filter(m => m.type.getOrInferTier(poolType) === highestItemTier); const heldItemTransferPromises: Promise[] = []; @@ -2782,8 +2785,8 @@ export function overrideHeldItems(scene: BattleScene, pokemon: Pokemon, player: const modifierType: ModifierType = modifierTypes[itemName](); // we retrieve the item in the list let itemModifier: PokemonHeldItemModifier; if (modifierType instanceof ModifierTypes.ModifierTypeGenerator) { - const pregenArgs = "type" in item ? [item.type] : null; - itemModifier = modifierType.generateType(null, pregenArgs).withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; + const pregenArgs = "type" in item ? [item.type] : undefined; + itemModifier = modifierType.generateType([], pregenArgs)?.withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; } else { itemModifier = modifierType.withIdFromFunc(modifierTypes[itemName]).newModifier(pokemon) as PokemonHeldItemModifier; } diff --git a/src/overrides.ts b/src/overrides.ts index 9dd394354aa..88db105475c 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -48,9 +48,9 @@ class DefaultOverrides { readonly BATTLE_TYPE_OVERRIDE: "double" | "single" | null = null; readonly STARTING_WAVE_OVERRIDE: integer = 0; readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; - readonly ARENA_TINT_OVERRIDE: TimeOfDay = null; + readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null; /** Multiplies XP gained by this value including 0. Set to null to ignore the override */ - readonly XP_MULTIPLIER_OVERRIDE: number = null; + readonly XP_MULTIPLIER_OVERRIDE: number | null = null; /** default 1000 */ readonly STARTING_MONEY_OVERRIDE: integer = 0; /** Sets all shop item prices to 0 */ @@ -96,7 +96,7 @@ class DefaultOverrides { readonly ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; - readonly GENDER_OVERRIDE: Gender = null; + readonly GENDER_OVERRIDE: Gender | null = null; readonly MOVESET_OVERRIDE: Array = []; readonly SHINY_OVERRIDE: boolean = false; readonly VARIANT_OVERRIDE: Variant = 0; @@ -109,7 +109,7 @@ class DefaultOverrides { readonly OPP_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_PASSIVE_ABILITY_OVERRIDE: Abilities = Abilities.NONE; readonly OPP_STATUS_OVERRIDE: StatusEffect = StatusEffect.NONE; - readonly OPP_GENDER_OVERRIDE: Gender = null; + readonly OPP_GENDER_OVERRIDE: Gender | null = null; readonly OPP_MOVESET_OVERRIDE: Array = []; readonly OPP_SHINY_OVERRIDE: boolean = false; readonly OPP_VARIANT_OVERRIDE: Variant = 0; @@ -119,9 +119,9 @@ class DefaultOverrides { // EGG OVERRIDES // ------------- readonly EGG_IMMEDIATE_HATCH_OVERRIDE: boolean = false; - readonly EGG_TIER_OVERRIDE: EggTier = null; + readonly EGG_TIER_OVERRIDE: EggTier | null = null; readonly EGG_SHINY_OVERRIDE: boolean = false; - readonly EGG_VARIANT_OVERRIDE: VariantTier = null; + readonly EGG_VARIANT_OVERRIDE: VariantTier | null = null; readonly EGG_FREE_GACHA_PULLS_OVERRIDE: boolean = false; readonly EGG_GACHA_PULL_COUNT_OVERRIDE: number = 0; diff --git a/src/phases.ts b/src/phases.ts index f561ea6f3fc..7397974846f 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -194,7 +194,7 @@ export class TitlePhase extends Phase { this.scene.playBgm("title", true); - this.scene.gameData.getSession(loggedInUser.lastSessionSlot).then(sessionData => { + this.scene.gameData.getSession(loggedInUser!.lastSessionSlot).then(sessionData => { // TODO: is this bang correct? if (sessionData) { this.lastSessionData = sessionData; const biomeKey = getBiomeKey(sessionData.arena.biome); @@ -210,11 +210,11 @@ export class TitlePhase extends Phase { showOptions(): void { const options: OptionSelectItem[] = []; - if (loggedInUser.lastSessionSlot > -1) { + if (loggedInUser && loggedInUser.lastSessionSlot > -1) { options.push({ - label: i18next.t("continue", null, { ns: "menu"}), + label: i18next.t("continue", {ns: "menu"}), handler: () => { - this.loadSaveSlot(this.lastSessionData ? -1 : loggedInUser.lastSessionSlot); + this.loadSaveSlot(this.lastSessionData || !loggedInUser ? -1 : loggedInUser.lastSessionSlot); return true; } }); @@ -318,9 +318,9 @@ export class TitlePhase extends Phase { } loadSaveSlot(slotId: integer): void { - this.scene.sessionSlotId = slotId > -1 ? slotId : loggedInUser.lastSessionSlot; + this.scene.sessionSlotId = slotId > -1 || !loggedInUser ? slotId : loggedInUser.lastSessionSlot; this.scene.ui.setMode(Mode.MESSAGE); - this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : null).then((success: boolean) => { + this.scene.gameData.loadSession(this.scene, slotId, slotId === -1 ? this.lastSessionData : undefined).then((success: boolean) => { if (success) { this.loaded = true; this.scene.ui.showText(i18next.t("menu:sessionSuccess"), null, () => this.end()); @@ -370,7 +370,8 @@ export class TitlePhase extends Phase { regenerateModifierPoolThresholds(party, ModifierPoolType.DAILY_STARTER); const modifiers: Modifier[] = Array(3).fill(null).map(() => modifierTypes.EXP_SHARE().withIdFromFunc(modifierTypes.EXP_SHARE).newModifier()) .concat(Array(3).fill(null).map(() => modifierTypes.GOLDEN_EXP_CHARM().withIdFromFunc(modifierTypes.GOLDEN_EXP_CHARM).newModifier())) - .concat(getDailyRunStarterModifiers(party)); + .concat(getDailyRunStarterModifiers(party)) + .filter((m) => m !== null); for (const m of modifiers) { this.scene.addModifier(m, true, false, false, true); @@ -392,7 +393,7 @@ export class TitlePhase extends Phase { // If Online, calls seed fetch from db to generate daily run. If Offline, generates a daily run based on current date. if (!Utils.isLocal) { fetchDailyRunSeed().then(seed => { - generateDaily(seed); + generateDaily(seed!); // TODO: is this bang correct? }).catch(err => { console.error("Failed to load daily run:\n", err); }); @@ -461,12 +462,12 @@ export class UnavailablePhase extends Phase { } export class ReloadSessionPhase extends Phase { - private systemDataStr: string; + private systemDataStr: string | null; constructor(scene: BattleScene, systemDataStr?: string) { super(scene); - this.systemDataStr = systemDataStr; + this.systemDataStr = systemDataStr!; // TODO: is this bang correct? } start(): void { @@ -599,9 +600,9 @@ export class SelectStarterPhase extends Phase { let starterFormIndex = Math.min(starterProps.formIndex, Math.max(starter.species.forms.length - 1, 0)); if ( starter.species.speciesId in Overrides.STARTER_FORM_OVERRIDES && - starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]] + starter.species.forms[Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!] ) { - starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]; + starterFormIndex = Overrides.STARTER_FORM_OVERRIDES[starter.species.speciesId]!; } let starterGender = starter.species.malePercent !== null @@ -612,7 +613,7 @@ export class SelectStarterPhase extends Phase { } const starterIvs = this.scene.gameData.dexData[starter.species.speciesId].ivs.slice(0); const starterPokemon = this.scene.addPlayerPokemon(starter.species, this.scene.gameMode.getStartingLevel(), starter.abilityIndex, starterFormIndex, starterGender, starterProps.shiny, starterProps.variant, starterIvs, starter.nature); - starterPokemon.tryPopulateMoveset(starter.moveset); + starter.moveset && starterPokemon.tryPopulateMoveset(starter.moveset); if (starter.passive) { starterPokemon.passive = true; } @@ -657,8 +658,8 @@ export class BattlePhase extends Phase { } showEnemyTrainer(trainerSlot: TrainerSlot = TrainerSlot.NONE): void { - const sprites = this.scene.currentBattle.trainer.getSprites(); - const tintSprites = this.scene.currentBattle.trainer.getTintSprites(); + const sprites = this.scene.currentBattle.trainer?.getSprites()!; // TODO: is this bang correct? + const tintSprites = this.scene.currentBattle.trainer?.getTintSprites()!; // TODO: is this bang correct? for (let i = 0; i < sprites.length; i++) { const visible = !trainerSlot || !i === (trainerSlot === TrainerSlot.TRAINER) || sprites.length < 2; [sprites[i], tintSprites[i]].map(sprite => { @@ -738,11 +739,11 @@ export abstract class PokemonPhase extends FieldPhase { public player: boolean; public fieldIndex: integer; - constructor(scene: BattleScene, battlerIndex: BattlerIndex | integer) { + constructor(scene: BattleScene, battlerIndex?: BattlerIndex | integer) { super(scene); if (battlerIndex === undefined) { - battlerIndex = scene.getField().find(p => p?.isActive()).getBattlerIndex(); + battlerIndex = scene.getField().find(p => p?.isActive())!.getBattlerIndex(); // TODO: is the bang correct here? } this.battlerIndex = battlerIndex; @@ -750,11 +751,11 @@ export abstract class PokemonPhase extends FieldPhase { this.fieldIndex = battlerIndex % 2; } - getPokemon() { + getPokemon(): Pokemon { if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex); + return this.scene.getPokemonById(this.battlerIndex)!; //TODO: is this bang correct? } - return this.scene.getField()[this.battlerIndex]; + return this.scene.getField()[this.battlerIndex]!; //TODO: is this bang correct? } } @@ -825,16 +826,16 @@ export class EncounterPhase extends BattlePhase { this.scene.unshiftPhase(new GameOverPhase(this.scene)); } - const loadEnemyAssets = []; + const loadEnemyAssets: Promise[] = []; const battle = this.scene.currentBattle; let totalBst = 0; - battle.enemyLevels.forEach((level, e) => { + battle.enemyLevels?.forEach((level, e) => { if (!this.loaded) { if (battle.battleType === BattleType.TRAINER) { - battle.enemyParty[e] = battle.trainer.genPartyMember(e); + battle.enemyParty[e] = battle.trainer?.genPartyMember(e)!; // TODO:: is the bang correct here? } else { const enemySpecies = this.scene.randomSpecies(battle.waveIndex, level, true); battle.enemyParty[e] = this.scene.addEnemyPokemon(enemySpecies, level, TrainerSlot.NONE, !!this.scene.getEncounterBossSegments(battle.waveIndex, level, enemySpecies)); @@ -881,7 +882,7 @@ export class EncounterPhase extends BattlePhase { } if (battle.battleType === BattleType.TRAINER) { - loadEnemyAssets.push(battle.trainer.loadAssets().then(() => battle.trainer.initSprite())); + loadEnemyAssets.push(battle.trainer?.loadAssets().then(() => battle.trainer?.initSprite())!); // TODO: is this bang correct? } else { // This block only applies for double battles to init the boss segments (idk why it's split up like this) if (battle.enemyParty.filter(p => p.isBoss()).length > 1) { @@ -908,7 +909,7 @@ export class EncounterPhase extends BattlePhase { enemyPokemon.tint(0, 0.5); } else if (battle.battleType === BattleType.TRAINER) { enemyPokemon.setVisible(false); - this.scene.currentBattle.trainer.tint(0, 0.5); + this.scene.currentBattle.trainer?.tint(0, 0.5); } if (battle.double) { enemyPokemon.setFieldPosition(e ? FieldPosition.RIGHT : FieldPosition.LEFT); @@ -923,7 +924,8 @@ export class EncounterPhase extends BattlePhase { this.scene.ui.setMode(Mode.MESSAGE).then(() => { if (!this.loaded) { - this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { + //@ts-ignore + this.scene.gameData.saveAll(this.scene, true, battle.waveIndex % 10 === 1 || this.scene.lastSavePlayTime >= 300).then(success => { // TODO: get rid of ts-ignore this.scene.disableMenu = false; if (!success) { return this.scene.reset(true); @@ -980,10 +982,10 @@ export class EncounterPhase extends BattlePhase { if (this.scene.currentBattle.battleType === BattleType.TRAINER) { if (this.scene.currentBattle.double) { - return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppearedDouble", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } else { - return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }); + return i18next.t("battle:trainerAppeared", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }); } } @@ -1012,8 +1014,8 @@ export class EncounterPhase extends BattlePhase { } } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { const trainer = this.scene.currentBattle.trainer; - trainer.untint(100, "Sine.easeOut"); - trainer.playAnim(); + trainer?.untint(100, "Sine.easeOut"); + trainer?.playAnim(); const doSummon = () => { this.scene.currentBattle.started = true; @@ -1036,21 +1038,21 @@ export class EncounterPhase extends BattlePhase { } }; - const encounterMessages = this.scene.currentBattle.trainer.getEncounterMessages(); + const encounterMessages = this.scene.currentBattle.trainer?.getEncounterMessages(); if (!encounterMessages?.length) { doSummon(); } else { let message: string; this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(encounterMessages), this.scene.currentBattle.waveIndex); - + message = message!; // tell TS compiler it's defined now const showDialogueAndSummon = () => { - this.scene.ui.showDialogue(message, trainer.getName(TrainerSlot.NONE, true), null, () => { + this.scene.ui.showDialogue(message, trainer?.getName(TrainerSlot.NONE, true), null, () => { this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => doSummon())); }); }; - if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer.getKey(), getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(trainer?.getKey()!, getCharVariantFromDialogue(encounterMessages[0])).then(() => showDialogueAndSummon())); // TODO: is this bang correct? } else { showDialogueAndSummon(); } @@ -1128,7 +1130,7 @@ export class EncounterPhase extends BattlePhase { case BattleSpec.FINAL_BOSS: const enemy = this.scene.getEnemyPokemon(); this.scene.ui.showText(this.getEncounterMessage(), null, () => { - this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].encounter, enemy.species.name, null, () => { + this.scene.ui.showDialogue(battleSpecDialogue[BattleSpec.FINAL_BOSS].encounter, enemy?.species.name, null, () => { this.doEncounterCommon(false); }); }, 1500, true); @@ -1261,14 +1263,14 @@ export class SelectBiomePhase extends BattlePhase { } else if (this.scene.gameMode.hasRandomBiomes) { setNextBiome(this.generateNextBiome()); } else if (Array.isArray(biomeLinks[currentBiome])) { - let biomes: Biome[]; + let biomes: Biome[] = []; this.scene.executeWithSeedOffset(() => { biomes = (biomeLinks[currentBiome] as (Biome | [Biome, integer])[]) .filter(b => !Array.isArray(b) || !Utils.randSeedInt(b[1])) .map(b => !Array.isArray(b) ? b : b[0]); }, this.scene.currentBattle.waveIndex); if (biomes.length > 1 && this.scene.findModifier(m => m instanceof MapModifier)) { - let biomeChoices: Biome[]; + let biomeChoices: Biome[] = []; this.scene.executeWithSeedOffset(() => { biomeChoices = (!Array.isArray(biomeLinks[currentBiome]) ? [biomeLinks[currentBiome] as Biome] @@ -1437,7 +1439,7 @@ export class SummonPhase extends PartyMemberPokemonPhase { }); this.scene.time.delayedCall(750, () => this.summon()); } else { - const trainerName = this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + const trainerName = this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); const pokemonName = this.getPokemon().getNameToRender(); const message = i18next.t("battle:trainerSendOut", { trainerName, pokemonName }); @@ -1581,7 +1583,8 @@ export class SwitchSummonPhase extends SummonPhase { preSummon(): void { if (!this.player) { if (this.slotIndex === -1) { - this.slotIndex = this.scene.currentBattle.trainer.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); + //@ts-ignore + this.slotIndex = this.scene.currentBattle.trainer?.getNextSummonIndex(!this.fieldIndex ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); // TODO: what would be the default trainer-slot fallback? } if (this.slotIndex > -1) { this.showEnemyTrainer(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER); @@ -1607,7 +1610,7 @@ export class SwitchSummonPhase extends SummonPhase { this.scene.ui.showText(this.player ? i18next.t("battle:playerComeBack", { pokemonName: getPokemonNameWithAffix(pokemon) }) : i18next.t("battle:trainerComeBack", { - trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), pokemonName: getPokemonNameWithAffix(pokemon) }) ); @@ -1650,7 +1653,7 @@ export class SwitchSummonPhase extends SummonPhase { this.scene.ui.showText(this.player ? i18next.t("battle:playerGo", { pokemonName: getPokemonNameWithAffix(switchedInPokemon) }) : i18next.t("battle:trainerGo", { - trainerName: this.scene.currentBattle.trainer.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), + trainerName: this.scene.currentBattle.trainer?.getName(!(this.fieldIndex % 2) ? TrainerSlot.TRAINER : TrainerSlot.TRAINER_PARTNER), pokemonName: this.getPokemon().getNameToRender() }) ); @@ -1929,8 +1932,8 @@ export class CommandPhase extends FieldPhase { this.fieldIndex = FieldPosition.CENTER; } else { const allyCommand = this.scene.currentBattle.turnCommands[this.fieldIndex - 1]; - if (allyCommand.command === Command.BALL || allyCommand.command === Command.RUN) { - this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand.command, skip: true }; + if (allyCommand?.command === Command.BALL || allyCommand?.command === Command.RUN) { + this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: allyCommand?.command, skip: true }; } } } @@ -1944,8 +1947,8 @@ export class CommandPhase extends FieldPhase { const moveQueue = playerPokemon.getMoveQueue(); while (moveQueue.length && moveQueue[0] - && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m.moveId === moveQueue[0].move) - || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m.moveId === moveQueue[0].move)].isUsable(playerPokemon, moveQueue[0].ignorePP))) { + && moveQueue[0].move && (!playerPokemon.getMoveset().find(m => m?.moveId === moveQueue[0].move) + || !playerPokemon.getMoveset()[playerPokemon.getMoveset().findIndex(m => m?.moveId === moveQueue[0].move)]!.isUsable(playerPokemon, moveQueue[0].ignorePP))) { // TODO: is the bang correct? moveQueue.shift(); } @@ -1954,8 +1957,8 @@ export class CommandPhase extends FieldPhase { if (!queuedMove.move) { this.handleCommand(Command.FIGHT, -1, false); } else { - const moveIndex = playerPokemon.getMoveset().findIndex(m => m.moveId === queuedMove.move); - if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex].isUsable(playerPokemon, queuedMove.ignorePP)) { + const moveIndex = playerPokemon.getMoveset().findIndex(m => m?.moveId === queuedMove.move); + if (moveIndex > -1 && playerPokemon.getMoveset()[moveIndex]!.isUsable(playerPokemon, queuedMove.ignorePP)) { // TODO: is the bang correct? this.handleCommand(Command.FIGHT, moveIndex, queuedMove.ignorePP, { targets: queuedMove.targets, multiple: queuedMove.targets.length > 1 }); } else { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); @@ -1976,8 +1979,8 @@ export class CommandPhase extends FieldPhase { let useStruggle = false; if (cursor === -1 || playerPokemon.trySelectMove(cursor, args[0] as boolean) || - (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m.isUsable(playerPokemon)).length)) { - const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor].moveId : Moves.NONE : Moves.STRUGGLE; + (useStruggle = cursor > -1 && !playerPokemon.getMoveset().filter(m => m?.isUsable(playerPokemon)).length)) { + const moveId = !useStruggle ? cursor > -1 ? playerPokemon.getMoveset()[cursor]!.moveId : Moves.NONE : Moves.STRUGGLE; // TODO: is the bang correct? const turnCommand: TurnCommand = { command: Command.FIGHT, cursor: cursor, move: { move: moveId, targets: [], ignorePP: args[0] }, args: args }; const moveTargets: MoveTargetSet = args.length < 3 ? getMoveTargets(playerPokemon, moveId) : args[2]; if (!moveId) { @@ -1988,16 +1991,16 @@ export class CommandPhase extends FieldPhase { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } if (moveTargets.targets.length <= 1 || moveTargets.multiple) { - turnCommand.move.targets = moveTargets.targets; + turnCommand.move!.targets = moveTargets.targets; //TODO: is the bang correct here? } else if (playerPokemon.getTag(BattlerTagType.CHARGING) && playerPokemon.getMoveQueue().length >= 1) { - turnCommand.move.targets = playerPokemon.getMoveQueue()[0].targets; + turnCommand.move!.targets = playerPokemon.getMoveQueue()[0].targets; //TODO: is the bang correct here? } else { this.scene.unshiftPhase(new SelectTargetPhase(this.scene, this.fieldIndex)); } this.scene.currentBattle.turnCommands[this.fieldIndex] = turnCommand; success = true; } else if (cursor < playerPokemon.getMoveset().length) { - const move = playerPokemon.getMoveset()[cursor]; + const move = playerPokemon.getMoveset()[cursor]!; //TODO: is this bang correct? this.scene.ui.setMode(Mode.MESSAGE); // Decides between a Disabled, Not Implemented, or No PP translation message @@ -2017,14 +2020,14 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballForce"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (this.scene.currentBattle.battleType === BattleType.TRAINER) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballTrainer"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { @@ -2033,23 +2036,23 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballMulti"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (cursor < 5) { const targetPokemon = this.scene.getEnemyField().find(p => p.isActive(true)); - if (targetPokemon.isBoss() && targetPokemon.bossSegmentIndex >= 1 && !targetPokemon.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { + if (targetPokemon?.isBoss() && targetPokemon?.bossSegmentIndex >= 1 && !targetPokemon?.hasAbility(Abilities.WONDER_GUARD, false, true) && cursor < PokeballType.MASTER_BALL) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noPokeballStrong"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { this.scene.currentBattle.turnCommands[this.fieldIndex] = { command: Command.BALL, cursor: cursor }; - this.scene.currentBattle.turnCommands[this.fieldIndex].targets = targets; + this.scene.currentBattle.turnCommands[this.fieldIndex]!.targets = targets; if (this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } success = true; } @@ -2063,14 +2066,14 @@ export class CommandPhase extends FieldPhase { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeForce"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else if (!isSwitch && this.scene.currentBattle.battleType === BattleType.TRAINER) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.showText(i18next.t("battle:noEscapeTrainer"), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); }, null, true); } else { @@ -2086,10 +2089,10 @@ export class CommandPhase extends FieldPhase { : { command: Command.RUN }; success = true; if (!isSwitch && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; } } else if (trapTag) { - if (trapTag.sourceMove === Moves.INGRAIN && this.scene.getPokemonById(trapTag.sourceId).isOfType(Type.GHOST)) { + if (trapTag.sourceMove === Moves.INGRAIN && trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId)?.isOfType(Type.GHOST)) { success = true; this.scene.currentBattle.turnCommands[this.fieldIndex] = isSwitch ? { command: Command.POKEMON, cursor: cursor, args: args } @@ -2102,13 +2105,13 @@ export class CommandPhase extends FieldPhase { } this.scene.ui.showText( i18next.t("battle:noEscapePokemon", { - pokemonName: getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)), + pokemonName: trapTag.sourceId && this.scene.getPokemonById(trapTag.sourceId) ? getPokemonNameWithAffix(this.scene.getPokemonById(trapTag.sourceId)!) : "", moveName: trapTag.getMoveName(), escapeVerb: isSwitch ? i18next.t("battle:escapeVerbSwitch") : i18next.t("battle:escapeVerbFlee") }), null, () => { - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); if (!isSwitch) { this.scene.ui.setMode(Mode.COMMAND, this.fieldIndex); } @@ -2118,11 +2121,11 @@ export class CommandPhase extends FieldPhase { break; } - if (success) { + if (success!) { // TODO: is the bang correct? this.end(); } - return success; + return success!; // TODO: is the bang correct? } cancel() { @@ -2142,9 +2145,9 @@ export class CommandPhase extends FieldPhase { return false; } - const moveIndex = pokemon.getMoveset().findIndex(m => m.moveId === encoreTag.moveId); + const moveIndex = pokemon.getMoveset().findIndex(m => m?.moveId === encoreTag.moveId); - if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex].isUsable(pokemon)) { + if (moveIndex === -1 || !pokemon.getMoveset()[moveIndex]!.isUsable(pokemon)) { // TODO: is this bang correct? return false; } @@ -2254,17 +2257,17 @@ export class SelectTargetPhase extends PokemonPhase { super.start(); const turnCommand = this.scene.currentBattle.turnCommands[this.fieldIndex]; - const move = turnCommand.move?.move; + const move = turnCommand?.move?.move; this.scene.ui.setMode(Mode.TARGET_SELECT, this.fieldIndex, move, (targets: BattlerIndex[]) => { this.scene.ui.setMode(Mode.MESSAGE); if (targets.length < 1) { this.scene.currentBattle.turnCommands[this.fieldIndex] = null; this.scene.unshiftPhase(new CommandPhase(this.scene, this.fieldIndex)); } else { - turnCommand.targets = targets; + turnCommand!.targets = targets; //TODO: is the bang correct here? } - if (turnCommand.command === Command.BALL && this.fieldIndex) { - this.scene.currentBattle.turnCommands[this.fieldIndex - 1].skip = true; + if (turnCommand?.command === Command.BALL && this.fieldIndex) { + this.scene.currentBattle.turnCommands[this.fieldIndex - 1]!.skip = true; //TODO: is the bang correct here? } this.end(); }); @@ -2297,24 +2300,24 @@ export class TurnStartPhase extends FieldPhase { const aCommand = this.scene.currentBattle.turnCommands[a]; const bCommand = this.scene.currentBattle.turnCommands[b]; - if (aCommand.command !== bCommand.command) { - if (aCommand.command === Command.FIGHT) { + if (aCommand?.command !== bCommand?.command) { + if (aCommand?.command === Command.FIGHT) { return 1; - } else if (bCommand.command === Command.FIGHT) { + } else if (bCommand?.command === Command.FIGHT) { return -1; } - } else if (aCommand.command === Command.FIGHT) { - const aMove = allMoves[aCommand.move.move]; - const bMove = allMoves[bCommand.move.move]; + } else if (aCommand?.command === Command.FIGHT) { + const aMove = allMoves[aCommand.move!.move];//TODO: is the bang correct here? + const bMove = allMoves[bCommand!.move!.move];//TODO: is the bang correct here? const aPriority = new Utils.IntegerHolder(aMove.priority); const bPriority = new Utils.IntegerHolder(bMove.priority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a), null, aMove, aPriority); - applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b), null, bMove, bPriority); + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? + applyMoveAttrs(IncrementMovePriorityAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? - applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a), null, aMove, aPriority); - applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b), null, bMove, bPriority); + applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === a)!, null, aMove, aPriority); //TODO: is the bang correct here? + applyAbAttrs(IncrementMovePriorityAbAttr, this.scene.getField().find(p => p?.isActive() && p.getBattlerIndex() === b)!, null, bMove, bPriority); //TODO: is the bang correct here? if (aPriority.value !== bPriority.value) { return aPriority.value < bPriority.value ? 1 : -1; @@ -2338,34 +2341,34 @@ export class TurnStartPhase extends FieldPhase { const pokemon = field[o]; const turnCommand = this.scene.currentBattle.turnCommands[o]; - if (turnCommand.skip) { + if (turnCommand?.skip) { continue; } - switch (turnCommand.command) { + switch (turnCommand?.command) { case Command.FIGHT: const queuedMove = turnCommand.move; pokemon.turnData.order = orderIndex++; if (!queuedMove) { continue; } - const move = pokemon.getMoveset().find(m => m.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); + const move = pokemon.getMoveset().find(m => m?.moveId === queuedMove.move) || new PokemonMove(queuedMove.move); if (pokemon.isPlayer()) { if (turnCommand.cursor === -1) { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move)); + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move));//TODO: is the bang correct here? } else { - const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move, false, queuedMove.ignorePP); + const playerPhase = new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP);//TODO: is the bang correct here? this.scene.pushPhase(playerPhase); } } else { - this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move.targets, move, false, queuedMove.ignorePP)); + this.scene.pushPhase(new MovePhase(this.scene, pokemon, turnCommand.targets || turnCommand.move!.targets, move, false, queuedMove.ignorePP));//TODO: is the bang correct here? } break; case Command.BALL: - this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets[0] % 2, turnCommand.cursor)); + this.scene.unshiftPhase(new AttemptCapturePhase(this.scene, turnCommand.targets![0] % 2, turnCommand.cursor!));//TODO: is the bang correct here? break; case Command.POKEMON: - this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor, true, turnCommand.args[0] as boolean, pokemon.isPlayer())); + this.scene.unshiftPhase(new SwitchSummonPhase(this.scene, pokemon.getFieldIndex(), turnCommand.cursor!, true, turnCommand.args![0] as boolean, pokemon.isPlayer()));//TODO: is the bang correct here? break; case Command.RUN: let runningPokemon = pokemon; @@ -2573,13 +2576,13 @@ export class NewBattlePhase extends BattlePhase { } export class CommonAnimPhase extends PokemonPhase { - private anim: CommonAnim; - private targetIndex: integer; + private anim: CommonAnim | null; + private targetIndex: integer | undefined; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, targetIndex: BattlerIndex, anim: CommonAnim) { + constructor(scene: BattleScene, battlerIndex?: BattlerIndex, targetIndex?: BattlerIndex | undefined, anim?: CommonAnim) { super(scene, battlerIndex); - this.anim = anim; + this.anim = anim!; // TODO: is this bang correct? this.targetIndex = targetIndex; } @@ -2651,8 +2654,8 @@ export class MovePhase extends BattlePhase { this.scene.arena.setIgnoreAbilities(); } } else { - this.pokemon.turnData.hitsLeft = undefined; - this.pokemon.turnData.hitCount = undefined; + this.pokemon.turnData.hitsLeft = 0; // TODO: is `0` correct? + this.pokemon.turnData.hitCount = 0; // TODO: is `0` correct? } // Move redirection abilities (ie. Storm Drain) only support single target moves @@ -2690,7 +2693,8 @@ export class MovePhase extends BattlePhase { if (this.scene.currentBattle.double && this.move.getMove().hasFlag(MoveFlags.REDIRECT_COUNTER)) { if (this.scene.getField()[this.targets[0]].hp === 0) { const opposingField = this.pokemon.isPlayer() ? this.scene.getEnemyField() : this.scene.getPlayerField(); - this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); + //@ts-ignore + this.targets[0] = opposingField.find(p => p.hp > 0)?.getBattlerIndex(); //TODO: fix ts-ignore } } } @@ -2764,7 +2768,7 @@ export class MovePhase extends BattlePhase { return this.end(); } - if (!moveQueue.length || !moveQueue.shift().ignorePP) { // using .shift here clears out two turn moves once they've been used + if (!moveQueue.length || !moveQueue.shift()?.ignorePP) { // using .shift here clears out two turn moves once they've been used this.move.usePp(ppUsed); this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), this.move.ppUsed)); } @@ -2782,7 +2786,7 @@ export class MovePhase extends BattlePhase { } else if (success && this.scene.arena.isMoveTerrainCancelled(this.pokemon, this.targets, this.move.getMove())) { success = false; if (failedText === null) { - failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain.terrainType); + failedText = getTerrainBlockMessage(targets[0], this.scene.arena.terrain?.terrainType!); // TODO: is this bang correct? } } @@ -2882,10 +2886,10 @@ export class MovePhase extends BattlePhase { pokemonNameWithAffix: getPokemonNameWithAffix(this.pokemon), moveName: this.move.getName() }), 500); - applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true), this.move.getMove()); + applyMoveAttrs(PreMoveMessageAttr, this.pokemon, this.pokemon.getOpponents().find(() => true)!, this.move.getMove()); //TODO: is the bang correct here? } - showFailedText(failedText: string = null): void { + showFailedText(failedText: string | null = null): void { this.scene.queueMessage(failedText || i18next.t("battle:attackFailed")); } @@ -2929,7 +2933,7 @@ export class MoveEffectPhase extends PokemonPhase { const move = this.move.getMove(); // Assume single target for override - applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget(), move, overridden, this.move.virtual).then(() => { + applyMoveAttrs(OverrideMoveEffectAttr, user, this.getTarget() ?? null, move, overridden, this.move.virtual).then(() => { if (overridden.value) { return this.end(); @@ -2940,7 +2944,7 @@ export class MoveEffectPhase extends PokemonPhase { if (user.turnData.hitsLeft === undefined) { const hitCount = new Utils.IntegerHolder(1); // Assume single target for multi hit - applyMoveAttrs(MultiHitAttr, user, this.getTarget(), move, hitCount); + applyMoveAttrs(MultiHitAttr, user, this.getTarget() ?? null, move, hitCount); applyPreAttackAbAttrs(AddSecondStrikeAbAttr, user, null, move, targets.length, hitCount, new Utils.IntegerHolder(0)); if (move instanceof AttackMove && !move.hasAttr(FixedDamageAttr)) { this.scene.applyModifiers(PokemonMultiHitModifier, user.isPlayer(), user, hitCount, new Utils.IntegerHolder(0)); @@ -2951,11 +2955,11 @@ export class MoveEffectPhase extends PokemonPhase { const moveHistoryEntry = { move: this.move.moveId, targets: this.targets, result: MoveResult.PENDING, virtual: this.move.virtual }; const targetHitChecks = Object.fromEntries(targets.map(p => [p.getBattlerIndex(), this.hitCheck(p)])); - const activeTargets = targets.map(t => t.isActive(true)); - if (!activeTargets.length || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { + const hasActiveTargets = targets.some(t => t.isActive(true)); + if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) { this.stopMultiHit(); - if (activeTargets.length) { - this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(this.getTarget()) })); + if (hasActiveTargets) { + this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" })); moveHistoryEntry.result = MoveResult.MISS; applyMoveAttrs(MissEffectAttr, user, null, move); } else { @@ -2969,7 +2973,7 @@ export class MoveEffectPhase extends PokemonPhase { const applyAttrs: Promise[] = []; // Move animation only needs one target - new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()).play(this.scene, () => { + new MoveAnim(move.id as Moves, user, this.getTarget()?.getBattlerIndex()!).play(this.scene, () => { // TODO: is the bang correct here? /** Has the move successfully hit a target (for damage) yet? */ let hasHit: boolean = false; for (const target of targets) { @@ -3018,7 +3022,7 @@ export class MoveEffectPhase extends PokemonPhase { applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.PRE_APPLY && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit) && hitResult !== HitResult.NO_EFFECT, user, target, move).then(() => { if (hitResult !== HitResult.FAIL) { - const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget(), move)); + const chargeEffect = !!move.getAttrs(ChargeAttr).find(ca => ca.usedChargeEffect(user, this.getTarget() ?? null, move)); // Charge attribute with charge effect takes all effect attributes and applies them to charge stage, so ignore them if this is present Utils.executeIf(!chargeEffect, () => applyFilteredMoveAttrs((attr: MoveAttr) => attr instanceof MoveEffectAttr && attr.trigger === MoveEffectTrigger.POST_APPLY && attr.selfTarget && (!attr.firstHitOnly || firstHit) && (!attr.lastHitOnly || lastHit), user, target, move)).then(() => { @@ -3082,13 +3086,13 @@ export class MoveEffectPhase extends PokemonPhase { move.type = move.defaultType; const user = this.getUserPokemon(); if (user) { - if (--user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { + if (user.turnData.hitsLeft && --user.turnData.hitsLeft >= 1 && this.getTarget()?.isActive()) { this.scene.unshiftPhase(this.getNewHitPhase()); } else { // Queue message for number of hits made by multi-move // If multi-hit attack only hits once, still want to render a message - const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0); - if (hitsTotal > 1 || user.turnData.hitsLeft > 0) { + const hitsTotal = user.turnData.hitCount! - Math.max(user.turnData.hitsLeft!, 0); // TODO: are those bangs correct? + if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { // If there are multiple hits, or if there are hits of the multi-hit move left this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } @@ -3105,7 +3109,7 @@ export class MoveEffectPhase extends PokemonPhase { return true; } - const user = this.getUserPokemon(); + const user = this.getUserPokemon()!; // TODO: is this bang correct? // Hit check only calculated on first hit for multi-hit moves unless flag is set to check all hits. // However, if an ability with the MaxMultiHitAbAttr, namely Skill Link, is present, act as a normal @@ -3134,7 +3138,7 @@ export class MoveEffectPhase extends PokemonPhase { return false; } - const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user, target); + const moveAccuracy = this.move.getMove().calculateBattleAccuracy(user!, target); // TODO: is the bang correct here? if (moveAccuracy === -1) { return true; @@ -3143,12 +3147,12 @@ export class MoveEffectPhase extends PokemonPhase { const accuracyMultiplier = user.getAccuracyMultiplier(target, this.move.getMove()); const rand = user.randSeedInt(100, 1); - return rand <= moveAccuracy * accuracyMultiplier; + return rand <= moveAccuracy * (accuracyMultiplier!); // TODO: is this bang correct? } - getUserPokemon(): Pokemon { + getUserPokemon(): Pokemon | undefined { if (this.battlerIndex > BattlerIndex.ENEMY_2) { - return this.scene.getPokemonById(this.battlerIndex); + return this.scene.getPokemonById(this.battlerIndex) ?? undefined; } return (this.player ? this.scene.getPlayerField() : this.scene.getEnemyField())[this.fieldIndex]; } @@ -3157,7 +3161,7 @@ export class MoveEffectPhase extends PokemonPhase { return this.scene.getField(true).filter(p => this.targets.indexOf(p.getBattlerIndex()) > -1); } - getTarget(): Pokemon { + getTarget(): Pokemon | undefined { return this.getTargets().find(() => true); } @@ -3178,8 +3182,8 @@ export class MoveEffectPhase extends PokemonPhase { * targets, completely cancel all subsequent strikes. */ if (!target || this.targets.length === 0 ) { - this.getUserPokemon().turnData.hitCount = 1; - this.getUserPokemon().turnData.hitsLeft = 1; + this.getUserPokemon()!.turnData.hitCount = 1; // TODO: is the bang correct here? + this.getUserPokemon()!.turnData.hitsLeft = 1; // TODO: is the bang correct here? } } @@ -3233,7 +3237,7 @@ export class MoveAnimTestPhase extends BattlePhase { initMoveAnim(this.scene, moveId).then(() => { loadMoveAnimAssets(this.scene, [moveId], true) .then(() => { - new MoveAnim(moveId, player ? this.scene.getPlayerPokemon() : this.scene.getEnemyPokemon(), (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon()).getBattlerIndex()).play(this.scene, () => { + new MoveAnim(moveId, player ? this.scene.getPlayerPokemon()! : this.scene.getEnemyPokemon()!, (player !== (allMoves[moveId] instanceof SelfStatusMove) ? this.scene.getEnemyPokemon()! : this.scene.getPlayerPokemon()!).getBattlerIndex()).play(this.scene, () => { // TODO: are the bangs correct here? if (player) { this.playMoveAnim(moveQueue, false); } else { @@ -3259,16 +3263,19 @@ export class ShowAbilityPhase extends PokemonPhase { const pokemon = this.getPokemon(); - this.scene.abilityBar.showAbility(pokemon, this.passive); - if (pokemon.battleData) { - pokemon.battleData.abilityRevealed = true; + if (pokemon) { + this.scene.abilityBar.showAbility(pokemon, this.passive); + + if (pokemon?.battleData) { + pokemon.battleData.abilityRevealed = true; + } } this.end(); } } -export type StatChangeCallback = (target: Pokemon, changed: BattleStat[], relativeChanges: number[]) => void; +export type StatChangeCallback = (target: Pokemon | null, changed: BattleStat[], relativeChanges: number[]) => void; export class StatChangePhase extends PokemonPhase { private stats: BattleStat[]; @@ -3277,10 +3284,10 @@ export class StatChangePhase extends PokemonPhase { private showMessage: boolean; private ignoreAbilities: boolean; private canBeCopied: boolean; - private onChange: StatChangeCallback; + private onChange: StatChangeCallback | null; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback = null) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, selfTarget: boolean, stats: BattleStat[], levels: integer, showMessage: boolean = true, ignoreAbilities: boolean = false, canBeCopied: boolean = true, onChange: StatChangeCallback | null = null) { super(scene, battlerIndex); this.selfTarget = selfTarget; @@ -3329,9 +3336,9 @@ export class StatChangePhase extends PokemonPhase { } const battleStats = this.getPokemon().summonData.battleStats; - const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats[stat] + levels.value, 6) : Math.max(battleStats[stat] + levels.value, -6)) - battleStats[stat]); + const relLevels = filteredStats.map(stat => (levels.value >= 1 ? Math.min(battleStats![stat] + levels.value, 6) : Math.max(battleStats![stat] + levels.value, -6)) - battleStats![stat]); - this.onChange?.(this.getPokemon(), filteredStats, relLevels); + this.onChange && this.onChange(this.getPokemon(), filteredStats, relLevels); const end = () => { if (this.showMessage) { @@ -3393,7 +3400,7 @@ export class StatChangePhase extends PokemonPhase { this.scene.playSound(`stat_${levels.value >= 1 ? "up" : "down"}`); - statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite)); + statSprite.setMask(new Phaser.Display.Masks.BitmapMask(this.scene, pokemonMaskSprite ?? undefined)); this.scene.tweens.add({ targets: statSprite, @@ -3426,7 +3433,7 @@ export class StatChangePhase extends PokemonPhase { getRandomStat(): BattleStat { const allStats = Utils.getEnumValues(BattleStat); - return allStats[this.getPokemon().randSeedInt(BattleStat.SPD + 1)]; + return this.getPokemon() ? allStats[this.getPokemon()!.randSeedInt(BattleStat.SPD + 1)] : BattleStat.ATK; // TODO: return default ATK on random? idk... } aggregateStatChanges(random: boolean = false): void { @@ -3491,7 +3498,7 @@ export class StatChangePhase extends PokemonPhase { } export class WeatherEffectPhase extends CommonAnimPhase { - public weather: Weather; + public weather: Weather | null; constructor(scene: BattleScene) { super(scene, undefined, undefined, CommonAnim.SUNNY + ((scene?.arena?.weather?.weatherType || WeatherType.NONE) - 1)); @@ -3519,7 +3526,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { const inflictDamage = (pokemon: Pokemon) => { const cancelled = new Utils.BooleanHolder(false); - applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather, cancelled); + applyPreWeatherEffectAbAttrs(PreWeatherDamageAbAttr, pokemon, this.weather , cancelled); applyAbAttrs(BlockNonDirectDamageAbAttr, pokemon, cancelled); if (cancelled.value) { @@ -3528,12 +3535,12 @@ export class WeatherEffectPhase extends CommonAnimPhase { const damage = Math.ceil(pokemon.getMaxHp() / 16); - this.scene.queueMessage(getWeatherDamageMessage(this.weather.weatherType, pokemon)); + this.scene.queueMessage(getWeatherDamageMessage(this.weather?.weatherType!, pokemon)!); // TODO: are those bangs correct? pokemon.damageAndUpdate(damage, HitResult.EFFECTIVE, false, false, true); }; this.executeForAll((pokemon: Pokemon) => { - const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather.isTypeDamageImmune(t)).length; + const immune = !pokemon || !!pokemon.getTypes(true, true).filter(t => this.weather?.isTypeDamageImmune(t)).length; if (!immune) { inflictDamage(pokemon); } @@ -3541,7 +3548,7 @@ export class WeatherEffectPhase extends CommonAnimPhase { } } - this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType), null, () => { + this.scene.ui.showText(getWeatherLapseMessage(this.weather.weatherType)!, null, () => { // TODO: is this bang correct? this.executeForAll((pokemon: Pokemon) => applyPostWeatherLapseAbAttrs(PostWeatherLapseAbAttr, pokemon, this.weather)); super.start(); @@ -3550,31 +3557,31 @@ export class WeatherEffectPhase extends CommonAnimPhase { } export class ObtainStatusEffectPhase extends PokemonPhase { - private statusEffect: StatusEffect; - private cureTurn: integer; - private sourceText: string; - private sourcePokemon: Pokemon; + private statusEffect: StatusEffect | undefined; + private cureTurn: integer | null; + private sourceText: string | null; + private sourcePokemon: Pokemon | null; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect: StatusEffect, cureTurn?: integer, sourceText?: string, sourcePokemon?: Pokemon) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, statusEffect?: StatusEffect, cureTurn?: integer | null, sourceText?: string, sourcePokemon?: Pokemon) { super(scene, battlerIndex); this.statusEffect = statusEffect; - this.cureTurn = cureTurn; - this.sourceText = sourceText; - this.sourcePokemon = sourcePokemon; // For tracking which Pokemon caused the status effect + this.cureTurn = cureTurn!; // TODO: is this bang correct? + this.sourceText = sourceText!; // TODO: is this bang correct? + this.sourcePokemon = sourcePokemon!; // For tracking which Pokemon caused the status effect // TODO: is this bang correct? } start() { const pokemon = this.getPokemon(); - if (!pokemon.status) { - if (pokemon.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { + if (!pokemon?.status) { + if (pokemon?.trySetStatus(this.statusEffect, false, this.sourcePokemon)) { if (this.cureTurn) { - pokemon.status.cureTurn = this.cureTurn; + pokemon.status!.cureTurn = this.cureTurn; // TODO: is this bang correct? } pokemon.updateInfo(true); - new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect - 1), pokemon).play(this.scene, () => { - this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText)); - if (pokemon.status.isPostTurn()) { + new CommonBattleAnim(CommonAnim.POISON + (this.statusEffect! - 1), pokemon).play(this.scene, () => { + this.scene.queueMessage(getStatusEffectObtainText(this.statusEffect, getPokemonNameWithAffix(pokemon), this.sourceText ?? undefined)); + if (pokemon.status?.isPostTurn()) { this.scene.pushPhase(new PostTurnStatusEffectPhase(this.scene, this.battlerIndex)); } this.end(); @@ -3640,17 +3647,17 @@ export class PostTurnStatusEffectPhase extends PokemonPhase { export class MessagePhase extends Phase { private text: string; - private callbackDelay: integer; - private prompt: boolean; - private promptDelay: integer; + private callbackDelay: integer | null; + private prompt: boolean | null; + private promptDelay: integer | null; - constructor(scene: BattleScene, text: string, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + constructor(scene: BattleScene, text: string, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { super(scene); this.text = text; - this.callbackDelay = callbackDelay; - this.prompt = prompt; - this.promptDelay = promptDelay; + this.callbackDelay = callbackDelay!; // TODO: is this bang correct? + this.prompt = prompt!; // TODO: is this bang correct? + this.promptDelay = promptDelay!; // TODO: is this bang correct? } start() { @@ -3758,7 +3765,7 @@ export class FaintPhase extends PokemonPhase { constructor(scene: BattleScene, battlerIndex: BattlerIndex, preventEndure?: boolean) { super(scene, battlerIndex); - this.preventEndure = preventEndure; + this.preventEndure = preventEndure!; // TODO: is this bang correct? } start() { @@ -3795,7 +3802,7 @@ export class FaintPhase extends PokemonPhase { if (pokemon.turnData?.attacksReceived?.length) { const lastAttack = pokemon.turnData.attacksReceived[0]; - applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId), new PokemonMove(lastAttack.move).getMove(), lastAttack.result); + applyPostFaintAbAttrs(PostFaintAbAttr, pokemon, this.scene.getPokemonById(lastAttack.sourceId)!, new PokemonMove(lastAttack.move).getMove(), lastAttack.result); // TODO: is this bang correct? } const alivePlayField = this.scene.getField(true); @@ -3916,7 +3923,7 @@ export class VictoryPhase extends PokemonPhase { const multipleParticipantExpBonusModifier = this.scene.findModifier(m => m instanceof MultipleParticipantExpBonusModifier) as MultipleParticipantExpBonusModifier; const nonFaintedPartyMembers = party.filter(p => p.hp); const expPartyMembers = nonFaintedPartyMembers.filter(p => p.level < this.scene.getMaxExpLevel()); - const partyMemberExp = []; + const partyMemberExp: number[] = []; if (participantIds.size) { let expValue = this.getPokemon().getExpValue(); @@ -3968,7 +3975,7 @@ export class VictoryPhase extends PokemonPhase { const medianLevel = Math.floor(totalLevel / expPartyMembers.length); - const recipientExpPartyMemberIndexes = []; + const recipientExpPartyMemberIndexes: number[] = []; expPartyMembers.forEach((expPartyMember, epm) => { if (expPartyMember.level <= medianLevel) { recipientExpPartyMemberIndexes.push(epm); @@ -4043,39 +4050,40 @@ export class TrainerVictoryPhase extends BattlePhase { start() { this.scene.disableMenu = true; - this.scene.playBgm(this.scene.currentBattle.trainer.config.victoryBgm); + this.scene.playBgm(this.scene.currentBattle.trainer?.config.victoryBgm); - this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer.config.moneyMultiplier)); + this.scene.unshiftPhase(new MoneyRewardPhase(this.scene, this.scene.currentBattle.trainer?.config.moneyMultiplier!)); // TODO: is this bang correct? - const modifierRewardFuncs = this.scene.currentBattle.trainer.config.modifierRewardFuncs; + const modifierRewardFuncs = this.scene.currentBattle.trainer?.config.modifierRewardFuncs!; // TODO: is this bang correct? for (const modifierRewardFunc of modifierRewardFuncs) { this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, modifierRewardFunc)); } - const trainerType = this.scene.currentBattle.trainer.config.trainerType; + const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct? if (vouchers.hasOwnProperty(TrainerType[trainerType])) { - if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer.config.isBoss) { + if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) { this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType])); } } - this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer.getName(TrainerSlot.NONE, true) }), null, () => { - const victoryMessages = this.scene.currentBattle.trainer.getVictoryMessages(); + this.scene.ui.showText(i18next.t("battle:trainerDefeated", { trainerName: this.scene.currentBattle.trainer?.getName(TrainerSlot.NONE, true) }), null, () => { + const victoryMessages = this.scene.currentBattle.trainer?.getVictoryMessages()!; // TODO: is this bang correct? let message: string; this.scene.executeWithSeedOffset(() => message = Utils.randSeedItem(victoryMessages), this.scene.currentBattle.waveIndex); + message = message!; // tell TS compiler it's defined now const showMessage = () => { const originalFunc = showMessageOrEnd; - showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer.getName(), null, originalFunc); + showMessageOrEnd = () => this.scene.ui.showDialogue(message, this.scene.currentBattle.trainer?.getName(), null, originalFunc); showMessageOrEnd(); }; let showMessageOrEnd = () => this.end(); if (victoryMessages?.length) { - if (this.scene.currentBattle.trainer.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { + if (this.scene.currentBattle.trainer?.config.hasCharSprite && !this.scene.ui.shouldSkipDialogue(message)) { const originalFunc = showMessageOrEnd; showMessageOrEnd = () => this.scene.charSprite.hide().then(() => this.scene.hideFieldOverlay(250).then(() => originalFunc())); - this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer.getKey(), getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); + this.scene.showFieldOverlay(500).then(() => this.scene.charSprite.showCharacter(this.scene.currentBattle.trainer?.getKey()!, getCharVariantFromDialogue(victoryMessages[0])).then(() => showMessage())); // TODO: is this bang correct? } else { showMessage(); } @@ -4136,7 +4144,7 @@ export class ModifierRewardPhase extends BattlePhase { const newModifier = this.modifierType.newModifier(); this.scene.addModifier(newModifier).then(() => { this.scene.playSound("item_fanfare"); - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier.type.name }), null, () => resolve(), null, true); + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => resolve(), null, true); }); }); } @@ -4154,7 +4162,7 @@ export class GameOverModifierRewardPhase extends ModifierRewardPhase { this.scene.playSound("level_up_fanfare"); this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.fadeIn(250).then(() => { - this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier.type.name }), null, () => { + this.scene.ui.showText(i18next.t("battle:rewardGain", { modifierName: newModifier?.type.name }), null, () => { this.scene.time.delayedCall(1500, () => this.scene.arenaBg.setVisible(true)); resolve(); }, null, true, 1500); @@ -4182,7 +4190,7 @@ export class RibbonModifierRewardPhase extends ModifierRewardPhase { this.scene.ui.showText(i18next.t("battle:beatModeFirstTime", { speciesName: this.species.name, gameMode: this.scene.gameMode.getName(), - newModifier: newModifier.type.name + newModifier: newModifier?.type.name }), null, () => { resolve(); }, null, true, 1500); @@ -4423,12 +4431,12 @@ export class UnlockPhase extends Phase { } export class PostGameOverPhase extends Phase { - private endCardPhase: EndCardPhase; + private endCardPhase: EndCardPhase | null; - constructor(scene: BattleScene, endCardPhase: EndCardPhase) { + constructor(scene: BattleScene, endCardPhase?: EndCardPhase) { super(scene); - this.endCardPhase = endCardPhase; + this.endCardPhase = endCardPhase!; // TODO: is this bang correct? } start() { @@ -4454,8 +4462,8 @@ export class PostGameOverPhase extends Phase { this.scene.ui.fadeOut(500).then(() => { this.scene.ui.getMessageHandler().bg.setVisible(true); - this.endCardPhase.endCard.destroy(); - this.endCardPhase.text.destroy(); + this.endCardPhase?.endCard.destroy(); + this.endCardPhase?.text.destroy(); saveAndReset(); }); } else { @@ -4734,7 +4742,7 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { } this.scene.ui.setMode(messageMode).then(() => { this.scene.ui.showText(i18next.t("battle:countdownPoof"), null, () => { - this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex].getName() }), null, () => { + this.scene.ui.showText(i18next.t("battle:learnMoveForgetSuccess", { pokemonName: getPokemonNameWithAffix(pokemon), moveName: pokemon.moveset[moveIndex]!.getName() }), null, () => { // TODO: is the bang correct? this.scene.ui.showText(i18next.t("battle:learnMoveAnd"), null, () => { pokemon.setMove(moveIndex, Moves.NONE); this.scene.unshiftPhase(new LearnMovePhase(this.scene, this.partyMemberIndex, this.moveId)); @@ -4756,14 +4764,14 @@ export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { export class PokemonHealPhase extends CommonAnimPhase { private hpHealed: integer; - private message: string; + private message: string | null; private showFullHpMessage: boolean; private skipAnim: boolean; private revive: boolean; private healStatus: boolean; private preventFullHeal: boolean; - constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { + constructor(scene: BattleScene, battlerIndex: BattlerIndex, hpHealed: integer, message: string | null, showFullHpMessage: boolean, skipAnim: boolean = false, revive: boolean = false, healStatus: boolean = false, preventFullHeal: boolean = false) { super(scene, battlerIndex, undefined, CommonAnim.HEALTH_UP); this.hpHealed = hpHealed; @@ -5171,7 +5179,7 @@ export class SelectModifierPhase extends BattlePhase { super(scene); this.rerollCount = rerollCount; - this.modifierTiers = modifierTiers; + this.modifierTiers = modifierTiers!; // TODO: is this bang correct? } start() { @@ -5214,7 +5222,7 @@ export class SelectModifierPhase extends BattlePhase { return false; } else { this.scene.reroll = true; - this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type.tier))); + this.scene.unshiftPhase(new SelectModifierPhase(this.scene, this.rerollCount + 1, typeOptions.map(o => o.type?.tier).filter(t => t !== undefined) as ModifierTier[])); this.scene.ui.clearText(); this.scene.ui.setMode(Mode.MESSAGE).then(() => super.end()); if (!Overrides.WAIVE_ROLL_FEE_OVERRIDE) { @@ -5252,17 +5260,21 @@ export class SelectModifierPhase extends BattlePhase { } return true; case 1: - modifierType = typeOptions[cursor].type; + if (typeOptions[cursor].type) { + modifierType = typeOptions[cursor].type; + } break; default: const shopOptions = getPlayerShopModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex, this.scene.getWaveMoneyAmount(1)); const shopOption = shopOptions[rowCursor > 2 || shopOptions.length <= SHOP_OPTIONS_ROW_LIMIT ? cursor : cursor + SHOP_OPTIONS_ROW_LIMIT]; - modifierType = shopOption.type; + if (shopOption.type) { + modifierType = shopOption.type; + } cost = shopOption.cost; break; } - if (cost && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { + if (cost! && (this.scene.money < cost) && !Overrides.WAIVE_ROLL_FEE_OVERRIDE) { // TODO: is the bang on cost correct? this.scene.ui.playError(); return false; } @@ -5297,12 +5309,12 @@ export class SelectModifierPhase extends BattlePhase { } }; - if (modifierType instanceof PokemonModifierType) { + if (modifierType! instanceof PokemonModifierType) { //TODO: is the bang correct? if (modifierType instanceof FusePokemonModifierType) { this.scene.ui.setModeWithoutClear(Mode.PARTY, PartyUiMode.SPLICE, -1, (fromSlotIndex: integer, spliceSlotIndex: integer) => { if (spliceSlotIndex !== undefined && fromSlotIndex < 6 && spliceSlotIndex < 6 && fromSlotIndex !== spliceSlotIndex) { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer()).then(() => { - const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex]); + const modifier = modifierType.newModifier(party[fromSlotIndex], party[spliceSlotIndex])!; //TODO: is the bang correct? applyModifier(modifier, true); }); } else { @@ -5330,7 +5342,7 @@ export class SelectModifierPhase extends BattlePhase { ? modifierType.newModifier(party[slotIndex]) : modifierType.newModifier(party[slotIndex], option as integer) : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1); - applyModifier(modifier, true); + applyModifier(modifier!, true); // TODO: is the bang correct? }); } else { this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); @@ -5338,10 +5350,10 @@ export class SelectModifierPhase extends BattlePhase { }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined, tmMoveId, isPpRestoreModifier); } } else { - applyModifier(modifierType.newModifier()); + applyModifier(modifierType!.newModifier()!); // TODO: is the bang correct? } - return !cost; + return !cost!;// TODO: is the bang correct? }; this.scene.ui.setMode(Mode.MODIFIER_SELECT, this.isPlayer(), typeOptions, modifierSelectCallback, this.getRerollCost(typeOptions, this.scene.lockModifierTiers)); } @@ -5361,7 +5373,7 @@ export class SelectModifierPhase extends BattlePhase { } else if (lockRarities) { const tierValues = [50, 125, 300, 750, 2000]; for (const opt of typeOptions) { - baseValue += tierValues[opt.type.tier]; + baseValue += opt.type?.tier ? tierValues[opt.type.tier] : 0; } } else { baseValue = 250; @@ -5496,7 +5508,7 @@ export class PartyHealPhase extends BattlePhase { pokemon.hp = pokemon.getMaxHp(); pokemon.resetStatus(); for (const move of pokemon.moveset) { - move.ppUsed = 0; + move!.ppUsed = 0; // TODO: is this bang correct? } pokemon.updateInfo(true); } diff --git a/src/pipelines/sprite.ts b/src/pipelines/sprite.ts index 741c31183d4..c9a76dc50a4 100644 --- a/src/pipelines/sprite.ts +++ b/src/pipelines/sprite.ts @@ -377,7 +377,7 @@ export default class SpritePipeline extends FieldSpritePipeline { this.set2f("texSize", sprite.texture.source[0].width, sprite.texture.source[0].height); this.set1f("yOffset", sprite.height - sprite.frame.height * (isEntityObj ? sprite.parentContainer.scale : sprite.scale)); this.set4fv("tone", tone); - this.bindTexture(this.game.textures.get("tera").source[0].glTexture, 1); + this.bindTexture(this.game.textures.get("tera").source[0].glTexture!, 1); // TODO: is this bang correct? if ((gameObject.scene as BattleScene).fusionPaletteSwaps) { const spriteColors = ((ignoreOverride && data["spriteColorsBase"]) || data["spriteColors"] || []) as number[][]; diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 54dc545868c..82cde7cd2ad 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -157,7 +157,7 @@ export async function initI18n(): Promise { postProcess: ["korean-postposition"], }); - await initFonts(localStorage.getItem("prLang")); + await initFonts(localStorage.getItem("prLang") ?? undefined); } export default i18next; diff --git a/src/system/achv.ts b/src/system/achv.ts index 0dcf74ce3a5..b1f575fb31c 100644 --- a/src/system/achv.ts +++ b/src/system/achv.ts @@ -6,6 +6,7 @@ import * as Utils from "../utils"; import { PlayerGender } from "#enums/player-gender"; import { ParseKeys } from "i18next"; import { Challenge, FreshStartChallenge, SingleGenerationChallenge, SingleTypeChallenge } from "#app/data/challenge.js"; +import { ConditionFn } from "#app/@types/common.js"; export enum AchvTier { COMMON, @@ -27,9 +28,9 @@ export class Achv { public hasParent: boolean; public parentId: string; - private conditionFunc: (scene: BattleScene, args: any[]) => boolean; + private conditionFunc: ConditionFn | undefined; - constructor(localizationKey:string, name: string, description: string, iconImage: string, score: integer, conditionFunc?: (scene: BattleScene, args: any[]) => boolean) { + constructor(localizationKey:string, name: string, description: string, iconImage: string, score: integer, conditionFunc?: ConditionFn) { this.name = name; this.description = description; this.iconImage = iconImage; @@ -63,7 +64,7 @@ export class Achv { return this; } - validate(scene: BattleScene, args: any[]): boolean { + validate(scene: BattleScene, args?: any[]): boolean { return !this.conditionFunc || this.conditionFunc(scene, args); } diff --git a/src/system/arena-data.ts b/src/system/arena-data.ts index f6db53d7f7f..886129edcf6 100644 --- a/src/system/arena-data.ts +++ b/src/system/arena-data.ts @@ -6,15 +6,15 @@ import { Terrain } from "#app/data/terrain.js"; export default class ArenaData { public biome: Biome; - public weather: Weather; - public terrain: Terrain; + public weather: Weather | null; + public terrain: Terrain | null; public tags: ArenaTag[]; constructor(source: Arena | any) { const sourceArena = source instanceof Arena ? source as Arena : null; this.biome = sourceArena ? sourceArena.biomeType : source.biome; - this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : undefined; - this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : undefined; + this.weather = sourceArena ? sourceArena.weather : source.weather ? new Weather(source.weather.weatherType, source.weather.turnsLeft) : null; + this.terrain = sourceArena ? sourceArena.terrain : source.terrain ? new Terrain(source.terrain.terrainType, source.terrain.turnsLeft) : null; this.tags = sourceArena ? sourceArena.tags : []; } } diff --git a/src/system/game-data.ts b/src/system/game-data.ts index 161b5a2c49b..2cc6ab93c61 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -41,6 +41,8 @@ import { Moves } from "#enums/moves"; import { PlayerGender } from "#enums/player-gender"; import { Species } from "#enums/species"; import { applyChallenges, ChallengeType } from "#app/data/challenge.js"; +import { WeatherType } from "#app/enums/weather-type.js"; +import { TerrainType } from "#app/data/terrain.js"; export const defaultStarterSpecies: Species[] = [ Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, @@ -78,7 +80,7 @@ export function getDataTypeKey(dataType: GameDataType, slotId: integer = 0): str export function encrypt(data: string, bypassLogin: boolean): string { return (bypassLogin ? (data: string) => btoa(data) - : (data: string) => AES.encrypt(data, saveKey))(data); + : (data: string) => AES.encrypt(data, saveKey))(data) as unknown as string; // TODO: is this correct? } export function decrypt(data: string, bypassLogin: boolean): string { @@ -230,7 +232,7 @@ export class StarterPrefs { } export interface StarterDataEntry { - moveset: StarterMoveset | StarterFormMoveData; + moveset: StarterMoveset | StarterFormMoveData | null; eggMoves: integer; candyCount: integer; friendship: integer; @@ -279,7 +281,7 @@ export class GameData { public gender: PlayerGender; public dexData: DexData; - private defaultDexData: DexData; + private defaultDexData: DexData | null; public starterData: StarterData; @@ -352,7 +354,7 @@ export class GameData { const maxIntAttrValue = 0x80000000; const systemData = JSON.stringify(data, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v); - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(systemData, bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemData, bypassLogin)); if (!bypassLogin) { Utils.apiPost(`savedata/system/update?clientSessionId=${clientSessionId}`, systemData, undefined, true) @@ -384,7 +386,7 @@ export class GameData { return new Promise(resolve => { console.log("Client Session:", clientSessionId); - if (bypassLogin && !localStorage.getItem(`data_${loggedInUser.username}`)) { + if (bypassLogin && !localStorage.getItem(`data_${loggedInUser?.username}`)) { return resolve(false); } @@ -404,11 +406,11 @@ export class GameData { return resolve(false); } - const cachedSystem = localStorage.getItem(`data_${loggedInUser.username}`); - this.initSystem(response, cachedSystem ? AES.decrypt(cachedSystem, saveKey).toString(enc.Utf8) : null).then(resolve); + const cachedSystem = localStorage.getItem(`data_${loggedInUser?.username}`); + this.initSystem(response, cachedSystem ? AES.decrypt(cachedSystem, saveKey).toString(enc.Utf8) : undefined).then(resolve); }); } else { - this.initSystem(decrypt(localStorage.getItem(`data_${loggedInUser.username}`), bypassLogin)).then(resolve); + this.initSystem(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)).then(resolve); // TODO: is this bang correct? } }); } @@ -431,7 +433,7 @@ export class GameData { console.debug(systemData); - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(systemDataStr, bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(systemDataStr, bypassLogin)); /*const versions = [ this.scene.game.config.gameVersion, data.gameVersion || '0.0.0' ]; @@ -606,9 +608,9 @@ export class GameData { if (bypassLogin) { return; } - localStorage.removeItem(`data_${loggedInUser.username}`); + localStorage.removeItem(`data_${loggedInUser?.username}`); for (let s = 0; s < 5; s++) { - localStorage.removeItem(`sessionData${s ? s : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${s ? s : ""}_${loggedInUser?.username}`); } } @@ -621,7 +623,7 @@ export class GameData { public saveSetting(setting: string, valueIndex: integer): boolean { let settings: object = {}; if (localStorage.hasOwnProperty("settings")) { - settings = JSON.parse(localStorage.getItem("settings")); + settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct? } setSetting(this.scene, setting, valueIndex); @@ -644,7 +646,7 @@ export class GameData { const key = deviceName.toLowerCase(); // Convert the gamepad name to lowercase to use as a key let mappingConfigs: object = {}; // Initialize an empty object to hold the mapping configurations if (localStorage.hasOwnProperty("mappingConfigs")) {// Check if 'mappingConfigs' exists in localStorage - mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")); + mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")!); // TODO: is this bang correct? } // Parse the existing 'mappingConfigs' from localStorage if (!mappingConfigs[key]) { mappingConfigs[key] = {}; @@ -669,7 +671,7 @@ export class GameData { return false; } // If 'mappingConfigs' does not exist, return false - const mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")); // Parse the existing 'mappingConfigs' from localStorage + const mappingConfigs = JSON.parse(localStorage.getItem("mappingConfigs")!); // Parse the existing 'mappingConfigs' from localStorage // TODO: is this bang correct? for (const key of Object.keys(mappingConfigs)) {// Iterate over the keys of the mapping configurations this.scene.inputController.injectConfig(key, mappingConfigs[key]); @@ -684,6 +686,7 @@ export class GameData { } // If 'mappingConfigs' does not exist, return false localStorage.removeItem("mappingConfigs"); this.scene.inputController.resetConfigs(); + return true; // TODO: is `true` the correct return value? } /** @@ -703,7 +706,7 @@ export class GameData { let settingsControls: object = {}; // Initialize an empty object to hold the gamepad settings if (localStorage.hasOwnProperty(localStoragePropertyName)) { // Check if 'settingsControls' exists in localStorage - settingsControls = JSON.parse(localStorage.getItem(localStoragePropertyName)); // Parse the existing 'settingsControls' from localStorage + settingsControls = JSON.parse(localStorage.getItem(localStoragePropertyName)!); // Parse the existing 'settingsControls' from localStorage // TODO: is this bang correct? } if (device === Device.GAMEPAD) { @@ -734,11 +737,13 @@ export class GameData { return false; } - const settings = JSON.parse(localStorage.getItem("settings")); + const settings = JSON.parse(localStorage.getItem("settings")!); // TODO: is this bang correct? for (const setting of Object.keys(settings)) { setSetting(this.scene, setting, settings[setting]); } + + return true; // TODO: is `true` the correct return value? } private loadGamepadSettings(): boolean { @@ -747,18 +752,20 @@ export class GameData { if (!localStorage.hasOwnProperty("settingsGamepad")) { return false; } - const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")); + const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct? for (const setting of Object.keys(settingsGamepad)) { setSettingGamepad(this.scene, setting as SettingGamepad, settingsGamepad[setting]); } + + return true; // TODO: is `true` the correct return value? } public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean { const key = getDataTypeKey(GameDataType.TUTORIALS); let tutorials: object = {}; if (localStorage.hasOwnProperty(key)) { - tutorials = JSON.parse(localStorage.getItem(key)); + tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? } Object.keys(Tutorial).map(t => t as Tutorial).forEach(t => { @@ -784,7 +791,7 @@ export class GameData { return ret; } - const tutorials = JSON.parse(localStorage.getItem(key)); + const tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? for (const tutorial of Object.keys(tutorials)) { ret[tutorial] = tutorials[tutorial]; @@ -812,7 +819,7 @@ export class GameData { return ret; } - const dialogues = JSON.parse(localStorage.getItem(key)); + const dialogues = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct? for (const dialogue of Object.keys(dialogues)) { ret[dialogue] = dialogues[dialogue]; @@ -843,7 +850,7 @@ export class GameData { } as SessionSaveData; } - getSession(slotId: integer): Promise { + getSession(slotId: integer): Promise { return new Promise(async (resolve, reject) => { if (slotId < 0) { return resolve(null); @@ -858,7 +865,7 @@ export class GameData { } }; - if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`)) { + if (!bypassLogin && !localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`)) { Utils.apiFetch(`savedata/session/get?slot=${slotId}&clientSessionId=${clientSessionId}`, true) .then(response => response.text()) .then(async response => { @@ -867,12 +874,12 @@ export class GameData { return resolve(null); } - localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`, encrypt(response, bypassLogin)); + localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypt(response, bypassLogin)); await handleSessionData(response); }); } else { - const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`); + const sessionData = localStorage.getItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); if (sessionData) { await handleSessionData(decrypt(sessionData, bypassLogin)); } else { @@ -934,7 +941,7 @@ export class GameData { const battleType = sessionData.battleType || 0; const trainerConfig = sessionData.trainer ? trainerConfigs[sessionData.trainer.trainerType] : null; - const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1); + const battle = scene.newBattle(sessionData.waveIndex, battleType, sessionData.trainer, battleType === BattleType.TRAINER ? trainerConfig?.doubleOnly || sessionData.trainer?.variant === TrainerVariant.DOUBLE : sessionData.enemyParty.length > 1)!; // TODO: is this bang correct? battle.enemyLevels = sessionData.enemyParty.map(p => p.level); scene.arena.init(); @@ -950,10 +957,10 @@ export class GameData { }); scene.arena.weather = sessionData.arena.weather; - scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(null, scene.arena.weather?.weatherType, scene.arena.weather?.turnsLeft)); + scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(WeatherType.NONE, scene.arena.weather?.weatherType!, scene.arena.weather?.turnsLeft!)); // TODO: is this bang correct? scene.arena.terrain = sessionData.arena.terrain; - scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(null, scene.arena.terrain?.terrainType, scene.arena.terrain?.turnsLeft)); + scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(TerrainType.NONE, scene.arena.terrain?.terrainType!, scene.arena.terrain?.turnsLeft!)); // TODO: is this bang correct? // TODO //scene.arena.tags = sessionData.arena.tags; @@ -983,7 +990,7 @@ export class GameData { initSessionFromData(sessionData); } else { this.getSession(slotId) - .then(data => initSessionFromData(data)) + .then(data => data && initSessionFromData(data)) .catch(err => { reject(err); return; @@ -999,7 +1006,7 @@ export class GameData { deleteSession(slotId: integer): Promise { return new Promise(resolve => { if (bypassLogin) { - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); return resolve(true); } @@ -1009,8 +1016,8 @@ export class GameData { } Utils.apiFetch(`savedata/session/delete?slot=${slotId}&clientSessionId=${clientSessionId}`, true).then(response => { if (response.ok) { - loggedInUser.lastSessionSlot = -1; - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); resolve(true); } return response.text(); @@ -1040,7 +1047,7 @@ export class GameData { if (sessionData.gameMode === GameModes.DAILY) { if (localStorage.hasOwnProperty("daily")) { - daily = JSON.parse(atob(localStorage.getItem("daily"))); + daily = JSON.parse(atob(localStorage.getItem("daily")!)); // TODO: is this bang correct? if (daily.includes(seed)) { return resolve(false); } else { @@ -1062,7 +1069,7 @@ export class GameData { tryClearSession(scene: BattleScene, slotId: integer): Promise<[success: boolean, newClear: boolean]> { return new Promise<[boolean, boolean]>(resolve => { if (bypassLogin) { - localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser.username}`); + localStorage.removeItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`); return resolve([true, true]); } @@ -1073,8 +1080,8 @@ export class GameData { const sessionData = this.getSessionSaveData(scene); Utils.apiPost(`savedata/session/clear?slot=${slotId}&trainerId=${this.trainerId}&secretId=${this.secretId}&clientSessionId=${clientSessionId}`, JSON.stringify(sessionData), undefined, true).then(response => { if (response.ok) { - loggedInUser.lastSessionSlot = -1; - localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser.username}`); + loggedInUser!.lastSessionSlot = -1; // TODO: is the bang correct? + localStorage.removeItem(`sessionData${this.scene.sessionSlotId ? this.scene.sessionSlotId : ""}_${loggedInUser?.username}`); } return response.json(); }).then(jsonResponse => { @@ -1161,10 +1168,10 @@ export class GameData { if (sync) { this.scene.ui.savingIcon.show(); } - const sessionData = useCachedSession ? this.parseSessionData(decrypt(localStorage.getItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}`), bypassLogin)) : this.getSessionSaveData(scene); + const sessionData = useCachedSession ? this.parseSessionData(decrypt(localStorage.getItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser?.username}`)!, bypassLogin)) : this.getSessionSaveData(scene); // TODO: is this bang correct? const maxIntAttrValue = 0x80000000; - const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser.username}`), bypassLogin)) : this.getSystemSaveData(); + const systemData = useCachedSystem ? this.parseSystemData(decrypt(localStorage.getItem(`data_${loggedInUser?.username}`)!, bypassLogin)) : this.getSystemSaveData(); // TODO: is this bang correct? const request = { system: systemData, @@ -1173,9 +1180,9 @@ export class GameData { clientSessionId: clientSessionId }; - localStorage.setItem(`data_${loggedInUser.username}`, encrypt(JSON.stringify(systemData, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), bypassLogin)); + localStorage.setItem(`data_${loggedInUser?.username}`, encrypt(JSON.stringify(systemData, (k: any, v: any) => typeof v === "bigint" ? v <= maxIntAttrValue ? Number(v) : v.toString() : v), bypassLogin)); - localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser.username}`, encrypt(JSON.stringify(sessionData), bypassLogin)); + localStorage.setItem(`sessionData${scene.sessionSlotId ? scene.sessionSlotId : ""}_${loggedInUser?.username}`, encrypt(JSON.stringify(sessionData), bypassLogin)); console.debug("Session data saved"); @@ -1212,7 +1219,7 @@ export class GameData { public tryExportData(dataType: GameDataType, slotId: integer = 0): Promise { return new Promise(resolve => { - const dataKey: string = `${getDataTypeKey(dataType, slotId)}_${loggedInUser.username}`; + const dataKey: string = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`; const handleData = (dataStr: string) => { switch (dataType) { case GameDataType.SYSTEM: @@ -1251,7 +1258,7 @@ export class GameData { } public importData(dataType: GameDataType, slotId: integer = 0): void { - const dataKey = `${getDataTypeKey(dataType, slotId)}_${loggedInUser.username}`; + const dataKey = `${getDataTypeKey(dataType, slotId)}_${loggedInUser?.username}`; let saveFile: any = document.getElementById("saveFile"); if (saveFile) { @@ -1270,7 +1277,7 @@ export class GameData { reader.onload = (_ => { return e => { let dataName: string; - let dataStr = AES.decrypt(e.target.result.toString(), saveKey).toString(enc.Utf8); + let dataStr = AES.decrypt(e.target?.result?.toString()!, saveKey).toString(enc.Utf8); // TODO: is this bang correct? let valid = false; try { dataName = GameDataType[dataType].toLowerCase(); @@ -1293,10 +1300,11 @@ export class GameData { console.error(ex); } - const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText(null, 0), Utils.fixedInt(1500)); + const displayError = (error: string) => this.scene.ui.showText(error, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500)); + dataName = dataName!; // tell TS compiler that dataName is defined! if (!valid) { - return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText(null, 0), Utils.fixedInt(1500)); + return this.scene.ui.showText(`Your ${dataName} data could not be loaded. It may be corrupted.`, null, () => this.scene.ui.showText("", 0), Utils.fixedInt(1500)); } this.scene.ui.showText(`Your ${dataName} data will be overridden and the page will reload. Proceed?`, null, () => { @@ -1329,7 +1337,7 @@ export class GameData { } }, () => { this.scene.ui.revertMode(); - this.scene.ui.showText(null, 0); + this.scene.ui.showText("", 0); }, false, -98); }); }; @@ -1652,7 +1660,7 @@ export class GameData { return dexAttr & DexAttr.SHINY ? dexAttr & DexAttr.VARIANT_3 ? 3 : dexAttr & DexAttr.VARIANT_2 ? 2 : 1 : 0; } - getNaturesForAttr(natureAttr: integer): Nature[] { + getNaturesForAttr(natureAttr: integer = 0): Nature[] { const ret: Nature[] = []; for (let n = 0; n < 25; n++) { if (natureAttr & (1 << (n + 1))) { @@ -1707,7 +1715,7 @@ export class GameData { entry.hatchedCount = 0; } if (!entry.hasOwnProperty("natureAttr") || (entry.caughtAttr && !entry.natureAttr)) { - entry.natureAttr = this.defaultDexData[k].natureAttr || (1 << Utils.randInt(25, 1)); + entry.natureAttr = this.defaultDexData?.[k].natureAttr || (1 << Utils.randInt(25, 1)); } } } diff --git a/src/system/modifier-data.ts b/src/system/modifier-data.ts index 6f169280da1..0f3e28fe11c 100644 --- a/src/system/modifier-data.ts +++ b/src/system/modifier-data.ts @@ -1,6 +1,6 @@ import BattleScene from "../battle-scene"; import { PersistentModifier } from "../modifier/modifier"; -import { GeneratedPersistentModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type"; +import { GeneratedPersistentModifierType, ModifierType, ModifierTypeGenerator, getModifierTypeFuncById } from "../modifier/modifier-type"; export default class ModifierData { private player: boolean; @@ -27,14 +27,14 @@ export default class ModifierData { this.className = sourceModifier ? sourceModifier.constructor.name : source.className; } - toModifier(scene: BattleScene, constructor: any): PersistentModifier { + toModifier(scene: BattleScene, constructor: any): PersistentModifier | null { const typeFunc = getModifierTypeFuncById(this.typeId); if (!typeFunc) { return null; } try { - let type = typeFunc(); + let type: ModifierType | null = typeFunc(); type.id = this.typeId; if (type instanceof ModifierTypeGenerator) { diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index ca072c9eec8..8f094379434 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -33,8 +33,8 @@ export default class PokemonData { public ivs: integer[]; public nature: Nature; public natureOverride: Nature | -1; - public moveset: PokemonMove[]; - public status: Status; + public moveset: (PokemonMove | null)[]; + public status: Status | null; public friendship: integer; public metLevel: integer; public metBiome: Biome | -1; @@ -117,7 +117,7 @@ export default class PokemonData { if (!forHistory) { this.status = source.status ? new Status(source.status.effect, source.status.turnCount, source.status.cureTurn) - : undefined; + : null; } this.summonData = new PokemonSummonData(); diff --git a/src/system/voucher.ts b/src/system/voucher.ts index b06e6e5309e..0c71e3c0286 100644 --- a/src/system/voucher.ts +++ b/src/system/voucher.ts @@ -3,6 +3,7 @@ import i18next from "i18next"; import { Achv, AchvTier, achvs, getAchievementDescription } from "./achv"; import { PlayerGender } from "#enums/player-gender"; import { TrainerType } from "#enums/trainer-type"; +import { ConditionFn } from "#app/@types/common.js"; export enum VoucherType { REGULAR, @@ -16,15 +17,15 @@ export class Voucher { public voucherType: VoucherType; public description: string; - private conditionFunc: (scene: BattleScene, args: any[]) => boolean; + private conditionFunc: ConditionFn | undefined; - constructor(voucherType: VoucherType, description: string, conditionFunc?: (scene: BattleScene, args: any[]) => boolean) { + constructor(voucherType: VoucherType, description: string, conditionFunc?: ConditionFn) { this.description = description; this.voucherType = voucherType; this.conditionFunc = conditionFunc; } - validate(scene: BattleScene, args: any[]): boolean { + validate(scene: BattleScene, args?: any[]): boolean { return !this.conditionFunc || this.conditionFunc(scene, args); } diff --git a/src/test/abilities/battle_bond.test.ts b/src/test/abilities/battle_bond.test.ts index ce41f9b6603..1a5c71b4c15 100644 --- a/src/test/abilities/battle_bond.test.ts +++ b/src/test/abilities/battle_bond.test.ts @@ -46,12 +46,12 @@ describe("Abilities - BATTLE BOND", () => { await game.startBattle([Species.MAGIKARP, Species.GRENINJA]); const greninja = game.scene.getParty().find((p) => p.species.speciesId === Species.GRENINJA); - expect(greninja).not.toBe(undefined); - expect(greninja.formIndex).toBe(ashForm); + expect(greninja).toBeDefined(); + expect(greninja!.formIndex).toBe(ashForm); - greninja.hp = 0; - greninja.status = new Status(StatusEffect.FAINT); - expect(greninja.isFainted()).toBe(true); + greninja!.hp = 0; + greninja!.status = new Status(StatusEffect.FAINT); + expect(greninja!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - BATTLE BOND", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(greninja.formIndex).toBe(baseForm); + expect(greninja!.formIndex).toBe(baseForm); }, TIMEOUT ); diff --git a/src/test/abilities/disguise.test.ts b/src/test/abilities/disguise.test.ts index 3a6fd540d27..09a0dbf7f8a 100644 --- a/src/test/abilities/disguise.test.ts +++ b/src/test/abilities/disguise.test.ts @@ -47,11 +47,11 @@ describe("Abilities - DISGUISE", () => { const mimikyu = game.scene.getParty().find((p) => p.species.speciesId === Species.MIMIKYU); expect(mimikyu).not.toBe(undefined); - expect(mimikyu.formIndex).toBe(bustedForm); + expect(mimikyu!.formIndex).toBe(bustedForm); - mimikyu.hp = 0; - mimikyu.status = new Status(StatusEffect.FAINT); - expect(mimikyu.isFainted()).toBe(true); + mimikyu!.hp = 0; + mimikyu!.status = new Status(StatusEffect.FAINT); + expect(mimikyu!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - DISGUISE", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(mimikyu.formIndex).toBe(baseForm); + expect(mimikyu!.formIndex).toBe(baseForm); }, TIMEOUT ); @@ -80,17 +80,17 @@ describe("Abilities - DISGUISE", () => { await game.startBattle([Species.MIMIKYU]); - const mimikyu = game.scene.getPlayerPokemon(); - const damage = (Math.floor(mimikyu.getMaxHp()/8)); + const mimikyu = game.scene.getPlayerPokemon()!; + const damage = (Math.floor(mimikyu!.getMaxHp()/8)); expect(mimikyu).not.toBe(undefined); - expect(mimikyu.formIndex).toBe(baseForm); + expect(mimikyu!.formIndex).toBe(baseForm); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(TurnEndPhase); - expect(mimikyu.formIndex).toBe(bustedForm); - expect(game.scene.getEnemyPokemon().turnData.currDamageDealt).toBe(damage); + expect(mimikyu!.formIndex).toBe(bustedForm); + expect(game.scene.getEnemyPokemon()!.turnData.currDamageDealt).toBe(damage); }, TIMEOUT ); diff --git a/src/test/abilities/dry_skin.test.ts b/src/test/abilities/dry_skin.test.ts index 6da5ad08571..20b85eab767 100644 --- a/src/test/abilities/dry_skin.test.ts +++ b/src/test/abilities/dry_skin.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); // first turn @@ -59,7 +59,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -82,7 +82,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; const initialHP = 1000; enemy.hp = initialHP; @@ -108,7 +108,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -123,7 +123,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; @@ -139,7 +139,7 @@ describe("Abilities - Dry Skin", () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; expect(enemy).not.toBe(undefined); enemy.hp = 1; diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index f9329017006..b64cbe2ef10 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -58,7 +58,7 @@ describe("Abilities - Gulp Missile", () => { it("changes to Gulping Form if HP is over half when Surf or Dive is used", async () => { await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); await game.toNextTurn(); @@ -72,7 +72,7 @@ describe("Abilities - Gulp Missile", () => { it("changes to Gorging Form if HP is under half when Surf or Dive is used", async () => { await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.49); expect(cramorant.getHpRatio()).toBe(.49); @@ -88,7 +88,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle([Species.CRAMORANT]); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); @@ -101,7 +101,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.TAIL_WHIP)); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); @@ -120,8 +120,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); @@ -144,8 +144,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "damageAndUpdate"); vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.45); @@ -159,7 +159,7 @@ describe("Abilities - Gulp Missile", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(enemy.damageAndUpdate).toHaveReturnedWith(getEffectDamage(enemy)); - expect(enemy.status.effect).toBe(StatusEffect.PARALYSIS); + expect(enemy.status?.effect).toBe(StatusEffect.PARALYSIS); expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_PIKACHU)).toBeUndefined(); expect(cramorant.formIndex).toBe(NORMAL_FORM); }); @@ -172,7 +172,7 @@ describe("Abilities - Gulp Missile", () => { .enemyLevel(5); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); await game.toNextTurn(); @@ -194,8 +194,8 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)).enemyAbility(Abilities.MAGIC_GUARD); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); @@ -218,7 +218,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.GASTRO_ACID)); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); @@ -238,7 +238,7 @@ describe("Abilities - Gulp Missile", () => { game.override.enemyMoveset(Array(4).fill(Moves.SKILL_SWAP)); await game.startBattle([Species.CRAMORANT]); - const cramorant = game.scene.getPlayerPokemon(); + const cramorant = game.scene.getPlayerPokemon()!; vi.spyOn(cramorant, "getHpRatio").mockReturnValue(.55); game.doAttack(getMovePosition(game.scene, 0, Moves.SURF)); @@ -261,6 +261,6 @@ describe("Abilities - Gulp Missile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(TurnStartPhase); - expect(game.scene.getEnemyPokemon().hasAbility(Abilities.GULP_MISSILE)).toBe(false); + expect(game.scene.getEnemyPokemon()?.hasAbility(Abilities.GULP_MISSILE)).toBe(false); }); }); diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 63e54dfb157..4434dc62a6b 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -38,7 +38,7 @@ describe("Abilities - Hustle", () => { it("increases the user's Attack stat by 50%", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; const atk = pikachu.stats[Stat.ATK]; vi.spyOn(pikachu, "getBattleStat"); @@ -53,7 +53,7 @@ describe("Abilities - Hustle", () => { it("lowers the accuracy of the user's physical moves by 20%", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); @@ -65,7 +65,7 @@ describe("Abilities - Hustle", () => { it("does not affect non-physical moves", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; const spatk = pikachu.stats[Stat.SPATK]; vi.spyOn(pikachu, "getBattleStat"); @@ -83,8 +83,8 @@ describe("Abilities - Hustle", () => { game.override.enemyLevel(30); await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); diff --git a/src/test/abilities/ice_face.test.ts b/src/test/abilities/ice_face.test.ts index a08a6a01b60..cdf8d5928ee 100644 --- a/src/test/abilities/ice_face.test.ts +++ b/src/test/abilities/ice_face.test.ts @@ -40,11 +40,11 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("takes no damage from the first hit of multihit physical move and transforms to Noice", async () => { @@ -54,7 +54,7 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SURGING_STRIKES)); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); // First hit @@ -82,7 +82,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -96,7 +96,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -112,15 +112,15 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(MoveEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.isFullHp()).toBe(true); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); await game.phaseInterceptor.to(MoveEndPhase); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBeNull(); expect(eiscue.formIndex).toBe(icefaceForm); }); @@ -133,9 +133,9 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SNOWSCAPE)); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon(); + let eiscue = game.scene.getPlayerPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.isFullHp()).toBe(true); @@ -145,7 +145,7 @@ describe("Abilities - Ice Face", () => { game.doSwitchPokemon(1); await game.phaseInterceptor.to(QuietFormChangePhase); - eiscue = game.scene.getPlayerPokemon(); + eiscue = game.scene.getPlayerPokemon()!; expect(eiscue.formIndex).toBe(icefaceForm); expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); @@ -158,17 +158,17 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); game.doAttack(getMovePosition(game.scene, 0, Moves.HAIL)); - const eiscue = game.scene.getPlayerPokemon(); + const eiscue = game.scene.getPlayerPokemon()!; await game.phaseInterceptor.to(QuietFormChangePhase); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); await game.phaseInterceptor.to(TurnEndPhase); expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("persists form change when switched out", async () => { @@ -179,9 +179,9 @@ describe("Abilities - Ice Face", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); await game.phaseInterceptor.to(TurnEndPhase); - let eiscue = game.scene.getPlayerPokemon(); + let eiscue = game.scene.getPlayerPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); expect(eiscue.formIndex).toBe(noiceForm); expect(eiscue.isFullHp()).toBe(true); @@ -192,7 +192,7 @@ describe("Abilities - Ice Face", () => { eiscue = game.scene.getParty()[1]; expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); }); it("reverts to Ice Face on arena reset", async () => { @@ -205,10 +205,10 @@ describe("Abilities - Ice Face", () => { await game.startBattle([Species.EISCUE]); - const eiscue = game.scene.getPlayerPokemon(); + const eiscue = game.scene.getPlayerPokemon()!; expect(eiscue.formIndex).toBe(noiceForm); - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeUndefined(); game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); await game.doKillOpponents(); @@ -229,7 +229,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -245,7 +245,7 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnEndPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); @@ -261,10 +261,10 @@ describe("Abilities - Ice Face", () => { await game.phaseInterceptor.to(TurnInitPhase); - const eiscue = game.scene.getEnemyPokemon(); + const eiscue = game.scene.getEnemyPokemon()!; expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); expect(eiscue.formIndex).toBe(icefaceForm); - expect(game.scene.getPlayerPokemon().hasAbility(Abilities.TRACE)).toBe(true); + expect(game.scene.getPlayerPokemon()!.hasAbility(Abilities.TRACE)).toBe(true); }); }); diff --git a/src/test/abilities/libero.test.ts b/src/test/abilities/libero.test.ts index c38ec082169..e4d99d5b56c 100644 --- a/src/test/abilities/libero.test.ts +++ b/src/test/abilities/libero.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -64,7 +64,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]); - let leadPokemon = game.scene.getPlayerPokemon(); + let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -86,7 +86,7 @@ describe("Abilities - Protean", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - leadPokemon = game.scene.getPlayerPokemon(); + leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -104,7 +104,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); @@ -128,7 +128,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -150,7 +150,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; @@ -169,7 +169,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); @@ -188,7 +188,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -196,7 +196,7 @@ describe("Abilities - Protean", () => { vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); await game.phaseInterceptor.to(TurnEndPhase); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, @@ -211,7 +211,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -230,7 +230,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -248,7 +248,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; @@ -267,7 +267,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); @@ -287,7 +287,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -305,7 +305,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -324,7 +324,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); @@ -342,7 +342,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); diff --git a/src/test/abilities/magic_guard.test.ts b/src/test/abilities/magic_guard.test.ts index 18e28284f72..23b3bad828f 100644 --- a/src/test/abilities/magic_guard.test.ts +++ b/src/test/abilities/magic_guard.test.ts @@ -53,9 +53,10 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; + expect(enemyPokemon).toBeDefined(); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -79,7 +80,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -91,7 +92,7 @@ describe("Abilities - Magic Guard", () => { * - The Pokemon's CatchRateMultiplier should be 1.5 */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(leadPokemon.status.effect)).toBe(1.5); + expect(getStatusEffectCatchRateMultiplier(leadPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); @@ -103,7 +104,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -127,7 +128,7 @@ describe("Abilities - Magic Guard", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -138,7 +139,7 @@ describe("Abilities - Magic Guard", () => { * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 */ expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status.effect)).toBe(1.5); + expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); @@ -151,9 +152,9 @@ describe("Abilities - Magic Guard", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; - const toxicStartCounter = enemyPokemon.status.turnCount; + const toxicStartCounter = enemyPokemon.status!.turnCount; //should be 0 await game.phaseInterceptor.to(TurnEndPhase); @@ -165,23 +166,23 @@ describe("Abilities - Magic Guard", () => { * - The enemy Pokemon's hypothetical CatchRateMultiplier should be 1.5 */ expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); - expect(enemyPokemon.status.turnCount).toBeGreaterThan(toxicStartCounter); - expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status.effect)).toBe(1.5); + expect(enemyPokemon.status!.turnCount).toBeGreaterThan(toxicStartCounter); + expect(getStatusEffectCatchRateMultiplier(enemyPokemon.status!.effect)).toBe(1.5); }, TIMEOUT ); it("Magic Guard prevents damage caused by entry hazards", async () => { //Adds and applies Spikes to both sides of the arena - const newTag = getArenaTag(ArenaTagType.SPIKES, 5, Moves.SPIKES, 0, 0, ArenaTagSide.BOTH); + const newTag = getArenaTag(ArenaTagType.SPIKES, 5, Moves.SPIKES, 0, 0, ArenaTagSide.BOTH)!; game.scene.arena.tags.push(newTag); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -197,17 +198,17 @@ describe("Abilities - Magic Guard", () => { it("Magic Guard does not prevent poison from Toxic Spikes", async () => { //Adds and applies Spikes to both sides of the arena - const playerTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.PLAYER); - const enemyTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.ENEMY); + const playerTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.PLAYER)!; + const enemyTag = getArenaTag(ArenaTagType.TOXIC_SPIKES, 5, Moves.TOXIC_SPIKES, 0, 0, ArenaTagSide.ENEMY)!; game.scene.arena.tags.push(playerTag); game.scene.arena.tags.push(enemyTag); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -217,8 +218,8 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon (with Magic Guard) has not taken damage from poison * - The enemy Pokemon (without Magic Guard) has taken damage from poison */ - expect(leadPokemon.status.effect).toBe(StatusEffect.POISON); - expect(enemyPokemon.status.effect).toBe(StatusEffect.POISON); + expect(leadPokemon.status!.effect).toBe(StatusEffect.POISON); + expect(enemyPokemon.status!.effect).toBe(StatusEffect.POISON); expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); }, TIMEOUT @@ -230,11 +231,11 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.CURSE]); game.override.enemyAbility(Abilities.MAGIC_GUARD); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; await game.phaseInterceptor.to(TurnEndPhase); @@ -254,7 +255,7 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.HIGH_JUMP_KICK]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HIGH_JUMP_KICK)); await game.phaseInterceptor.to(MoveEffectPhase, false); @@ -274,7 +275,7 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.TAKE_DOWN]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.TAKE_DOWN)); @@ -292,7 +293,7 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.STRUGGLE]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -311,7 +312,7 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.STEEL_BEAM]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.STEEL_BEAM)); @@ -339,7 +340,7 @@ describe("Abilities - Magic Guard", () => { game.override.moveset([Moves.BELLY_DRUM]); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); @@ -362,7 +363,7 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -374,7 +375,7 @@ describe("Abilities - Magic Guard", () => { * - The player Pokemon is asleep */ expect(leadPokemon.hp).toBe(leadPokemon.getMaxHp()); - expect(leadPokemon.status.effect).toBe(StatusEffect.SLEEP); + expect(leadPokemon.status!.effect).toBe(StatusEffect.SLEEP); }, TIMEOUT ); @@ -385,9 +386,9 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.hp = 1; game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -410,9 +411,9 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); await game.phaseInterceptor.to(TurnEndPhase); @@ -434,9 +435,9 @@ describe("Abilities - Magic Guard", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ABSORB)); await game.phaseInterceptor.to(TurnEndPhase); @@ -457,7 +458,7 @@ describe("Abilities - Magic Guard", () => { game.override.weather(WeatherType.SUNNY); await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/parental_bond.test.ts b/src/test/abilities/parental_bond.test.ts index 45d65fca083..1fb27ad7e1f 100644 --- a/src/test/abilities/parental_bond.test.ts +++ b/src/test/abilities/parental_bond.test.ts @@ -47,10 +47,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); let enemyStartingHp = enemyPokemon.hp; @@ -80,10 +80,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.POWER_UP_PUNCH)); @@ -102,10 +102,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BABY_DOLL_EYES)); @@ -122,10 +122,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DOUBLE_HIT)); @@ -147,10 +147,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SELF_DESTRUCT)); @@ -168,10 +168,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ROLLOUT)); @@ -192,10 +192,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; @@ -215,10 +215,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const playerStartingHp = leadPokemon.hp; @@ -268,10 +268,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.EARTHQUAKE)); @@ -288,10 +288,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.PIDGEOT]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.MIND_BLOWN)); @@ -314,10 +314,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -342,10 +342,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -364,10 +364,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; @@ -395,10 +395,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; @@ -425,10 +425,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); @@ -455,10 +455,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.ANCHOR_SHOT)); @@ -487,10 +487,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SMACK_DOWN)); @@ -516,10 +516,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); @@ -542,10 +542,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WAKE_UP_SLAP)); @@ -572,10 +572,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -594,10 +594,10 @@ describe("Abilities - Parental Bond", () => { await game.startBattle([Species.CHARIZARD]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.WATER_GUN)); diff --git a/src/test/abilities/pastel_veil.test.ts b/src/test/abilities/pastel_veil.test.ts index 8035d54a7e5..e3d52a720b3 100644 --- a/src/test/abilities/pastel_veil.test.ts +++ b/src/test/abilities/pastel_veil.test.ts @@ -51,7 +51,7 @@ describe("Abilities - Pastel Veil", () => { it("it heals the poisoned status condition of allies if user is sent out into battle", async () => { await game.startBattle([Species.MAGIKARP, Species.MAGIKARP, Species.GALAR_PONYTA]); - const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA); + const ponyta = game.scene.getParty().find(p => p.species.speciesId === Species.GALAR_PONYTA)!; vi.spyOn(ponyta, "getAbility").mockReturnValue(allAbilities[Abilities.PASTEL_VEIL]); @@ -66,7 +66,7 @@ describe("Abilities - Pastel Veil", () => { const poisonedMon = game.scene.getPlayerField().find(p => p.status?.effect === StatusEffect.POISON); await game.phaseInterceptor.to(CommandPhase); - game.doAttack(getMovePosition(game.scene, (poisonedMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); + game.doAttack(getMovePosition(game.scene, (poisonedMon!.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); game.doSwitchPokemon(2); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/src/test/abilities/power_construct.test.ts b/src/test/abilities/power_construct.test.ts index 769499c0e53..dd8fd836e51 100644 --- a/src/test/abilities/power_construct.test.ts +++ b/src/test/abilities/power_construct.test.ts @@ -47,11 +47,11 @@ describe("Abilities - POWER CONSTRUCT", () => { const zygarde = game.scene.getParty().find((p) => p.species.speciesId === Species.ZYGARDE); expect(zygarde).not.toBe(undefined); - expect(zygarde.formIndex).toBe(completeForm); + expect(zygarde!.formIndex).toBe(completeForm); - zygarde.hp = 0; - zygarde.status = new Status(StatusEffect.FAINT); - expect(zygarde.isFainted()).toBe(true); + zygarde!.hp = 0; + zygarde!.status = new Status(StatusEffect.FAINT); + expect(zygarde!.isFainted()).toBe(true); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.doKillOpponents(); @@ -59,7 +59,7 @@ describe("Abilities - POWER CONSTRUCT", () => { game.doSelectModifier(); await game.phaseInterceptor.to(QuietFormChangePhase); - expect(zygarde.formIndex).toBe(baseForm); + expect(zygarde!.formIndex).toBe(baseForm); }, TIMEOUT ); diff --git a/src/test/abilities/protean.test.ts b/src/test/abilities/protean.test.ts index 7c6cca2eccd..63d1a0ea719 100644 --- a/src/test/abilities/protean.test.ts +++ b/src/test/abilities/protean.test.ts @@ -46,7 +46,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -64,7 +64,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR]); - let leadPokemon = game.scene.getPlayerPokemon(); + let leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -86,7 +86,7 @@ describe("Abilities - Protean", () => { game.doSwitchPokemon(1); await game.toNextTurn(); - leadPokemon = game.scene.getPlayerPokemon(); + leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -104,7 +104,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.weather = new Weather(WeatherType.SUNNY); @@ -128,7 +128,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -150,7 +150,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.scene.arena.biomeType = Biome.MOUNTAIN; @@ -169,7 +169,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.DIG)); @@ -188,7 +188,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -196,7 +196,7 @@ describe("Abilities - Protean", () => { vi.spyOn(game.scene.getCurrentPhase() as MoveEffectPhase, "hitCheck").mockReturnValueOnce(false); await game.phaseInterceptor.to(TurnEndPhase); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon.isFullHp()).toBe(true); testPokemonTypeMatchesDefaultMoveType(leadPokemon, Moves.TACKLE); }, @@ -211,7 +211,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -230,7 +230,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TACKLE)); @@ -248,7 +248,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); leadPokemon.summonData.types = [allMoves[Moves.SPLASH].defaultType]; @@ -267,7 +267,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); vi.spyOn(leadPokemon, "isTerastallized").mockReturnValue(true); @@ -287,7 +287,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.STRUGGLE)); @@ -305,7 +305,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.BURN_UP)); @@ -324,7 +324,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.TRICK_OR_TREAT)); @@ -342,7 +342,7 @@ describe("Abilities - Protean", () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); game.doAttack(getMovePosition(game.scene, 0, Moves.CURSE)); diff --git a/src/test/abilities/quick_draw.test.ts b/src/test/abilities/quick_draw.test.ts index 56f21cab838..75bb9ec6a0a 100644 --- a/src/test/abilities/quick_draw.test.ts +++ b/src/test/abilities/quick_draw.test.ts @@ -41,8 +41,8 @@ describe("Abilities - Quick Draw", () => { test("makes pokemon going first in its priority bracket", async () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; @@ -61,8 +61,8 @@ describe("Abilities - Quick Draw", () => { }, async () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; @@ -81,8 +81,8 @@ describe("Abilities - Quick Draw", () => { await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; pokemon.hp = 1; enemy.hp = 1; diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts index dacfe7ad27e..d3d1a8e0028 100644 --- a/src/test/abilities/sand_spit.test.ts +++ b/src/test/abilities/sand_spit.test.ts @@ -42,7 +42,7 @@ describe("Ability Timing", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.toNextTurn(); - expect(game.scene.arena.weather.weatherType).toBe(WeatherType.SANDSTORM); + expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); }, 20000); it("should not trigger when targetted with status moves", async() => { diff --git a/src/test/abilities/sap_sipper.test.ts b/src/test/abilities/sap_sipper.test.ts index 20a677a3244..6fbe57978e9 100644 --- a/src/test/abilities/sap_sipper.test.ts +++ b/src/test/abilities/sap_sipper.test.ts @@ -87,7 +87,7 @@ describe("Abilities - Sap Sipper", () => { await game.phaseInterceptor.to(TurnEndPhase); expect(game.scene.arena.terrain).toBeDefined(); - expect(game.scene.arena.terrain.terrainType).toBe(TerrainType.GRASSY); + expect(game.scene.arena.terrain!.terrainType).toBe(TerrainType.GRASSY); expect(game.scene.getEnemyParty()[0].summonData.battleStats[BattleStat.ATK]).toBe(0); }); diff --git a/src/test/abilities/schooling.test.ts b/src/test/abilities/schooling.test.ts index 7671534a549..e55b7795006 100644 --- a/src/test/abilities/schooling.test.ts +++ b/src/test/abilities/schooling.test.ts @@ -45,7 +45,7 @@ describe("Abilities - SCHOOLING", () => { await game.startBattle([Species.MAGIKARP, Species.WISHIWASHI]); - const wishiwashi = game.scene.getParty().find((p) => p.species.speciesId === Species.WISHIWASHI); + const wishiwashi = game.scene.getParty().find((p) => p.species.speciesId === Species.WISHIWASHI)!; expect(wishiwashi).not.toBe(undefined); expect(wishiwashi.formIndex).toBe(schoolForm); diff --git a/src/test/abilities/serene_grace.test.ts b/src/test/abilities/serene_grace.test.ts index 3e8ff0bbea0..c486604e4f9 100644 --- a/src/test/abilities/serene_grace.test.ts +++ b/src/test/abilities/serene_grace.test.ts @@ -66,8 +66,8 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - console.log(move.chance + " Their ability is " + phase.getUserPokemon().getAbility().name); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); + console.log(move.chance + " Their ability is " + phase.getUserPokemon()!.getAbility().name); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); expect(chance.value).toBe(30); }, 20000); @@ -99,7 +99,7 @@ describe("Abilities - Serene Grace", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); expect(chance.value).toBe(60); }, 20000); diff --git a/src/test/abilities/sheer_force.test.ts b/src/test/abilities/sheer_force.test.ts index d3e1ae904cc..3f1bcadd43f 100644 --- a/src/test/abilities/sheer_force.test.ts +++ b/src/test/abilities/sheer_force.test.ts @@ -69,8 +69,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(0); expect(power.value).toBe(move.power * 5461/4096); @@ -108,8 +108,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -147,8 +147,8 @@ describe("Abilities - Sheer Force", () => { const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon(), phase.getTarget(), move, power); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreAttackAbAttrs(MovePowerBoostAbAttr, phase.getUserPokemon()!, phase.getTarget()!, move, power); expect(chance.value).toBe(-1); expect(power.value).toBe(move.power); @@ -187,8 +187,8 @@ describe("Abilities - Sheer Force", () => { //Disable color change due to being hit by Sheer Force const power = new Utils.IntegerHolder(move.power); const chance = new Utils.IntegerHolder(move.chance); - const user = phase.getUserPokemon(); - const target = phase.getTarget(); + const user = phase.getUserPokemon()!; + const target = phase.getTarget()!; const opponentType = target.getTypes()[0]; applyAbAttrs(MoveEffectChanceMultiplierAbAttr, user, null, chance, move, target, false); diff --git a/src/test/abilities/shield_dust.test.ts b/src/test/abilities/shield_dust.test.ts index f6906cb7b85..c7e62b333d7 100644 --- a/src/test/abilities/shield_dust.test.ts +++ b/src/test/abilities/shield_dust.test.ts @@ -67,8 +67,8 @@ describe("Abilities - Shield Dust", () => { expect(move.id).toBe(Moves.AIR_SLASH); const chance = new Utils.IntegerHolder(move.chance); - applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon(), null, chance, move, phase.getTarget(), false); - applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget(),phase.getUserPokemon(),null,null, chance); + applyAbAttrs(MoveEffectChanceMultiplierAbAttr, phase.getUserPokemon()!, null, chance, move, phase.getTarget(), false); + applyPreDefendAbAttrs(IgnoreMoveEffectsAbAttr, phase.getTarget()!, phase.getUserPokemon()!, null!, null!, chance); expect(chance.value).toBe(0); }, 20000); diff --git a/src/test/abilities/shields_down.test.ts b/src/test/abilities/shields_down.test.ts index 64904c80032..4d85e8aa47c 100644 --- a/src/test/abilities/shields_down.test.ts +++ b/src/test/abilities/shields_down.test.ts @@ -45,7 +45,7 @@ describe("Abilities - SHIELDS DOWN", () => { await game.startBattle([Species.MAGIKARP, Species.MINIOR]); - const minior = game.scene.getParty().find((p) => p.species.speciesId === Species.MINIOR); + const minior = game.scene.getParty().find((p) => p.species.speciesId === Species.MINIOR)!; expect(minior).not.toBe(undefined); expect(minior.formIndex).toBe(coreForm); diff --git a/src/test/abilities/steely_spirit.test.ts b/src/test/abilities/steely_spirit.test.ts index e21067539df..5d5514bc3a1 100644 --- a/src/test/abilities/steely_spirit.test.ts +++ b/src/test/abilities/steely_spirit.test.ts @@ -40,7 +40,7 @@ describe("Abilities - Steely Spirit", () => { it("increases Steel-type moves' power used by the user and its allies by 50%", async () => { await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); @@ -57,7 +57,7 @@ describe("Abilities - Steely Spirit", () => { it("stacks if multiple users with this ability are on the field.", async () => { await game.startBattle([Species.PIKACHU, Species.PIKACHU]); - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; game.scene.getPlayerField().forEach(p => { vi.spyOn(p, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); @@ -79,7 +79,7 @@ describe("Abilities - Steely Spirit", () => { it("does not take effect when suppressed", async () => { await game.startBattle([Species.PIKACHU, Species.SHUCKLE]); const boostSource = game.scene.getPlayerField()[1]; - const enemyToCheck = game.scene.getEnemyPokemon(); + const enemyToCheck = game.scene.getEnemyPokemon()!; vi.spyOn(boostSource, "getAbility").mockReturnValue(allAbilities[Abilities.STEELY_SPIRIT]); expect(boostSource.hasAbility(Abilities.STEELY_SPIRIT)).toBe(true); diff --git a/src/test/abilities/sweet_veil.test.ts b/src/test/abilities/sweet_veil.test.ts index 19c1210b443..03e9452a358 100644 --- a/src/test/abilities/sweet_veil.test.ts +++ b/src/test/abilities/sweet_veil.test.ts @@ -92,7 +92,7 @@ describe("Abilities - Sweet Veil", () => { await game.phaseInterceptor.to(TurnEndPhase); - const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY)); + const drowsyMon = game.scene.getPlayerField().find(p => !!p.getTag(BattlerTagType.DROWSY))!; await game.phaseInterceptor.to(CommandPhase); game.doAttack(getMovePosition(game.scene, (drowsyMon.getBattlerIndex() as BattlerIndex.PLAYER | BattlerIndex.PLAYER_2), Moves.SPLASH)); diff --git a/src/test/abilities/unseen_fist.test.ts b/src/test/abilities/unseen_fist.test.ts index 0ee4dbef513..a6cad8b03ce 100644 --- a/src/test/abilities/unseen_fist.test.ts +++ b/src/test/abilities/unseen_fist.test.ts @@ -72,10 +72,10 @@ async function testUnseenFistHitResult(game: GameManager, attackMove: Moves, pro await game.startBattle(); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; expect(leadPokemon).not.toBe(undefined); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; expect(enemyPokemon).not.toBe(undefined); const enemyStartingHp = enemyPokemon.hp; diff --git a/src/test/abilities/wind_power.test.ts b/src/test/abilities/wind_power.test.ts index 8a4c2969cc4..670544a89ef 100644 --- a/src/test/abilities/wind_power.test.ts +++ b/src/test/abilities/wind_power.test.ts @@ -34,7 +34,7 @@ describe("Abilities - Wind Power", () => { it("it becomes charged when hit by wind moves", async () => { await game.startBattle([Species.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon(); + const shiftry = game.scene.getEnemyPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -49,7 +49,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -64,8 +64,8 @@ describe("Abilities - Wind Power", () => { game.override.ability(Abilities.WIND_POWER); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); expect(magikarp.getTag(BattlerTagType.CHARGED)).toBeUndefined(); @@ -82,7 +82,7 @@ describe("Abilities - Wind Power", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.getTag(BattlerTagType.CHARGED)).toBeUndefined(); diff --git a/src/test/abilities/wind_rider.test.ts b/src/test/abilities/wind_rider.test.ts index 855def63b6f..e27349efe41 100644 --- a/src/test/abilities/wind_rider.test.ts +++ b/src/test/abilities/wind_rider.test.ts @@ -34,7 +34,7 @@ describe("Abilities - Wind Rider", () => { it("takes no damage from wind moves and its Attack is increased by one stage when hit by one", async () => { await game.startBattle([Species.MAGIKARP]); - const shiftry = game.scene.getEnemyPokemon(); + const shiftry = game.scene.getEnemyPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -51,7 +51,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -67,8 +67,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -85,8 +85,8 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const magikarp = game.scene.getEnemyPokemon(); - const shiftry = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon()!; + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(magikarp.summonData.battleStats[BattleStat.ATK]).toBe(0); @@ -103,7 +103,7 @@ describe("Abilities - Wind Rider", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.SHIFTRY]); - const shiftry = game.scene.getPlayerPokemon(); + const shiftry = game.scene.getPlayerPokemon()!; expect(shiftry.summonData.battleStats[BattleStat.ATK]).toBe(0); expect(shiftry.isFullHp()).toBe(true); diff --git a/src/test/abilities/zen_mode.test.ts b/src/test/abilities/zen_mode.test.ts index 01d3724825c..63c6a3d4acf 100644 --- a/src/test/abilities/zen_mode.test.ts +++ b/src/test/abilities/zen_mode.test.ts @@ -150,7 +150,7 @@ describe("Abilities - ZEN MODE", () => { await game.startBattle([Species.MAGIKARP, Species.DARMANITAN]); - const darmanitan = game.scene.getParty().find((p) => p.species.speciesId === Species.DARMANITAN); + const darmanitan = game.scene.getParty().find((p) => p.species.speciesId === Species.DARMANITAN)!; expect(darmanitan).not.toBe(undefined); expect(darmanitan.formIndex).toBe(zenForm); diff --git a/src/test/abilities/zero_to_hero.test.ts b/src/test/abilities/zero_to_hero.test.ts index 409fb35a78c..c58761ce621 100644 --- a/src/test/abilities/zero_to_hero.test.ts +++ b/src/test/abilities/zero_to_hero.test.ts @@ -45,7 +45,7 @@ describe("Abilities - ZERO TO HERO", () => { await game.startBattle([Species.MAGIKARP, Species.PALAFIN]); - const palafin = game.scene.getParty().find((p) => p.species.speciesId === Species.PALAFIN); + const palafin = game.scene.getParty().find((p) => p.species.speciesId === Species.PALAFIN)!; expect(palafin).not.toBe(undefined); expect(palafin.formIndex).toBe(heroForm); diff --git a/src/test/account.spec.ts b/src/test/account.spec.ts index 28e48ce9933..d5d0458c7e8 100644 --- a/src/test/account.spec.ts +++ b/src/test/account.spec.ts @@ -8,21 +8,21 @@ describe("account", () => { it("should set loggedInUser to Guest and lastSessionSlot to -1", () => { initLoggedInUser(); - expect(loggedInUser.username).toBe("Guest"); - expect(loggedInUser.lastSessionSlot).toBe(-1); + expect(loggedInUser!.username).toBe("Guest"); + expect(loggedInUser!.lastSessionSlot).toBe(-1); }); }); describe("updateUserInfo", () => { - it("should set loggedInUser to Guest if bypassLogin is true", async () => { + it("should set loggedInUser! to Guest if bypassLogin is true", async () => { vi.spyOn(battleScene, "bypassLogin", "get").mockReturnValue(true); const [success, status] = await updateUserInfo(); expect(success).toBe(true); expect(status).toBe(200); - expect(loggedInUser.username).toBe("Guest"); - expect(loggedInUser.lastSessionSlot).toBe(-1); + expect(loggedInUser!.username).toBe("Guest"); + expect(loggedInUser!.lastSessionSlot).toBe(-1); }); it("should fetch user info from the API if bypassLogin is false", async () => { @@ -43,8 +43,8 @@ describe("account", () => { expect(success).toBe(true); expect(status).toBe(200); - expect(loggedInUser.username).toBe("test"); - expect(loggedInUser.lastSessionSlot).toBe(99); + expect(loggedInUser!.username).toBe("test"); + expect(loggedInUser!.lastSessionSlot).toBe(99); }); it("should handle resolved API errors", async () => { diff --git a/src/test/achievements/achievement.test.ts b/src/test/achievements/achievement.test.ts index 14466a71086..5cd9c4d4094 100644 --- a/src/test/achievements/achievement.test.ts +++ b/src/test/achievements/achievement.test.ts @@ -3,12 +3,12 @@ import { Achv, AchvTier, DamageAchv, HealAchv, LevelAchv, ModifierAchv, MoneyAch import GameManager from "#test/utils/gameManager"; import { IntegerHolder, NumberHolder } from "#app/utils.js"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import BattleScene from "../../battle-scene"; describe("check some Achievement related stuff", () => { it ("should check Achievement creation", () => { - const ach = new MoneyAchv("", "Achievement", 1000, null, 100); + const ach = new MoneyAchv("", "Achievement", 1000, null!, 100); expect(ach.name).toBe("Achievement"); }); }); @@ -58,7 +58,7 @@ describe("Achv", () => { }); it("should validate the achievement based on the condition function", () => { - const conditionFunc = jest.fn((scene: BattleScene, args: any[]) => args[0] === 10); + const conditionFunc = vi.fn((scene: BattleScene, args: any[]) => args[0] === 10); const achv = new Achv("", "Test Achievement", "Test Description", "test_icon", 10, conditionFunc); expect(achv.validate(new BattleScene(), [5])).toBe(false); @@ -196,7 +196,7 @@ describe("ModifierAchv", () => { it("should validate the achievement based on the modifier function", () => { const modifierAchv = new ModifierAchv("", "Test Modifier Achievement", "Test Description", "modifier_icon", 10, () => true); const scene = new BattleScene(); - const modifier = new TurnHeldItemTransferModifier(null, 3, 1); + const modifier = new TurnHeldItemTransferModifier(null!, 3, 1); expect(modifierAchv.validate(scene, [modifier])).toBe(true); }); diff --git a/src/test/arena/weather_strong_winds.test.ts b/src/test/arena/weather_strong_winds.test.ts index 0fd45173033..d9f626cfb83 100644 --- a/src/test/arena/weather_strong_winds.test.ts +++ b/src/test/arena/weather_strong_winds.test.ts @@ -35,8 +35,8 @@ describe("Weather - Strong Winds", () => { game.override.enemySpecies(Species.RAYQUAZA); await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); @@ -46,8 +46,8 @@ describe("Weather - Strong Winds", () => { it("electric type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THUNDERBOLT)); @@ -57,8 +57,8 @@ describe("Weather - Strong Winds", () => { it("ice type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ICE_BEAM)); @@ -68,8 +68,8 @@ describe("Weather - Strong Winds", () => { it("rock type move is neutral for flying type pokemon", async () => { await game.startBattle([Species.PIKACHU]); - const pikachu = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const pikachu = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ROCK_SLIDE)); diff --git a/src/test/battle/battle.test.ts b/src/test/battle/battle.test.ts index 21b890d9cf0..a4713f90506 100644 --- a/src/test/battle/battle.test.ts +++ b/src/test/battle/battle.test.ts @@ -90,7 +90,7 @@ describe("Test Battle Phase", () => { it("newGame one-liner", async() => { await game.startBattle(); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("do attack wave 3 - single battle - regular - OHKO", async() => { @@ -218,7 +218,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("1vs1", async() => { @@ -230,7 +230,7 @@ describe("Test Battle Phase", () => { Species.BLASTOISE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("2vs2", async() => { @@ -244,7 +244,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("4vs2", async() => { @@ -260,7 +260,7 @@ describe("Test Battle Phase", () => { Species.GABITE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("kill opponent pokemon", async() => { @@ -342,7 +342,7 @@ describe("Test Battle Phase", () => { .startingHeldItems([{ name: "TEMP_STAT_BOOSTER", type: TempBattleStat.ACC }]); await game.startBattle(); - game.scene.getPlayerPokemon().hp = 1; + game.scene.getPlayerPokemon()!.hp = 1; game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(BattleEndPhase); diff --git a/src/test/battle/special_battle.test.ts b/src/test/battle/special_battle.test.ts index b8a29e82f69..6130df703f5 100644 --- a/src/test/battle/special_battle.test.ts +++ b/src/test/battle/special_battle.test.ts @@ -40,7 +40,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 boss", async() => { @@ -52,7 +52,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async() => { @@ -64,7 +64,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 trainer", async() => { @@ -76,7 +76,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs1 rival", async() => { @@ -88,7 +88,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 rival", async() => { @@ -100,7 +100,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 1vs1 trainer", async() => { @@ -111,7 +111,7 @@ describe("Test Battle Phase", () => { Species.BLASTOISE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 2vs2 trainer", async() => { @@ -123,7 +123,7 @@ describe("Test Battle Phase", () => { Species.CHARIZARD, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); it("startBattle 4vs2 trainer", async() => { @@ -137,7 +137,7 @@ describe("Test Battle Phase", () => { Species.GABITE, ]); expect(game.scene.ui?.getMode()).toBe(Mode.COMMAND); - expect(game.scene.getCurrentPhase().constructor.name).toBe(CommandPhase.name); + expect(game.scene.getCurrentPhase()!.constructor.name).toBe(CommandPhase.name); }, 20000); }); diff --git a/src/test/battlerTags/octolock.test.ts b/src/test/battlerTags/octolock.test.ts index 4c1c58d4345..369a84e21fa 100644 --- a/src/test/battlerTags/octolock.test.ts +++ b/src/test/battlerTags/octolock.test.ts @@ -6,7 +6,7 @@ import { StatChangePhase } from "#app/phases.js"; import { BattleStat } from "#app/data/battle-stat.js"; import { BattlerTagType } from "#app/enums/battler-tag-type.js"; -jest.mock("#app/battle-scene.js"); +vi.mock("#app/battle-scene.js"); describe("BattlerTag - OctolockTag", () => { describe("lapse behavior", () => { @@ -49,7 +49,7 @@ describe("BattlerTag - OctolockTag", () => { it("cannot be added to pokemon who are octolocked", { timeout: 2000 }, async => { const mockPokemon = { - getTag: vi.fn().mockReturnValue(new BattlerTag(null, null, null, null)) as Pokemon["getTag"], + getTag: vi.fn().mockReturnValue(new BattlerTag(null!, null!, null!, null!)) as Pokemon["getTag"], } as Pokemon; const subject = new OctolockTag(1); diff --git a/src/test/battlerTags/stockpiling.test.ts b/src/test/battlerTags/stockpiling.test.ts index 5cf07c47ce1..005f1e1593c 100644 --- a/src/test/battlerTags/stockpiling.test.ts +++ b/src/test/battlerTags/stockpiling.test.ts @@ -27,7 +27,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onAdd(mockPokemon); @@ -54,7 +54,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onAdd(mockPokemon); @@ -79,7 +79,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["levels"]).toEqual(1); expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.DEF, BattleStat.SPDEF], [1, 1]); }); subject.onOverlap(mockPokemon); @@ -109,7 +109,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); // def doesn't change - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); }); subject.onAdd(mockPokemon); @@ -121,7 +121,7 @@ describe("BattlerTag - StockpilingTag", () => { expect((phase as StatChangePhase)["stats"]).toEqual(expect.arrayContaining([BattleStat.DEF, BattleStat.SPDEF])); // def doesn't change - (phase as StatChangePhase)["onChange"](mockPokemon, [BattleStat.SPDEF], [1]); + (phase as StatChangePhase)["onChange"]!(mockPokemon, [BattleStat.SPDEF], [1]); }); subject.onOverlap(mockPokemon); diff --git a/src/test/evolution.test.ts b/src/test/evolution.test.ts index acb012741ff..c8ebffa6119 100644 --- a/src/test/evolution.test.ts +++ b/src/test/evolution.test.ts @@ -64,7 +64,7 @@ describe("Evolution", () => { it("should handle illegal abilityIndex values", async () => { await game.runToSummon([Species.SQUIRTLE]); - const squirtle = game.scene.getPlayerPokemon(); + const squirtle = game.scene.getPlayerPokemon()!; squirtle.abilityIndex = 5; squirtle.evolve(pokemonEvolutions[Species.SQUIRTLE][0], squirtle.getSpeciesForm()); @@ -74,7 +74,7 @@ describe("Evolution", () => { it("should handle nincada's unique evolution", async () => { await game.runToSummon([Species.NINCADA]); - const nincada = game.scene.getPlayerPokemon(); + const nincada = game.scene.getPlayerPokemon()!; nincada.abilityIndex = 2; nincada.evolve(pokemonEvolutions[Species.NINCADA][0], nincada.getSpeciesForm()); diff --git a/src/test/evolutions/evolutions.test.ts b/src/test/evolutions/evolutions.test.ts index e6e9e0d56e2..af43e91b059 100644 --- a/src/test/evolutions/evolutions.test.ts +++ b/src/test/evolutions/evolutions.test.ts @@ -34,14 +34,14 @@ describe("Evolution tests", () => { * 1, 2 or 3, it's a 4 family maushold */ await game.startBattle([Species.TANDEMAUS]); // starts us off with a tandemaus - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.level = 25; // tandemaus evolves at level 25 vi.spyOn(Utils, "randSeedInt").mockReturnValue(0); // setting the random generator to be 0 to force a three family maushold - const threeForm = playerPokemon.getEvolution(); + const threeForm = playerPokemon.getEvolution()!; expect(threeForm.evoFormKey).toBe("three"); // as per pokemon-forms, the evoFormKey for 3 family mausholds is "three" for (let f = 1; f < 4; f++) { vi.spyOn(Utils, "randSeedInt").mockReturnValue(f); // setting the random generator to 1, 2 and 3 to force 4 family mausholds - const fourForm = playerPokemon.getEvolution(); + const fourForm = playerPokemon.getEvolution()!; expect(fourForm.evoFormKey).toBe(null); // meanwhile, according to the pokemon-forms, the evoFormKey for a 4 family maushold is null } }, 5000); diff --git a/src/test/field/pokemon.test.ts b/src/test/field/pokemon.test.ts new file mode 100644 index 00000000000..2220a6f613e --- /dev/null +++ b/src/test/field/pokemon.test.ts @@ -0,0 +1,31 @@ +import { Species } from "#app/enums/species.js"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "../utils/gameManager"; + +describe("Spec - Pokemon", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + it("should not crash when trying to set status of undefined", async () => { + await game.runToSummon([Species.ABRA]); + + const pkm = game.scene.getPlayerPokemon()!; + expect(pkm).toBeDefined(); + + expect(pkm.trySetStatus(undefined)).toBe(true); + }); +}); diff --git a/src/test/final_boss.test.ts b/src/test/final_boss.test.ts index bc950e45767..c087bd5ba82 100644 --- a/src/test/final_boss.test.ts +++ b/src/test/final_boss.test.ts @@ -35,7 +35,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).toBe(Species.ETERNATUS); }); it("should NOT spawn Eternatus before wave 200 in END biome", async () => { @@ -44,7 +44,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).not.toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).not.toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); }); it("should NOT spawn Eternatus outside of END biome", async () => { @@ -53,7 +53,7 @@ describe("Final Boss", () => { expect(game.scene.currentBattle.waveIndex).toBe(FinalWave.Classic); expect(game.scene.arena.biomeType).not.toBe(Biome.END); - expect(game.scene.getEnemyPokemon().species.speciesId).not.toBe(Species.ETERNATUS); + expect(game.scene.getEnemyPokemon()!.species.speciesId).not.toBe(Species.ETERNATUS); }); it.todo("should change form on direct hit down to last boss fragment", () => {}); diff --git a/src/test/internals.test.ts b/src/test/internals.test.ts index 2ada42d0c8c..d186849830d 100644 --- a/src/test/internals.test.ts +++ b/src/test/internals.test.ts @@ -24,7 +24,7 @@ describe("Internals", () => { it("should provide Eevee with 3 defined abilities", async () => { await game.runToSummon([Species.EEVEE]); - const eevee = game.scene.getPlayerPokemon(); + const eevee = game.scene.getPlayerPokemon()!; expect(eevee.getSpeciesForm().getAbilityCount()).toBe(3); @@ -35,7 +35,7 @@ describe("Internals", () => { it("should set Eeeve abilityIndex between 0-2", async () => { await game.runToSummon([Species.EEVEE]); - const eevee = game.scene.getPlayerPokemon(); + const eevee = game.scene.getPlayerPokemon()!; expect(eevee.abilityIndex).toBeGreaterThanOrEqual(0); expect(eevee.abilityIndex).toBeLessThanOrEqual(2); diff --git a/src/test/items/exp_booster.test.ts b/src/test/items/exp_booster.test.ts index 7ea457eda1f..3348304b2e7 100644 --- a/src/test/items/exp_booster.test.ts +++ b/src/test/items/exp_booster.test.ts @@ -31,7 +31,7 @@ describe("EXP Modifier Items", () => { game.override.startingHeldItems([{name: "LUCKY_EGG"}, {name: "GOLDEN_EGG"}]); await game.startBattle(); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; const modifierBonusExp = new Utils.NumberHolder(1); partyMember.scene.applyModifiers(PokemonExpBoosterModifier, true, partyMember, modifierBonusExp); expect(modifierBonusExp.value).toBe(2.4); diff --git a/src/test/items/leek.test.ts b/src/test/items/leek.test.ts index 8532f0df8d9..2cfd4d8dc64 100644 --- a/src/test/items/leek.test.ts +++ b/src/test/items/leek.test.ts @@ -56,7 +56,7 @@ describe("Items - Leek", () => { Species.FARFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -76,7 +76,7 @@ describe("Items - Leek", () => { Species.GALAR_FARFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -96,7 +96,7 @@ describe("Items - Leek", () => { Species.SIRFETCHD ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); @@ -186,7 +186,7 @@ describe("Items - Leek", () => { Species.PIKACHU ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); diff --git a/src/test/items/leftovers.test.ts b/src/test/items/leftovers.test.ts index 6850528a664..e791c4426a1 100644 --- a/src/test/items/leftovers.test.ts +++ b/src/test/items/leftovers.test.ts @@ -40,7 +40,7 @@ describe("Items - Leftovers", () => { // Make sure leftovers are there expect(game.scene.modifiers[0].type.id).toBe("LEFTOVERS"); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; // We should have full hp expect(leadPokemon.isFullHp()).toBe(true); diff --git a/src/test/items/light_ball.test.ts b/src/test/items/light_ball.test.ts index fef5793d26c..ff7dfa4eba5 100644 --- a/src/test/items/light_ball.test.ts +++ b/src/test/items/light_ball.test.ts @@ -83,7 +83,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -122,7 +122,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -161,7 +161,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); @@ -189,7 +189,7 @@ describe("Items - Light Ball", () => { expect(spAtkValue.value / spAtkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["LIGHT_BALL"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["LIGHT_BALL"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPATK, spAtkValue); diff --git a/src/test/items/metal_powder.test.ts b/src/test/items/metal_powder.test.ts index da5d9109c9a..966762e4175 100644 --- a/src/test/items/metal_powder.test.ts +++ b/src/test/items/metal_powder.test.ts @@ -79,7 +79,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -112,7 +112,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -145,7 +145,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(2); @@ -167,7 +167,7 @@ describe("Items - Metal Powder", () => { expect(defValue.value / defStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["METAL_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["METAL_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.DEF, defValue); expect(defValue.value / defStat).toBe(1); diff --git a/src/test/items/quick_powder.test.ts b/src/test/items/quick_powder.test.ts index 939d25070bc..d2435dab431 100644 --- a/src/test/items/quick_powder.test.ts +++ b/src/test/items/quick_powder.test.ts @@ -79,7 +79,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -112,7 +112,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -145,7 +145,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(2); @@ -167,7 +167,7 @@ describe("Items - Quick Powder", () => { expect(spdValue.value / spdStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["QUICK_POWDER"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["QUICK_POWDER"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.SPD, spdValue); expect(spdValue.value / spdStat).toBe(1); diff --git a/src/test/items/scope_lens.test.ts b/src/test/items/scope_lens.test.ts index 72dc838548e..60f7aec171b 100644 --- a/src/test/items/scope_lens.test.ts +++ b/src/test/items/scope_lens.test.ts @@ -55,7 +55,7 @@ describe("Items - Scope Lens", () => { Species.GASTLY ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; // Making sure modifier is not applied without holding item const critLevel = new Utils.IntegerHolder(0); diff --git a/src/test/items/thick_club.test.ts b/src/test/items/thick_club.test.ts index 6b56cf98099..841cd7c90ac 100644 --- a/src/test/items/thick_club.test.ts +++ b/src/test/items/thick_club.test.ts @@ -79,7 +79,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -101,7 +101,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -123,7 +123,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -160,7 +160,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -197,7 +197,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(2); @@ -219,7 +219,7 @@ describe("Items - Thick Club", () => { expect(atkValue.value / atkStat).toBe(1); // Giving Eviolite to party member and testing if it applies - partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType(null, ["THICK_CLUB"]).newModifier(partyMember), true); + partyMember.scene.addModifier(modifierTypes.SPECIES_STAT_BOOSTER().generateType([], ["THICK_CLUB"])!.newModifier(partyMember), true); partyMember.scene.applyModifiers(SpeciesStatBoosterModifier, true, partyMember, Stat.ATK, atkValue); expect(atkValue.value / atkStat).toBe(1); diff --git a/src/test/items/toxic_orb.test.ts b/src/test/items/toxic_orb.test.ts index b5e9b6c4d65..69f55cb2bbc 100644 --- a/src/test/items/toxic_orb.test.ts +++ b/src/test/items/toxic_orb.test.ts @@ -72,6 +72,6 @@ describe("Items - Toxic orb", () => { const message2 = game.textInterceptor.getLatestMessage(); expect(message2).toContain("is hurt"); expect(message2).toContain("by poison"); - expect(game.scene.getParty()[0].status.effect).toBe(StatusEffect.TOXIC); + expect(game.scene.getParty()[0].status!.effect).toBe(StatusEffect.TOXIC); }, 20000); }); diff --git a/src/test/localization/terrain.test.ts b/src/test/localization/terrain.test.ts index f97b7813948..c072f9cc9ab 100644 --- a/src/test/localization/terrain.test.ts +++ b/src/test/localization/terrain.test.ts @@ -37,18 +37,18 @@ describe("terrain", () => { mockI18next(); const text = getTerrainStartMessage(terrainType); - expect(text).toBe(undefined); + expect(text).toBeNull(); }); it("should return the clear text", () => { mockI18next(); const text = getTerrainClearMessage(terrainType); - expect(text).toBe(undefined); + expect(text).toBeNull(); }); it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -80,7 +80,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:mistyBlockMessage"); @@ -112,7 +112,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -144,7 +144,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); @@ -176,7 +176,7 @@ describe("terrain", () => { it("should return the block text", async () => { await game.startBattle([Species.MAGIKARP]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; mockI18next(); const text = getTerrainBlockMessage(pokemon, terrainType); expect(text).toBe("terrain:defaultBlockMessage"); diff --git a/src/test/moves/astonish.test.ts b/src/test/moves/astonish.test.ts index c5d59f82102..358e4a9bec3 100644 --- a/src/test/moves/astonish.test.ts +++ b/src/test/moves/astonish.test.ts @@ -43,9 +43,9 @@ describe("Moves - Astonish", () => { async () => { await game.startBattle([Species.MEOWSCARADA]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.ASTONISH)); diff --git a/src/test/moves/aurora_veil.test.ts b/src/test/moves/aurora_veil.test.ts index f98e9e9cc0c..a10c9b6b60a 100644 --- a/src/test/moves/aurora_veil.test.ts +++ b/src/test/moves/aurora_veil.test.ts @@ -49,7 +49,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -64,7 +64,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -77,7 +77,7 @@ describe("Moves - Aurora Veil", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -92,7 +92,7 @@ describe("Moves - Aurora Veil", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); diff --git a/src/test/moves/baton_pass.test.ts b/src/test/moves/baton_pass.test.ts index ba961613998..9f0cb3619b2 100644 --- a/src/test/moves/baton_pass.test.ts +++ b/src/test/moves/baton_pass.test.ts @@ -45,7 +45,7 @@ describe("Moves - Baton Pass", () => { // round 1 - buff game.doAttack(getMovePosition(game.scene, 0, Moves.NASTY_PLOT)); await game.toNextTurn(); - expect(game.scene.getPlayerPokemon().summonData.battleStats[BattleStat.SPATK]).toEqual(2); + expect(game.scene.getPlayerPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); // round 2 - baton pass game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); @@ -53,8 +53,9 @@ describe("Moves - Baton Pass", () => { await game.phaseInterceptor.to(TurnEndPhase); // assert - expect(game.scene.getPlayerPokemon().species.speciesId).toEqual(Species.SHUCKLE); - expect(game.scene.getPlayerPokemon().summonData.battleStats[BattleStat.SPATK]).toEqual(2); + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.species.speciesId).toEqual(Species.SHUCKLE); + expect(playerPkm.summonData.battleStats[BattleStat.SPATK]).toEqual(2); }, 20000); it("passes stat stage buffs when AI uses it", async() => { @@ -72,17 +73,17 @@ describe("Moves - Baton Pass", () => { await game.toNextTurn(); // round 2 - baton pass - game.scene.getEnemyPokemon().hp = 100; + game.scene.getEnemyPokemon()!.hp = 100; game.override.enemyMoveset(new Array(4).fill(Moves.BATON_PASS)); game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); await game.phaseInterceptor.to(PostSummonPhase, false); // assert // check buffs are still there - expect(game.scene.getEnemyPokemon().summonData.battleStats[BattleStat.SPATK]).toEqual(2); + expect(game.scene.getEnemyPokemon()!.summonData.battleStats[BattleStat.SPATK]).toEqual(2); // confirm that a switch actually happened. can't use species because I // can't find a way to override trainer parties with more than 1 pokemon species - expect(game.scene.getEnemyPokemon().hp).not.toEqual(100); + expect(game.scene.getEnemyPokemon()!.hp).not.toEqual(100); expect(game.phaseInterceptor.log.slice(-4)).toEqual([ "MoveEffectPhase", "SwitchSummonPhase", diff --git a/src/test/moves/beat_up.test.ts b/src/test/moves/beat_up.test.ts index e720c625956..a5e4b3cbd34 100644 --- a/src/test/moves/beat_up.test.ts +++ b/src/test/moves/beat_up.test.ts @@ -42,8 +42,8 @@ describe("Moves - Beat Up", () => { async () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); @@ -66,7 +66,7 @@ describe("Moves - Beat Up", () => { async () => { await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; game.scene.getParty()[1].trySetStatus(StatusEffect.BURN); @@ -84,8 +84,8 @@ describe("Moves - Beat Up", () => { game.override.startingHeldItems([{name: "MULTI_LENS", count: 1}]); await game.startBattle([Species.MAGIKARP, Species.BULBASAUR, Species.CHARMANDER, Species.SQUIRTLE, Species.PIKACHU, Species.EEVEE]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; let enemyStartingHp = enemyPokemon.hp; game.doAttack(getMovePosition(game.scene, 0, Moves.BEAT_UP)); diff --git a/src/test/moves/belly_drum.test.ts b/src/test/moves/belly_drum.test.ts index 5bb27394805..74afc910faf 100644 --- a/src/test/moves/belly_drum.test.ts +++ b/src/test/moves/belly_drum.test.ts @@ -43,7 +43,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.BELLY_DRUM)); @@ -58,7 +58,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); // Here - BattleStat.ATK -> -3 and BattleStat.SPATK -> 6 @@ -78,7 +78,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; @@ -94,7 +94,7 @@ describe("Moves - BELLY DRUM", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/ceaseless_edge.test.ts b/src/test/moves/ceaseless_edge.test.ts index 288ea6ac277..c5ce8375102 100644 --- a/src/test/moves/ceaseless_edge.test.ts +++ b/src/test/moves/ceaseless_edge.test.ts @@ -45,7 +45,7 @@ describe("Moves - Ceaseless Edge", () => { async () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; @@ -70,7 +70,7 @@ describe("Moves - Ceaseless Edge", () => { game.override.startingHeldItems([{name: "MULTI_LENS"}]); await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; diff --git a/src/test/moves/clangorous_soul.test.ts b/src/test/moves/clangorous_soul.test.ts index 251225e7068..5493466ba56 100644 --- a/src/test/moves/clangorous_soul.test.ts +++ b/src/test/moves/clangorous_soul.test.ts @@ -44,7 +44,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.CLANGOROUS_SOUL)); @@ -63,7 +63,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPDEF -> 4 @@ -88,7 +88,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.DEF] = 6; @@ -112,7 +112,7 @@ describe("Moves - CLANGOROUS_SOUL", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/double_team.test.ts b/src/test/moves/double_team.test.ts index 48b322f5f11..2153b856517 100644 --- a/src/test/moves/double_team.test.ts +++ b/src/test/moves/double_team.test.ts @@ -36,8 +36,8 @@ describe("Moves - Double Team", () => { it("increases the user's evasion by one stage.", async () => { await game.startBattle([Species.MAGIKARP]); - const ally = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const ally = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getAccuracyMultiplier"); expect(ally.summonData.battleStats[BattleStat.EVA]).toBe(0); diff --git a/src/test/moves/dragon_rage.test.ts b/src/test/moves/dragon_rage.test.ts index 3c6e2b83baa..6ec7521f678 100644 --- a/src/test/moves/dragon_rage.test.ts +++ b/src/test/moves/dragon_rage.test.ts @@ -51,7 +51,7 @@ describe("Moves - Dragon Rage", () => { await game.startBattle(); partyPokemon = game.scene.getParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon(); + enemyPokemon = game.scene.getEnemyPokemon()!; // remove berries game.scene.removePartyMemberModifiers(0); diff --git a/src/test/moves/dynamax_cannon.test.ts b/src/test/moves/dynamax_cannon.test.ts index 8af6a624797..57846c1aef7 100644 --- a/src/test/moves/dynamax_cannon.test.ts +++ b/src/test/moves/dynamax_cannon.test.ts @@ -81,7 +81,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(120); }, 20000); @@ -98,7 +98,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(140); }, 20000); @@ -115,7 +115,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(160); }, 20000); @@ -132,7 +132,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(180); }, 20000); @@ -149,7 +149,7 @@ describe("Moves - Dynamax Cannon", () => { const phase = game.scene.getCurrentPhase() as MoveEffectPhase; expect(phase.move.moveId).toBe(dynamaxCannon.id); // Force level cap to be 100 - vi.spyOn(phase.getTarget().scene, "getMaxExpLevel").mockReturnValue(100); + vi.spyOn(phase.getTarget()!.scene, "getMaxExpLevel").mockReturnValue(100); await game.phaseInterceptor.to(DamagePhase, false); expect(dynamaxCannon.calculateBattlePower).toHaveLastReturnedWith(200); }, 20000); diff --git a/src/test/moves/fillet_away.test.ts b/src/test/moves/fillet_away.test.ts index a3922b18468..6965ced46d9 100644 --- a/src/test/moves/fillet_away.test.ts +++ b/src/test/moves/fillet_away.test.ts @@ -44,7 +44,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); game.doAttack(getMovePosition(game.scene, 0, Moves.FILLET_AWAY)); @@ -61,7 +61,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); //Here - BattleStat.SPD -> 0 and BattleStat.SPATK -> 3 @@ -82,7 +82,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; leadPokemon.summonData.battleStats[BattleStat.ATK] = 6; leadPokemon.summonData.battleStats[BattleStat.SPATK] = 6; @@ -102,7 +102,7 @@ describe("Moves - FILLET AWAY", () => { async() => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; const hpLost = Math.floor(leadPokemon.getMaxHp() / RATIO); leadPokemon.hp = hpLost - PREDAMAGE; diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index f2f58212209..979bc40646c 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -45,7 +45,7 @@ describe("Moves - Fissure", () => { await game.startBattle(); partyPokemon = game.scene.getParty()[0]; - enemyPokemon = game.scene.getEnemyPokemon(); + enemyPokemon = game.scene.getEnemyPokemon()!; // remove berries game.scene.removePartyMemberModifiers(0); diff --git a/src/test/moves/flower_shield.test.ts b/src/test/moves/flower_shield.test.ts index a00c84f8714..7ca5fb8bc62 100644 --- a/src/test/moves/flower_shield.test.ts +++ b/src/test/moves/flower_shield.test.ts @@ -39,8 +39,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(Species.CHERRIM); await game.startBattle([Species.MAGIKARP]); - const cherrim = game.scene.getEnemyPokemon(); - const magikarp = game.scene.getPlayerPokemon(); + const cherrim = game.scene.getEnemyPokemon()!; + const magikarp = game.scene.getPlayerPokemon()!; expect(magikarp.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -81,8 +81,8 @@ describe("Moves - Flower Shield", () => { game.override.enemyLevel(50); await game.startBattle([Species.CHERRIM]); - const paras = game.scene.getEnemyPokemon(); - const cherrim = game.scene.getPlayerPokemon(); + const paras = game.scene.getEnemyPokemon()!; + const cherrim = game.scene.getPlayerPokemon()!; expect(paras.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(cherrim.summonData.battleStats[BattleStat.DEF]).toBe(0); @@ -100,8 +100,8 @@ describe("Moves - Flower Shield", () => { game.override.enemySpecies(Species.MAGIKARP); await game.startBattle([Species.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon(); - const ally = game.scene.getPlayerPokemon(); + const enemy = game.scene.getEnemyPokemon()!; + const ally = game.scene.getPlayerPokemon()!; expect(enemy.summonData.battleStats[BattleStat.DEF]).toBe(0); expect(ally.summonData.battleStats[BattleStat.DEF]).toBe(0); diff --git a/src/test/moves/foresight.test.ts b/src/test/moves/foresight.test.ts index 4a2b4f8af03..5d847ca1bc4 100644 --- a/src/test/moves/foresight.test.ts +++ b/src/test/moves/foresight.test.ts @@ -35,7 +35,7 @@ describe("Internals", () => { it("should allow Normal and Fighting moves to hit Ghost types", async () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.QUICK_ATTACK)); await game.toNextTurn(); @@ -59,7 +59,7 @@ describe("Internals", () => { game.override.enemyMoveset(Array(4).fill(Moves.MINIMIZE)); await game.startBattle(); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getAccuracyMultiplier"); game.doAttack(getMovePosition(game.scene, 0, Moves.FORESIGHT)); diff --git a/src/test/moves/fusion_bolt.test.ts b/src/test/moves/fusion_bolt.test.ts index 3ba4148db9f..c7a21e2c736 100644 --- a/src/test/moves/fusion_bolt.test.ts +++ b/src/test/moves/fusion_bolt.test.ts @@ -41,7 +41,7 @@ describe("Moves - Fusion Bolt", () => { Species.ZEKROM, ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; const initialHp = partyMember.hp; game.doAttack(getMovePosition(game.scene, 0, fusionBolt)); diff --git a/src/test/moves/fusion_flare.test.ts b/src/test/moves/fusion_flare.test.ts index 9862995be32..9ae42e7977f 100644 --- a/src/test/moves/fusion_flare.test.ts +++ b/src/test/moves/fusion_flare.test.ts @@ -41,7 +41,7 @@ describe("Moves - Fusion Flare", () => { Species.RESHIRAM, ]); - const partyMember = game.scene.getPlayerPokemon(); + const partyMember = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, fusionFlare)); @@ -49,7 +49,7 @@ describe("Moves - Fusion Flare", () => { // Inflict freeze quietly and check if it was properly inflicted partyMember.trySetStatus(StatusEffect.FREEZE, false); - expect(partyMember.status.effect).toBe(StatusEffect.FREEZE); + expect(partyMember.status!.effect).toBe(StatusEffect.FREEZE); await game.toNextTurn(); diff --git a/src/test/moves/gastro_acid.test.ts b/src/test/moves/gastro_acid.test.ts index c6692d3442c..77cf35f39e2 100644 --- a/src/test/moves/gastro_acid.test.ts +++ b/src/test/moves/gastro_acid.test.ts @@ -83,6 +83,6 @@ describe("Moves - Gastro Acid", () => { await game.phaseInterceptor.to("TurnInitPhase"); - expect(game.scene.getPlayerPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); + expect(game.scene.getPlayerPokemon()!.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }, TIMEOUT); }); diff --git a/src/test/moves/glaive_rush.test.ts b/src/test/moves/glaive_rush.test.ts index fc9f6ee1c7f..ce63da6b565 100644 --- a/src/test/moves/glaive_rush.test.ts +++ b/src/test/moves/glaive_rush.test.ts @@ -38,7 +38,7 @@ describe("Moves - Glaive Rush", () => { it("takes double damage from attacks", async() => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; vi.spyOn(game.scene, "randBattleSeedInt").mockReturnValue(0); @@ -50,11 +50,11 @@ describe("Moves - Glaive Rush", () => { await game.phaseInterceptor.to(DamagePhase); expect(enemy.hp).toBeLessThanOrEqual(1001 - (damageDealt * 3)); - }, 20000); + }, 5000); // TODO: revert back to 20s it("always gets hit by attacks", async() => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.AVALANCHE].accuracy = 0; @@ -68,8 +68,8 @@ describe("Moves - Glaive Rush", () => { game.override.startingHeldItems([{name: "MULTI_LENS", count: 2}]); game.override.enemyMoveset(Array(4).fill(Moves.AVALANCHE)); await game.startBattle(); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; player.hp = 1000; @@ -87,8 +87,8 @@ describe("Moves - Glaive Rush", () => { it("secondary effects only last until next move", async() => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); await game.startBattle(); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; player.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; @@ -112,8 +112,8 @@ describe("Moves - Glaive Rush", () => { game.override.enemyMoveset(Array(4).fill(Moves.SHADOW_SNEAK)); game.override.starterSpecies(0); await game.startBattle([Species.KLINK, Species.FEEBAS]); - const player = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1000; allMoves[Moves.SHADOW_SNEAK].accuracy = 0; diff --git a/src/test/moves/hard_press.test.ts b/src/test/moves/hard_press.test.ts index 6a45e49137b..baf63a1ad23 100644 --- a/src/test/moves/hard_press.test.ts +++ b/src/test/moves/hard_press.test.ts @@ -48,7 +48,7 @@ describe("Moves - Hard Press", () => { it("should return 50 power if target HP ratio is at 50%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .5; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -61,7 +61,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is at 1%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .01; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); @@ -74,7 +74,7 @@ describe("Moves - Hard Press", () => { it("should return 1 power if target HP ratio is less than 1%", async () => { await game.startBattle([Species.PIKACHU]); const targetHpRatio = .005; - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; vi.spyOn(enemy, "getHpRatio").mockReturnValue(targetHpRatio); diff --git a/src/test/moves/hyper_beam.test.ts b/src/test/moves/hyper_beam.test.ts index e7d3454ba32..369d4cac853 100644 --- a/src/test/moves/hyper_beam.test.ts +++ b/src/test/moves/hyper_beam.test.ts @@ -43,8 +43,8 @@ describe("Moves - Hyper Beam", () => { async () => { await game.startBattle([Species.MAGIKARP]); - const leadPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const leadPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.HYPER_BEAM)); diff --git a/src/test/moves/light_screen.test.ts b/src/test/moves/light_screen.test.ts index e47b6fe93ea..9de1f8c492b 100644 --- a/src/test/moves/light_screen.test.ts +++ b/src/test/moves/light_screen.test.ts @@ -48,7 +48,7 @@ describe("Moves - Light Screen", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -63,7 +63,7 @@ describe("Moves - Light Screen", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -75,7 +75,7 @@ describe("Moves - Light Screen", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power); }); diff --git a/src/test/moves/lucky_chant.test.ts b/src/test/moves/lucky_chant.test.ts index 1a29edb8052..1232ce9ffc3 100644 --- a/src/test/moves/lucky_chant.test.ts +++ b/src/test/moves/lucky_chant.test.ts @@ -41,7 +41,7 @@ describe("Moves - Lucky Chant", () => { async () => { await game.startBattle([Species.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); @@ -91,8 +91,8 @@ describe("Moves - Lucky Chant", () => { await game.startBattle([Species.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; enemyPokemon.addTag(BattlerTagType.ALWAYS_CRIT, 2, Moves.NONE, 0); diff --git a/src/test/moves/make_it_rain.test.ts b/src/test/moves/make_it_rain.test.ts index 850f8f644b8..72386930873 100644 --- a/src/test/moves/make_it_rain.test.ts +++ b/src/test/moves/make_it_rain.test.ts @@ -55,8 +55,8 @@ describe("Moves - Make It Rain", () => { await game.startBattle([Species.CHARIZARD]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.MAKE_IT_RAIN)); diff --git a/src/test/moves/miracle_eye.test.ts b/src/test/moves/miracle_eye.test.ts index 188c5736fc4..53d575ea96d 100644 --- a/src/test/moves/miracle_eye.test.ts +++ b/src/test/moves/miracle_eye.test.ts @@ -35,7 +35,7 @@ describe("Internals", () => { it("should allow Psychic moves to hit Dark types", async () => { await game.startBattle(); - const enemy = game.scene.getEnemyPokemon(); + const enemy = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.CONFUSION)); await game.toNextTurn(); diff --git a/src/test/moves/purify.test.ts b/src/test/moves/purify.test.ts index 4d108c43d4b..0367cc5a9b2 100644 --- a/src/test/moves/purify.test.ts +++ b/src/test/moves/purify.test.ts @@ -42,8 +42,8 @@ describe("Moves - Purify", () => { async () => { await game.startBattle(); - const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon(); - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon(); + const enemyPokemon: EnemyPokemon = game.scene.getEnemyPokemon()!; + const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.hp = playerPokemon.getMaxHp() - 1; enemyPokemon.status = new Status(StatusEffect.BURN); @@ -51,7 +51,7 @@ describe("Moves - Purify", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.PURIFY)); await game.phaseInterceptor.to(MoveEndPhase); - expect(enemyPokemon.status).toBe(undefined); + expect(enemyPokemon.status).toBeNull(); expect(playerPokemon.isFullHp()).toBe(true); }, TIMEOUT @@ -62,7 +62,7 @@ describe("Moves - Purify", () => { async () => { await game.startBattle(); - const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon(); + const playerPokemon: PlayerPokemon = game.scene.getPlayerPokemon()!; playerPokemon.hp = playerPokemon.getMaxHp() - 1; const playerInitialHp = playerPokemon.hp; diff --git a/src/test/moves/reflect.test.ts b/src/test/moves/reflect.test.ts index 821d9df437a..f5ea489a75e 100644 --- a/src/test/moves/reflect.test.ts +++ b/src/test/moves/reflect.test.ts @@ -47,7 +47,7 @@ describe("Moves - Reflect", () => { game.doAttack(getMovePosition(game.scene, 0, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); }); @@ -62,7 +62,7 @@ describe("Moves - Reflect", () => { game.doAttack(getMovePosition(game.scene, 1, moveToUse)); await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); @@ -75,7 +75,7 @@ describe("Moves - Reflect", () => { await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon(), game.scene.getPlayerPokemon(), allMoves[moveToUse]); + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); expect(mockedDmg).toBe(allMoves[moveToUse].power); }); diff --git a/src/test/moves/roost.test.ts b/src/test/moves/roost.test.ts index 9ebd52e457d..a9036dcb478 100644 --- a/src/test/moves/roost.test.ts +++ b/src/test/moves/roost.test.ts @@ -40,7 +40,7 @@ describe("Moves - Roost", () => { async () => { await game.startBattle([Species.MAGIKARP]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyStartingHp = enemyPokemon.hp; diff --git a/src/test/moves/spit_up.test.ts b/src/test/moves/spit_up.test.ts index 298c96d6d66..ec0a53028ff 100644 --- a/src/test/moves/spit_up.test.ts +++ b/src/test/moves/spit_up.test.ts @@ -45,10 +45,10 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -69,11 +69,11 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -94,12 +94,12 @@ describe("Moves - Spit Up", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -118,9 +118,9 @@ describe("Moves - Spit Up", () => { it("fails without stacks", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); @@ -137,10 +137,10 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); vi.spyOn(allMoves[Moves.SPIT_UP], "calculateBattlePower"); @@ -166,10 +166,10 @@ describe("Moves - Spit Up", () => { it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly diff --git a/src/test/moves/stockpile.test.ts b/src/test/moves/stockpile.test.ts index 604b08745eb..375eeab3c95 100644 --- a/src/test/moves/stockpile.test.ts +++ b/src/test/moves/stockpile.test.ts @@ -41,7 +41,7 @@ describe("Moves - Stockpile", () => { it("Gains a stockpile stack and increases DEF and SPDEF by 1 on each use, fails at max stacks (3)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const user = game.scene.getPlayerPokemon(); + const user = game.scene.getPlayerPokemon()!; // Unfortunately, Stockpile stacks are not directly queryable (i.e. there is no pokemon.getStockpileStacks()), // we just have to know that they're implemented as a BattlerTag. @@ -59,7 +59,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTag = user.getTag(StockpilingTag); + const stockpilingTag = user.getTag(StockpilingTag)!; const def = user.summonData.battleStats[BattleStat.DEF]; const spdef = user.summonData.battleStats[BattleStat.SPDEF]; @@ -82,7 +82,7 @@ describe("Moves - Stockpile", () => { it("Gains a stockpile stack even if DEF and SPDEF are at +6", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const user = game.scene.getPlayerPokemon(); + const user = game.scene.getPlayerPokemon()!; user.summonData.battleStats[BattleStat.DEF] = 6; user.summonData.battleStats[BattleStat.SPDEF] = 6; @@ -94,7 +94,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTag = user.getTag(StockpilingTag); + const stockpilingTag = user.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(1); expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); @@ -106,7 +106,7 @@ describe("Moves - Stockpile", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.STOCKPILE)); await game.phaseInterceptor.to(TurnInitPhase); - const stockpilingTagAgain = user.getTag(StockpilingTag); + const stockpilingTagAgain = user.getTag(StockpilingTag)!; expect(stockpilingTagAgain).toBeDefined(); expect(stockpilingTagAgain.stockpiledCount).toBe(2); expect(user.summonData.battleStats[BattleStat.DEF]).toBe(6); diff --git a/src/test/moves/swallow.test.ts b/src/test/moves/swallow.test.ts index 9a0a19dce2b..aed30445fd2 100644 --- a/src/test/moves/swallow.test.ts +++ b/src/test/moves/swallow.test.ts @@ -44,13 +44,13 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -71,14 +71,14 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 1; pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -99,7 +99,7 @@ describe("Moves - Swallow", () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); pokemon["hp"] = 0.0001; @@ -107,7 +107,7 @@ describe("Moves - Swallow", () => { pokemon.addTag(BattlerTagType.STOCKPILING); pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); @@ -126,9 +126,9 @@ describe("Moves - Swallow", () => { it("fails without stacks", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); game.doAttack(0); @@ -141,10 +141,10 @@ describe("Moves - Swallow", () => { it("decreases stats based on stored values (both boosts equal)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); game.doAttack(0); @@ -166,10 +166,10 @@ describe("Moves - Swallow", () => { it("decreases stats based on stored values (different boosts)", { timeout: 10000 }, async () => { await game.startBattle([Species.ABOMASNOW]); - const pokemon = game.scene.getPlayerPokemon(); + const pokemon = game.scene.getPlayerPokemon()!; pokemon.addTag(BattlerTagType.STOCKPILING); - const stockpilingTag = pokemon.getTag(StockpilingTag); + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly diff --git a/src/test/moves/tailwind.test.ts b/src/test/moves/tailwind.test.ts index d88de680d8a..b2643dc68f3 100644 --- a/src/test/moves/tailwind.test.ts +++ b/src/test/moves/tailwind.test.ts @@ -80,8 +80,8 @@ describe("Abilities - Wind Rider", () => { await game.startBattle([Species.MAGIKARP]); - const ally = game.scene.getPlayerPokemon(); - const enemy = game.scene.getEnemyPokemon(); + const ally = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; const allySpd = ally.getStat(Stat.SPD); const enemySpd = enemy.getStat(Stat.SPD); diff --git a/src/test/moves/thousand_arrows.test.ts b/src/test/moves/thousand_arrows.test.ts index 3b15610f896..84a71ee5256 100644 --- a/src/test/moves/thousand_arrows.test.ts +++ b/src/test/moves/thousand_arrows.test.ts @@ -39,7 +39,7 @@ describe("Moves - Thousand Arrows", () => { async () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); @@ -62,7 +62,7 @@ describe("Moves - Thousand Arrows", () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); @@ -84,9 +84,9 @@ describe("Moves - Thousand Arrows", () => { await game.startBattle([ Species.ILLUMISE ]); - const enemyPokemon = game.scene.getEnemyPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, null, Moves.MAGNET_RISE); + enemyPokemon.addTag(BattlerTagType.MAGNET_RISEN, undefined, Moves.MAGNET_RISE); game.doAttack(getMovePosition(game.scene, 0, Moves.THOUSAND_ARROWS)); diff --git a/src/test/moves/tidy_up.test.ts b/src/test/moves/tidy_up.test.ts index 83e1dda07cb..e35a438c562 100644 --- a/src/test/moves/tidy_up.test.ts +++ b/src/test/moves/tidy_up.test.ts @@ -106,7 +106,7 @@ describe("Moves - Tidy Up", () => { it("user's stats are raised with no traps set", async() => { await game.startBattle(); - const player = game.scene.getPlayerPokemon().summonData.battleStats; + const player = game.scene.getPlayerPokemon()!.summonData.battleStats; expect(player[BattleStat.ATK]).toBe(0); expect(player[BattleStat.SPD]).toBe(0); diff --git a/src/test/moves/u_turn.test.ts b/src/test/moves/u_turn.test.ts index c54a94dde2b..2c12a4da43b 100644 --- a/src/test/moves/u_turn.test.ts +++ b/src/test/moves/u_turn.test.ts @@ -43,7 +43,7 @@ describe("Moves - U-turn", () => { Species.RAICHU, Species.SHUCKLE ]); - game.scene.getPlayerPokemon().hp = playerHp; + game.scene.getPlayerPokemon()!.hp = playerHp; // act game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); @@ -53,7 +53,7 @@ describe("Moves - U-turn", () => { // assert expect(game.scene.getParty()[1].hp).toEqual(Math.floor(game.scene.getParty()[1].getMaxHp() * 0.33 + playerHp)); expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase"); - expect(game.scene.getPlayerPokemon().species.speciesId).toBe(Species.SHUCKLE); + expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE); }, 20000); it("triggers rough skin on the u-turn user before a new pokemon is switched in", async() => { @@ -70,9 +70,10 @@ describe("Moves - U-turn", () => { await game.phaseInterceptor.to(SwitchPhase, false); // assert - expect(game.scene.getPlayerPokemon().hp).not.toEqual(game.scene.getPlayerPokemon().getMaxHp()); - expect(game.scene.getEnemyPokemon().battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated - expect(game.scene.getPlayerPokemon().species.speciesId).toEqual(Species.RAICHU); + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.hp).not.toEqual(playerPkm.getMaxHp()); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated + expect(playerPkm.species.speciesId).toEqual(Species.RAICHU); expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }, 20000); @@ -83,16 +84,17 @@ describe("Moves - U-turn", () => { Species.RAICHU, Species.SHUCKLE ]); - vi.spyOn(game.scene.getEnemyPokemon(), "randSeedInt").mockReturnValue(0); + vi.spyOn(game.scene.getEnemyPokemon()!, "randSeedInt").mockReturnValue(0); // act game.doAttack(getMovePosition(game.scene, 0, Moves.U_TURN)); await game.phaseInterceptor.to(SwitchPhase, false); // assert - expect(game.scene.getPlayerPokemon().status?.effect).toEqual(StatusEffect.POISON); - expect(game.scene.getPlayerPokemon().species.speciesId).toEqual(Species.RAICHU); - expect(game.scene.getEnemyPokemon().battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated + const playerPkm = game.scene.getPlayerPokemon()!; + expect(playerPkm.status?.effect).toEqual(StatusEffect.POISON); + expect(playerPkm.species.speciesId).toEqual(Species.RAICHU); + expect(game.scene.getEnemyPokemon()!.battleData.abilityRevealed).toBe(true); // proxy for asserting ability activated expect(game.phaseInterceptor.log).not.toContain("SwitchSummonPhase"); }, 20000); }); diff --git a/src/test/settingMenu/helpers/menuManip.ts b/src/test/settingMenu/helpers/menuManip.ts index f33867476cf..4fd5f526897 100644 --- a/src/test/settingMenu/helpers/menuManip.ts +++ b/src/test/settingMenu/helpers/menuManip.ts @@ -70,7 +70,7 @@ export class MenuManip { weWantThisBindInstead(keycode) { this.keycode = Phaser.Input.Keyboard.KeyCodes[keycode]; const icon = getIconWithKeycode(this.config, this.keycode); - const key = getKeyWithKeycode(this.config, this.keycode); + const key = getKeyWithKeycode(this.config, this.keycode)!; // TODO: is this bang correct? const _keys = key.toLowerCase().split("_"); const iconIdentifier = _keys[_keys.length-1]; expect(icon.toLowerCase().includes(iconIdentifier)).toEqual(true); @@ -78,7 +78,7 @@ export class MenuManip { } whenWeDelete(settingName?: string) { - this.settingName = SettingKeyboard[settingName] || this.settingName; + this.settingName = settingName ? SettingKeyboard[settingName] : this.settingName; // const key = getKeyWithSettingName(this.config, this.settingName); deleteBind(this.config, this.settingName); // expect(this.config.custom[key]).toEqual(-1); @@ -86,7 +86,7 @@ export class MenuManip { } whenWeTryToDelete(settingName?: string) { - this.settingName = SettingKeyboard[settingName] || this.settingName; + this.settingName = settingName ? SettingKeyboard[settingName] : this.settingName; deleteBind(this.config, this.settingName); return this; } diff --git a/src/test/settingMenu/rebinding_setting.test.ts b/src/test/settingMenu/rebinding_setting.test.ts index 3bc17109c95..eead23972c2 100644 --- a/src/test/settingMenu/rebinding_setting.test.ts +++ b/src/test/settingMenu/rebinding_setting.test.ts @@ -37,13 +37,13 @@ describe("Test Rebinding", () => { expect(button).toEqual(Button.LEFT); }); it("Check key for Keyboard KeyCode", () => { - const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.LEFT); + const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.LEFT) ?? ""; const settingName = config.custom[key]; const button = config.settings[settingName]; expect(button).toEqual(Button.LEFT); }); it("Check key for currenly Assigned to action not alt", () => { - const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.A); + const key = getKeyWithKeycode(config, Phaser.Input.Keyboard.KeyCodes.A) ?? ""; const settingName = config.custom[key]; const button = config.settings[settingName]; expect(button).toEqual(Button.LEFT); @@ -66,25 +66,25 @@ describe("Test Rebinding", () => { }); it("Check icon for currenly Assigned to key code", () => { const keycode = Phaser.Input.Keyboard.KeyCodes.LEFT; - const key = getKeyWithKeycode(config, keycode); + const key = getKeyWithKeycode(config, keycode) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("KEY_ARROW_LEFT.png"); }); it("Check icon for currenly Assigned to key code", () => { const keycode = Phaser.Input.Keyboard.KeyCodes.A; - const key = getKeyWithKeycode(config, keycode); + const key = getKeyWithKeycode(config, keycode) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("A.png"); }); it("Check icon for currenly Assigned to setting name", () => { const settingName = SettingKeyboard.Button_Left; - const key = getKeyWithSettingName(config, settingName); + const key = getKeyWithSettingName(config, settingName) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("KEY_ARROW_LEFT.png"); }); it("Check icon for currenly Assigned to setting name alt", () => { const settingName = SettingKeyboard.Alt_Button_Left; - const key = getKeyWithSettingName(config, settingName); + const key = getKeyWithSettingName(config, settingName) ?? ""; const icon = config.icons[key]; expect(icon).toEqual("A.png"); }); @@ -369,7 +369,7 @@ describe("Test Rebinding", () => { it("test keyboard listener", () => { const keyDown = Phaser.Input.Keyboard.KeyCodes.S; - const key = getKeyWithKeycode(config, keyDown); + const key = getKeyWithKeycode(config, keyDown) ?? ""; const settingName = config.custom[key]; const buttonDown = config.settings[settingName]; expect(buttonDown).toEqual(Button.DOWN); diff --git a/src/test/sprites/pokemonSprite.test.ts b/src/test/sprites/pokemonSprite.test.ts index ce847751b6b..deb5844d677 100644 --- a/src/test/sprites/pokemonSprite.test.ts +++ b/src/test/sprites/pokemonSprite.test.ts @@ -23,9 +23,12 @@ describe("check if every variant's sprite are correctly set", () => { expVariant = masterlist.exp; femaleVariant = masterlist.female; backVariant = masterlist.back; - delete masterlist.exp; - delete masterlist.female; - delete masterlist.back; + //@ts-ignore + delete masterlist.exp; //TODO: resolve ts-ignore + //@ts-ignore + delete masterlist.female; //TODO: resolve ts-ignore + //@ts-ignore + delete masterlist.back; //TODO: resolve ts-ignore }); it("data should not be undefined", () => { @@ -103,7 +106,7 @@ describe("check if every variant's sprite are correctly set", () => { } function getMissingFiles(keys: Record, dirPath: string): string[] { - const errors = []; + const errors: string[] = []; for (const key of Object.keys(keys)) { const row = keys[key]; for (const [index, elm] of row.entries()) { diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index eae5b3fe3a5..b61338bfcad 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -55,8 +55,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -69,7 +69,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -116,8 +116,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -130,7 +130,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -180,8 +180,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -194,7 +194,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -242,8 +242,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -256,7 +256,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -303,8 +303,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -317,7 +317,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -364,8 +364,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -378,7 +378,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -426,8 +426,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -440,7 +440,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { @@ -486,8 +486,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -500,7 +500,7 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); let starterSelectUiHandler: StarterSelectUiHandler; await new Promise((resolve) => { @@ -551,8 +551,8 @@ describe("UI - Starter select", () => { game.phaseInterceptor.unlock(); }); await game.phaseInterceptor.run(SelectStarterPhase); - let options: OptionSelectItem[]; - let optionSelectUiHandler: OptionSelectUiHandler; + let options: OptionSelectItem[] = []; + let optionSelectUiHandler: OptionSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.OPTION_SELECT, () => { optionSelectUiHandler = game.scene.ui.getHandler() as OptionSelectUiHandler; @@ -565,9 +565,9 @@ describe("UI - Starter select", () => { expect(options.some(option => option.label === "Manage Moves")).toBe(true); expect(options.some(option => option.label === "Use Candies")).toBe(true); expect(options.some(option => option.label === "Cancel")).toBe(true); - optionSelectUiHandler.processInput(Button.ACTION); + optionSelectUiHandler?.processInput(Button.ACTION); - let starterSelectUiHandler: StarterSelectUiHandler; + let starterSelectUiHandler: StarterSelectUiHandler | undefined; await new Promise((resolve) => { game.onNextPrompt("SelectStarterPhase", Mode.STARTER_SELECT, () => { starterSelectUiHandler = game.scene.ui.getHandler() as StarterSelectUiHandler; @@ -576,11 +576,11 @@ describe("UI - Starter select", () => { }); }); - expect(starterSelectUiHandler.starterSpecies.length).toBe(1); - expect(starterSelectUiHandler.starterSpecies[0].generation).toBe(1); - expect(starterSelectUiHandler.starterSpecies[0].speciesId).toBe(32); - expect(starterSelectUiHandler.cursorObj.x).toBe(53); - expect(starterSelectUiHandler.cursorObj.y).toBe(31); + expect(starterSelectUiHandler?.starterSpecies.length).toBe(1); + expect(starterSelectUiHandler?.starterSpecies[0].generation).toBe(1); + expect(starterSelectUiHandler?.starterSpecies[0].speciesId).toBe(32); + expect(starterSelectUiHandler?.cursorObj.x).toBe(53); + expect(starterSelectUiHandler?.cursorObj.y).toBe(31); game.onNextPrompt("SelectStarterPhase", Mode.CONFIRM, () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; diff --git a/src/test/utils/TextInterceptor.ts b/src/test/utils/TextInterceptor.ts index a49f41f6be0..507161eb6d0 100644 --- a/src/test/utils/TextInterceptor.ts +++ b/src/test/utils/TextInterceptor.ts @@ -1,6 +1,6 @@ export default class TextInterceptor { private scene; - public logs = []; + public logs: string[] = []; constructor(scene) { this.scene = scene; scene.messageWrapper = this; @@ -17,6 +17,6 @@ export default class TextInterceptor { } getLatestMessage(): string { - return this.logs.pop(); + return this.logs.pop() ?? ""; } } diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 842c6086058..771ab1e7081 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -81,7 +81,7 @@ export default class GameManager { * Ends the current phase. */ endPhase() { - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); } /** @@ -174,7 +174,7 @@ export default class GameManager { // Confirm target selection if move is multi-target this.onNextPrompt("SelectTargetPhase", Mode.TARGET_SELECT, () => { const handler = this.scene.ui.getHandler() as TargetSelectUiHandler; - const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition].getMove(); + const move = (this.scene.getCurrentPhase() as SelectTargetPhase).getPokemon().getMoveset()[movePosition]!.getMove(); // TODO: is the bang correct? if (move.isMultiTarget()) { handler.processInput(Button.ACTION); } @@ -255,7 +255,7 @@ export default class GameManager { */ isCurrentPhase(phaseTarget) { const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; - return this.scene.getCurrentPhase().constructor.name === targetName; + return this.scene.getCurrentPhase()?.constructor.name === targetName; } /** @@ -278,7 +278,7 @@ export default class GameManager { await waitUntil(() => this.scene.ui?.getMode() === Mode.TITLE); await this.scene.gameData.tryExportData(GameDataType.SESSION, 0); await waitUntil(() => localStorage.hasOwnProperty("toExport")); - return resolve(localStorage.getItem("toExport")); + return resolve(localStorage.getItem("toExport")!); // TODO: is this bang correct?; }); } @@ -332,7 +332,7 @@ export default class GameManager { doRevivePokemon(pokemonIndex: number) { const party = this.scene.getParty(); const candidate = new ModifierTypeOption(modifierTypes.MAX_REVIVE(), 0); - const modifier = candidate.type.newModifier(party[pokemonIndex]); + const modifier = candidate.type!.newModifier(party[pokemonIndex]); this.scene.addModifier(modifier, false); } diff --git a/src/test/utils/gameManagerUtils.ts b/src/test/utils/gameManagerUtils.ts index 74a73bb7875..dfba55fc75c 100644 --- a/src/test/utils/gameManagerUtils.ts +++ b/src/test/utils/gameManagerUtils.ts @@ -85,6 +85,7 @@ export function waitUntil(truth) { export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: Moves) { const playerPokemon = scene.getPlayerField()[pokemonIndex]; const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex((m) => m.moveId === move); + const index = moveSet.findIndex((m) => m?.moveId === move); + console.log(`Move position for ${Moves[move]} (=${move}):`, index); return index; } diff --git a/src/test/utils/inputsHandler.ts b/src/test/utils/inputsHandler.ts index e41667dc873..148329ada32 100644 --- a/src/test/utils/inputsHandler.ts +++ b/src/test/utils/inputsHandler.ts @@ -7,13 +7,17 @@ import TouchControl from "#app/touch-controls"; import { JSDOM } from "jsdom"; import fs from "fs"; +interface LogEntry { + type: string; + button: any; +} export default class InputsHandler { private scene: BattleScene; private events: Phaser.Events.EventEmitter; private inputController: InputsController; - public log = []; - public logUp = []; + public log: LogEntry[] = []; + public logUp: LogEntry[] = []; private fakePad: Fakepad; private fakeMobile: FakeMobile; @@ -22,7 +26,7 @@ export default class InputsHandler { this.inputController = this.scene.inputController; this.fakePad = new Fakepad(pad_xbox360); this.fakeMobile = new FakeMobile(); - this.scene.input.gamepad.gamepads.push(this.fakePad); + this.scene.input.gamepad?.gamepads.push(this.fakePad); this.init(); } @@ -37,18 +41,18 @@ export default class InputsHandler { pressGamepadButton(button: integer, duration: integer): Promise { return new Promise(async (resolve) => { - this.scene.input.gamepad.emit("down", this.fakePad, {index: button}); + this.scene.input.gamepad?.emit("down", this.fakePad, {index: button}); await holdOn(duration); - this.scene.input.gamepad.emit("up", this.fakePad, {index: button}); + this.scene.input.gamepad?.emit("up", this.fakePad, {index: button}); resolve(); }); } pressKeyboardKey(key: integer, duration: integer): Promise { return new Promise(async (resolve) => { - this.scene.input.keyboard.emit("keydown", {keyCode: key}); + this.scene.input.keyboard?.emit("keydown", {keyCode: key}); await holdOn(duration); - this.scene.input.keyboard.emit("keyup", {keyCode: key}); + this.scene.input.keyboard?.emit("keyup", {keyCode: key}); resolve(); }); } @@ -57,7 +61,7 @@ export default class InputsHandler { const touchControl = new TouchControl(this.scene); touchControl.deactivatePressedKey(); //test purpose this.events = this.inputController.events; - this.scene.input.gamepad.emit("connected", this.fakePad); + this.scene.input.gamepad?.emit("connected", this.fakePad); this.listenInputs(); } @@ -77,7 +81,8 @@ class Fakepad extends Phaser.Input.Gamepad.Gamepad { public index: number; constructor(pad) { - super(undefined, {...pad, buttons: pad.deviceMapping, axes: []}); + //@ts-ignore + super(undefined, {...pad, buttons: pad.deviceMapping, axes: []}); //TODO: resolve ts-ignore this.id = "xbox_360_fakepad"; this.index = 0; } diff --git a/src/test/utils/mocks/mockClock.ts b/src/test/utils/mocks/mockClock.ts index ba12dc002cc..3664a831305 100644 --- a/src/test/utils/mocks/mockClock.ts +++ b/src/test/utils/mocks/mockClock.ts @@ -2,7 +2,7 @@ import Clock = Phaser.Time.Clock; export class MockClock extends Clock { - public overrideDelay: number; + public overrideDelay: number | undefined; constructor(scene) { super(scene); this.overrideDelay = undefined; diff --git a/src/test/utils/mocks/mockConsoleLog.ts b/src/test/utils/mocks/mockConsoleLog.ts index 61c6c14b989..f44a0c7d6cd 100644 --- a/src/test/utils/mocks/mockConsoleLog.ts +++ b/src/test/utils/mocks/mockConsoleLog.ts @@ -1,12 +1,12 @@ const MockConsoleLog = (_logDisabled= false, _phaseText=false) => { - let logs = []; + let logs: any[] = []; const logDisabled: boolean = _logDisabled; const phaseText: boolean = _phaseText; const originalLog = console.log; const originalError = console.error; const originalDebug = console.debug; const originalWarn = console.warn; - const notified = []; + const notified: any[] = []; const blacklist = ["Phaser", "variant icon does not exist", "Texture \"%s\" not found"]; const whitelist = ["Phase"]; diff --git a/src/test/utils/mocks/mockGameObject.ts b/src/test/utils/mocks/mockGameObject.ts new file mode 100644 index 00000000000..9138e0f687a --- /dev/null +++ b/src/test/utils/mocks/mockGameObject.ts @@ -0,0 +1,3 @@ +export interface MockGameObject { + +} diff --git a/src/test/utils/mocks/mockTextureManager.ts b/src/test/utils/mocks/mockTextureManager.ts index 36a09efcf5a..95e8996836f 100644 --- a/src/test/utils/mocks/mockTextureManager.ts +++ b/src/test/utils/mocks/mockTextureManager.ts @@ -5,6 +5,7 @@ import MockNineslice from "#test/utils/mocks/mocksContainer/mockNineslice"; import MockImage from "#test/utils/mocks/mocksContainer/mockImage"; import MockText from "#test/utils/mocks/mocksContainer/mockText"; import MockPolygon from "#test/utils/mocks/mocksContainer/mockPolygon"; +import { MockGameObject } from "./mockGameObject"; export default class MockTextureManager { @@ -12,7 +13,7 @@ export default class MockTextureManager { private scene; public add; public displayList; - public list = []; + public list: MockGameObject[] = []; constructor(scene) { this.scene = scene; diff --git a/src/test/utils/mocks/mocksContainer/mockContainer.ts b/src/test/utils/mocks/mocksContainer/mockContainer.ts index b74c46b4e86..d3672cb5235 100644 --- a/src/test/utils/mocks/mocksContainer/mockContainer.ts +++ b/src/test/utils/mocks/mocksContainer/mockContainer.ts @@ -1,6 +1,7 @@ import MockTextureManager from "#test/utils/mocks/mockTextureManager"; +import { MockGameObject } from "../mockGameObject"; -export default class MockContainer { +export default class MockContainer implements MockGameObject { protected x; protected y; protected scene; @@ -11,7 +12,7 @@ export default class MockContainer { private style; public frame; protected textureManager; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager: MockTextureManager, x, y) { this.x = x; diff --git a/src/test/utils/mocks/mocksContainer/mockGraphics.ts b/src/test/utils/mocks/mocksContainer/mockGraphics.ts index a08f150fe61..e026b212e16 100644 --- a/src/test/utils/mocks/mocksContainer/mockGraphics.ts +++ b/src/test/utils/mocks/mocksContainer/mockGraphics.ts @@ -1,6 +1,8 @@ -export default class MockGraphics { +import { MockGameObject } from "../mockGameObject"; + +export default class MockGraphics implements MockGameObject { private scene; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, config) { this.scene = textureManager.scene; } diff --git a/src/test/utils/mocks/mocksContainer/mockRectangle.ts b/src/test/utils/mocks/mocksContainer/mockRectangle.ts index db323268037..26c2f74ea42 100644 --- a/src/test/utils/mocks/mocksContainer/mockRectangle.ts +++ b/src/test/utils/mocks/mocksContainer/mockRectangle.ts @@ -1,7 +1,9 @@ -export default class MockRectangle { +import { MockGameObject } from "../mockGameObject"; + +export default class MockRectangle implements MockGameObject { private fillColor; private scene; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, x, y, width, height, fillColor) { this.fillColor = fillColor; diff --git a/src/test/utils/mocks/mocksContainer/mockSprite.ts b/src/test/utils/mocks/mocksContainer/mockSprite.ts index fb7f84741e8..9c566fc4bcb 100644 --- a/src/test/utils/mocks/mocksContainer/mockSprite.ts +++ b/src/test/utils/mocks/mocksContainer/mockSprite.ts @@ -1,9 +1,10 @@ +import { MockGameObject } from "../mockGameObject"; import Sprite = Phaser.GameObjects.Sprite; import Frame = Phaser.Textures.Frame; import Phaser from "phaser"; -export default class MockSprite { +export default class MockSprite implements MockGameObject { private phaserSprite; public pipelineData; public texture; @@ -12,10 +13,11 @@ export default class MockSprite { public textureManager; public scene; public anims; - public list = []; + public list: MockGameObject[] = []; constructor(textureManager, x, y, texture) { this.textureManager = textureManager; this.scene = textureManager.scene; + // @ts-ignore Phaser.GameObjects.Sprite.prototype.setInteractive = this.setInteractive; // @ts-ignore Phaser.GameObjects.Sprite.prototype.setTexture = this.setTexture; diff --git a/src/test/utils/mocks/mocksContainer/mockText.ts b/src/test/utils/mocks/mocksContainer/mockText.ts index f4513e20926..5d405efadfd 100644 --- a/src/test/utils/mocks/mocksContainer/mockText.ts +++ b/src/test/utils/mocks/mocksContainer/mockText.ts @@ -1,12 +1,13 @@ import UI from "#app/ui/ui"; +import { MockGameObject } from "../mockGameObject"; -export default class MockText { +export default class MockText implements MockGameObject { private phaserText; private wordWrapWidth; private splitRegExp; private scene; private textureManager; - public list = []; + public list: MockGameObject[] = []; public style; public text = ""; diff --git a/src/test/utils/overridesHelper.ts b/src/test/utils/overridesHelper.ts index e51928cc784..e77cd49b742 100644 --- a/src/test/utils/overridesHelper.ts +++ b/src/test/utils/overridesHelper.ts @@ -192,7 +192,7 @@ export class OverridesHelper { * @param battleType battle type to set * @returns this */ - battleType(battleType: "single" | "double"): this { + battleType(battleType: "single" | "double" | null): this { vi.spyOn(Overrides, "BATTLE_TYPE_OVERRIDE", "get").mockReturnValue(battleType); this.log(`Battle type set to ${battleType} only!`); return this; diff --git a/src/test/utils/phaseInterceptor.ts b/src/test/utils/phaseInterceptor.ts index aabde643aa8..34f79f93b6e 100644 --- a/src/test/utils/phaseInterceptor.ts +++ b/src/test/utils/phaseInterceptor.ts @@ -289,7 +289,7 @@ export default class PhaseInterceptor { setMode(mode: Mode, ...args: any[]): Promise { const currentPhase = this.scene.getCurrentPhase(); const instance = this.scene.ui; - console.log("setMode", mode, args); + console.log("setMode", `${Mode[mode]} (=${mode})`, args); const ret = this.originalSetMode.apply(instance, [mode, ...args]); if (!this.phases[currentPhase.constructor.name]) { throw new Error(`missing ${currentPhase.constructor.name} in phaseInterceptior PHASES list`); @@ -328,7 +328,7 @@ export default class PhaseInterceptor { * @param callback - The callback function to execute. * @param expireFn - The function to determine if the prompt has expired. */ - addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn: () => void, awaitingActionInput: boolean = false) { + addToNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn?: () => void, awaitingActionInput: boolean = false) { this.prompts.push({ phaseTarget, mode, diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index dac67bd7b4e..0502080d3a5 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -52,32 +52,32 @@ export class TimedEventManager { let multiplier = 1; const shinyEvents = timedEvents.filter((te) => te.eventType === EventType.SHINY && this.isActive(te)); shinyEvents.forEach((se) => { - multiplier *= se.shinyMultiplier; + multiplier *= se.shinyMultiplier!; // TODO: is this bang correct? }); return multiplier; } getEventBannerFilename(): string { - return timedEvents.find((te: TimedEvent) => this.isActive(te)).bannerFilename ?? null; + return timedEvents.find((te: TimedEvent) => this.isActive(te))?.bannerFilename!; // TODO: is this bang correct? } } export class TimedEventDisplay extends Phaser.GameObjects.Container { - private event: TimedEvent; + private event: TimedEvent | null; private eventTimerText: Phaser.GameObjects.Text; private banner: Phaser.GameObjects.Image; private bannerShadow: Phaser.GameObjects.Rectangle; - private eventTimer: NodeJS.Timeout; + private eventTimer: NodeJS.Timeout | null; - constructor(scene: BattleScene, x: number, y: number, event: TimedEvent) { + constructor(scene: BattleScene, x: number, y: number, event?: TimedEvent) { super(scene, x, y); - this.event = event; + this.event = event!; // TODO: is this bang correct? this.setVisible(false); } setup() { - this.banner = new Phaser.GameObjects.Image(this.scene, 29, 64, this.event.bannerFilename); + this.banner = new Phaser.GameObjects.Image(this.scene, 29, 64, this.event!.bannerFilename!); // TODO: are the bangs correct here? this.banner.setName("img-event-banner"); this.banner.setOrigin(0, 0); this.banner.setScale(0.07); @@ -97,7 +97,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { this.scene, this.banner.x + 8, this.banner.y + 100, - this.timeToGo(this.event.endDate), + this.timeToGo(this.event!.endDate), // TODO: is the bang correct here? TextStyle.WINDOW ); this.eventTimerText.setName("text-event-timer"); @@ -118,7 +118,7 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { clear() { this.setVisible(false); - clearInterval(this.eventTimer); + this.eventTimer && clearInterval(this.eventTimer); this.eventTimer = null; } @@ -145,6 +145,6 @@ export class TimedEventDisplay extends Phaser.GameObjects.Container { } updateCountdown() { - this.eventTimerText.setText(this.timeToGo(this.event.endDate)); + this.eventTimerText.setText(this.timeToGo(this.event!.endDate)); // TODO: is the bang correct here? } } diff --git a/src/touch-controls.ts b/src/touch-controls.ts index 9953e99c81a..1726b47a4bd 100644 --- a/src/touch-controls.ts +++ b/src/touch-controls.ts @@ -48,7 +48,7 @@ export default class TouchControl { node.addEventListener("touchend", event => { event.preventDefault(); - this.touchButtonUp(node, key, event.target["id"]); + this.touchButtonUp(node, key, event.target?.["id"]); }); } @@ -120,7 +120,7 @@ export default class TouchControl { * Prevent zoom on specified element * @param {HTMLElement} element */ - preventElementZoom(element: HTMLElement): void { + preventElementZoom(element: HTMLElement | null): void { if (!element) { return; } diff --git a/src/ui/ability-bar.ts b/src/ui/ability-bar.ts index 7a4ae559975..b8259af9f3d 100644 --- a/src/ui/ability-bar.ts +++ b/src/ui/ability-bar.ts @@ -12,8 +12,8 @@ export default class AbilityBar extends Phaser.GameObjects.Container { private bg: Phaser.GameObjects.Image; private abilityBarText: Phaser.GameObjects.Text; - private tween: Phaser.Tweens.Tween; - private autoHideTimer: NodeJS.Timeout; + private tween: Phaser.Tweens.Tween | null; + private autoHideTimer: NodeJS.Timeout | null; public shown: boolean; diff --git a/src/ui/abstact-option-select-ui-handler.ts b/src/ui/abstact-option-select-ui-handler.ts index bf8a79ae194..f0148509220 100644 --- a/src/ui/abstact-option-select-ui-handler.ts +++ b/src/ui/abstact-option-select-ui-handler.ts @@ -36,7 +36,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { protected optionSelectText: Phaser.GameObjects.Text; protected optionSelectIcons: Phaser.GameObjects.Sprite[]; - protected config: OptionSelectConfig; + protected config: OptionSelectConfig | null; protected blockInput: boolean; @@ -44,10 +44,10 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { protected scale: number = 0.1666666667; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? } abstract getWindowWidth(): integer; @@ -96,7 +96,7 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { this.optionSelectBg.width = Math.max(this.optionSelectText.displayWidth + 24, this.getWindowWidth()); - if (this.config?.options.length > this.config?.maxOptions) { + if (this.config?.options && this.config?.options.length > (this.config?.maxOptions!)) { // TODO: is this bang correct? this.optionSelectText.setText(this.getOptionsWithScroll().map(o => o.label).join("\n")); } @@ -123,8 +123,10 @@ export default abstract class AbstractOptionSelectUiHandler extends UiHandler { itemOverlayIcon.setPositionRelative(this.optionSelectText, 36 * this.scale, 7 + i * (114 * this.scale - 3)); - itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); - itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + if (option.itemArgs) { + itemIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[0]))); + itemOverlayIcon.setTint(argbFromRgba(Utils.rgbHexToRgba(option.itemArgs[1]))); + } } } }); diff --git a/src/ui/achv-bar.ts b/src/ui/achv-bar.ts index 05dde03aed7..0f3ab7c2e47 100644 --- a/src/ui/achv-bar.ts +++ b/src/ui/achv-bar.ts @@ -28,7 +28,7 @@ export default class AchvBar extends Phaser.GameObjects.Container { this.defaultWidth = 160; this.defaultHeight = 40; - this.bg = this.scene.add.nineslice(0, 0, "achv_bar", null, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4); + this.bg = this.scene.add.nineslice(0, 0, "achv_bar", undefined, this.defaultWidth, this.defaultHeight, 41, 6, 16, 4); this.bg.setOrigin(0, 0); this.add(this.bg); @@ -119,7 +119,8 @@ export default class AchvBar extends Phaser.GameObjects.Container { this.shown = false; this.setVisible(false); if (this.queue.length) { - this.showAchv(this.queue.shift()); + const shifted = this.queue.shift(); + shifted && this.showAchv(shifted); } } }); diff --git a/src/ui/achvs-ui-handler.ts b/src/ui/achvs-ui-handler.ts index b5e17ce2b67..304b5df8340 100644 --- a/src/ui/achvs-ui-handler.ts +++ b/src/ui/achvs-ui-handler.ts @@ -19,10 +19,10 @@ export default class AchvsUiHandler extends MessageUiHandler { private scoreText: Phaser.GameObjects.Text; private unlockText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? } setup() { @@ -210,7 +210,7 @@ export default class AchvsUiHandler extends MessageUiHandler { let updateAchv = ret; if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", null, 16, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.achvIconsContainer.add(this.cursorObj); updateAchv = true; diff --git a/src/ui/awaitable-ui-handler.ts b/src/ui/awaitable-ui-handler.ts index 18635b132f9..33be11ccc27 100644 --- a/src/ui/awaitable-ui-handler.ts +++ b/src/ui/awaitable-ui-handler.ts @@ -5,7 +5,7 @@ import {Button} from "#enums/buttons"; export default abstract class AwaitableUiHandler extends UiHandler { protected awaitingActionInput: boolean; - protected onActionInput: Function; + protected onActionInput: Function | null; public tutorialActive: boolean = false; constructor(scene: BattleScene, mode: Mode) { diff --git a/src/ui/ball-ui-handler.ts b/src/ui/ball-ui-handler.ts index 9fc591f209f..d8b3e5e3ee8 100644 --- a/src/ui/ball-ui-handler.ts +++ b/src/ui/ball-ui-handler.ts @@ -13,7 +13,7 @@ export default class BallUiHandler extends UiHandler { private pokeballSelectBg: Phaser.GameObjects.NineSlice; private countsText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; private scale: number = 0.1666666667; diff --git a/src/ui/battle-info.ts b/src/ui/battle-info.ts index d78b05a569f..98ebdf21078 100644 --- a/src/ui/battle-info.ts +++ b/src/ui/battle-info.ts @@ -22,12 +22,12 @@ export default class BattleInfo extends Phaser.GameObjects.Container { private boss: boolean; private bossSegments: integer; private offset: boolean; - private lastName: string; + private lastName: string | null; private lastTeraType: Type; private lastStatus: StatusEffect; private lastHp: integer; private lastMaxHp: integer; - private lastHpFrame: string; + private lastHpFrame: string | null; private lastExp: integer; private lastLevelExp: integer; private lastLevel: integer; @@ -267,7 +267,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.add(this.effectivenessContainer); this.effectivenessText = addTextObject(this.scene, 5, 4.5, "", TextStyle.BATTLE_INFO); - this.effectivenessWindow = addWindow((this.scene as BattleScene), 0, 0, 0, 20, false, false, null, null, WindowVariant.XTHIN); + this.effectivenessWindow = addWindow((this.scene as BattleScene), 0, 0, 0, 20, undefined, false, undefined, undefined, WindowVariant.XTHIN); this.effectivenessContainer.add(this.effectivenessWindow); this.effectivenessContainer.add(this.effectivenessText); @@ -293,7 +293,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.teraIcon.setVisible(this.lastTeraType !== Type.UNKNOWN); this.teraIcon.on("pointerover", () => { if (this.lastTeraType !== Type.UNKNOWN) { - (this.scene as BattleScene).ui.showTooltip(null, `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`); + (this.scene as BattleScene).ui.showTooltip("", `${Utils.toReadableString(Type[this.lastTeraType])} Terastallized`); } }); this.teraIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); @@ -303,7 +303,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { this.splicedIcon.setPositionRelative(this.nameText, nameTextWidth + this.genderText.displayWidth + 1 + (this.teraIcon.visible ? this.teraIcon.displayWidth + 1 : 0), 2.5); this.splicedIcon.setVisible(isFusion); if (this.splicedIcon.visible) { - this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies.getName(pokemon.fusionFormIndex)}`)); + this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${pokemon.species.getName(pokemon.formIndex)}/${pokemon.fusionSpecies?.getName(pokemon.fusionFormIndex)}`)); this.splicedIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -318,7 +318,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`)); + this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`)); this.shinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -330,7 +330,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { if (!this.player) { if (this.nameText.visible) { - this.nameText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, i18next.t("battleInfo:generation", { generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`) }))); + this.nameText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", i18next.t("battleInfo:generation", { generation: i18next.t(`starterSelectUiHandler:gen${pokemon.species.generation}`) }))); this.nameText.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -472,7 +472,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { updateBossSegmentDividers(pokemon: EnemyPokemon): void { while (this.hpBarSegmentDividers.length) { - this.hpBarSegmentDividers.pop().destroy(); + this.hpBarSegmentDividers.pop()?.destroy(); } if (this.boss && this.bossSegments > 1) { @@ -768,7 +768,7 @@ export default class BattleInfo extends Phaser.GameObjects.Container { } this.currentEffectiveness = effectiveness; - if (!(this.scene as BattleScene).typeHints || effectiveness === undefined || this.flyoutMenu.flyoutVisible) { + if (!(this.scene as BattleScene).typeHints || effectiveness === undefined || this.flyoutMenu?.flyoutVisible) { this.effectivenessContainer.setVisible(false); return; } diff --git a/src/ui/battle-message-ui-handler.ts b/src/ui/battle-message-ui-handler.ts index f2da553c6da..6c5049394a7 100644 --- a/src/ui/battle-message-ui-handler.ts +++ b/src/ui/battle-message-ui-handler.ts @@ -147,19 +147,21 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } } } + + return false; } clear() { super.clear(); } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.hideNameText(); super.showText(text, delay, callback, callbackDelay, prompt, promptDelay); } - showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { - this.showNameText(name); + showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + name && this.showNameText(name); super.showDialogue(text, name, delay, callback, callbackDelay, prompt, promptDelay); } @@ -180,7 +182,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { this.awaitingActionInput = true; this.onActionInput = () => { if (!showTotals) { - return this.promptLevelUpStats(partyMemberIndex, null, true).then(() => resolve()); + return this.promptLevelUpStats(partyMemberIndex, [], true).then(() => resolve()); } else { this.levelUpStatsContainer.setVisible(false); resolve(); @@ -206,8 +208,8 @@ export default class BattleMessageUiHandler extends MessageUiHandler { highestIv = ivs[s]; } }); - shownStats.push(shownStat); - statsPool.splice(statsPool.indexOf(shownStat), 1); + shownStats.push(shownStat!); // TODO: is the bang correct? + statsPool.splice(statsPool.indexOf(shownStat!), 1); // TODO: is the bang correct? } } else { shownStats = stats; @@ -228,7 +230,7 @@ export default class BattleMessageUiHandler extends MessageUiHandler { } getIvDescriptor(value: integer, typeIv: integer, pokemonId: integer): string { - const starterSpecies = this.scene.getPokemonById(pokemonId).species.getRootSpeciesId(true); + const starterSpecies = this.scene.getPokemonById(pokemonId)!.species.getRootSpeciesId(true); // TODO: is this bang correct? const starterIvs: number[] = this.scene.gameData.dexData[starterSpecies].ivs; const uiTheme = (this.scene as BattleScene).uiTheme; // Assuming uiTheme is accessible diff --git a/src/ui/bgm-bar.ts b/src/ui/bgm-bar.ts index 076eec26a8e..936ab91d8fa 100644 --- a/src/ui/bgm-bar.ts +++ b/src/ui/bgm-bar.ts @@ -24,7 +24,7 @@ export default class BgmBar extends Phaser.GameObjects.Container { this.defaultWidth = 230; this.defaultHeight = 100; - this.bg = this.scene.add.nineslice(-5, -5, "bgm_bar", null, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); + this.bg = this.scene.add.nineslice(-5, -5, "bgm_bar", undefined, this.defaultWidth, this.defaultHeight, 0, 0, 10, 10); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/candy-bar.ts b/src/ui/candy-bar.ts index 3a9f180dd59..5496ce9afce 100644 --- a/src/ui/candy-bar.ts +++ b/src/ui/candy-bar.ts @@ -11,8 +11,8 @@ export default class CandyBar extends Phaser.GameObjects.Container { private countText: Phaser.GameObjects.Text; private speciesId: Species; - private tween: Phaser.Tweens.Tween; - private autoHideTimer: NodeJS.Timeout; + private tween: Phaser.Tweens.Tween | null; + private autoHideTimer: NodeJS.Timeout | null; public shown: boolean; @@ -21,7 +21,7 @@ export default class CandyBar extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", null, 8, 18, 21, 5, 6, 4); + this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", undefined, 8, 18, 21, 5, 6, 4); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/challenges-select-ui-handler.ts b/src/ui/challenges-select-ui-handler.ts index 6dcc359ef31..1200b3f3b40 100644 --- a/src/ui/challenges-select-ui-handler.ts +++ b/src/ui/challenges-select-ui-handler.ts @@ -30,12 +30,12 @@ export default class GameChallengesUiHandler extends UiHandler { private challengeLabels: Array<{ label: Phaser.GameObjects.Text, value: Phaser.GameObjects.Text }>; private monoTypeValue: Phaser.GameObjects.Sprite; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private startCursor: Phaser.GameObjects.NineSlice; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? } setup() { @@ -110,7 +110,7 @@ export default class GameChallengesUiHandler extends UiHandler { startText.setOrigin(0, 0); startText.setPositionRelative(startBg, 8, 4); - this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 18) - 10, 16, 1, 1, 1, 1); + this.startCursor = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 18) - 10, 16, 1, 1, 1, 1); this.startCursor.setName("9s-start-cursor"); this.startCursor.setOrigin(0, 0); this.startCursor.setPositionRelative(startBg, 4, 4); @@ -258,7 +258,7 @@ export default class GameChallengesUiHandler extends UiHandler { } else { this.scene.clearPhaseQueue(); this.scene.pushPhase(new TitlePhase(this.scene)); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); } success = true; } else if (button === Button.SUBMIT || button === Button.ACTION) { @@ -267,7 +267,7 @@ export default class GameChallengesUiHandler extends UiHandler { const totalMinDifficulty = this.scene.gameMode.challenges.reduce((v, c) => v + c.getMinDifficulty(), 0); if (totalDifficulty >= totalMinDifficulty) { this.scene.unshiftPhase(new SelectStarterPhase(this.scene)); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); success = true; } else { success = false; @@ -354,7 +354,7 @@ export default class GameChallengesUiHandler extends UiHandler { let ret = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 9) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 9) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.valuesContainer.add(this.cursorObj); } diff --git a/src/ui/command-ui-handler.ts b/src/ui/command-ui-handler.ts index f083acd2f5b..11814a25240 100644 --- a/src/ui/command-ui-handler.ts +++ b/src/ui/command-ui-handler.ts @@ -17,7 +17,7 @@ export enum Command { export default class CommandUiHandler extends UiHandler { private commandsContainer: Phaser.GameObjects.Container; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; protected fieldIndex: integer = 0; protected cursor2: integer = 0; diff --git a/src/ui/daily-run-scoreboard.ts b/src/ui/daily-run-scoreboard.ts index 212d7b2a66c..b535a94d35c 100644 --- a/src/ui/daily-run-scoreboard.ts +++ b/src/ui/daily-run-scoreboard.ts @@ -59,14 +59,14 @@ export class DailyRunScoreboard extends Phaser.GameObjects.Container { } setup() { - const titleWindow = addWindow(this.scene, 0, 0, 114, 18, false, false, null, null, WindowVariant.THIN); + const titleWindow = addWindow(this.scene, 0, 0, 114, 18, false, false, undefined, undefined, WindowVariant.THIN); this.add(titleWindow); this.titleLabel = addTextObject(this.scene, titleWindow.displayWidth / 2, titleWindow.displayHeight / 2, i18next.t("menu:loading"), TextStyle.WINDOW, { fontSize: "64px" }); this.titleLabel.setOrigin(0.5, 0.5); this.add(this.titleLabel); - const window = addWindow(this.scene, 0, 17, 114, 118, false, false, null, null, WindowVariant.THIN); + const window = addWindow(this.scene, 0, 17, 114, 118, false, false, undefined, undefined, WindowVariant.THIN); this.add(window); this.rankingsContainer = this.scene.add.container(6, 21); diff --git a/src/ui/dropdown.ts b/src/ui/dropdown.ts index 80935d16a71..f1723559b57 100644 --- a/src/ui/dropdown.ts +++ b/src/ui/dropdown.ts @@ -233,9 +233,9 @@ export class DropDownOption extends Phaser.GameObjects.Container { /** * @returns the x position to use for the current label depending on if it has a sprite or not */ - getCurrentLabelX(): number { + getCurrentLabelX(): number | undefined { if (this.labels[this.currentLabelIndex].sprite) { - return this.labels[this.currentLabelIndex].sprite.x; + return this.labels[this.currentLabelIndex].sprite?.x; } return this.text.x; } @@ -313,7 +313,7 @@ export class DropDown extends Phaser.GameObjects.Container { } }); - this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, null, null, WindowVariant.XTHIN); + this.window = addWindow(scene, 0, 0, optionWidth, options[options.length - 1].y + optionHeight + optionPaddingY, false, false, undefined, undefined, WindowVariant.XTHIN); this.add(this.window); this.add(options); this.add(this.cursorObj); @@ -465,7 +465,7 @@ export class DropDown extends Phaser.GameObjects.Container { * - the settings dictionary is like this { val: any, state: DropDownState, cursor: boolean, dir: SortDirection } */ private getSettings(): any[] { - const settings = []; + const settings : any[] = []; for (let i = 0; i < this.options.length; i++) { settings.push({ val: this.options[i].val, state: this.options[i].state , cursor: (this.cursor === i), dir: this.options[i].dir }); } @@ -570,7 +570,7 @@ export class DropDown extends Phaser.GameObjects.Container { const optionWidth = this.options[i].getWidth(); if (optionWidth > maxWidth) { maxWidth = optionWidth; - x = this.options[i].getCurrentLabelX(); + x = this.options[i].getCurrentLabelX() ?? 0; } } this.window.width = maxWidth + x - this.window.x + 6; diff --git a/src/ui/egg-gacha-ui-handler.ts b/src/ui/egg-gacha-ui-handler.ts index 61031ba1c5c..92f3aaea1a4 100644 --- a/src/ui/egg-gacha-ui-handler.ts +++ b/src/ui/egg-gacha-ui-handler.ts @@ -60,7 +60,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { this.eggGachaContainer.setVisible(false); ui.add(this.eggGachaContainer); - const bg = this.scene.add.nineslice(0, 0, "default_bg", null, 320, 180, 0, 0, 16, 0); + const bg = this.scene.add.nineslice(0, 0, "default_bg", undefined, 320, 180, 0, 0, 16, 0); bg.setOrigin(0, 0); this.eggGachaContainer.add(bg); @@ -99,7 +99,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { const gachaInfoContainer = this.scene.add.container(160, 46); - const currentLanguage = i18next.resolvedLanguage; + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? let gachaTextStyle = TextStyle.WINDOW_ALT; let gachaX = 4; let gachaY = 0; @@ -217,7 +217,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { { multiplier: multiplierOne, description: `25 ${i18next.t("egg:pulls")}`, icon: getVoucherTypeIcon(VoucherType.GOLDEN) } ]; - const { resolvedLanguage } = i18next; + const resolvedLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? const pullOptionsText = pullOptions.map(option =>{ const desc = option.description.split(" "); if (desc[0].length < 2) { @@ -335,7 +335,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { return Utils.fixedInt(delay); } - pull(pullCount?: integer, count?: integer, eggs?: Egg[]): void { + pull(pullCount: integer = 0, count: integer = 0, eggs?: Egg[]): void { if (Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE && !count) { pullCount = Overrides.EGG_GACHA_PULL_COUNT_OVERRIDE; } @@ -345,10 +345,10 @@ export default class EggGachaUiHandler extends MessageUiHandler { const doPull = () => { if (this.transitionCancelled) { - return this.showSummary(eggs); + return this.showSummary(eggs!); } - const egg = this.scene.add.sprite(127, 75, "egg", `egg_${eggs[count].getKey()}`); + const egg = this.scene.add.sprite(127, 75, "egg", `egg_${eggs![count].getKey()}`); egg.setScale(0.5); this.gachaContainers[this.gachaCursor].add(egg); @@ -391,7 +391,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { if (++count < pullCount) { this.pull(pullCount, count, eggs); } else { - this.showSummary(eggs); + this.showSummary(eggs!); } } }); @@ -584,7 +584,7 @@ export default class EggGachaUiHandler extends MessageUiHandler { } showError(text: string): void { - this.showText(text, null, () => this.showText(this.defaultText), Utils.fixedInt(1500)); + this.showText(text, undefined, () => this.showText(this.defaultText), Utils.fixedInt(1500)); } setTransitioning(transitioning: boolean): void { diff --git a/src/ui/egg-hatch-scene-handler.ts b/src/ui/egg-hatch-scene-handler.ts index 0e247da60ab..f567861e0b7 100644 --- a/src/ui/egg-hatch-scene-handler.ts +++ b/src/ui/egg-hatch-scene-handler.ts @@ -36,7 +36,7 @@ export default class EggHatchSceneHandler extends UiHandler { show(_args: any[]): boolean { super.show(_args); - this.getUi().showText(null, 0); + this.getUi().showText("", 0); this.scene.setModifiersVisible(false); diff --git a/src/ui/evolution-scene-handler.ts b/src/ui/evolution-scene-handler.ts index 64d190c4d3f..ffbd06afde3 100644 --- a/src/ui/evolution-scene-handler.ts +++ b/src/ui/evolution-scene-handler.ts @@ -83,6 +83,8 @@ export default class EvolutionSceneHandler extends MessageUiHandler { } } } + + return false; } setCursor(_cursor: integer): boolean { diff --git a/src/ui/fight-ui-handler.ts b/src/ui/fight-ui-handler.ts index ed520512443..8279ab72a70 100644 --- a/src/ui/fight-ui-handler.ts +++ b/src/ui/fight-ui-handler.ts @@ -21,7 +21,7 @@ export default class FightUiHandler extends UiHandler { private powerText: Phaser.GameObjects.Text; private accuracyLabel: Phaser.GameObjects.Text; private accuracyText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; private moveCategoryIcon: Phaser.GameObjects.Sprite; protected fieldIndex: integer = 0; @@ -176,7 +176,7 @@ export default class FightUiHandler extends UiHandler { const hasMove = cursor < moveset.length; if (hasMove) { - const pokemonMove = moveset[cursor]; + const pokemonMove = moveset[cursor]!; // TODO: is the bang correct? this.typeIcon.setTexture(`types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[pokemonMove.getMove().type].toLowerCase()).setScale(0.8); this.moveCategoryIcon.setTexture("categories", MoveCategory[pokemonMove.getMove().category].toLowerCase()).setScale(1.0); @@ -246,7 +246,7 @@ export default class FightUiHandler extends UiHandler { moveText.setName("text-empty-move"); if (moveIndex < moveset.length) { - const pokemonMove = moveset[moveIndex]; + const pokemonMove = moveset[moveIndex]!; // TODO is the bang correct? moveText.setText(pokemonMove.getName()); moveText.setName(pokemonMove.getName()); moveText.setColor(this.getMoveColor(pokemon, pokemonMove) ?? moveText.style.color); @@ -273,7 +273,7 @@ export default class FightUiHandler extends UiHandler { const moveColors = opponents.map((opponent) => { return opponent.getMoveEffectiveness(pokemon, pokemonMove); - }).sort((a, b) => b - a).map((effectiveness) => { + }).filter((eff) => !!eff).sort((a, b) => b - a).map((effectiveness) => { return getTypeDamageMultiplierColor(effectiveness, "offense"); }); diff --git a/src/ui/filter-bar.ts b/src/ui/filter-bar.ts index 52266a3a06b..31d7c562da2 100644 --- a/src/ui/filter-bar.ts +++ b/src/ui/filter-bar.ts @@ -31,7 +31,7 @@ export class FilterBar extends Phaser.GameObjects.Container { this.width = width; this.height = height; - this.window = addWindow(scene, 0, 0, width, height, false, false, null, null, WindowVariant.THIN); + this.window = addWindow(scene, 0, 0, width, height, false, false, undefined, undefined, WindowVariant.THIN); this.add(this.window); this.cursorObj = this.scene.add.image(1, 1, "cursor"); diff --git a/src/ui/form-modal-ui-handler.ts b/src/ui/form-modal-ui-handler.ts index 2c576a0fbaa..a4539c2369f 100644 --- a/src/ui/form-modal-ui-handler.ts +++ b/src/ui/form-modal-ui-handler.ts @@ -17,7 +17,7 @@ export abstract class FormModalUiHandler extends ModalUiHandler { protected inputContainers: Phaser.GameObjects.Container[]; protected inputs: InputText[]; protected errorMessage: Phaser.GameObjects.Text; - protected submitAction: Function; + protected submitAction: Function | null; protected tween: Phaser.Tweens.Tween; constructor(scene: BattleScene, mode?: Mode) { diff --git a/src/ui/game-stats-ui-handler.ts b/src/ui/game-stats-ui-handler.ts index 8f1e8890a76..3d6d8081d4e 100644 --- a/src/ui/game-stats-ui-handler.ts +++ b/src/ui/game-stats-ui-handler.ts @@ -219,7 +219,7 @@ export default class GameStatsUiHandler extends UiHandler { private statValues: Phaser.GameObjects.Text[]; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.statLabels = []; this.statValues = []; @@ -299,7 +299,7 @@ export default class GameStatsUiHandler extends UiHandler { const statKeys = Object.keys(displayStats).slice(this.cursor * 2, this.cursor * 2 + 18); statKeys.forEach((key, s) => { const stat = displayStats[key] as DisplayStat; - const value = stat.sourceFunc(this.scene.gameData); + const value = stat.sourceFunc!(this.scene.gameData); // TODO: is this bang correct? this.statLabels[s].setText(!stat.hidden || isNaN(parseInt(value)) || parseInt(value) ? i18next.t(`gameStatsUiHandler:${stat.label_key}`) : "???"); this.statValues[s].setText(value); }); diff --git a/src/ui/login-form-ui-handler.ts b/src/ui/login-form-ui-handler.ts index 971bef6ea6b..450c583a26c 100644 --- a/src/ui/login-form-ui-handler.ts +++ b/src/ui/login-form-ui-handler.ts @@ -130,7 +130,7 @@ export default class LoginFormUiHandler extends FormModalUiHandler { .then(response => { if (response.hasOwnProperty("token")) { Utils.setCookie(Utils.sessionIdKey, response.token); - originalLoginAction(); + originalLoginAction && originalLoginAction(); } else { onFail(response); } diff --git a/src/ui/menu-ui-handler.ts b/src/ui/menu-ui-handler.ts index c0c5e69b893..27db60aa5da 100644 --- a/src/ui/menu-ui-handler.ts +++ b/src/ui/menu-ui-handler.ts @@ -38,7 +38,7 @@ export default class MenuUiHandler extends MessageUiHandler { private menuBg: Phaser.GameObjects.NineSlice; protected optionSelectText: Phaser.GameObjects.Text; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; private excludedMenus: () => ConditionalMenu[]; private menuOptions: MenuOptions[]; @@ -52,7 +52,7 @@ export default class MenuUiHandler extends MessageUiHandler { constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.excludedMenus = () => [ { condition: [Mode.COMMAND, Mode.TITLE].includes(mode ?? Mode.TITLE), options: [ MenuOptions.EGG_GACHA, MenuOptions.EGG_LIST] }, @@ -69,7 +69,7 @@ export default class MenuUiHandler extends MessageUiHandler { setup(): void { const ui = this.getUi(); // wiki url directs based on languges available on wiki - const lang = i18next.resolvedLanguage.substring(0,2); + const lang = i18next.resolvedLanguage?.substring(0,2)!; // TODO: is this bang correct? if (["de", "fr", "ko", "zh"].includes(lang)) { wikiUrl = `https://wiki.pokerogue.net/${lang}:start`; } @@ -147,7 +147,7 @@ export default class MenuUiHandler extends MessageUiHandler { this.menuContainer.add(this.menuMessageBoxContainer); - const manageDataOptions = []; + const manageDataOptions: any[] = []; // TODO: proper type const confirmSlot = (message: string, slotFilter: (i: integer) => boolean, callback: (i: integer) => void) => { ui.revertMode(); @@ -159,7 +159,7 @@ export default class MenuUiHandler extends MessageUiHandler { handler: () => { callback(i); ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); return true; } }; @@ -167,7 +167,7 @@ export default class MenuUiHandler extends MessageUiHandler { label: i18next.t("menuUiHandler:cancel"), handler: () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); return true; } }]), @@ -259,7 +259,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Wiki", handler: () => { - window.open(wikiUrl, "_blank").focus(); + window.open(wikiUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -267,7 +267,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Discord", handler: () => { - window.open(discordUrl, "_blank").focus(); + window.open(discordUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -275,7 +275,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "GitHub", handler: () => { - window.open(githubUrl, "_blank").focus(); + window.open(githubUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -283,7 +283,7 @@ export default class MenuUiHandler extends MessageUiHandler { { label: "Reddit", handler: () => { - window.open(redditUrl, "_blank").focus(); + window.open(redditUrl, "_blank")?.focus(); return true; }, keepOpen: true @@ -386,7 +386,7 @@ export default class MenuUiHandler extends MessageUiHandler { if (!bypassLogin && !this.manageDataConfig.options.some(o => o.label === i18next.t("menuUiHandler:linkDiscord") || o.label === i18next.t("menuUiHandler:unlinkDiscord"))) { this.manageDataConfig.options.splice(this.manageDataConfig.options.length-1,0, { - label: loggedInUser.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"), + label: loggedInUser?.discordId === "" ? i18next.t("menuUiHandler:linkDiscord") : i18next.t("menuUiHandler:unlinkDiscord"), handler: () => { if (loggedInUser?.discordId === "") { const token = Utils.getCookie(Utils.sessionIdKey); @@ -442,7 +442,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { ui.setOverlayMode(Mode.CONFIRM, () => this.scene.gameData.saveAll(this.scene, true, true, true, true).then(() => this.scene.reset(true)), () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, -98); }); } else { @@ -467,7 +467,7 @@ export default class MenuUiHandler extends MessageUiHandler { ui.showText(i18next.t("menuUiHandler:losingProgressionWarning"), null, () => { ui.setOverlayMode(Mode.CONFIRM, doLogout, () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, -98); }); } else { diff --git a/src/ui/message-ui-handler.ts b/src/ui/message-ui-handler.ts index 05c91ca1643..c870ef13a27 100644 --- a/src/ui/message-ui-handler.ts +++ b/src/ui/message-ui-handler.ts @@ -4,8 +4,8 @@ import { Mode } from "./ui"; import * as Utils from "../utils"; export default abstract class MessageUiHandler extends AwaitableUiHandler { - protected textTimer: Phaser.Time.TimerEvent; - protected textCallbackTimer: Phaser.Time.TimerEvent; + protected textTimer: Phaser.Time.TimerEvent | null; + protected textCallbackTimer: Phaser.Time.TimerEvent | null; public pendingPrompt: boolean; public message: Phaser.GameObjects.Text; @@ -17,15 +17,15 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { this.pendingPrompt = false; } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay); } - showDialogue(text: string, name: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showDialogue(text: string, name?: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { this.showTextInternal(text, delay, callback, callbackDelay, prompt, promptDelay); } - private showTextInternal(text: string, delay: integer, callback: Function, callbackDelay: integer, prompt: boolean, promptDelay: integer) { + private showTextInternal(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null) { if (delay === null || delay === undefined) { delay = 20; } @@ -33,7 +33,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { const delayMap = new Map(); const soundMap = new Map(); const actionPattern = /@(c|d|s)\{(.*?)\}/; - let actionMatch: RegExpExecArray; + let actionMatch: RegExpExecArray | null; while ((actionMatch = actionPattern.exec(text))) { switch (actionMatch[1]) { case "c": @@ -99,7 +99,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { this.textTimer = this.scene.time.addEvent({ delay: delay, callback: () => { - const charIndex = text.length - this.textTimer.repeatCount; + const charIndex = text.length - (this.textTimer?.repeatCount!); // TODO: is this bang correct? const charVar = charVarMap.get(charIndex); const charSound = soundMap.get(charIndex); const charDelay = delayMap.get(charIndex); @@ -111,7 +111,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { if (charSound) { this.scene.playSound(charSound); } - if (callback && !this.textTimer.repeatCount) { + if (callback && !this.textTimer?.repeatCount) { if (callbackDelay && !prompt) { this.textCallbackTimer = this.scene.time.delayedCall(callbackDelay, () => { if (this.textCallbackTimer) { @@ -126,11 +126,11 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { } }; if (charDelay) { - this.textTimer.paused = true; + this.textTimer!.paused = true; // TODO: is the bang correct? this.scene.tweens.addCounter({ duration: Utils.getFrameMs(charDelay), onComplete: () => { - this.textTimer.paused = false; + this.textTimer!.paused = false; // TODO: is the bang correct? advance(); } }); @@ -151,7 +151,7 @@ export default abstract class MessageUiHandler extends AwaitableUiHandler { } } - showPrompt(callback: Function, callbackDelay: integer) { + showPrompt(callback?: Function | null, callbackDelay?: integer | null) { const wrappedTextLines = this.message.runWordWrap(this.message.text).split(/\n/g); const textLinesCount = wrappedTextLines.length; const lastTextLine = wrappedTextLines[wrappedTextLines.length - 1]; diff --git a/src/ui/modal-ui-handler.ts b/src/ui/modal-ui-handler.ts index 85b20e423c3..5aac6ac194e 100644 --- a/src/ui/modal-ui-handler.ts +++ b/src/ui/modal-ui-handler.ts @@ -17,7 +17,7 @@ export abstract class ModalUiHandler extends UiHandler { protected buttonBgs: Phaser.GameObjects.NineSlice[]; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.buttonContainers = []; this.buttonBgs = []; diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts index 76cda280720..016708027ca 100644 --- a/src/ui/modifier-select-ui-handler.ts +++ b/src/ui/modifier-select-ui-handler.ts @@ -35,7 +35,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { public options: ModifierOption[]; public shopOptionsRows: ModifierOption[][]; - private cursorObj: Phaser.GameObjects.Image; + private cursorObj: Phaser.GameObjects.Image | null; constructor(scene: BattleScene) { super(scene, Mode.CONFIRM); @@ -53,9 +53,12 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { const canvas = document.createElement("canvas"); const context = canvas.getContext("2d"); const styleOptions = getTextStyleOptions(TextStyle.PARTY, (this.scene as BattleScene).uiTheme).styleOptions; - context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily; - this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width; - this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width; + + if (context) { + context.font = styleOptions.fontSize + "px " + styleOptions.fontFamily; + this.transferButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:transfer")).width; + this.checkButtonWidth = context.measureText(i18next.t("modifierSelectUiHandler:checkTeam")).width; + } this.transferButtonContainer = this.scene.add.container((this.scene.game.canvas.width - this.checkButtonWidth) / 6 - 21, -64); this.transferButtonContainer.setName("transfer-btn"); @@ -394,7 +397,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler { } const type = options[this.cursor].modifierTypeOption.type; - ui.showText(type.getDescription(this.scene)); + type && ui.showText(type.getDescription(this.scene)); if (type instanceof TmModifierType) { // prepare the move overlay to be shown with the toggle this.moveInfoOverlay.show(allMoves[type.moveId]); @@ -574,7 +577,7 @@ class ModifierOption extends Phaser.GameObjects.Container { this.add(this.itemContainer); const getItem = () => { - const item = this.scene.add.sprite(0, 0, "items", this.modifierTypeOption.type.iconImage); + const item = this.scene.add.sprite(0, 0, "items", this.modifierTypeOption.type?.iconImage); return item; }; @@ -587,10 +590,10 @@ class ModifierOption extends Phaser.GameObjects.Container { this.itemContainer.add(this.itemTint); } - this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type.name, TextStyle.PARTY, { align: "center" }); + this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type?.name!, TextStyle.PARTY, { align: "center" }); // TODO: is this bang correct? this.itemText.setOrigin(0.5, 0); this.itemText.setAlpha(0); - this.itemText.setTint(getModifierTierTextTint(this.modifierTypeOption.type.tier)); + this.itemText.setTint(this.modifierTypeOption.type?.tier ? getModifierTierTextTint(this.modifierTypeOption.type?.tier) : undefined); this.add(this.itemText); if (this.modifierTypeOption.cost) { @@ -722,7 +725,7 @@ class ModifierOption extends Phaser.GameObjects.Container { } getPbAtlasKey(tierOffset: integer = 0) { - return getPokeballAtlasKey((this.modifierTypeOption.type.tier + tierOffset) as integer as PokeballType); + return getPokeballAtlasKey((this.modifierTypeOption.type?.tier! + tierOffset) as integer as PokeballType); // TODO: is this bang correct? } updateCostText(): void { diff --git a/src/ui/move-info-overlay.ts b/src/ui/move-info-overlay.ts index 3b947cb842d..ded19b01a12 100644 --- a/src/ui/move-info-overlay.ts +++ b/src/ui/move-info-overlay.ts @@ -30,7 +30,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem private move: Move; private desc: Phaser.GameObjects.Text; - private descScroll : Phaser.Tweens.Tween = null; + private descScroll : Phaser.Tweens.Tween | null = null; private val: Phaser.GameObjects.Container; private pp: Phaser.GameObjects.Text; @@ -131,7 +131,7 @@ export default class MoveInfoOverlay extends Phaser.GameObjects.Container implem // show this component with infos for the specific move show(move : Move):boolean { if (!(this.scene as BattleScene).enableMoveInfo) { - return; // move infos have been disabled + return false; // move infos have been disabled // TODO:: is `false` correct? i used to be `undeefined` } this.move = move; this.pow.setText(move.power >= 0 ? move.power.toString() : "---"); diff --git a/src/ui/party-exp-bar.ts b/src/ui/party-exp-bar.ts index 506b8b5c825..d2521225375 100644 --- a/src/ui/party-exp-bar.ts +++ b/src/ui/party-exp-bar.ts @@ -7,7 +7,7 @@ export default class PartyExpBar extends Phaser.GameObjects.Container { private pokemonIcon: Phaser.GameObjects.Container; private expText: Phaser.GameObjects.Text; - private tween: Phaser.Tweens.Tween; + private tween: Phaser.Tweens.Tween | null; public shown: boolean; @@ -16,7 +16,7 @@ export default class PartyExpBar extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", null, 8, 18, 21, 5, 6, 4); + this.bg = this.scene.add.nineslice(0, 0, "party_exp_bar", undefined, 8, 18, 21, 5, 6, 4); this.bg.setOrigin(0, 0); this.add(this.bg); diff --git a/src/ui/party-ui-handler.ts b/src/ui/party-ui-handler.ts index 7b77b71f4ec..3a9b3463ef1 100644 --- a/src/ui/party-ui-handler.ts +++ b/src/ui/party-ui-handler.ts @@ -118,9 +118,9 @@ export enum PartyOption { export type PartySelectCallback = (cursor: integer, option: PartyOption) => void; export type PartyModifierTransferSelectCallback = (fromCursor: integer, index: integer, itemQuantity?: integer, toCursor?: integer) => void; export type PartyModifierSpliceSelectCallback = (fromCursor: integer, toCursor?: integer) => void; -export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string; -export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string; -export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string; +export type PokemonSelectFilter = (pokemon: PlayerPokemon) => string | null; +export type PokemonModifierTransferSelectFilter = (pokemon: PlayerPokemon, modifier: PokemonHeldItemModifier) => string | null; +export type PokemonMoveSelectFilter = (pokemonMove: PokemonMove) => string | null; export default class PartyUiHandler extends MessageUiHandler { private partyUiMode: PartyUiMode; @@ -142,7 +142,7 @@ export default class PartyUiHandler extends MessageUiHandler { /** This is only public for test/ui/transfer-item.test.ts */ public optionsContainer: Phaser.GameObjects.Container; private optionsBg: Phaser.GameObjects.NineSlice; - private optionsCursorObj: Phaser.GameObjects.Image; + private optionsCursorObj: Phaser.GameObjects.Image | null; private options: integer[]; private transferMode: boolean; @@ -156,7 +156,7 @@ export default class PartyUiHandler extends MessageUiHandler { private transferAll: boolean; private lastCursor: integer = 0; - private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback; + private selectCallback: PartySelectCallback | PartyModifierTransferSelectCallback | null; private selectFilter: PokemonSelectFilter | PokemonModifierTransferSelectFilter; private moveSelectFilter: PokemonMoveSelectFilter; private tmMoveId: Moves; @@ -377,17 +377,17 @@ export default class PartyUiHandler extends MessageUiHandler { this.moveInfoOverlay.clear(); const filterResult = (this.selectFilter as PokemonSelectFilter)(pokemon); if (filterResult === null) { - this.selectCallback(this.cursor, option); + this.selectCallback?.(this.cursor, option); this.clearOptions(); } else { this.clearOptions(); - this.showText(filterResult as string, null, () => this.showText(null, 0), null, true); + this.showText(filterResult as string, undefined, () => this.showText(null, 0), undefined, true); } ui.playSelect(); return true; } else if ((option !== PartyOption.SUMMARY && option !== PartyOption.UNPAUSE_EVOLUTION && option !== PartyOption.UNSPLICE && option !== PartyOption.RELEASE && option !== PartyOption.CANCEL && option !== PartyOption.RENAME) || (option === PartyOption.RELEASE && this.partyUiMode === PartyUiMode.RELEASE)) { - let filterResult: string; + let filterResult: string | null; const getTransferrableItemsFromPokemon = (pokemon: PlayerPokemon) => this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[]; if (option !== PartyOption.TRANSFER && option !== PartyOption.SPLICE) { @@ -396,7 +396,7 @@ export default class PartyUiHandler extends MessageUiHandler { filterResult = this.FilterChallengeLegal(pokemon); } if (filterResult === null && this.partyUiMode === PartyUiMode.MOVE_MODIFIER) { - filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]); + filterResult = this.moveSelectFilter(pokemon.moveset[this.optionsCursor]!); // TODO: is this bang correct? } } else { filterResult = (this.selectFilter as PokemonModifierTransferSelectFilter)(pokemon, getTransferrableItemsFromPokemon(this.scene.getParty()[this.transferCursor])[this.transferOptionCursor]); @@ -451,7 +451,7 @@ export default class PartyUiHandler extends MessageUiHandler { return true; } else { this.clearOptions(); - this.showText(filterResult as string, null, () => this.showText(null, 0), null, true); + this.showText(filterResult as string, undefined, () => this.showText(null, 0), undefined, true); } } else if (option === PartyOption.SUMMARY) { ui.playSelect(); @@ -461,18 +461,18 @@ export default class PartyUiHandler extends MessageUiHandler { this.clearOptions(); ui.playSelect(); pokemon.pauseEvolutions = false; - this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), null, () => this.showText(null, 0), null, true); + this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), undefined, () => this.showText(null, 0), null, true); } else if (option === PartyOption.UNSPLICE) { this.clearOptions(); ui.playSelect(); - this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies.name, pokemonName: pokemon.name }), null, () => { + this.showText(i18next.t("partyUiHandler:unspliceConfirmation", { fusionName: pokemon.fusionSpecies?.name, pokemonName: pokemon.name }), null, () => { ui.setModeWithoutClear(Mode.CONFIRM, () => { const fusionName = pokemon.name; pokemon.unfuse().then(() => { this.clearPartySlots(); this.populatePartySlots(); ui.setMode(Mode.PARTY); - this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.name }), null, () => { + this.showText(i18next.t("partyUiHandler:wasReverted", { fusionName: fusionName, pokemonName: pokemon.name }), undefined, () => { ui.setMode(Mode.PARTY); this.showText(null, 0); }, null, true); @@ -732,7 +732,7 @@ export default class PartyUiHandler extends MessageUiHandler { return changed; } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer) { + showText(text: string | null, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean, promptDelay?: integer) { if (text === null) { text = defaultMessage; } @@ -792,7 +792,7 @@ export default class PartyUiHandler extends MessageUiHandler { const learnableLevelMoves = this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER ? pokemon.getLearnableLevelMoves() - : null; + : []; if (this.partyUiMode === PartyUiMode.REMEMBER_MOVE_MODIFIER && learnableLevelMoves?.length) { // show the move overlay with info for the first move @@ -802,7 +802,7 @@ export default class PartyUiHandler extends MessageUiHandler { const itemModifiers = this.partyUiMode === PartyUiMode.MODIFIER_TRANSFER ? this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier && m.isTransferrable && m.pokemonId === pokemon.id) as PokemonHeldItemModifier[] - : null; + : []; if (this.options.length) { this.options.splice(0, this.options.length); @@ -810,7 +810,7 @@ export default class PartyUiHandler extends MessageUiHandler { this.eraseOptionsCursor(); } - let formChangeItemModifiers: PokemonFormChangeItemModifier[]; + let formChangeItemModifiers: PokemonFormChangeItemModifier[] | undefined; if (this.partyUiMode !== PartyUiMode.MOVE_MODIFIER && this.partyUiMode !== PartyUiMode.REMEMBER_MOVE_MODIFIER && (this.transferMode || this.partyUiMode !== PartyUiMode.MODIFIER_TRANSFER)) { switch (this.partyUiMode) { @@ -951,7 +951,7 @@ export default class PartyUiHandler extends MessageUiHandler { case PartyOption.MOVE_2: case PartyOption.MOVE_3: case PartyOption.MOVE_4: - const move = pokemon.moveset[option - PartyOption.MOVE_1]; + const move = pokemon.moveset[option - PartyOption.MOVE_1]!; // TODO: is the bang correct? if (this.showMovePp) { const maxPP = move.getMovePp(); const currPP = maxPP - move.ppUsed; @@ -1060,7 +1060,7 @@ export default class PartyUiHandler extends MessageUiHandler { if (this.partyUiMode === PartyUiMode.RELEASE) { const selectCallback = this.selectCallback; this.selectCallback = null; - selectCallback(this.cursor, PartyOption.RELEASE); + selectCallback && selectCallback(this.cursor, PartyOption.RELEASE); } this.showText(null, 0); }, null, true); diff --git a/src/ui/pokeball-tray.ts b/src/ui/pokeball-tray.ts index 00a8cdadc97..cf3b24c4d11 100644 --- a/src/ui/pokeball-tray.ts +++ b/src/ui/pokeball-tray.ts @@ -15,7 +15,7 @@ export default class PokeballTray extends Phaser.GameObjects.Container { } setup(): void { - this.bg = this.scene.add.nineslice(0, 0, `pb_tray_overlay_${this.player ? "player" : "enemy"}`, null, 104, 4, 48, 8, 0, 0); + this.bg = this.scene.add.nineslice(0, 0, `pb_tray_overlay_${this.player ? "player" : "enemy"}`, undefined, 104, 4, 48, 8, 0, 0); this.bg.setOrigin(this.player ? 1 : 0, 0); this.add(this.bg); diff --git a/src/ui/pokemon-icon-anim-handler.ts b/src/ui/pokemon-icon-anim-handler.ts index 99fe39c4184..d6796d5cb5d 100644 --- a/src/ui/pokemon-icon-anim-handler.ts +++ b/src/ui/pokemon-icon-anim-handler.ts @@ -21,7 +21,9 @@ export default class PokemonIconAnimHandler { const value = tween.getValue(); this.toggled = !!value; for (const i of this.icons.keys()) { - i.y += this.getModeYDelta(this.icons.get(i)) * (this.toggled ? 1 : -1); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y += delta * (this.toggled ? 1 : -1); } }; scene.tweens.addCounter({ @@ -56,7 +58,7 @@ export default class PokemonIconAnimHandler { } if (this.toggled) { const lastYDelta = this.icons.has(i) - ? this.icons.get(i) + ? this.icons.get(i)! : 0; const yDelta = this.getModeYDelta(mode); i.y += yDelta + lastYDelta; @@ -71,7 +73,9 @@ export default class PokemonIconAnimHandler { } for (const i of icons) { if (this.toggled) { - i.y -= this.getModeYDelta(this.icons.get(i)); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y -= delta; } this.icons.delete(i); } @@ -80,7 +84,9 @@ export default class PokemonIconAnimHandler { removeAll(): void { for (const i of this.icons.keys()) { if (this.toggled) { - i.y -= this.getModeYDelta(this.icons.get(i)); + const icon = this.icons.get(i); + const delta = icon ? this.getModeYDelta(icon) : 0; + i.y -= delta; } this.icons.delete(i); } diff --git a/src/ui/pokemon-info-container.ts b/src/ui/pokemon-info-container.ts index b2ee5a9164a..edb85ecff7a 100644 --- a/src/ui/pokemon-info-container.ts +++ b/src/ui/pokemon-info-container.ts @@ -80,8 +80,8 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { setup(): void { this.setName("pkmn-info"); - const currentLanguage = i18next.resolvedLanguage; - const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage?.includes(lang))!; // TODO: is this bang correct? const textSettings = languageSettings[langSettingKey]; const infoBg = addWindow(this.scene, 0, 0, this.infoWindowWidth, 132); infoBg.setOrigin(0.5, 0.5); @@ -243,7 +243,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { this.pokemonFormText.setText(formName.length > this.numCharsBeforeCutoff ? formName.substring(0, this.numCharsBeforeCutoff - 3) + "..." : formName); if (formName.length > this.numCharsBeforeCutoff) { this.pokemonFormText.setInteractive(new Phaser.Geom.Rectangle(0, 0, this.pokemonFormText.width, this.pokemonFormText.height), Phaser.Geom.Rectangle.Contains); - this.pokemonFormText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, pokemon.species.forms?.[pokemon.formIndex]?.formName, true)); + this.pokemonFormText.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", pokemon.species.forms?.[pokemon.formIndex]?.formName, true)); this.pokemonFormText.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } else { this.pokemonFormText.disableInteractive(); @@ -302,7 +302,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.pokemonShinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); + this.pokemonShinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); this.pokemonShinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); const newShiny = BigInt(1 << (pokemon.shiny ? 1 : 0)); @@ -324,11 +324,11 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } const starterSpeciesId = pokemon.species.getRootSpeciesId(); - const originalIvs: integer[] = this.scene.gameData.dexData[starterSpeciesId].caughtAttr + const originalIvs: integer[] | null = this.scene.gameData.dexData[starterSpeciesId].caughtAttr ? this.scene.gameData.dexData[starterSpeciesId].ivs : null; - this.statsContainer.updateIvs(pokemon.ivs, originalIvs); + this.statsContainer.updateIvs(pokemon.ivs, originalIvs!); // TODO: is this bang correct? this.scene.tweens.add({ targets: this, @@ -352,7 +352,7 @@ export default class PokemonInfoContainer extends Phaser.GameObjects.Container { } for (let m = 0; m < 4; m++) { - const move = m < pokemon.moveset.length ? pokemon.moveset[m].getMove() : null; + const move = m < pokemon.moveset.length && pokemon.moveset[m] ? pokemon.moveset[m]!.getMove() : null; this.pokemonMoveBgs[m].setFrame(Type[move ? move.type : Type.UNKNOWN].toString().toLowerCase()); this.pokemonMoveLabels[m].setText(move ? move.name : "-"); this.pokemonMovesContainers[m].setVisible(!!move); diff --git a/src/ui/registration-form-ui-handler.ts b/src/ui/registration-form-ui-handler.ts index d5e7f239378..733aab79b05 100644 --- a/src/ui/registration-form-ui-handler.ts +++ b/src/ui/registration-form-ui-handler.ts @@ -92,7 +92,7 @@ export default class RegistrationFormUiHandler extends FormModalUiHandler { .then(response => { if (response.hasOwnProperty("token")) { Utils.setCookie(Utils.sessionIdKey, response.token); - originalRegistrationAction(); + originalRegistrationAction && originalRegistrationAction(); } else { onFail(response); } diff --git a/src/ui/save-slot-select-ui-handler.ts b/src/ui/save-slot-select-ui-handler.ts index 8a81ac4858d..e6ab0d3b3c3 100644 --- a/src/ui/save-slot-select-ui-handler.ts +++ b/src/ui/save-slot-select-ui-handler.ts @@ -29,11 +29,11 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { private sessionSlots: SessionSlot[]; private uiMode: SaveSlotUiMode; - private saveSlotSelectCallback: SaveSlotSelectCallback; + private saveSlotSelectCallback: SaveSlotSelectCallback | null; private scrollCursor: integer = 0; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private sessionSlotsContainerInitialY: number; @@ -106,16 +106,16 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { switch (this.uiMode) { case SaveSlotUiMode.LOAD: this.saveSlotSelectCallback = null; - originalCallback(cursor); + originalCallback && originalCallback(cursor); break; case SaveSlotUiMode.SAVE: const saveAndCallback = () => { const originalCallback = this.saveSlotSelectCallback; this.saveSlotSelectCallback = null; ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); ui.setMode(Mode.MESSAGE); - originalCallback(cursor); + originalCallback && originalCallback(cursor); }; if (this.sessionSlots[cursor].hasData) { ui.showText(i18next.t("saveSlotSelectUiHandler:overwriteData"), null, () => { @@ -129,7 +129,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { }); }, () => { ui.revertMode(); - ui.showText(null, 0); + ui.showText("", 0); }, false, 0, 19, 2000); }); } else if (this.sessionSlots[cursor].hasData === false) { @@ -143,7 +143,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { } } else { this.saveSlotSelectCallback = null; - originalCallback(-1); + originalCallback && originalCallback(-1); success = true; } } else { @@ -202,7 +202,7 @@ export default class SaveSlotSelectUiHandler extends MessageUiHandler { const changed = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", null, 296, 44, 6, 6, 6, 6); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight_thick", undefined, 296, 44, 6, 6, 6, 6); this.cursorObj.setOrigin(0, 0); this.sessionSlotsContainer.add(this.cursorObj); } @@ -292,7 +292,7 @@ class SessionSlot extends Phaser.GameObjects.Container { const icon = this.scene.addPokemonIcon(pokemon, 0, 0, 0, 0); const text = addTextObject(this.scene, 32, 20, `${i18next.t("saveSlotSelectUiHandler:lv")}${Utils.formatLargeNumber(pokemon.level, 1000)}`, TextStyle.PARTY, { fontSize: "54px", color: "#f8f8f8" }); - text.setShadow(0, 0, null); + text.setShadow(0, 0, undefined); text.setStroke("#424242", 14); text.setOrigin(1, 0); @@ -316,9 +316,11 @@ class SessionSlot extends Phaser.GameObjects.Container { if (modifier instanceof PokemonHeldItemModifier) { continue; } - const icon = modifier.getIcon(this.scene, false); - icon.setPosition(24 * visibleModifierIndex, 0); - modifierIconsContainer.add(icon); + const icon = modifier?.getIcon(this.scene, false); + if (icon) { + icon.setPosition(24 * visibleModifierIndex, 0); + modifierIconsContainer.add(icon); + } if (++visibleModifierIndex === 12) { break; } diff --git a/src/ui/settings/abstract-binding-ui-handler.ts b/src/ui/settings/abstract-binding-ui-handler.ts index 809c4ffa2f8..3da555e2c96 100644 --- a/src/ui/settings/abstract-binding-ui-handler.ts +++ b/src/ui/settings/abstract-binding-ui-handler.ts @@ -7,6 +7,8 @@ import {Button} from "#enums/buttons"; import {NavigationManager} from "#app/ui/settings/navigationMenu"; import i18next from "i18next"; +type CancelFn = (succes?: boolean) => boolean; + /** * Abstract class for handling UI elements related to button bindings. */ @@ -35,7 +37,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { protected targetButtonIcon: Phaser.GameObjects.Sprite; // Function to call on cancel or completion of binding. - protected cancelFn: (boolean?) => boolean; + protected cancelFn: CancelFn | null; abstract swapAction(): boolean; protected timeLeftAutoClose: number = 5; @@ -51,7 +53,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { * @param mode - The UI mode. */ constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? } /** @@ -107,7 +109,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { if (this.timeLeftAutoClose >= 0) { this.manageAutoCloseTimer(); } else { - this.cancelFn(); + this.cancelFn && this.cancelFn(); } }, 1000); } @@ -163,7 +165,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { */ processInput(button: Button): boolean { if (this.buttonPressed === null) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } const ui = this.getUi(); let success = false; @@ -177,11 +179,11 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { case Button.ACTION: // Process actions based on current cursor position. if (this.cursor === 0) { - this.cancelFn(); + this.cancelFn && this.cancelFn(); } else { success = this.swapAction(); NavigationManager.getInstance().updateIcons(); - this.cancelFn(success); + this.cancelFn && this.cancelFn(success); } break; } @@ -242,7 +244,7 @@ export default abstract class AbstractBindingUiHandler extends UiHandler { * @param assignedButtonIcon - The icon of the button that is assigned. * @param type - The type of button press. */ - onInputDown(buttonIcon: string, assignedButtonIcon: string, type: string): void { + onInputDown(buttonIcon: string, assignedButtonIcon: string | null, type: string): void { clearTimeout(this.countdownTimer); this.timerText.setText(""); this.newButtonIcon.setTexture(type); diff --git a/src/ui/settings/abstract-control-settings-ui-handler.ts b/src/ui/settings/abstract-control-settings-ui-handler.ts index 9bf0cb40975..1f0c2b98fbd 100644 --- a/src/ui/settings/abstract-control-settings-ui-handler.ts +++ b/src/ui/settings/abstract-control-settings-ui-handler.ts @@ -33,7 +33,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler protected scrollCursor: integer; protected optionCursors: integer[]; - protected cursorObj: Phaser.GameObjects.NineSlice; + protected cursorObj: Phaser.GameObjects.NineSlice | null; protected optionsBg: Phaser.GameObjects.NineSlice; protected actionsBg: Phaser.GameObjects.NineSlice; @@ -74,13 +74,13 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler * @param mode - The UI mode. */ constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.rowsToDisplay = 8; } getLocalStorageSetting(): object { // Retrieve the settings from local storage or use an empty object if none exist. - const settings: object = localStorage.hasOwnProperty(this.localStoragePropertyName) ? JSON.parse(localStorage.getItem(this.localStoragePropertyName)) : {}; + const settings: object = localStorage.hasOwnProperty(this.localStoragePropertyName) ? JSON.parse(localStorage.getItem(this.localStoragePropertyName)!) : {}; // TODO: is this bang correct? return settings; } @@ -442,7 +442,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler switch (button) { case Button.ACTION: if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || !setting.includes("BUTTON_")) { success = false; @@ -490,7 +490,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler break; case Button.LEFT: // Move selection left within the current option set. if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) { success = false; @@ -500,7 +500,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler break; case Button.RIGHT: // Move selection right within the current option set. if (!this.optionCursors || !this.optionValueLabels) { - return; + return false; // TODO: is false correct as default? (previously was `undefined`) } if (this.settingBlacklisted.includes(setting) || setting.includes("BUTTON_")) { success = false; @@ -526,7 +526,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler resetScroll() { this.cursorObj?.destroy(); this.cursorObj = null; - this.cursor = null; + this.cursor = 0; this.setCursor(0); this.setScrollCursor(0); this.updateSettingsScroll(); @@ -547,7 +547,7 @@ export default abstract class AbstractControlSettingsUiHandler extends UiHandler // Check if the cursor object exists, if not, create it. if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); // Set the origin to the top-left corner. this.optionsContainer.add(this.cursorObj); // Add the cursor to the options container. } diff --git a/src/ui/settings/abstract-settings-ui-handler.ts b/src/ui/settings/abstract-settings-ui-handler.ts index 4747a17be32..96d8e790c8a 100644 --- a/src/ui/settings/abstract-settings-ui-handler.ts +++ b/src/ui/settings/abstract-settings-ui-handler.ts @@ -30,7 +30,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { protected navigationIcons: InputsIcons; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; private reloadSettings: Array; private reloadRequired: boolean; @@ -41,7 +41,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { protected localStorageKey: string; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.reloadRequired = false; this.rowsToDisplay = 8; @@ -180,7 +180,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { super.show(args); this.updateBindings(); - const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)) : {}; + const settings: object = localStorage.hasOwnProperty(this.localStorageKey) ? JSON.parse(localStorage.getItem(this.localStorageKey)!) : {}; // TODO: is this bang correct? this.settings.forEach((setting, s) => this.setOptionCursor(s, settings.hasOwnProperty(setting.key) ? settings[setting.key] : this.settings[s].default)); @@ -285,7 +285,7 @@ export default class AbstractSettingsUiHandler extends UiHandler { const ret = super.setCursor(cursor); if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "summary_moves_cursor", undefined, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.optionsContainer.add(this.cursorObj); } diff --git a/src/ui/settings/gamepad-binding-ui-handler.ts b/src/ui/settings/gamepad-binding-ui-handler.ts index 70291da15a2..16a8b9d2c30 100644 --- a/src/ui/settings/gamepad-binding-ui-handler.ts +++ b/src/ui/settings/gamepad-binding-ui-handler.ts @@ -10,7 +10,7 @@ export default class GamepadBindingUiHandler extends AbstractBindingUiHandler { constructor(scene: BattleScene, mode?: Mode) { super(scene, mode); - this.scene.input.gamepad.on("down", this.gamepadButtonDown, this); + this.scene.input.gamepad?.on("down", this.gamepadButtonDown, this); } setup() { super.setup(); diff --git a/src/ui/settings/keyboard-binding-ui-handler.ts b/src/ui/settings/keyboard-binding-ui-handler.ts index 2a92bb0fa6c..0bc74da86ce 100644 --- a/src/ui/settings/keyboard-binding-ui-handler.ts +++ b/src/ui/settings/keyboard-binding-ui-handler.ts @@ -11,7 +11,7 @@ export default class KeyboardBindingUiHandler extends AbstractBindingUiHandler { constructor(scene: BattleScene, mode?: Mode) { super(scene, mode); // Listen to gamepad button down events to initiate binding. - scene.input.keyboard.on("keydown", this.onKeyDown, this); + scene.input.keyboard?.on("keydown", this.onKeyDown, this); } setup() { super.setup(); diff --git a/src/ui/settings/settings-keyboard-ui-handler.ts b/src/ui/settings/settings-keyboard-ui-handler.ts index 5c4444991ad..3408359f33b 100644 --- a/src/ui/settings/settings-keyboard-ui-handler.ts +++ b/src/ui/settings/settings-keyboard-ui-handler.ts @@ -42,10 +42,10 @@ export default class SettingsKeyboardUiHandler extends AbstractControlSettingsUi this.settingBlacklisted = settingKeyboardBlackList; this.device = Device.KEYBOARD; - const deleteEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE); - const restoreDefaultEvent = scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.HOME); - deleteEvent.on("up", this.onDeleteDown, this); - restoreDefaultEvent.on("up", this.onHomeDown, this); + const deleteEvent = scene.input.keyboard?.addKey(Phaser.Input.Keyboard.KeyCodes.DELETE); + const restoreDefaultEvent = scene.input.keyboard?.addKey(Phaser.Input.Keyboard.KeyCodes.HOME); + deleteEvent && deleteEvent.on("up", this.onDeleteDown, this); + restoreDefaultEvent && restoreDefaultEvent.on("up", this.onHomeDown, this); } setSetting = setSettingKeyboard; diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index 028786c85f0..447f5c95e46 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -286,7 +286,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private abilityCursor: number = -1; private natureCursor: number = -1; private filterBarCursor: integer = 0; - private starterMoveset: StarterMoveset; + private starterMoveset: StarterMoveset | null; private scrollCursor: number; private allSpecies: PokemonSpecies[] = []; @@ -298,7 +298,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private starterAbilityIndexes: integer[] = []; private starterNatures: Nature[] = []; private starterMovesets: StarterMoveset[] = []; - private speciesStarterDexEntry: DexEntry; + private speciesStarterDexEntry: DexEntry | null; private speciesStarterMoves: Moves[]; private canCycleShiny: boolean; private canCycleForm: boolean; @@ -309,7 +309,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private value: integer = 0; private canAddParty: boolean; - private assetLoadCancelled: Utils.BooleanHolder; + private assetLoadCancelled: Utils.BooleanHolder | null; public cursorObj: Phaser.GameObjects.Image; private starterCursorObjs: Phaser.GameObjects.Image[]; private pokerusCursorObjs: Phaser.GameObjects.Image[]; @@ -331,7 +331,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private instructionRowY = 0; private instructionRowTextOffset = 12; - private starterSelectCallback: StarterSelectCallback; + private starterSelectCallback: StarterSelectCallback | null; private starterPreferences: StarterPreferences; @@ -343,8 +343,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { setup() { const ui = this.getUi(); - const currentLanguage = i18next.resolvedLanguage; - const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang)); + const currentLanguage = i18next.resolvedLanguage!; // TODO: is this bang correct? + const langSettingKey = Object.keys(languageSettings).find(lang => currentLanguage.includes(lang))!; // TODO: is this bang correct? const textSettings = languageSettings[langSettingKey]; this.starterSelectContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6); @@ -574,7 +574,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { startLabel.setOrigin(0.5, 0); this.starterSelectContainer.add(startLabel); - this.startCursorObj = this.scene.add.nineslice(teamWindowX+4, 160, "select_cursor", null, 26, 15, 6, 6, 6, 6); + this.startCursorObj = this.scene.add.nineslice(teamWindowX+4, 160, "select_cursor", undefined, 26, 15, 6, 6, 6, 6); this.startCursorObj.setVisible(false); this.startCursorObj.setOrigin(0, 0); this.starterSelectContainer.add(this.startCursorObj); @@ -845,7 +845,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.executeWithSeedOffset(() => { for (let c = 0; c < 3; c++) { let randomSpeciesId: Species; - let species: PokemonSpecies; + let species: PokemonSpecies | undefined; const generateSpecies = () => { randomSpeciesId = Utils.randSeedItem(starterSpecies); @@ -867,7 +867,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } while (dupe); - this.pokerusSpecies.push(species); + this.pokerusSpecies.push(species!); // TODO: is the bang correct? } }, 0, date.getTime().toString()); @@ -1296,7 +1296,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { species = this.starterSpecies[this.starterIconsCursorIndex]; } const ui = this.getUi(); - let options = []; + let options: any[] = []; // TODO: add proper type const [isDupe, removeIndex]: [boolean, number] = this.isInParty(species); // checks to see if the pokemon is a duplicate; if it is, returns the index that will be removed @@ -1322,7 +1322,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const cursorObj = this.starterCursorObjs[this.starterSpecies.length]; cursorObj.setVisible(true); cursorObj.setPosition(this.cursorObj.x, this.cursorObj.y); - this.addToParty(species, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset.slice(0) as StarterMoveset); + this.addToParty(species, this.dexAttrCursor, this.abilityCursor, this.natureCursor as unknown as Nature, this.starterMoveset?.slice(0) as StarterMoveset); ui.playSelect(); } else { ui.playError(); // this should be redundant as there is now a trigger for when a pokemon can't be added to party @@ -1378,7 +1378,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { label: allMoves[sm].name, handler: () => { this.switchMoveHandler(i, sm, m); - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; }, onHover: () => { @@ -1389,7 +1389,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { }).concat({ label: i18next.t("menu:cancel"), handler: () => { - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; }, onHover: () => { @@ -1433,7 +1433,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { options.push({ label: i18next.t("starterSelectUiHandler:manageMoves"), handler: () => { - showSwapOptions(this.starterMoveset); + showSwapOptions(this.starterMoveset!); // TODO: is this bang correct? return true; } }); @@ -1449,7 +1449,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { ui.setMode(Mode.STARTER_SELECT).then(() => { ui.showText(i18next.t("starterSelectUiHandler:selectNature"), null, () => { - const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr); + const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); ui.setModeWithoutClear(Mode.OPTION_SELECT, { options: natures.map((n: Nature, i: number) => { const option: OptionSelectItem = { @@ -1518,7 +1518,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } const showUseCandies = () => { // this lets you use your candies - const options = []; + const options: any[] = []; // TODO: add proper type if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarters[this.lastSpecies.speciesId]); options.push({ @@ -1691,7 +1691,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { let newFormIndex = props.formIndex; do { newFormIndex = (newFormIndex + 1) % formCount; - if (this.lastSpecies.forms[newFormIndex].isStarterSelectable && this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(newFormIndex)) { + if (this.lastSpecies.forms[newFormIndex].isStarterSelectable && this.speciesStarterDexEntry!.caughtAttr! & this.scene.gameData.getFormAttr(newFormIndex)) { // TODO: are those bangs correct? break; } } while (newFormIndex !== props.formIndex); @@ -1736,7 +1736,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { break; case Button.CYCLE_NATURE: if (this.canCycleNature) { - const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr); + const natures = this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr); const natureIndex = natures.indexOf(this.natureCursor); const newNature = natures[natureIndex < natures.length - 1 ? natureIndex + 1 : 0]; // store cycled nature as default @@ -1751,24 +1751,24 @@ export default class StarterSelectUiHandler extends MessageUiHandler { do { newVariant = (newVariant + 1) % 3; if (!newVariant) { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT) { // TODO: is this bang correct? break; } } else if (newVariant === 1) { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_2) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2) { // TODO: is this bang correct? break; } } else { - if (this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_3) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) { // TODO: is this bang correct? break; } } } while (newVariant !== props.variant); starterAttributes.variant = newVariant; // store the selected variant - this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newVariant, undefined, undefined); + this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, newVariant as Variant, undefined, undefined); // Cycle tint based on current sprite tint - const tint = getVariantTint(newVariant); - this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)); + const tint = getVariantTint(newVariant as Variant); + this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); this.pokemonShinyIcon.setTint(tint); success = true; } @@ -1950,34 +1950,34 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switchMoveHandler(i: number, newMove: Moves, move: Moves) { const speciesId = this.lastSpecies.speciesId; - const existingMoveIndex = this.starterMoveset.indexOf(newMove); - this.starterMoveset[i] = newMove; + const existingMoveIndex = this.starterMoveset?.indexOf(newMove)!; // TODO: is this bang correct? + this.starterMoveset![i] = newMove; // TODO: is this bang correct? if (existingMoveIndex > -1) { - this.starterMoveset[existingMoveIndex] = move; + this.starterMoveset![existingMoveIndex] = move; // TODO: is this bang correct? } const props: DexAttrProps = this.scene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.dexAttrCursor); // species has different forms if (pokemonFormLevelMoves.hasOwnProperty(speciesId)) { // starterMoveData doesn't have base form moves or is using the single form format if (!this.scene.gameData.starterData[speciesId].moveset || Array.isArray(this.scene.gameData.starterData[speciesId].moveset)) { - this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset.slice(0) as StarterMoveset }; + this.scene.gameData.starterData[speciesId].moveset = { [props.formIndex]: this.starterMoveset?.slice(0) as StarterMoveset }; } const starterMoveData = this.scene.gameData.starterData[speciesId].moveset; // starterMoveData doesn't have active form moves if (!starterMoveData.hasOwnProperty(props.formIndex)) { - this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice(0) as StarterMoveset; } // does the species' starter move data have its form's starter moves and has it been updated if (starterMoveData.hasOwnProperty(props.formIndex)) { // active form move hasn't been updated if (starterMoveData[props.formIndex][existingMoveIndex] !== newMove) { - this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset[props.formIndex] = this.starterMoveset?.slice(0) as StarterMoveset; } } } else { - this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset.slice(0) as StarterMoveset; + this.scene.gameData.starterData[speciesId].moveset = this.starterMoveset?.slice(0) as StarterMoveset; } this.setSpeciesDetails(this.lastSpecies, undefined, undefined, undefined, undefined, undefined, undefined, false); @@ -1986,7 +1986,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { Array.from({ length: this.starterSpecies.length }, (_, i) => { const starterSpecies = this.starterSpecies[i]; if (starterSpecies.speciesId === speciesId) { - this.starterMovesets[i] = this.starterMoveset; + this.starterMovesets[i] = this.starterMoveset!; // TODO: is this bang correct? } }); } @@ -2382,22 +2382,22 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } - setSpecies(species: PokemonSpecies) { + setSpecies(species: PokemonSpecies | null) { this.speciesStarterDexEntry = species ? this.scene.gameData.dexData[species.speciesId] : null; this.dexAttrCursor = species ? this.scene.gameData.getSpeciesDefaultDexAttr(species, false, true) : 0n; this.abilityCursor = species ? this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species) : 0; this.natureCursor = species ? this.scene.gameData.getSpeciesDefaultNature(species) : 0; - const starterAttributes : StarterAttributes = species ? {...this.starterPreferences[species.speciesId]} : null; + const starterAttributes : StarterAttributes | null = species ? {...this.starterPreferences[species.speciesId]} : null; // validate starterAttributes if (starterAttributes) { // this may cause changes so we created a copy of the attributes before - if (!isNaN(starterAttributes.variant)) { + if (starterAttributes.variant && !isNaN(starterAttributes.variant)) { if (![ - this.speciesStarterDexEntry.caughtAttr & DexAttr.NON_SHINY, - this.speciesStarterDexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, - this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_2, - this.speciesStarterDexEntry.caughtAttr & DexAttr.VARIANT_3 + this.speciesStarterDexEntry!.caughtAttr & DexAttr.NON_SHINY, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2, // TODO: is that bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3 // TODO: is that bang correct? ][starterAttributes.variant+1]) { // add 1 as -1 = non-shiny // requested variant wasn't unlocked, purging setting delete starterAttributes.variant; @@ -2405,29 +2405,29 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (typeof starterAttributes.female !== "boolean" || !(starterAttributes.female ? - this.speciesStarterDexEntry.caughtAttr & DexAttr.FEMALE : - this.speciesStarterDexEntry.caughtAttr & DexAttr.MALE + this.speciesStarterDexEntry!.caughtAttr & DexAttr.FEMALE : // TODO: is this bang correct? + this.speciesStarterDexEntry!.caughtAttr & DexAttr.MALE // TODO: is this bang correct? )) { // requested gender wasn't unlocked, purging setting delete starterAttributes.female; } - const abilityAttr = this.scene.gameData.starterData[species.speciesId].abilityAttr; + const abilityAttr = this.scene.gameData.starterData[species!.speciesId].abilityAttr; // TODO: is this bang correct? if (![ abilityAttr & AbilityAttr.ABILITY_1, - species.ability2 ? (abilityAttr & AbilityAttr.ABILITY_2) : abilityAttr & AbilityAttr.ABILITY_HIDDEN, - species.ability2 && abilityAttr & AbilityAttr.ABILITY_HIDDEN - ][starterAttributes.ability]) { + species!.ability2 ? (abilityAttr & AbilityAttr.ABILITY_2) : abilityAttr & AbilityAttr.ABILITY_HIDDEN, // TODO: is this bang correct? + species!.ability2 && abilityAttr & AbilityAttr.ABILITY_HIDDEN // TODO: is this bang correct? + ][starterAttributes.ability!]) { // TODO: is this bang correct? // requested ability wasn't unlocked, purging setting delete starterAttributes.ability; } - if (!(species.forms[starterAttributes.form]?.isStarterSelectable && this.speciesStarterDexEntry.caughtAttr & this.scene.gameData.getFormAttr(starterAttributes.form))) { + if (!(species?.forms[starterAttributes.form!]?.isStarterSelectable && this.speciesStarterDexEntry!.caughtAttr & this.scene.gameData.getFormAttr(starterAttributes.form!))) { // TODO: are those bangs correct? // requested form wasn't unlocked/isn't a starter form, purging setting delete starterAttributes.form; } - if (this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry.natureAttr).indexOf(starterAttributes.nature as unknown as Nature) < 0) { + if (this.scene.gameData.getNaturesForAttr(this.speciesStarterDexEntry?.natureAttr).indexOf(starterAttributes.nature as unknown as Nature) < 0) { // requested nature wasn't unlocked, purging setting delete starterAttributes.nature; } @@ -2437,7 +2437,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // load default nature from stater save data, if set this.natureCursor = starterAttributes.nature; } - if (!isNaN(starterAttributes?.ability)) { + if (starterAttributes?.ability && !isNaN(starterAttributes.ability)) { // load default nature from stater save data, if set this.abilityCursor = starterAttributes.ability; } @@ -2448,7 +2448,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.showStats(); } else { this.statsContainer.setVisible(false); - this.statsContainer.updateIvs(null); + //@ts-ignore + this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. what. how? huh? } } @@ -2465,7 +2466,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.tweens.getTweensOf(icon).forEach(tween => tween.resume()); } - this.lastSpecies = species; + this.lastSpecies = species!; // TODO: is this bang correct? if (species && (this.speciesStarterDexEntry?.seenAttr || this.speciesStarterDexEntry?.caughtAttr)) { this.pokemonNumberText.setText(Utils.padInt(species.speciesId, 4)); @@ -2547,7 +2548,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const candyCropY = 16 - (16 * (currentFriendship / friendshipCap)); if (this.pokemonCandyDarknessOverlay.visible) { - this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true)); + this.pokemonCandyDarknessOverlay.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true)); this.pokemonCandyDarknessOverlay.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -2582,7 +2583,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { // load default nature from stater save data, if set const defaultNature = starterAttributes?.nature || this.scene.gameData.getSpeciesDefaultNature(species); props = this.scene.gameData.getSpeciesDexAttrProps(species, defaultDexAttr); - if (!isNaN(starterAttributes?.variant)) { + if (starterAttributes?.variant && !isNaN(starterAttributes.variant)) { if (props.shiny = (starterAttributes.variant >= 0)) { props.variant = starterAttributes.variant as Variant; } @@ -2594,7 +2595,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const speciesForm = getPokemonSpeciesForm(species.speciesId, props.formIndex); - this.setTypeIcons(speciesForm.type1, speciesForm.type2); + this.setTypeIcons(speciesForm.type1, speciesForm!.type2!); // TODO: are those bangs correct? this.pokemonSprite.clearTint(); if (this.pokerusSpecies.includes(species)) { @@ -2648,14 +2649,14 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonCandyCountText.setVisible(false); this.pokemonFormText.setVisible(false); - this.setSpeciesDetails(species, false, 0, false, 0, 0, 0); + this.setSpeciesDetails(species!, false, 0, false, 0, 0, 0); // TODO: is this bang correct? this.pokemonSprite.clearTint(); } } - setSpeciesDetails(species: PokemonSpecies, shiny: boolean, formIndex: integer, female: boolean, variant: Variant, abilityIndex: integer, natureIndex: integer, forSeen: boolean = false): void { + setSpeciesDetails(species: PokemonSpecies, shiny?: boolean, formIndex?: integer, female?: boolean, variant?: Variant, abilityIndex?: integer, natureIndex?: integer, forSeen: boolean = false): void { const oldProps = species ? this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor) : null; const oldAbilityIndex = this.abilityCursor > -1 ? this.abilityCursor : this.scene.gameData.getStarterSpeciesDefaultAbilityIndex(species); const oldNatureIndex = this.natureCursor > -1 ? this.natureCursor : this.scene.gameData.getSpeciesDefaultNature(species); @@ -2672,10 +2673,10 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (species) { - this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; - this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps.female)) ? DexAttr.MALE : DexAttr.FEMALE; - this.dexAttrCursor |= (variant !== undefined ? !variant : !(variant = oldProps.variant)) ? DexAttr.DEFAULT_VARIANT : variant === 1 ? DexAttr.VARIANT_2 : DexAttr.VARIANT_3; - this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps.formIndex)); + this.dexAttrCursor |= (shiny !== undefined ? !shiny : !(shiny = oldProps?.shiny)) ? DexAttr.NON_SHINY : DexAttr.SHINY; + this.dexAttrCursor |= (female !== undefined ? !female : !(female = oldProps?.female)) ? DexAttr.MALE : DexAttr.FEMALE; + this.dexAttrCursor |= (variant !== undefined ? !variant : !(variant = oldProps?.variant)) ? DexAttr.DEFAULT_VARIANT : variant === 1 ? DexAttr.VARIANT_2 : DexAttr.VARIANT_3; + this.dexAttrCursor |= this.scene.gameData.getFormAttr(formIndex !== undefined ? formIndex : (formIndex = oldProps!.formIndex)); // TODO: is this bang correct? this.abilityCursor = abilityIndex !== undefined ? abilityIndex : (abilityIndex = oldAbilityIndex); this.natureCursor = natureIndex !== undefined ? natureIndex : (natureIndex = oldNatureIndex); } @@ -2717,7 +2718,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } } - this.shinyOverlay.setVisible(shiny); + this.shinyOverlay.setVisible(shiny ?? false); // TODO: is false the correct default? this.pokemonNumberText.setColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, false)); this.pokemonNumberText.setShadowColor(this.getTextColor(shiny ? TextStyle.SUMMARY_GOLD : TextStyle.SUMMARY, true)); @@ -2733,16 +2734,16 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const assetLoadCancelled = new Utils.BooleanHolder(false); this.assetLoadCancelled = assetLoadCancelled; - species.loadAssets(this.scene, female, formIndex, shiny, variant, true).then(() => { + species.loadAssets(this.scene, female!, formIndex, shiny, variant, true).then(() => { // TODO: is this bang correct? if (assetLoadCancelled.value) { return; } this.assetLoadCancelled = null; this.speciesLoaded.set(species.speciesId, true); - this.pokemonSprite.play(species.getSpriteKey(female, formIndex, shiny, variant)); + this.pokemonSprite.play(species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.pokemonSprite.setPipelineData("shiny", shiny); this.pokemonSprite.setPipelineData("variant", variant); - this.pokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female, formIndex, shiny, variant)); + this.pokemonSprite.setPipelineData("spriteKey", species.getSpriteKey(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.pokemonSprite.setVisible(!this.statsMode); }); @@ -2750,7 +2751,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const isValidForChallenge = new Utils.BooleanHolder(true); Challenge.applyChallenges(this.scene.gameMode, Challenge.ChallengeType.STARTER_CHOICE, species, isValidForChallenge, this.scene.gameData.getSpeciesDexAttrProps(species, this.dexAttrCursor), !!this.starterSpecies.length); const starterSprite = this.filteredStarterContainers[this.cursor].icon as Phaser.GameObjects.Sprite; - starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female, formIndex, shiny, variant)); + starterSprite.setTexture(species.getIconAtlasKey(formIndex, shiny, variant), species.getIconId(female!, formIndex, shiny, variant)); // TODO: is this bang correct? this.filteredStarterContainers[this.cursor].checkIconId(female, formIndex, shiny, variant); this.canCycleShiny = !!(dexEntry.caughtAttr & DexAttr.NON_SHINY && dexEntry.caughtAttr & DexAttr.SHINY); this.canCycleGender = !!(dexEntry.caughtAttr & DexAttr.MALE && dexEntry.caughtAttr & DexAttr.FEMALE); @@ -2758,7 +2759,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.canCycleForm = species.forms.filter(f => f.isStarterSelectable || !pokemonFormChanges[species.speciesId]?.find(fc => fc.formKey)) .map((_, f) => dexEntry.caughtAttr & this.scene.gameData.getFormAttr(f)).filter(f => f).length > 1; this.canCycleNature = this.scene.gameData.getNaturesForAttr(dexEntry.natureAttr).length > 1; - this.canCycleVariant = shiny && [ dexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, dexEntry.caughtAttr & DexAttr.VARIANT_2, dexEntry.caughtAttr & DexAttr.VARIANT_3].filter(v => v).length > 1; + this.canCycleVariant = !!shiny && [ dexEntry.caughtAttr & DexAttr.DEFAULT_VARIANT, dexEntry.caughtAttr & DexAttr.VARIANT_2, dexEntry.caughtAttr & DexAttr.VARIANT_3].filter(v => v).length > 1; } if (dexEntry.caughtAttr && species.malePercent !== null) { @@ -2771,7 +2772,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } if (dexEntry.caughtAttr) { - const ability = this.lastSpecies.getAbility(abilityIndex); + const ability = this.lastSpecies.getAbility(abilityIndex!); // TODO: is this bang correct? this.pokemonAbilityText.setText(allAbilities[ability].name); const isHidden = abilityIndex === (this.lastSpecies.ability2 ? 2 : 1); @@ -2786,7 +2787,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonNatureText.setText(getNatureName(natureIndex as unknown as Nature, true, true, false, this.scene.uiTheme)); let levelMoves: LevelMoves; - if (pokemonFormLevelMoves.hasOwnProperty(species.speciesId) && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)) { + if (pokemonFormLevelMoves.hasOwnProperty(species.speciesId) && formIndex && pokemonFormLevelMoves[species.speciesId].hasOwnProperty(formIndex)) { levelMoves = pokemonFormLevelMoves[species.speciesId][formIndex]; } else { levelMoves = pokemonSpeciesLevelMoves[species.speciesId]; @@ -2801,41 +2802,42 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } const speciesMoveData = this.scene.gameData.starterData[species.speciesId].moveset; - const moveData: StarterMoveset = speciesMoveData + const moveData: StarterMoveset | null = speciesMoveData ? Array.isArray(speciesMoveData) ? speciesMoveData as StarterMoveset - : (speciesMoveData as StarterFormMoveData)[formIndex] + : (speciesMoveData as StarterFormMoveData)[formIndex!] // TODO: is this bang correct? : null; const availableStarterMoves = this.speciesStarterMoves.concat(speciesEggMoves.hasOwnProperty(species.speciesId) ? speciesEggMoves[species.speciesId].filter((_, em: integer) => this.scene.gameData.starterData[species.speciesId].eggMoves & (1 << em)) : []); this.starterMoveset = (moveData || (this.speciesStarterMoves.slice(0, 4) as StarterMoveset)).filter(m => availableStarterMoves.find(sm => sm === m)) as StarterMoveset; // Consolidate move data if it contains an incompatible move if (this.starterMoveset.length < 4 && this.starterMoveset.length < availableStarterMoves.length) { - this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length)); + this.starterMoveset.push(...availableStarterMoves.filter(sm => this.starterMoveset?.indexOf(sm) === -1).slice(0, 4 - this.starterMoveset.length)); } // Remove duplicate moves this.starterMoveset = this.starterMoveset.filter( (move, i) => { - return this.starterMoveset.indexOf(move) === i; + return this.starterMoveset?.indexOf(move) === i; }) as StarterMoveset; - const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex); - const formText = Utils.capitalizeString(species?.forms[formIndex]?.formKey, "-", false, false); + const speciesForm = getPokemonSpeciesForm(species.speciesId, formIndex!); // TODO: is the bang correct? + const formText = Utils.capitalizeString(species?.forms[formIndex!]?.formKey, "-", false, false); // TODO: is the bang correct? const speciesName = Utils.capitalizeString(Species[species.speciesId], "_", true, false); if (species.speciesId === Species.ARCEUS) { - this.pokemonFormText.setText(i18next.t(`pokemonInfo:Type.${formText.toUpperCase()}`)); + this.pokemonFormText.setText(i18next.t(`pokemonInfo:Type.${formText?.toUpperCase()}`)); } else { this.pokemonFormText.setText(formText ? i18next.t(`pokemonForm:${speciesName}${formText}`) : ""); } - this.setTypeIcons(speciesForm.type1, speciesForm.type2); + this.setTypeIcons(speciesForm.type1, speciesForm.type2!); // TODO: is this bang correct? } else { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - this.setTypeIcons(null, null); + // @ts-ignore + this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? } } else { this.shinyOverlay.setVisible(false); @@ -2845,7 +2847,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityText.setText(""); this.pokemonPassiveText.setText(""); this.pokemonNatureText.setText(""); - this.setTypeIcons(null, null); + // @ts-ignore + this.setTypeIcons(null, null); // TODO: resolve ts-ignore.. huh!? } if (!this.starterMoveset) { @@ -2868,7 +2871,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonEggMoveLabels[em].setText(eggMove && eggMoveUnlocked ? eggMove.name : "???"); } - this.pokemonEggMovesContainer.setVisible(this.speciesStarterDexEntry?.caughtAttr && hasEggMoves); + this.pokemonEggMovesContainer.setVisible(!!this.speciesStarterDexEntry?.caughtAttr && hasEggMoves); this.pokemonAdditionalMoveCountLabel.setText(`(+${Math.max(this.speciesStarterMoves.length - 4, 0)})`); this.pokemonAdditionalMoveCountLabel.setVisible(this.speciesStarterMoves.length > 4); @@ -3064,7 +3067,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.scene.pushPhase(new TitlePhase(this.scene)); } this.clearText(); - this.scene.getCurrentPhase().end(); + this.scene.getCurrentPhase()?.end(); }, cancel, null, null, 19); }); @@ -3097,7 +3100,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const thisObj = this; const originalStarterSelectCallback = this.starterSelectCallback; this.starterSelectCallback = null; - originalStarterSelectCallback(new Array(this.starterSpecies.length).fill(0).map(function (_, i) { + originalStarterSelectCallback && originalStarterSelectCallback(new Array(this.starterSpecies.length).fill(0).map(function (_, i) { const starterSpecies = thisObj.starterSpecies[i]; return { species: starterSpecies, @@ -3116,7 +3119,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { } else { const handler = this.scene.ui.getHandler() as AwaitableUiHandler; handler.tutorialActive = true; - this.scene.ui.showText(i18next.t("starterSelectUiHandler:invalidParty"), null, () => this.scene.ui.showText(null, 0, () => handler.tutorialActive = false), null, true); + this.scene.ui.showText(i18next.t("starterSelectUiHandler:invalidParty"), null, () => this.scene.ui.showText("", 0, () => handler.tutorialActive = false), null, true); } return true; } @@ -3147,7 +3150,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.statsMode = false; this.statsContainer.setVisible(false); this.pokemonSprite.setVisible(!!this.speciesStarterDexEntry?.caughtAttr); - this.statsContainer.updateIvs(null); + //@ts-ignore + this.statsContainer.updateIvs(null); // TODO: resolve ts-ignore. !?!? } } diff --git a/src/ui/summary-ui-handler.ts b/src/ui/summary-ui-handler.ts index 405429fcd3f..030e67fcc30 100644 --- a/src/ui/summary-ui-handler.ts +++ b/src/ui/summary-ui-handler.ts @@ -2,7 +2,7 @@ import BattleScene, { starterColors } from "../battle-scene"; import { Mode } from "./ui"; import UiHandler from "./ui-handler"; import * as Utils from "../utils"; -import { PlayerPokemon } from "../field/pokemon"; +import { PlayerPokemon, PokemonMove } from "../field/pokemon"; import { getStarterValueFriendshipCap, speciesStarters } from "../data/pokemon-species"; import { argbFromRgba } from "@material/material-color-utilities"; import { Type, getTypeRgb } from "../data/type"; @@ -24,7 +24,6 @@ import i18next from "i18next"; import {modifierSortFunc} from "../modifier/modifier"; import { PlayerGender } from "#enums/player-gender"; - enum Page { PROFILE, STATS, @@ -41,11 +40,11 @@ interface abilityContainer { /** An image displaying the summary label */ labelImage: Phaser.GameObjects.Image, /** The ability object */ - ability: Ability, + ability: Ability | null, /** The text object displaying the name of the ability */ - nameText: Phaser.GameObjects.Text, + nameText: Phaser.GameObjects.Text | null, /** The text object displaying the description of the ability */ - descriptionText: Phaser.GameObjects.Text, + descriptionText: Phaser.GameObjects.Text | null, } export default class SummaryUiHandler extends UiHandler { @@ -79,8 +78,8 @@ export default class SummaryUiHandler extends UiHandler { private summaryPageContainer: Phaser.GameObjects.Container; private movesContainer: Phaser.GameObjects.Container; private moveDescriptionText: Phaser.GameObjects.Text; - private moveCursorObj: Phaser.GameObjects.Sprite; - private selectedMoveCursorObj: Phaser.GameObjects.Sprite; + private moveCursorObj: Phaser.GameObjects.Sprite | null; + private selectedMoveCursorObj: Phaser.GameObjects.Sprite | null; private moveRowsContainer: Phaser.GameObjects.Container; private extraMoveRowContainer: Phaser.GameObjects.Container; private moveEffectContainer: Phaser.GameObjects.Container; @@ -89,14 +88,14 @@ export default class SummaryUiHandler extends UiHandler { private moveCategoryIcon: Phaser.GameObjects.Sprite; private summaryPageTransitionContainer: Phaser.GameObjects.Container; - private descriptionScrollTween: Phaser.Tweens.Tween; - private moveCursorBlinkTimer: Phaser.Time.TimerEvent; + private descriptionScrollTween: Phaser.Tweens.Tween | null; + private moveCursorBlinkTimer: Phaser.Time.TimerEvent | null; - private pokemon: PlayerPokemon; + private pokemon: PlayerPokemon | null; private playerParty: boolean; /**This is set to false when checking the summary of a freshly caught Pokemon as it is not part of a player's party yet but still needs to display its items**/ - private newMove: Move; - private moveSelectFunction: Function; + private newMove: Move | null; + private moveSelectFunction: Function | null; private transitioning: boolean; private statusVisible: boolean; private moveEffectsVisible: boolean; @@ -104,7 +103,7 @@ export default class SummaryUiHandler extends UiHandler { private moveSelect: boolean; private moveCursor: integer; private selectedMoveIndex: integer; - private selectCallback: Function; + private selectCallback: Function | null; constructor(scene: BattleScene) { super(scene, Mode.SUMMARY); @@ -138,7 +137,7 @@ export default class SummaryUiHandler extends UiHandler { this.numberText.setOrigin(0, 1); this.summaryContainer.add(this.numberText); - this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, "pkmn__sub"), null, false, true); + this.pokemonSprite = this.scene.initPokemonSprite(this.scene.add.sprite(56, -106, "pkmn__sub"), undefined, false, true); this.summaryContainer.add(this.pokemonSprite); this.nameText = addTextObject(this.scene, 6, -54, "", TextStyle.SUMMARY); @@ -305,10 +304,10 @@ export default class SummaryUiHandler extends UiHandler { this.pokemonSprite.setPipelineData("variant", this.pokemon.variant); [ "spriteColors", "fusionSpriteColors" ].map(k => { delete this.pokemonSprite.pipelineData[`${k}Base`]; - if (this.pokemon.summonData?.speciesForm) { + if (this.pokemon?.summonData?.speciesForm) { k += "Base"; } - this.pokemonSprite.pipelineData[k] = this.pokemon.getSprite().pipelineData[k]; + this.pokemonSprite.pipelineData[k] = this.pokemon?.getSprite().pipelineData[k]; }); this.pokemon.cry(); @@ -319,7 +318,7 @@ export default class SummaryUiHandler extends UiHandler { this.splicedIcon.setPositionRelative(this.nameText, this.nameText.displayWidth + 2, 3); this.splicedIcon.setVisible(isFusion); if (this.splicedIcon.visible) { - this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${this.pokemon.species.getName(this.pokemon.formIndex)}/${this.pokemon.fusionSpecies.getName(this.pokemon.fusionFormIndex)}`, true)); + this.splicedIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${this.pokemon?.species.getName(this.pokemon.formIndex)}/${this.pokemon?.fusionSpecies?.getName(this.pokemon?.fusionFormIndex)}`, true)); this.splicedIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -338,7 +337,7 @@ export default class SummaryUiHandler extends UiHandler { const candyCropY = 16 - (16 * (currentFriendship / friendshipCap)); if (this.candyShadow.visible) { - this.candyShadow.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${currentFriendship}/${friendshipCap}`, true)); + this.candyShadow.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${currentFriendship}/${friendshipCap}`, true)); this.candyShadow.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -357,7 +356,7 @@ export default class SummaryUiHandler extends UiHandler { const shinyDescriptor = doubleShiny || baseVariant ? `${baseVariant === 2 ? i18next.t("common:epicShiny") : baseVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}${doubleShiny ? `/${this.pokemon.fusionVariant === 2 ? i18next.t("common:epicShiny") : this.pokemon.fusionVariant === 1 ? i18next.t("common:rareShiny") : i18next.t("common:commonShiny")}` : ""}` : ""; - this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip(null, `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); + this.shinyIcon.on("pointerover", () => (this.scene as BattleScene).ui.showTooltip("", `${i18next.t("common:shinyOnHover")}${shinyDescriptor ? ` (${shinyDescriptor})` : ""}`, true)); this.shinyIcon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); } @@ -416,16 +415,16 @@ export default class SummaryUiHandler extends UiHandler { if (this.moveSelect) { if (button === Button.ACTION) { - if (this.moveCursor < this.pokemon.moveset.length) { + if (this.pokemon && this.moveCursor < this.pokemon.moveset.length) { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.moveSelectFunction(this.moveCursor); + this.moveSelectFunction && this.moveSelectFunction(this.moveCursor); } else { if (this.selectedMoveIndex === -1) { this.selectedMoveIndex = this.moveCursor; this.setCursor(this.moveCursor); } else { if (this.selectedMoveIndex !== this.moveCursor) { - const tempMove = this.pokemon.moveset[this.selectedMoveIndex]; + const tempMove = this.pokemon?.moveset[this.selectedMoveIndex]; this.pokemon.moveset[this.selectedMoveIndex] = this.pokemon.moveset[this.moveCursor]; this.pokemon.moveset[this.moveCursor] = tempMove; @@ -483,15 +482,15 @@ export default class SummaryUiHandler extends UiHandler { if (this.cursor === Page.MOVES) { this.showMoveSelect(); success = true; - } else if (this.cursor === Page.PROFILE && this.pokemon.hasPassive()) { + } else if (this.cursor === Page.PROFILE && this.pokemon?.hasPassive()) { // if we're on the PROFILE page and this pokemon has a passive unlocked.. // Since abilities are displayed by default, all we need to do is toggle visibility on all elements to show passives - this.abilityContainer.nameText.setVisible(!this.abilityContainer.descriptionText.visible); - this.abilityContainer.descriptionText.setVisible(!this.abilityContainer.descriptionText.visible); + this.abilityContainer.nameText?.setVisible(!this.abilityContainer.descriptionText?.visible); + this.abilityContainer.descriptionText?.setVisible(!this.abilityContainer.descriptionText.visible); this.abilityContainer.labelImage.setVisible(!this.abilityContainer.labelImage.visible); - this.passiveContainer.nameText.setVisible(!this.passiveContainer.descriptionText.visible); - this.passiveContainer.descriptionText.setVisible(!this.passiveContainer.descriptionText.visible); + this.passiveContainer.nameText?.setVisible(!this.passiveContainer.descriptionText?.visible); + this.passiveContainer.descriptionText?.setVisible(!this.passiveContainer.descriptionText.visible); this.passiveContainer.labelImage.setVisible(!this.passiveContainer.labelImage.visible); } } else if (button === Button.CANCEL) { @@ -523,7 +522,7 @@ export default class SummaryUiHandler extends UiHandler { } const isDown = button === Button.DOWN; const party = this.scene.getParty(); - const partyMemberIndex = party.indexOf(this.pokemon); + const partyMemberIndex = this.pokemon ? party.indexOf(this.pokemon) : -1; if ((isDown && partyMemberIndex < party.length - 1) || (!isDown && partyMemberIndex)) { const page = this.cursor; this.clear(); @@ -609,7 +608,7 @@ export default class SummaryUiHandler extends UiHandler { loop: true, delay: Utils.fixedInt(600), callback: () => { - this.moveCursorObj.setVisible(false); + this.moveCursorObj?.setVisible(false); this.scene.time.delayedCall(Utils.fixedInt(100), () => { if (!this.moveCursorObj) { return; @@ -727,16 +726,16 @@ export default class SummaryUiHandler extends UiHandler { return typeIcon; }; - const types = this.pokemon.getTypes(false, false, true); + const types = this.pokemon?.getTypes(false, false, true)!; // TODO: is this bang correct? profileContainer.add(getTypeIcon(0, types[0])); if (types.length > 1) { profileContainer.add(getTypeIcon(1, types[1])); } - if (this.pokemon.isTerastallized()) { + if (this.pokemon?.isTerastallized()) { profileContainer.add(getTypeIcon(types.length, this.pokemon.getTeraType(), true)); } - if (this.pokemon.getLuck()) { + if (this.pokemon?.getLuck()) { const luckLabelText = addTextObject(this.scene, 141, 28, i18next.t("common:luckIndicator"), TextStyle.SUMMARY_ALT); luckLabelText.setOrigin(0, 0); profileContainer.add(luckLabelText); @@ -749,13 +748,13 @@ export default class SummaryUiHandler extends UiHandler { this.abilityContainer = { labelImage: this.scene.add.image(0, 0, "summary_profile_ability"), - ability: this.pokemon.getAbility(true), + ability: this.pokemon?.getAbility(true)!, // TODO: is this bang correct? nameText: null, descriptionText: null}; const allAbilityInfo = [this.abilityContainer]; // Creates an array to iterate through // Only add to the array and set up displaying a passive if it's unlocked - if (this.pokemon.hasPassive()) { + if (this.pokemon?.hasPassive()) { this.passiveContainer = { labelImage: this.scene.add.image(0, 0, "summary_profile_passive"), ability: this.pokemon.getPassiveAbility(), @@ -777,11 +776,11 @@ export default class SummaryUiHandler extends UiHandler { abilityInfo.labelImage.setOrigin(0, 0); profileContainer.add(abilityInfo.labelImage); - abilityInfo.nameText = addTextObject(this.scene, 7, 66, abilityInfo.ability.name, TextStyle.SUMMARY_ALT); + abilityInfo.nameText = addTextObject(this.scene, 7, 66, abilityInfo.ability?.name!, TextStyle.SUMMARY_ALT); // TODO: is this bang correct? abilityInfo.nameText.setOrigin(0, 1); profileContainer.add(abilityInfo.nameText); - abilityInfo.descriptionText = addTextObject(this.scene, 7, 69, abilityInfo.ability.description, TextStyle.WINDOW_ALT, { wordWrap: { width: 1224 } }); + abilityInfo.descriptionText = addTextObject(this.scene, 7, 69, abilityInfo.ability?.description!, TextStyle.WINDOW_ALT, { wordWrap: { width: 1224 } }); // TODO: is this bang correct? abilityInfo.descriptionText.setOrigin(0, 0); profileContainer.add(abilityInfo.descriptionText); @@ -813,17 +812,17 @@ export default class SummaryUiHandler extends UiHandler { }); // Turn off visibility of passive info by default this.passiveContainer?.labelImage.setVisible(false); - this.passiveContainer?.nameText.setVisible(false); - this.passiveContainer?.descriptionText.setVisible(false); + this.passiveContainer?.nameText?.setVisible(false); + this.passiveContainer?.descriptionText?.setVisible(false); const closeFragment = getBBCodeFrag("", TextStyle.WINDOW_ALT); - const rawNature = Utils.toReadableString(Nature[this.pokemon.getNature()]); - const nature = `${getBBCodeFrag(Utils.toReadableString(getNatureName(this.pokemon.getNature())), TextStyle.SUMMARY_RED)}${closeFragment}`; + const rawNature = Utils.toReadableString(Nature[this.pokemon?.getNature()!]); // TODO: is this bang correct? + const nature = `${getBBCodeFrag(Utils.toReadableString(getNatureName(this.pokemon?.getNature()!)), TextStyle.SUMMARY_RED)}${closeFragment}`; // TODO: is this bang correct? const memoString = i18next.t("pokemonSummary:memoString", { - metFragment: i18next.t(`pokemonSummary:metFragment.${this.pokemon.metBiome === -1? "apparently": "normal"}`, { - biome: `${getBBCodeFrag(getBiomeName(this.pokemon.metBiome), TextStyle.SUMMARY_RED)}${closeFragment}`, - level: `${getBBCodeFrag(this.pokemon.metLevel.toString(), TextStyle.SUMMARY_RED)}${closeFragment}`, + metFragment: i18next.t(`pokemonSummary:metFragment.${this.pokemon?.metBiome === -1? "apparently": "normal"}`, { + biome: `${getBBCodeFrag(getBiomeName(this.pokemon?.metBiome!), TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? + level: `${getBBCodeFrag(this.pokemon?.metLevel.toString()!, TextStyle.SUMMARY_RED)}${closeFragment}`, // TODO: is this bang correct? }), natureFragment: i18next.exists(`pokemonSummary:natureFragment.${rawNature}`) ? @@ -831,7 +830,7 @@ export default class SummaryUiHandler extends UiHandler { nature, }); - const memoText = addBBCodeTextObject(this.scene, 7, 113, memoString, TextStyle.WINDOW_ALT); + const memoText = addBBCodeTextObject(this.scene, 7, 113, String(memoString), TextStyle.WINDOW_ALT); memoText.setOrigin(0, 0); profileContainer.add(memoText); break; @@ -846,15 +845,15 @@ export default class SummaryUiHandler extends UiHandler { const rowIndex = s % 3; const colIndex = Math.floor(s / 3); - const natureStatMultiplier = getNatureStatMultiplier(this.pokemon.getNature(), s); + const natureStatMultiplier = getNatureStatMultiplier(this.pokemon?.getNature()!, s); // TODO: is this bang correct? const statLabel = addTextObject(this.scene, 27 + 115 * colIndex + (colIndex === 1 ? 5 : 0), 56 + 16 * rowIndex, statName, natureStatMultiplier === 1 ? TextStyle.SUMMARY : natureStatMultiplier > 1 ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY_BLUE); statLabel.setOrigin(0.5, 0); statsContainer.add(statLabel); const statValueText = stat !== Stat.HP - ? Utils.formatStat(this.pokemon.stats[s]) - : `${Utils.formatStat(this.pokemon.hp, true)}/${Utils.formatStat(this.pokemon.getMaxHp(), true)}`; + ? Utils.formatStat(this.pokemon?.stats[s]!) // TODO: is this bang correct? + : `${Utils.formatStat(this.pokemon?.hp!, true)}/${Utils.formatStat(this.pokemon?.getMaxHp()!, true)}`; // TODO: are those bangs correct? const statValue = addTextObject(this.scene, 120 + 88 * colIndex, 56 + 16 * rowIndex, statValueText, TextStyle.WINDOW_ALT); statValue.setOrigin(1, 0); @@ -862,7 +861,7 @@ export default class SummaryUiHandler extends UiHandler { }); const itemModifiers = (this.scene.findModifiers(m => m instanceof PokemonHeldItemModifier - && m.pokemonId === this.pokemon.id, this.playerParty) as PokemonHeldItemModifier[]) + && m.pokemonId === this.pokemon?.id, this.playerParty) as PokemonHeldItemModifier[]) .sort(modifierSortFunc); itemModifiers.forEach((item, i) => { @@ -876,8 +875,12 @@ export default class SummaryUiHandler extends UiHandler { icon.on("pointerout", () => (this.scene as BattleScene).ui.hideTooltip()); }); - const relLvExp = getLevelRelExp(this.pokemon.level + 1, this.pokemon.species.growthRate); - const expRatio = this.pokemon.level < this.scene.getMaxExpLevel() ? this.pokemon.levelExp / relLvExp : 0; + const pkmLvl = this.pokemon?.level!; // TODO: is this bang correct? + const pkmLvlExp = this.pokemon?.levelExp!; // TODO: is this bang correct? + const pkmExp = this.pokemon?.exp!; // TODO: is this bang correct? + const pkmSpeciesGrowthRate = this.pokemon?.species.growthRate!; // TODO: is this bang correct? + const relLvExp = getLevelRelExp(pkmLvl + 1, pkmSpeciesGrowthRate); + const expRatio = pkmLvl < this.scene.getMaxExpLevel() ? pkmLvlExp / relLvExp : 0; const expLabel = addTextObject(this.scene, 6, 112, i18next.t("pokemonSummary:expPoints"), TextStyle.SUMMARY); expLabel.setOrigin(0, 0); @@ -887,12 +890,12 @@ export default class SummaryUiHandler extends UiHandler { nextLvExpLabel.setOrigin(0, 0); statsContainer.add(nextLvExpLabel); - const expText = addTextObject(this.scene, 208, 112, this.pokemon.exp.toString(), TextStyle.WINDOW_ALT); + const expText = addTextObject(this.scene, 208, 112, pkmExp.toString(), TextStyle.WINDOW_ALT); expText.setOrigin(1, 0); statsContainer.add(expText); - const nextLvExp = this.pokemon.level < this.scene.getMaxExpLevel() - ? getLevelTotalExp(this.pokemon.level + 1, this.pokemon.species.growthRate) - this.pokemon.exp + const nextLvExp = pkmLvl < this.scene.getMaxExpLevel() + ? getLevelTotalExp(pkmLvl + 1, pkmSpeciesGrowthRate) - pkmExp : 0; const nextLvExpText = addTextObject(this.scene, 208, 128, nextLvExp.toString(), TextStyle.WINDOW_ALT); nextLvExpText.setOrigin(1, 0); @@ -924,14 +927,14 @@ export default class SummaryUiHandler extends UiHandler { extraRowOverlay.setOrigin(0, 1); this.extraMoveRowContainer.add(extraRowOverlay); - const extraRowText = addTextObject(this.scene, 35, 0, this.summaryUiMode === SummaryUiMode.LEARN_MOVE ? this.newMove.name : i18next.t("pokemonSummary:cancel"), + const extraRowText = addTextObject(this.scene, 35, 0, this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.newMove ? this.newMove.name : i18next.t("pokemonSummary:cancel"), this.summaryUiMode === SummaryUiMode.LEARN_MOVE ? TextStyle.SUMMARY_PINK : TextStyle.SUMMARY); extraRowText.setOrigin(0, 1); this.extraMoveRowContainer.add(extraRowText); if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { this.extraMoveRowContainer.setVisible(true); - const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[this.newMove.type].toLowerCase()); + const newMoveTypeIcon = this.scene.add.sprite(0, 0, `types${Utils.verifyLang(i18next.resolvedLanguage) ? `_${i18next.resolvedLanguage}` : ""}`, Type[this.newMove?.type!].toLowerCase()); // TODO: is this bang correct? newMoveTypeIcon.setOrigin(0, 1); this.extraMoveRowContainer.add(newMoveTypeIcon); @@ -939,7 +942,7 @@ export default class SummaryUiHandler extends UiHandler { ppOverlay.setOrigin(0, 1); this.extraMoveRowContainer.add(ppOverlay); - const pp = Utils.padInt(this.newMove.pp, 2, " "); + const pp = Utils.padInt(this.newMove?.pp!, 2, " "); // TODO: is this bang correct? const ppText = addTextObject(this.scene, 173, 1, `${pp}/${pp}`, TextStyle.WINDOW); ppText.setOrigin(0, 1); this.extraMoveRowContainer.add(ppText); @@ -949,7 +952,7 @@ export default class SummaryUiHandler extends UiHandler { this.movesContainer.add(this.moveRowsContainer); for (let m = 0; m < 4; m++) { - const move = m < this.pokemon.moveset.length ? this.pokemon.moveset[m] : null; + const move: PokemonMove | null = this.pokemon && this.pokemon.moveset.length > m ? this.pokemon?.moveset[m] : null; const moveRowContainer = this.scene.add.container(0, 16 * m); this.moveRowsContainer.add(moveRowContainer); @@ -1020,13 +1023,13 @@ export default class SummaryUiHandler extends UiHandler { }); } - getSelectedMove(): Move { + getSelectedMove(): Move | null { if (this.cursor !== Page.MOVES) { return null; } - if (this.moveCursor < 4 && this.moveCursor < this.pokemon.moveset.length) { - return this.pokemon.moveset[this.moveCursor].getMove(); + if (this.moveCursor < 4 && this.pokemon && this.moveCursor < this.pokemon.moveset.length) { + return this.pokemon.moveset[this.moveCursor]!.getMove(); // TODO: is this bang correct? } else if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE && this.moveCursor === 4) { return this.newMove; } @@ -1043,7 +1046,7 @@ export default class SummaryUiHandler extends UiHandler { hideMoveSelect() { if (this.summaryUiMode === SummaryUiMode.LEARN_MOVE) { - this.moveSelectFunction(4); + this.moveSelectFunction && this.moveSelectFunction(4); return; } diff --git a/src/ui/target-select-ui-handler.ts b/src/ui/target-select-ui-handler.ts index 48799473343..9a0922715e8 100644 --- a/src/ui/target-select-ui-handler.ts +++ b/src/ui/target-select-ui-handler.ts @@ -18,7 +18,7 @@ export default class TargetSelectUiHandler extends UiHandler { private isMultipleTargets: boolean = false; private targets: BattlerIndex[]; private targetsHighlighted: Pokemon[]; - private targetFlashTween: Phaser.Tweens.Tween; + private targetFlashTween: Phaser.Tweens.Tween | null; private targetBattleInfoMoveTween: Phaser.Tweens.Tween[] = []; constructor(scene: BattleScene) { @@ -68,12 +68,12 @@ export default class TargetSelectUiHandler extends UiHandler { switch (button) { case Button.UP: if (this.cursor < BattlerIndex.ENEMY && this.targets.findIndex(t => t >= BattlerIndex.ENEMY) > -1) { - success = this.setCursor(this.targets.find(t => t >= BattlerIndex.ENEMY)); + success = this.setCursor(this.targets.find(t => t >= BattlerIndex.ENEMY)!); // TODO: is the bang correct here? } break; case Button.DOWN: if (this.cursor >= BattlerIndex.ENEMY && this.targets.findIndex(t => t < BattlerIndex.ENEMY) > -1) { - success = this.setCursor(this.targets.find(t => t < BattlerIndex.ENEMY)); + success = this.setCursor(this.targets.find(t => t < BattlerIndex.ENEMY)!); // TODO: is the bang correct here? } break; case Button.LEFT: diff --git a/src/ui/text.ts b/src/ui/text.ts index ab4a26b6e98..8b76c2f5c62 100644 --- a/src/ui/text.ts +++ b/src/ui/text.ts @@ -185,7 +185,7 @@ export function getTextStyleOptions(style: TextStyle, uiTheme: UiTheme, extraSty if (extraStyleOptions) { if (extraStyleOptions.fontSize) { - const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize.toString().slice(0, -2)); + const sizeRatio = parseInt(extraStyleOptions.fontSize.toString().slice(0, -2)) / parseInt(styleOptions.fontSize?.toString().slice(0, -2) ?? "1"); shadowXpos *= sizeRatio; } styleOptions = Object.assign(styleOptions, extraStyleOptions); diff --git a/src/ui/time-of-day-widget.ts b/src/ui/time-of-day-widget.ts index e71839a84c5..5d2f184e679 100644 --- a/src/ui/time-of-day-widget.ts +++ b/src/ui/time-of-day-widget.ts @@ -147,7 +147,10 @@ export default class TimeOfDayWidget extends Phaser.GameObjects.Container { // Swaps all elements of the icon arrays by shifting the first element onto the end of the array // This ensures index[0] is always the new time of day icon and index[1] is always the current one this.timeOfDayIconPairs.forEach( - icons => icons.push(icons.shift())); + icons => { + const shifted = icons.shift(); + shifted && icons.push(shifted); + }); } /** diff --git a/src/ui/title-ui-handler.ts b/src/ui/title-ui-handler.ts index e2a704f7541..3c25ed34d61 100644 --- a/src/ui/title-ui-handler.ts +++ b/src/ui/title-ui-handler.ts @@ -14,7 +14,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { private splashMessageText: Phaser.GameObjects.Text; private eventDisplay: TimedEventDisplay; - private titleStatsTimer: NodeJS.Timeout; + private titleStatsTimer: NodeJS.Timeout | null; constructor(scene: BattleScene, mode: Mode = Mode.TITLE) { super(scene, mode); @@ -118,7 +118,7 @@ export default class TitleUiHandler extends OptionSelectUiHandler { this.eventDisplay?.clear(); - clearInterval(this.titleStatsTimer); + this.titleStatsTimer && clearInterval(this.titleStatsTimer); this.titleStatsTimer = null; this.scene.tweens.add({ diff --git a/src/ui/ui-theme.ts b/src/ui/ui-theme.ts index 58490e1c618..75725910b82 100644 --- a/src/ui/ui-theme.ts +++ b/src/ui/ui-theme.ts @@ -43,7 +43,7 @@ export function addWindow(scene: BattleScene, x: number, y: number, width: numbe const borderSize = scene.uiTheme ? 6 : 8; - const window = scene.add.nineslice(x, y, `window_${scene.windowType}${getWindowVariantSuffix(windowVariant)}`, null, width, height, borderSize, borderSize, borderSize, borderSize); + const window = scene.add.nineslice(x, y, `window_${scene.windowType}${getWindowVariantSuffix(windowVariant)}`, undefined, width, height, borderSize, borderSize, borderSize, borderSize); window.setOrigin(0, 0); if (mergeMaskLeft || mergeMaskTop || maskOffsetX || maskOffsetY) { diff --git a/src/ui/ui.ts b/src/ui/ui.ts index ec6e91fa526..8ea4270deb3 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -271,10 +271,10 @@ export default class UI extends Phaser.GameObjects.Container { return handler.processInput(button); } - showText(text: string, delay?: integer, callback?: Function, callbackDelay?: integer, prompt?: boolean, promptDelay?: integer): void { + showText(text: string, delay?: integer | null, callback?: Function | null, callbackDelay?: integer | null, prompt?: boolean | null, promptDelay?: integer | null): void { if (prompt && text.indexOf("$") > -1) { const messagePages = text.split(/\$/g).map(m => m.trim()); - let showMessageAndCallback = () => callback(); + let showMessageAndCallback = () => callback && callback(); for (let p = messagePages.length - 1; p >= 0; p--) { const originalFunc = showMessageAndCallback; showMessageAndCallback = () => this.showText(messagePages[p], null, originalFunc, null, true); @@ -290,18 +290,18 @@ export default class UI extends Phaser.GameObjects.Container { } } - showDialogue(text: string, name: string, delay: integer = 0, callback: Function, callbackDelay?: integer, promptDelay?: integer): void { + showDialogue(text: string, name: string | undefined, delay: integer | null = 0, callback: Function, callbackDelay?: integer, promptDelay?: integer): void { // First get the gender of the player (default male) (also used if UNSET) let playerGenderPrefix = "PGM"; if ((this.scene as BattleScene).gameData.gender === PlayerGender.FEMALE) { playerGenderPrefix = "PGF"; } // Add the prefix to the text - const localizationKey = playerGenderPrefix + text; + const localizationKey: string = playerGenderPrefix + text; // Get localized dialogue (if available) let hasi18n = false; - if (i18next.exists(localizationKey as ParseKeys) ) { + if (i18next.exists(localizationKey) ) { text = i18next.t(localizationKey as ParseKeys); hasi18n = true; @@ -341,7 +341,7 @@ export default class UI extends Phaser.GameObjects.Container { const key = playerGenderPrefix + text; - if (i18next.exists(key as ParseKeys) ) { + if (i18next.exists(key) ) { if ((this.scene as BattleScene).skipSeenDialogues && (this.scene as BattleScene).gameData.getSeenDialogues()[key] === true) { return true; } @@ -371,8 +371,8 @@ export default class UI extends Phaser.GameObjects.Container { update(): void { if (this.tooltipContainer.visible) { - const reverse = this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; - this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer.x / 6 + 2 : this.scene.game.input.mousePointer.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer.y / 6 + 2); + const reverse = this.scene.game.input.mousePointer && this.scene.game.input.mousePointer.x >= this.scene.game.canvas.width - this.tooltipBg.width * 6 - 12; + this.tooltipContainer.setPosition(!reverse ? this.scene.game.input.mousePointer!.x / 6 + 2 : this.scene.game.input.mousePointer!.x / 6 - this.tooltipBg.width - 2, this.scene.game.input.mousePointer!.y / 6 + 2); // TODO: are these bangs correct? } } @@ -507,7 +507,7 @@ export default class UI extends Phaser.GameObjects.Container { const doRevertMode = () => { this.getHandler().clear(); - this.mode = this.modeChain.pop(); + this.mode = this.modeChain.pop()!; // TODO: is this bang correct? const touchControls = document.getElementById("touchControls"); if (touchControls) { touchControls.dataset.uiMode = Mode[this.mode]; diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts index c864801d9b4..873bef4ba79 100644 --- a/src/ui/unavailable-modal-ui-handler.ts +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -7,7 +7,7 @@ import * as Utils from "#app/utils"; import i18next from "i18next"; export default class UnavailableModalUiHandler extends ModalUiHandler { - private reconnectTimer: NodeJS.Timeout; + private reconnectTimer: NodeJS.Timeout | null; private reconnectDuration: number; private reconnectCallback: () => void; diff --git a/src/ui/vouchers-ui-handler.ts b/src/ui/vouchers-ui-handler.ts index 370859bed54..169d851599b 100644 --- a/src/ui/vouchers-ui-handler.ts +++ b/src/ui/vouchers-ui-handler.ts @@ -22,10 +22,10 @@ export default class VouchersUiHandler extends MessageUiHandler { private itemsTotal: integer; private scrollCursor: integer; - private cursorObj: Phaser.GameObjects.NineSlice; + private cursorObj: Phaser.GameObjects.NineSlice | null; constructor(scene: BattleScene, mode?: Mode) { - super(scene, mode); + super(scene, mode!); // TODO: is this bang correct? this.itemsTotal = Object.keys(vouchers).length; this.scrollCursor = 0; @@ -192,7 +192,7 @@ export default class VouchersUiHandler extends MessageUiHandler { let updateVoucher = ret; if (!this.cursorObj) { - this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", null, 16, 16, 1, 1, 1, 1); + this.cursorObj = this.scene.add.nineslice(0, 0, "select_cursor_highlight", undefined, 16, 16, 1, 1, 1, 1); this.cursorObj.setOrigin(0, 0); this.voucherIconsContainer.add(this.cursorObj); updateVoucher = true; diff --git a/src/utils.ts b/src/utils.ts index 6a816d5b84d..01a0b8faa42 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -116,7 +116,7 @@ export function randSeedWeightedItem(items: T[]): T { : Phaser.Math.RND.weightedPick(items); } -export function randSeedEasedWeightedItem(items: T[], easingFunction: string = "Sine.easeIn"): T { +export function randSeedEasedWeightedItem(items: T[], easingFunction: string = "Sine.easeIn"): T | null { if (!items.length) { return null; } @@ -246,16 +246,16 @@ export function formatStat(stat: integer, forHp: boolean = false): string { return formatLargeNumber(stat, forHp ? 100000 : 1000000); } -export function getEnumKeys(enumType): string[] { - return Object.values(enumType).filter(v => isNaN(parseInt(v.toString()))).map(v => v.toString()); +export function getEnumKeys(enumType: any): string[] { + return Object.values(enumType).filter(v => isNaN(parseInt(v!.toString()))).map(v => v!.toString()); } -export function getEnumValues(enumType): integer[] { - return Object.values(enumType).filter(v => !isNaN(parseInt(v.toString()))).map(v => parseInt(v.toString())); +export function getEnumValues(enumType: any): integer[] { + return Object.values(enumType).filter(v => !isNaN(parseInt(v!.toString()))).map(v => parseInt(v!.toString())); } -export function executeIf(condition: boolean, promiseFunc: () => Promise): Promise { - return condition ? promiseFunc() : new Promise(resolve => resolve(null)); +export function executeIf(condition: boolean, promiseFunc: () => Promise): Promise { + return condition ? promiseFunc() : new Promise(resolve => resolve(null)); } export const sessionIdKey = "pokerogue_sessionId"; @@ -432,7 +432,7 @@ export function deltaRgb(rgb1: integer[], rgb2: integer[]): integer { } export function rgbHexToRgba(hex: string) { - const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i); + const color = hex.match(/^([\da-f]{2})([\da-f]{2})([\da-f]{2})$/i)!; // TODO: is this bang correct? return { r: parseInt(color[1], 16), g: parseInt(color[2], 16), diff --git a/tsconfig.json b/tsconfig.json index 546ea2a9d12..f8e019a1b8b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -5,7 +5,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "esModuleInterop": true, - "strictNullChecks": false, + "strictNullChecks": true, "sourceMap": false, "strict": false, "rootDir": "./src",