Deleted member 247165
D
Deleted member 247165
This is the correct section where you must upload this: Spells | HIVE
// -----------------------------------------------------------------------------
// Getters
// -----------------------------------------------------------------------------
function GetUnitWhiteArmor takes unit whichUnit returns real // Gets white armor (unit-type + upgrades + agility + code) of the unit.
function GetUnitGreenArmor takes unit whichUnit returns real // Gets green armor (abilities/buffs) of the unit.
function GetUnitTotalArmor takes unit whichUnit returns real // Gets total (white + green) armor of the unit. Same as BlzGetUnitArmor(u) or BlzGetUnitRealField(u, UNIT_RF_DEFENSE)
// If you prefer to manipulate the hidden code bonus directly
function GetUnitCodeDefenseBonus takes unit whichUnit returns real // Gets the vaue of the hidden code armor.
// For information
function GetUnitAgiDefenseBonus takes unit whichUnit, boolean includeBonuses returns real // Gets the white armor bonus from Agility (= AgiDefenseBase + Hero white agility x AgiDefenseBonus)
function GetUnitBaseAndUpgradeDefense takes unit whichUnit returns real // Gets the base + upgrade defense (= def + upgrade count * defUp)
// -----------------------------------------------------------------------------
// Setters (beware that the only editable value in real time is the Bonus from code)
// -----------------------------------------------------------------------------
function UnitAddWhiteArmor takes unit whichUnit, real armorDiff returns nothing // Adds an amount to white armor (by changing the hidden code value). Negative value substracts.
function UnitSetWhiteArmor takes unit whichUnit, real desiredWhiteArmor returns nothing // Changes the white armor to the desired value (by changing the hidden code value).
function UnitResetWhiteArmor takes unit whichUnit returns nothing // Resets all previous modifications via this system or calls to BlzSetUnitArmor/BlzSetUnitIntegerField(UNIT_RF_DEFENSE).
// If you prefer to manipulate the hidden code bonus directly
function UnitSetCodeDefenseBonus takes unit whichUnit, real desiredCodeArmor returns nothing // Changes the hidden code value. Is 0 by default.
// -----------------------------------------------------------------------------
// Misc
// -----------------------------------------------------------------------------
function GetUnitTotalArmorFactor takes unit whichUnit returns real // Get the damage factor applied to damage received by this unit (1.0 = no reduction, 0.0<f<1.0 = reduced).
function ArmorConvertAmountToFactor takes real whichArmor returns real // Computes the damage factor matching the total armor value (1.0 = no reduction, 0.0<f<1.0 = reduced).
function ArmorConvertFactorToAmount takes real whichFactor returns real // Computes the total armor matching a damage factor.
/*
ArmorUtils v2.0.1 (12/10/2023) by Ricola3D
requires Logarithm (by Vexorian) https://www.hiveworkshop.com/threads/graveyard-logarithm-by-vexorian.350941/
API:
// -----------------------------------------------------------------------------
// Getters
// -----------------------------------------------------------------------------
function GetUnitWhiteArmor takes unit whichUnit returns real // Gets white armor (unit-type + upgrades + agility + code) of the unit.
function GetUnitGreenArmor takes unit whichUnit returns real // Gets green armor (abilities/buffs) of the unit.
function GetUnitTotalArmor takes unit whichUnit returns real // Gets total (white + green) armor of the unit. Same as BlzGetUnitArmor(u) or BlzGetUnitRealField(u, UNIT_RF_DEFENSE)
// If you prefer to manipulate the hidden code bonus directly
function GetUnitCodeDefenseBonus takes unit whichUnit returns real // Gets the vaue of the hidden code armor.
// For information
function GetUnitAgiDefenseBonus takes unit whichUnit, boolean includeBonuses returns real // Gets the white armor bonus from Agility (= AgiDefenseBase + Hero white agility x AgiDefenseBonus)
function GetUnitBaseUpgradeDefense takes unit whichUnit returns real // Gets the base + upgrade defense (= def + upgrade count * defUp)
// -----------------------------------------------------------------------------
// Setters (beware that the only editable value in real time is the Bonus from code)
// -----------------------------------------------------------------------------
function UnitAddWhiteArmor takes unit whichUnit, real armorDiff returns nothing // Adds an amount to white armor (by changing the hidden code value). Negative value substracts.
function UnitSetWhiteArmor takes unit whichUnit, real desiredWhiteArmor returns nothing // Changes the white armor to the desired value (by changing the hidden code value).
function UnitResetWhiteArmor takes unit whichUnit returns nothing // Resets all previous modifications via this system or calls to BlzSetUnitArmor/BlzSetUnitIntegerField(UNIT_RF_DEFENSE).
// If you prefer to manipulate the hidden code bonus directly
function UnitSetCodeDefenseBonus takes unit whichUnit, real desiredCodeArmor returns nothing // Changes the hidden code value. Is 0 by default.
// -----------------------------------------------------------------------------
// Misc
// -----------------------------------------------------------------------------
function GetUnitTotalArmorFactor takes unit whichUnit returns real // Get the damage factor applied to damage received by this unit (1.0 = no reduction, 0.0<f<1.0 = reduced).
function ArmorConvertAmountToFactor takes real whichArmor returns real // Computes the damage factor matching the total armor value (1.0 = no reduction, 0.0<f<1.0 = reduced).
function ArmorConvertFactorToAmount takes real whichFactor returns real // Computes the total armor matching a damage factor.
*/
native UnitAlive takes unit id returns boolean
library ArmorUtils requires Logarithm
globals
// ----------------------------------------------------------------------------
// BASE SETTINGS
// ----------------------------------------------------------------------------
// Values shall be taken from gameplay constants
constant real DEFENSE_ARMOR = 0.060 // "Combat - Armor Damage Reduction Multiplier"/DefenseArmor
constant real AGI_DEFENSE_BASE = -2.000 // "Hero Attributes - Defense Base Value (before Agility Bonus)"/AgiDefenseBase
constant real AGI_DEFENSE_BONUS = 0.300 // "Hero Attributes - Defense Bonus per Agility Point"/AgiDefenseBonus
// System cleaning - Ability to detect unit removal from game (derived from Defend - Adef)
// If you already have a similar ability from on of the systems below, REUSE IT:
// - Jesus4Lyf' AIDS: LEAVE_DETECTION_ABILITY
// - Bribe's GUI Unit Event: DetectRemoveAbility
// - Nestharus' UnitIndexer or Unit Event: ABILITIES_UNIT_INDEXER
// - Grim001's AutoIndex or AutoEvents: LeaveDetectAbilityID
// - TriggerHappy's UnitDex: DETECT_LEAVE_ABILITY
// - Chopinski's Indexer: ability
// I prefered just using the ability, and not adding any library as requirement
private constant integer DETECT_REMOVE_ABILITY = 'A001'
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// Coordinates to create the copy
private real WorldMaxX
private real WorldMaxY
// A boolean to decide if we catch or not the unit enters region event
private integer CatchEnteringUnitType = 0
// Internal storage for code armor value
private hashtable Memory
endglobals
// ----------------------------------------------------------------------------
// SaveCode.
// Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
// ----------------------------------------------------------------------------
private function SaveCode takes unit whichUnit, real codeArmor returns nothing
if ( null != whichUnit ) then
// In case there is no Unit Event system, add the remove detect ability manually.
if ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) ) then
call UnitAddAbility(whichUnit, DETECT_REMOVE_ABILITY)
call UnitMakeAbilityPermanent(whichUnit, true, DETECT_REMOVE_ABILITY)
endif
call SaveReal( Memory, GetHandleId(whichUnit), StringHash("code_armor"), codeArmor )
endif
endfunction
// ----------------------------------------------------------------------------
// GetUnitAgiDefenseBonus: get armor bonus from agility. Returns 0 for non-hero units.
// ----------------------------------------------------------------------------
function GetUnitAgiDefenseBonus takes unit whichUnit, boolean includeBonuses returns real
local real agiArmor = 0.0
local integer agi = 0
local boolean isHero = false
if ( null != whichUnit ) then
set isHero = IsUnitIdType(GetUnitTypeId(whichUnit), UNIT_TYPE_HERO)
if (isHero) then
// Unit is hero, illusion of hero, ...
set agi = GetHeroAgi(whichUnit, includeBonuses)
set agiArmor = AGI_DEFENSE_BASE + AGI_DEFENSE_BONUS * I2R(agi)
endif
endif
return agiArmor
endfunction
// ----------------------------------------------------------------------------
// GetUnitCodeDefenseBonus: get code armor. This is the only armor you can modify directly (with BlzSetUnitArmor primitive).
// ----------------------------------------------------------------------------
function GetUnitCodeDefenseBonus takes unit whichUnit returns real
local real codeArmor = 0.0
if ( null != whichUnit ) then
set codeArmor = LoadReal( Memory, GetHandleId(whichUnit), StringHash("code_armor") )
endif
return codeArmor
endfunction
// ----------------------------------------------------------------------------
// GetUnitBaseUpgradeDefense: get what Blizzard considers "base" armor.
// If no upgrades, equal to the number in World Editor > Object Editor > Unit > "[def] Combat - Defense base".
// Otherwise also includes upgrades count x "[defUp] Defense Upgrade Bonus"
// Notes:
// - Meaningless in 99% of cases. Equal to white armor ONLY for non-hero units whose armor have not been modified by code...)
// - This is the value taken as base by Devotion Aura with "[DataB1] Data - Percent Bonus" set to true.
// ----------------------------------------------------------------------------
function GetUnitBaseUpgradeDefense takes unit whichUnit returns real
local player owner
local unit copy
local real copyWhiteArmor
local real copyBaseUpgradeArmor = 0.
local boolean interfaceIconVisible
// We create a new unit of the same type and same owner. It has
// - the same Combat Base Defense & Defense Upgrade Bonus
// - the same upgrades (because of owner)
// - no items (=no bonus from them)
// - no buffs (even auras need 1s to apply)
// - internal code defense still equal to 0.
// So base+upgrade is equal for the original unit and the copy.
if ( whichUnit != null ) then
set owner = GetOwningPlayer(whichUnit)
set CatchEnteringUnitType = GetUnitTypeId(whichUnit)
set copy = CreateUnit(owner, CatchEnteringUnitType, WorldMaxX, WorldMaxY, 0.) // Create a corpse would be safer for side effects (hero UI, buffs...), but it's impossible if unit death type is "can't raise, doesn't decay"...
// If the unit is a hero, you can remove the death message
set copyWhiteArmor = BlzGetUnitArmor(copy) // Copy shouldn't have bonus, so white armor = total armor returned by natives
set copyBaseUpgradeArmor = copyWhiteArmor - GetUnitAgiDefenseBonus(copy, false) // White = base + upgrades + agi + code. For the copy normally code = 0.
call SetUnitOwner(copy, Player(PLAYER_NEUTRAL_PASSIVE), true) // If hero, easiest way to avoid the death message/sound and left visible portrait on the left of the screen after killing or removing. Should also avoid interfering with the score.
call RemoveUnit(copy)
endif
return copyBaseUpgradeArmor
endfunction
// ----------------------------------------------------------------------------
// GetUnitWhiteArmor: get armor before bonus (= number displayed in white in unit status).
// ----------------------------------------------------------------------------
function GetUnitWhiteArmor takes unit whichUnit returns real
local real whiteArmor = 0.0
local integer baseAgi = 0
if ( null != whichUnit ) then
set whiteArmor = GetUnitBaseUpgradeDefense(whichUnit) + GetUnitAgiDefenseBonus(whichUnit, false) + GetUnitCodeDefenseBonus(whichUnit)
endif
return whiteArmor
endfunction
// ----------------------------------------------------------------------------
// GetTotal: get total armor. Same as BlzGetUnitArmor.
// ----------------------------------------------------------------------------
function GetUnitTotalArmor takes unit whichUnit returns real
local real armor = 0.0
if ( null != whichUnit ) then
set armor = BlzGetUnitArmor(whichUnit)
endif
return armor
endfunction
// ----------------------------------------------------------------------------
// GetUnitGreenArmor: get current armor bonus (= number displayed in green in unit status.
// ----------------------------------------------------------------------------
function GetUnitGreenArmor takes unit whichUnit returns real
return ( GetUnitTotalArmor(whichUnit) - GetUnitWhiteArmor(whichUnit) )
endfunction
// ----------------------------------------------------------------------------
// UnitAddWhiteArmor: apply difference to unit code armor (and thus to white armor). Difference can be positive or negative.
// Also modifies white armor and total armor of the same difference.
// ----------------------------------------------------------------------------
function UnitAddWhiteArmor takes unit whichUnit, real armorDiff returns nothing
local real currentTotalArmor = 0.0
if ( null != whichUnit ) then
set currentTotalArmor = BlzGetUnitArmor(whichUnit)
call BlzSetUnitArmor(whichUnit, currentTotalArmor + armorDiff )
endif
endfunction
// ----------------------------------------------------------------------------
// SetCode: set unit code armor to desired value.
// ----------------------------------------------------------------------------
function UnitSetCodeDefenseBonus takes unit whichUnit, real desiredCodeArmor returns nothing
local real currentCodeArmor = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
set currentCodeArmor = GetUnitCodeDefenseBonus(whichUnit)
set armorDiff = desiredCodeArmor - currentCodeArmor
call UnitAddWhiteArmor(whichUnit, armorDiff)
endif
endfunction
// ----------------------------------------------------------------------------
// UnitSetWhiteArmor: set unit code armor such as white armor reaches the desired amount.
// ----------------------------------------------------------------------------
function UnitSetWhiteArmor takes unit whichUnit, real desiredWhiteArmor returns nothing
local real currentWhiteArmor = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
set currentWhiteArmor = GetUnitWhiteArmor(whichUnit)
set armorDiff = desiredWhiteArmor - currentWhiteArmor
call UnitAddWhiteArmor(whichUnit, armorDiff)
endif
endfunction
// ----------------------------------------------------------------------------
// UnitResetWhiteArmor: reset unit white armor to default, i.e. code armor to 0.
// ----------------------------------------------------------------------------
function UnitResetWhiteArmor takes unit whichUnit returns nothing
call UnitSetCodeDefenseBonus(whichUnit, 0.)
endfunction
// ----------------------------------------------------------------------------
// ArmorConvertAmountToFactor: get damage modifier corresponding to a armor value.
// Notes:
// - "0.02" means damages are reduced by 98%.
// - "1.25" means damages are amplified by 25%.
// - For the moment Blizzard displays -71% as min reduction
// ----------------------------------------------------------------------------
function ArmorConvertAmountToFactor takes real whichArmor returns real
local real factor = 0.0
if (whichArmor >= 0) then
// Damage reduction - factor is inferior or equal to 1
set factor = 1 - ( whichArmor * DEFENSE_ARMOR ) / ( 1 + whichArmor * DEFENSE_ARMOR )
else
// DamageAmplification - factor is superior to 1
set factor = 2 - Pow( 1 - DEFENSE_ARMOR , -1 * whichArmor )
endif
return factor
endfunction
// ----------------------------------------------------------------------------
// ArmorConvertFactorToAmount: computes the armor necessary to reach this damage factor.
// Notes:
// - Does not work for 0 walue (= dodge, nullify damage).
// - Does not work for negative values (=heal).
// ----------------------------------------------------------------------------
function ArmorConvertFactorToAmount takes real whichFactor returns real
local real armor = 0.0
if (whichFactor <= 0.0) then
// Negative factor does not work - infinite armor or healing
elseif (whichFactor <= 1.0) then
// Damage reduction - armor is positive or null
set armor = ( 1 - whichFactor ) / ( whichFactor * DEFENSE_ARMOR )
else
// Damage amplification - armor is negative
set armor = -1.0 * Logarithm( 1.0 - DEFENSE_ARMOR, 2.0 - whichFactor )
endif
return armor
endfunction
// ----------------------------------------------------------------------------
// GetUnitTotalArmorFactor: get current damage factor taking into account total armor. Does not consider attack type, damage type and armor type!
// Notes:
// - If factor is <1, then damage reduction
// - If factor >1, then damage amplification
// - If factor ==1, damages unchanged, null armor.
// - If factor ==0, then error.
// ----------------------------------------------------------------------------
function GetUnitTotalArmorFactor takes unit whichUnit returns real
local real damageFactor = 0.0
local real totalArmor = 0.0
if ( null != whichUnit ) then
set totalArmor = BlzGetUnitArmor(whichUnit)
set damageFactor = ArmorConvertAmountToFactor(totalArmor)
endif
return damageFactor
endfunction
// ----------------------------------------------------------------------------
// onBlzSetUnitArmor: called just before everytime code sets unit armor with "BlzSetUnitArmor" native.
// Notes: since there is no native to access internal "code armor" value from Blizzard. This function stores a copy of it.
// ----------------------------------------------------------------------------
private function onBlzSetUnitArmor takes unit whichUnit, real armorAmount returns nothing
local real previousCodeArmor = 0.0
local real nextCodeArmor = 0.0
local real previousArmorAmount = 0.0
local real armorDiff = 0.0
if ( null != whichUnit ) then
// Detect armor difference
set previousArmorAmount = BlzGetUnitArmor(whichUnit)
set armorDiff = armorAmount - previousArmorAmount
// Get previous code armor (0 if not existing), update it, and save-it back
set previousCodeArmor = GetUnitCodeDefenseBonus(whichUnit)
set nextCodeArmor = previousCodeArmor + armorDiff
call SaveCode(whichUnit, nextCodeArmor)
endif
endfunction
hook BlzSetUnitArmor onBlzSetUnitArmor
// ----------------------------------------------------------------------------
// onBlzSetUnitRealField: called just before everytime code sets unit armor with "BlzSetUnitRealField" native for UNIT_RF_DEFENSE field.
// Notes: internally just calls onBlzSetUnitArmor function.
// ----------------------------------------------------------------------------
private function onBlzSetUnitRealField takes unit whichUnit, unitrealfield whichField, real value returns nothing
if ( UNIT_RF_DEFENSE == whichField ) then
call onBlzSetUnitArmor(whichUnit, value)
endif
endfunction
hook BlzSetUnitRealField onBlzSetUnitRealField
private function onUnitEnter takes nothing returns boolean
// Unfortunately there is no way to completely remove the unit before other listeners of the "Unit enter region" event are triggered...
// So let's try to minimize side effects
// Creating the copy on the edges of the maps already minimize the odds of it being detected by "unit enters region" events (with playable map area, or with user-defined regions).
local unit u = GetFilterUnit()
local integer unitType = GetUnitTypeId(u)
if unitType == CatchEnteringUnitType then
// We only catch once
set CatchEnteringUnitType = 0
// It's not necessary to have it visible
call ShowUnit(u, false)
// We could pause it if we want
//call PauseUnit(u, false)
// Many players filter out the units with Aloc from their code (cause it generally is dummy caster)
call UnitAddAbility(u, 'Aloc')
// The following indexers will skip units whose UserData is not 0:
// - Jesus4Lyf's AIDS: https://www.thehelper.net/threads/advanced-indexing-data-storage.116539/
// - Grim001's AutoIndex: https://wc3modding.info/4542/autoindex/
// - TriggerHappy's UnitDex: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
// - Bribe's GUI Unit Indexer: https://www.hiveworkshop.com/threads/gui-unit-indexer-1-4-0-0.197329/
// - Chopinski's Indexer: https://www.hiveworkshop.com/threads/damage-interface-vjass-lua.324257/
call SetUnitUserData(u, -1)
// If you use Grimm001's AutoIndex with option "UseUnitUserData = false", I can't do a thing. Because everything is private.
// But you can use change the AutoIndex' "UnitFilter" function that is made for that.
// If you use Nestharus' UnitIndexer
static if LIBRARY_UnitIndexer then
// Trying to set the condition "if (enabled and not p_UnitIndex.exists(indexedUnit))" to false
p_UnitIndex.create(u) // Should create an index but not fire the event
endif
endif
return false
endfunction
// ----------------------------------------------------------------------------
// onUnitOrdered: called just before a unit is removed from the game. Cleans internal storage.
// ----------------------------------------------------------------------------
private function onUnitOrdered takes nothing returns nothing
local unit whichUnit = GetTriggerUnit()
local boolean unitBeingRemoved
static if LIBRARY_UnitIndexer then // If using Nestharus' Unit Indexer
local p_UnitIndex index
endif
// Because unit has DETECT_REMOVE_ABILITY, it will fire a "undefend" event just before being removed from game.
if ( OrderId2StringBJ(GetIssuedOrderIdBJ()) == "undefend" ) then
set unitBeingRemoved = ( 0 == GetUnitAbilityLevel(whichUnit, DETECT_REMOVE_ABILITY) )
if (unitBeingRemoved) then
call FlushChildHashtable( Memory, GetHandleId(whichUnit) )
static if LIBRARY_UnitIndexer then // If using Nestharus' Unit Indexer
set index = GetUnitUserData(whichUnit)
call index.destroy()
endif
endif
endif
endfunction
// ----------------------------------------------------------------------------
// onInit: system initializer
// ----------------------------------------------------------------------------
private module M
private static method onInit takes nothing returns nothing
local integer i = bj_MAX_PLAYER_SLOTS
local player p
local rect worldRect = GetWorldBounds()
local region worldRegion = CreateRegion()
local trigger cleanTrigger = CreateTrigger() // trigger to detect unit removal (and clean hashtable)
local trigger unitEnterRegionTrigger = CreateTrigger() // trigger to catch created copies
// Register coordinates for copies creation
set WorldMaxX = GetRectMaxX(worldRect)
set WorldMaxY = GetRectMaxY(worldRect)
// First, deactive DETECT_REMOVE_ABILITY for all players (removes the ability effect, hides the button
loop
set i = i - 1
set p = Player(i)
call SetPlayerAbilityAvailable(p, DETECT_REMOVE_ABILITY, false)
exitwhen i == 0
endloop
// Set up the enter trigger (since we are within module initialization, it should execute before most systems)
call RegionAddRect(worldRegion, worldRect)
call RemoveRect(worldRect)
call TriggerRegisterEnterRegion(unitEnterRegionTrigger, worldRegion, Filter(function onUnitEnter))
// Set up the clean trigger
call TriggerRegisterAnyUnitEventBJ( cleanTrigger, EVENT_PLAYER_UNIT_ISSUED_ORDER )
call TriggerAddAction( cleanTrigger, function onUnitOrdered )
// Initialize the memory hashtable
set Memory = InitHashtable() // Init hashtable (avoid adding dependency to an indexer)
endmethod
endmodule
private struct S extends array
implement M
endstruct
endlibrary