All about the Target
The following was originally an article that compiled documentation about the Target key, which wasn't well documented anywhere:
"Target" is one tricky key.
And yet, it's arguably the most important KV to understand, as its found in almost every datadriven Action.
Having a wrong "Target" block is the number one reason of having to relaunch the game, testing with many combinations until you finally get the right context.
Quoting the wiki on this:
Note: These names mean different things in different events. It may require some experimentation to discover exactly what in each case.
This article intends to compile every Target-related decision.
To test everything and get results, I used some simple scripts to test which parameters are seen in each event context. The KV and Lua Scripts are found below, along with all the debug data.
After running many tests with different ability behaviors, here's what I got:
ABILITY EVENT CONTEXTS
Behavior: DOTA_ABILITY_BEHAVIOR_NO_TARGET
| Event | Target |
|---|---|
| OnSpellStart | CASTER, UNIT, ATTACKER |
| OnAbilityPhaseStart | CASTER, UNIT, ATTACKER |
DOTA_ABILITY_BEHAVIOR_UNIT_TARGET
This behavior adds TARGET as a possible value for the Target key:
| Event | Target |
|---|---|
| OnSpellStart | CASTER, TARGET, UNIT, ATTACKER |
| OnAbilityPhaseStart | CASTER, TARGET, UNIT, ATTACKER |
AbilityChannelTime > 0
DOTA_ABILITY_BEHAVIOR_CHANNELLED only purpose is to change the ability Tooltip to "Channeled". In fact, the BEHAVIOR_CHANNELED isn't even needed, "AbilityChannelTime" is all that matters for the actual ability behavior.
| Event | Target |
|---|---|
| OnChannelFinish | CASTER, TARGET, UNIT, ATTACKER |
| OnChannelInterrupted | CASTER, TARGET, UNIT, ATTACKER |
| OnChannelSucceeded | CASTER, TARGET, UNIT, ATTACKER |
With Linear and Tracking Projectile Actions
| Event | Target |
|---|---|
| OnProjectileFinish | CASTER, TARGET, PROJECTILE |
| OnProjectileHitUnit | CASTER, TARGET, PROJECTILE |
DOTA_ABILITY_BEHAVIOR_TOGGLE
| Event | Target |
|---|---|
| OnToggleOff | CASTER |
| OnToggleOn | CASTER |
Item pickup and drop
| Event | Target |
|---|---|
| OnEquip | CASTER |
| OnUnequip | CASTER |
Any Behavior
| Event | Target |
|---|---|
| OnOwnerDied | CASTER |
| OnOwnerSpawned | CASTER |
| OnUpgrade | CASTER |
MODIFIER EVENT CONTEXTS
Caster-Target
Target always refers to the owner of the modifier.
| Event | Target |
|---|---|
| OnCreated | CASTER, TARGET |
| OnDestroy | CASTER, TARGET |
| OnIntervalThink | CASTER, TARGET |
| OnProjectileDodge | CASTER, TARGET |
Damage
In these events we can send an extra parameter, referenced as %attack_damage
Note that this is not a value taken from an AbilityValues, instead it is generated by the system to be used in very particular events.
| Event | Target | %attack_damage value |
|---|---|---|
| OnTakeDamage | CASTER, UNIT, ATTACKER | post reduction |
| OnDealDamage | CASTER, UNIT, ATTACKER | post reduction |
Attacks
In some of the attack events we can also use the %attack_damage
| Event | Target | %attack_damage value |
|---|---|---|
| OnAttack | CASTER, TARGET, ATTACKER | 0 |
| OnAttackStart | CASTER, TARGET, ATTACKER | 0 |
| OnAttackAllied | CASTER, TARGET, ATTACKER | 0 |
| OnAttacked | CASTER, TARGET, ATTACKER | post reduction |
| OnAttackLanded | CASTER, TARGET, ATTACKER | before reduction, the real attack value |
| OnAttackFailed | CASTER, TARGET, ATTACKER | before reduction, the real attack value |
Killing
| Event | Target |
|---|---|
| OnDeath | CASTER, UNIT, ATTACKER |
| OnKill | CASTER, UNIT, ATTACKER |
| OnHeroKilled | CASTER, TARGET, ATTACKER |
Caster-Unit
| Event | Target | Extra |
|---|---|---|
| OnAbilityEndChannel | CASTER, UNIT | TARGET if the ability isn't NO_TARGET |
| OnAbilityExecuted | CASTER, UNIT | TARGET if the ability isn't NO_TARGET |
| OnOrder | CASTER, UNIT | TARGET if the Order is an ability with target |
| OnRespawn | CASTER, UNIT | needs "Attributes" "PERMANENT" on its modifier |
| OnManaGained | CASTER, UNIT | |
| OnSpentMana | CASTER, UNIT | |
| OnStateChanged | CASTER, UNIT | |
| OnTeleporting | CASTER, UNIT | |
| OnTeleported | CASTER, UNIT | |
| OnUnitMoved | CASTER, UNIT | |
| OnHealReceived | CASTER, UNIT | |
| OnHealthGained | CASTER, UNIT |
Never Triggered
OnAbilityStart - Broken?
Raw Data
This section has the raw data used to compile the above.
Scripts:
KV
OnSomethingEvent
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnSomethingEvent"
}
}2
3
4
5
6
7
8
9
The entire ability script to test them all in the same cast is in the full test ability script at the bottom of this article.
Lua
function TargetTest( event )
local caster = event.caster
local target = event.target
local unit = event.unit
local attacker = event.attacker
local ability = event.ability
-- Tables
local target_points = event.target_points
local target_entities = event.target_entities
-- Extra parameter
local EventName = event.EventName
local Damage = event.Damage
print("**"..EventName.."**")
print("~~~")
if caster then print("CASTER: "..caster:GetUnitName()) end
if target then print("TARGET: "..target:GetUnitName()) end
if unit then print("UNIT: "..unit:GetUnitName()) end
if attacker then print("ATTACKER: "..attacker:GetUnitName()) end
if Damage then print("DAMAGE: "..Damage) end
if target_points then
for k,v in pairs(target_points) do
print("POINT",k,v)
end
end
-- Multiple Targets
if target_entities then
for k,v in pairs(target_entities) do
print("TARGET "..k..": "..v:GetUnitName())
end
end
--DeepPrintTable(event)
print("~~~")
end2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
Will produce console messages like this:
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
Results on casting abilities with every Ability Event at the same time, Alchemist is our caster, Abaddon is an enemy hero.
Ability Event Context
Any Behaviors
OnUpgrade
CASTER: npc_dota_hero_alchemistDOTA_ABILITY_BEHAVIOR_NO_TARGET
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
OnSpellStart
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
DOTA_ABILITY_BEHAVIOR_UNIT_TARGET
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
DOTA_ABILITY_BEHAVIOR_UNIT_TARGET | DOTA_ABILITY_BEHAVIOR_CHANNELLED
Without an AbilityChannelTime value it will just do the OnSpellStart and OnAbilityPhaseStart, AbilityChannelTime is "0" by default.
In fact, the BEHAVIOR_CHANNELED isn't even needed, AbilityChannelTime is all that matters for the actual ability behavior while the BEHAVIOR_CHANNELED is just for tooltip.
"AbilityChannelTime" "1.0"
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
After 1 second, it displays:
OnChannelFinish
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnChannelSucceeded
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
Moving after the spell started channeling results in:
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnChannelFinish
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnChannelInterrupted
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
DOTA_ABILITY_BEHAVIOR_POINT
If you don't pass POINT to the RunScript, you'll get this:
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
OnSpellStart
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
If you add UNIT_TARGET to the behaviors, it produces this, but only when you target an enemy instead of the ground:
OnAbilityPhaseStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist2
3
4
Adding "Target" "POINT" to the RunScript gives us the point targeted when iterating over the table of target_points
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAbilityPhaseStart"
"Target" "POINT"
}2
3
4
5
6
7
OnSpellStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist
POINT 1 Vector 000000000322E9D8 [-296.343750 -84.625000 128.000000]2
3
4
5
In fact, it doesn't need to be a behavior POINT ability to produce a point entity.
PROJECTILE
Added a Linear Projectile to the event_test ability in the OnSpellStart, DOTA_ABILITY_BEHAVIOR_UNIT_TARGET
"LinearProjectile"
{
"Target" "POINT"
"EffectName" "particles/units/heroes/hero_mirana/mirana_spell_arrow.vpcf"
"MoveSpeed" "1000"
"StartRadius" "100"
"StartPosition" "attach_attack1"
"EndRadius" "100"
"HasFrontalCone" "0"
"FixedDistance" "1000"
"TargetTeams" "DOTA_UNIT_TARGET_TEAM_ENEMY"
"TargetTypes" "DOTA_UNIT_TARGET_BASIC | DOTA_UNIT_TARGET_HERO"
"TargetFlags" "DOTA_UNIT_TARGET_FLAG_NONE"
}2
3
4
5
6
7
8
9
10
11
12
13
14
Triggers up this action when the projectile collides with the enemy hero. Changing the Linear to a Tracking doesn't change the basic targeting.
OnProjectileHitUnit
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon2
If I fire the projectile and it goes for the entire fixed distance, it results in this:
OnProjectileFinish
CASTER: npc_dota_hero_alchemistPassing the Point and we'll get a POINT 1
Now with this test we can check if POINT and PROJECTILE are the same Vector:
"OnSpellStart"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnSpellStart"
"Target" "POINT"
}
}
"OnProjectileHitUnit"
{
"DeleteOnHit" "1"
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnProjectileHitUnitUnit"
"Target" "POINT"
}
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnProjectileHitUnitProjectile"
"Target" "PROJECTILE"
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Result after a couple of units hit:
OnSpellStart
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_alchemist
POINT 1 Vector 0000000003255BA0 [-513.468750 -16.468750 128.000000]2
3
4
OnProjectileHitUnitUnit
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_neutral_kobold
POINT 1 Vector 0000000003244688 [-742.161560 22.603647 162.187149]2
3
OnProjectileHitUnitProjectile
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_neutral_kobold
POINT 1 Vector 000000000322FA00 [-742.161560 22.603647 162.187149]2
3
OnProjectileHitUnitUnit
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_neutral_kobold
POINT 1 Vector 000000000325CCC8 [-676.447083 11.376265 162.187149]2
3
OnProjectileHitUnitProjectile
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_neutral_kobold
POINT 1 Vector 000000000326CDA8 [-676.447083 11.376265 162.187149]2
3
OnProjectileFinishPoint
CASTER: npc_dota_hero_alchemist
POINT 1 Vector 0000000003231680 [210.698059 -140.193390 162.187149]2
OnProjectileFinishProjectile
CASTER: npc_dota_hero_alchemist
POINT 1 Vector 00000000032692B8 [210.698059 -140.193390 162.187149]2
We can see how OnSpellStart has the POINT where we clicked
Note: Projectiles are not an entity.
DYING
OnOwnerDied
CASTER: npc_dota_hero_alchemistRESPAWN LATER
OnOwnerSpawned
CASTER: npc_dota_hero_alchemistITEM
OnEquip
CASTER: npc_dota_hero_alchemistOnUnequip
CASTER: npc_dota_hero_alchemistModifier Event Context
Applying a modifier on an enemy abaddon with all the modifier events (2nd ability in the full script below)
OnManaGained
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon2
OnHealReceived
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon2
These 2 spam the console heavily (even though the target is on full health and mana) so I'll move to others.
First triggered after the modifier is applied
OnCreated
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon2
Enemy learned an ability triggered the OnOrder function, pressing Stop key repeatedly did the same
OnOrder
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon2
Enemy moved only triggered this (meaning OnOrder doesn't count moving)
OnUnitMoved
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon2
Added if ability then print("ABILITY: "..ability:GetAbilityName()) end to the script to check that the ability handle is always the name of the modifier.
Enemy cast a damaging unit target spell on caster of the modifier, these 3 events trigger on this order:
OnOrder
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon
ABILITY: event_test2
3
4
OnAbilityExecuted
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon
ABILITY: event_test2
3
4
OnDealDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
ABILITY: event_test2
3
4
Now testing the Damage events, making use of the magic %attack_damage that Valve added at some point.
"OnTakeDamage"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnTakeDamage"
"Damage" "%attack_damage"
}
}
"OnDealDamage"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnDealDamage"
"Damage" "%attack_damage"
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
This started triggering the OnHealthGained, as the target with the modifier gained health, unlike the OnHealReceived which triggered every time.
OnHealthGained
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon2
Back to testing the Damage events, I added local Damage = event.Damage to the script and confirmed that both OnTakeDamage and OnDealDamage have access to the attack_damage:
OnTakeDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon
ATTACKER: npc_dota_hero_alchemist
DAMAGE: 56.2080535888672
3
4
OnAttackLanded
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon2
3
We also get triggers from the OnAttack events, when Abaddon attacks caster Alchemist:
OnAttackStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon2
3
OnAttack
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon2
3
OnAttackLanded
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon2
3
OnDealDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 67.1540298461912
3
4
Alchemist attacks Abaddon with our test modifier on Abaddon:
OnAttacked
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
ATTACKER: npc_dota_hero_alchemist
DAMAGE: 55.3691291809082
3
4
OnTakeDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_abaddon
ATTACKER: npc_dota_hero_alchemist
DAMAGE: 55.3691291809082
3
4
So both OnDealDamage and OnTakeDamage have access to the %attack_damage
What if we add attack_damage to the attack landed?
2 attacks:
OnAttackStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 02
3
4
OnAttack
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 02
3
4
OnAttackLanded
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 832
3
4
OnDealDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 70.5542297363282
3
4
OnAttackStart
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 02
3
4
OnAttack
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 02
3
4
OnAttackLanded
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 802
3
4
OnDealDamage
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon
DAMAGE: 68.0040817260742
3
4
Abaddon has an attack range of 79 to 89

OnAttackLanded attack_damage passes the attack value!
Casting a teleport scroll:
OnTeleporting
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
OnAbilityExecuted
CASTER: npc_dota_hero_alchemist
TARGET: npc_building_hut
UNIT: npc_dota_hero_alchemist2
3
After ending successfully:
OnTeleported
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
OnAbilityEndChannel
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
If the scroll is canceled, no OnTeleported is triggered.
I gave Abaddon some Evasion:
OnAttackFailed
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
ATTACKER: npc_dota_hero_alchemist
DAMAGE: 692
3
4
OnAttackFailed also has the real, pre mitigation attack_damage of the ATTACKER.
Kill some kobold
OnKill
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_neutral_kobold
ATTACKER: npc_dota_hero_alchemist2
3
Killed Abaddon, triggered
OnHeroKilled
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon
ATTACKER: npc_dota_hero_alchemist2
3
I used Chemical Rage, which is NO_TARGET, triggered these:
OnSpentMana
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
OnAbilityExecuted
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
OnStateChanged
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
OnStateChanged triggers every time a new State is applied (the State Changed because the ability applies a short INVULNERABLE state)
When Dying, OnDeath occurs before the modifiers are destroyed
OnDeath
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist
ATTACKER: npc_dota_hero_abaddon2
3
OnDestroy
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist2
OnRespawn will only trigger with PERMANENT modifiers, because without this key the mods will be destroy when the owner dies.
"Attributes" "MODIFIER_ATTRIBUTE_PERMANENT"
"OnRespawn"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnRespawn"
}
}2
3
4
5
6
7
8
9
10
OnRespawn
CASTER: npc_dota_hero_alchemist
UNIT: npc_dota_hero_alchemist2
Dodging a projectile
OnProjectileDodge
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist2
OnDestroy has the same scope as OnCreated
OnDestroy
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_abaddon2
OnIntervalThink same as OnCreated
OnIntervalThink
CASTER: npc_dota_hero_alchemist
TARGET: npc_dota_hero_alchemist2
Full Test Ability Script
// Ability Events
"event_test"
{
"BaseClass" "ability_datadriven"
"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" //Change this to trigger different events
"AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_BOTH"
"AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC"
"AbilityCastRange" "700"
"AbilityCastPoint" "0.3"
"AbilityCooldown" "0.0"
"OnSpellStart"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnSpellStart"
}
}
"OnAbilityPhaseStart"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAbilityPhaseStart"
}
}
"OnToggleOff"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnToggleOff"
}
}
"OnToggleOn"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnToggleOn"
}
}
"OnChannelFinish"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnChannelFinish"
}
}
"OnChannelInterrupted"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnChannelInterrupted"
}
}
"OnChannelSucceeded"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnChannelSucceeded"
}
}
"OnEquip"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnEquip"
}
}
"OnUnequip"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnUnequip"
}
}
"OnOwnerDied"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnOwnerDied"
}
}
"OnOwnerSpawned"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnOwnerSpawned"
}
}
"OnProjectileFinish"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnProjectileFinish"
}
}
"OnProjectileHitUnit"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnProjectileHitUnit"
}
}
"OnUpgrade"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnUpgrade"
}
}
"OnSpawn"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnSpawn"
}
}
}
// Modifier Events
"event_test"
{
"BaseClass" "ability_datadriven"
"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_UNIT_TARGET" //Change this to trigger different events
"AbilityUnitTargetTeam" "DOTA_UNIT_TARGET_TEAM_BOTH"
"AbilityUnitTargetType" "DOTA_UNIT_TARGET_HERO | DOTA_UNIT_TARGET_BASIC"
"AbilityCastRange" "700"
"AbilityCastPoint" "0.3"
"AbilityCooldown" "0.0"
"OnSpellStart"
{
"ApplyModifier"
{
"ModifierName" "modifier_test"
"Target" "TARGET"
}
}
"Modifiers"
{
"modifier_test"
{
"OnCreated"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnCreated"
}
}
"OnAbilityEndChannel"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAbilityEndChannel"
}
}
"OnAbilityExecuted"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAbilityExecuted"
}
}
"OnManaGained"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnManaGained"
}
}
"OnRespawn"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnRespawn"
}
}
"OnSpentMana"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnSpentMana"
}
}
"OnStateChanged"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnStateChanged"
}
}
"OnTakeDamage"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnTakeDamage"
}
}
"OnDealDamage"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnDealDamage"
}
}
"OnTeleported"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnTeleported"
}
}
"OnTeleporting"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnTeleporting"
}
}
"OnOrder"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnOrder"
}
}
"OnProjectileDodge"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnProjectileDodge"
}
}
"OnUnitMoved"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnUnitMoved"
}
}
"OnHealReceived"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnHealReceived"
}
}
"OnHealthGained"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnHealthGained"
}
}
"OnAttack"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttack"
}
}
"OnAttackStart"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttackStart"
}
}
"OnAttackAllied"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttackAllied"
}
}
"OnAttacked"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttacked"
}
}
"OnAttackFailed"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttackFailed"
}
}
"OnAttackLanded"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAttackLanded"
}
}
"OnDeath"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnDeath"
}
}
"OnKill"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnKill"
}
}
"OnHeroKilled"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnHeroKill"
}
}
// "ThinkInterval" "1.0"
"OnIntervalThink"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnIntervalThink"
}
}
"OnDestroy"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnDestroy"
}
}
// This doesn't trigger.
"OnAbilityStart"
{
"RunScript"
{
"ScriptFile" "test.lua"
"Function" "TargetTest"
"EventName" "OnAbilityStart"
}
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
OnAbilityStart -> NEGATORY, Fails.