1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. 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
  3. 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
  4. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  5. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  6. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  7. The results are out! Check them out.
    Dismiss Notice
  8. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  9. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  10. 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.

Trigger Viewer

Blind.w3x
Variables
Blind Folder
Spell Triggers
Blind Config
Blind Cast
Blind Loop
Blind Damage Text
Requirement
Unit Indexer
DamageEvent
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.

		
Name Type Is Array Initial Value
allocatedAttacks integer No
allocCounter integer No
amount real No
ATTACK_TYPE_UNIVERSAL attacktype No
BL_BlindAbility abilcode No
BL_BlindAbilityDisableAttack abilcode No
BL_BlindAbilityVisionReducer abilcode No
BL_BlindConfuseTargetAttack boolean No
BL_BlindDebuff buffcode No
BL_BlindDebuffAbility abilcode No
BL_BlindDisablesAttack boolean No
BL_BlindDummy unit No
BL_BlindDuration real Yes
BL_BlindedUnitDuration real Yes
BL_BlindGroup group No
BL_BlindIncreaseMagicalDamage boolean No
BL_BlindIncreasePhysicalDamage boolean No
BL_BlindIncreaseTriggerDamage boolean No
BL_BlindLoopSpeed real No
BL_CastingUnit unit No
BL_ConfusionRange real No
BL_Counter integer No
BL_DummyType unitcode No
BL_LastFloatingText texttag No
BL_MagicalRate real No
BL_PhysicalRate real No
BL_RandomTarget unit No
BL_RandomTargetGroup group No
BL_ShowNewDamageRateText boolean No
BL_TargetUnit unit No
BL_TempGroup group No
BL_TempPoint location No
BL_TriggeredRate real No
BRACERS_SPELL_DAMAGE_REDUCTION real No
CODE integer No
DAMAGE_TYPE_DETECTOR integer No
damageEvent trigger No
damageEventTrigger real No
damageHandler trigger No
damageType integer No
ETHEREAL_DAMAGE_FACTOR real No
h hashtable No
HeroLevel integer No
PHYSICAL integer No
pureAmount real No
runAllocatedAttacks trigger No
SET_MAX_LIFE integer No
source unit No
SPELL integer No
SPELL_DAMAGE_REDUCTION_ITEM integer No
SPELL_RESISTANCE_AUTO_DETECT boolean No
target unit No
TargetValue integer No
totalAllocs integer No
TRIGGER_CLEANUP_PERIOD real No
UDex integer No
UDexGen integer No
UDexNext integer Yes
UDexPrev integer Yes
UDexRecycle integer No
UDexUnits unit Yes
UDexWasted integer No
UNIT_MIN_LIFE real No
UnitIndexerEnabled boolean No
UnitIndexEvent real No
UnitIndexLock integer Yes
Credits :
Bribe [UnitIndexer]
looking_for_help [DamageEvent]
1. Import the Abilities, Buff and the Dummy
2. Make sure the Blind Debuff Ability have Blind Buff
3. Copy the Triggers with Auto Variable Creation Ticked
4. Setup the Config Trigger
5. Done!
Blind Config
  Events
    Map initialization
  Conditions
  Actions
    -------- Important Stuff! Setup them correctly! --------
    Set BL_BlindAbility = Blind
    Set BL_BlindAbilityVisionReducer = Blind Vision
    Set BL_BlindAbilityDisableAttack = Blind Disable
    Set BL_BlindDebuffAbility = Blind Debuff
    Set BL_BlindDebuff = Blind
    Set BL_DummyType = Dummy
    -------- Customizable Effects --------
    -------- Blinded unit can't attack (disables Confuse) --------
    Set BL_BlindDisablesAttack = True
    -------- Blinded unit takes more X damage from Y attack type --------
    Set BL_BlindIncreaseMagicalDamage = True
    Set BL_BlindIncreasePhysicalDamage = True
    Set BL_BlindIncreaseTriggerDamage = True
    -------- Blinded unit may attack allies or enemies (completely random) --------
    Set BL_BlindConfuseTargetAttack = False
    -------- Confusion Range (ConfuseTargetAttack must be true) --------
    Set BL_ConfusionRange = 600.00
    -------- New Damage Rate --------
    Set BL_MagicalRate = 1.25
    Set BL_PhysicalRate = 1.75
    Set BL_TriggeredRate = 1.25
    Set BL_ShowNewDamageRateText = True
    -------- Duration of Blind --------
    Set BL_BlindDuration[1] = 1.00
    Set BL_BlindDuration[2] = 2.00
    Set BL_BlindDuration[3] = 3.00
    Set BL_BlindDuration[4] = 4.00
    -------- END CONFIG --------
    Custom script: set udg_BL_BlindLoopSpeed = 0.03125
    Trigger - Add to Blind_Loop <gen> the event (Every BL_BlindLoopSpeed seconds of game time)
    -------- Securities (Avoid unwated behaviors) --------
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_BlindIncreasePhysicalDamage Equal to False
        BL_BlindIncreaseMagicalDamage Equal to False
        BL_BlindIncreaseTriggerDamage Equal to False
      Then - Actions
        Set BL_ShowNewDamageRateText = False
        Trigger - Turn off Blind_Damage_Text <gen>
      Else - Actions
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_BlindDisablesAttack Equal to True
      Then - Actions
        Set BL_BlindConfuseTargetAttack = False
      Else - Actions
Blind Cast
  Events
    Unit - A unit Starts the effect of an ability
  Conditions
    (Ability being cast) Equal to BL_BlindAbility
  Actions
    Set BL_CastingUnit = (Triggering unit)
    Set BL_TargetUnit = (Target unit of ability being cast)
    Set TargetValue = (Custom value of BL_TargetUnit)
    Set HeroLevel = (Level of BL_BlindAbility for BL_CastingUnit)
    Set BL_BlindedUnitDuration[TargetValue] = BL_BlindDuration[HeroLevel]
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        (BL_TargetUnit has buff BL_BlindDebuff) Equal to True
      Then - Actions
        Skip remaining actions
      Else - Actions
    Unit - Add BL_BlindAbilityVisionReducer to BL_TargetUnit
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_BlindDisablesAttack Equal to True
      Then - Actions
        Unit - Add BL_BlindAbilityDisableAttack to BL_TargetUnit
      Else - Actions
    Set BL_TempPoint = (Position of BL_CastingUnit)
    Unit - Create 1 BL_DummyType for (Owner of BL_CastingUnit) at BL_TempPoint facing Default building facing degrees
    Custom script: call RemoveLocation(udg_BL_TempPoint)
    Set BL_BlindDummy = (Last created unit)
    Unit - Add BL_BlindDebuffAbility to BL_BlindDummy
    Unit - Order BL_BlindDummy to Undead Banshee - Curse BL_TargetUnit
    Unit - Add a 1.00 second Generic expiration timer to BL_BlindDummy
    Unit Group - Add BL_TargetUnit to BL_BlindGroup
    Set BL_Counter = (BL_Counter + 1)
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_Counter Equal to 1
      Then - Actions
        Trigger - Turn on Blind_Loop <gen>
      Else - Actions
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_BlindConfuseTargetAttack Equal to True
      Then - Actions
        Set BL_TempPoint = (Position of BL_TargetUnit)
        Set BL_RandomTargetGroup = (Units within BL_ConfusionRange of BL_TempPoint)
        Set BL_RandomTarget = (Random unit from BL_RandomTargetGroup)
        Unit - Order BL_TargetUnit to Attack BL_RandomTarget
        Custom script: call RemoveLocation(udg_BL_TempPoint)
        Custom script: call DestroyGroup(udg_BL_RandomTargetGroup)
      Else - Actions
Blind Loop
  Events
  Conditions
  Actions
    Unit Group - Pick every unit in BL_BlindGroup and do (Actions)
      Loop - Actions
        Set BL_TargetUnit = (Picked unit)
        Set TargetValue = (Custom value of BL_TargetUnit)
        Set BL_BlindedUnitDuration[TargetValue] = (BL_BlindedUnitDuration[TargetValue] - BL_BlindLoopSpeed)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            BL_BlindedUnitDuration[TargetValue] Less than or equal to 0.00
          Then - Actions
            Set BL_TargetUnit = (Picked unit)
            Unit Group - Remove BL_TargetUnit from BL_BlindGroup
            Set BL_Counter = (BL_Counter - 1)
            Unit - Remove BL_BlindAbilityVisionReducer from BL_TargetUnit
            Unit - Remove BL_BlindDebuff buff from BL_TargetUnit
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              If - Conditions
                BL_BlindDisablesAttack Equal to True
              Then - Actions
                Unit - Remove BL_BlindAbilityDisableAttack from BL_TargetUnit
              Else - Actions
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              If - Conditions
                BL_BlindConfuseTargetAttack Equal to True
              Then - Actions
                Unit - Order BL_TargetUnit to Stop
              Else - Actions
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              If - Conditions
                BL_Counter Equal to 0
              Then - Actions
                Trigger - Turn off Blind_Loop <gen>
              Else - Actions
          Else - Actions
Blind Damage Text
  Events
    Game - damageEventTrigger becomes Equal to 1.00
  Conditions
    ((Triggering unit) has buff BL_BlindDebuff) Equal to True
  Actions
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        damageType Equal to PHYSICAL
        BL_BlindIncreasePhysicalDamage Equal to True
      Then - Actions
        Set amount = (amount x BL_PhysicalRate)
      Else - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            damageType Equal to SPELL
            BL_BlindIncreaseMagicalDamage Equal to True
          Then - Actions
            Set amount = (amount x BL_MagicalRate)
          Else - Actions
            If (All Conditions are True) then do (Then Actions) else do (Else Actions)
              If - Conditions
                damageType Equal to CODE
                BL_BlindIncreaseTriggerDamage Equal to True
              Then - Actions
                Set amount = (amount x BL_TriggeredRate)
              Else - Actions
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        BL_ShowNewDamageRateText Equal to True
      Then - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            damageType Equal to PHYSICAL
          Then - Actions
            Floating Text - Create floating text that reads (String((Integer(amount)))) above target with Z offset 0, using font size 10, color (100%, 0.00%, 0.00%), and 0% transparency
          Else - Actions
            Floating Text - Create floating text that reads (String((Integer(amount)))) above target with Z offset 0, using font size 10, color (0.00%, 0.00%, 100.00%), and 0% transparency
        Set BL_LastFloatingText = (Last created floating text)
        Floating Text - Set the velocity of BL_LastFloatingText to 64 towards 90 degrees
        Floating Text - Change BL_LastFloatingText: Disable permanence
        Floating Text - Change the fading age of BL_LastFloatingText to 4.00 seconds
        Floating Text - Change the lifespan of BL_LastFloatingText to 6.00 seconds
      Else - Actions
Unit Indexer gives you an array-safe (1-8190) custom value for units, eliminating the need for hashtables to store unit-specific data.
Just use "Set MyArrayData[(Custom value of Unit)] = (Some data)".
--------
If you want to get the unit assigned to an index (reverse lookup) use "UDexUnits[(Index)]".
--------
If you want to detect when an index is created or when it is released, use "UnitIndexEvent Equal to 1.00" (created) or "UnitIndexEvent Equal to 2.00" (released). The index being created/released is called "UDex".
--------
You can enable/disable Unit Indexer to protect some of your undesirable units from being indexed like this:

Trigger - Turn off Unit Indexer <gen>
Unit - Create 1 Dummy for (Triggering player) at TempPoint facing 0.00 degrees
Trigger - Turn on Unit Indexer <gen>
--------
If you want to use a Map Initialization trigger that uses custom value of units, to make sure that UnitIndexer initializes first, use the event "UnitIndexEvent Equal to 3.00". Otherwise the custom value of units may be zero.
--------
Advanced:
--------
If you want to lock the index of a unit, use "Set UnitIndexLock[(Index)] = (UnitIndexLock[(Index)] + 1)". This will prevent the index from being recycled. If you want to unlock it and allow it to be recycled, run the Unit Indexer <gen> trigger.

Note: Make sure if you add a lock that you will eventually remove the lock, otherwise the index will never be recycled.
Unit Indexer
  Events
    Map initialization
  Conditions
  Actions
    Custom script: call ExecuteFunc("InitializeUnitIndexer")
    Custom script: endfunction
    Custom script:
    Custom script: function ClearUnitIndex takes nothing returns nothing
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        (Custom value of UDexUnits[UDex]) Equal to 0
      Then - Actions
        Set UnitIndexLock[UDex] = (UnitIndexLock[UDex] - 1)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            UnitIndexLock[UDex] Equal to 0
          Then - Actions
            Set UDexNext[UDexPrev[UDex]] = UDexNext[UDex]
            Set UDexPrev[UDexNext[UDex]] = UDexPrev[UDex]
            Set UDexPrev[UDex] = 0
            Set UnitIndexEvent = 0.00
            Set UnitIndexEvent = 2.00
            Set UnitIndexEvent = 0.00
            Set UDexUnits[UDex] = No unit
            Set UDexNext[UDex] = UDexRecycle
            Set UDexRecycle = UDex
          Else - Actions
      Else - Actions
    Custom script: endfunction
    Custom script:
    Custom script: function IndexUnit takes nothing returns boolean
    Custom script: local integer pdex = udg_UDex
    Custom script: local integer ndex
    -------- - --------
    -------- You can customize the following block - if conditions are false the (Matching unit) won't be indexed. --------
    -------- - --------
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        UnitIndexerEnabled Equal to True
        (Custom value of (Matching unit)) Equal to 0
      Then - Actions
        Set UDexWasted = (UDexWasted + 1)
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            UDexWasted Equal to 15
          Then - Actions
            Set UDexWasted = 0
            Set UDex = UDexNext[0]
            Custom script: loop
            Custom script: exitwhen udg_UDex == 0
            Custom script: set ndex = udg_UDexNext[udg_UDex]
            Custom script: call ClearUnitIndex()
            Custom script: set udg_UDex = ndex
            Custom script: endloop
          Else - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            UDexRecycle Equal to 0
          Then - Actions
            Set UDex = (UDexGen + 1)
            Set UDexGen = UDex
          Else - Actions
            Set UDex = UDexRecycle
            Set UDexRecycle = UDexNext[UDex]
        Set UDexUnits[UDex] = (Matching unit)
        Unit - Set the custom value of UDexUnits[UDex] to UDex
        Set UDexPrev[UDexNext[0]] = UDex
        Set UDexNext[UDex] = UDexNext[0]
        Set UDexNext[0] = UDex
        Set UnitIndexLock[UDex] = 1
        Set UnitIndexEvent = 0.00
        Set UnitIndexEvent = 1.00
        Set UnitIndexEvent = 0.00
        Custom script: set udg_UDex = pdex
      Else - Actions
    Custom script: return false
    Custom script: endfunction
    Custom script:
    Custom script: function InitializeUnitIndexer takes nothing returns nothing
    Custom script: local integer i = 16
    Custom script: local boolexpr b = Filter(function IndexUnit)
    Custom script: local region re = CreateRegion()
    Custom script: local trigger t = GetTriggeringTrigger()
    Custom script: local rect r = GetWorldBounds()
    Custom script: call RegionAddRect(re, r)
    Custom script: call TriggerRegisterEnterRegion(t, re, b)
    Custom script: call TriggerClearActions(t)
    Custom script: call TriggerAddAction(t, function ClearUnitIndex)
    Set UnitIndexerEnabled = True
    Custom script: loop
    Custom script: set i = i - 1
    Custom script: call GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), b)
    Custom script: exitwhen i == 0
    Custom script: endloop
    Custom script: call RemoveRect(r)
    Custom script: set re = null
    Custom script: set r = null
    Custom script: set t = null
    Custom script: set b = null
    Set UnitIndexEvent = 3.00
    Set UnitIndexEvent = 0.00
//TESH.scrollpos=119
//TESH.alwaysfold=0
////////////////////////////////////////////////////////////////////////////////////
//
//  Physical Damage Detection Engine GUI v 1.0.0.2
//  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//  By looking_for_help aka eey
//
//  This system is able to detect, modify and deal damage. It can differentiate
//  between physical and spell damage, as well as block, reduce or increase the
//  applied damage to a desired value. You can also allocate damage events from
//  running damage events.
//
//  This is the GUI version of the system, meaning that you can use this with the
//  standard editor and basically without any knowledge of JASS.
//
////////////////////////////////////////////////////////////////////////////////////
//
//  Implementation
//  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//  1.  Create all variables that this system uses (compare the variable editor).
//      You don't have to set them to specific values, they get initialized
//      automatically by the system so just create them.
//  2.  Copy this trigger to your map. You can then add damage handlers by using
//      the event "value of real variable becomes equal to 1" with the variable
//      damageEventTrigger. Compare the OnDamage trigger for an example usage.
//  3.  Copy the two custom abilities to your map and make sure they have the
//      correct ID. You can specify the ID in the function InitGlobalVariables
//      above.
//  4.  Go to the locust swarm ability and invert its damage return portion
//      from (default) 0.75 to -0.75.
//  5.  Remove the spell damage reduction ability from the spell damage reduction
//      items you use (runed bracers). You can configure the resistance of this
//      item in the InitGlobalVariables function, modifying the golbal variable
//      BRACERS_SPELL_DAMAGE_REDUCTION.
//
////////////////////////////////////////////////////////////////////////////////////
//
//  Important Notes
//  ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//  1.  Life Drain does not work with this system, so you should use a triggered
//      version of this spell if you want to use it.
//  2.  Same for Finger of Death, if you want to use this spell bug free with this
//      system, you should use a triggered version of it.
//  3.  If you use damage modifiers by setting the damage amount variable, you have
//      to use GetUnitLife, GetUnitMaxLife and SetUnitLife instead of GetWidgetLife,
//      GetUnitState for UNIT_STATE_MAX_LIFE and SetWidgetLife in your whole map to
//      ensure there occure no bugs.
//  4.  The boolean USE_SPELL_RESISTANCE_AUTO_DETECT is only neccessary set to true
//      if you want to use a customized damage table with spell damage resistance
//      above 100%. If this is not the case, it should be set to false, as the
//      system is faster then and still works correct.
//  5.  As already mentioned you can't use the spell reduction ability when using this
//      system (runed bracers and elunes grace). If you want to use them, you can
//      trigger them by using the damage modifiers. Runed bracers is already considered
//      in this system, so you don't have to code it.
//
////////////////////////////////////////////////////////////////////////////////////
//  
//  System API
//  ¯¯¯¯¯¯¯¯¯¯
//  real damageEventTrigger
//      - Use the event "value of real variable becomes equal to 1" to detect a damage
//        event. In this event you can use the following API. Compare the OnDamage
//        trigger for an example usage.
//
//  unit target
//      - In this global unit variable, the damaged unit is saved. Don't write any-
//        thing into this variable, use it as readonly.
//
//  unit source
//      - In this global unit variable, the damage source is saved. Don't write any-
//        thing into this variable, use it as readonly.
//
//  real amount
//      - In this global real variable, the amount of damage is saved. This amount
//        can be modified by simply setting it to the desired amount that should be
//        applied to the target. Set the amount to 0.0 to block the damage completly.
//        Negative values will result in heal.
//
//  integer damageType
//      - In this global integer variable, the damage type of the current damage is
//        saved. Use it to differentiate between physical, spell and code damage. The
//        variable can takes the values PHYSICAL == 0, SPELL == 1 and CODE == 2. Don't
//        write anything into this variable, use it as readonly.        
//
//  function GetUnitLife takes unit u returns real
//      - Use this function instead of the GetWidgetLife native. It ensures that you
//        get the correct health value, even when damage modifiers are applied. If
//        you don't use damage modifiers at all, you don't need this function.
//
//  function GetUnitMaxLife takes unit u returns real
//      - Use this function instead of the GetUnitState(u, UNIT_STATE_MAX_LIFE) native.
//        It will return the correct value, even when damage modifiers are applied. If
//        you don't use damage modifiers at all, you don't need this function.
//
//  function SetUnitLife takes unit u, real newLife returns nothing
//      - Use this function instead of the SetWidgetLife(u, newLife) native if you use
//        damage modifiers in your map. Same rules as for the GetUnitMaxLife and the
//        GetUnitMaxLife functions.
//
//  function AddDamageHandler takes code damageHandler returns nothing
//      - Allows you to add a damage handler function to the system. This is not
//        required for GUI users, only for vanilla JASS users. GUI users should
//        use the real variable damageEventTrigger to add damage handlers to this
//        system as explained above.
//
//  function RemoveDamageHandler takes code damageHandler returns nothing
//      - Allows you to remove a damage handler function from the system dynamic-
//        ally. If you added the same handler function multiple times to the sys-
//        tem, this function will remove all of these equal functions. This is not
//        required for GUI users, only for vanilla JASS users.
//
//////////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////////
//  Configurable globals
//////////////////////////////////////////////////////////////////////////////////

function InitGlobalVariables takes nothing returns nothing
    // Put here the correct ability IDs
    set udg_DAMAGE_TYPE_DETECTOR = 'DTD1'
    set udg_SET_MAX_LIFE = 'SMXL'
   
    // Here you can configure some stuff, read the documentation for further info
    set udg_SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
    set udg_SPELL_RESISTANCE_AUTO_DETECT = false
    set udg_ETHEREAL_DAMAGE_FACTOR = 1.66
    set udg_BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
    set udg_TRIGGER_CLEANUP_PERIOD = 60.0
endfunction

//////////////////////////////////////////////////////////////////////////////////
//  End of configurable globals
//////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////
//  User functions
//////////////////////////////////////////////////////////////////////////////////

function AddDamageHandler takes code func returns nothing
    local integer id = GetHandleId(Condition(func))
    local integer index = 0
   
    // Loop to manage equal damage handlers
    loop
        exitwhen ( LoadTriggerConditionHandle(udg_h, id, index) == null )
        set index = index + 1
    endloop
   
    // Store the desired damage handler function
    call SaveTriggerConditionHandle(udg_h, id, index, TriggerAddCondition(udg_damageHandler, Filter(func)))
endfunction

function RemoveDamageHandler takes code func returns nothing
    local integer id = GetHandleId(Condition(func))
    local integer index = 0
   
    // Loop through all equal damage handlers
    loop
        exitwhen ( LoadTriggerConditionHandle(udg_h, id, index) == null )
        call TriggerRemoveCondition(udg_damageHandler, LoadTriggerConditionHandle(udg_h, id, index))
        set index = index + 1
    endloop
   
    // Clean things up
    call FlushChildHashtable(udg_h, id)
endfunction

function GetUnitLife takes unit u returns real
    local boolean duringModification
    local integer uId = GetHandleId(u)
    local real health
    local real storedHealth = LoadReal(udg_h, uId, 2)
    local real storedDamage = LoadReal(udg_h, uId, 1)
   
    // Check if the unit is being rescued from damage
    set duringModification = GetUnitAbilityLevel(u, udg_SET_MAX_LIFE) > 0
    if duringModification then
        call UnitRemoveAbility(u, udg_SET_MAX_LIFE)
    endif

    // Get the correct health value of the unit
    if storedHealth != 0.0 then
        set health = storedHealth - storedDamage
    else
        set health = GetWidgetLife(u) - storedDamage
    endif

    // Restore the rescue ability and return
    if duringModification then
        call UnitAddAbility(u, udg_SET_MAX_LIFE)
    endif
    return health
endfunction

function GetUnitMaxLife takes unit u returns real
    local real maxHealth
   
    // Check if the unit is being rescued from damage
    if GetUnitAbilityLevel(u, udg_SET_MAX_LIFE) > 0 then
        call UnitRemoveAbility(u, udg_SET_MAX_LIFE)
        set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
        call UnitAddAbility(u, udg_SET_MAX_LIFE)
        return maxHealth
    endif
   
    // If the unit isn't being rescued, use the standard native
    return GetUnitState(u, UNIT_STATE_MAX_LIFE)
endfunction

function SetUnitLife takes unit u, real newLife returns nothing
    local integer targetId
    local integer oldTimerId
    local real oldHealth

    // Check if the unit is being rescued from damage
    if GetUnitAbilityLevel(u, udg_SET_MAX_LIFE) > 0 then
        call UnitRemoveAbility(u, udg_SET_MAX_LIFE)
        call SetWidgetLife(u, newLife)
        call UnitAddAbility(u, udg_SET_MAX_LIFE)
       
        // Get the unit specific timer information
        set targetId = GetHandleId(u)
        set oldHealth = LoadReal(udg_h, targetId, 0)
       
        // Update the unit specific timer information
        if oldHealth != 0.0 then
            set oldTimerId = LoadInteger(udg_h, targetId, 3)
            call SaveReal(udg_h, targetId, 2, newLife)
            call SaveReal(udg_h, targetId, 0, newLife)
            call SaveReal(udg_h, oldTimerId, 4, newLife)
        endif
        return
    endif
   
    // If the unit isn't being rescued, use the standard native
    call SetWidgetLife(u, newLife)
endfunction

function UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, boolean ranged, attacktype localAttackType, damagetype localDamageType, weapontype localWeaponType returns boolean
    // Avoid infinite loop due to recursion
    if udg_damageType == udg_CODE then
        return false
    endif
   
    // Avoid allocating attacks on units that are about to be killed
    if ( localTarget == udg_target and GetUnitLife(localTarget) - udg_amount < udg_UNIT_MIN_LIFE ) then
        return false
    endif
   
    // Store all damage parameters determined by the user
    set udg_allocatedAttacks = udg_allocatedAttacks + 1
    call SaveUnitHandle(udg_h, udg_allocatedAttacks, 0, localSource)
    call SaveUnitHandle(udg_h, udg_allocatedAttacks, 1, localTarget)
    call SaveReal(udg_h, udg_allocatedAttacks, 2, localAmount)
    call SaveBoolean(udg_h, udg_allocatedAttacks, 3, attack)
    call SaveBoolean(udg_h, udg_allocatedAttacks, 4, ranged)
    call SaveInteger(udg_h, udg_allocatedAttacks, 5, GetHandleId(localAttackType))
    call SaveInteger(udg_h, udg_allocatedAttacks, 6, GetHandleId(localDamageType))
    call SaveInteger(udg_h, udg_allocatedAttacks, 7, GetHandleId(localWeaponType))

    // Return true if the damage was allocated
    return true
endfunction

////////////////////////////////////////////////////////////////////////////////////
//  Sub functions
////////////////////////////////////////////////////////////////////////////////////

function DealFixDamage takes unit source, unit target, real pureAmount returns nothing
    local real MAX_DAMAGE = 1000000.0
    local real beforeHitpoints
    local real newHitpoints

    // Ensure the amount is positive
    if pureAmount < 0 then
        set pureAmount = -pureAmount
    endif

    // Save the targets hitpoints
    set beforeHitpoints = GetWidgetLife(target)
    set newHitpoints = beforeHitpoints - pureAmount

    // Apply the desired, fixed amount
    if newHitpoints >= udg_UNIT_MIN_LIFE then
        call SetUnitState(target, UNIT_STATE_LIFE, newHitpoints)
    else
        if ( IsUnitType(target, UNIT_TYPE_ETHEREAL) == false ) then
            call SetWidgetLife(target, 1.0)
            call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, udg_ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
        else
            call UnitRemoveAbility(target, udg_DAMAGE_TYPE_DETECTOR)
            call SetWidgetLife(target, 1.0)
            call UnitDamageTarget(source, target, MAX_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
        endif
    endif
endfunction

function GetUnitSpellResistance takes unit u returns real
    local real originalHP
    local real beforeHP
    local real afterHP
    local real resistance
    local real DUMMY_DAMAGE = 100
    local real DUMMY_FACTOR = 0.01
   
    // Deal spell damage in order to get the units resistance
    call UnitRemoveAbility(udg_target, udg_DAMAGE_TYPE_DETECTOR)
    set originalHP = GetWidgetLife(udg_target)
    call UnitAddAbility(udg_target, udg_SET_MAX_LIFE)
    call SetWidgetLife(udg_target, 20000.0)
    set beforeHP = GetWidgetLife(udg_target)
    call DisableTrigger(udg_damageEvent)
    call UnitDamageTarget(udg_source, udg_target, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
    call EnableTrigger(udg_damageEvent)
    set afterHP = GetWidgetLife(udg_target)
    call UnitRemoveAbility(udg_target, udg_SET_MAX_LIFE)
    call SetWidgetLife(udg_target, originalHP)
    call UnitAddAbility(udg_target, udg_DAMAGE_TYPE_DETECTOR)
    call UnitMakeAbilityPermanent(udg_target, true, udg_DAMAGE_TYPE_DETECTOR)

    // Calculate this resistance
    set resistance = DUMMY_FACTOR*(beforeHP - afterHP)

    // If the resistance was greater than 100%, return it
    if resistance > 1.0 then
        return resistance
    else
        return 1.0
    endif
endfunction

function UnitHasItemOfType takes unit u, integer itemId returns integer
    local integer index = 0
    local item indexItem

    // Check if the target has a spell damage reducing item
    loop
        set indexItem = UnitItemInSlot(u, index)
        if ( indexItem != null ) and ( GetItemTypeId(indexItem) == itemId ) then
            return index + 1
        endif

        set index = index + 1
        exitwhen index >= bj_MAX_INVENTORY
    endloop
    return 0
endfunction


////////////////////////////////////////////////////////////////////////////////////
//  Damage Engine
////////////////////////////////////////////////////////////////////////////////////

function AfterDamage takes nothing returns nothing
    local timer time = GetExpiredTimer()
    local integer id = GetHandleId(time)
    local unit localSource = LoadUnitHandle(udg_h, id, 0)
    local unit localTarget = LoadUnitHandle(udg_h, id, 1)
    local real pureAmount = LoadReal(udg_h, id, 2)
    local real amount = LoadReal(udg_h, id, 3)
    local real originalHealth = LoadReal(udg_h, id, 4)

    // If the damage was modified, restore units health
    if originalHealth != 0.0 then
        call UnitRemoveAbility(localTarget, udg_SET_MAX_LIFE)
        call SetWidgetLife(localTarget, originalHealth)
    endif

    // Apply the desired amount of damage
    if amount > 0.0 then
        call DisableTrigger(udg_damageEvent)
        call DealFixDamage(localSource, localTarget, amount)
        call EnableTrigger(udg_damageEvent)
    else
        call SetWidgetLife(localTarget, originalHealth - amount)
    endif
   
    // Clean things up
    call FlushChildHashtable(udg_h, id)
    call FlushChildHashtable(udg_h, GetHandleId(localTarget))
    call DestroyTimer(time)
    set time = null
    set localSource = null
    set localTarget = null
endfunction

function DamageEngine takes nothing returns nothing
    local timer time
    local integer id
    local real health
    local real rawAmount
    local real originalHealth
    local integer targetId
    local integer oldTimerId
    local real oldHealth
    local real oldOriginalHealth
    local real oldAmount

    // Set damage variables  
    set udg_source = GetEventDamageSource()
    set udg_target = GetTriggerUnit()
    set rawAmount = GetEventDamage()
   
    // Determine the damage type
    if udg_damageType == udg_CODE and rawAmount != 0.0 then
        set udg_damageType = udg_CODE
    elseif rawAmount > 0.0 then
        set udg_damageType = udg_PHYSICAL
    elseif rawAmount < 0.0 then
        set udg_damageType = udg_SPELL
    else
        return
    endif
   
    // Correct the damage amount
    if rawAmount < 0.0 then
        set udg_amount = -rawAmount
    else
        set udg_amount = rawAmount
    endif
   
    // Register spell reduction above 100%
    if udg_SPELL_RESISTANCE_AUTO_DETECT then
        set udg_amount = GetUnitSpellResistance(udg_target)*udg_amount
    else
        if ( IsUnitType(udg_target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(udg_target, GetOwningPlayer(udg_source)) and rawAmount < 0.0 ) then
            set udg_amount = udg_ETHEREAL_DAMAGE_FACTOR*udg_amount
        endif
    endif
   
    // Register spell damage reducing items like runed bracers
    if ( IsUnitType(udg_target, UNIT_TYPE_HERO) and UnitHasItemOfType(udg_target, udg_SPELL_DAMAGE_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
        set udg_amount = (1 - udg_BRACERS_SPELL_DAMAGE_REDUCTION)*udg_amount
    endif
   
    // Save health and damage variables
    if udg_damageType != udg_CODE then
        call UnitRemoveAbility(udg_target, udg_SET_MAX_LIFE)
    endif
    set udg_pureAmount = udg_amount
    set originalHealth = GetWidgetLife(udg_target)
    set oldTimerId = 0
   
    // Call damage handlers
    set udg_damageEventTrigger = 1.0
    set udg_damageEventTrigger = 0.0

    // If the damage was modified, apply the rescue ability
    if udg_amount != udg_pureAmount then
        call UnitAddAbility(udg_target, udg_SET_MAX_LIFE)
        call SetWidgetLife(udg_target, GetWidgetLife(udg_target) + udg_pureAmount)
    endif
   
    // Check if a previous timer didn't yet expire
    set targetId = GetHandleId(udg_target)
    set oldHealth = LoadReal(udg_h, targetId, 0)
   
    // If this is the case, update the timer information
    if oldHealth != 0.0 then
        set oldTimerId = LoadInteger(udg_h, targetId, 3)
        set oldOriginalHealth = LoadReal(udg_h, targetId, 2)
        set oldAmount = LoadReal(udg_h, targetId, 1)
        set originalHealth = oldOriginalHealth - oldAmount
        call SaveReal(udg_h, oldTimerId, 4, oldOriginalHealth)
    endif

    // Call after damage event if damage was spell, modified, code or parallel
    if ( rawAmount < 0.0 or udg_pureAmount != udg_amount or oldTimerId != 0 or udg_allocatedAttacks > 0 ) then
        set time = CreateTimer()
        set id = GetHandleId(time)
       
        // Save handles for after damage event
        call SaveUnitHandle(udg_h, id, 0, udg_source)
        call SaveUnitHandle(udg_h, id, 1, udg_target)
        call SaveReal(udg_h, id, 2, udg_pureAmount)
        call SaveReal(udg_h, id, 3, udg_amount)
        call SaveReal(udg_h, id, 4, originalHealth)

        // Save this extra to manage parallel damage instances
        call SaveReal(udg_h, targetId, 0, GetWidgetLife(udg_target))
        call SaveReal(udg_h, targetId, 1, udg_amount)
        call SaveReal(udg_h, targetId, 2, originalHealth)
        call SaveInteger(udg_h, targetId, 3, id)

        // Avoid healing of negative spell damage
        if rawAmount < 0.0 then
            call DisableTrigger(udg_damageEvent)
            call DealFixDamage(udg_source, udg_target, -rawAmount)
            if ( originalHealth - udg_amount < udg_UNIT_MIN_LIFE ) then
                call UnitRemoveAbility(udg_target, udg_SET_MAX_LIFE)
                call DealFixDamage(udg_source, udg_target, udg_amount)
            endif
            call EnableTrigger(udg_damageEvent)
        endif
       
        // Guarantee unit exploding
        if originalHealth - udg_amount < udg_UNIT_MIN_LIFE then
            if rawAmount > 0.0 then
                call UnitRemoveAbility(udg_target, udg_SET_MAX_LIFE)
                call SetWidgetLife(udg_target, udg_UNIT_MIN_LIFE)
            endif
        endif
       
        // Start the after damage event
        call TimerStart(time, 0.0, false, function AfterDamage)
    endif

    // Handle allocated attacks from UnitDamageTargetEx
    if udg_totalAllocs == 0 then
        set udg_totalAllocs = udg_allocatedAttacks
    endif
    if udg_allocatedAttacks > 0 then
        set udg_allocatedAttacks = udg_allocatedAttacks - 1
        set udg_allocCounter = udg_allocCounter + 1
        call TriggerEvaluate(udg_runAllocatedAttacks)
    endif

    // Reset all required variables
    set udg_damageType = -1
    set udg_totalAllocs = 0
    set udg_allocCounter = -1
endfunction


////////////////////////////////////////////////////////////////////////////////////
//  Initialization
////////////////////////////////////////////////////////////////////////////////////

function RestoreTriggers takes nothing returns nothing
    local unit enumUnit = GetEnumUnit()

    // Re-register units that are alive
    if GetWidgetLife(enumUnit) >= udg_UNIT_MIN_LIFE then
        call TriggerRegisterUnitEvent(udg_damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
    endif
    set enumUnit = null
endfunction

function ClearMemory_Actions takes nothing returns nothing
    local group g = CreateGroup()
    local code c = function DamageEngine
   
    // Reset the damage event
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
    call ResetTrigger(udg_damageEvent)
    call DestroyTrigger(udg_damageEvent)
    set udg_damageEvent = null
   
    // Rebuild it then
    set udg_damageEvent = CreateTrigger()
    call TriggerAddCondition(udg_damageEvent, Filter(c))
    call ForGroup(g, function RestoreTriggers)
   
    // Clean things up
    call DestroyGroup(g)
    set g = null
    set c = null
endfunction

function MapInit takes nothing returns nothing
    local unit enumUnit = GetEnumUnit()

    // Register units on map initialization
    call UnitAddAbility(enumUnit, udg_DAMAGE_TYPE_DETECTOR)
    call UnitMakeAbilityPermanent(enumUnit, true, udg_DAMAGE_TYPE_DETECTOR)
    call TriggerRegisterUnitEvent(udg_damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
    set enumUnit = null
endfunction

function UnitEntersMap takes nothing returns nothing
    local unit triggerUnit = GetTriggerUnit()

    // Register units that enter the map
    if ( GetUnitAbilityLevel(triggerUnit, udg_DAMAGE_TYPE_DETECTOR) < 1 ) then
        call UnitAddAbility(triggerUnit, udg_DAMAGE_TYPE_DETECTOR)
        call UnitMakeAbilityPermanent(triggerUnit, true, udg_DAMAGE_TYPE_DETECTOR)
        call TriggerRegisterUnitEvent(udg_damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
    endif
    set triggerUnit = null
endfunction

function RunAllocatedAttacks takes nothing returns nothing
    local integer localAllocAttacks = udg_allocatedAttacks + 1

    // Calculate the correct sequence of allocated attacks
    set localAllocAttacks = localAllocAttacks - udg_totalAllocs + 1 + 2*udg_allocCounter
   
    // Deal code damage if the unit isn't exploding
    set udg_damageType = udg_CODE
    if GetUnitLife(LoadUnitHandle(udg_h, localAllocAttacks, 1)) >= udg_UNIT_MIN_LIFE then
        call UnitDamageTarget(LoadUnitHandle(udg_h, localAllocAttacks, 0), LoadUnitHandle(udg_h, localAllocAttacks, 1), LoadReal(udg_h, localAllocAttacks, 2), LoadBoolean(udg_h, localAllocAttacks, 3), LoadBoolean(udg_h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(udg_h, localAllocAttacks, 5)), ConvertDamageType(LoadInteger(udg_h, localAllocAttacks, 6)), ConvertWeaponType(LoadInteger(udg_h, localAllocAttacks, 7)))
    else
        call FlushChildHashtable(udg_h, localAllocAttacks - 1)
    endif
   
    // Clean things up
    call FlushChildHashtable(udg_h, localAllocAttacks)
endfunction

function InitTrig_DamageEvent takes nothing returns nothing
    local group g = CreateGroup()
    local region r = CreateRegion()
    local trigger UnitEnters = CreateTrigger()
    local trigger ClearMemory = CreateTrigger()
    local code cDamageEngine = function DamageEngine
    local code cUnitEnters = function UnitEntersMap
    local code cClearMemory = function ClearMemory_Actions
    local code cRunAllocatedAttacks = function RunAllocatedAttacks
   
    // Initialize global variables
    set udg_h = InitHashtable()
    set udg_damageEvent = CreateTrigger()
    set udg_damageHandler = CreateTrigger()
    set udg_damageType = -1
    set udg_allocatedAttacks = 0
    set udg_runAllocatedAttacks = CreateTrigger()
   
    // Initialize global configurable constants
    call InitGlobalVariables()
   
    // Initialize global fixed constants
    set udg_PHYSICAL = 0
    set udg_SPELL = 1
    set udg_CODE = 2
    set udg_UNIT_MIN_LIFE = 0.406
    set udg_ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
    set udg_totalAllocs = 0
    set udg_allocCounter = -1
    set udg_damageEventTrigger = 0.0
   
    // Register units on map initialization
    call TriggerRegisterVariableEvent(udg_damageHandler, "damageEventTrigger", EQUAL, 1.0)
    call TriggerAddCondition(udg_damageEvent, Filter(cDamageEngine))  
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)
    call ForGroup(g, function MapInit)
   
    // Register units that enter the map
    call RegionAddRect(r, bj_mapInitialPlayableArea)
    call TriggerRegisterEnterRegion(UnitEnters, r, null)
    call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
   
    // Register trigger for allocated attacks
    call TriggerAddCondition(udg_runAllocatedAttacks, Filter(cRunAllocatedAttacks))
   
    // Clear memory leaks
    call TriggerRegisterTimerEvent(ClearMemory, udg_TRIGGER_CLEANUP_PERIOD, true)
    call TriggerAddCondition(ClearMemory, Filter(cClearMemory))

    // Clean things up
    call DestroyGroup(g)
    set UnitEnters = null
    set ClearMemory = null
    set cDamageEngine = null
    set cUnitEnters = null
    set cClearMemory = null
    set cRunAllocatedAttacks = null
    set g = null
    set r = null
endfunction