Spaces:
Paused
Paused
| export const Scripts: ModdedBattleScriptsData = { | |
| gen: 9, | |
| actions: { | |
| getDamage(source, target, move, suppressMessages) { | |
| if (typeof move === 'string') move = this.dex.getActiveMove(move); | |
| if (typeof move === 'number') { | |
| const basePower = move; | |
| move = new Dex.Move({ | |
| basePower, | |
| type: '???', | |
| category: 'Physical', | |
| willCrit: false, | |
| }) as ActiveMove; | |
| move.hit = 0; | |
| } | |
| if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) { | |
| if (!target.runImmunity(move.type, !suppressMessages)) { | |
| return false; | |
| } | |
| } | |
| if (move.ohko) return target.maxhp; | |
| if (move.damageCallback) return move.damageCallback.call(this.battle, source, target); | |
| if (move.damage === 'level') { | |
| return source.level; | |
| } else if (move.damage) { | |
| return move.damage; | |
| } | |
| let basePower: number | false | null = move.basePower; | |
| if (move.basePowerCallback) { | |
| basePower = move.basePowerCallback.call(this.battle, source, target, move); | |
| } | |
| if (!basePower) return basePower === 0 ? undefined : basePower; | |
| basePower = this.battle.clampIntRange(basePower, 1); | |
| let critMult; | |
| let critRatio = this.battle.runEvent('ModifyCritRatio', source, target, move, move.critRatio || 0); | |
| if (this.battle.gen <= 5) { | |
| critRatio = this.battle.clampIntRange(critRatio, 0, 5); | |
| critMult = [0, 16, 8, 4, 3, 2]; | |
| } else { | |
| critRatio = this.battle.clampIntRange(critRatio, 0, 4); | |
| if (this.battle.gen === 6) { | |
| critMult = [0, 16, 8, 2, 1]; | |
| } else { | |
| critMult = [0, 24, 8, 2, 1]; | |
| } | |
| } | |
| const moveHit = target.getMoveHitData(move); | |
| moveHit.crit = move.willCrit || false; | |
| if (move.willCrit === undefined) { | |
| if (critRatio) { | |
| moveHit.crit = this.battle.randomChance(1, critMult[critRatio]); | |
| } | |
| } | |
| if (moveHit.crit) { | |
| moveHit.crit = this.battle.runEvent('CriticalHit', target, null, move); | |
| } | |
| // happens after crit calculation | |
| basePower = this.battle.runEvent('BasePower', source, target, move, basePower, true); | |
| if (!basePower) return 0; | |
| basePower = this.battle.clampIntRange(basePower, 1); | |
| // Hacked Max Moves have 0 base power, even if you Dynamax | |
| if ((!source.volatiles['dynamax'] && move.isMax) || (move.isMax && this.dex.moves.get(move.baseMove).isMax)) { | |
| basePower = 0; | |
| } | |
| const level = source.level; | |
| const attacker = move.overrideOffensivePokemon === 'target' ? target : source; | |
| const defender = move.overrideDefensivePokemon === 'source' ? source : target; | |
| const isPhysical = move.category === 'Physical'; | |
| const defenseStat: StatIDExceptHP = move.overrideDefensiveStat || (isPhysical ? 'def' : 'spd'); | |
| const statTable: { [k in StatIDExceptHP]: string } = { atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe' }; | |
| let maxAttack = 0; | |
| let defBoosts = defender.boosts[defenseStat]; | |
| let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive; | |
| let ignorePositiveDefensive = !!move.ignorePositiveDefensive; | |
| if (moveHit.crit) { | |
| ignoreNegativeOffensive = true; | |
| ignorePositiveDefensive = true; | |
| } | |
| const ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0)); | |
| if (ignoreDefensive) { | |
| this.battle.debug('Negating (sp)def boost/penalty.'); | |
| defBoosts = 0; | |
| } | |
| let attack = 0; | |
| for (const attackStat in statTable) { | |
| let atkBoosts = attacker.boosts[attackStat as keyof BoostsTable]; | |
| const ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0)); | |
| if (ignoreOffensive) { | |
| this.battle.debug('Negating (sp)atk boost/penalty.'); | |
| atkBoosts = 0; | |
| } | |
| attack = attacker.calculateStat(attackStat as any, atkBoosts, 1, source); | |
| attack = this.battle.runEvent('Modify' + (statTable as any)[attackStat], source, target, move, attack); | |
| if (attack > maxAttack) maxAttack = attack; | |
| } | |
| let defense = defender.calculateStat(defenseStat, defBoosts, 1, target); | |
| // Apply Stat Modifiers | |
| defense = this.battle.runEvent('Modify' + statTable[defenseStat], target, source, move, defense); | |
| if (this.battle.gen <= 4 && ['explosion', 'selfdestruct'].includes(move.id) && defenseStat === 'def') { | |
| defense = this.battle.clampIntRange(Math.floor(defense / 2), 1); | |
| } | |
| const tr = this.battle.trunc; | |
| // int(int(int(2 * L / 5 + 2) * A * P / D) / 50); | |
| const baseDamage = tr(tr(tr(tr(2 * level / 5 + 2) * basePower * maxAttack) / defense) / 50); | |
| // Calculate damage modifiers separately (order differs between generations) | |
| return this.modifyDamage(baseDamage, source, target, move, suppressMessages); | |
| }, | |
| }, | |
| }; | |