library ArmorDamageReduction
globals
private constant integer ATTACK_TYPE_COUNT = 8
private constant integer DEFENSE_TYPE_COUNT = 8
private real effectiveHPFactor = 0.06 // Assumes a default value of 0.06 based on the Gameplay Constants.
private real array armorDmgMult
endglobals
// Dynamically compute the damage multipliers based on the Gameplay Constants.
private module M
private static method onInit takes nothing returns nothing
local unit dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'hpea', 0.0, 0.0, 0.0)
local real testDmg = 10.0
local real delta = 1.0
local real prevHP = GetWidgetLife(dummy)
local integer i = 0
local integer j = 0
local integer unityIndex = -1
loop
exitwhen i >= DEFENSE_TYPE_COUNT
call BlzSetUnitIntegerField(dummy, UNIT_IF_DEFENSE_TYPE, i)
loop
exitwhen j >= ATTACK_TYPE_COUNT
call UnitDamageTarget(dummy, dummy, 1.0, false, false, ConvertAttackType(j), DAMAGE_TYPE_UNKNOWN, null)
set armorDmgMult[i*ATTACK_TYPE_COUNT + j] = prevHP - GetWidgetLife(dummy)
call SetWidgetLife(dummy, prevHP)
// This attack type and defense type combination is the first neutral entry
if (unityIndex == -1) and (armorDmgMult[i*ATTACK_TYPE_COUNT + j] == 1.0) then
set unityIndex = i*ATTACK_TYPE_COUNT + j
endif
set j = j + 1
endloop
set i = i + 1
set j = 0
endloop
// Check if there are any neutral attack type to defense type multipliers
if (unityIndex == -1) then
set unityIndex = 0
set delta = armorDmgMult[unityIndex]
endif
// Change the dummy unit's armor to follow suit.
// Compute damage reduction factor
call BlzSetUnitIntegerField(dummy, UNIT_IF_DEFENSE_TYPE, unityIndex / ATTACK_TYPE_COUNT)
call BlzSetUnitArmor(dummy, 1.0)
// Kinda paranoid that the dummy's health isn't prevHP here...
call SetWidgetLife(dummy, prevHP)
call UnitDamageTarget(dummy, dummy, testDmg, true, false, ConvertAttackType(ModuloInteger(unityIndex, ATTACK_TYPE_COUNT)), DAMAGE_TYPE_NORMAL, null)
set delta = (prevHP - GetWidgetLife(dummy)) / (delta * testDmg)
// We can extract the damage multiplier from the following info:
// - Neutral attack-type to defense-type damage multiplier
// - A base armor value of 1.0
// - The delta value itself (the most meaningful)
set effectiveHPFactor = (1.0 / delta) - 1.0
call RemoveUnit(dummy)
set dummy = null
endmethod
endmodule
private struct S extends array
implement M
endstruct
// Renamed the original function from GetUnitArmorReduction to GetUnitArmorTypeReduction
function GetUnitArmorTypeReduction takes unit whichUnit, attacktype a returns real
local integer i = BlzGetUnitIntegerField(whichUnit, UNIT_IF_DEFENSE_TYPE)
return armorDmgMult[i*ATTACK_TYPE_COUNT + GetHandleId(a)]
endfunction
// Repurposed the function to take the unit only as the function argument.
function GetUnitArmorReduction takes unit whichUnit returns real
local real armor = BlzGetUnitArmor(whichUnit)
if (armor > 0.0) then
set armor = armor*effectiveHPFactor
return 1.0 / (1.0 + armor)
endif
// As it turns out, the damage multiplier effect is hardcoded for negative numbers. I believe the damage
// reduction only goes so far up to -21.00.
set armor = RMaxBJ(armor, -21.0)
// For some reason, wc3 treats -16.00 armor as -15.00 armor. Weird interaction.
// Also, for units that start out with negative armor due to items, the calculated
// damage may be off by at least 1 armor point.
if (armor < -15.00) then
set armor = armor + 1.0
endif
return 2.00 - Pow(0.94, -armor)
endfunction
endlibrary