@@ -0,0 +1,36 @@ | |||
//Prebound Methods | |||
const mathRandom = Math.random.bind(Math); | |||
//Helpers | |||
const willBlock = ({ isAttack, tgtValues: { blockAttackChance, blockSpellChance } }) => { | |||
const blockChance = isAttack ? blockAttackChance : blockSpellChance; | |||
const roll = mathRandom() * 100; | |||
const result = roll < blockChance; | |||
return result; | |||
}; | |||
const willDodge = ({ isAttack, tgtValues: { dodgeAttackChance, dodgeSpellChance } }) => { | |||
const dodgeChance = isAttack ? dodgeAttackChance : dodgeSpellChance; | |||
const roll = mathRandom() * 100; | |||
const result = roll < dodgeChance; | |||
return result; | |||
}; | |||
//Method | |||
const avoid = (config, result) => { | |||
//Heals, among other things, should not be avoided | |||
if (config.noMitigate) | |||
return; | |||
result.blocked = willBlock(config); | |||
if (!result.blocked) | |||
result.dodged = willDodge(config); | |||
}; | |||
//Exports | |||
module.exports = avoid; |
@@ -1,91 +1,39 @@ | |||
let max = Math.max.bind(Math); | |||
let mathRandom = Math.random.bind(Math); | |||
//Imports | |||
const avoid = require('./avoid'); | |||
const scale = require('./scale'); | |||
const mitigate = require('./mitigate'); | |||
//Method | |||
const getDamage = config => { | |||
const { damage, element } = config; | |||
//Add convenience properties | |||
config.srcValues = config.source.stats.values; | |||
config.tgtValues = config.target.stats.values; | |||
if (element) | |||
config.elementName = `element${element[0].toUpperCase()}${element.substr(1)}`; | |||
const result = { | |||
amount: damage, | |||
blocked: false, | |||
dodged: false, | |||
crit: false, | |||
element | |||
}; | |||
avoid(config, result); | |||
scale(config, result); | |||
mitigate(config, result); | |||
//Remove convenience properties | |||
delete config.srcValues; | |||
delete config.tgtValues; | |||
delete config.elementName; | |||
return result; | |||
}; | |||
//Exports | |||
module.exports = { | |||
getDamage: function (config) { | |||
let srcValues = config.source.stats.values; | |||
let tgtValues = config.target.stats.values; | |||
let amount = config.damage; | |||
let blocked = false; | |||
let dodged = false; | |||
let isCrit = false; | |||
//Don't block heals | |||
if (!config.noMitigate) { | |||
let blockChance = config.isAttack ? tgtValues.blockAttackChance : tgtValues.blockSpellChance; | |||
if (mathRandom() * 100 < blockChance) { | |||
blocked = true; | |||
amount = 0; | |||
} | |||
if (!blocked) { | |||
let dodgeChance = config.isAttack ? tgtValues.dodgeAttackChance : tgtValues.dodgeSpellChance; | |||
if (mathRandom() * 100 < dodgeChance) { | |||
dodged = true; | |||
amount = 0; | |||
} | |||
} | |||
} | |||
if (!blocked && !dodged && !config.noScale) { | |||
if (config.statType) { | |||
let statValue = 0; | |||
let statType = config.statType; | |||
if (!(statType instanceof Array)) | |||
statType = [statType]; | |||
statType.forEach(function (s) { | |||
statValue += srcValues[s]; | |||
}); | |||
statValue = max(1, statValue); | |||
let statMult = config.statMult || 1; | |||
amount *= statValue * statMult; | |||
} | |||
let dmgPercent = 100 + (srcValues.dmgPercent || 0); | |||
if (config.isAttack) | |||
dmgPercent += srcValues.physicalPercent || 0; | |||
else | |||
dmgPercent += srcValues.spellPercent || 0; | |||
if (config.element) { | |||
let elementName = 'element' + config.element[0].toUpperCase() + config.element.substr(1); | |||
dmgPercent += (srcValues[elementName + 'Percent'] || 0); | |||
//Don't mitigate heals | |||
if (!config.noMitigate) { | |||
let resist = tgtValues[elementName + 'Resist'] || 0; | |||
amount *= max(0.5 + max((1 - (resist / 100)) / 2, -0.5), 0.5); | |||
} | |||
} else if (!config.noMitigate) | |||
amount *= max(0.5 + max((1 - ((tgtValues.armor || 0) / (srcValues.level * 50))) / 2, -0.5), 0.5); | |||
amount *= (dmgPercent / 100); | |||
if (!config.noCrit) { | |||
let critChance = srcValues.critChance; | |||
critChance += (config.isAttack) ? srcValues.attackCritChance : srcValues.spellCritChance; | |||
let critMultiplier = srcValues.critMultiplier; | |||
critMultiplier += (config.isAttack) ? srcValues.attackCritMultiplier : srcValues.spellCritMultiplier; | |||
if ((config.crit) || (mathRandom() * 100 < critChance)) { | |||
isCrit = true; | |||
amount *= (critMultiplier / 100); | |||
} | |||
} | |||
} | |||
return { | |||
amount, | |||
blocked, | |||
dodged, | |||
crit: isCrit, | |||
element: config.element | |||
}; | |||
} | |||
getDamage | |||
}; |
@@ -0,0 +1,56 @@ | |||
//Prebound Methods | |||
const max = Math.max.bind(Math); | |||
const pow = Math.pow.bind(Math); | |||
//Helpers | |||
const mitigateResistances = ({ elementName, noMitigate, tgtValues }, result) => { | |||
//Don't mitigate physical damage | |||
if (!elementName) | |||
return; | |||
const resist = tgtValues[elementName + 'Resist'] || 0; | |||
const resistanceMultiplier = max(0.5 + max((1 - (resist / 100)) / 2, -0.5), 0.5); | |||
result.amount *= resistanceMultiplier; | |||
}; | |||
const mitigateArmor = ({ element, tgtValues, srcValues }, result) => { | |||
//Don't mitigate elemental damage | |||
if (element) | |||
return; | |||
const armorMultiplier = max(0.5 + max((1 - ((tgtValues.armor || 0) / (srcValues.level * 50))) / 2, -0.5), 0.5); | |||
result.amount *= armorMultiplier; | |||
}; | |||
const mitigatePvp = ({ source, target, srcValues }, result) => { | |||
const isPvp = ( | |||
source.player || (source.follower && source.follower.master && source.follower.master.player) && | |||
target.player || (target.follower && target.follower.master && target.follower.master.player) | |||
); | |||
if (!isPvp) | |||
return; | |||
const multiplier = pow(2, srcValues.level / 5); | |||
result.amount *= multiplier; | |||
}; | |||
//Method | |||
const mitigate = (config, result) => { | |||
const { blocked, dodged } = result; | |||
//Heals, among other things, should not be mitigated | |||
if (blocked || dodged || config.noMitigate) | |||
return; | |||
mitigateResistances(config, result); | |||
mitigateArmor(config, result); | |||
mitigatePvp(config, result); | |||
}; | |||
//Exports | |||
module.exports = mitigate; |
@@ -0,0 +1,82 @@ | |||
//Prebound Methods | |||
const mathRandom = Math.random.bind(Math); | |||
const max = Math.max.bind(Math); | |||
//Helpers | |||
const scaleStatType = (config, result) => { | |||
const { statType, statMult = 1, srcValues } = config; | |||
if (!statType) | |||
return; | |||
let statValue = 0; | |||
if (!statType.push) | |||
statValue = srcValues[statType]; | |||
else { | |||
statType.forEach(s => { | |||
statValue += srcValues[s]; | |||
}); | |||
} | |||
statValue = max(1, statValue); | |||
result.amount *= statValue * statMult; | |||
}; | |||
const scalePercentMultipliers = ({ isAttack, elementName, srcValues }, result) => { | |||
const { dmgPercent = 0, physicalPercent = 0, spellPercent = 0 } = srcValues; | |||
let totalPercent = 100 + dmgPercent; | |||
if (isAttack) | |||
totalPercent += physicalPercent; | |||
else | |||
totalPercent += spellPercent; | |||
if (elementName) | |||
totalPercent += (srcValues[elementName + 'Percent'] || 0); | |||
result.amount *= (totalPercent / 100); | |||
}; | |||
const scaleCrit = ({ noCrit, isAttack, crit: forceCrit, srcValues }, result) => { | |||
if (noCrit) | |||
return; | |||
const { critChance, attackCritChance, spellCritChance } = srcValues; | |||
const { critMultiplier, attackCritMultiplier, spellCritMultiplier } = srcValues; | |||
let totalCritChance = critChance; | |||
let totalCritMultiplier = critMultiplier; | |||
if (isAttack) { | |||
totalCritChance += attackCritChance; | |||
totalCritMultiplier += attackCritMultiplier; | |||
} else { | |||
totalCritChance += spellCritChance; | |||
totalCritMultiplier += spellCritMultiplier; | |||
} | |||
const didCrit = forceCrit || mathRandom() * 100 < totalCritChance; | |||
if (didCrit) { | |||
result.isCrit = true; | |||
result.amount *= (totalCritMultiplier / 100); | |||
} | |||
}; | |||
//Method | |||
const scale = (config, result) => { | |||
const { blocked, dodged } = result; | |||
if (blocked || dodged || config.noScale) | |||
return; | |||
scaleStatType(config, result); | |||
scalePercentMultipliers(config, result); | |||
scaleCrit(config, result); | |||
}; | |||
//Exports | |||
module.exports = scale; |