浏览代码

balance #1745: Refactored combat.js completely and added a mitigator for pvp attacks

tags/v0.9.0^2
Shaun 3 年前
父节点
当前提交
917cb49779
共有 4 个文件被更改,包括 210 次插入88 次删除
  1. +36
    -0
      src/server/combat/avoid.js
  2. +36
    -88
      src/server/combat/combat.js
  3. +56
    -0
      src/server/combat/mitigate.js
  4. +82
    -0
      src/server/combat/scale.js

+ 36
- 0
src/server/combat/avoid.js 查看文件

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

+ 36
- 88
src/server/combat/combat.js 查看文件

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

+ 56
- 0
src/server/combat/mitigate.js 查看文件

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

+ 82
- 0
src/server/combat/scale.js 查看文件

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

正在加载...
取消
保存