• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] [System] DamageDetectSystem

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
[System] Dalv's DamageDetection (DDD)

Greetings
Why use this?
- Lite & fast
- GUI friendly
- Low requirement
- Supports damage type detection
- Efficient memory usage​

Code
JASS:
    //x=============================================================== //
    //x
    //          Dalv's Damage Detection
    //                      v1.7.1
    //
    //x=============================================================== //
    //x
    //      Advantages:
    //          - Lite & fast
    //          - GUI friendly
    //          - Low requirement
    //          - Supports damage type detection
    //          - Efficient memory usage
    //
    //
    //      Requirement(s):
    //          - Bribe's UnitIndexer
    //
    //
    //      How to install:
    //          - Copy DDD_ABILITY_1 ability to your map
    //          - Copy DDD_ABILITY_2 ability to your map
    //          - Copy the DDD trigger folder to your map
    //          - Delete VarCreator trigger
    //          - Install required systems correctly
    //          - Configure the system correctly
    //
    //
    //      User API:
    //          1. Register a unit into the system, will ignore unit filtering.
    //              | function DDD__RegisterUnit takes unit whichUnit returns boolean
    //
    //
    //          2. Remove a unit from the system.
    //              | function DDD__UnregisterUnit takes unit whichUnit returns boolean
    //
    //
    //          3. Catch the damage event.
    //              | DDD__Event__Trigger EQUAL 1.00
    //
    //
    //          4. Event responses (customizable).
    //              | DDD__Event__Source  => source of damage (attacker)
    //
    //              | DDD__Event__Target  => target of damage (attacked)
    //
    //              | DDD__Event__Amount  => amount of damage
    //
    //              | DDD__Event__Type    => type of damage
    //                    - DDD__DamageTypeSpell
    //                    - DDD__DamageTypePhysic
    //                    - DDD__DamageTypeCode
    //
    //
    //          5. All damages by this function will be classified as "code" damage.
    //              | function DDD__DealCodeDamage takes unit source, widget target, reeal amount, boolean attack, boolean ranged,
    //                                  attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
    //
    //
    //          6. Turn on/off the damage detection.
    //              | function DDD__EnableDetection takes boolean enable returns boolean
    //                    - true  => on
    //                    - false => off
    //
    //              To check whether the detection is currently on/off (do not modify!)
    //              | DDD_IsDetectionEnabled
    //
    //
    //          7. Turn on/off damage detection for a specific unit.
    //              | function DDD__EnableDetectionForUnit takes unit whichUnit, boolean enable returns boolean
    //                    - true  => on
    //                    - false => off
    //
    //
    //          8. Ignore (avoid) damage, damage won't be dealt completely.
    //              | set DDD__IgnoreThisEvent = true
    //
    //
    //                  (You are not recommended to use any function or
    //                    variable that is not mentioned in user API.)
    //
    //
    //      Credits:
    //          - Bribe for his GUI friendly UnitIndexer and UnitEvent.
    //
    //          - Nestharus for his get spell resistance method
    //              and for his guidances.
    //
    //          - Special thanks to looking_for_help for his
    //              damage type detection method.
    //
    //x=============================================================== //
    //x
    //                        CONFIGURATION                            //
    //
        
        // DDD_ABILITY_1's raw code at Object Editor
        constant function DDD__Ability_1 takes nothing returns integer
            return 'A000'
        endfunction
        
        // DDD_ABILITY_2's raw code at Object Editor
        constant function DDD__Ability_2 takes nothing returns integer
            return 'A001'
        endfunction
        
        // Number of units for each group.
        // Higher value will increase the memory usage efficiency
        // but will slow down the trigger clean up process
        constant function DDD__GroupMaxMember takes nothing returns integer
            return 25
        endfunction
        
        // If true, all classified unit will be registered automatically to
        // the system
        constant function DDD__EnableAutoRegister takes nothing returns boolean
            return true
        endfunction
        
        // Classification for units that can be automatically registered to
        // the system
        function DDD__FilterUnit takes unit whichUnit returns boolean
            return true // Currently it allows any unit to be registered
        endfunction
    
    //
    //x=============================================================== //
    
    // Enable/disable detection for specific unit
    function DDD__EnableDetectionForUnit takes unit whichUnit, boolean enable returns boolean
        
        local integer data = GetUnitUserData(whichUnit)
        
        // If unit is registered
        if udg_DDD__UnitIndex[data] != 0 and enable != udg_DDD__UnitDetectionState[data] then
        
            set udg_DDD__UnitDetectionState[data] = enable
            
            // Add or remove damage type detector
            if enable and UnitAddAbility(whichUnit, DDD__Ability_1()) and UnitMakeAbilityPermanent(whichUnit, true, DDD__Ability_1()) then
            elseif GetUnitAbilityLevel(whichUnit, DDD__Ability_1()) > 0 and UnitRemoveAbility(whichUnit, DDD__Ability_1()) then
            endif
            
        endif
        
        return udg_DDD__UnitDetectionState[data]
    endfunction
    
    // Enable/disable detection for all registered units
    function DDD__EnableDetection takes boolean enable returns boolean
        
        local integer i = 1
        local group tempGroup
        local unit first
        
        if enable != udg_DDD__IsDetectionEnabled then
            loop
                exitwhen i > udg_DDD__Group__Index
                
                // Enable/disable detection triggers
                if enable then
                    call EnableTrigger(udg_DDD__Group__Trigger[i])
                else
                    call DisableTrigger(udg_DDD__Group__Trigger[i])
                endif
                
                // Disable detection for all registered units
                set tempGroup = CreateGroup()
                loop
                    set first = FirstOfGroup(udg_DDD__Group__Group[i])
                    exitwhen first == null
                    call GroupRemoveUnit(udg_DDD__Group__Group[i], first)
                    
                    call GroupAddUnit(tempGroup, first)
                    call DDD__EnableDetectionForUnit(first, enable)
                endloop
                
                // Recycle group
                call DestroyGroup(udg_DDD__Group__Group[i])
                set udg_DDD__Group__Group[i] = tempGroup
                set tempGroup = null
                
                set i = i + 1
            endloop
            set udg_DDD__IsDetectionEnabled = enable
        endif
        
        return udg_DDD__IsDetectionEnabled
    endfunction
    
    // Function to obtain unit's spell resistance
    function DDD__GetSpellResistance takes unit whichUnit returns real
        
        local real life = GetWidgetLife(whichUnit)
        local real max  = GetUnitState(whichUnit, UNIT_STATE_MAX_LIFE)
        local real result
        
        call SetWidgetLife(whichUnit, max)
        
        // Deal test damage
        set udg_DDD__Enabled = false
        call UnitDamageTarget(whichUnit, whichUnit, -max/2, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
        set udg_DDD__Enabled = true
        
        // Calculate resistance based on collected data
        set result = 2*(max-GetWidgetLife(whichUnit))/max
        call SetWidgetLife(whichUnit, life)
        
        return result
    endfunction
    
    // Detect damage type
    function DDD__GetDamageType takes nothing returns integer
    
        if udg_DDD__CodeDetector then
            set udg_DDD__CodeDetector = false
            return udg_DDD__DamageTypeCode
        elseif udg_DDD__Event__Amount < 0 then
            set udg_DDD__Event__Amount = -udg_DDD__Event__Amount
            return udg_DDD__DamageTypeSpell
        else
            return udg_DDD__DamageTypePhysic
        endif
        
        return 0
    endfunction
    
    // Remove protection
    function DDD__Deprotect takes nothing returns boolean
        
        local unit u = GetTriggerUnit()
        
        // Remove health bonus
        if GetUnitAbilityLevel(u, DDD__Ability_2()) > 0 and UnitRemoveAbility(u, DDD__Ability_2()) then
        endif
        
        // Refresh life
        call SetWidgetLife(u, udg_DDD__Protect__Health[GetUnitUserData(u)])
        call DestroyTrigger(GetTriggeringTrigger())
        set u = null
        
        return false
    endfunction
    
    // Protect unit from a native damage
    function DDD__Protect takes unit whichUnit, real damage returns nothing
    
        local trigger t = CreateTrigger()
        local real life = GetWidgetLife(whichUnit)
        local real max  = GetUnitState(whichUnit, UNIT_STATE_MAX_LIFE)
        
        set udg_DDD__Protect__Health[GetUnitUserData(whichUnit)] = life
        
        // If the unit needs additional hp
        if life + RAbsBJ(damage) > max then
            call UnitAddAbility(whichUnit, DDD__Ability_2())
        endif
        
        // If physical damage
        if damage > 0 then
            call SetWidgetLife(whichUnit, life+damage)
            call TriggerRegisterUnitStateEvent(t, whichUnit, UNIT_STATE_LIFE, LESS_THAN, life+damage/2)
        else
            call SetWidgetLife(whichUnit, life)
            call TriggerRegisterUnitStateEvent(t, whichUnit, UNIT_STATE_LIFE, GREATER_THAN, life-damage/2)
        endif
        
        call TriggerAddCondition(t, Condition(function DDD__Deprotect))
        set t = null
        
    endfunction
    
    function DDD__killTarget takes nothing returns nothing
        call DDD__EnableDetectionForUnit(udg_DDD__Event__Target, false)
        call UnitDamageTarget(udg_DDD__Event__Source, udg_DDD__Event__Target, 10000000, false, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null)
        call DDD__EnableDetectionForUnit(udg_DDD__Event__Target, true)
    endfunction
    
    // Executed on damage event
    function DDD__onDamage takes nothing returns boolean
        
        local real resistance
        local real life
        local real damage
        
        local unit target
        local unit ptarget
        local unit psource
        
        local real pamount
        local integer ptype
        local integer data
        
        if udg_DDD__Enabled then
            
            // Original target
            set target = GetTriggerUnit()
            set data = GetUnitUserData(target)
            
            // If target is registered in the system
            if udg_DDD__UnitIndex[data] != 0 and udg_DDD__UnitDetectionState[data] then
            
                // Original damage
                set damage  = GetEventDamage()
                
                // Previous data
                set ptarget = udg_DDD__Event__Target
                set psource = udg_DDD__Event__Source
                set pamount = udg_DDD__Event__Amount
                set ptype   = udg_DDD__Event__Type
                
                // If spell damage is detected
                if damage < 0 then
                    set resistance = DDD__GetSpellResistance(target)
                    if resistance > 1 then
                        set damage = damage*resistance
                    endif
                endif
            
                // Modifiable data
                set udg_DDD__Event__Target = target
                set udg_DDD__Event__Amount = damage
                set udg_DDD__Event__Source = GetEventDamageSource()
                set udg_DDD__Event__Type   = DDD__GetDamageType()
                
                // Fire the damage event
                set udg_DDD__Event__Trigger  = 1
                set udg_DDD__Event__Trigger  = 0
                
                // Substract life by damage amount
                set life = GetWidgetLife(udg_DDD__Event__Target) - udg_DDD__Event__Amount
                if life > 0.405 then
                    call SetWidgetLife(udg_DDD__Event__Target, life)
                endif
                
                // If event target isn't modified
                if target == udg_DDD__Event__Target then
                    
                    // If need to be protected
                    if life > 0.405 then
                        if RAbsBJ(damage) > 0.01 then
                            call DDD__Protect(target, damage)
                        endif
                    // If need damage to kill
                    elseif damage < 0 then
                        call DDD__killTarget()
                    else
                        call SetWidgetLife(target, damage)
                    endif
                    
                else
                    
                    // If need to be protected
                    if RAbsBJ(damage) > 0.01 then
                        call DDD__Protect(target, damage)
                    endif
                    
                    // If need damage to kill
                    if life <= 0.405 then
                        call DDD__killTarget()
                    endif
                    
                endif
                
                // Reset the data
                set udg_DDD__Event__Source = psource
                set udg_DDD__Event__Target = ptarget
                set udg_DDD__Event__Amount = pamount
                set udg_DDD__Event__Type   = ptype
                
                // Remove leak
                set psource = null
                set ptarget = null
                
            endif
            
            set target = null
        endif
        
        return false
    endfunction
    
    // Register a unit into the system
    function DDD__RegisterUnit takes unit whichUnit returns boolean
    
        local integer data = GetUnitUserData(whichUnit)
    
        // If unit hasn't been registered yet
        if udg_DDD__UnitIndex[data] == 0 then
            
            // If new node is required then create it
            if udg_DDD__Group__Counter[udg_DDD__Group__Index] == DDD__GroupMaxMember() then
                set udg_DDD__Group__Index = udg_DDD__Group__Index + 1
                set udg_DDD__Group__Group[udg_DDD__Group__Index]   = CreateGroup()
                set udg_DDD__Group__Trigger[udg_DDD__Group__Index] = CreateTrigger()
                call TriggerAddCondition(udg_DDD__Group__Trigger[udg_DDD__Group__Index], Condition(function DDD__onDamage))
            endif
            
            // Register unit into the current node
            set udg_DDD__UnitDetectionState[data] = true
            set udg_DDD__UnitIndex[data] = udg_DDD__Group__Index
            set udg_DDD__Group__Counter[udg_DDD__Group__Index] = udg_DDD__Group__Counter[udg_DDD__Group__Index] + 1
            
            // Add damage type detector ability
            if UnitAddAbility(whichUnit, DDD__Ability_1()) and UnitMakeAbilityPermanent(whichUnit, true, DDD__Ability_1()) then
            endif
            
            call GroupAddUnit(udg_DDD__Group__Group[udg_DDD__Group__Index], whichUnit)
            call TriggerRegisterUnitEvent(udg_DDD__Group__Trigger[udg_DDD__Group__Index], whichUnit, EVENT_UNIT_DAMAGED)
            
            return true
        endif
        
        return false
    endfunction
    
    // Remove a unit from the system
    function DDD__UnregisterUnit takes unit whichUnit returns boolean
        
        local integer data  = GetUnitUserData(whichUnit)
        local integer index = udg_DDD__UnitIndex[data]
        local unit first
        
        // If unit is registered
        if index > 0 then
        
            // Remove from current node
            call GroupRemoveUnit(udg_DDD__Group__Group[index], whichUnit)
            set udg_DDD__Group__Counter[index] = udg_DDD__Group__Counter[index] - 1
            set udg_DDD__UnitDetectionState[data] = false
            set udg_DDD__UnitIndex[data] = 0
            
            // Check whether unit is in the latest node or not
            if index == udg_DDD__Group__Index then
                
                // If node is empty then destroy it
                if udg_DDD__Group__Counter[index] == 0 then
                
                    // Remove leaks
                    call DestroyTrigger(udg_DDD__Group__Trigger[index])
                    call DestroyGroup(udg_DDD__Group__Group[index])
                    set udg_DDD__Group__Trigger[index] = null
                    set udg_DDD__Group__Group[index]   = null
                    
                    set udg_DDD__Group__Index = udg_DDD__Group__Index - 1
                endif
                
            // If unit isn't located in the last node and
            // that node is empty already, then destroy
            elseif udg_DDD__Group__Counter[index] == 0 then
                
                call DestroyTrigger(udg_DDD__Group__Trigger[index])
                
                // Transfer data from last node to the destroyed node
                loop
                    set first = FirstOfGroup(udg_DDD__Group__Group[udg_DDD__Group__Index])
                    exitwhen first == null
                    call GroupRemoveUnit(udg_DDD__Group__Group[udg_DDD__Group__Index], first)
                    
                    call GroupAddUnit(udg_DDD__Group__Group[index], first)
                    set udg_DDD__UnitIndex[GetUnitUserData(first)] = index
                endloop
                set udg_DDD__Group__Trigger[index] = udg_DDD__Group__Trigger[udg_DDD__Group__Index]
                set udg_DDD__Group__Counter[index] = udg_DDD__Group__Counter[udg_DDD__Group__Index]
                
                // Remove leaks
                call DestroyGroup(udg_DDD__Group__Group[udg_DDD__Group__Index])
                set udg_DDD__Group__Trigger[udg_DDD__Group__Index] = null
                set udg_DDD__Group__Group[udg_DDD__Group__Index]   = null
                
                set udg_DDD__Group__Index = udg_DDD__Group__Index - 1
            endif
            
            return true
        endif
        
        return false
    endfunction
        
    // All damages caused by this function will be classified as "code" damage
    function DDD__DealCodeDamage takes unit source, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
        set udg_DDD__CodeDetector = true
        call UnitDamageTarget(source, target, amount, attack, ranged, attackType, damageType, weaponType)
    endfunction
    
    // Executed on index event
    function DDD__onIndex takes nothing returns boolean
        if DDD__FilterUnit(udg_UDexUnits[udg_UDex]) then
            call DDD__RegisterUnit(udg_UDexUnits[udg_UDex])
        endif
        return false
    endfunction
    
    // Executed on deindex event
    function DDD__onDeindex takes nothing returns boolean
        call DDD__UnregisterUnit(udg_UDexUnits[udg_UDex])
        return false
    endfunction
    
    function InitTrig_DDD takes nothing returns nothing
    
        local trigger t
        
        // Init damage types
        set udg_DDD__DamageTypePhysic = 1
        set udg_DDD__DamageTypeSpell  = 2
        set udg_DDD__DamageTypeCode   = 3
        
        set udg_DDD__Enabled = true
        set udg_DDD__Group__Counter[0] = DDD__GroupMaxMember()
        
        if DDD__EnableAutoRegister() then
            set t = CreateTrigger()
            call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
            call TriggerAddCondition(t, Condition(function DDD__onIndex))
        endif
        
        set t = CreateTrigger()
        call TriggerRegisterVariableEvent(t, "udg_DeathEvent", EQUAL, 3.00)
        call TriggerAddCondition(t, Condition(function DDD__onDeindex))
        set t = null
        
    endfunction

Demo
  • on damage
    • Events
      • Game - DDD__Event__Trigger becomes Equal to 1.00
    • Conditions
      • DDD__Event__Amount Not equal to (!=) 0.00
    • Actions
      • -------- --------------------------------------------------- --------
      • -------- ----------Manipulating Event---------- --------
      • -------- --------------------------------------------------- --------
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Source) Equal to (==) Sorceress
          • Then - Actions
            • -------- Switch to code damage type --------
            • Set DDD__Event__Type = DDD__DamageTypeCode
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Source) Equal to (==) Priest
          • Then - Actions
            • -------- Switch to spell damage type --------
            • Set DDD__Event__Type = DDD__DamageTypeSpell
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Source) Equal to (==) Knight
          • Then - Actions
            • -------- Reduce Knight's damage to 10% --------
            • Set DDD__Event__Amount = (DDD__Event__Amount x 0.10)
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Target) Equal to (==) Peasant
          • Then - Actions
            • -------- Peasant heals the damage source --------
            • Set DDD__Event__Target = DDD__Event__Source
            • Set DDD__Event__Amount = (DDD__Event__Amount x -1.00)
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Source) Equal to (==) Footman
          • Then - Actions
            • -------- Footman hurts himself --------
            • Set DDD__Event__Target = DDD__Event__Source
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Source) Equal to (==) Mountain King
          • Then - Actions
            • -------- MK deals 4x damage --------
            • Set DDD__Event__Amount = (DDD__Event__Amount x 4.00)
          • Else - Actions
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • (Unit-type of DDD__Event__Target) Equal to (==) Priest
          • Then - Actions
            • Custom script: if GetWidgetLife(udg_DDD__Event__Target) < 100 then
            • -------- Priest explodes on certain condition --------
            • Custom script: call SetUnitExploded(udg_DDD__Event__Target, true)
            • Unit - Cause DDD__Event__Source to damage DDD__Event__Target, dealing 99999.00 damage of attack type Chaos and damage type Normal
            • Custom script: endif
          • Else - Actions
      • -------- #################### --------
      • -------- --------------------------------------------------- --------
      • -------- to deal un-detected damage, you can do this trick --------
      • Custom script: call DDD__EnableDetectionForUnit(udg_DDD__Event__Target, false)
      • Unit - Cause DDD__Event__Source to damage DDD__Event__Target, dealing 100.00 damage of attack type Spells and damage type Normal
      • Custom script: call DDD__EnableDetectionForUnit(udg_DDD__Event__Target, true)
      • -------- the damage will be dealt without being recognized by the detector, and you can do it anywhere --------
      • -------- --------------------------------------------------- --------
      • -------- #################### --------
      • -------- --------------------------------------------------- --------
      • -------- ----------Displaying Damage---------- --------
      • -------- --------------------------------------------------- --------
      • -------- If damage type is physic --------
        • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • DDD__Event__Type Equal to (==) DDD__DamageTypePhysic
          • Then - Actions
            • -------- give red color --------
            • Set demo_color = |cffff0000
          • Else - Actions
            • -------- If damage type is spell --------
              • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • DDD__Event__Type Equal to (==) DDD__DamageTypeSpell
                • Then - Actions
                  • -------- give blue color --------
                  • Set demo_color = |cff0000ff
                • Else - Actions
                  • -------- If damage type is code --------
                    • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
                      • If - Conditions
                        • DDD__Event__Type Equal to (==) DDD__DamageTypeCode
                      • Then - Actions
                        • -------- give green color --------
                        • Set demo_color = |cff00ff00
                      • Else - Actions
      • -------- display damage as floating text --------
      • Floating Text - Create floating text that reads (demo_color + ((String(DDD__Event__Amount)) + |r)) above DDD__Event__Target with Z offset 0.00, using font size 10.00, color (100.00%, 100.00%, 100.00%), and 0.00% transparency
      • Floating Text - Set the velocity of (Last created floating text) to 64.00 towards 90.00 degrees
      • Floating Text - Change (Last created floating text): Disable permanence
      • Floating Text - Change the lifespan of (Last created floating text) to 2.50 seconds
      • Floating Text - Change the fading age of (Last created floating text) to 2.00 seconds

Credits
- looking_for_help for his damage type detection method
and for being my main inspiration for this system.
- Bribe for his GUI unit indexer.
- Nestharus​
 

Attachments

  • [DvG] DDD v1.7.w3x
    43.7 KB · Views: 154
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Sigh

Flashing lifebars
Breaks GetWdgetLife
After damage, normally used for cleaning, runs code on it in order -.-. Displaying damage won't work on it either because it's not run in reverse. Just make things easy and drop this event -.-. It isn't trivial to run a trigger in reverse.


Why do we need yet another DDS? We have 4.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Your's: too big
lfh's: too slow
Bribe's: GUI
Cokeymonkey's: Poor feature

Just check my Illusion snippet, I have just halved the code size after using my DDS :D

(And guess what, I will write another UnitIndexer that GUI friendly as well. Since Bribe's deindexing method is not really good.)

After damage, normally used for cleaning, runs code on it in order -.-. Displaying damage won't work on it either because it's not run in reverse. Just make things easy and drop this event -.-. It isn't trivial to run a trigger in reverse.
I don't get this part.
 
Last edited:
Level 22
Joined
Sep 24, 2005
Messages
4,821
You should disable the OnDamage trigger before giving control to the user to protect against infinite damage event recursions.

JASS:
        // Fire the "before damage" event
        call DisableTrigger(GetTriggeringTrigger())
        set udg_DDS__Event__Trigger = 1
        set udg_DDS__Event__Trigger = 0

        ...

        // Remove leak
        set target = null
        call EnableTrigger(GetTriggeringTrigger())
        return false
    endfunction
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
If you're talking about damage events being fired by coded damage then it's not a bug, code damage invokes damage events.

I'd say yes to fixing that, since warcraft instantly terminates without errors(on my machine) when it happens. A debug conditional block catching that error would be helpful too.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
You should disable the OnDamage trigger before giving control to the user to protect against infinite damage event recursions.
Have been thinking about this. DDS is made to detect "any damage". So the infinite recursive thingy shouldn't be negated. If it happens, so the fault is on user. But my DDS will have some features to ease user on avoiding that problem.

There will be something called "IgnoreThisEvent" that will skip the current triggering event, and "IgnoreNextEvent" that will skip the next triggering event. I think it's the most possible elegant way to fix this problem. "IgnoreThisEvent" can only be casted on "before damage" event, while the other one can be casted anytime.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
if I want one time 100% damage reflect, with your DDS, I will crash the game without error, thats why you dont ignore recursive events.

You must count on the fact that people will do all kinds of weird shit. Even my ChatCommand is recursion-safe, and I dont know if you can even force fire "Player chat event" from triggers, just in case
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
if I want one time 100% damage reflect, with your DDS
Easy:
  • Set DDS__Event__DamageTarget = DDS__Event__DamageSource
Just change the target before damage is dealt :p

And if you want to modify the damage:
  • Set DDS__Event__DamageAmount = (DDS__Event__DamageAmount x factor)
Just wait for my update, you will find my solution is better.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
This is the way you do that (in the next update):

  • Multiple FunctionsIf (All Conditions are True) then do (Then Actions) else do (Else Actions)
    • If - Conditions
      • (Unit-type of DDS__Event__DamageSource) Equal to (==) Footman
    • Then - Actions
      • Set DDS__IgnoreNextEvent = True
      • Unit - Cause DDS__Event__DamageTarget to damage DDS__Event__DamageSource, dealing (DDS__Event__DamageAmount x 0.50) damage of attack type Spells and damage type Normal
    • Else - Actions
  • Set DDS__IgnoreNextEvent = True
means the next "before damage" event wont be fired, while the "after damage" event will still fire so user can display the damage.

Remember that "before damage" event is where you can do your mumbo jumbo, while the "after damage" event is for displaying information (damage, etc.) only

Sounds good? I won't prohibit the recursive thingy completely because if I do that, user can't access the next "after damage" event, in case they want to display the damage.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Okay, updated!

The IgnoreNextEvent thingy is canceled. And it will automatically negate the infinite recursion problem without disturbing the next post damage event so user can still display damage. If you deal damage at post damage event it's still recursed, but does no harm, it won't crash the game (perhaps due to the code efficiency :>).

The flashing lifebar problem has been fixed as well.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
But I can :/

Try to re-download, I have just updated the attachment since I forgot to update the variable creator.

@edo494:
The key why it's not recursed is here:
JASS:
        set udg_DDS__Event__Trigger = 1
        set udg_DDS__Event__Trigger = 0

I don't initially reset the trigger back to 0. If I do it like this,

JASS:
        set udg_DDS__Event__Trigger = 0
        set udg_DDS__Event__Trigger = 1
        set udg_DDS__Event__Trigger = 0
Then yes it's going to be recursed, u know why :D
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
[23-15-27] :user: Almia: Dalvengyr, why make a system already done and has the same structures as the original, excep that it uses BJs? seriosly?
[23-15-42] :user: Almia: Dalvengyr, that system can't even beat Coke's
[23-15-52] :user: Almia: Dalvengyr, and if some needs plain JASS, they should use lfh
Same structure? But all jass spells always have same structure :D

Anyway, my DDS works differently compared to Bribe's. Cokeymonkey's DDS doesn't allow user to modify the damage, etc. on damage event. While lfh's, as Nestharus said, it's poorly coded. And it uses hashtable :c
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
lfh's: too slow

Too slow for what?

Please prove such statements with realistic benchmarks such that people also can see how much faster it is. Otherwise its just cheap propaganda ;)

While lfh's, as Nestharus said, it's poorly coded. And it uses hashtable :c

Same applies here. If you make such statements, please elaborate and don't just cite someone who you think is right. Because the coding is perfectly fine.

More complex != better. Higher complexity has to be justified by significant benefits. Yes, Nestharus system uses algorithms that are in theory better. In practise you won't ever notice it.



About the system itself, its unfortunatly heavily broken, or better: poorly coded (basically everything I tried in 5 minutes didn't work at all).

Siege doesn't explode units correctly, life bars are flickering, the spell damage handling mechanism doesn't work at all (take a bloodmage and cast a level 3 flame strike into the peons -> your system goes mad), spirit link leaves units after the first hit with only 1 HP and so on. And all that even without any damage modifiers but just pure damage detection. You don't get XP for killing enemy units. etc. etc.

You will have to do some massive research on how DDS work internally and what tricks have to be used.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I don't get this part.

This is why I said to just drop your event then. Trust me, it won't work and it can be difficult for some people to understand why. I spent 3 days explaining why to someone else and they still don't understand why. The more experienced users do understand why though and they agreed.

onAfterDamage is not used for just display. It's also used for clean up.

System 1: Create
System 2: Create
System 3: Create
System 1: Destroy
System 2: Error (system 1 destroyed)
System 3: ___ (never reached)

How it should be

System 1: Create
System 2: Create
System 3: Create
System 3: Destroy
System 2: Destroy
System 1: Destroy

Thus you must run onAfterDamage in reverse order


If you still don't get it, just drop it ;)
 
Level 22
Joined
Sep 24, 2005
Messages
4,821
I don't see how the infinite recursion can be a feature, it immediately halts the application, how can anyone effectively make use of that?

Anyways, the other guys already pointed out the major flaws, give it your best shot.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
@lfh:
First, I'm sorry to call your DDS too slow :p No offense.

But I see how it can be faster (better), first is by eliminating the use of hashtable. I haven't checked how your DDS refresh the trigger but it should be likewise Cokey's SDD.

At first thought I saw your system as "overhead". Because I see your system does many things which unrelated to "DDS". Like "GetUnitLife" etc. But after you mentioned these bugs:
About the system itself, its unfortunatly heavily broken, or better: poorly coded (basically everything I tried in 5 minutes didn't work at all).

Siege doesn't explode units correctly, life bars are flickering, the spell damage handling mechanism doesn't work at all (take a bloodmage and cast a level 3 flame strike into the peons -> your system goes mad), spirit link leaves units after the first hit with only 1 HP and so on. And all that even without any damage modifiers but just pure damage detection. You don't get XP for killing enemy units. etc. etc.

You will have to do some massive research on how DDS work internally and what tricks have to be used.
Heck, I didn't thought of those bugs Xd. And it doesn't mean poorly coded, it refreshes trigger correctly and handle the event in the simplest way: apply the modified damage and avoid the original damage. But now I will have to deal with those bugs so I don't know if I can keep this simple-ness :p

And I thought that life bar issue has gone in the last update, let's see if I can encounter the same issue.

@Nestha:
I still don't get the problem. I see how important and useful is it to have 2 events as well. I will do anything to keep this feature even if I have to reconstruct the system.

As far as I understood, you are talking about recursive thingy:

if you deal damage within an event, another event is fired even before the first event has been finished.

Like (e.g. only recured 3 times):

execute 1st event
execute 2nd event
execute 3rd event

finish on 3rd event
continue the 2nd event
continue the 1st event​

and it happens right here:
JASS:
        @set udg_DDS__Event__Trigger = 1@
        set udg_DDS__Event__Trigger = 0

But as far as I understand, my system handles it correctly by doin this:
JASS:
        // Previous data
        local unit ptarget = udg_DDS__Event__DamageTarget
        local unit psource = udg_DDS__Event__DamageSource
        local real pamount = udg_DDS__Event__DamageAmount
        local integer ptype = udg_DDS__Event__DamageType


        // Reset the data
        set udg_DDS__Event__DamageSource = psource
        set udg_DDS__Event__DamageTarget = ptarget
        set udg_DDS__Event__DamageAmount = pamount
        set udg_DDS__Event__DamageType   = ptype

That's why it can recurs correctly.

That's if I understand you correctly.
onAfterDamage is not used for just display. It's also used for clean up.
Cleaning what?

Just tell me the problem why I should get rid the second event.

@chobibo
I don't see how the infinite recursion can be a feature, it immediately halts the application, how can anyone effectively make use of that?
It won't recurs on the last update.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I'm not talking about recursion

If you really want to keep this, consider this scenario

System 1: A combat system that sets up an environment for combat to occur. Creates variables and so on.

System 2: Requires system 1. Creates a damage table where damage can be inputted.

System X: Various damage modifiers run locally for a given unit. These modifiers modify the damage in the damage table. At the end, the damage table puts it into a damage variable for the combat system and the combat system feeds it into the DDS where it is finally applied. This depends on the damage table.

System Y: This is run to display damage from the Damage Table. This depends on damage table and is only run in onAfter.

The systems registers in the following order

System 1: Registers to your system, onDamage and onDamageAfter (create and clean)
System 2: Registers to your system, onDamage and onDamageAfter (create and clean)
System X: Registers to your system onDamage
System Y: Registers to your system onDamageAfter

What happens when damage occurs based on your current architecture?

onDamage
Run System 1
Run System 2
Run System X
Run System Y

onDamageAfter
Run System 1 (the environment is now destroyed)
Run System 2 (the damage table has no environment to put damage into! Uh oh)
Run System Y (there is no damage table to output because it was destroyed, so the texttag is gone)


Whenever you deal with post events, that is events that run after something has occurred, these post events must always run in reverse order of how they were registered. This is because of scope. A system is always going to register to something before a system that depends on it. You don't want the system to destroy itself before a system that depends on it. Thus, you always want to run like this.

onDamage

System
System That Depends

onDamageAfter

System That Depends
System


This is always the case. Thus, it is always the case that you want to run post events in reverse order.

Now, to get a trigger to run in reverse means that you need to construct a boolean expression tree in reverse. This is not trivial.

Instead of something like

TriggerAddCondition()

or

Or(function A, function B)

you just do a bunch of

Or(function B, function A)
Or(function D, function C)

Or(Or2, Or1)

Etc. This will build a trigger that, when evaluated, runs all code registered to it in reverse order. This is how my Trigger resource does it.

You'll have to look at the BooleanExpression code to see how to properly build, manipulate, and clean this stuff if you want to stick to vanilla JASS. You could also have a set of triggers or a single trigger that is evaluated once for every piece of code. For example, when I supported the old GUI with priorities, I'd set the variable to one value for each possible priority. You would have to do around the same thing if you still want to use a variable event.

It's really just easier to drop the post event because GUI support with it isn't really feasible.



You also want recursion. With something like thorns, the damage for the thorns may be modified by something else, and so forth. It is up to the user to define how deep recursion goes with something like a combat system ; ). My own DDS has a few examples with some recursion usage. I never disable the system, I just stop at one point and use ifs.


Also, keep in mind that you are missing one damage event. This is onDamageOutgoing. You capture when a unit A is damaged, but you don't capture when a unit B deals damage. What's the difference? A big one. Without "a unit deals damage," you then have to register code to every unit on the map to capture when unit B might damage them.

My DDS uses 3-4 phases of damage. There are also global and local events. The DDS on wc3c has an extension that supports local events. My DDS is the only other system with support for local events. Why are these useful?

A lot of the time, when a unit equips an item, that item enhances the unit's damage. This might be about plugging values into a damage table. You want to register the event for that item for that unit and only that unit, not every unit on the map ;). You can achieve the same thing with if-statements in every global event, but now you are just adding lots and lots of overhead. Some things should be global and some things should be local.


In order to fix your life, you can't use a timer to get past a damage event. You have to use the UNIT_STATE_HEALTH or whatever event. When the unit health goes down/up (depending on the damage), you are then at the other side of the damage event, where the damage was applied, with absolutely no delay. This means that there is no chance that a user will ever run into the buff that's applied to save the unit, because there is no delay in between when the buff is applied and when it is taken off. This is the approach my DDS uses, which means that it is the only one that does not break GetWidgetLife.


If you really want to learn all there is to know about the makings of a DDS, go through the labs for DDS and take a look through the code. It has all of the best tech. It's a solid reference. You can then take pieces you like and structure it your own way ^)^. However, it'll show you how to do a DDS absolutely correctly. Why do I also say to look at DDS and not LFH's or coke's? They don't give the complete picture, like onAfterDamage, local damage events, and onDamageOutgoing. If you want the complete picture, look at DDS.

Also, onDamageOutgoing is meant to work with an attack indexer. The parameters are copied when the attack is created (so that there are no leaks, don't wanna make handles and stuff), and then they are used to create things in onDamageOutgoing. This is done because damage may not be applied when an attack is generated, and there is no way to capture when an attack is destroyed.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
This should be moved to the lab. I will re-submit after I'm sure this is bug-free. I was pretty sure this is bug-free already, but after further testing, no it's not.

Anyway, here is an update:
- Units are killed correctly, including explode thingy
- Healthbar issue has been removed completely (as far as my eyes can see)
- Spell damage wasn't detected correctly, fixed
- Faster trigger recycling process
- After damage event is removed (credits to Nestharus >.>)
- Added turn on/off detection feature

Known bugs:
- This
- Locust ability deals damage to caster when return. Probably caused by the damage resistance, still trying to find a way to catch the event. (kinda nasty bug)

Further updates (just for reminder):
- Fixed bug (hopefully :D)
- Modify event, where user can modify the event, like changing damage target, amount, etc. It will automatically ignore any damage done by user (in case the user is troll-er) during the event. Second thought, this could have the same issue with the after damage event, so cancelled :D
- Better "protect" usage.
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
This should be moved to the lab. I will re-submit after I'm sure this is bug-free. I was pretty sure this is bug-free already, but after further testing, no it's not.

Updated:
- Better protect usage
- Some minor improvements
- Registration now returns boolean which indicate whether the unit is (un)registered successfully or not
- Magic resistance detection (stolen from nestharus :p)
- Improved demo
- Sorry the bug is too nasty :p
 
Last edited:
Level 10
Joined
Sep 19, 2011
Messages
527
on DDS__GetSpellResistanceFactor:

JASS:
        // Deal test damage
        set udg_DDS__Enabled = false
        call UnitDamageTarget(whichUnit, whichUnit, -max/2, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
        set udg_DDS__Enabled = true

->

JASS:
        local boolean prevEnabled = udg_DDS__Enabled
        // ...
        // Deal test damage
        set udg_DDS__Enabled = false
        call UnitDamageTarget(whichUnit, whichUnit, -max/2, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
        set udg_DDS__Enabled = prevEnabled
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Nice, Nestharus.

Your's: too big
lfh's: too slow
Bribe's: GUI
Cokeymonkey's: Poor feature
I must admit, you got balls.
As pointed out, we got enough DDSs - whatmore, you can choose the flavour which fits you the most.

In regard to jass-GUI-friendly version, I'm sorry, but lfh's script is more than enough. I've been testing and playing with it many times, the improvements were made and if you care about GUI-jass portability, you should be satisfied.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I saw a post somewhere mentioned all advantages/disadvantages of each DDS, then I saw something can still be improved from all of them. Then I made this.

If you guys really hardly accept "improvements" then okay, somebody may GY this. Those DDS exist for a long long time ago and have became a legend, but that doesn't mean they are perfect.

Don't force me to mention every advantages of this DDS compared to every DDS. I don't want to be a prick.

The only one that I can't even get close it is Nestharus' DDS, but it requires way too much things, which may become a big advantage of this DDS.
 
Well he obviosly thinks it's more efficient than some others, needs less external stuff than by Nes, but still has nice features. I think he stated his point.

Your's: too big (nes)
lfh's: too slow
Bribe's: GUI
Cokeymonkey's: Poor feature

So the new DDS should obviosuly do better in the named field named above.

It might be true or not, I don't know. But it will need some work to convince people to use your's over others, that has been used for a while already.

People be like "never change a winning team". :D If they like the current one, it might be hard to convince them.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I would like to see the list of advantages and disadvantages of your system vs other DDSs. And only objective list, so nothing like "GUI, uuuugh" etc

I told you I won't, that would be provocative.

I tried to maintain the speed and efficiency, while retaining important features of a DDS, as well as keeping as low requirements as possible.

Your's: too big (nes)
lfh's: too slow
Bribe's: GUI
Cokeymonkey's: Poor feature
That was too provocative and cocky, I'm sorry. But it's too late to take my words back. It's been quoted everywhere. :C

@IcemanBo,
Yeah, the difference between this and lfh's is really negligible, it's just a matter of speed and perhaps in some other places.

Anyway, an update is approaching. Still trying to improve the trigger refreshing even more.

And I wonder, while I'm requesting Bribe to improve his deindex event, is it better to use "unit dies" event or "unit leaves world" event as deindex event?

Updated:
- Fixed ethereal unit bug
- Improved variable usage
- Improved enable/disable detection safety
- Fixed logical mistakes on damage handling
- Improved documentation

Omegod, forgot to restore the preplaced units >.>

Updated
 
Last edited:
Level 10
Joined
Sep 19, 2011
Messages
527
i think DDS__FilterUnit should be used in DDS__RegisterUnit.

edit:

suggestion: i would leave the auto-registration part up to the user, so no requirements at all so users can pick any unit indexer.

edit: you're not using the DDS__Mod function.
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
So, here are my usual criteria for system replacement:

-> the new system is clearly better than the old system. This might be because of improved algorithms or just better coding. If the original author was lazy and didn't want to improve it, they shouldn't be upset when someone does what they did, but better ; ). Either that, or the new code should be applied to the original system.

-> In the case of brand new algorithms, then the new system should be replaced regardless. The new algorithms are the work of the new author and might involve complete refactoring of the entire thing. In fact, no code may even be used from the original system. For example, my Dummy used one equation from Bribe for turn rate, but everything else was entirely different. There was a huge fight over this.

-> In the case where the new algorithms are not clearly superior, but have their own advantages, the system should be placed alongside the original, showing the advantages and disadvantages in detail. There was a big debate over Coke's DDS. It took an entire different approach from the other DDS's on the site, thus it was approved as a parallel standard.



Show that yours isn't just a clone of lfh's with fluffy bunnies ^)^. If it is a clone with fluffy bunnies, then we gotta get lfh to improve his code : P.



This is my own stance on the matter and does not represent the stance of THW as a whole in any way. I just think that this is how it should be done, but things aren't necessarily done this way =). When things are done this way, the original authors can get pretty bitter. Not me, but other people.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I will just share my experience with those DDS:

Personally, I like lfh's DDS the most. It basically has the most completed features compared to the other ones. It's very easy to use, but not to be installed. Sometimes I prefer to use Cokey's SDD or Weep's GDD. Because why? lfh's DDS is way too complicated to be installed: I have to copy this, copy that. I have to remove this, remove that. I have to change this and that. I was just "Arghh.. Srsly? I have to do this every time I make spells using this DDS?". And that happens over years!

Then I moved to Weep's GDD (literally). Because it's very easy to be installed. Then I tried, Cokeymonkey's SDD (check previous version of my Illusion snippet), they are very easy to be used, especially Cokey's. But they have some big disadvantages: they can't detect damage types and doesn't allow to modify the event freely. That's why my Illusion snippet was so over sized.

But they have the same problem here: they do not prevent damage event recurrence. But, I can do a trick to prevent it using lfh's DDS, but it's still not easy to do and disallowing me to do many things as the cost. This problem has been killing me over years.

While Bribe's Damage Engine can avoid damage event recurrence easily, but it can not detect the damage type (automatically) like Physic, Spell, and Code. And it doesn't allow me to modify damage source and the target as well.

Why I'm not discussing your DDS? Because it wasn't here at the time, I haven't tried using yours. But it clearly has one big big disadvantage: tons of requirements. It's totally okay if I want to use it for a game, in that case your DDS is the best option. But if I just want to use DDS for a simple spell, no way I will use yours.

Then I tried to read lfh's DDS code, and those purple lines (hashtable) really hurt my eyes. I imagine how faster is it to eliminate the use of hashtable. But suggesting this to lfh is just like suggesting him to remake it, and the fact that he has two DDS, that means he has to double-remake. Not to mention that I need more features out of his DDS like ignoring damage event for easier and more elegant damage blocking. There are just too many things to be suggested, and they are all mixed inside my head. It's just crazy to suggest them one by one to all authors. Perhaps I will need to wait for years for the complete update.

Then something came to my mind. Why not make something "between" those DDS? Something which covers all of those disadvantages but still not the best of all. Something that has all features needed by DDS but still easy to use and install (even though I failed to retain the after damage event). Something that do many things but without overhead and still has fast code execution and with fair efficiency. Then there comes my DDS, for easier human life. :) I'm not really hoping this to be approved, but if I have to make a spell with DDS, I will absolutely use mine. Not for promotion, but my DDS has eliminated all of those nightmares that has been surrounding me for years.

-> the new system is clearly better than the old system. This might be because of improved algorithms or just better coding. If the original author was lazy and didn't want to improve it, they shouldn't be upset when someone does what they did, but better ; ). Either that, or the new code should be applied to the original system.
As have been said, that would be a crazy work for lfh to remove those hashtables. Not to mention additional features I need and a new approach that he should use to eliminate GetWidgetLife problem elegantly. I see lfh is less active nowadays, perhaps it will require me years to wait for him to finish the update.

-> In the case of brand new algorithms, then the new system should be replaced regardless. The new algorithms are the work of the new author and might involve complete refactoring of the entire thing. In fact, no code may even be used from the original system. For example, my Dummy used one equation from Bribe for turn rate, but everything else was entirely different. There was a huge fight over this.
I haven't checked how lfh manage/refresh the trigger, but again, it should be likewise Cokey's SDD or your DDS.

-> In the case where the new algorithms are not clearly superior, but have their own advantages, the system should be placed alongside the original, showing the advantages and disadvantages in detail. There was a big debate over Coke's DDS. It took an entire different approach from the other DDS's on the site, thus it was approved as a parallel standard.
Advantages like what? Speed? Feature? My DDS has all of those.
 
I don't get it, just detect the damage done and the source and target and type then feel free to edit. What else is there besides fixing the hashtables? Edit: LFH's is pretty easy to use for spells too, just turn it into a function then call it into your spells if you want a one liner in your spells and use some variable to grab the required information, unless I am missing something.....

Edit2: I won't ever use a VJASS DDS just because they're over-complicated as well just because they're VJASS. Vanilla forever. Though doesn't mean I can't help. =)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I will just share my experience with those DDS:

Personally, I like lfh's DDS the most. It basically has the most completed features compared to the other ones. It's very easy to use, but not to be installed. Sometimes I prefer to use Cokey's SDD or Weep's GDD. Because why? lfh's DDS is way too complicated to be installed: I have to copy this, copy that. I have to remove this, remove that.

I have to change this and that. I was just "Arghh.. Srsly? I have to do this every time I make spells using this DDS?". And that happens over years!

It seems you don't know what you are talking about ...

You have to do these changes in every DDS with type detection support (including yours). It is necessary to copy two abilities, setup their IDs, invert locust swarm damage and remove spell damage reduction from bracers.

You have to do this anyway, in every DDS. And it takes maybe 3 minutes all in all, so... "Srsly"? Can't see your point here. Just because the other DDS don't list those installation steps in their documentation doesn't mean that you don't have to perform them. You do have to.


But, I can do a trick to prevent it using lfh's DDS, but it's still not easy to do and disallowing me to do many things as the cost. This problem has been killing me over years.

I don't understand. PDDS supports this out of the box and without any problems. Its even demonstrated in the test map. Maybe spend 5 minutes learning how the system works instead of "wasting years"?

While Bribe's Damage Engine can avoid damage event recurrence easily, but it can not detect the damage type (automatically) like Physic, Spell, and Code. And it doesn't allow me to modify damage source and the target as well.

Bribes system is bugged anyways.


Then I tried to read lfh's DDS code, and those purple lines (hashtable) really hurt my eyes. I imagine how faster is it to eliminate the use of hashtable.

I measured it. Hashtables are very efficient, the speed difference is neglectable. Its just not worth the effort. I already told you to do benchmarks. And counting purple lines is not a benchmark.


But suggesting this to lfh is just like suggesting him to remake it, and the fact that he has two DDS, that means he has to double-remake.

I see lfh is less active nowadays, perhaps it will require me years to wait for him to finish the update.

No. Its not the amount of work, its that your suggestions are bad. Not using a UnitIndexer and using hashtables instead was a design desicion as its not worth to add a dependecy for a basically non-measurable speed gain. Every dependency and every complication of the system has to be justified by significant benefits. If it doesn't make any difference under real world conditions, its worthless.

Not to mention that I need more features out of his DDS like ignoring damage event for easier and more elegant damage blocking.

set PDDS.amount = 0 // not elegant enough?

Then there comes my DDS, for easier human life. :) I'm not really hoping this to be approved, but if I have to make a spell with DDS, I will absolutely use mine. Not for promotion, but my DDS has eliminated all of those nightmares that has been surrounding me for years.

Really, can you give a concrete example for that? I don't really see a problem with the existing DDS neither does yours do anything significantly different?
 
Level 10
Joined
Sep 19, 2011
Messages
527
yes, hashtable vs arrays performance isn't worth to talk about it.

lfh, the only con that i found in your library is the life stuff (since you have to use your custom functions to retrieve unit's correct life). is there a reason why you didn't update the resource so no custom function are required? (other than because it already works).
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
There's a lot of misunderstanding in this thread.

* LFH's system is complicated for 2 reasons: 1: he has a lot of control flow checking to prevent some native abilities like locust swarm from breaking. 2: he uses native hashtables to reduce dependency load and not err in favor of any one Table implementation.

* Nestharus' system is complicated because he has optimized it for asymptotic worst-case scenarios (with 1,000 units and 100 damage handlers it will outperform any other DDS)

* My system lacks features because I decoupled damage type tracking from damage detection. See http://www.hiveworkshop.com/forums/...tem-damagetype-structureddd-extension-228883/

I haven't read your code but I don't doubt this could be worthwhile. There are so many factors in choosing a library. My system will never be GUI-friendly, and equally, I will never use a GUI-friendly system. Nestharus will never make a system with less than perfrect time-complexity, and I will never use a unit indexer. These differences alone are enough to have three actively maintained damage detection systems here. Is it really so surprising there could be a fourth?

Side note: As far as I know, the last time those damage detection systems were benchmarked in any capacity was here: http://www.hiveworkshop.com/forums/2335240-post52.html and this was using a crude FPS-based method. Now that we have accurate stopwatch in SharpCraft, (http://www.hiveworkshop.com/forums/lab-715/jass-benchmarking-results-245362/), we should really get some new statistics.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
lfh, the only con that i found in your library is the life stuff (since you have to use your custom functions to retrieve unit's correct life). is there a reason why you didn't update the resource so no custom function are required? (other than because it already works).

Maybe I will update this someday, at the moment I'm too busy with the new TESH...

1: he has a lot of control flow checking to prevent some native abilities like locust swarm from breaking.

Actually locust swarm has only to be adopted once in the object editor, the control flow doesn't check for that.

Is it really so surprising there could be a fourth?

Not at all, actually it would be good (more fine-granular choices for end-users).

There is even something yet to be coded in DDSs, namely real after damage events... that would be real nice actually (and justify a new DDS without any discussions).
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
JASS:
library AfterDamageEvent requires StructuredDD
/*
    StructuredDD - [url]http://www.hiveworkshop.com/forums/jass-resources-412/system-structureddd-structured-damage-detection-216968/[/url]
*/

private module M
    method onInit takes nothing returns nothing
        set t = CreateTrigger()
        call StructuredDD.addHandler(thistype.exec)
    endmethod
endmodule

private struct S
    static trigger t
    private static method realExec takes nothing returns nothing
        call TriggerEvaluate(t)
        call DestroyTimer(GetExpiredTimer())
    endmethod

    private static method exec takes nothing returns nothing
        call TimerStart(CreateTimer(), false, 0, realExec)
    endmethod
    
    implement M
endstruct

function AddAfterDamageEvent takes code toRun returns nothing
    call TriggerAddCondition(S.t, Filter(toRun))
endfunction

endlibrary

Do I get a medal? :D
 

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
I don't really encourage it, but you can assert handler priority like this:

JASS:
library Important initializer init
	private function h takes nothing returns nothing
		// will be exeucted first
	endfunction

	private function init takes nothing returns nothing
		call StructuredDD.addHandler(function h)
	endfunction
endlibrary

library Deferred initializer init requires Important
	private function h takes nothing returns nothing
		// will be executed last
	endfunction

	private function init takes nothing returns nothing
		call StructuredDD.addHandler(function h)
	endfunction
endlibrary
 
Top