Implement DPP damage formula (#3583)

This commit is contained in:
Caleb Young 2017-06-11 08:18:34 -07:00 committed by Guangcong Luo
parent 096e236afc
commit 9dab8e50e1
5 changed files with 190 additions and 15 deletions

View File

@ -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) {

View File

@ -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));
},
},
},

View File

@ -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) {

View File

@ -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);
},

View File

@ -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;
}