UNPKG

borderlands2

Version:

Borderlands 2 weapon damage and DPS calculation library

348 lines 19 kB
"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