Lua Abilities and Modifiers
https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/Lua_Abilities_and_Modifiers
Lua Abilities and Modifiers
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.
Contents
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