--[[
Version 2.3
Last updated 15/1/2026
Calculator made & maintained by Firewatch (navihurricane) <-- Blud who forced a DR rework after making a build with 96% DR
DR has a softcap
Damage Taken here refers to the difference between your HP before hit and your HP after hit (as that's the only way we can measure it)

Let x be the sum_of_all_selected_DR_source
TotalDR = [1 - 2 ^ (x/-75)] * 100
DamageTaken = baseDMG * 2 ^ (x/-75)

ADDITIVE DR = sum_of_all_selected_DR_source, this isn't your actual damage resistance
ACTUAL DR = Refer to TotalDR formula, this is your actual damage resistance converted over using 100% as maximum

From Laz:
Every 75 DR you have from any source (vet, savage, crc, tower specs, arch buffs, whatever, add it all up)
will cut damage recieved in half

hasArmor needs to be set to false if you want to calculate Enemy Plate Carrier & Matrix aswell
]]

local input = {
    baseDmg = 20,
    baseHP = 100, -- please add up hp buffs yourself its basic math i will cry if you tell me to make this easier
    -------------------------------------
    -- Check if you want to include armor
    hasArmor = false,
    -------------------------------------
    -- TOWER
    die_hard = false,
    fury_road = false,
    hasFaceOff = false,
    old_boy = false,
    hasMatrix = false, -- Must enable hasArmor 

    -- AVENGER
    guardian_of_exile = false,     
    guardian_of_exile_allies = 0,  -- Number of allies (0-5)

    -- ARCHANGEL
    resistantnanites_self = false,
    resistantnanites_others = false,
    command_surge = false,
    adrenaline_shot = false,
    -------------------------------------
    -- CONTRACTOR
    ARK_corps_guardian = false,
    merc_vet_agent = false,
    
    -- ARCHITECT
    hasFort6B = false,

    -- FRONTLINER
    savage = false,

    -- JUGGERNAUT
    hasVET = false,
    crc = false,
    hasEnemyPlateCarrier = false, -- Must enable hasArmor

    -- UTILITARIAN
    arm_implants = false,

    -- WILDCARD
    foak_hearts = false
    -------------------------------------
}

local function roundBankers(num)
    if math.abs(num - math.floor(num)) < 0.0001 then
        return math.floor(num)
    end
    
    local integer, fractional = math.modf(num)
    fractional = math.abs(fractional)
    
    if math.abs(fractional - 0.5) < 0.0001 then
        return integer + 1
    end
    
    if fractional > 0.5001 then
        return integer + 1
    else
        return integer
    end
end

function calculateDamageResistance(input)
    local baseDmg = input.baseDmg or 20
    local baseHP = input.baseHP or 100
    local totalDR = 0
    local hasFaceOff = input.hasFaceOff or false
    local command_surge = input.command_surge or false
    
    -- Sum all DR sources for front
    if input.die_hard then totalDR = totalDR + 15 end
    if input.old_boy then totalDR = totalDR + 30 end
    if input.crc then totalDR = totalDR + 30 end
    if input.adrenaline_shot then totalDR = totalDR + 30 end
    if input.resistantnanites_self then totalDR = totalDR + 20 end
    if input.overwatch_on_standby then totalDR = totalDR - 15 end
    if input.ARK_corps_guardian then totalDR = totalDR + 25 end
    if input.merc_vet_agent then totalDR = totalDR + 20 end
    if input.fury_road then totalDR = totalDR + 15 end
    if input.savage then totalDR = totalDR + 25 end
    if input.resistantnanites_others then totalDR = totalDR + 20 end
    if input.berserker_essence then totalDR = totalDR + 30 end
    if input.arm_implants then totalDR = totalDR + 20 end
    if input.hasVET then totalDR = totalDR + 25 end
    if input.hasMatrix and input.hasArmor then totalDR = totalDR + 15 end
    if input.hasFort6B then totalDR = totalDR + 75 end
    if input.foak_hearts then totalDR = totalDR + 44 end
    
    local armor_multi = 1
    if input.hasArmor then
        if input.hasEnemyPlateCarrier then
            armor_multi = 75
        else
            local log2 = math.log(0.67) / math.log(2)
            armor_multi = -75 * log2 -- around 43.33252
        end
        totalDR = totalDR + armor_multi
    end
    
    if input.guardian_of_exile then
        local alliesSheltered = math.min(input.guardian_of_exile_allies or 0, 5)
        totalDR = totalDR + (0.125 * alliesSheltered)
    end
    if input.soultaker then
        local soulstacks = math.min(input.soultaker_stacks or 0, 6)
        totalDR = totalDR + (0.1 * soulstacks)
    end
    
    -- Face/off is a bitch
    local back_requirements = hasFaceOff or command_surge
    local totalDR_back 
    if back_requirements then 
        totalDR_back = totalDR
        if hasFaceOff then totalDR = totalDR + 40 end
        if command_surge then totalDR = totalDR + 45 end
    end

    -- Reusing code so this is messy
    local generalResistance = totalDR 
    local backResistance = (back_requirements) and (totalDR_back) or nil

    -- DR with decimals
    local damageMultiplier = 2 ^ (totalDR / -75)
    local backDamageMultiplier = (back_requirements) and 2 ^ (totalDR_back / -75) or nil

    -- DR %-ified
    local actualResistance = (1 - damageMultiplier) * 100
    local actualResistance_back = (back_requirements) and (1 - backDamageMultiplier) * 100 or nil
    
    local frontDamageRaw = baseDmg * damageMultiplier
    local backDamageRaw = (back_requirements) and baseDmg * backDamageMultiplier * 1.4 or nil
    
    local after_hit_hp = math.floor(baseHP - frontDamageRaw)
    local after_hit_hp_back = (back_requirements) and math.floor(baseHP - backDamageRaw) or nil
    
    local frontDamage = baseHP - after_hit_hp
    local backDamage = (back_requirements) and baseHP - after_hit_hp_back or nil

    -- EHP stuff
    local EHP = roundBankers(baseHP / damageMultiplier)
    local EHP_back = (back_requirements) and roundBankers(baseHP / backDamageMultiplier) or nil

    local DTP = roundBankers(frontDamage / baseHP * 100)
    local DTP_back = (back_requirements) and roundBankers(backDamage / baseHP * 100) or nil

    return {
        baseDamage = baseDmg,
        baseHP = baseHP,
        EHP = EHP,
        EHP_back = EHP_back,
        actualResistance = actualResistance,
        actualResistance_back = actualResistance_back,
        finalResistance = generalResistance,
        backResistance = backResistance,
        frontDamageRaw = frontDamageRaw,
        frontDamage = frontDamage,
        frontDamageMin = math.max(1, frontDamage - 2),
        frontDamageMax = frontDamage + 2,
        DTP = DTP,
        backDamageRaw = backDamageRaw,
        backDamage = backDamage,
        backDamageMin = backDamage and math.max(1, backDamage - 2) or nil,
        backDamageMax = backDamage and (backDamage + 2) or nil,
        DTP_back = DTP_back,
        hasFaceOff = hasFaceOff,
        command_surge = command_surge
    }
end

function printResults(results)
    print("----------------------------")
    
    print("BASE VALUES")
    print(string.format("Base Damage:             %5d", results.baseDamage))
    print(string.format("Base HP:                 %5d", results.baseHP))

    -- Print individual DR buffs
    print("\nDAMAGE RESISTANCE BUFFS")

    local hasDRBuffs = false
    local drBuffs = {
        -- Special conditional
        { name = "• Guardian of Exile:", value = 0, 
        condition = input.guardian_of_exile, special = function()
            local alliesSheltered = math.min(input.guardian_of_exile_allies or 0, 5)
            return string.format("%-25s%6.1f%%", "• Guardian of Exile (" .. alliesSheltered .. " allies):", 12.5 * alliesSheltered)
        end },
        
        { name = "• Soultaker:", value = 0, 
        condition = input.soultaker, special = function()
            local soulstacks = math.min(input.soultaker_stacks or 0, 3)
            return string.format("%-25s%+6d", "• Soultaker (" .. soulstacks .. " stacks):", 10 * soulstacks)
        end },

        { name = "• Res. Nanites (Self):", value = 20, condition = input.resistantnanites_self },
        { name = "• Res. Nanites (Other):", value = 20, condition = input.resistantnanites_others },
        { name = "• Adrenaline Shot:", value = 30, condition = input.adrenaline_shot },
        { name = "• Command Surge:", value = 45, condition = input.command_surge },
        { name = "• Berserker Essence:", value = 30, condition = input.berserker_essence },
        
        { name = "• Die Hard:", value = 15, condition = input.die_hard },
        { name = "• Old Boy:", value = 30, condition = input.old_boy },
 --       { name = "• Overwatch on Standby:", value = -15, condition = input.overwatch_on_standby },
        { name = "• ARK Corps Guardian:", value = 25, condition = input.ARK_corps_guardian },
        { name = "• MERC Veteran Agent:", value = 20, condition = input.merc_vet_agent },
        { name = "• Fury Road:", value = 15, condition = input.fury_road },
        { name = "• CRC Light Vest:", value = 30, condition = input.crc },
        { name = "• Savage:", value = 25, condition = input.savage },
        { name = "• V.E.T. Suit:", value = 25, condition = input.hasVET },
        { name = "• ARM Implants:", value = 20, condition = input.arm_implants },
        { name = "• Fort-6B:", value = 75, condition = input.hasFort6B },
        { name = "• Four of a Kind (Hearts):", value = 44, condition = input.foak_hearts },
        
        -- Armor
        { name = "• Matrix Plates:", value = 15, condition = input.hasArmor and input.hasMatrix },
        { name = "• Enemy Plate Carrier:", value = input.hasArmor and 75 or 0, 
        condition = input.hasArmor and input.hasEnemyPlateCarrier },
        { name = "• Base Armor:", value = input.hasArmor and 43 or 0, 
        condition = input.hasArmor and not input.hasEnemyPlateCarrier },
    
        
        { name = "• Face/Off Front Buff:", value = 40, condition = input.hasFaceOff },
    }

    for _, buff in ipairs(drBuffs) do
        if buff.condition then
            hasDRBuffs = true
            if buff.special then
                print(buff.special())
            else
                print(string.format("%-25s%+7d", buff.name, buff.value))
            end
        end
    end

    if not hasDRBuffs then
        print(string.format("%-25s%s", "• None", ""))
    end

    print("\nTOTAL ADDITIVE RESISTANCE")
    if results.hasFaceOff or results.command_surge then
        print(string.format("Front Additive:       %6d", roundBankers(results.finalResistance)))
        print(string.format("Back Additive:        %6d", roundBankers(results.backResistance)))
    else
        print(string.format("ADD.  Resistance:       %6d", roundBankers(results.finalResistance)))
    end

    print("\nACTUAL RESISTANCE")
    if results.hasFaceOff or results.command_surge then
        print(string.format("Front Resistance:       %5d%%", roundBankers(results.actualResistance)))
        print(string.format("Back Resistance:        %5d%%", roundBankers(results.actualResistance_back)))
    else
        print(string.format("Final Resistance:       %5d%%", roundBankers(results.actualResistance)))
    end

    print("\nEFFECTIVE HP")
    if results.hasFaceOff or results.command_surge then 
        print(string.format("Front Effective HP:      %5d", results.EHP))
        print(string.format("Back Effective HP:       %5d", results.EHP_back))
    else
        print(string.format("Effective HP:            %5d", results.EHP))
    end
    
    print("\nDAMAGE TAKEN")
    print(string.format("Damage Received:        %2d (%.1f%% of HP)", 
        results.frontDamage, results.DTP))
    if results.hasFaceOff or results.command_surge then
        print(string.format("Back Damage:            %2d (%.1f%% of HP)", 
            results.backDamage, results.DTP_back))
    end

    print("\nDAMAGE TAKEN RAW")
    print(string.format("Damage Received:        %.3f", results.frontDamageRaw))
    if results.hasFaceOff or results.command_surge then
        print(string.format("Back Damage:            %.3f", results.backDamageRaw))
    end
    
    print("\n----------------------------")
end

local results = calculateDamageResistance(input)
printResults(results)

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: