This article relates to the game "Dota 2". Click here for more information.
This article relates to the SDK/Workshop Tools for "Dota 2 Workshop Tools". Click here for more information.
This article's documentation is for Source 2. Click here for more information.

Lua Abilities and Modifiers

From Valve Developer Community
Jump to navigation Jump to search
English (en)Русский (ru)中文 (zh)Translate (Translate)

Lua is now capable of specifying abilities and modifiers entirely in Lua; this is appropriate if you're familiar with Lua and the abilities/modifiers you want to make may have more advanced logic in their effects. Lua-derived abilities and modifiers behave similarly to their in-game counterparts, calling out to certain virtual functions at certain times. As an author of a Lua ability or modifier, you have the choice of overriding those functions in your script.

Lua Abilities

To setup a Lua ability, you'll want to start with these steps:

In your addon's scripts\npc\npc_abilities_custom.txt:

//=================================================================================================================
// Test My Ability
//=================================================================================================================
"test_lua_ability"
{
	// General
	//-------------------------------------------------------------------------------------------------------------
	"BaseClass"						"ability_lua"
	"ScriptFile"					        "test_lua_ability"


At a minimum, you'll want these three lines to enable the Lua-based ability.


Next, in your scripts\vscripts directory, you'll want to make a new file with the same name as the "ScriptFile" entry in the npc_abilities_custom.txt file. In this example, the file name would be test_lua_ability.lua.


At the top of the new file, declare a new Lua class using the same name you've been using:

test_lua_ability = class ({})


You've now correctly setup a function Lua-based ability. To give it functionality, begin by overriding functions exposed on the Ability_Lua class ( you can see the full list of these by using script_help2 ). Because these functions are communicating with our engine, they expect certain return types and parameters. Some common examples are below.


Events

The engine calls these functions at certain points during an ability. Add them to your script if your ability wants to have functionality at those times.

* OnSpellStart() -- When cast time ends, resources have been spent - most abilities begin to do their work in this function. No return type, no parameters.
* OnAbilityPhaseStart() -- When cast time begins, resources have not been spent. Return true for successful cast, or false for unsuccessful, no parameters.
* OnAbilityPhaseInterrupted() -- When cast time is cancelled for any reason. No return type, no parameters.
* OnProjectileThink( vLocation ) -- If this ability has created a projectile, this function will be called many times while the projectile is travelling. vLocation is the current projectile location. No return type.
* OnProjectileHit( hTarget, vLocation ) -- When a projectile has travelled its max distance OR collided with an NPC that fits its targeting type. If hTarget is null, it means the projectile has expired. Return true to destroy the particle, return false to continue the projectile ( this applies for linear projectiles that can hit multiple NPCs, like Dragon Slave. If the projectile has reached its end, it will expire even if false is passed ) 
* GetIntrinsicModifierName() -- Return "modifier_name" of the modifier that is passively added by this ability.
* OnChannelFinish( bInterrupted ) -- When channel finishes, bInterrupted parameter notifies if the channel finished or not. No return type.
* OnUpgrade() -- When the ability is leveled up. No parameters, no return type.

Casting Behavior

Like normal abilities, ability_lua will read in the npc_abilities_custom.txt to determine a lot of its properties, such as targeting type, mana cost, flags, team, and behavior. If you have an ability that operates in different ways under different conditions, you can override the default behaviors by adding certain functions in your script.

* GetBehavior() -- Determines the type of targeting behavior used with the cursor, return expects value from DOTA_ABILITY_BEHAVIOR enum ( i.e. DOTA_ABILITY_BEHAVIOR_UNIT_TARGET, DOTA_ABILITY_BEHAVIOR_POINT )
* GetCooldown( nLevel ) -- Determines the cooldown started when the spell is cast. Return float value.
* GetCastRange( vLocation, hTarget ) -- Determines the cast range. Return integer value.
* GetChannelTime() -- Determines the channel time. Return float value.


When using the functions above, often you will want to return the default behavior under certain conditions. An example of this is Vengeful Spirit's Nether Swap; this ability only wants special GetCooldown() behavior if the caster has Aghanim's Scepter. In these cases, you can call the "BaseClass" to do what the function would normally do had you not overriden it in your script.

	if self:GetCaster():HasScepter() then
		return self:GetSpecialValueFor( "nether_swap_cooldown_scepter" )
	end

	return self.BaseClass.GetCooldown( self, nLevel )


Some abilities may want to generate custom cast errors that are more expressive than the default flags will allow. This is called a CastFilter. Based on the casting Behavior of the ability, you'll want to choose one of the following function pairs:

CastFilterResultTarget( hTarget ) -- hTarget is the targeted NPC.
GetCustomCastErrorTarget( hTarget) -- hTarget is the targeted NPC. 

CastFilterResultLocation( vLocation ) -- vLocation is the targeted location.
GetCustomCastErrorLocation( vLocation ) -- vLocation is the targeted location.

CastFilterResult() -- No target abilities
GetCustomCastError() -- No target abilities

All CastFilterResult functions must return a UnitFilterResult enum value ( i.e. UF_SUCCESS, UF_FAIL_CUSTOM ). Return UF_SUCCESS causes the ability to pass the filter, calling UF_FAIL_CUSTOM causes the appropriate GetCustomCastError function to be called.
All GetCustomCastError functions must return a string value ( pointing to an appropriate entry in the addon_english.txt file )


Here is an example of a CastFilter from Vengeful Spirit's Nether Swap. Nether Swap's casting behavior allows creeps, but only if the caster has Aghanim's Scepter. Nether Swap also cannot be cast on Vengeful herself. Note that CastFilterResult and GetCustomCastError are shared functions, meaning they run on both server and client.

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:CastFilterResultTarget( hTarget )
	if self:GetCaster() == hTarget then
		return UF_FAIL_CUSTOM
	end

	if ( hTarget:IsCreep() and ( not self:GetCaster():HasScepter() ) ) or hTarget:IsAncient() then
		return UF_FAIL_CUSTOM
	end

	local nResult = UnitFilter( hTarget, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP, DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, self:GetCaster():GetTeamNumber() )
	if nResult ~= UF_SUCCESS then
		return nResult
	end

	return UF_SUCCESS
end

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:GetCustomCastErrorTarget( hTarget )
	if self:GetCaster() == hTarget then
		return "#dota_hud_error_cant_cast_on_self"
	end

	if hTarget:IsAncient() then
		return "#dota_hud_error_cant_cast_on_ancient"
	end

	if hTarget:IsCreep() and ( not self:GetCaster():HasScepter() ) then
		return "#dota_hud_error_cant_cast_on_creep"
	end

	return ""
end

Properties

There are also several properties abilities have that are not exposed in npc_abilities_custom.txt. In general, you only want to override these functions if your ability wants special behavior different from the default. Some examples:

* IsStealable() -- Return true if Rubick can steal this spell.
* ProcsMagicStick() -- Return true if enemy heroes gain magic stick charges when this spell is cast.
* IsRefreshable() -- Return true if this spell can be refreshed.
* GetPlaybackRateOverride() -- Return the rate (float value) at which the cast animation should be played


Example

Here is the full script for a lua version of Vengeful Spirit's Nether Swap.

vengefulspirit_nether_swap_lua = class({})

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:GetAOERadius()
	return self:GetSpecialValueFor( "radius" )
end

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:CastFilterResultTarget( hTarget )
	if self:GetCaster() == hTarget then
		return UF_FAIL_CUSTOM
	end

	if ( hTarget:IsCreep() and ( not self:GetCaster():HasScepter() ) ) or hTarget:IsAncient() then
		return UF_FAIL_CUSTOM
	end

	local nResult = UnitFilter( hTarget, DOTA_UNIT_TARGET_TEAM_BOTH, DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_CREEP, DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES, self:GetCaster():GetTeamNumber() )
	if nResult ~= UF_SUCCESS then
		return nResult
	end

	return UF_SUCCESS
end

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:GetCustomCastErrorTarget( hTarget )
	if self:GetCaster() == hTarget then
		return "#dota_hud_error_cant_cast_on_self"
	end

	if hTarget:IsAncient() then
		return "#dota_hud_error_cant_cast_on_ancient"
	end

	if hTarget:IsCreep() and ( not self:GetCaster():HasScepter() ) then
		return "#dota_hud_error_cant_cast_on_creep"
	end

	return ""
end

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:GetCooldown( nLevel )
	if self:GetCaster():HasScepter() then
		return self:GetSpecialValueFor( "nether_swap_cooldown_scepter" )
	end

	return self.BaseClass.GetCooldown( self, nLevel )
end

--------------------------------------------------------------------------------

function vengefulspirit_nether_swap_lua:OnSpellStart()
	local hCaster = self:GetCaster()
	local hTarget = self:GetCursorTarget()

	if hCaster == nil or hTarget == nil or hTarget:TriggerSpellAbsorb( this ) then
		return
	end

	local vPos1 = hCaster:GetOrigin()
	local vPos2 = hTarget:GetOrigin()

	GridNav:DestroyTreesAroundPoint( vPos1, 300, false )
	GridNav:DestroyTreesAroundPoint( vPos2, 300, false )

	hCaster:SetOrigin( vPos2 )
	hTarget:SetOrigin( vPos1 )

	FindClearSpaceForUnit( hCaster, vPos2, true )
	FindClearSpaceForUnit( hTarget, vPos1, true )
	
	hTarget:Interrupt()

	local nCasterFX = ParticleManager:CreateParticle( "particles/units/heroes/hero_vengeful/vengeful_nether_swap.vpcf", PATTACH_ABSORIGIN_FOLLOW, hCaster )
	ParticleManager:SetParticleControlEnt( nCasterFX, 1, hTarget, PATTACH_ABSORIGIN_FOLLOW, nil, hTarget:GetOrigin(), false )
	ParticleManager:ReleaseParticleIndex( nCasterFX )

	local nTargetFX = ParticleManager:CreateParticle( "particles/units/heroes/hero_vengeful/vengeful_nether_swap_target.vpcf", PATTACH_ABSORIGIN_FOLLOW, hTarget )
	ParticleManager:SetParticleControlEnt( nTargetFX, 1, hCaster, PATTACH_ABSORIGIN_FOLLOW, nil, hCaster:GetOrigin(), false )
	ParticleManager:ReleaseParticleIndex( nTargetFX )

	EmitSoundOn( "Hero_VengefulSpirit.NetherSwap", hCaster )
	EmitSoundOn( "Hero_VengefulSpirit.NetherSwap", hTarget )

	hCaster:StartGesture( ACT_DOTA_CHANNEL_END_ABILITY_4 )
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

Lua Modifiers

Modifiers work much in the same way as the Lua Abilities, but the setup is slightly different. Similar to the Lua Abilities, start with a .lua file named appropriate to the name. Here is an example from Sven's Warcry. At the top of that file, create a class with the same name as the file. In this case, modifier_sven_warcry_lua .

modifier_sven_warcry_lua = class({})
--------------------------------------------------------------------------------


There is an additional step that is unique to lua modifiers; you must register their use with the engine before creating them. A good place to do this is in the related ability file. Typically, modifiers are created by abilities, but not always; if that is the case, you can call them in your base game mode script. The only requirement is that it is called before any instances of the modifier are created. Because modifier_sven_warcry_lua is related to the Warcry ability, it can be registered there. Note that this is found in the "sven_warcry_lua.lua" file, which is different than the file the modifier is defined in.

sven_warcry_lua = class({})
LinkLuaModifier( "modifier_sven_warcry_lua", LUA_MODIFIER_MOTION_NONE )

--------------------------------------------------------------------------------


The first parameter is the name (also the script file name) of the modifier you're registering; the second parameter is an enum type LuaModifierType, which is used to establish which, if any, motion controller type the modifier will apply on its parent. The different types are as follows:

* LUA_MODIFIER_MOTION_NONE - No motion controllers
* LUA_MODIFIER_MOTION_HORIZONTAL - Horizontal Motion Controller
* LUA_MODIFIER_MOTION_VERTICAL - Vertical Motion Controller
* LUA_MODIFIER_MOTION_BOTH - Both Horizontal and Vertical Motion


The modifier is now setup and can be added via the normal methods of adding modifiers. Note that new in this update, Modifiers (CDOTA_Buff) is an understood type in Lua, so you can create locals from them and call functions on them (see script_help2 for a full list), which is useful when defining your own modifier. Much like Abilities, the lua Modifier is overriding function calls from the engine. Many modifiers call a similar set of functions that are unrelated to their specific function and useful for basic setup and declaring variables. Here are some examples:

* OnCreated( kv ) -- Called when the modifier is created, with a table of values in "kv". Client/Server. No return type.
* OnRefresh( kv ) -- Called when the modifier is refreshed (created when already existing ). Client/Server, No return type.
* IsHidden() -- Return true if this modifier should not appear on the buff bar. Client/Server, boolean return type.
* IsDebuff() -- Return true if this modifier should appear as a debuff on the buff bar. Client/Server, boolean return type.
* IsPurgable() -- Return true if this modifier can be purged. Client/Server, boolean return type.
* GetEffectName() -- Return the name of the particle effect that is applied on the parent when this buff is active. Client/Server, string return type.


Note that these functions will be called separately on the client and the server. In general, it is best to perform game logic only on the server. Client functionality on modifiers is typically only used for tooltip reasons (so that bonuses show correctly in the HUD). If you are getting Lua errors when creating your modifier in the game, but the modifier still performs its game logic correctly, this is usually a sign of trying to do game logic on the client.


Modifier Functions

A function many lua modifiers want to use is DeclareFunctions(). This function should return a table of events and properties that your modifier wants to affect. The full list of the enum and function values is quite extensive, and can be seen by typing script_help2 in the game. Here is an example from Sven's Warcry modifier. This modifier wants to affect its parent's armor and movement speed, so in DeclareFunctions it must return the related enum values.

function modifier_sven_warcry_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE,
		MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS,
		MODIFIER_PROPERTY_TRANSLATE_ACTIVITY_MODIFIERS,
	}

	return funcs
end


The second step in affecting the above properties is to override their associated functions. Each property expects a specific function name.

--------------------------------------------------------------------------------

function modifier_sven_warcry_lua:GetActivityTranslationModifiers( params )
	if self:GetParent() == self:GetCaster() then
		return "sven_warcry"
	end

	return 0
end

--------------------------------------------------------------------------------

function modifier_sven_warcry_lua:GetModifierMoveSpeedBonus_Percentage( params )
	return self.warcry_movespeed
end

--------------------------------------------------------------------------------

function modifier_sven_warcry_lua:GetModifierPhysicalArmorBonus( params )
	return self.warcry_armor
end

--------------------------------------------------------------------------------


The params parameter is common to all property and event related functions. It is a table loaded with contents that are specific to the event happening at the time. Using a loop and Msg() function, you can print the contents of the table. Warcry's property functions are quite simple, so here is an example from Sven's Great Cleave where this is used. Note that property functions are Client and Server, so gameplay logic is only being performed on the Server.

--------------------------------------------------------------------------------

function modifier_sven_great_cleave_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_EVENT_ON_ATTACK_LANDED,
	}

	return funcs
end

--------------------------------------------------------------------------------

function modifier_sven_great_cleave_lua:OnAttackLanded( params )
	if IsServer() then
		if params.attacker == self:GetParent() and ( not self:GetParent():IsIllusion() ) then
			if self:GetParent():PassivesDisabled() then
				return 0
			end

			local target = params.target
			if target ~= nil and target:GetTeamNumber() ~= self:GetParent():GetTeamNumber() then
				local cleaveDamage = ( self.great_cleave_damage * params.damage ) / 100.0
				DoCleaveAttack( self:GetParent(), target, self:GetAbility(), cleaveDamage, self.great_cleave_radius, "particles/units/heroes/hero_sven/sven_spell_great_cleave.vpcf" )
			end
		end
	end
	
	return 0
end


OnIntervalThink

Some modifiers want to perform gameplay logic on certain intervals. You can do this by calling StartIntervalThink() on the modifier. Once the interval has been started, the engine will look for the function OnIntervalThink(), which will be called each time the think interval happens. The think can be stopped by calling StartIntervalThink() with -1 as the parameter. Lina's Fiery Soul makes use of these functions, here is an example:

--------------------------------------------------------------------------------

function modifier_lina_fiery_soul_lua:OnIntervalThink()
	if IsServer() then
		self:StartIntervalThink( -1 )
		self:SetStackCount( 0 )
	end
end

--------------------------------------------------------------------------------

function modifier_lina_fiery_soul_lua:OnAbilityExecuted( params )
	if IsServer() then
		if params.unit == self:GetParent() then
			if self:GetParent():PassivesDisabled() then
				return 0
			end

			local hAbility = params.ability 
			if hAbility ~= nil and ( not hAbility:IsItem() ) and ( not hAbility:IsToggle() ) then
				if self:GetStackCount() < self.fiery_soul_max_stacks then
					self:IncrementStackCount()
				else
					self:SetStackCount( self:GetStackCount() )
					self:ForceRefresh()
				end

				self:SetDuration( self.duration_tooltip, true )
				self:StartIntervalThink( self.duration_tooltip )
			end
		end
	end

	return 0
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


Thinkers

Sometimes modifiers want to do their gameplay effects around a location instead of around an NPC. In this situation you want to use a "thinker." Thinker modifiers are effectively invisible, invulnerable units with a modifier. Their structure is no different on the modifier side, but the creation is slightly different. Lina's Light Strike Array creates a delayed effect on an area, so a modifier thinker is used in this situation. Here's the example of how it is created:

--------------------------------------------------------------------------------

function lina_light_strike_array_lua:OnSpellStart()
	self.light_strike_array_aoe = self:GetSpecialValueFor( "light_strike_array_aoe" )
	self.light_strike_array_delay_time = self:GetSpecialValueFor( "light_strike_array_delay_time" )

	local kv = {}
	CreateModifierThinker( self:GetCaster(), self, "modifier_lina_light_strike_array_thinker_lua", kv, self:GetCursorPosition(), self:GetCaster():GetTeamNumber(), false )
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------

One thing to remember with a thinker is that it is important to remove the dummy unit once the Thinker has expired. UTIL_Remove( self:GetParent() ) is a good way to do this. Doing this will also remove the modifier.

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_thinker_lua:OnIntervalThink()
	if IsServer() then
		GridNav:DestroyTreesAroundPoint( self:GetParent():GetOrigin(), self.light_strike_array_aoe, false )
		local enemies = FindUnitsInRadius( self:GetParent():GetTeamNumber(), self:GetParent():GetOrigin(), self:GetParent(), self.light_strike_array_aoe, DOTA_UNIT_TARGET_TEAM_ENEMY, DOTA_UNIT_TARGET_HERO + DOTA_UNIT_TARGET_BASIC, 0, 0, false )
		if #enemies > 0 then
			for _,enemy in pairs(enemies) do
				if enemy ~= nil and ( not enemy:IsMagicImmune() ) and ( not enemy:IsInvulnerable() ) then

					local damage = {
						victim = enemy,
						attacker = self:GetCaster(),
						damage = self.light_strike_array_damage,
						damage_type = DAMAGE_TYPE_MAGICAL,
						ability = self:GetAbility()
					}

					ApplyDamage( damage )
					enemy:AddNewModifier( self:GetCaster(), self:GetAbility(), "modifier_lina_light_strike_array_lua", { duration = self.light_strike_array_stun_duration } )
				end
			end
		end

		local nFXIndex = ParticleManager:CreateParticle( "particles/units/heroes/hero_lina/lina_spell_light_strike_array.vpcf", PATTACH_WORLDORIGIN, nil )
		ParticleManager:SetParticleControl( nFXIndex, 0, self:GetParent():GetOrigin() )
		ParticleManager:SetParticleControl( nFXIndex, 1, Vector( self.light_strike_array_aoe, 1, 1 ) )
		ParticleManager:ReleaseParticleIndex( nFXIndex )

		EmitSoundOnLocationWithCaster( self:GetParent():GetOrigin(), "Ability.LightStrikeArray", self:GetCaster() )

		UTIL_Remove( self:GetParent() )
	end
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


Modifier State

Separate from properties and events, modifiers can apply "state." Some examples of state include being stunned, being invisible, being phased, etc. The full list is detailed in script_help2 in the modifierstate enum. State is applied by adding the function CheckState() in your modifier. A common case is to apply a stun. Note that the syntax is slightly different from that of DeclareFunctions().

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:CheckState()
	local state = {
	[MODIFIER_STATE_STUNNED] = true,
	}

	return state
end

Here is an example of the stun modifier that is applied from Light Strike Array.

modifier_lina_light_strike_array_lua = class({})

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:IsDebuff()
	return true
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:IsStunDebuff()
	return true
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:GetEffectName()
	return "particles/generic_gameplay/generic_stunned.vpcf"
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:GetEffectAttachType()
	return PATTACH_OVERHEAD_FOLLOW
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:DeclareFunctions()
	local funcs = {
		MODIFIER_PROPERTY_OVERRIDE_ANIMATION,
	}

	return funcs
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:GetOverrideAnimation( params )
	return ACT_DOTA_DISABLED
end

--------------------------------------------------------------------------------

function modifier_lina_light_strike_array_lua:CheckState()
	local state = {
	[MODIFIER_STATE_STUNNED] = true,
	}

	return state
end

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


Modifier Functions

--- Enum modifierfunction
MODIFIER_EVENT_ON_ABILITY_END_CHANNEL = 131 -- OnAbilityEndChannel
MODIFIER_EVENT_ON_ABILITY_EXECUTED = 128 -- OnAbilityExecuted
MODIFIER_EVENT_ON_ABILITY_FULLY_CAST = 129 -- OnAbilityFullyCast
MODIFIER_EVENT_ON_ABILITY_START = 127 -- OnAbilityStart
MODIFIER_EVENT_ON_ATTACK = 120 -- OnAttack
MODIFIER_EVENT_ON_ATTACKED = 137 -- OnAttacked
MODIFIER_EVENT_ON_ATTACK_ALLIED = 123 -- OnAttackAllied
MODIFIER_EVENT_ON_ATTACK_FAIL = 122 -- OnAttackFail
MODIFIER_EVENT_ON_ATTACK_FINISHED = 168 -- OnAttackFinished
MODIFIER_EVENT_ON_ATTACK_LANDED = 121 -- OnAttackLanded
MODIFIER_EVENT_ON_ATTACK_RECORD = 118 -- OnAttackRecord
MODIFIER_EVENT_ON_ATTACK_START = 119 -- OnAttackStart
MODIFIER_EVENT_ON_BREAK_INVISIBILITY = 130 -- OnBreakInvisibility
MODIFIER_EVENT_ON_BUILDING_KILLED = 149 -- OnBuildingKilled
MODIFIER_EVENT_ON_DEATH = 138 -- OnDeath
MODIFIER_EVENT_ON_DOMINATED = 165 -- OnDominated
MODIFIER_EVENT_ON_HEALTH_GAINED = 144 -- OnHealthGained
MODIFIER_EVENT_ON_HEAL_RECEIVED = 148 -- OnHealReceived
MODIFIER_EVENT_ON_HERO_KILLED = 147 -- OnHeroKilled
MODIFIER_EVENT_ON_MANA_GAINED = 145 -- OnManaGained
MODIFIER_EVENT_ON_MODEL_CHANGED = 150 -- OnModelChanged
MODIFIER_EVENT_ON_ORB_EFFECT = 136
MODIFIER_EVENT_ON_ORDER = 125 -- OnOrder
MODIFIER_EVENT_ON_PROCESS_UPGRADE = 132
MODIFIER_EVENT_ON_PROJECTILE_DODGE = 124 -- OnProjectileDodge
MODIFIER_EVENT_ON_REFRESH = 133
MODIFIER_EVENT_ON_RESPAWN = 139 -- OnRespawn
MODIFIER_EVENT_ON_SET_LOCATION = 143 -- OnSetLocation
MODIFIER_EVENT_ON_SPELL_TARGET_READY = 117 -- OnSpellTargetReady
MODIFIER_EVENT_ON_SPENT_MANA = 140 -- OnSpentMana
MODIFIER_EVENT_ON_STATE_CHANGED = 135 -- OnStateChanged
MODIFIER_EVENT_ON_TAKEDAMAGE = 134 -- OnTakeDamage
MODIFIER_EVENT_ON_TAKEDAMAGE_KILLCREDIT = 146 -- OnTakeDamageKillCredit
MODIFIER_EVENT_ON_TELEPORTED = 142 -- OnTeleported
MODIFIER_EVENT_ON_TELEPORTING = 141 -- OnTeleporting
MODIFIER_EVENT_ON_UNIT_MOVED = 126 -- OnUnitMoved
MODIFIER_FUNCTION_INVALID = 255
MODIFIER_FUNCTION_LAST = 173
MODIFIER_PROPERTY_ABILITY_LAYOUT = 164 -- GetModifierAbilityLayout
MODIFIER_PROPERTY_ABSOLUTE_NO_DAMAGE_MAGICAL = 104 -- GetAbsoluteNoDamageMagical
MODIFIER_PROPERTY_ABSOLUTE_NO_DAMAGE_PHYSICAL = 103 -- GetAbsoluteNoDamagePhysical
MODIFIER_PROPERTY_ABSOLUTE_NO_DAMAGE_PURE = 105 -- GetAbsoluteNoDamagePure
MODIFIER_PROPERTY_ABSORB_SPELL = 93 -- GetAbsorbSpell
MODIFIER_PROPERTY_ALWAYS_ALLOW_ATTACK = 112 -- GetAlwaysAllowAttack
MODIFIER_PROPERTY_ATTACKSPEED_BASE_OVERRIDE = 22 -- GetModifierAttackSpeedBaseOverride
MODIFIER_PROPERTY_ATTACKSPEED_BONUS_CONSTANT = 24 -- GetModifierAttackSpeedBonus_Constant
MODIFIER_PROPERTY_ATTACK_POINT_CONSTANT = 27 -- GetModifierAttackPointConstant
MODIFIER_PROPERTY_ATTACK_RANGE_BONUS = 69 -- GetModifierAttackRangeBonus
MODIFIER_PROPERTY_ATTACK_RANGE_BONUS_UNIQUE = 70 -- GetModifierAttackRangeBonusUnique
MODIFIER_PROPERTY_AVOID_DAMAGE = 42 -- GetModifierAvoidDamage
MODIFIER_PROPERTY_AVOID_SPELL = 43 -- GetModifierAvoidSpell
MODIFIER_PROPERTY_BASEATTACK_BONUSDAMAGE = 3 -- GetModifierBaseAttack_BonusDamage
MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE = 34 -- GetModifierBaseDamageOutgoing_Percentage
MODIFIER_PROPERTY_BASEDAMAGEOUTGOING_PERCENTAGE_UNIQUE = 35 -- GetModifierBaseDamageOutgoing_PercentageUnique
MODIFIER_PROPERTY_BASE_ATTACK_TIME_CONSTANT = 26 -- GetModifierBaseAttackTimeConstant
MODIFIER_PROPERTY_BASE_MANA_REGEN = 51 -- GetModifierBaseRegen
MODIFIER_PROPERTY_BONUS_DAY_VISION = 96 -- GetBonusDayVision
MODIFIER_PROPERTY_BONUS_NIGHT_VISION = 97 -- GetBonusNightVision
MODIFIER_PROPERTY_BONUS_NIGHT_VISION_UNIQUE = 98 -- GetBonusNightVisionUnique
MODIFIER_PROPERTY_BONUS_VISION_PERCENTAGE = 99 -- GetBonusVisionPercentage
MODIFIER_PROPERTY_BOUNTY_CREEP_MULTIPLIER = 115 -- GetModifierBountyCreepMultiplier
MODIFIER_PROPERTY_BOUNTY_OTHER_MULTIPLIER = 116 -- GetModifierBountyOtherMultiplier
MODIFIER_PROPERTY_CAN_ATTACK_TREES = 170 -- GetModifierCanAttackTrees
MODIFIER_PROPERTY_CASTTIME_PERCENTAGE = 79 -- GetModifierPercentageCasttime
MODIFIER_PROPERTY_CAST_RANGE_BONUS = 67 -- GetModifierCastRangeBonus
MODIFIER_PROPERTY_CAST_RANGE_BONUS_STACKING = 68 -- GetModifierCastRangeBonusStacking
MODIFIER_PROPERTY_CHANGE_ABILITY_VALUE = 163 -- GetModifierChangeAbilityValue
MODIFIER_PROPERTY_COOLDOWN_PERCENTAGE = 77 -- GetModifierPercentageCooldown
MODIFIER_PROPERTY_COOLDOWN_PERCENTAGE_STACKING = 78 -- GetModifierPercentageCooldownStacking
MODIFIER_PROPERTY_COOLDOWN_REDUCTION_CONSTANT = 25 -- GetModifierCooldownReduction_Constant
MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE = 28 -- GetModifierDamageOutgoing_Percentage
MODIFIER_PROPERTY_DAMAGEOUTGOING_PERCENTAGE_ILLUSION = 29 -- GetModifierDamageOutgoing_Percentage_Illusion
MODIFIER_PROPERTY_DEATHGOLDCOST = 81 -- GetModifierConstantDeathGoldCost
MODIFIER_PROPERTY_DISABLE_AUTOATTACK = 95 -- GetDisableAutoAttack
MODIFIER_PROPERTY_DISABLE_HEALING = 111 -- GetDisableHealing
MODIFIER_PROPERTY_DISABLE_TURNING = 161 -- GetModifierDisableTurning
MODIFIER_PROPERTY_EVASION_CONSTANT = 40 -- GetModifierEvasion_Constant
MODIFIER_PROPERTY_EXP_RATE_BOOST = 82 -- GetModifierPercentageExpRateBoost
MODIFIER_PROPERTY_EXTRA_HEALTH_BONUS = 61 -- GetModifierExtraHealthBonus
MODIFIER_PROPERTY_EXTRA_HEALTH_PERCENTAGE = 63 -- GetModifierExtraHealthPercentage
MODIFIER_PROPERTY_EXTRA_MANA_BONUS = 62 -- GetModifierExtraManaBonus
MODIFIER_PROPERTY_EXTRA_STRENGTH_BONUS = 60 -- GetModifierExtraStrengthBonus
MODIFIER_PROPERTY_FIXED_ATTACK_RATE = 23 -- GetModifierAttackSpeedBaseOverride
MODIFIER_PROPERTY_FIXED_DAY_VISION = 100 -- GetFixedDayVision
MODIFIER_PROPERTY_FIXED_NIGHT_VISION = 101 -- GetFixedNightVision
MODIFIER_PROPERTY_FORCE_DRAW_MINIMAP = 160 -- GetForceDrawOnMinimap
MODIFIER_PROPERTY_HEALTH_BONUS = 58 -- GetModifierHealthBonus
MODIFIER_PROPERTY_HEALTH_REGEN_CONSTANT = 56 -- GetModifierConstantHealthRegen
MODIFIER_PROPERTY_HEALTH_REGEN_PERCENTAGE = 57 -- GetModifierHealthRegenPercentage
MODIFIER_PROPERTY_HEAL_AMPLIFY_PERCENTAGE = 32 -- GetModifierHealAmplify_Percentage
MODIFIER_PROPERTY_IGNORE_CAST_ANGLE = 162 -- GetModifierIgnoreCastAngle
MODIFIER_PROPERTY_IGNORE_COOLDOWN = 169 -- GetModifierIgnoreCooldown
MODIFIER_PROPERTY_ILLUSION_LABEL = 107 -- GetModifierIllusionLabel
MODIFIER_PROPERTY_INCOMING_DAMAGE_ILLUSION = 172
MODIFIER_PROPERTY_INCOMING_DAMAGE_PERCENTAGE = 36 -- GetModifierIncomingDamage_Percentage
MODIFIER_PROPERTY_INCOMING_PHYSICAL_DAMAGE_CONSTANT = 38 -- GetModifierIncomingPhysicalDamageConstant
MODIFIER_PROPERTY_INCOMING_PHYSICAL_DAMAGE_PERCENTAGE = 37 -- GetModifierIncomingPhysicalDamage_Percentage
MODIFIER_PROPERTY_INCOMING_SPELL_DAMAGE_CONSTANT = 39 -- GetModifierIncomingSpellDamageConstant
MODIFIER_PROPERTY_INVISIBILITY_LEVEL = 9 -- GetModifierInvisibilityLevel
MODIFIER_PROPERTY_IS_ILLUSION = 106 -- GetIsIllusion
MODIFIER_PROPERTY_IS_SCEPTER = 154 -- GetModifierScepter
MODIFIER_PROPERTY_LIFETIME_FRACTION = 157 -- GetUnitLifetimeFraction
MODIFIER_PROPERTY_MAGICAL_CONSTANT_BLOCK = 85 -- GetModifierMagical_ConstantBlock
MODIFIER_PROPERTY_MAGICAL_RESISTANCE_BONUS = 49 -- GetModifierMagicalResistanceBonus
MODIFIER_PROPERTY_MAGICAL_RESISTANCE_DECREPIFY_UNIQUE = 50 -- GetModifierMagicalResistanceDecrepifyUnique
MODIFIER_PROPERTY_MAGICAL_RESISTANCE_DIRECT_MODIFICATION = 48 -- GetModifierMagicalResistanceDirectModification
MODIFIER_PROPERTY_MAGICDAMAGEOUTGOING_PERCENTAGE = 33 -- GetModifierMagicDamageOutgoing_Percentage
MODIFIER_PROPERTY_MANACOST_PERCENTAGE = 80 -- GetModifierPercentageManacost
MODIFIER_PROPERTY_MANA_BONUS = 59 -- GetModifierManaBonus
MODIFIER_PROPERTY_MANA_REGEN_CONSTANT = 52 -- GetModifierConstantManaRegen
MODIFIER_PROPERTY_MANA_REGEN_CONSTANT_UNIQUE = 53 -- GetModifierConstantManaRegenUnique
MODIFIER_PROPERTY_MANA_REGEN_PERCENTAGE = 54 -- GetModifierPercentageManaRegen
MODIFIER_PROPERTY_MANA_REGEN_TOTAL_PERCENTAGE = 55 -- GetModifierTotalPercentageManaRegen
MODIFIER_PROPERTY_MAX_ATTACK_RANGE = 71 -- GetModifierMaxAttackRange
MODIFIER_PROPERTY_MIN_HEALTH = 102 -- GetMinHealth
MODIFIER_PROPERTY_MISS_PERCENTAGE = 44 -- GetModifierMiss_Percentage
MODIFIER_PROPERTY_MODEL_CHANGE = 152 -- GetModifierModelChange
MODIFIER_PROPERTY_MODEL_SCALE = 153 -- GetModifierModelScale
MODIFIER_PROPERTY_MOVESPEED_ABSOLUTE = 18 -- GetModifierMoveSpeed_Absolute
MODIFIER_PROPERTY_MOVESPEED_ABSOLUTE_MIN = 19 -- GetModifierMoveSpeed_AbsoluteMin
MODIFIER_PROPERTY_MOVESPEED_BASE_OVERRIDE = 12 -- GetModifierMoveSpeedOverride
MODIFIER_PROPERTY_MOVESPEED_BONUS_CONSTANT = 11 -- GetModifierMoveSpeedBonus_Constant
MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE = 13 -- GetModifierMoveSpeedBonus_Percentage
MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE_UNIQUE = 14 -- GetModifierMoveSpeedBonus_Percentage_Unique
MODIFIER_PROPERTY_MOVESPEED_BONUS_PERCENTAGE_UNIQUE_2 = 15 -- GetModifierMoveSpeedBonus_Percentage_Unique_2
MODIFIER_PROPERTY_MOVESPEED_BONUS_UNIQUE = 16 -- GetModifierMoveSpeedBonus_Special_Boots
MODIFIER_PROPERTY_MOVESPEED_BONUS_UNIQUE_2 = 17 -- GetModifierMoveSpeedBonus_Special_Boots_2
MODIFIER_PROPERTY_MOVESPEED_LIMIT = 20 -- GetModifierMoveSpeed_Limit
MODIFIER_PROPERTY_MOVESPEED_MAX = 21 -- GetModifierMoveSpeed_Max
MODIFIER_PROPERTY_NEGATIVE_EVASION_CONSTANT = 41 -- GetModifierNegativeEvasion_Constant
MODIFIER_PROPERTY_OVERRIDE_ANIMATION = 90 -- GetOverrideAnimation
MODIFIER_PROPERTY_OVERRIDE_ANIMATION_RATE = 92 -- GetOverrideAnimationRate
MODIFIER_PROPERTY_OVERRIDE_ANIMATION_WEIGHT = 91 -- GetOverrideAnimationWeight
MODIFIER_PROPERTY_OVERRIDE_ATTACK_MAGICAL = 113 -- GetOverrideAttackMagical
MODIFIER_PROPERTY_PERSISTENT_INVISIBILITY = 10 -- GetModifierPersistentInvisibility
MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS = 45 -- GetModifierPhysicalArmorBonus
MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS_UNIQUE = 46 -- GetModifierPhysicalArmorBonusUnique
MODIFIER_PROPERTY_PHYSICAL_ARMOR_BONUS_UNIQUE_ACTIVE = 47 -- GetModifierPhysicalArmorBonusUniqueActive
MODIFIER_PROPERTY_PHYSICAL_CONSTANT_BLOCK = 86 -- GetModifierPhysical_ConstantBlock
MODIFIER_PROPERTY_PHYSICAL_CONSTANT_BLOCK_SPECIAL = 87 -- GetModifierPhysical_ConstantBlockSpecial
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE = 0 -- GetModifierPreAttack_BonusDamage
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE_POST_CRIT = 2 -- GetModifierPreAttack_BonusDamagePostCrit
MODIFIER_PROPERTY_PREATTACK_BONUS_DAMAGE_PROC = 1 -- GetModifierPreAttack_BonusDamage_Proc
MODIFIER_PROPERTY_PREATTACK_CRITICALSTRIKE = 83 -- GetModifierPreAttack_CriticalStrike
MODIFIER_PROPERTY_PREATTACK_TARGET_CRITICALSTRIKE = 84 -- GetModifierPreAttack_Target_CriticalStrike
MODIFIER_PROPERTY_PRESERVE_PARTICLES_ON_MODEL_CHANGE = 167 -- PreserveParticlesOnModelChanged
MODIFIER_PROPERTY_PRE_ATTACK = 8 -- GetModifierPreAttack
MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_MAGICAL = 5 -- GetModifierProcAttack_BonusDamage_Magical
MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_PHYSICAL = 4 -- GetModifierProcAttack_BonusDamage_Physical
MODIFIER_PROPERTY_PROCATTACK_BONUS_DAMAGE_PURE = 6 -- GetModifierProcAttack_BonusDamage_Pure
MODIFIER_PROPERTY_PROCATTACK_FEEDBACK = 7 -- GetModifierProcAttack_Feedback
MODIFIER_PROPERTY_PROJECTILE_SPEED_BONUS = 72 -- GetModifierProjectileSpeedBonus
MODIFIER_PROPERTY_PROVIDES_FOW_POSITION = 158 -- GetModifierProvidesFOWVision
MODIFIER_PROPERTY_REFLECT_SPELL = 94 -- GetReflectSpell
MODIFIER_PROPERTY_REINCARNATION = 73 -- ReincarnateTime
MODIFIER_PROPERTY_RESPAWNTIME = 74 -- GetModifierConstantRespawnTime
MODIFIER_PROPERTY_RESPAWNTIME_PERCENTAGE = 75 -- GetModifierPercentageRespawnTime
MODIFIER_PROPERTY_RESPAWNTIME_STACKING = 76 -- GetModifierStackingRespawnTime
MODIFIER_PROPERTY_SPELLS_REQUIRE_HP = 159 -- GetModifierSpellsRequireHP
MODIFIER_PROPERTY_SPELL_AMPLIFY_PERCENTAGE = 31 -- GetModifierSpellAmplify_Percentage
MODIFIER_PROPERTY_STATS_AGILITY_BONUS = 65 -- GetModifierBonusStats_Agility
MODIFIER_PROPERTY_STATS_INTELLECT_BONUS = 66 -- GetModifierBonusStats_Intellect
MODIFIER_PROPERTY_STATS_STRENGTH_BONUS = 64 -- GetModifierBonusStats_Strength
MODIFIER_PROPERTY_SUPER_ILLUSION = 108 -- GetModifierSuperIllusion
MODIFIER_PROPERTY_SUPER_ILLUSION_WITH_ULTIMATE = 109 -- GetModifierSuperIllusionWithUltimate
MODIFIER_PROPERTY_TEMPEST_DOUBLE = 166 -- GetModifierTempestDouble
MODIFIER_PROPERTY_TOOLTIP = 151 -- OnTooltip
MODIFIER_PROPERTY_TOTALDAMAGEOUTGOING_PERCENTAGE = 30 -- GetModifierTotalDamageOutgoing_Percentage
MODIFIER_PROPERTY_TOTAL_CONSTANT_BLOCK = 89 -- GetModifierTotal_ConstantBlock
MODIFIER_PROPERTY_TOTAL_CONSTANT_BLOCK_UNAVOIDABLE_PRE_ARMOR = 88 -- GetModifierPhysical_ConstantBlockUnavoidablePreArmor
MODIFIER_PROPERTY_TRANSLATE_ACTIVITY_MODIFIERS = 155 -- GetActivityTranslationModifiers
MODIFIER_PROPERTY_TRANSLATE_ATTACK_SOUND = 156 -- GetAttackSound
MODIFIER_PROPERTY_TURN_RATE_PERCENTAGE = 110 -- GetModifierTurnRate_Percentage
MODIFIER_PROPERTY_UNIT_STATS_NEEDS_REFRESH = 114 -- GetModifierUnitStatsNeedsRefresh
MODIFIER_PROPERTY_VISUAL_Z_DELTA = 171 -- GetVisualZDelta