• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] [System] Physical Damage Detection

Level 14
Joined
Dec 12, 2012
Messages
1,007
Detection of Physical and Spell Damage

> GUI Version available here <

1. Introduction and motivation

I remember the problem of how to detect physical damage when I stopped mapping years ago. Back then there was no real way to achieve this, and so it is still said today. Although I found some real awesome systems for damage detection after starting mapping again, none of them is really able to detect if the damage was physical or dealt by a spell. There have been attempts with the frost-orb ability which causes unfortunately some bugs, especially with the attack ground command. There was also an attempt with a giant dummy unit which gets damaged by splash (so you have to make all physical damage to splash), but the system also had quite much problems and was never really finished.

But I really need such a system for the things that I want to accomplish on my new map and as I want to use all off the standard melee spells, retriggering all spells is no option. But after weeks of thinking, trying, failing and programming, I think I found an universal solution to the problem.


2. Basic Idea

The idea of the system is quite simple. Every unit on the map gets a modified ability of runed bracers with 200% damage reduction from spells. Because the damage reduction is bigger than 100%, all spell damage is negativ. Now by simply checking the sign of the damage, it is possible to identify if the damage was physical of not.

More complicated was to actually deal the damage on the target again, so that the heal of the negative damage gets denied. This is the opposite problem of damage-blocking: The unit has to get damaged first and later gets healed automatically. This was problematic if the damage would be mortal. Anyway I solved it, so the damage gets allways dealt correctly. For more details, see section 5.


3. Implementation

The implementation is quite easy, just follow these steps:

  • Copy this trigger to your map. With the AddDamageHandler function you can add as many handlers as you like (compare the OnDamage scope).
  • Copy the two custom abilities to your map and make sure they have the correct ID in the globals variable block.
  • Go to the locust swarm ability and invert its damage return portion from (default) 0.75 to -0.75.
  • 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 globals block, modifying BRACERS_SPELL_DAMAGE_REDUCTION.

Remarks:
  • The fact that you deleted the ability of the standard runed bracers does not mean you can't use runed bracers in your map. They are allready considered in the system, so they work just as they do in the normal game. If you want to customize its values, don't change them in the object editor but in the code with the globals variable BRACERS_SPELL_DAMAGE_REDUCTION.
  • LifeDrain does not work with this system, as the caster doesn't get healed. So if you want to use LifeDrain in your map, you should use a triggered version of this spell.
  • Finger of Death should also be triggered, as units don't explode when killed by this spell.

4. Screenshots

Enclosed are two screenshots of the testmap. You can see the damage detection registering the damage events and identifying the correct damage type by coloring the displayed amount of damage either red (physical) or blue (spell).

wc3scrnshot010913115934.jpg


wc3scrnshot010913115957.jpg


5. Code

The following code shows the Implementation of the system on my test map. To achieve an after damage event, which is needed here, a 0.0 second timer is used.


JASS:
library DamageEvent/* v 1.3.0.0
**********************************************************************************
*
*   Physical Damage Detection Engine
*   --------------------------------
*   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.
*
**********************************************************************************
*
*   Implementation
*   --------------
*   1.  Copy this trigger to your map. With the AddDamageHandler function you can 
*       add as many handlers as you like (compare the OnDamage scope).
*   2.  Copy the two custom abilities to your map and make sure they have the
*       correct ID in the globals variable block.
*   3.  Go to the locust swarm ability and invert its damage return portion
*       from (default) 0.75 to -0.75.
*   4.  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 globals block, modifying 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
*   ----------
*   unit PDDS.target
*       - In this global unit variable, the damaged unit is saved. Don't write any-
*         thing into this variable, use it as readonly.
*
*   unit PDDS.source
*       - In this global unit variable, the damage source is saved. Don't write any-
*         thing into this variable, use it as readonly.
*
*   real PDDS.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 PDDS.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 UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, 
*                                     boolean attack, boolean ranged, attacktype localAttackType, 
*                                     damagetype localDamageType, weapontype localWeaponType 
*                                     returns boolean
*       - Use this function to allocate attacks from a running damage event. You can use
*         it in exactly the same way as the standard native UnitDamageTarget. The function
*         is designed recursive, so you can allocate as many attacks as you want. Do not 
*         use this function outside of a damage event.
*
*   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. Compare the
*         OnDamage scope for an example usage.
*
*   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.
*
*********************************************************************************/
    
    globals
        /*************************************************************************
        *   Customizable globals
        *************************************************************************/
        
        private constant integer DAMAGE_TYPE_DETECTOR = 'A000'
        private constant integer SET_MAX_LIFE = 'A001'
        private constant integer SPELL_DAMAGE_REDUCTION_ITEM = 'brac'
        
        private constant boolean USE_SPELL_RESISTANCE_AUTO_DETECT = false
        private constant real ETHEREAL_DAMAGE_FACTOR = 1.66
        private constant real BRACERS_SPELL_DAMAGE_REDUCTION = 0.33
        private constant real TRIGGER_CLEANUP_PERIOD = 10.0
        
        /*************************************************************************
        *   End of Customizable globals
        *************************************************************************/
        
        constant integer PHYSICAL = 0
        constant integer SPELL = 1
        constant integer CODE = 2
        constant real UNIT_MIN_LIFE = 0.406
        
        private constant attacktype ATTACK_TYPE_UNIVERSAL = ConvertAttackType(7)
        private hashtable h
        private real pureAmount
        private trigger damageEvent
        private trigger damageHandler
        private trigger runAllocatedAttacks
        private integer allocatedAttacks 
        private integer totalAllocs
        private integer allocCounter
		private real damageEventTrigger        
    endglobals
    
    struct PDDS extends array
		static unit source
		static unit target
		static real amount
		static integer damageType
	endstruct
	
    /******************************************************************************
    *   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(h, id, index) == null )
            set index = index + 1
        endloop
        
        // Store the desired damage handler function
        call SaveTriggerConditionHandle(h, id, index, TriggerAddCondition(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(h, id, index) == null )
            call TriggerRemoveCondition(damageHandler, LoadTriggerConditionHandle(h, id, index))
            set index = index + 1
        endloop
        
        // Clean things up
        call FlushChildHashtable(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(h, uId, 2)
        local real storedDamage = LoadReal(h, uId, 1)
        
        // Check if the unit is being rescued from damage
        set duringModification = GetUnitAbilityLevel(u, SET_MAX_LIFE) > 0
        if duringModification then
            call UnitRemoveAbility(u, 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, 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, SET_MAX_LIFE) > 0 then
            call UnitRemoveAbility(u, SET_MAX_LIFE)
            set maxHealth = GetUnitState(u, UNIT_STATE_MAX_LIFE)
            call UnitAddAbility(u, 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, SET_MAX_LIFE) > 0 then
            call UnitRemoveAbility(u, SET_MAX_LIFE)
            call SetWidgetLife(u, newLife)
            call UnitAddAbility(u, SET_MAX_LIFE)
            
            // Get the unit specific timer information
            set targetId = GetHandleId(u)
            set oldHealth = LoadReal(h, targetId, 0)
            
            // Update the unit specific timer information
            if oldHealth != 0.0 then
                set oldTimerId = LoadInteger(h, targetId, 3)
                call SaveReal(h, targetId, 2, newLife)
                call SaveReal(h, targetId, 0, newLife)
                call SaveReal(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 PDDS.damageType == CODE then
            return false
        endif
        
        // Avoid allocating attacks on units that are about to be killed
        if ( localTarget == PDDS.target and GetUnitLife(localTarget) - PDDS.amount < UNIT_MIN_LIFE ) then
            return false
        endif
        
        // Store all damage parameters determined by the user
        set allocatedAttacks = allocatedAttacks + 1
        call SaveUnitHandle(h, allocatedAttacks, 0, localSource)
        call SaveUnitHandle(h, allocatedAttacks, 1, localTarget)
        call SaveReal(h, allocatedAttacks, 2, localAmount)
        call SaveBoolean(h, allocatedAttacks, 3, attack)
        call SaveBoolean(h, allocatedAttacks, 4, ranged)
        call SaveInteger(h, allocatedAttacks, 5, GetHandleId(localAttackType))
        call SaveInteger(h, allocatedAttacks, 6, GetHandleId(localDamageType))
        call SaveInteger(h, allocatedAttacks, 7, GetHandleId(localWeaponType))

        // Return true if the damage was allocated
        return true
    endfunction
    
    
    /******************************************************************************
    *   Sub functions
    ******************************************************************************/
    
    private 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 >= 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, ATTACK_TYPE_UNIVERSAL, DAMAGE_TYPE_UNIVERSAL, null)
            else
                call UnitRemoveAbility(target, 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

    private 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(u, DAMAGE_TYPE_DETECTOR)
        set originalHP = GetWidgetLife(u)
        call UnitAddAbility(u, SET_MAX_LIFE)
        call SetWidgetLife(u, 20000.0)
        set beforeHP = GetWidgetLife(u)
        call DisableTrigger(damageEvent)
        call UnitDamageTarget(PDDS.source, u, DUMMY_DAMAGE, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
        call EnableTrigger(damageEvent)
        set afterHP = GetWidgetLife(u)
        call UnitRemoveAbility(u, SET_MAX_LIFE)
        call SetWidgetLife(u, originalHP)
        call UnitAddAbility(u, DAMAGE_TYPE_DETECTOR)
        call UnitMakeAbilityPermanent(u, true, 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

    private 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
				set indexItem = null
                return index + 1
            endif

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

    /******************************************************************************
    *   Damage Engine
    ******************************************************************************/

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

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

        // Apply the desired amount of damage
        if amount > 0.0 then
            call DisableTrigger(damageEvent)
            call DealFixDamage(localSource, localTarget, amount)
            call EnableTrigger(damageEvent)
        else
            call SetWidgetLife(localTarget, originalHealth - amount)
        endif
        
        // Clean things up
        call FlushChildHashtable(h, id)
        call FlushChildHashtable(h, GetHandleId(localTarget))
        call DestroyTimer(time)
        set time = null
        set localSource = null
        set localTarget = null
    endfunction
    
    private function DamageEngine takes nothing returns nothing
        local timer time
        local integer id
        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 rawAmount = GetEventDamage()
		if rawAmount == 0.0 then
			return
		endif
		set PDDS.source = GetEventDamageSource()
        set PDDS.target = GetTriggerUnit()
        
        // Determine the damage type
        if rawAmount > 0.0 then
			if PDDS.damageType != CODE then
				set PDDS.damageType = PHYSICAL
			endif
			set PDDS.amount = rawAmount
        else
			if PDDS.damageType != CODE then
				set PDDS.damageType = SPELL
			endif
			set PDDS.amount = -rawAmount
        endif
        
        // Register spell reduction above 100%
        static if USE_SPELL_RESISTANCE_AUTO_DETECT then
            set PDDS.amount = GetUnitSpellResistance(PDDS.target)*PDDS.amount
        else
            if ( IsUnitType(PDDS.target, UNIT_TYPE_ETHEREAL) and IsUnitEnemy(PDDS.target, GetOwningPlayer(PDDS.source)) and rawAmount < 0.0 ) then
                set PDDS.amount = ETHEREAL_DAMAGE_FACTOR*PDDS.amount
            endif
        endif
        
        // Register spell damage reducing items like runed bracers
        if ( IsUnitType(PDDS.target, UNIT_TYPE_HERO) and UnitHasItemOfType(PDDS.target, SPELL_DAMAGE_REDUCTION_ITEM) > 0 ) and rawAmount < 0.0 then
            set PDDS.amount = (1 - BRACERS_SPELL_DAMAGE_REDUCTION)*PDDS.amount
        endif
        
        // Save health and damage variables
        if PDDS.damageType != CODE then
            call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
        endif
        set pureAmount = PDDS.amount
        set originalHealth = GetWidgetLife(PDDS.target)
        set oldTimerId = 0
        
        // Call damage handlers
		set damageEventTrigger = 0.0
        set damageEventTrigger = 1.0
        set damageEventTrigger = 0.0

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

        // Call after damage event if damage was spell, modified, code or parallel 
        if ( rawAmount < 0.0 or pureAmount != PDDS.amount or oldTimerId != 0 or allocatedAttacks > 0 ) then
            set time = CreateTimer()
            set id = GetHandleId(time)
            
            // Save handles for after damage event
            call SaveUnitHandle(h, id, 0, PDDS.source)
            call SaveUnitHandle(h, id, 1, PDDS.target)
            call SaveReal(h, id, 2, pureAmount)
            call SaveReal(h, id, 3, PDDS.amount)
            call SaveReal(h, id, 4, originalHealth)

            // Save this extra to manage parallel damage instances
            call SaveReal(h, targetId, 0, GetWidgetLife(PDDS.target))
            call SaveReal(h, targetId, 1, PDDS.amount)
            call SaveReal(h, targetId, 2, originalHealth)
            call SaveInteger(h, targetId, 3, id)

            // Avoid healing of negative spell damage
            if rawAmount < 0.0 then
                call DisableTrigger(damageEvent)
                call DealFixDamage(PDDS.source, PDDS.target, -rawAmount)
                if  ( originalHealth - PDDS.amount < UNIT_MIN_LIFE ) then
                    call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
                    call DealFixDamage(PDDS.source, PDDS.target, PDDS.amount)
                endif
                call EnableTrigger(damageEvent)
            endif
            
            // Guarantee unit exploding
            if originalHealth - PDDS.amount < UNIT_MIN_LIFE then
                if rawAmount > 0.0 then
                    call UnitRemoveAbility(PDDS.target, SET_MAX_LIFE)
                    call SetWidgetLife(PDDS.target, 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 totalAllocs == 0 then
            set totalAllocs = allocatedAttacks
        endif
        if allocatedAttacks > 0 then
            set allocatedAttacks = allocatedAttacks - 1
            set allocCounter = allocCounter + 1
            call TriggerEvaluate(runAllocatedAttacks)
        endif

        // Reset all required variables
        set totalAllocs = 0
        set allocCounter = -1
        set PDDS.damageType = -1
    endfunction
    
    
    /******************************************************************************
    *   Initialization
    ******************************************************************************/
    
    private function RestoreTriggers takes nothing returns nothing
        local unit enumUnit = GetEnumUnit()
    
        // Re-register units that are alive
        if GetUnitTypeId(enumUnit) != 0 then
            call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
        endif
        set enumUnit = null
    endfunction

    private function ClearMemory_Actions takes nothing returns nothing
        local group g = CreateGroup()
        local code c = function DamageEngine
        
        // Reset the damage event
        call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
        call ResetTrigger(damageEvent)
        call DestroyTrigger(damageEvent)
        set damageEvent = null
       
        // Rebuild it then
        set damageEvent = CreateTrigger()
        call TriggerAddCondition(damageEvent, Filter(c))
        call ForGroup(g, function RestoreTriggers)
        
        // Clean things up
        call DestroyGroup(g)
        set g = null
        set c = null
    endfunction
    
    private function MapInit takes nothing returns nothing
        local unit enumUnit = GetEnumUnit()
    
        // Register units on map initialization
        call UnitAddAbility(enumUnit, DAMAGE_TYPE_DETECTOR)
        call UnitMakeAbilityPermanent(enumUnit, true, DAMAGE_TYPE_DETECTOR)
        call TriggerRegisterUnitEvent(damageEvent, enumUnit, EVENT_UNIT_DAMAGED)
        set enumUnit = null
    endfunction
    
    private function UnitEntersMap takes nothing returns nothing
        local unit triggerUnit = GetTriggerUnit()
    
        // Register units that enter the map
        if ( GetUnitAbilityLevel(triggerUnit, DAMAGE_TYPE_DETECTOR) < 1 ) then
            call UnitAddAbility(triggerUnit, DAMAGE_TYPE_DETECTOR)
            call UnitMakeAbilityPermanent(triggerUnit, true, DAMAGE_TYPE_DETECTOR)
            call TriggerRegisterUnitEvent(damageEvent, triggerUnit, EVENT_UNIT_DAMAGED)
        endif
        set triggerUnit = null
    endfunction
    
    private function RunAllocatedAttacks takes nothing returns nothing
        local integer localAllocAttacks = allocatedAttacks + 1

        // Calculate the correct sequence of allocated attacks
        set localAllocAttacks = localAllocAttacks - totalAllocs + 1 + 2*allocCounter
        
        // Deal code damage if the unit isn't exploding
        set PDDS.damageType = CODE
        if GetUnitLife(LoadUnitHandle(h, localAllocAttacks, 1)) >= UNIT_MIN_LIFE then
            call UnitDamageTarget(LoadUnitHandle(h, localAllocAttacks, 0), LoadUnitHandle(h, localAllocAttacks, 1), /*
                                */LoadReal(h, localAllocAttacks, 2), LoadBoolean(h, localAllocAttacks, 3), /*
                                */LoadBoolean(h, localAllocAttacks, 4), ConvertAttackType(LoadInteger(h, /*
                                */localAllocAttacks, 5)), ConvertDamageType(LoadInteger(h, localAllocAttacks, 6)), /*
                                */ConvertWeaponType(LoadInteger(h, localAllocAttacks, 7)))
        else
            call FlushChildHashtable(h, localAllocAttacks - 1)
        endif
        
        // Clean things up
        call FlushChildHashtable(h, localAllocAttacks)
    endfunction
    
    private module Inits
        private static method onInit 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 variables
            set h = InitHashtable()
            set damageEvent = CreateTrigger()
            set damageHandler = CreateTrigger()
            set PDDS.damageType = -1
            set allocatedAttacks = 0
            set runAllocatedAttacks = CreateTrigger()
            set totalAllocs = 0
            set allocCounter = -1
            
            // Register units on map initialization
            call TriggerRegisterVariableEvent(damageHandler, SCOPE_PRIVATE + "damageEventTrigger", EQUAL, 1.0)
            call TriggerAddCondition(damageEvent, Filter(cDamageEngine))   
            call GroupEnumUnitsInRect(g, GetWorldBounds(), null)
            call ForGroup(g, function MapInit)
            
            // Register units that enter the map
            call RegionAddRect(r, GetWorldBounds())
            call TriggerRegisterEnterRegion(UnitEnters, r, null)
            call TriggerAddCondition(UnitEnters, Filter(cUnitEnters))
                    
            // Register trigger for allocated attacks
            call TriggerAddCondition(runAllocatedAttacks, Filter(cRunAllocatedAttacks))
            
            // Clear memory leaks
            call TriggerRegisterTimerEvent(ClearMemory, 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
        endmethod
    endmodule
    
    private struct Init extends array
        implement Inits
    endstruct
endlibrary



unit PDDS.target
In this global unit variable, the damaged unit is saved. Don't write anything into this variable, use it as readonly.

unit PDDS.source
In this global unit variable, the damage source is saved. Don't write anything into this variable, use it as readonly.

real PDDS.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 PDDS.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 UnitDamageTargetEx takes unit localSource, unit localTarget, real localAmount, boolean attack, boolean ranged, attacktype localAttackType, damagetype localDamageType, weapontype localWeaponType returns boolean
Use this function to allocate attacks from a running damage event. You can use it in exactly the same way as the standard native UnitDamageTarget. The function is designed recursive, so you can allocate as many attacks as you want. Do not use this function outside of a damage event.

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. Compare the OnDamage scope for an example usage.

function RemoveDamageHandler takes code damageHandler returns nothing
Allows you to remove a damage handler function from the system dynamically. If you added the same handler function multiple times to the system, this function will remove all of these equal functions.


Have fun and feel free to test/comment!


Greetings,
looking_for help


UPDATE Version 0.9:
- Fixed a bug with the carrion swarm ability
- Updated the documentation due to this
- Replaced some BJs (still have to learn quite much about WE though^^)
- Added a fixed testmap

UPDATE Version 0.9a:
- Fixed bugs with damage tables above 100% spell damage
- Fixed bugs with ethereal units
- Implemented a custom version of Life Drain to fix LifeDrain Bug
- Merged everything into one trigger and to vJASS
- Replaced all BJs with natives
- Updated the documentation


UPDATE Version 0.9.9.1:
- Implemented damage modifiers that allow to block, reduce, increase or invert damage
- Fixed bugs with attacks that share the same instance
- Fixed rounding errors with very high spell damages
- Removed all memory leaks
- Fixed a bug with locust swarm ability
- Added additional support for customized damage tables with spell damage above 100%
- Updated the documentation and added a new testmap

UPDATE Version 1.0.0.0:
- Completly reworked the whole system
- Increased performance dramatically
- Added a GUI Version of the system
- Implemented a function to allocate damage events from already running damage events
- Allocating damage events is implemented recursive, so users can allocate as many events as they want
- Implemented a function to dynamically remove damage handlers from the system
- Implemented a function to get a units life always correct, even when damage modifiers are used
- Implemented a function to get a units max life always correct, even when damage modifiers are used
- Implemented a function to set a units life always correct, even when damage modifiers are used
- Changed the way events are triggered and fixed a bug with this
- Moved from trigger actions to trigger conditions
- Added optional support for the TriggerRefresh library by Nestharus
- Reworked the system API, users can now set the damage amount directly without a function call
- Added documentation for all user-relevant functions
- Narrowed the window for the rescue ability to get applied
- Fixed bugs with negative damage events that share the same instance
- Fixed a bug with mortal spell and physical damage that run in the same instance
- Fixed a bug that could occure with zero damage events
- Proved bug-freeness for parallel damage events by brute forcing through all possibilities
- Updated the documentation and added a new testmap


UPDATE Version 1.0.0.1:
- Fixed a minor bug with allocated attacks

UPDATE Version 1.0.0.2:
- Fixed a minor bug with the in-built bracer item
- Now using a module initializer to fix bugs with damage on map init

UPDATE Version 1.1.0.0:
- Fixed a bug that made functions like GetWidgetLife return invalid values after killing a unit with spell damage

UPDATE Version 1.1.1.0:
- Removed two unused local variables
- Removed optional support for TriggerRefresh

UPDATE to Version 1.2.0.0
- Fixed a bug that sometimes wront target and source units were asigned
- Made all variables private that should be (damageEventTrigger variable now uses SCOPE_PRIVATE for that).
- System variables like target and source are now summarized in one struct and can be accessed by typing PDDS.target or PDDS.amount to avoid naming conflicts.

UPDATE to Version 1.3.0.0
- Fixed a bug that revived units were not re-registered by the system under certain circumstances
- Added the possibility to deal damage in an onDamage event before the original damage executes
- Fixed a minor memory leak
 

Attachments

  • WC3_screenshot1.jpg
    WC3_screenshot1.jpg
    526.7 KB · Views: 967
  • WC3_screenshot2.jpg
    WC3_screenshot2.jpg
    550.7 KB · Views: 901
  • Physical Damage Detection v1.3.0.0.w3x
    36.8 KB · Views: 540
Last edited:
It's too bad that the system was executed so poorly : \

in its current state, i can't see it getting approved

yes, great technique for detecting physical damage, but very poor and naive system design

edit
you should probably just extend off of DamageEvent as you are likely not going to be able to make a DDS that'll be able to compete with that. I would personally just make a damage type integer, like DAMAGE_TYPE_PHYSICAL, DAMAGE_TYPE_SPELL, and DAMAGE_TYPE_CODE

if you don't want to, your only option is to make a core DDS that is as good as DamageEvent that also includes all of your other bells and whistles. This will require some coding prowess. If you can't do that, then there is no chance of getting this approved as it is crap compared to currently approved resources : \.

edit
as I see that you want to use pure JASS, you'll have to code a good DDS in pure JASS... good luck with that

you'll need knowledge of a variety of data structures
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Could you please explain why exactly it is "crap" if it fullfills its job very good?

I will make the needed changes to improve the system, but you have to be a "little" bit more specific what is so bad about the system.

you should probably just extend off of DamageEvent as you are likely not going to be able to make a DDS that'll be able to compete with that.

What do you mean? I don't understand the sentence.

yes, great technique for detecting physical damage, but very poor and naive system design

Why? Please explain, I want to learn.
 
Could you please explain why exactly it is "crap" if it fullfills its job very good?

Because it fulfills its job in a horrible and naive manner?

I will make the needed changes to improve the system, but you have to be a "little" bit more specific what is so bad about the system.

98% of the code, that specific enough? : p

I'm not talking little improvements, i'm talking erase all ur code and start over

What do you mean? I don't understand the sentence.

It's better to use the existing DamageEvent resource in JASS section than to attempt to write your own, because chances are that anything you write won't be able to compete with it.


Here are the specifics on what you need to support
JASS:
/************************************************************************************
*
*   SETTINGS
*/
globals
    /*************************************************************************************
    *
    *   How many units can refresh at a given moment (when a trigger is rebuilt).
    *   larger size means less triggers but harder refreshes.
    *
    *************************************************************************************/
    private constant integer TRIGGER_SIZE = 80
endglobals

/*       static constant PriorityEvent ANY

*       static boolean enabled

*       readonly static real amount //damage amount

*       readonly static real life
*           -   The unit's current life.
*       static real modifiedAmount
*           -   This is the current damage that is going to be applied to the unit.
*/

Ofc, when you register a new unit, it should be added to the trigger with the least amount of units on it. When the trigger reaches a certain off balance (lots of destroyed units and few existing units), the trigger should be rebuilt.

* static boolean enabled

this'll enable or disable the system

/* static constant PriorityEvent ANY

This runs events in priority. Events aren't run in order that they are registered, they are run in order of priority. If you have a high priority event, that will run first.

And I'll let you know, to do the refreshes efficiently on the triggers is going to require quite a lot of code and a binary heap

if you have no idea what a binary heap is, then you'll have to learn it if you want to code your own DDS

edit
you will ofc have to detect when a unit goes out of scope as well, which goes into the territory of unit indexing

I estimate that this system will be around 800 lines of code when completed, and that's if you do a good job

edit
the big reasons edo494's new resource was shut down was because it was a pack and contained a poorly written DDS as well, so he's now having to move to the currently written DamageEvent

edit
I'd also be willing to support this as an extension to DamageEvent to improve performance (cut down on firing triggers)

I'd give you the macro to write in and then you can write whatever code you like in it =). This will make your job much easier ^_^

This is ofc entirely up to you

edit
btw, for registering triggers, it must be done in O(1). O(1) on the registration and O(1) on the update. A Binary Heap, given that the triggers only ever change by 1, will do these things in O(1).
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Because it fulfills its job in a horrible and naive manner?

Can you calm down and discuss the issue in an objectiv and professional manner? I didn't know it is neccessary to insult people who solve a problem just because their programming is not yet perfect.

98% of the code, that specific enough? : p

No, not really.

As I mentioned, the system still contains problems (like small memory leaks) and that I may need help in making it perfect.
 
No, essentially everything except your technique in the code is bad... hence why I said erase everything and start over

Dude, I've coded all of this before, I know what kind of code to expect in a DDS, and I know the different ways to do a DDS. You do naive approach #1, complete with memory leaks. The correct approach requires a binary heap and A LOT of code.

Seriously, I know what I'm talking about

edit
the problem isn't just memory leaks.. your entire design is bad >.<

edit
you don't even do naive approach #1, you do nothing, lol....

the difficulty of a DDS is the trigger refreshing

edit
weep's simple damage event for GUI people does naive approach #2, which still isn't good enough for the JASS section =)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Dude, I've coded all of this before, I know what kind of code to expect in a DDS, and I know the different ways to do a DDS. You do naive approach #1, complete with memory leaks. The correct approach requires a binary heap and A LOT of code.

Well, thats your opinion. I will wait for some more opinions from other people.

There is also the http://www.hiveworkshop.com/forums/spells-569/gui-friendly-damage-detection-v1-2-1-a-149098/?prev=Weep by Weep, and it is considered very good and approved.

And it doesn't contain "800 lines of code" but fullfills its job very good.
 
Well, thats your opinion. I will wait for some more opinions from other people.

I'm just telling you how it is

In it's current state, it has absolutely no chance of getting approved

if you do it like Weep's, it still has no chance of getting approved

if it can't compete with currently approved systems, there is no reason to approve it

current approved systems set the bar, and if the new stuff can't meet that bar, the new stuff doesn't make it : \

if the new stuff exceeds the bar, then the currently approved stuff gets replaced by the new stuff

you can argue with me all you like, but that doesn't change the fact that this'll never get approved in its current state and that if you try to make your own DDS, you are 99.99% likely not going to get it approved

edit
and if you are super stubborn about this, someone else is just going to take your exact idea and do it in an approveable format, which has happened in the past
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
if it can't compete with currently approved systems, there is no reason to approve it

There is no system that can detect physical and spell damage seperatly, so you can't compare them, as they fullfill completly different things. There is no "competition" at all, as a competition requires two. But actually, let an approver decide this.

The system is meant to be intuitiv, easy to understand and to be implemented without requiring tons of libraries.

and if you are super stubborn about this...

Well, I will do the required changes if more people agree with you. I see absolutley no reason why you are allways getting personal and insulting.
Right now, I am just waiting for more feedback and opinions.

So manner up, plz.
 
to be implemented without requiring tons of libraries

yea, that's one thing that actually hurts your system in the JASS section and makes it less likely to get approved

But actually, let an approver decide this.

they are going to agree with me on this... this has been the policy in the JASS section for like 2 years now.

There is no system that can detect physical and spell damage seperatly, so you can't compare them, as the fullfill completly different things.

and if you are super stubborn about this, someone else is just going to take your exact idea and do it in an approveable format, which has happened in the past

your point is pointless, as someone is just going to redo this in an approveable state, so you either do it, or someone else does it (you'll still get credit for the technique in the approved one ofc)

Well, I will do the required changes if more people agree with you. I see absolutley no reason why you are allways getting personal and insulting.
Right now, I am just waiting for more feedback and opinions.

the opinions won't matter

I'll make it very simple for you

---- if this can't make the bar, this can't get approved

end of story

comparing resources from Spells section is pointless as the bar in JASS section is based on the JASS section resources, not the Spell section resources

if you want to get it approved in its current state, you are better off trying the Spells section (although you will likely get a low rating, possibly 0 like Weep's)

edit
similar case to yours (recent)

http://www.hiveworkshop.com/forums/2256035-post35.html

edit
btw, I'm not arguing against your special technique for detecting physical damage, that's great

However, the entire DDS portion of this system is likely never going to get approved, which is why I'm saying to do your technique with an existing DDS

refer to mag's post (the resource moderator for JASS section) as to why

I say likely because unlike the person in that scenario, you actually have a new feature. Therefore, if you absolutely perfect this, then you'll have a clone + 1 new feature, which'll get this approved.

Stuff has also been approved that has done the exact same stuff as approved stuff, just in different ways (meaning different strengths and weaknesses)

If a piece of your resource is clearly weak compared to an existing resource, then you gotta use the existing resource and rip out that piece of yours (in this case, the DDS part), otherwise your thing'll never get approved

you can argue rules, you can cry about it, you can say I'm mean, you can beg for other opinions, but as I told you, this has been the policy of the JASS section for the pass 2 years, and I even provided a link to a post from the resource moderator of this section as proof

edit
and btw, if you look up save/load systems in the Spells section, those were all cleared out and replaced by 1 save/load system as that 1 save/load system made them all ridiculously obsolete >.<. Even the GUI friendly ones written in pure JASS were removed as they used very poor methods to do save/load.

We can argue about this all day, but I have a lot of examples to prove my point
 
Last edited:
Level 25
Joined
Jul 10, 2006
Messages
3,315
lfh, I'm sorry but I have to side with nes on this one.

You definitely have a clever approach here, but reinventing the wheel is a bad idea.

Just make it as an extension to DamageEvent, or even simpler go look up Bribe's GUI Damage Engine and make it work with that.

Don't take Nestharus' posts offensively, he has a lot of experience in this subject.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Don't take Nestharus' posts offensively, he has a lot of experience in this subject.

Its ok, I know he has lots of experience with this stuff ;)

When researching for systems that can differ between physical and spell damage I obviously found his attempts with the frost orb and the other stuff. I just take his posts offensively because they are written offensively. And in an effective and proffesional discussion there is absolutly no need for that.

This is also more a proof-of-concept. I didn't know that its not allowed to post that here in the JASS section, as one might notice I'm quite new to this forum. I just thought its the best way to publish the results and discuss about them.

I just read the rules by Bribe and as the proposed systems fullfills them I thought it is fine. Its not said there that you have to use existing systems.

Just make it as an extension to DamageEvent, or even simpler go look up Bribe's GUI Damage Engine and make it work with that.

I can try that, but how will this work? Do I have to ask Bribe or Nestharus for permission?
 
You know, I would totally recommend taking Nes' DamageEvent, compiling it with JassHelper, and taking the output JASS code.

Then you would optimize that code, and you have yourself a JASS version of a very efficient and well-written DDS.

What you would do then is make a post in Nestharus' DamageEvent thread containing this version and ask him to link your post on the first post of the thread.

Then, you would use this DDS as a base to create this Spell vs. Physical vs. Code Damage classifier.

The only problems you have are code cleanliness, the use of BJ functions (the ones in red) and the inefficiency of the DDS algorithm (which is why I'm recommending to just use a JASS version of Nestharus' DDS. It's approved because it's nearly flawless, more or less).

The technique is awesome.
Don't let it go to waste.

edit

The reason I'm asking everyone to use other people's resources is because I don't want this section to fall into the same fate that most Java code snippet collections fall into. Everyone does things their way and it all becomes a huge mess of resources that do the same thing in different ways instead of 1 dominating resource that everything is interconnected with and a ton of other branches to this tree of interconnected resources.

The best UnitIndexer a person could use in this forum is Nestharus' UnitIndexer because so many other systems use it and depend on it. By using it, you are associating your system with every other system coded that supports it, allowing people to implement your system easily without having to change the method of unit indexing/deindexing depended on. On thehelper.net, you would use AIDS by Jesus4Lyf because that's what everyone's using when they post resources on the site.
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,220
I can see that this is a bad way of creating such a systm without reading the actually code. The main thing with jass stuff is that they only need one trigger they can put everything inside one single trigger while you use 4. hm, and I also find a BJ that is not liked in jass. oh and you dont need the function that creates the event + actions + conditions.

since im new too jass I only count as a half person
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Well ok, I will try to use Nestharus DDS then. Is it this one? Because that doesn't look like the 800 lines of code he mentioned. Also, isn't there a test map where one could try out that system? I couldn't find one...

Magtheridon96 said:
The reason I'm asking everyone to use other people's resources is because I don't want this section to fall into the same fate that most Java code snippet collections fall into. Everyone does things their way and it all becomes a huge mess of resources that do the same thing in different ways instead of 1 dominating resource that everything is interconnected with and a ton of other branches to this tree of interconnected resources.

Ok, I understand.

Allthough I prefer the Java case, as most systems have their specific advantages and disadvantages and the user then can decide for himself which system he wants to use. I mean, efficiency is an abstract term. It can be applied to performance obviously, but also to things like understandability. But this goes into a philosophical direction and if this is the philosophy of the board, I fully respect it.

Magtheridon96 said:
Don't let it go to waste.

I won't, you can be sure about that ;)
It took me so much headache to figure that out, right now I'm just happy it works anyway. :D
 
Allthough I prefer the Java case, as most systems have their specific advantages and disadvantages and the user then can decide for himself which system he wants to use. I mean, efficiency is an abstract term. It can be applied to performance obviously, but also to things like understandability. But this goes into a philosophical direction and if this is the philosophy of the board, I fully respect it.

java case is allowed so long as you have your own strengths/weaknesses compared to the already approved resource

if everything about your resource is weak compared to the approved one, that's not exactly the java case ;p

edit
and I said that I'd willingly support this as an extension, which would make his job much easier as he wouldn't even really need to know vjass then

edit
put your code inside of this

JASS:
library GetDamageType uses DamageEvent

//! textmacro GET_DAMAGE_TYPE

struct DamageType extends array
    static constant integer PHYSICAL = 0
    static constant integer CODE = 1
    static constant integer SPELL = 2
endstruct

private function GetDamageType takes unit attacked, real damage returns integer
endfunction

private function FixDamage takes unit attacked, real damage returns real
endfunction

//! endtextmacro

endlibrary

IsDamagePhysical would be called followed by FixDamage
FixDamage would fix the damage, apply correct damage to unit, and then return the corrected damage amount
This is only called if GetDamageType returns DAMAGE_TYPE_SPELL

edit
DamageEvent already supports damage types, so your system will tie in very neatly with it

It also supports running trigger code, which can easily be determined

Your code should default return PHYSICAL (don't try to determine physical/code). If it returns PHYSICAL, then DamageEvent will check to see if code was run or not (damageType == 0)
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
java case is allowed so long as you have your own strengths/weaknesses compared to the already approved resource

if everything about your resource is weak compared to the approved one, that's not exactly the java case ;p

edit
and I said that I'd willingly support this as an extension, which would make his job much easier as he wouldn't even really need to know vjass then

Ok cool.

Then lets forget the differences and lets build an awesome system together. Unfortunatly I'm not so good at coding, so I might not see all the weaknesses of my system. Sorry for that, maybe caused by the euphory of finding a solution to this^^

I will start with it on friday, as I don't have much time today and tomorrow, but I will post the code then.

Greetings,
lfh
 
Already gave you template, all you have to do is put your technique into the functions and you are done =)

edit
btw, I'm as hard on other people as I am on myself. I take pride in being as blunt as possible and speaking my exact thoughts. If you'll look at some of my resources, if I see one tiny flaw in them that constitutes a complete rewrite, I will completely rewrite them. The big version # = full rewrite
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Already gave you template, all you have to do is put your technique into the functions and you are done =)

Hm, I don't fully understand how your DamageEvent works, thats the problem.

Right now I'm using this to make the dynamic trigger to detect when the heal took place (which happens right after the damage event).

JASS:
    call TriggerRegisterUnitLifeEvent( t, udg_damaged, GREATER_THAN, HP )
    call TriggerAddAction(t,function DealSpellDmg)

The FixDamage function would by my DealSpellDmg function, but afaik you can't use a function that requires parameters as a code parameter itself - so how should I give FixDamage any parameters like unit attacked or real damage?

Or do you have already implemented this with the PRIORITY of a damage?

I mean: Should I just detect the damage type and the amount of the damage to be fixed or do I also have to do the correction (dealing the damage) or is this done by your system?

lfh
 
All you have to use are the parameters passed into the function

The functions will be called at the appropriate times : )

So you don't need any triggers or anything, just the functions ;o

if damage < 0, then it was from a spell : )

edit
oh, you'll also need an OnEnter function to add the ability ;o

and you'll need to add a global to contain the ability id
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
All you have to use are the parameters passed into the function

The functions will be called at the appropriate times : )

So you don't need any triggers or anything, just the functions ;o

All right, now I got it :D

Then I would put it like this:

JASS:
library GetDamageType uses DamageEvent

//! textmacro GET_DAMAGE_TYPE

struct DamageType extends array
    static constant integer PHYSICAL = 0
    static constant integer SPELL = 1
    static constant integer CODE = 2
    static constant real HERO_SPELL_REDUCTION = (1/0.7)
    static constant real BRACERS_DAMAGE_REDUCTION = 0.67
endstruct

private function GetDamageType takes unit attacked, real damage returns integer

    // Determinating the correct DamageType
    if udg_triggerDamage == 1 then
        return CODE
    elseif damage < 0 then 
        return SPELL
    elseif damage == 0 and IsUnitIllusion(attacker) == true then
        return PHYSICAL
    elseif damage == 0 then
        return SPELL
    else
        return PHYSICAL
    endif

endfunction

private function FixDamage takes unit attacked, real damage returns real

    // Initialising the damage reduction of runed bracers
    local real bracersDmgReduction = 1.00

    // This is checking for runed bracers (this BJ is ugly to remove :o)
    if ( UnitHasItemOfTypeBJ(attacked,'brac') == true ) and ( IsUnitType(attacked, UNIT_TYPE_HERO) == true ) then
        set bracersDmgReduction = BRACERS_DAMAGE_REDUCTION
    endif

    // Determines the correct damage to apply on the attacked unit
    // Note: This damage must be applied two times: Once before the
    // unit is healed by the negativ damage event and once right after
    // the healing.
    if ( IsUnitType(attacked, UNIT_TYPE_HERO) == true ) then
        return HERO_SPELL_REDUCTION*bracersDmgReduction*(-damage)
    else
        return bracersDmgReduction*(-damage)
    endif

endfunction

//! endtextmacro

endlibrary
 
no... lol

#1, no BJs

and where did these values come from?
JASS:
globals
    private constant real HERO_SPELL_REDUCTION = (1/0.7)
    private constant real BRACERS_DAMAGE_REDUCTION = 0.67
endglobals

You can't depend on default armor values

no GUI variables, the only thing that you should be able to detect is whether it's a spell or not
if udg_triggerDamage == 1 then

edit
your thing will also bug on 0 damage spells and 0 damage fires

edit
IsUnitType(attacked, UNIT_TYPE_HERO) == true

to

IsUnitType(attacked, UNIT_TYPE_HERO)

etc

edit
this line is pointless

UnitHasItemOfTypeBJ(attacked,'brac')

you aren't considering that there will be custom items with spell damage reduction

in fact, I doubt any custom map will use that specific item, but will rather use custom ones

given this, I'm starting to see how this is actually impossible to do, as you won't know what items reduce spell damage

you could register said items with the system, but then that'd be extremely annoying and not really acceptable :\

I now move for a dismissal of this resource as the technique it uses is flawed : )

edit
very nice attempt though, it woulda been really cool if the spell damage reduction thing was actually possible to use ; D
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
you could register said items with the system, but then that'd be extremely annoying and not really acceptable :\

I now move for a dismissal of this resource as the technique it uses is flawed : )

edit
very nice attempt though, it woulda been really cool if the spell damage reduction thing was actually possible to use ; D

Lol are you joking? :D

It shouldn't really be a problem to add the few custom items-IDs to an integer array and then check them. Even if you have 20 different items for spell reduction its a work of 1 minute to add them.

But well, I will stay with my system then as it works perfectly. Anyway I think you are too much a theoretician for me ;)
 
Lol are you joking? :D

It shouldn't really be a problem to add the few custom items-IDs to an integer array and then check them. Even if you have 20 different items for spell reduction its a work of 1 minute to add them.

But well, I will stay with my system then as it works perfectly. Anyway I think you are too much a theoretician for me ;)

that's fine and dandy in a map, but not in a public resource =)
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
That would be no problem at all, you just would have to consider them in the code. As I said, a work of a few minutes. You can also let your custom bracers stack, you can do whatever you want.

@Nestharus: I'm surprised how you can call 98% of the system crap if you didn't even looked at how it really works^^
 
That would be no problem at all, you just would have to consider them in the code. As I said, a work of a few minutes. You can also let your custom bracers stack, you can do whatever you want.

@Nestharus: I'm surprised how you can call 98% of the system crap if you didn't even looked at how it really works^^

98% was being gentle ; )
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
edit
very nice attempt though, it woulda been really cool if the spell damage reduction thing was actually possible to use ; D

that's fine and dandy in a map, but not in a public resource =)

I see no reason why this shouldn't be allowed in a public resource. Again, there is nothing like this stated in the rules. Then please explain me what these ruled were made for. It also doesn't make really a difference if I customize the values in the object editor or in the code. Its quite the same amount of work. "Extremely annoying" would be to re-trigger all spells just to achieve such a simple thing like damagetype detection. And this is what you have to do if you don't use this system. So you can't deny its practical relevance only because such a minor "flaw".

I now move for a dismissal of this resource as the technique it uses is flawed : )

given this, I'm starting to see how this is actually impossible to do, as you won't know what items reduce spell damage

For a guy that critics so hard and judges so fast, you say the word 'impossible' quite quickly ;)

It is really easy to identify this. Just add a dummy unit which get all the items and buffs that the attacked unit has (except the damage detector buff off course). Then deal some spell damage to it and compare the real dealt damage with the one it should have taken -> the normalized damage difference is the individual spell reduction.

Already tried it out and it works. I think the only flaw in the technique is your limited phantasy ;)
 
That's still not automatic, so not really acceptable

the reason it needs to be automatic is ease of use

if someone were to add the ability 4 months after placing this into their map, they'd likely have forgotten all about this

by requiring custom map specific setup, you really make this a rather map specific system

unless you can figure out an automatic method to detect spell damage or physical damage >.<

the attempt I made was automatic, but it had its own problems, which is why it was later gy'd

also,as stated, this will bug on 0 damage events, so that's yet another issue

0 damage events are either spell or physical
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
That's still not automatic, so not really acceptable

It is automatic. As I posted, I tested it. If it is implemented this way it is FULLY automatic as in each DamageEvent there would be a check to update the Spelldamage reduction, no matter where it came from (Items or customized damage table).

also,as stated, this will bug on 0 damage events, so that's yet another issue

0 damage events are either spell or physical

I'm really starting to get sad.

I don't know why I discuss with a guy that didn't even read my initial post. You obviously didn't look at the code (ok), but you could at least have read what was written in the first post. I allready gave a very clear comment on this issue.

By the way, your words:

Simple light damage detection. Will not run 0 damage events as only invalid attacks and spells can cause these.

Now stop annoying me. Come again when you actually read my posts.

Good evening,
lfh
 
last edit
man, if I had known that the spell resistance thing didn't stack, I woulda had a completely different stance on this, lol.
end last edit

Solved how to do this automatically, so writing it up

edit
writeup failed

bad news (you likely knew this, I didn't, lol)...

you may only have 1 spell resistance ability on a unit at a time (this includes items, so this ability added to a unit will disable all spell resistance items from working)

edit
oh yea, when using this technique, DDS stuff will have to be modified a bit (remove the ability on kill)

for direct damage dealing, the spell resistance thing poses a major problem... hmmm

actually, we'll see what we can do with this

will need a spell resistance lib (easy)
will use spell damage to deal 100% damage, which'll take advantage of the technique I used to get armor reduction in my own attempt of this thing u came up with, lol
will need the primary lib that acts as extension to DDS
will need to modify the DDS stuff accordingly to work with the extension properly

lots of work that needs to be done

edit
this was my first attempt where I learned that the spell damage reduction ability didn't stack : p, item or not
JASS:
library DamageSourceType uses DamageEvent
    globals
        constant integer ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION = 'A002'
    endglobals
    
    //! textmacro DAMAGE_TYPE_FIELDS
        private static real damageTypeAmount = 0
        private static boolean running = false
        
        readonly static integer damageSourceType = 0
        
        static constant integer DAMAGE_SOURCE_TYPE_PHYSICAL = 0
        static constant integer DAMAGE_SOURCE_TYPE_SPELL = 1
        static constant integer DAMAGE_SOURCE_TYPE_CODE = 2
    //! endtextmacro

    //! textmacro DAMAGE_TYPE_LOC
        local integer prevDamageType
        local real curLife
    //! endtextmacro

    //! textmacro DAMAGE_TYPE_EXT
        set prevDamageType = damageSourceType

        if (running) then
            set running = false
        
            call UnitAddAbility(GetIndexedUnit(), ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION)
            call UnitMakeAbilityPermanent(GetIndexedUnit(), true, ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION)
            
            set thistype.damageTypeAmount = amount/(GetUnitState(target, UNIT_STATE_MAX_LIFE)*.1)
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Reduction: "+R2S(thistype.damageTypeAmount))
        elseif (enabled) then
            if (amount < 0) then
                call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000,"Damage: "+R2S(GetEventDamage()))
            
                set running = true
                set enabled = false
                
                set curLife = GetWidgetLife(GetUnitById(targetId))
                call SetWidgetLife(GetUnitById(targetId), GetUnitState(target, UNIT_STATE_MAX_LIFE))
                
                call UnitRemoveAbility(GetUnitById(targetId), ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION)
                call UnitDamageTarget(GetUnitById(sourceId), GetUnitById(targetId), GetUnitState(target, UNIT_STATE_MAX_LIFE)*.1, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null)
                
                call SetWidgetLife(GetUnitById(targetId), curLife)
                
                set enabled = true
                
                set amount = amount - amount*damageTypeAmount
                set damageSourceType = DAMAGE_SOURCE_TYPE_SPELL
            elseif (0 == damageType_p[runAttackCount_p + 1]) then
                set damageSourceType = DAMAGE_SOURCE_TYPE_PHYSICAL
            else
                set damageSourceType = DAMAGE_SOURCE_TYPE_CODE
            endif
        endif
    //! endtextmacro
    
    private module Init
        private static method onInit takes nothing returns nothing
            call init()
        endmethod
    endmodule
    
    private struct AddAbility extends array
        private static method onIndex takes nothing returns boolean
            call UnitAddAbility(GetIndexedUnit(), ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION)
            call UnitMakeAbilityPermanent(GetIndexedUnit(), true, ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION)
            
            return false
        endmethod
    
        private static method init takes nothing returns nothing
            local integer playerId
            
            set playerId = 15
            loop
                call SetPlayerAbilityAvailable(Player(playerId), ABILITIES_DAMAGE_SOURCE_TYPE_SPELL_REDUCTION, false)
                
                exitwhen 0 == playerId
                set playerId = playerId - 1
            endloop
        
            call RegisterUnitIndexEvent(Condition(function thistype.onIndex), UnitIndexer.INDEX)
        endmethod
    
        implement Init
    endstruct
endlibrary

oh well, I'll code all of this stuff up tonight/tomorrow >.<

edit
writeup succeeded

if you want to do a version using Bribe's or a pure JASS version, go for it : )

edit
and yea, it's best just to ignore 0 damage events

0 damage may come from expiring projectiles (the first one)
0 damage may or may not come from illusions (depends)
0 damage may come from attacks (reduce to 0 gameplay constant)
0 damage may come from spells (reduce to 0 gameplay constant, stun (invalid), etc)

edit
so after all was said and done, this resource ended up being fine, given that it gets fixed ; )

I ended up writing the extension to DamageEvent myself -> http://www.hiveworkshop.com/forums/submissions-414/extension-damage-source-type-228545/
You'll likely end up writing one for http://www.hiveworkshop.com/forums/...e-v2-2-1-0-a-201016/?prev=d=list&r=20&u=Bribe so as to have a complete JASS version, since you seem to like JASS : )

and all's well >.<

man, if I had known that the spell resistance thing didn't stack, I woulda had a completely different stance on this from post 23+, lol.

No stack = no ability (just tell people to code their own thing) whereas Stack = game breaker wtfx with complex nasty registrations to get correct spell resistance (omgz omgz).

edit
if ur still into making ur own DDS from scratch, gl

edit
it was from here on that I assumed that it stacked, heh

http://www.hiveworkshop.com/forums/2273618-post23.html

this doesn't mean that the code in your original post is suddenly not crap though, so you still need to fix it up
 
Last edited:
Level 14
Joined
Dec 12, 2012
Messages
1,007
Cool :D

Sorry for the hard words, but I really appreciate your reaction, not many people would react like this! I did'nt know you didn't know about the stack, so I didn't understood the problem :D

Have to go to work now, will later write more.

Bye,
lfh


EDIT:

Hi, I tried the testmap and it works but if I add another unit to the map, I get the error "expected end of line" at

JASS:
library DamageSourceType /* v1.0.0.0

Do I have to change anything to add new units to the map?


EDIT:

Ok, it works now, didn't know how this Newgen WE works^^

Looks good, I will do some testing now.
 
Last edited:
First of all, welcome to the forums, looking_for_help.

Second of all, I would like to congratulate you on finding yet another engine abusation that may lead to groundbreaking new maps and systems - 11 years after the frozen throne's release nonetheless.

Third of all, I want to express my disappointment with the response to this thread.

Nestharus, you should be absolutely ashamed of yourself. Let me spell it out for you.

You are a grown-ass man with an extensive knowledge of algorithms and data structures. You have been a member of the hive for 6 years during which you enumerated countless beneficial posts and resources. You've averaged more than 2 posts per day, and have likely taken a look at 90% of worthwhile resources here and at thehelper.net and wc3c.net.

You should do your best to do something good in every post you write, but you don't. Your response to this thread was disgusting, pedantic, and narcissistic; and I suggest you take a good look at what you pride yourself on, and flush it down the toilet. Start acting like an adult.

Your system is not ideal, but that's okay because every system has flaws. Take a look at some of the well-known systems uploaded to wc3c.net. Every single one of them has as list of pros and cons, because different methods have different advantages.

Magtheridon, I appreciate your well-considered and explanatory post in response to this, I appreciate your encouragement towards looking_for_help, and I especially appreciate that you have a vision and goal for the state of this jass section, but I respectfully disagree.

The section and the systems uploaded have strong ties towards UnitIndexer and other "standard" systems because you guys have made it that way. You deny 80% of differing resources, and a majority of "linked" resources are made by the same handful of people. The result, while potentially or theoretically utopistic, doesn't actually work for users.

Take a look at the top-ten most downloaded and highest rated maps in the maps section. None of them were made using UnitIndexer, and certainly no map of mine will ever use it.

This is a fundamental flaw in your priorities, and if you want to encourage map developers and players to stay with warcraft 3, you have to change something, because the current model is completely stagnant and redundant.

Want to know why no one uses UnitIndexer? Nestharus' API is non-existant, his installation methods are always involving LUA (because they require object data), and his support for his own resources requires the client to decode the enigmatic forum prose he consistently bathes everyone in.

Lastly, looking_for_help, I would love to help you realize this system - in fact, I think it would be extremely useful for a public resource I'm working on today. I'll need to make sure you have an understanding of vJass though. I also don't use UnitIndexer, nor do I use GUI, in fact, the only DD I use still today is my own (also an approved resource). If you are interested, let me know, otherwise I will likely just create an add-on for my own system (and credit you of course).

TL;DR: Nestharus, don't be such a douche to new users who exhibit all the kindness and clarity that you should also have.

Magtheridon: Maybe you should reconsider your vision of an ideal jass resources forum.

looking_for_help: Great job. I am interested in helping you.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Hi again,

First of all, welcome to the forums, looking_for_help.

Second of all, I would like to congratulate you on finding yet another engine abusation that may lead to groundbreaking new maps and systems - 11 years after the frozen throne's release nonetheless.

thank you very much. I really hope that this technique is helpfull to many other people, no matter which DDS they use at the end.

Third of all, I want to express my disappointment with the response to this thread.

Well, not everything went perfect at the beginning, thats right ;)
I was disappointed too in the beginning, but I'm not a vengeful person and I don't have problems with anyone from this forum, so everything should be cool now. And I have to admit Nestharus did a very good job with his DDS (although even his has some system-specific flaws). In the end we worked quite good together, so everything is fine.

Magtheridon, I appreciate your well-considered and explanatory post in response to this, I appreciate your encouragement towards looking_for_help, and I especially appreciate that you have a vision and goal for the state of this jass section, but I respectfully disagree.

You had some very interessting arguments in your post, in my opinion. I think a compromise between the two extremes would be the best way to go.

looking_for_help: Great job. I am interested in helping you.

Thanks, right now I'm doing very intense testing on the system. I already tested over 200 native abilities and I didn't found a bug untill now. My aim is to test all native abilities, so that it is garanteed the system works for 100%.

When I'm done with this, I will come back to your offer.

Greetings,
lfh
 
Thanks, right now I'm doing very intense testing on the system. I already tested over 200 native abilities and I didn't found a bug untill now. My aim is to test all native abilities, so that it is garanteed the system works for 100%.

When I'm done with this, I will come back to your offer.

Hi,

That sounds like a wise decision. I am a very impatient person and have thus reinvented the wheel and used your method in my own script. If and when I uploaded it publicly, you will obviously be credited.

I have done a few things slightly different in mine which is quite a bit more optimized. If you would like to see it immediately (as in, before I sort out some API issues and upload it), let me know.

I'm sorry if I rushed you on my offer, but this resource was actually necessary for a project I've been working on in the last few days (hence how I found this thread in the first place), and I can't upload my project without first uploading my version of your resource.

Cheers,
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
I am a very impatient person and have thus reinvented the wheel and used your method in my own script.

Hey, thats absolutley no problem, just take your time ;)

Testing all abilities will require some more days anyway.

I'm sorry if I rushed you on my offer, but this resource was actually necessary for a project I've been working on in the last few days

If it helped you in your project then it fullfilled its purpose and I'm happy. No need to excuse ;)

Greetings,
lfh
 
Hmm...

This would actually capture spell resist items too, nice

JASS:
    function GetUnitSpellResistance takes unit u returns real
        local real originalHP
        local real beforeHP
        local real afterHP
        local real resistance
        local real DUMMY_DAMAGE = 100000
        local real DUMMY_FACTOR = 0.00001
        
        call UnitRemoveAbility(target, DAMAGE_TYPE_DETECTOR)

        set originalHP = GetWidgetLife(target)
        call UnitAddAbility(target, SET_MAX_LIFE)
        set beforeHP = GetWidgetLife(target)
        call DisableTrigger(DamageEvent)
        call UnitDamageTarget(source, target, DUMMY_DAMAGE, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
        call EnableTrigger(DamageEvent)
        set afterHP = GetWidgetLife(target)
        call UnitRemoveAbility(target, SET_MAX_LIFE)
        call SetWidgetLife(target, originalHP)

        call UnitAddAbility(target, DAMAGE_TYPE_DETECTOR)
        call UnitMakeAbilityPermanent(target, true, DAMAGE_TYPE_DETECTOR)

        set resistance = DUMMY_FACTOR*(beforeHP - afterHP)
      
        if resistance > 1.0 then
            return resistance
        else
            return 1.0
        endif
    endfunction

My suggestion.. make that a separate resource, then we can work on it from there
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
My suggestion.. make that a separate resource, then we can work on it from there

Ok. I thought its to simple to make it an extra resource, so I included it directly in my system. But if you think its worth it, I can do so.

I will do this tomorrow as it's getting late and I really need to get some sleep^^

But thanks for the very efficient discussion!

See you,
lfh
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Allright, then the system should be completly bug free by now and work as its supposed to.

This would actually capture spell resist items too, nice

Unfortunatly not as spell resist items would overwrite the 200% damage reduction from spells due to their unstackability. But thats not really a big deal as there are only two options:

1.) The mapper only has one spell reduction item, then he can simply set its reduction value by just changing one value.

2.) He has multiple spell reduction items, then they have to be triggered anyway, as they don't stack.
 
Well, nice job on physical damage detection, but this still has no chance of getting approved, lol. Don't know why you waste your time on it.

#1: it leaks
#2: if it didn't leak, it'd likely be using a poor algorithm to clean the leaks
#3: it comes coupled with a lot of extra functions : \

I guess it's ok practice, but just don't expect an approval out of it =).


TriggerRegister returns a trigger event. Over time, these trigger events pile up (leaks). Units may die or be removed, making the trigger events on the trigger useless. The only way to clean the leaks is to destroy the trigger and then reregister all existing units to it. However, this still won't be good enough as this could cause FPS drops. Some systems create 1 trigger per unit, but now imagine the massive memory usage >.<.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Well, nice job on physical damage detection, but this still has no chance of getting approved, lol. Don't know why you waste your time on it.

Uhm, its not wasted time.

I really learned a lot while developing and improving this system, and thats in my opinion the most efficient way to learn a new programming language.
Also many bugs only got discovered because of me wasting my time ;)

Also I will provide soon a GUI version of this, so that everybody can benefit from this technique... I allready got 2 emails from people who asked me if I can make this GUI, so I will do so.

#3: it comes coupled with a lot of extra functions : \

Hm, I don't understand this point, could you detail what you mean? All functions provided by the system are absolutely required to garantee a bug free detection.

The only way to clean the leaks is to destroy the trigger and then reregister all existing units to it.

And with this all leaks would be removed?

lfh
 
And with this all leaks would be removed?

Yes, recreating the DD trigger will clean the leaks, but it's a pretty naive approach to this.

I suggest taking a look at the design explanation section of my resource StructuredDD if you're curious about a more efficient method. Nestharus' DD system is more efficient if analyzed by complexity, but any suggestion that his method is faster is just speculation since we have to work in the JASS virtual machine.

I'll be responding to your other content directed at me in due time.
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
Yes, recreating the DD trigger will clean the leaks, but it's a pretty naive approach to this.

So, this should then clean all my memory leaks, right?

JASS:
function RestoreTriggers takes nothing returns nothing
    local real UNIT_MIN_LIFE = 0.406

    if GetWidgetLife(GetEnumUnit()) > UNIT_MIN_LIFE then
        call TriggerRegisterUnitEvent(DamageEvent, GetEnumUnit(), EVENT_UNIT_DAMAGED)
    endif

endfunction

function Trig_ClearMemory_Actions takes nothing returns nothing
    local group g = CreateGroup()
    call GroupEnumUnitsInRect(g, bj_mapInitialPlayableArea, null)

    call ResetTrigger(DamageEvent)
    call DestroyTrigger(DamageEvent)
    set DamageEvent = null
    
    set DamageEvent = CreateTrigger(  )
    call TriggerAddAction(DamageEvent, function DamageEvent_Actions)
    call TriggerAddAction(DamageEvent, function OnDamage_Actions)
    
    call ForGroup( g, function RestoreTriggers )

    call DestroyGroup(g)
    set g = null
endfunction

//===========================================================================
function InitTrig_ClearMemory takes nothing returns nothing
    set gg_trg_ClearMemory = CreateTrigger(  )
    call TriggerRegisterTimerEvent( gg_trg_ClearMemory, 5.0, true )
    call TriggerAddAction( gg_trg_ClearMemory, function Trig_ClearMemory_Actions )
endfunction

Because according to my handle counter, it doesn't. But then again, my handle counter does sometimes strange things, so: is there a way to prove that there are no leaks in a map?

#3: it comes coupled with a lot of extra functions : \

Still interested in what you meant with that. I claim that this system uses the absolute minimum of extra functions possible.

I'll be responding to your other content directed at me in due time.

Allright, looking forward to it.

Greetings,
lfh
 
Top