1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  4. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  5. The results are out! Check them out.
    Dismiss Notice
  6. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  7. The raddest synthwave tracks were chosen - Check out our Music Contest #12 - Results and congratulate the winners!
    Dismiss Notice
  8. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Attack-damage detection engine (JASS)

Discussion in 'JASS/AI Scripts Tutorials' started by paskovich, Apr 29, 2007.

  1. paskovich

    paskovich

    Joined:
    Jul 12, 2005
    Messages:
    794
    Resources:
    2
    Tutorials:
    2
    Resources:
    2
    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)
    Code (vJASS):
     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

    From now on, I’ll illuminate the function(s) only that need to be refreshed/completed!

    2. The AT creates a new (local) trigger that fires when the attacked unit takes damage. Let’s call this the “DamageTrigger” – DT.
    Code (vJASS):
     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.
    Code (vJASS):
     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
    I hope it’s clearer now…

    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:
    Code (vJASS):
     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.
    Code (vJASS):
     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…

    For the first bug there’s no solution.

    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:
    Code (vJASS):
     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).
    Code (vJASS):
     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: Sep 1, 2007
  2. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Hmm, it seems like you are leaking a triggeraction. use TriggerRemoveAction for that.
     
  3. wyrmlord

    wyrmlord

    Joined:
    Oct 13, 2005
    Messages:
    252
    Resources:
    5
    Tools:
    1
    Maps:
    1
    Tutorials:
    3
    Resources:
    5
    Also, you need to remove the condition too. In addition to that, the Condition function returns a boolexpr which needs to be destroyed too (either that or just make it a global variable and use the global every time).
     
  4. paskovich

    paskovich

    Joined:
    Jul 12, 2005
    Messages:
    794
    Resources:
    2
    Tutorials:
    2
    Resources:
    2
    Umm... WHAT? :D
    Could you give me an example on how to make a local trigger leakless?
    -what to store
    -what to destroy/remove
    -what actions to use
     
  5. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    acording to Vexorian boolexpr's doesent leak when you use Condtition() I think it actually is risky destroying them.
    Easy to make a local trigger leakless.
    Code (vJASS):

    call SetHandleHandle(DamageTrigger,TriggerAddAction)(DamageTrigger, function DamageTrigger_Actions,"Action")
    call SetHandleHandle(DamageTrigger,TriggerAddCondition(DamageTrigger, Condition(function DamageTrigger_Conditions)),"Cond")
    // to destroy:
    call TriggerRemoveAction(GetHandleTriggerAction(DamageTrigger,"Action"))
    call TriggerRemoveCondition(GetHandleCondition(DamageTrigger,"Cond")) //not 100% sure on if this one should be removed. i guess it should.
    call DestroyTrigger(DamageTrigger)
     
     
  6. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    I tend to remove triggerconditions for safety, but I can't say I know if they leak.

    Anyways, I'm pretty sure you leak a boolexpr with Condition, never heard anything against it as you said, so I would do it to be safe.

    Meh, I guess it's something that's worth testing, though.
     
  7. Diablo-dk

    Diablo-dk

    Joined:
    Nov 10, 2004
    Messages:
    369
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
  8. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Insane. The most useful thing to me there was the GroupEnum... thing, but anyways, insane.
     
  9. HappyTauren

    HappyTauren

    Joined:
    Nov 3, 2006
    Messages:
    8,414
    Resources:
    87
    Models:
    61
    Icons:
    23
    Packs:
    1
    Tutorials:
    2
    Resources:
    87
    hm... in that case, only one person can either approve or disapprove this
    VEXORIAN
     
  10. PurplePoot

    PurplePoot

    Joined:
    Dec 14, 2005
    Messages:
    11,161
    Resources:
    3
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    Resources:
    3
    Vexorian doesn't visit here, as far as I know.
     
  11. Dr Super Good

    Dr Super Good

    Spell Reviewer

    Joined:
    Jan 18, 2005
    Messages:
    25,543
    Resources:
    3
    Maps:
    1
    Spells:
    2
    Resources:
    3
    All handles leak, and I believe that includeds boolexpr since why should it be an exception, but I believe in the common use for it it does not leak and thus it does not leak but if you make triggers constantly as part of a spell it probably will leak.

    Code (vJASS):
    native TriggerAddAction takes trigger whichTrigger,code actionFunc returns triggeraction
    native TriggerAddCondition takes trigger whichTrigger,boolexpr condition returns triggercondition
    //BLANK LINE

    This is the specification of the functions we are talking about.

    Code (vJASS):
    native TriggerRemoveAction takes trigger whichTrigger,triggeraction whichAction returns nothing
    native TriggerRemoveCondition takes trigger whichTrigger,triggercondition whichCondition returns nothing
    //BLANK LINE

    These are the functions that diablo recomended to remove the leak, notice that they require the storing of the handles of triggeraction and triggercondition when you make them to use them and thus it would require more handle opperations making it inconveinent to use and could result overall lower map preformance on slow computers. These seem too inconvinent to really try and use alot.

    Code (vJASS):
    native TriggerClearActions takes trigger whichTrigger returns nothing
    native TriggerClearConditions takes trigger whichTrigger returns nothing
    //BLANK LINE

    Now correct me if I am wrong but do these not do exactly the same as the ones diablo recommended but better since it requires nothing but the trigger and it removes all the conditions/actions from the trigger? If so you couldeasily fix it via adding these onto the trigger destruction section to help flush it and much more efficently than Diablo's idea.

    So I say this should be approved once this minor action/condition leak is fixed and who really cares if the removing of the boolexpr is not necescary but it is better to be safe than sorry (ruin others maps with leaks).

    You may also concider submiting a version of this to the JASS functions section if you have not already.

    You may also like to add how to make it single unit specfic (or include the code for that) since some people may not want it to trigger when every unit attacks especially if it is for just 1 heroes ability.

    I would like to remind members that vexorian is not part of this site and thus do not yell for him when some of our trigger moderators have no idea about something.
     
    Last edited: Jul 8, 2007
  12. Bobo_The_Kodo

    Bobo_The_Kodo

    Joined:
    Jan 15, 2007
    Messages:
    192
    Resources:
    0
    Resources:
    0
    TriggerClearActions does leak after several tests, you do need to do TriggerRemoveAction()
     
  13. gorillabull

    gorillabull

    Joined:
    Jul 17, 2011
    Messages:
    1,368
    Resources:
    2
    Spells:
    2
    Resources:
    2
    i like the approach but this is insufficient and extremely basic. example there is a damage over time on the unit.
     
  14. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,253
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    it was 7 years ago man :grin: