Lua Abilities and Modifiers

From Valve Developer Community
Jump to: navigation, search
Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024

Теперь у вас есть возможность создавать способности и модификаторы полностью используя Lua; Это позволяет создавать способности/модификаторы с необычными эффектами и продвинутой логикой скриптов.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 Способности

Для того чтобы создать Lua Способность, вам понадобится совершить несколько шагов :

В папке вашего аддона откройте scripts\npc\npc_abilities_custom.txt:

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

Для начала работы вашей Lua способности этого кода будет достаточно.

Далее в папке scripts\vscripts , вам необходимо создать файл с таким же именем которое вы указали в графе "ScriptFile" вашего npc_abilities_custom.txt . В нашем примере , название файла будет test_lua_ability.lua.

Открыв только что созданный файл, обозначьте новый Lua класс используя название вашей способности:

test_lua_ability = class ({})

Теперь у вас имеется основа для вашей Lua способности.Для того чтобы она начала действовать необходимо создать функции, которые движок игры будет вызывать при определенных условиях(script_help2 позволяет найти все эти функции).Несколько примеров.

Events

Движок вызывает данные функции при определенном состоянии способности.Добавьте их в свой скрипт если ваша способность должна функционировать при данном действии.

* OnSpellStart() -- Вызывается, когда закончилось время каста способности, ресурсы(мана) были потрачены - большинство способностей начинают свои действия здесь. Нет параметров.
* OnAbilityPhaseStart() -- Вызывается, когда начинается время каста способности W, ресурсы не были потрачены. Здесь могут быть условия при которых каст может быть успешном или безуспешным (Например враг слишком низкого уровня).Для этого добавьте в функцию return true или return false .
* OnAbilityPhaseInterrupted() -- Вызывается, когда способность была остановлена во время каста.
* OnProjectileThink( vLocation ) -- Если способность создала "projectile"((снаряд)стрела POTM,стан Леорика), данная функция будет вызываться до тех пор, пока снаряд движется, vLocation текущая позиция снаряда.
* OnProjectileHit( hTarget, vLocation ) -- Вызывается когда снаряд достиг цели, либо достиг максимальной дистанции. Если hTarget имеет значение null, это значит что снаряд исчез.Для того чтобы уничтожить снаряд добавьте в функцию  return true , для продолжения движения добавьте return false ( это применимо к линейным снарядам которые могут задеть множество юнитов, как Dragon Slave. Если снаряд достиг своей границы , он исчезнет даже если вы прописали return false ) 
* GetIntrinsicModifierName() -- Return возвращает название модификатора который пассивно добавляется способностью (например return "modifier_nevermore_necromastery" ).
* OnChannelFinish( bInterrupted ) -- Вызывается, когда применение способности было окончено(Lion's Mana Drain), параметр bInterrupted было применение успешным или оно было прервано.
* OnUpgrade() -- Вызывается, когда способность была улучшена.

Поведение при Использование

Как и обычные способности, ability_lua используют данные из npc_abilities_custom.txt для определения своих свойств, таких как кто может быть целью, mana cost, флаги(Например можно ли использовать на неуязвимых юнитов), какая команда может быть целью, а также поведение. Если ваша способность имеет разный эффект при различных условиях, вы можете данные из npc_abilities_custom.txt в вашем скрипте.

* GetBehavior() -- Определяет тип применения способности для изменения используйте  return DOTA_ABILITY_BEHAVIOR_UNIT_TARGET , а также другие значения с DOTA_ABILITY_BEHAVIOR.
* GetCooldown( nLevel ) -- Определяет cooldown способности. nLevel - уровень способности. Пример: return 37.0 .
* GetCastRange( vLocation, hTarget ) -- Определяет дальность применения. Пример: return 475 .
* GetChannelTime() -- Определяет время применения. Пример: return 5.0 .

Используя функция выше, вам скорее всего будет нужно возвращать стандартные данные при определенных условиях. Примером выступит Vengeful Spirit's Nether Swap; данная способность будет использовать измененную часть GetCooldown() если владелец имеет Aghanim's Scepter. В таких случаях, вы можете вызвать "BaseClass" для выполнения стандартной операции которая не использует ваши изменения в скрипте.

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

	return self.BaseClass.GetCooldown( self, nLevel )

Некоторые способности хотят использовать более красочные ошибки при использовании чем обычные. Для этого используется CastFilter. Основываясь на способе применения способности , вам надо выбрать нужную пару функцмий:

CastFilterResultTarget( hTarget ) -- hTarget юнит на которого использована способность.
GetCustomCastErrorTarget( hTarget) -- hTarget юнит на которого использована способность.

CastFilterResultLocation( vLocation ) -- vLocation вектор куда использована способность.
GetCustomCastErrorLocation( vLocation ) -- vLocation вектор куда использована способность.

CastFilterResult() -- Если способность не имеет цели(DOTA_ABILITY_BEHAVIOR_NO_TARGET)
GetCustomCastError() -- Если способность не имеет цели(DOTA_ABILITY_BEHAVIOR_NO_TARGET)

Все функции CastFilterResult должны возвращать UnitFilterResult ( например UF_SUCCESS, UF_FAIL_CUSTOM ). Return UF_SUCCESS продолжает выполнять способность, а return UF_FAIL_CUSTOM выдает соответствующую ошибку используя GetCustomCastError.
Все функции GetCustomCastError должны возвращать строку(string) ( ведущую к соответствующей локализации в файле addon_english.txt )

Вот пример CastFilter используемый Vengeful Spirit's Nether Swap. Nether Swap's можно использовать на крипах, но только при условии наличия Aghanim's Scepter. Nether Swap также нельзя применить на себя. Заметьте что CastFilterResult и GetCustomCastError являются общими функциями, это значит что они работают и на Клиенте и на Сервере.

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

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

Свойства

Есть также несколько свойств способностей которые не отображены в npc_abilities_custom.txt. В общем, вам нужны \эти свойства если ваша способность имеет отличающееся от обычных поведение. Примеры:

* IsStealable() -- Return true если вы хотите чтобы Rubick мог украсть данную способность.
* ProcsMagicStick() -- Return true если вы хотите чтобы вражеские герои получали заряды Magick Stick при использовании.
* IsRefreshable() -- Return true если вы хотите чтобы данная способность могла сбрасывает время перезарядки.
* GetPlaybackRateOverride() -- Return (число) скорость с которой должна проигрываться анимация использования.

Пример

Полная версия кода 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 Модификаторы

Устройство можификаторов во многом похоже на Lua Способности, но все же имеет различия. Также как и с Lua Способностью, вам надо создать .lua файл с именем вашего модификатора. Рассмотрим на примере Sven's Warcry. В начале файла, создайте класс с таким же названием как и файл. В данном случае, modifier_sven_warcry_lua .

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

Следующее действие требуется только для lua модификаторов; вы должны зарегистрировать их в движке, прежде чем создавать их. Хорошим местом для этого является способность к которой мы создаем модификатор. Обычно, модификаторы создаются способностями, но не всегда; если у вас именно такой случай, вы можете добавить данную функцию в код своего мода. Единственным условием является то, что модификатор должен быть зарегистрирован до того, как он будет использован. Так как modifier_sven_warcry_lua относится к способности Warcry, мы можем зарегистрировать его в ней. Обратите внимание что это находится в файле "sven_warcry_lua.lua", который отличается от того где находится модификатор.

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

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

Первый параметр - имя (также и имя файла с модификатором) модификатора который вы регистрируете; второй параметр тип модификатора, который отвечает за наличие контроллера движения и его тип. Существует несколько типов:

* LUA_MODIFIER_MOTION_NONE - Не применяет контроллеры движения
* LUA_MODIFIER_MOTION_HORIZONTAL - Горизонтальный контроллер
* LUA_MODIFIER_MOTION_VERTICAL - Вертикальный контроллер
* LUA_MODIFIER_MOTION_BOTH - Вертикальный и Горизонтальный контроллер

Теперь наш модификатор работоспособен и может быть добавлен любым обычным методом. Обратите внимание , Модификаторы (CDOTA_Buff) распознаваемый Lua тип файлов, таким образом вы можете создавать переменный и вызывать функции в модификаторах (script_help2 полный список), что полезно при создании собственных модификаторов. 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.

Обратите внимание, что эти функции вызываются раздельно друг от друга для Сервера и для Клиента. В целом, производить игровые вычисления лучше на стороне сервера.Клиентская сторона модификаторов используется в основном для отображения подсказок(бонусов которые отображаются в HUD).Если у вас появляются ошибки, но модификатор работает так, как задумано, скорей всего сторона клиента пытается произвести игровые вычисления.

Функции Модификаторов

Функции которые модификатор будет использовать задаются функцией DeclareFunctions(). Данная функция должна вернуть таблицу содержащую события и свойства которые ваш модификатор хочет задействовать. Целый список функций и событий довольно большой вы можете увидеть его прописав script_help2 в консоли. Рассмотрим модификатор Sven's Warcry . Он изменяет количество брони и скорость передвижения владельца, так что DeclareFunctions вернет соответствующие значения.

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

Следующим шагом мы изменим значения задействованых выше свойств. Каждое свойство имеет свою функцию к которой оно обращается.

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

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

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

Params параметр который является обычным для всех функций относящихся к событиям и свойствам. Это таблица содержащая данные определенные для данного события/свойства. Используя повтор и функцию Msg() , вы можете увидеть данные из этой таблицы. Свойства Warcry's просты, давайте посмторим на Sven's Great Cleave. Оьратите внимание что функции свойств выполняются как на Клиенте так и на Сервере, таким образом игровые вычисления будут происходить только на сервере.

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

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

Некоторые модификаторы должны производить действия через определенные интервалы. Вы можете добиться этого используя StartIntervalThink() на модификаторе. Как только функция была вызвана, движок игры будет искать функцию OnIntervalThink(), которая будет вызываться каждый интервал времени который вы указали. Выполнение OnIntervalThink() модно остановить используя StartIntervalThink() с значением -1 . Lina's Fiery Soul использует данные функции:

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

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