library ArmorUtils requires Logarithm, optional IntuitiveDamageSystem
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This is used to get the exact armor of a given unit. It deals damage to the
//* unit in order to determine armor, meaning that damage detection systems will
//* detect it. If your map has the Intuitive Damage Detection System (IDDS) in
//* it, it will use that system's internal ignored damage type for the check.
//* Using that removes the need to disable/enable any damage detection triggers
//* to avoid infinite loops.
//*
//* The attacktype constant, ATTACK_TYPE_USED, is by default set to use
//* ATTACK_TYPE_CHAOS. Chaos is used because it deals 100% to all types, so if
//* you change it in your map, make sure you update the constant to some type
//* with 100% damage to all armor types.
//*
//* This system can also be used as a means to detect if a unit is invulnerable
//* or not. If GetUnitArmor returns ARMOR_INVULNERABLE, then the unit is
//* invulnerable. The value for it is entirely random and will never be the
//* actual armor value for a unit, so there should be no conflicts.
//*
//* Damage reduction in WC3 due to negative armor is capped at -71%, which has
//* an equivalent armor value of -20. If you have a unit with less than -20
//* armor, this system will always return exactly -20.
//*
//* Default WC3 gameplay constants have the 'Armor Damage Reduction Multiplier'
//* set to 0.06. If you change this constant for your map, be sure to adjust the
//* ARMOR_REDUCTION_MULTIPLIER constant in this script to match.
//*
//* Example Usage:
//* local real armor = GetUnitArmor(MyUnit)
//*
//* Two other functions are included in this library. They are used for
//* calculating a unit's base damage knowing armor-factoring damage and a unit's
//* armor-factoring damage knowing its base damage. These functions do not
//* consider armor type in their calculations. If you want armor type damage
//* adjusting, it is recommended to trigger it.
//*
//* Example Usage:
//* local real basedmg = GetFullDamage(MyUnit, Armor)
//* local real armodmg = GetReducedDamage(MyUnit, Armor)
//*
//* An objectmerger call is included in this script to automatically generate
//* the bonus life ability if necessary. If you do not modify the 'AIlz' ability
//* in your map, you can replace the LIFE_BONUS_SPELL_ID constant with it and
//* not use the objectmerger call. The
//*
globals
//Values that should be changed for your map
private constant real ARMOR_REDUCTION_MULTIPLIER = 0.06
private constant integer LIFE_BONUS_SPELL_ID = 'lif&'
private constant attacktype ATTACK_TYPE_USED = ATTACK_TYPE_CHAOS
//Values that do not need to be changed
constant real ARMOR_INVULNERABLE = 917451.519
private constant real DAMAGE_TEST = 16.
private constant real DAMAGE_LIFE = 30.
private constant real NATLOG_094 =-0.061875
endglobals
////! external ObjectMerger w3a AIlz lif& anam "GetUnitArmorLifeBonus" ansf "" Ilif 1 30 aite 0
function GetUnitArmor takes unit u returns real
local real life = GetWidgetLife(u)
local real test = life
local real redc = 0.
local boolean enab = false
local trigger trig = GetTriggeringTrigger()
if u != null and life >= 0.405 then
if GetUnitState(u, UNIT_STATE_MAX_LIFE) <= DAMAGE_TEST then
//Add max life to keep it alive
call UnitAddAbility(u, LIFE_BONUS_SPELL_ID)
endif
if life <= DAMAGE_LIFE then
//If under the threshold, heal it for the moment
call SetWidgetLife(u, DAMAGE_LIFE)
set test = DAMAGE_LIFE
endif
static if LIBRARY_IntuitiveDamageSystem then
//Use the IGNORED type in the IDDS if present so it is 100% ignored
call UnitDamageTargetEx(u, u, DAMAGE_TEST, ATTACK_TYPE_USED, DAMAGE_TYPE_IGNORED, true)
else
if trig != null and IsTriggerEnabled(trig) then
//Disable the trigger to prevent it registering with damage detection systems
call DisableTrigger(trig)
set enab = true
endif
call UnitDamageTarget(u, u, DAMAGE_TEST, true, false, ATTACK_TYPE_USED, DAMAGE_TYPE_NORMAL, null)
if enab then
//Re-enable the trigger
call EnableTrigger(trig)
endif
endif
set redc = (DAMAGE_TEST-test+GetWidgetLife(u))/DAMAGE_TEST
//Remove the max life ability
call UnitRemoveAbility(u, LIFE_BONUS_SPELL_ID)
call SetWidgetLife(u, life)
set trig = null
if redc >= 1. then
//Invulnerable
return ARMOR_INVULNERABLE
elseif redc < 0. then
//Negative Armor
return -Log(redc+1.)/NATLOG_094
else
//Positive Armor
return redc/(ARMOR_REDUCTION_MULTIPLIER*(1.-redc))
endif
endif
set trig = null
return 0.
endfunction
function GetReducedDamage takes real baseDamage, real armor returns real
if armor >= 0. then
return baseDamage*(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
else
return baseDamage*(2.-Pow(0.94,-armor))
endif
endfunction
function GetFullDamage takes real damage, real armor returns real
if armor >= 0. then
return damage/(1.-((armor*ARMOR_REDUCTION_MULTIPLIER)/(1.+ARMOR_REDUCTION_MULTIPLIER*armor)))
else
return damage/(2.-Pow(0.94,-armor))
endif
endfunction
endlibrary