1
\$\begingroup\$

I am developing a third-person shooting style game and I want to implement an armor system. The system that I have envisioned does not reduce the damage to 0, but by half. So the incoming damage is 50%. The armor takes the other 50% of damage and is reduced by that amount. Players pick up armor items from the map.

The issue that I'm encountering is an edge case where the damage > armor. Here's what I have tried thus far:

Attempt 1:

-- Performs damage reduction based on armor, if any.  Automatically
-- adds the armor attribute to the humanoid if it doesn't already
-- exist.
local function processArmor(target, damage)
    -- Check if the player actually has armor.  If not
    -- then return full damage.
    local armor = target.human:GetAttribute("Armor")
    if armor == nil or armor == 0 then
        if armor == nil then
            target.human:SetAttribute("Armor", 0)
        end
        return damage
    end

    -- If the player does have armor > 0, then reduce the incoming
    -- damage.
    local d2 = damage / 2
    local t = d2
    d2 -= armor
    armor -= t - d2
    damage = d2

    -- Update armor stat and return.
    target.human:SetAttribute("Armor", armor)
    return damage
end

Attempt 2:

-- Performs damage reduction based on armor, if any.  Automatically
-- adds the armor attribute to the humanoid if it doesn't already
-- exist.
local function processArmor(target, damage)
    -- Check if the player actually has armor.  If not
    -- then return full damage.
    local armor = target.human:GetAttribute("Armor")
    if armor == nil or armor == 0 then
        if armor == nil then
            target.human:SetAttribute("Armor", 0)
        end
        return damage
    end

    -- If the player does have armor > 0, then reduce the incoming
    -- damage.
    if damage == armor then
        armor = 0
    elseif damage < armor then
        armor -= damage
    elseif damage > (armor * 2) then
    else
        local t = damage - armor * 2
        damage -= armor
    end

    -- Update armor stat and return.
    target.human:SetAttribute("Armor", armor)
    return damage
end

Attempt 3:

-- Performs damage reduction based on armor, if any.  Automatically
-- adds the armor attribute to the humanoid if it doesn't already
-- exist.
local function processArmor(target, damage)
    -- Check if the player actually has armor.  If not
    -- then return full damage.
    local armor = target.human:GetAttribute("Armor")
    if armor == nil or armor == 0 then
        if armor == nil then
            target.human:SetAttribute("Armor", 0)
        end
        return damage
    end
    
    -- If the player does have armor > 0, then reduce the incoming
    -- damage.
    local d2 = damage / 2
    if armor == d2 then
        damage = d2
        armor = 0
    elseif armor > d2 then
        damage = d2
        armor -= d2
    else
        -- damage / 2 > armor
        damage = ((d2 - armor) * 2) + armor
        armor = 0
    end

    -- Update armor stat and return.
    target.human:SetAttribute("Armor", armor)
    return damage
 end

Target is just a table that contains references to the parts of the player that was hit. In this case, I'm using the Humanoid under the player's character model. Attempt 3 seems to be the best bet, but under testing, the formula under the else seems to reduce to damage -= armor.

What I expect to see the the incoming damage cut in half. So if player 1 uses a weapon that does 20 damage against player 2, if player 2 has armor, the damage that player 2 see is 10, and the armor value is reduced by 10 (the other half of the damage). This I have mostly correct, except if the armor value is less than half the damage. I'm not sure if I got that correct or not. What I'm seeing is if the armor is 5 and the damage is 20, player 2 is getting 15 damage. I'm not sure if that's correct or not. Currently the formula is:

\$damage = (((damage / 2) - armor) * 2) + armor\$

I have done some research on the subject, but everything that I've found so far involves RPG style games. At this point, I'm looking for suggestions.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The problem with this question is that unless you can tell us what you're expecting to see, we cannot help you with the math involved. \$\endgroup\$
    – Engineer
    Nov 25, 2023 at 23:33
  • \$\begingroup\$ @Engineer Added. \$\endgroup\$ Nov 26, 2023 at 10:45

1 Answer 1

4
\$\begingroup\$

If I've understood correctly, I'd do this as a two step process...

Calculate how much the armor absorbs, then apply the rest to the player.

Something like:

total_damage = 100
armor = 25

damage_to_armor = min(armor, total_damage / 2)
damage_to_person = total_damage - damage_to_armor

In the above scenario, you'd get 25 to the armor (destroying it) and the remaining 75 to the player.

For scenarios where armor >= damage / 2, you get a 50:50 split.

When armor = 0, you get 100% damage to the player.

\$\endgroup\$
3
  • \$\begingroup\$ you could add a line "setArmor(armor - damage_to_armor)" to round this off. \$\endgroup\$
    – datacube
    Nov 27, 2023 at 7:37
  • \$\begingroup\$ I think I see where you're going with this. So the player get's damage / 2. The other damage / 2 goes to the armor. Whatever is left over of the armor damage is added back to the player damage. Is that correct? \$\endgroup\$ Nov 30, 2023 at 1:05
  • \$\begingroup\$ That's the net effect. We assume 50:50 armor:player, then try to take the 50 from the armor. the min() allows us to check if there's enough armor to absorb the damage. If so, the armor gets 50 and the player gets the remaining 50. If, however, there's not enough armor, whatever we didn't take from the armor, we take from the player. So the total will always be 100, with up to 50 from the armor. \$\endgroup\$
    – Basic
    Nov 30, 2023 at 13:32

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .