borderlands2
Version:
Borderlands 2 weapon damage and DPS calculation library
348 lines • 19 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
var typescript_memoize_1 = require("typescript-memoize");
var stat_service_1 = require("../../build/service/stat_service");
var stat_type_1 = require("../../build/value_object/stat_type");
var manufacturer_1 = require("../value_object/manufacturer");
var type_1 = require("../value_object/type");
var target_type_1 = require("../../enemy/value_object/target_type");
var elemental_effect_1 = require("../value_object/elemental_effect");
var red_text_1 = require("../../build/object/red_text");
var elemental_damage_coefficients_1 = require("../../enemy/value_object/elemental_damage_coefficients");
// TODO: pure splash damage
var DamageService = /** @class */ (function () {
function DamageService(weapon, context) {
this.weapon = weapon;
this.context = context;
this.statService = new stat_service_1.StatService(weapon, context);
}
DamageService.prototype.getDps = function () {
return this.calculateDps(this.getDamage(), this.getFirstShotDamage());
};
DamageService.prototype.getCritDps = function () {
return this.calculateDps(this.getCritDamage(), this.getFirstShotCritDamage());
};
DamageService.prototype.getTargetTypeDps = function (targetType) {
var dps = this.calculateDps(this.getDamage(targetType), this.getFirstShotDamage(targetType)) + this.getElementalDps(targetType);
return Math.round(dps * 100) / 100;
};
DamageService.prototype.getTargetTypeCritDps = function (targetType) {
var dps = this.calculateDps(this.getCritDamage(targetType), this.getFirstShotCritDamage(targetType)) + this.getElementalDps(targetType);
return Math.round(dps * 100) / 100;
};
DamageService.prototype.getDamage = function (targetType) {
return this.getBaseDamage(targetType) + this.getSplashDamage(targetType);
};
DamageService.prototype.getFirstShotDamage = function (targetType) {
return this.getFirstShotBaseDamage(targetType) + this.getSplashDamage(targetType);
};
DamageService.prototype.getBaseDamage = function (targetType) {
var _a = this.weapon, damage = _a.damage, _b = _a.pellets, pellets = _b === void 0 ? 1 : _b, elementalEffect = _a.elementalEffect;
var buildGunDamage = this.statService.getStat(stat_type_1.StatType.GunDamage);
var elementalEffectiveness = targetType ? this.getElementalEffectiveness(targetType, elementalEffect) : 1;
var ampDamage = this.statService.getStat(stat_type_1.StatType.AmpDamage);
return damage * pellets * (1 + buildGunDamage) * elementalEffectiveness + ampDamage;
};
// refactor duplicate code
DamageService.prototype.getFirstShotBaseDamage = function (targetType) {
var _a = this.weapon, damage = _a.damage, _b = _a.pellets, pellets = _b === void 0 ? 1 : _b, elementalEffect = _a.elementalEffect;
var firstShotGunDamage = this.statService.getStat(stat_type_1.StatType.FirstShotGunDamage);
var buildGunDamage = this.statService.getStat(stat_type_1.StatType.GunDamage);
var elementalEffectiveness = targetType ? this.getElementalEffectiveness(targetType, elementalEffect) : 1;
var ampDamage = this.statService.getStat(stat_type_1.StatType.AmpDamage);
return damage * pellets * (1 + buildGunDamage + firstShotGunDamage) * elementalEffectiveness + ampDamage;
};
DamageService.prototype.getCritDamage = function (targetType) {
var type = this.weapon.type;
// Rocket launcher's can't crit
if (type === type_1.Type.RocketLauncher)
return 0;
var multiplier = this.getWeaponCritMultiplier();
var baseBonus = this.getWeaponCritBaseBonus();
var penalty = this.getWeaponCritPenalty();
var buildCritDamage = this.statService.getStat(stat_type_1.StatType.CritHitDamage);
var splashDamage = this.getSplashDamage(targetType);
return this.getBaseDamage(targetType) * multiplier * (1 + baseBonus + buildCritDamage) / (1 + penalty) + splashDamage;
};
// refactor duplicate code
DamageService.prototype.getFirstShotCritDamage = function (targetType) {
var type = this.weapon.type;
// Rocket launcher's can't crit
if (type === type_1.Type.RocketLauncher)
return 0;
var multiplier = this.getWeaponCritMultiplier();
var baseBonus = this.getWeaponCritBaseBonus();
var penalty = this.getWeaponCritPenalty();
var buildCritDamage = this.statService.getStat(stat_type_1.StatType.CritHitDamage);
var splashDamage = this.getSplashDamage(targetType);
return this.getFirstShotBaseDamage(targetType) * multiplier * (1 + baseBonus + buildCritDamage) / (1 + penalty) + splashDamage;
};
DamageService.prototype.getElementalDps = function (targetType) {
var _a = this.weapon, elementalChance = _a.elementalChance, elementalDps = _a.elementalDps, elementalEffect = _a.elementalEffect, _b = _a.ammoPerShot, ammoPerShot = _b === void 0 ? 1 : _b, _c = _a.pellets, pellets = _c === void 0 ? 1 : _c;
if (!elementalChance || !elementalDps || elementalEffect === undefined)
return 0;
var reloadSpeed = this.getReloadSpeed();
var fireRate = this.getFireRate();
var magazineSize = this.getMagazineSize();
var duration = 0;
switch (elementalEffect) {
case elemental_effect_1.ElementalEffect.Incendiary:
duration = 5;
break;
case elemental_effect_1.ElementalEffect.Shock:
duration = 2;
break;
case elemental_effect_1.ElementalEffect.Corrosive:
duration = 8;
break;
}
var buildElementalEffectChance = this.statService.getStat(stat_type_1.StatType.ElementalEffectChance);
var buildElementalEffectDamage = this.statService.getStat(stat_type_1.StatType.ElementalEffectDamage);
var elementalEffectiveness = targetType ? this.getElementalEffectiveness(targetType, elementalEffect) : 1;
var effectiveProcDps = elementalDps * (1 + buildElementalEffectDamage) * (elementalEffectiveness + buildElementalEffectChance);
var clipEffectiveNumberOfShots = magazineSize / ammoPerShot;
var procsPerClip = clipEffectiveNumberOfShots * elementalChance * pellets;
var clipElementalDamage = procsPerClip * effectiveProcDps * duration;
var clipSpeed = clipEffectiveNumberOfShots / fireRate;
var totalSpeed = reloadSpeed + clipSpeed;
var finalDps = clipElementalDamage / totalSpeed;
return Math.round(finalDps * 100) / 100;
};
DamageService.prototype.calculateDps = function (damage, firstBulletDamage) {
var _a = this.weapon.ammoPerShot, ammoPerShot = _a === void 0 ? 1 : _a;
var reloadSpeed = this.getReloadSpeed();
var fireRate = this.getFireRate();
var magazineSize = this.getMagazineSize();
var clipEffectiveNumberOfShots = magazineSize / ammoPerShot;
var clipSpeed = clipEffectiveNumberOfShots / fireRate;
var totalSpeed = reloadSpeed + clipSpeed;
var totalClipDamage = damage * (clipEffectiveNumberOfShots - 1) + firstBulletDamage;
var finalDps = totalClipDamage / totalSpeed;
return Math.round(finalDps * 100) / 100;
};
DamageService.prototype.getWeaponCritMultiplier = function () {
var _a, _b;
var _c;
var _d = this.weapon, manufacturer = _d.manufacturer, type = _d.type, redText = _d.redText;
var redTextStat = this.getRedTextStat(stat_type_1.StatType.CritHitMultiplier, redText);
var multipliers = (_a = {},
_a[manufacturer_1.Manufacturer.Jakobs] = (_b = {},
_b[type_1.Type.Shotgun] = 2.3,
_b[type_1.Type.AssaultRifle] = 2.3,
_b[type_1.Type.Pistol] = 2.5,
_b),
_a);
var result = (_c = multipliers[manufacturer]) === null || _c === void 0 ? void 0 : _c[type];
return (result !== null && result !== void 0 ? result : 2) + redTextStat;
};
DamageService.prototype.getWeaponCritBaseBonus = function () {
var _a = this.weapon, manufacturer = _a.manufacturer, type = _a.type;
// If the weapon has CritHitDamage property, we just use that since it
// includes any manufacturer or weapon type specific bonuses
var weaponCritDamage = this.getStat(stat_type_1.StatType.CritHitDamage);
if (weaponCritDamage)
return weaponCritDamage;
if (type === type_1.Type.SniperRifle) {
if (manufacturer === manufacturer_1.Manufacturer.Jakobs) {
return 1.6;
}
return 1;
}
return 0;
};
DamageService.prototype.getWeaponCritPenalty = function () {
var _a = this.weapon, manufacturer = _a.manufacturer, type = _a.type, isEtech = _a.isEtech;
if (manufacturer === manufacturer_1.Manufacturer.Jakobs) {
return 0;
}
if (isEtech) {
if (type === type_1.Type.AssaultRifle) {
return 0.7;
}
else if (type === type_1.Type.SniperRifle) {
return 1;
}
}
if (type === type_1.Type.AssaultRifle) {
return 0.2;
}
return 0;
};
DamageService.prototype.getReloadSpeed = function () {
var reloadSpeed = this.weapon.reloadSpeed;
var buildReloadSpeed = this.statService.getStat(stat_type_1.StatType.ReloadSpeed);
return reloadSpeed / (1 + buildReloadSpeed);
};
DamageService.prototype.getFireRate = function () {
var fireRate = this.weapon.fireRate;
var buildFireRate = this.statService.getStat(stat_type_1.StatType.FireRate);
return fireRate * (1 + buildFireRate);
};
DamageService.prototype.getMagazineSize = function () {
var magazineSize = this.weapon.magazineSize;
var buildMagazineSize = this.statService.getStat(stat_type_1.StatType.MagazineSize);
return magazineSize * (1 + buildMagazineSize);
};
DamageService.prototype.getElementalEffectiveness = function (targetType, elementalEffect) {
var _a;
if (elementalEffect === undefined) {
if (targetType === target_type_1.TargetType.Armor) {
return 0.8;
}
return 1;
}
var coefficients = elemental_damage_coefficients_1.ElementalDamageCoefficients(this.context.gameMode || elemental_damage_coefficients_1.GameModeEnum.NormalMode);
var result = (_a = coefficients[elementalEffect]) === null || _a === void 0 ? void 0 : _a[targetType];
return result !== null && result !== void 0 ? result : 1;
};
DamageService.prototype.getSplashDamage = function (targetType) {
var _a = this.weapon, elementalEffect = _a.elementalEffect, type = _a.type, manufacturer = _a.manufacturer;
// Explosive seems to be exclusively splash damage and additional splash damage is not calculated?
// Update: Mostly not true - mostly for Maliwan/Torgue and launchers
// https://forums.gearboxsoftware.com/t/complete-splash-damage-guide/1553510
// maybe this gets calculated as splash multiplier?
if (elementalEffect === elemental_effect_1.ElementalEffect.Explosive) {
if (type === type_1.Type.RocketLauncher) {
// TODO: many launchers have splash damage
return 0;
}
}
return this.getBaseDamage(targetType) * this.getSplashDamageMultiplier();
};
DamageService.prototype.getSplashDamageMultiplier = function () {
// this method needs so much work
var _a = this.weapon, type = _a.type, elementalEffect = _a.elementalEffect, manufacturer = _a.manufacturer, dealsBonusElementalDamage = _a.dealsBonusElementalDamage, redText = _a.redText;
var grenadeDamageStat = this.statService.getStat(stat_type_1.StatType.GrenadeDamage);
// TODO: red text should have SplashDamage stat, pull it
if (redText === red_text_1.RedTextEnum.ByThePeople) {
return 0.7 * (1 + grenadeDamageStat);
}
if (redText === red_text_1.RedTextEnum.GoodForStartingFires) {
return 1 * (1 + grenadeDamageStat);
}
if (redText === red_text_1.RedTextEnum.PeleHumblyRequestsASacrifice || redText === red_text_1.RedTextEnum.FearTheSwarm) {
return 0.8 * (1 + grenadeDamageStat);
}
// We need a doesSplashDamage method
if (!dealsBonusElementalDamage && elementalEffect !== elemental_effect_1.ElementalEffect.Explosive) {
return 0;
}
// TODO: We need a coefficients object to lookup values - type, make and type/make
if (type === type_1.Type.Pistol) {
if (manufacturer === manufacturer_1.Manufacturer.Torgue) {
return 1 * (1 + grenadeDamageStat);
}
else if (manufacturer === manufacturer_1.Manufacturer.Maliwan) {
return 0.8 * (1 + grenadeDamageStat);
}
return 0.8;
}
if (type === type_1.Type.AssaultRifle) {
if (manufacturer === manufacturer_1.Manufacturer.Torgue) {
return 0.9 * (1 + grenadeDamageStat);
}
}
if (type === type_1.Type.SniperRifle) {
if (manufacturer === manufacturer_1.Manufacturer.Maliwan) {
return 0.5 * (1 + grenadeDamageStat);
}
}
return 0.5;
};
DamageService.prototype.getStat = function (statType) {
var _a = this.weapon, stats = _a.stats, redText = _a.redText;
var redTextStat = this.getRedTextStat(statType, redText);
// hacky - if RedText has this stat, it trumps everything
if (redTextStat || !stats)
return redTextStat;
var result = stats.filter(function (stat) { return stat.type === statType; });
return result.reduce(function (memo, stat) { return memo + stat.value; }, 0);
};
DamageService.prototype.getRedTextStat = function (statType, redText) {
if (!redText)
return 0;
return red_text_1.RedTextFactory(redText).getStat(statType);
};
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getDps", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getCritDps", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType; })
], DamageService.prototype, "getTargetTypeDps", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType; })
], DamageService.prototype, "getTargetTypeCritDps", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getFirstShotDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getBaseDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getFirstShotBaseDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getCritDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getFirstShotCritDamage", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getElementalDps", null);
__decorate([
typescript_memoize_1.Memoize(function (damage, firstBulletDamage) { return damage.toString() + firstBulletDamage.toString(); })
], DamageService.prototype, "calculateDps", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getWeaponCritMultiplier", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getWeaponCritBaseBonus", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getWeaponCritPenalty", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getReloadSpeed", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getFireRate", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getMagazineSize", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType, elementalEffect) {
return targetType + ';' + (elementalEffect !== null && elementalEffect !== void 0 ? elementalEffect : '');
})
], DamageService.prototype, "getElementalEffectiveness", null);
__decorate([
typescript_memoize_1.Memoize(function (targetType) { return targetType !== null && targetType !== void 0 ? targetType : ''; })
], DamageService.prototype, "getSplashDamage", null);
__decorate([
typescript_memoize_1.Memoize()
], DamageService.prototype, "getSplashDamageMultiplier", null);
__decorate([
typescript_memoize_1.Memoize(function (statType) { return statType !== null && statType !== void 0 ? statType : ''; })
], DamageService.prototype, "getStat", null);
__decorate([
typescript_memoize_1.Memoize(function (statType, redText) {
return statType + ';' + (redText !== null && redText !== void 0 ? redText : '');
})
], DamageService.prototype, "getRedTextStat", null);
return DamageService;
}());
exports.DamageService = DamageService;
//# sourceMappingURL=damage_service.js.map