- Joined
- Jul 12, 2005
- Messages
- 764
Attack-damage detection engine
This system uses Kattana's Handle Vars!
This system is supposed to detect when an attack hits the target unit. It is important, that this system detects only damage coming from attack, so it’s not the same as the WEU trigger ‘Any unit damaged’! (I just heared about it, I don’t use WEU on purpose)
Advantages of this system (over ‘Any unit is attacked’ triggers):
-Detects when the attack HITS the target. Noticable on ranged attacks.
-Does care about “missed” attacks.
-We can get the exact value of the damage!
How it works:
1. First we create a trigger that detects when a unit is attacked (let’s call this the “AttackTrigger” - AT)
JASS:
function AttackTrigger_Conditions takes nothing returns boolean
//Conditions will be put here later.
endfunction
function AttackTrigger_Actions takes nothing returns nothing
local unit AttackingUnit = GetAttacker() //We store the attacking unit
local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
endfunction
function InitTrig_AttackDamageDetection takes nothing returns nothing
local trigger AttackTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(AttackTrigger, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(AttackTrigger, Condition( function AttackTrigger_Conditions))
call TriggerAddAction(AttackTrigger, function AttackTrigger_Actions)
set AttackTrigger = null
endfunction
2. The AT creates a new (local) trigger that fires when the attacked unit takes damage. Let’s call this the “DamageTrigger” – DT.
JASS:
function DamageTrigger_Conditions takes nothing returns boolean
endfunction
function DamageTrigger_Actions takes nothing returns nothing
endfunction
function AttackTrigger_Actions takes nothing returns nothing
local unit AttackingUnit = GetAttacker() //We store the attacking unit
local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
local trigger DamageTrigger = CreateTrigger() //We create the damage trigger
call TriggerRegisterUnitEvent(DamageTrigger, AttackedUnit, EVENT_UNIT_DAMAGED)
call TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions))
call TriggerAddAction(DamageTrigger, function DamageTrigger_Actions)
endfunction
3. Note that there is duration between the attack and the hit (damage)! Think of ranged attacks as an example. In this period, the attacked unit can take damage from other units, so we must filter the one that’s caused by the attacking unit. For this, we must attach/store the attacker onto the DT, and make a check in the condition.
JASS:
function DamageTrigger_Conditions takes nothing returns boolean
return GetEventDamageSource() == GetHandleUnit(GetTriggeringTrigger(), "Attacker")
endfunction
function DamageTrigger_Actions takes nothing returns nothing
endfunction
function AttackTrigger_Actions takes nothing returns nothing
local unit AttackingUnit = GetAttacker() //We store the attacking unit
local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
local trigger DamageTrigger = CreateTrigger() //We create the damage trigger
call SetHandleHandle(DamageTrigger, "Attacker", AttackingUnit)
call TriggerRegisterUnitEvent(DamageTrigger, AttackedUnit, EVENT_UNIT_DAMAGED)
call TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions))
call TriggerAddAction(DamageTrigger, function DamageTrigger_Actions)
endfunction
4. Now that we filtered the attacker, we can go on to the DT. We have to identify which unit is which. Here it goes:
JASS:
function DamageTrigger_Actions takes nothing returns nothing
local unit AttackedUnit = GetTriggerUnit()
local unit AttackingUnit = GetEventDamageSource()
local trigger DamageTrigger = GetTriggeringTrigger()
call FlushHandleLocals(DamageTrigger)
call DestroyTrigger(DamageTrigger)
//Here come the actions you want originally with the attacked/attacking unit.
set DamageTrigger = null
set AttackingUnit = null
set AttackedUnit = null
endfunction
5. We are nearly(!) ready. We still have to deals with bugs that could occur:
The unit attacks, the DT is created, but the attacker misses. The DT remains forever. To eliminate this bug, we have to destroy the DT in the AT after a time period. After that, it’s time to null the locals.
JASS:
function AttackTrigger_Actions takes nothing returns nothing
local unit AttackingUnit = GetAttacker() //We store the attacking unit
local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
local trigger DamageTrigger = CreateTrigger() //We create the damage trigger
call SetHandleHandle(DamageTrigger, "Attacker", AttackingUnit)
call TriggerRegisterUnitEvent(DamageTrigger, AttackedUnit, EVENT_UNIT_DAMAGED)
call TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions))
call TriggerAddAction(DamageTrigger, function DamageTrigger_Actions)
call TriggerSleepAction(1.5)
call FlushHandleLocals(DamageTrigger)
call DestroyTrigger(DamageTrigger)
set DamageTrigger = null
set AttackingUnit = null
set AttackingUnit = null
endfunction
“BUG”And here, I have to mention something, because this is the only point where this system is not (it can’t be) 100% accurate. Because of that wait time. Let’s see some situations. Ideally Hunter attacks the Wild with an axe (melee). The DT is created, the damage is taken, the DT is destroyed, everything is just fine. Bug situations:
1) Hunter attacks the Wild with a bow (ranged). Same happens, but let’s say that the arrow does not reach Wild in 1.5 seconds! The DT is already destroyed, so no effect will go off…
2) Hunter attacks, but misses! The DT remains for 1.5 seconds, but what if Hunter’s attack-rate is less than 1.5 (0.8 for instance). He attacks again, creates a new DT, but the other one is still active, so double effect will go off…
6. Let’s solve bug #2. For this, we must store the DT on the attacker, and make a check in the AT’s condition if there’s an active DT already. I’ll highlight newly needed lines only:
JASS:
function AttackTrigger_Conditions takes nothing returns boolean
return GetHandleTrigger(GetAttacker(), "DamageTrig") == null
endfunction
function DamageTrigger_Conditions takes nothing returns boolean
// return GetEventDamageSource() == GetHandleUnit(GetTriggeringTrigger(), "Attacker")
endfunction
function DamageTrigger_Actions takes nothing returns nothing
// local unit AttackedUnit = GetTriggerUnit()
// local unit AttackingUnit = GetEventDamageSource()
// local trigger DamageTrigger = GetTriggeringTrigger()
// call FlushHandleLocals(DamageTrigger)
// call DestroyTrigger(DamageTrigger)
call SetHandleHandle(AttackingUnit, "DamageTrig", null)
// set DamageTrigger = null
// set AttackingUnit = null
// set AttackedUnit = null
endfunction
//
//function AttackTrigger_Actions takes nothing returns nothing
// local unit AttackingUnit = GetAttacker() //We store the attacking unit
// local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
// local trigger DamageTrigger = CreateTrigger() //We create the damage trigger
call SetHandleHandle(AttackingUnit, "DamageTrig", DamageTrigger)
// call SetHandleHandle(DamageTrigger, "Attacker", AttackingUnit)
// call TriggerRegisterUnitEvent(DamageTrigger, AttackedUnit, EVENT_UNIT_DAMAGED)
// call TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions))
// call TriggerAddAction(DamageTrigger, function DamageTrigger_Actions)
// call TriggerSleepAction(2)
// call FlushHandleLocals(DamageTrigger)
// call DestroyTrigger(DamageTrigger)
call SetHandleHandle(AttackingUnit, "DamageTrig", null)
// set DamageTrigger = null
// set AttackingUnit = null
// set AttackingUnit = null
endfunction
function InitTrig_AttackDamageDetection takes nothing returns nothing
// local trigger AttackTrigger = CreateTrigger()
// call TriggerRegisterAnyUnitEventBJ(AttackTrigger, EVENT_PLAYER_UNIT_ATTACKED)
// call TriggerAddCondition(AttackTrigger, Condition( function AttackTrigger_Conditions))
// call TriggerAddAction(AttackTrigger, function AttackTrigger_Actions)
// set AttackTrigger = null
endfunction
7. The final code should look like this:
This is an “artificial” critical strike (100% chance, 2x damage, without damage display just to keep the code clean).
JASS:
function AttackTrigger_Conditions takes nothing returns boolean
return GetHandleTrigger(GetAttacker(), "DamageTrig") == null and GetUnitAbilityLevel(GetAttacker(), 'A000') > 0
endfunction
function DamageTrigger_Conditions takes nothing returns boolean
return GetEventDamageSource() == GetHandleUnit(GetTriggeringTrigger(), "Attacker")
endfunction
function DamageTrigger_Actions takes nothing returns nothing
local unit AttackedUnit = GetTriggerUnit()
local unit AttackingUnit = GetEventDamageSource()
local trigger DamageTrigger = GetTriggeringTrigger()
call FlushHandleLocals(DamageTrigger)
call DestroyTrigger(DamageTrigger)
call SetHandleHandle(AttackingUnit, "DamageTrig", null)
//Here come the actions you want originally with the attacked/attacking unit.
call UnitDamageTarget(AttackingUnit, AttackedUnit, GetEventDamage(), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)
set DamageTrigger = null
set AttackingUnit = null
set AttackedUnit = null
endfunction
function AttackTrigger_Actions takes nothing returns nothing
local unit AttackingUnit = GetAttacker() //We store the attacking unit
local unit AttackedUnit = GetTriggerUnit() //We store the attacked unit - this one will take the damage
local trigger DamageTrigger = CreateTrigger() //We create the damage trigger
call SetHandleHandle(AttackingUnit, "DamageTrig", DamageTrigger)
call SetHandleHandle(DamageTrigger, "Attacker", AttackingUnit)
call TriggerRegisterUnitEvent(DamageTrigger, AttackedUnit, EVENT_UNIT_DAMAGED)
call TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions))
call TriggerAddAction(DamageTrigger, function DamageTrigger_Actions)
call TriggerSleepAction(2)
call FlushHandleLocals(DamageTrigger)
call DestroyTrigger(DamageTrigger)
call SetHandleHandle(AttackingUnit, "DamageTrig", null)
set DamageTrigger = null
set AttackingUnit = null
set AttackingUnit = null
endfunction
function InitTrig_AttackDamageDetection takes nothing returns nothing
local trigger AttackTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(AttackTrigger, EVENT_PLAYER_UNIT_ATTACKED)
call TriggerAddCondition(AttackTrigger, Condition( function AttackTrigger_Conditions))
call TriggerAddAction(AttackTrigger, function AttackTrigger_Actions)
set AttackTrigger = null
endfunction
Last edited by a moderator: