• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Armor Altering v1.2.3

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: Rilene
The Armor Altering library is a simple way to change the way armor works.
The damage, dealt to the unit is calculated with the formula:
base damage - armor
Instead of the normal way:
base damage / (armor*armor_coef + 1)

The struct can also be used to get unit's armor. Just use local DamageAndArmor base = DamageAndArmor.create(unit, 0). And then base.armor contains the value of the unit's armor.

Requires Bribe's Damage Engine, which requires the GUI unit Indexer

Version 1.2.3:
1) Added a constant boolean "NEGATIVE_DAMAGE_HEALS"
2) Added a check to see if the base damage is less than the damage dealt (and actions taken depend on the boolean, mentioned in (1)).

Version 1.2.2.1:
1) made all the globals private
2) cleared refference leak (inside the method)

Version 1.2.2:
- Improved the Logarithmic functions. Only 1 loop is needed to determine the armor, instead of 2 loops.

Version 1.2.1:
- Increased the logarithm's accuracy a bit :p

Version 1.2:
1) Now it's working properly with negative armor values
2) Added a trigger, that makes testing easier (on the sample map)

Version 1.1:
1) Added a condition, checking if the damage is spell or not, so spells wouldn't get their damage reduced by armor.
2) Started using the Damage Engine's variable for the bonus HP ability, instead of making another variable, with the same value :p

Version 1:
Initial release


JASS:
library ArmorAltering

    globals
// The higher the test damage - the more accurate results it gives. When it's set to "10" - it will think that a unit with
// 2 armor has "1.96" armor (or something like that)
        private constant real TEST_DAMAGE = 50000.00
// The value you have set for the armor rating in the Gameplay constants. DO NOT SET IT TO 0 (neither there, neither here)!
// The 2 values must be the same for this system to work.
        private constant real ARMOR_COEFICIENT = 0.06
        private constant integer ITERATIONS = 20
// If a unit's armor is higher than the damage, dealt to it - it gets healed instead.
        private constant boolean NEGATIVE_DAMAGE_HEALS = false
    endglobals

    function Ln takes real x, real min, real max returns real
        local real mid
        local integer i = ITERATIONS
        loop
            set mid = ( min + max )/2
            exitwhen(i <= 0)
            set i = i - 1
            if (Pow(bj_E,mid) > x) then
                set max = mid
            else
                set min = mid
            endif
        endloop
        return mid
    endfunction
    
    function Log takes real v, real x,real min, real max returns real
        local real mid
        local integer i=ITERATIONS
        loop
            set mid=(min+max)/2
            exitwhen(i<=0)
            set i=i-1
            if (Pow(v,mid) > x) then
                if v >= 1 then
                    set max = mid
                else
                    set min = mid
                endif
            else
                if v >= 1 then
                    set min = mid
                else
                    set max = mid
                endif
            endif
        endloop
        return mid
    endfunction
    
    function Lg takes real x, real min, real max returns real
        local real mid
        local integer i=ITERATIONS
        loop
            set mid=(min+max)/2
            exitwhen(i<=0)
            set i=i-1
            if (Pow(10,mid) > x) then
                set max = mid
            else
                set min = mid
            endif
        endloop
        return mid
    endfunction

    struct DamageAndArmor
        real damage
        real armor

        static method create takes unit u, real init_dam returns DamageAndArmor
            local DamageAndArmor DnA = DamageAndArmor.allocate()
// Remembers the unit's original health.
            local real health = GetWidgetLife(u)
            local real max_hp
            local real armor_rating
// Gives the unit extra health, so it can survive the damage, that it's about to take.
            call UnitAddAbility(u, udg_DamageBlockingAbility)
            set max_hp = GetUnitState(u, UNIT_STATE_MAX_LIFE)
// Heals the unit to max hp (with the bonus).
            call SetWidgetLife(u, max_hp)
// Disables the damage event trigger to avoid a loop until the unit dies.
            call DisableTrigger(udg_DamageEventTrigger)
// Damage the unit.
            call UnitDamageTarget( udg_DamageEventSource, u, TEST_DAMAGE, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS )
// Re-enables the damage event trigger.
            call EnableTrigger(udg_DamageEventTrigger)
// Gets the unit's armor rating (which is different than the unit's armor), based on the damage the unit has actually taken.
            set armor_rating = TEST_DAMAGE/(max_hp - GetWidgetLife(u))
// Calculates the original damage the unit was supposed to take.
            set DnA.damage = init_dam*armor_rating
            debug call BJDebugMsg(R2S(armor_rating))
// Calculates the unit's armor.
            if armor_rating >= 1 then
                set DnA.armor = (armor_rating - 1)/ARMOR_COEFICIENT
            else
                set DnA.armor = -Log(1 - ARMOR_COEFICIENT, 2 - (1/armor_rating), 0, 20)
                if DnA.armor + I2R(R2I(DnA.armor)) < -0.95 then
                    set DnA.armor = I2R(R2I(DnA.armor - 1))
                endif
            endif
// Heals the unit to its max HP (with the bonus healt), so it wouldn't die when the bonus is removed.
            call SetWidgetLife( u , max_hp )
// Removes bonus health.
            call UnitRemoveAbility( u , udg_DamageBlockingAbility )
// Returns unit's original health.
            call SetWidgetLife( u , health )
            set u = null
            return DnA
        endmethod
    endstruct

    private function Armor_altering takes nothing returns boolean
        local DamageAndArmor Base
        if not udg_IsDamageSpell then
            set Base = DamageAndArmor.create(udg_DamageEventTarget, udg_DamageEventAmount)
            debug call BJDebugMsg("armor = "+R2S(Base.armor)+", base damage = "+R2S(Base.damage))
// Sets the damage dealt to the unit to be 'the base damage, dealt to it - its armor'.
            static if NEGATIVE_DAMAGE_HEALS then
                set udg_DamageEventAmount = Base.damage - Base.armor
                if Base.damage >= Base.armor then
                    set udg_DamageEventType = udg_DamageTypeReduced
                else
                    set udg_DamageEventType = udg_DamageTypeHeal
                endif
            else
                if Base.damage > Base.armor then
                    set udg_DamageEventAmount = Base.damage - Base.armor
                    set udg_DamageEventType = udg_DamageTypeReduced
                else
                    set udg_DamageEventAmount = 0
                    set udg_DamageEventType = udg_DamageTypeBlocked
                endif
            endif
            call DamageAndArmor.destroy(Base)
        endif
        return false
    endfunction

//===========================================================================
    function InitTrig_Armor_altering takes nothing returns nothing
        set gg_trg_Armor_altering = CreateTrigger(  )
        call TriggerRegisterVariableEvent( gg_trg_Armor_altering, "udg_DamageModifierEvent", EQUAL, 1.00 )
        call TriggerAddCondition( gg_trg_Armor_altering, Condition(function Armor_altering) )
    endfunction
    
endlibrary

Cridits given to Vexorian for his Logarithm library, and to Bribe for his Damage Engine.

Keywords:
Armor, Alternative, Damage, Calculating, Altering.
Contents

Damage Engine Testmap (Map)

Reviews
4th Apr 2016 Your resource has been reviewed by BPower. In case of any questions or for reconfirming the moderator's rating, please make use of the Quick Reply function of this thread. Review: Useful short piece of code. I'm struggling a...

Moderator

M

Moderator

4th Apr 2016

General Info

Your resource has been reviewed by BPower.
In case of any questions or for reconfirming the moderator's rating,
please make use of the Quick Reply function of this thread.
Review:

Useful short piece of code. I'm struggling a bit with function Ln, Log and Lg not beeing private.
Logarithm isn't used very frequently in wc3 coding, yet it would be great to outsource these
functions into a seperate library with the name Logarithm.

I think this kind of snippet would be a good candidate for the JASS section.

Troubleshooting:

  • The debug messages must be removed.
    Debug messages should only pop up if an error is encountered or for your own tests.
    ---
  • set u = null is not required.


Review changelog:
  1. -
 
Level 12
Joined
Jan 2, 2016
Messages
973
Well, just a note for those, using my Custom Multishot:
I haven't really tested the compatibility between my multishot and this system, but I think that they wouldn't work together, since my multishot has this system in-built in it :p (Tho it doesn't calcuate the armor, it only uses the armor_rating to get the base damage).

However, with the current version of my multishot - only few lines of code need to be changed in order for it to work with this system, and I would make a compatible MS version, if anyone wants to use both systems :p

Oh, and by the way, only Log is used from the Logarithmic functions. The other 2 are there, just in case someone wants to use them on their map (tho that's doubtful)

(I actually used the Lg function to test how low can reals go... they can reach smaller values than I thought - 37 diggits, after the decimal point)
Reached -37 without a problem, and the next number I got was -37.930

JASS:
function Lg takes real x, real min, real max returns real
    local real mid
    local integer i = 20
    loop
        set mid = (min+max)/2
        exitwhen(i<=0)
        set i=i-1
        if (Pow(10,mid) > x) then
            set max = mid
        else
            set min = mid
        endif
    endloop
    return mid
endfunction

function Test takes nothing returns boolean
    local integer i = 1
    local integer j
    local real r
    loop
        exitwhen i > 77
        set j = 1
        set r = 1
        loop
            exitwhen j > i
            set r = r/10
            set j = j + 1
        endloop
        call BJDebugMsg(R2S(Lg(r, -77, 0)))
        set i = i + 1
    endloop
    return false
endfunction

function InitTrig_Test takes nothing returns nothing
    set gg_trg_Test = CreateTrigger(  )
    call TriggerRegisterPlayerEventEndCinematic( gg_trg_Test, Player(0) )
    call TriggerAddCondition( gg_trg_Test, Condition(function Test) )
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
Well, I'm kind a using Vexorian's Logarithm library, which can be found here.
I just improved it a bit, by adding Lg and Ln (his Log function was actually Ln, but I made another Log, following his algorithm, and renamed his Log to Ln), and by setting the min and max every time the function is called (for more accurate results).
Didn't really notice this :D
I can fix it if you want :p
 
Level 4
Joined
Feb 12, 2016
Messages
71
Why would it not be private just because it's constant?

The idea behind public and private so not the same as "should be changeable from outside or not". It's about encapsulation and clear statement that this variable is intern to the class and of no interest to the user (of the class).

As a matter of fact, as user of a class I really don't want to see internal stuff of these "classes", because it would only confuse me and make me wonder what they are used for.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Bad copy of Rising_Dusk "ArmorUtils"

I didn't really know that exsisted so I can "copy" it.
And further, why is it a "bad" copy?
I'd say that my way is a bit easier to configure, cuz in AromrUtils you need to calculate Ln(1 - armor_reduction_rating) manually and write it there as a constant (if you for some reason change it from 0.06 to something else). While here it's done automatically, since I added the normal Log function.

+ His system could kill a unit with HP near the test damage amount if it has negative armor.

+ The idea of THIS system is to change the way armor works, while ArmorUtils only calculates armor, and the base damage (and the actual damage the unit will take if you deal xx base damage to it).
And I'd say that my system is doing that more efficiently, cuz it's getting the base damage and the armor in the same function. It doesn't need to get unit armor trough a function call, and then use the returnes value in another function call to get the base damage.
 
Level 10
Joined
Aug 21, 2010
Messages
316
You say that you did not know that there is such a system.So how do you know so much about him.You took so many details about the system and claim that you did not know of its existence.In any case , when I compare the two systems , the similarity is evident.And one more thing , I use his system for years and there is no problem as you claim.
 
Level 12
Joined
Jan 2, 2016
Messages
973
You say that you did not know that there is such a system.So how do you know so much about him.

As BPower said "Google", I simply wrote "Warcraft III ArmorUtils" in google, and found his system.
Since our systems are indeed similar - it was easy to understand what he's doing, and it was easy to tell the differences between our 2 systems.

I just don't understand why he has the attack type configurable. If it's set to anything different than "ATTACK_TYPE_CHAOS" - he will get bad results for the armor.
My system, as it is now handles the attack type vs armor type this way:
a unit deals 100 base damage to a unit with 10 armor, and armor type, that takes 50% more damage from the attack type of the attacking unit.
The unit will take 93.75 damage, after calculating the armor - it will calculate the base damage as 150 (100*1.5).
Then it will deal 140 damage to the unit (150-10).

I can't really make it work any other way, without making a hashtable, and there put the attack type and armor type of all the units.
But then people would need to manually put the att type and armor type of all the custom units they have on their map, which would be inconvenient.

If I were to do that, then I could've made it work like:
a unit deals 100 base damage to a unit with 10 armor, and armor type, that takes 50% more damage from the attack type of the attacking unit.
The unit will take 93.75 damage, after calculating the armor - it will calculate the base damage as 100.
Then it will deal 135 damage to the unit (100 - 10)*1.5

However, there isn't much difference between the damage actually dealt (for armor values up to 20), yet the amount of work required is immense to apply the 2-nd way xP
+ Personally I LIKE the way it is now. Both ways are "realistic" :p
The difference is that 1-st way calculates the armor type ratio 1-st, and armor value ratio later, while the 2-nd way calculates the armor value ration 1-st and the armor type ratio later..
 
Level 12
Joined
Jan 2, 2016
Messages
973
Nonsense! This is simply not true.

Oh, yeah, I had another look at it.
Thought he's setting the unit's life to DAMAGE_TEST if it's lower than that value, but he was setting it to DAMAGE_LIFE (which is almost 2-ce higher amount, and a unit can't deal more than 171% of its damage, unless someone sets the attack type of the damage to the attack type the target unit is vulnrable to... THEN it can die :p.
 
Level 10
Joined
Aug 21, 2010
Messages
316
Oh, yeah, I had another look at it.
Thought he's setting the unit's life to DAMAGE_TEST if it's lower than that value, but he was setting it to DAMAGE_LIFE (which is almost 2-ce higher amount, and a unit can't deal more than 171% of its damage, unless someone sets the attack type of the damage to the attack type the target unit is vulnrable to... THEN it can die :p.

first armor, now damage. I think you're totally confused lol. Have you tried to test the ArmorUtils,probably did not
 
Level 10
Joined
Aug 21, 2010
Messages
316
@zv27 if you have some constructive to contribute go ahead.
While WereElf outlines facts and comparisons of the two system, you
react with subliminal agression which is lacking in substance.
Consider that as warning.

"subliminal agression "???

There is no intention for any aggression,only ironic comedy. Not for a moment I did not hurt anyone.There is no reason to threaten me.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
I didn't find the time to read ArmorUtils and check what you wrote about it.
I'll try to do it tomorrow.

Btw I think this one would be also good material for the JASS section.
Meeting requirements there is hard, but those submission who pass there are normally top quality.

You would have our veterans full attention. :)
 
Top