mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-04-26 02:39:38 -05:00
Implement DPP damage formula (#3583)
This commit is contained in:
parent
096e236afc
commit
9dab8e50e1
|
|
@ -28,6 +28,24 @@ exports.BattleAbilities = {
|
|||
}
|
||||
},
|
||||
},
|
||||
"flashfire": {
|
||||
inherit: true,
|
||||
effect: {
|
||||
noCopy: true, // doesn't get copied by Baton Pass
|
||||
onStart: function (target) {
|
||||
this.add('-start', target, 'ability: Flash Fire');
|
||||
},
|
||||
onModifyDamagePhase1: function (atk, attacker, defender, move) {
|
||||
if (move.type === 'Fire') {
|
||||
this.debug('Flash Fire boost');
|
||||
return this.chainModify(1.5);
|
||||
}
|
||||
},
|
||||
onEnd: function (target) {
|
||||
this.add('-end', target, 'ability: Flash Fire', '[silent]');
|
||||
},
|
||||
},
|
||||
},
|
||||
"flowergift": {
|
||||
inherit: true,
|
||||
onAllyModifyAtk: function (atk) {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,10 @@ exports.BattleItems = {
|
|||
if (!target.volatiles['substitute']) {
|
||||
user.addVolatile('lifeorb');
|
||||
}
|
||||
return basePower * 1.3;
|
||||
return basePower;
|
||||
},
|
||||
onModifyDamagePhase2: function (damage, source, target, move) {
|
||||
return damage * 1.3;
|
||||
},
|
||||
effect: {
|
||||
duration: 1,
|
||||
|
|
@ -197,18 +200,24 @@ exports.BattleItems = {
|
|||
"metronome": {
|
||||
inherit: true,
|
||||
effect: {
|
||||
onBasePower: function (basePower, pokemon, target, move) {
|
||||
if (pokemon.item !== 'metronome') {
|
||||
onStart: function (pokemon) {
|
||||
this.effectData.numConsecutive = 0;
|
||||
this.effectData.lastMove = '';
|
||||
},
|
||||
onBeforeMove: function (pokemon, target, move) {
|
||||
if (!pokemon.hasItem('metronome')) {
|
||||
pokemon.removeVolatile('metronome');
|
||||
return;
|
||||
}
|
||||
if (!this.effectData.move || this.effectData.move !== move.id) {
|
||||
this.effectData.move = move.id;
|
||||
this.effectData.numConsecutive = 0;
|
||||
} else if (this.effectData.numConsecutive < 10) {
|
||||
if (this.effectData.lastMove === move.id) {
|
||||
this.effectData.numConsecutive++;
|
||||
} else {
|
||||
this.effectData.numConsecutive = 0;
|
||||
}
|
||||
return basePower * (1 + (this.effectData.numConsecutive / 10));
|
||||
this.effectData.lastMove = move.id;
|
||||
},
|
||||
onModifyDamagePhase2: function (damage, source, target, move) {
|
||||
return damage * (1 + (this.effectData.numConsecutive / 10));
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -719,6 +719,34 @@ exports.BattleMovedex = {
|
|||
inherit: true,
|
||||
basePower: 130,
|
||||
},
|
||||
lightscreen: {
|
||||
inherit: true,
|
||||
effect: {
|
||||
duration: 5,
|
||||
durationCallback: function (target, source, effect) {
|
||||
if (source && source.hasItem('lightclay')) {
|
||||
return 8;
|
||||
}
|
||||
return 5;
|
||||
},
|
||||
onAnyModifyDamagePhase1: function (damage, source, target, move) {
|
||||
if (target !== source && target.side === this.effectData.target && this.getCategory(move) === 'Special') {
|
||||
if (!move.crit && !move.infiltrates) {
|
||||
this.debug('Light Screen weaken');
|
||||
if (target.side.active.length > 1) return this.chainModify([0xAAC, 0x1000]);
|
||||
return this.chainModify(0.5);
|
||||
}
|
||||
}
|
||||
},
|
||||
onStart: function (side) {
|
||||
this.add('-sidestart', side, 'Light Screen');
|
||||
},
|
||||
onResidualOrder: 21,
|
||||
onEnd: function (side) {
|
||||
this.add('-sideend', side, 'Light Screen');
|
||||
},
|
||||
},
|
||||
},
|
||||
luckychant: {
|
||||
inherit: true,
|
||||
flags: {},
|
||||
|
|
@ -795,6 +823,15 @@ exports.BattleMovedex = {
|
|||
},
|
||||
},
|
||||
},
|
||||
mefirst: {
|
||||
inherit: true,
|
||||
effect: {
|
||||
duration: 1,
|
||||
onModifyDamagePhase2: function (damage) {
|
||||
return damage * 1.5;
|
||||
},
|
||||
},
|
||||
},
|
||||
metronome: {
|
||||
inherit: true,
|
||||
onHit: function (target) {
|
||||
|
|
@ -950,6 +987,34 @@ exports.BattleMovedex = {
|
|||
inherit: true,
|
||||
flags: {},
|
||||
},
|
||||
reflect: {
|
||||
inherit: true,
|
||||
effect: {
|
||||
duration: 5,
|
||||
durationCallback: function (target, source, effect) {
|
||||
if (source && source.hasItem('lightclay')) {
|
||||
return 8;
|
||||
}
|
||||
return 5;
|
||||
},
|
||||
onAnyModifyDamagePhase1: function (damage, source, target, move) {
|
||||
if (target !== source && target.side === this.effectData.target && this.getCategory(move) === 'Physical') {
|
||||
if (!move.crit && !move.infiltrates) {
|
||||
this.debug('Reflect weaken');
|
||||
if (target.side.active.length > 1) return this.chainModify([0xAAC, 0x1000]);
|
||||
return this.chainModify(0.5);
|
||||
}
|
||||
}
|
||||
},
|
||||
onStart: function (side) {
|
||||
this.add('-sidestart', side, 'Reflect');
|
||||
},
|
||||
onResidualOrder: 21,
|
||||
onEnd: function (side) {
|
||||
this.add('-sideend', side, 'Reflect');
|
||||
},
|
||||
},
|
||||
},
|
||||
reversal: {
|
||||
inherit: true,
|
||||
basePowerCallback: function (pokemon, target) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,82 @@ exports.BattleScripts = {
|
|||
}
|
||||
},
|
||||
|
||||
modifyDamage: function (baseDamage, pokemon, target, move, suppressMessages) {
|
||||
// DPP divides modifiers into several mathematically important stages
|
||||
// The modifiers run earlier than other generations are called with ModifyDamagePhase1 and ModifyDamagePhase2
|
||||
|
||||
if (!move.type) move.type = '???';
|
||||
let type = move.type;
|
||||
|
||||
// Burn
|
||||
if (pokemon.status === 'brn' && baseDamage && move.category === 'Physical' && !pokemon.hasAbility('guts')) {
|
||||
baseDamage = this.modify(baseDamage, 0.5);
|
||||
}
|
||||
|
||||
// Other modifiers (Reflect/Light Screen/etc)
|
||||
baseDamage = this.runEvent('ModifyDamagePhase1', pokemon, target, move, baseDamage);
|
||||
|
||||
// Double battle multi-hit
|
||||
if (move.spreadHit) {
|
||||
let spreadModifier = move.spreadModifier || 0.75;
|
||||
this.debug('Spread modifier: ' + spreadModifier);
|
||||
baseDamage = this.modify(baseDamage, spreadModifier);
|
||||
}
|
||||
|
||||
// Weather
|
||||
baseDamage = this.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage);
|
||||
|
||||
baseDamage += 2;
|
||||
|
||||
if (move.crit) {
|
||||
baseDamage = this.modify(baseDamage, move.critModifier || 2);
|
||||
}
|
||||
|
||||
// Mod 2 (Damage is floored after all multipliers are in)
|
||||
baseDamage = Math.floor(this.runEvent('ModifyDamagePhase2', pokemon, target, move, baseDamage));
|
||||
|
||||
// this is not a modifier
|
||||
baseDamage = this.randomizer(baseDamage);
|
||||
|
||||
// STAB
|
||||
if (move.hasSTAB || type !== '???' && pokemon.hasType(type)) {
|
||||
// The "???" type never gets STAB
|
||||
// Not even if you Roost in Gen 4 and somehow manage to use
|
||||
// Struggle in the same turn.
|
||||
// (On second thought, it might be easier to get a Missingno.)
|
||||
baseDamage = this.modify(baseDamage, move.stab || 1.5);
|
||||
}
|
||||
// types
|
||||
move.typeMod = target.runEffectiveness(move);
|
||||
|
||||
move.typeMod = this.clampIntRange(move.typeMod, -6, 6);
|
||||
if (move.typeMod > 0) {
|
||||
if (!suppressMessages) this.add('-supereffective', target);
|
||||
|
||||
for (let i = 0; i < move.typeMod; i++) {
|
||||
baseDamage *= 2;
|
||||
}
|
||||
}
|
||||
if (move.typeMod < 0) {
|
||||
if (!suppressMessages) this.add('-resisted', target);
|
||||
|
||||
for (let i = 0; i > move.typeMod; i--) {
|
||||
baseDamage = Math.floor(baseDamage / 2);
|
||||
}
|
||||
}
|
||||
|
||||
if (move.crit && !suppressMessages) this.add('-crit', target);
|
||||
|
||||
// Final modifier.
|
||||
baseDamage = this.runEvent('ModifyDamage', pokemon, target, move, baseDamage);
|
||||
|
||||
if (!Math.floor(baseDamage)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Math.floor(baseDamage);
|
||||
},
|
||||
|
||||
calcRecoilDamage: function (damageDealt, move) {
|
||||
return this.clampIntRange(Math.floor(damageDealt * move.recoil[0] / move.recoil[1]), 1);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1703,9 +1703,7 @@ class Battle extends Dex.ModdedDex {
|
|||
if (!move) {
|
||||
move = {};
|
||||
}
|
||||
if (!move.type) move.type = '???';
|
||||
let type = move.type;
|
||||
// '???' is typeless damage: used for Struggle and Confusion etc
|
||||
|
||||
let category = this.getCategory(move);
|
||||
let defensiveCategory = move.defensiveCategory || category;
|
||||
|
||||
|
|
@ -1795,7 +1793,16 @@ class Battle extends Dex.ModdedDex {
|
|||
defense = this.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense);
|
||||
|
||||
//int(int(int(2 * L / 5 + 2) * A * P / D) / 50);
|
||||
let baseDamage = Math.floor(Math.floor(Math.floor(2 * level / 5 + 2) * basePower * attack / defense) / 50) + 2;
|
||||
let baseDamage = Math.floor(Math.floor(Math.floor(2 * level / 5 + 2) * basePower * attack / defense) / 50);
|
||||
|
||||
// Calculate damage modifiers separately (order differs between generations)
|
||||
return this.modifyDamage(baseDamage, pokemon, target, move, suppressMessages);
|
||||
}
|
||||
modifyDamage(baseDamage, pokemon, target, move, suppressMessages) {
|
||||
if (!move.type) move.type = '???';
|
||||
let type = move.type;
|
||||
|
||||
baseDamage += 2;
|
||||
|
||||
// multi-target modifier (doubles only)
|
||||
if (move.spreadHit) {
|
||||
|
|
@ -1844,14 +1851,14 @@ class Battle extends Dex.ModdedDex {
|
|||
|
||||
if (move.crit && !suppressMessages) this.add('-crit', target);
|
||||
|
||||
if (pokemon.status === 'brn' && basePower && move.category === 'Physical' && !pokemon.hasAbility('guts')) {
|
||||
if (pokemon.status === 'brn' && move.category === 'Physical' && !pokemon.hasAbility('guts')) {
|
||||
if (this.gen < 6 || move.id !== 'facade') {
|
||||
baseDamage = this.modify(baseDamage, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
// Generation 5 sets damage to 1 before the final damage modifiers only
|
||||
if (this.gen === 5 && basePower && !Math.floor(baseDamage)) {
|
||||
if (this.gen === 5 && !Math.floor(baseDamage)) {
|
||||
baseDamage = 1;
|
||||
}
|
||||
|
||||
|
|
@ -1864,7 +1871,7 @@ class Battle extends Dex.ModdedDex {
|
|||
this.add('-message', target.name + " couldn't fully protect itself and got hurt! (placeholder)");
|
||||
}
|
||||
|
||||
if (this.gen !== 5 && basePower && !Math.floor(baseDamage)) {
|
||||
if (this.gen !== 5 && !Math.floor(baseDamage)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user