• 🏆 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!

[Extension] Advanced Damage Event

Level 31
Joined
Jul 10, 2007
Messages
6,306
important note
Due to a horrible warcraft 3 bug, artillery units running on this system can't be ordered to attack ground or wacraft 3 will instantly crash
->http://forums.battle.net/thread.html?topicId=27807930612&sid=3000

It is impossible to remove the artillery command from an artillery unit, so players need to be warned about this. It's impossible to stop the bug. It is all up to Blizzard to fix it.

Also, units with bouncing attacks will not attack due to yet another wc3 bug.

As this uses ice abilities like frost attack and frozen breath and they don't stack, maps with those types of abilities will not work with this.

Also, wc3 doesn't correctly remove ice/frost buffs, meaning that even when the ability is removed, if a unit was hit enough times, that unit will permanently be frozen >.>.

The wc3 engine is just a major failure. This resource counts on wc3 working correctly, which it does not.
end important note

An extension to DamageEvent that allows one to retrieve the type of damage dealt (Physical, Spell, or JASS).

Physical damage is a result of melee/ranged attacks from a unit
Spell damage is a result of an ability, aura, buff, and etc
JASS is a result of damage inflicted by code

Also allows you to use SetWidgetLife to deal direct damage without screwing up bounty
Can retrieve all damage dealt as well as the original amount (in case you want to deal damage based on %)
Must use properties within the system to retrieve unit life and unit max life (unit life can actually be negative in this) (accurate)
Can retrieve buffs from attacks on damage (buffs normally applied after damage, so this is a plus)

Coupled with DamageEvent because that's the way it has to be done to be accurate.

LUA_DUMMY_PHYSICAL_ABILITY
LUA_FILE_HEADER
Installation Script
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! runtextmacro LUA_FILE_HEADER()
    
    //! i dofile("DummyPhysicalAbility")
    
    //! i local object = getdummyphysicalability("ADV_DAMAGE_EVENT", 1, false)
    
    //! i writejass("AdvDamageEvent_GLOBALS", 
        //! i [[//! textmacro ADV_DAMAGE_EVENT_EXT_GLOB_1
            //! i globals
                //! i private constant integer DUMMY_ABILITY=']] .. object.ability .. [['
                //! i private constant integer DUMMY_ABILITY_2=']] .. object.ability2 .. [['
                //! i private constant integer DUMMY_BUFF=']] .. object.buffs[1] .. [['
            //! i endglobals
        //! i //! endtextmacro]])
        
    //! i local saveBuff = getvarobject("AIlf", "abilities", "ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY", true)
    //! i createobject("AIlf", saveBuff)
    //! i makechange(current, "Ilif", "1", "500000")
    //! i makechange(current, "ahdu", "1", "1")
    //! i makechange(current, "adur", "1", "1")
    
    //! i local playerDamage = getvarobject("AIlf", "abilities", "ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY", true)
    
    //! i updateobjects()
//! endexternalblock

Library
JASS:
library AdvDamageEvent uses /*
    */DamageEvent, /*    Version: 2.1.0.5
                        hiveworkshop.com/forums/submissions-414/snippet-damageevent-186829/
                   */
                    
    //Version 4.0.1.0
        //static constant Event PHYSICAL
        //static constant Event SPELL
        //static constant Event JASS
        //readonly static integer type
        //readonly static real unitLife
        //readonly static real totalDamage
        //readonly static real unitMaxLife
        
        //static method fireSpell takes unit source, unit target, real damage returns nothing
        //static method firePhysical takes unit whichUnit, unit target, real amount, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
        //static method fireJASS takes unit whichUnit, unit target, real amount, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_0
        globals
            private integer array hooked
            private integer array damageType
            private boolean array ec
            private integer array es
            private integer ls = 0
        endglobals
    //! endtextmacro
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_1
        private function H takes unit u returns nothing
            local integer i
            if (DamageEvent.enabled and DamageEvent.overType == 0) then
                set i = GetUnitUserData(u)
                set hooked[i] = hooked[i] + 1
            endif
        endfunction
        private function HDP takes unit u, real d, real r, real x, real y, real a, boolean a2, boolean r2, attacktype a3, damagetype d2, weapontype w returns nothing
            call H(u)
        endfunction
        private function HDT takes unit u, widget t, real a, boolean a2, boolean r, attacktype a3, damagetype d, weapontype w returns nothing
            call H(u)
        endfunction
        private function HPL takes unit u, real d, real r, location l, real a, attacktype w, damagetype w2 returns nothing
            call H(u)
        endfunction
        private function HTB takes unit u, unit t, real a, attacktype w, damagetype w2 returns nothing
            call H(u)
        endfunction
        
        hook UnitDamagePoint HDP
        hook UnitDamageTarget HDT
        hook UnitDamagePointLoc HPL
        hook UnitDamageTargetBJ HTB
    //! endtextmacro
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_2
        readonly static Event PHYSICAL = 0
        readonly static Event SPELL = 0
        readonly static Event JASS = 0
        readonly static integer type = 0
        private static timer eventTimer = CreateTimer()
        private static integer array saved
        private static integer array eventSourceId
        private static integer array eventTargetId
        private static integer array eventType
        private static real array eventDamage
        private static integer eventCount = 0
        private static integer array eventBuffer
        private static integer eventBufferCount = 0
        private static real prevLife = 0
        private static boolean ran = false
        readonly static integer overType = 0
        
        static method operator unitMaxLife takes nothing returns real
            return GetUnitState(GetUnitById(targetId), UNIT_STATE_MAX_LIFE)-500000
        endmethod
        static method operator unitLife takes nothing returns real
            return GetWidgetLife(GetUnitById(targetId))-500000
        endmethod
        static method operator totalDamage takes nothing returns real
            return prevLife-(GetWidgetLife(GetUnitById(targetId))-500000)
        endmethod
        static method fireSpell takes unit source, unit target, real damage returns nothing
            set overType = SPELL
            call UnitDamageTarget(source, target, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, WEAPON_TYPE_WHOKNOWS)
        endmethod
        static method firePhysical takes unit whichUnit, unit target, real amount, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
            set overType = PHYSICAL
            call UnitDamageTarget(whichUnit, target, amount, true, ranged, attackType, damageType, weaponType)
        endmethod
        static method fireJASS takes unit whichUnit, unit target, real amount, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
            set overType = JASS
            call UnitDamageTarget(whichUnit, target, amount, false, ranged, attackType, damageType, weaponType)
        endmethod
        
        private static method handleEvent takes nothing returns nothing
            local UnitIndex prev = sourceId
            local UnitIndex prev2 = targetId
            local real prev3 = amount
            local integer prev4 = type
            local integer curEvent = 0
            local real life
            local real prev5 = prevLife
            local boolean explode = false
            local integer maxEvent = eventBufferCount
            local integer i
            set ran = true
            loop
                set curEvent = curEvent + 1
                set i = eventBuffer[curEvent]
                
                set sourceId = eventSourceId[i]
                set targetId = eventTargetId[i]
                set amount = eventDamage[i]
                set type = eventType[i]
                set ec[sourceId] = false
                set es[sourceId] = 0
                set prevLife = GetWidgetLife(target)-500000+amount
                if (type == 0) then
                    if (GetUnitAbilityLevel(target, DUMMY_BUFF) > 0) then
                        set type = PHYSICAL
                        set explode = true
                    else
                        set type = SPELL
                        set explode = false
                    endif
                else
                    set explode = false
                endif
                if (type == PHYSICAL) then
                    call UnitRemoveAbility(target, DUMMY_BUFF)
                endif
                call ANY.fire()
                call Event(type).fire()
                call sourceId.unlock()
                call targetId.unlock()
                
                set life = GetWidgetLife(target)-500000
                set saved[targetId] = saved[targetId] - 1
                if (life < .405) then
                    call SetUnitExploded(target, explode)
                    set saved[targetId] = 0
                    call disable()
                    call SetWidgetLife(target, .5)
                    call UnitDamageTarget(GetUnitById(sourceId), GetUnitById(targetId), 10000, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                    call enable()
                elseif (saved[targetId] == 0) then
                    call SetWidgetLife(target, GetUnitState(target, UNIT_STATE_MAX_LIFE))
                    call UnitRemoveAbility(GetUnitById(targetId), ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY)
                    call SetWidgetLife(target, life)
                endif
                
                exitwhen curEvent == maxEvent
            endloop
            set ran = false
            set eventBufferCount = 0
            set sourceId = prev
            set targetId = prev2
            set amount = prev3
            set type = prev4
            set prevLife = prev5
            set ls = 0
            if (eventCount != i) then
                loop
                    set i = i + 1
                    set eventBufferCount = eventBufferCount + 1
                    set eventBuffer[eventBufferCount] = i
                    exitwhen i == eventCount
                endloop
            else
                set eventCount = 0
            endif
        endmethod
    //! endtextmacro
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_4
        if (GetWidgetLife(GetUnitById(targetId)) >= .405) then
            if (GetUnitAbilityLevel(GetUnitById(targetId), DUMMY_BUFF) > 0) then
                //was a physical attack
                if (ec[eventCount]) then
                    //initial splash (runs through w/ only splash abils on splash attack)
                    loop
                        set eventType[i] = PHYSICAL
                        set es[i] = 0
                        set i = i - 1
                        exitwhen not ec[i] or es[i] != sourceId
                        set ec[i] = false
                    endloop
                else
                    //not splash (doesn't run w/ splash abil no non splash attack)
                    set eventCount = eventCount + 1
                    set eventType[eventCount] = PHYSICAL
                    set update = true
                endif
            elseif (ls == sourceId and eventType[eventCount] == PHYSICAL) then
                //this will only run with non-splash abil and splash abil on splash attack
                //first the 0 dmg runs, then the actual damage runs, alternates
                if (ec[eventCount]) then
                    set ec[eventCount] = false
                else
                    set eventCount = eventCount + 1
                    set eventType[eventCount] = PHYSICAL
                    set ec[eventCount] = true
                    set update = true
                endif
            elseif (hooked[sourceId] > 0) then
                //JASS attack
                set hooked[sourceId] = hooked[sourceId] - 1
                set eventCount = eventCount + 1
                set eventType[eventCount] = JASS
                set update = true
            elseif (overType != 0) then
                //known attack
                set eventCount = eventCount + 1
                set eventType[eventCount] = overType
                set overType = 0
                set update = true
            else
                //unknown attack
                //will initially run for all splash attacks
                //artillery attacks won't be known until after timer
                set eventCount = eventCount + 1
                set eventType[eventCount] = 0
                set update = true
                set ec[eventCount] = true
                set es[eventCount] = sourceId
            endif
            //for chaining attacks together, last source = current source
            set ls = sourceId
            //if update (didn't skip), then add to timer stack
            if (update) then
                set eventSourceId[eventCount] = sourceId
                set eventTargetId[eventCount] = targetId
                set eventDamage[eventCount] = amount
                call sourceId.lock()
                call targetId.lock()
                if (not ran) then
                    set eventBufferCount = eventBufferCount + 1
                    set eventBuffer[eventBufferCount] = eventCount
                endif
            
                //if haven't saved the unit, add life abil to save it
                if (saved[targetId] == 0) then
                    set life = GetWidgetLife(GetUnitById(targetId))
                    call UnitAddAbility(GetUnitById(targetId), ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY)
                    call SetWidgetLife(GetUnitById(targetId), life+500000)
                endif
                //always increase the save count so life ability is removed at right time
                set saved[targetId] = saved[targetId] + 1
                
                //start the timer to actually handle (only needed because of artillery attacks)
                //artillery attacks are *never* known until after timer
                call TimerStart(eventTimer, 0, false, function thistype.handleEvent)
            endif
        endif
    //! endtextmacro
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_6
        call UnitAddAbility(GetIndexedUnit(), DUMMY_ABILITY)
        call UnitMakeAbilityPermanent(GetIndexedUnit(), true, DUMMY_ABILITY)
        call UnitAddAbility(GetIndexedUnit(), DUMMY_ABILITY_2)
        call UnitMakeAbilityPermanent(GetIndexedUnit(), true, DUMMY_ABILITY_2)
    //! endtextmacro
    
    //! textmacro DAMAGE_EVENT_ADV_EXT_7
        set PHYSICAL = CreateEvent()
        set SPELL = CreateEvent()
        set JASS = CreateEvent()
    //! endtextmacro
endlibrary
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Updated this to work, but it still has a bug that can only be fixed with attack indexing ; |.

This bug has to do with bounty
JASS:
                            set enabled = false
                            call UnitDamageTarget(GetUnitById(sourceId), GetUnitById(targetId), 1000, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                            set enabled = true

As can be seen, if the source no longer exists or changes in that instant, the bounty may never be given or may be given to the wrong player.

One of the interesting things that can now be done with AdvDamage is that SetWidgetLife can be used to apply direct damage and the bounty will still be given to the proper player (you could deal 0 damage and then use SetWidgetLife).

There are other bugs that can only be fixed with an attack indexer, and those bugs have to do with how wc3 handles attacks... with splash, an attack is applied to a unit 2x if there is only 1 unit getting hit (first attack deals damage, second hits for 0).

Attacks that never hit a unit (like projectiles) expire after 60 seconds. The first attack to expire deals 0 damage. Subsequent attacks deal the actual attack damage... (an obvious wc3 bug)


AttackIndexer is still being worked on, but I'm running into problems with indexing attacks, so who knows when I'm going to get it out ; O (some attacks may never be indexed as not all attacks require unit targets and units are only ever issued orders once). This means that if there was a canon tower attacking the ground, those attacks would not get indexed ; |. There is a fix though, although it will be costly ; P.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Updated to fix a bug with the ANY event

Back code changed a little bit

This should always be used when working with any sort of buff as buffs are applied after damage. This should pretty much always be used ;O.

One thing I have noticed is that, if for example artillery damage is used (which results in unit's exploding), the unit will not explode ;O.

edit
Now units are immortal if they don't die in one hit. Fixing : O
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, fixed immortal units bug


This now works except for two things.


Problem #1: Units will no longer explode from artillery attacks... : |
Problem #2: Bounty may be given to the wrong player or not given at all

Problem #2 is a super rare issue. It occurs if a unit is no longer around at the time of damage (source is null).


Problem #2 can be fixed with AttackIndexer and I will add a static if when AttackIndexer is released to fix it. I suggest you start using AttackIndexer in all of your maps when it's out... : (

Problem #1 can't be fixed because there's no way to see if a unit was flagged to explode or not... yes, there is a setter, but there is no getter... that just made me go wtf.

And oh yes, you can freely do SetWidgetLife to add/remove hp to the unit (will still give bounty). You can also retrieve buffs.

edit
Added a few new properties as unit life is totally screwy with AdvDamageEvent on ; ). They allow you to retrieve current total damage, current max life, and current life.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
JASS:
call UnitDamageTarget(GetUnitById(sourceId), GetUnitById(targetId), 100, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)

This should be:

JASS:
call UnitDamageTarget(GetUnitById(sourceId), GetUnitById(targetId), 100, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, null)

Chaos damage is needed if the enemy has divine armor, and "null" weapontype functions the same as WEAPON_TYPE_WHOKNOWS, but takes up less space on an already overbearingly-stretched function parameter list and is almost certainly faster.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok... attackground is an order, not an ability, so it'll always return 0. All units have the capability to be ordered to attackground (tested), even if that just means they are going to stand there and do nothing.

What's funny is that things like move and attack are abilities. Stop isn't an ability.

: |

Any other ideas for detecting whether or not a unit is of type artillery?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
This returns true..

JASS:
if IssueTargetOrder(CreateUnit(Player(0), 'hrif', -200, -1700, 0), "attack", CreateDestructable('ATtr', -400, -1700, 60, 1, 1)) then
    call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "attacked")
endif

next?

edit
Hold it!

JASS:
    private static method display takes nothing returns boolean
        if (GetOrderTarget() != null) then
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "ordered")
        else
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "not ordered")
        endif
        return false
    endmethod

JASS:
        local trigger t = CreateTrigger()
        set u = CreateUnit(Player(0), 'hrif', -200, -1700, 0)
        call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
        call TriggerRegisterPlayerUnitEvent(t, Player(0), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
        call TriggerAddCondition(t, Condition(function thistype.display))
        if IssueTargetOrder(u, "attack", CreateDestructable('ATtr', -400, -1700, 60, 1, 1)) then
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "attacked")
        endif

That returns not ordered!

On canon it returns ordered!

Problem solved!!!

Now the only prob is the fact that each unit has 2 attacks, and there are techs... plus the memory leaks incurred with cataloging units as they are indexed
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,467
When the unit takes damage, have a dummy-unit with tons of life, regen, armor get moved to the damaged-unit's exact position, targetable only as tree? Will it still be hit by the same attack?

If so, hold off on killing/exploding the original-damaged unit until after detecting whether or not it was a splash attack from a siege unit.
 
Level 7
Joined
Jun 15, 2010
Messages
218
Could anyone make an example map and attach it. Then this could be very usefull for people who aint Pro at Jass like me.
 
Level 14
Joined
Apr 20, 2009
Messages
1,543
Nestharus, could you explain to me if it's possible to use global variables to store the damage source and damage target?

So that people without a lot of jass knowledge can use this system with GUI perhaps :)?
 
Level 14
Joined
Apr 20, 2009
Messages
1,543
It already stores it into global variables. I imagine that you can use custom script in GUI to read them.

0.o really? I must have over-looked that script, could you point me into the right direction on which those variables are ^^?

(the complexity is making me blind) I'm just a half-beginning jass user :p

EDIT: I didn't actually paste the script into my map yet since I'm at work right now. Does this system automatically create the global variables into your variable editor when you paste them? (sorry for such newbie questions :p)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
All public fields in structs are actually global variables. This is an extension, so all of the stuff is placed into the DamageEvent struct.

JASS:
        //static constant Event PHYSICAL
        //static constant Event SPELL
        //static constant Event JASS
        //readonly static integer type
        //readonly static real unitLife
        //readonly static real totalDamage
        //readonly static real unitMaxLife

So you'd read like DamageEvent.totalDamage (that'd be the global).

edit
nvm
 
Last edited:
Level 14
Joined
Apr 20, 2009
Messages
1,543
ok, thank you for your explenation. I understand :)

You can read the variables in GUI, but you can't register anything with GUI : \.

That's a shame, I would know a lot of users who would really love to see a gui compatible system that can detect damage types :(

Ow well, time to learn vJass anyways (I still have to learn a bit more about jass, but it's never bad to take a look) :p

Could you please send me the link to your in-depth vjass tutorial, I really would like to learn more.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Here

JASS:
/*
        //readonly static integer targetId
        //readonly static integer sourceId
        //readonly static unit target
        //readonly static unit source
        //readonly static real amount
*/
scope GUIDamageEvent
    private module Globals
        //global variables
        set udg_target = DamageEvent.target
        set udg_source = DamageEvent.source
        set udg_amount = DamageEvent.amount
        set udg_damageType = DamageEvent.type
    endmodule

    private module Register
        //register GUI here
        call DamageEvent.PHYSICAL.registerTrigger(gg_trg_Untitled_Trigger_001)
    endmodule

    private module Init
        private static method update takes nothing returns boolean
            implement Globals
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //required
            call DamageEvent.SPELL.register(Condition(function thistype.update))
            call DamageEvent.JASS.register(Condition(function thistype.update))
            call DamageEvent.PHYSICAL.register(Condition(function thistype.update))

            implement Register
        endmethod
    endmodule

    private struct Inits extends array
        implement Init
    endstruct
endscope
 
Level 14
Joined
Apr 20, 2009
Messages
1,543
Here

JASS:
/*
        //readonly static integer targetId
        //readonly static integer sourceId
        //readonly static unit target
        //readonly static unit source
        //readonly static real amount
*/
scope GUIDamageEvent
    private module Globals
        //global variables
        set udg_target = DamageEvent.target
        set udg_source = DamageEvent.source
        set udg_amount = DamageEvent.amount
        set udg_damageType = DamageEvent.type
    endmodule

    private module Register
        //register GUI here
        call DamageEvent.PHYSICAL.registerTrigger(gg_trg_Untitled_Trigger_001)
    endmodule

    private module Init
        private static method update takes nothing returns boolean
            implement Globals
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //required
            call DamageEvent.SPELL.register(Condition(function thistype.update))
            call DamageEvent.JASS.register(Condition(function thistype.update))
            call DamageEvent.PHYSICAL.register(Condition(function thistype.update))

            implement Register
        endmethod
    endmodule

    private struct Inits extends array
        implement Init
    endstruct
endscope

holy **** 0.o
This means that whenever I create a trigger called Untitled Trigger 001
it will automatically register an event for damage detection 0.o?
And I can even use the global variables in GUI to set it to my own needs?

You sir, are awesome!
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
You never declare the UnitAlive native and there are some problems with it as well - it returns true if called the instant the unit dies/was removed :-/

For the remove it's because the unit is removed AFTER the end of the thread where RemoveUnit was called, and in fact even after all trigger conditions/actions which fired with the same event.
I mean it's not something about the function, just about when an unit is removed, you still can kill it before remove it if it works.

And maybe it's the same with the KillUnit or whatever function, even if i don't think so i can't test it yet, but it's easy to test.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Explode bug fixed

Non splash bug fixed

RemoveUnit/KillUnit/etc bug fixed


This is now ready to be approved, gg.


The only bug remaining is the attack expiration bug, which happens when mass projectiles in wc3 expire (they expire after 60 seconds). This is actually a wc3 bug that applies the damage on expiration. This bug can be fixed with AttackIndexer, but I don't think that it's present in any wc3 maps atm... I had to really work at it to get it going (super slow projectiles with super fast attack speed).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I'm too tired to make a demo map, it's 2:30 am and I gotta get up in <5 hours. Approve it and I make the demo map tomorrow, deal? : P


It is now bug free and by far the best damage library for wc3, plus I was the first one of everyone to solve that dang exploder bug =P, a bug that could never be solved at wc3c, here, or TH =D. I think I deserve a cookie ^_^, I spent all day testing and etc to solve it.
 
Level 7
Joined
Oct 11, 2008
Messages
304
cookie.png
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Actually, I'm not doing a demo map for this. The implementation is straight forward, so it doesn't need one. I only do demo maps if the implementation is abstract. For example, I did a demo map for Encoder. It's not worth my time to do it as I can be spending my time doing much more productive things.

If anyone else wants to really do a demo map for this, go for it, but you will never be seeing one from me =).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, king pointed out a few interesting issues for this system

#1- the recursion doesn't exist for instant times when it should. This will be fixed.
#2- structure of events in events is event 1 -> nothing -> event 1 -> event 2 -> event 3 -> etc because of the 0 second timer (has to be done that way). This can't be fixed, 100% impossible. Damage types can only be detected after timer.


I noticed one problem- if 2 units each attack one unit with a physical attack at the same instant, one will be flagged as a spell and the other will be flagged as physical. It is possible to fix this, so I will fix it.


Another problem-
If a unit attacks with a spell attack and another attacks with a physical attack at the same instant, the spell could be registered as physical and physical as spell... will think about how to fix this one.

edit
Came up with timer fix, will prob release the update 4-7-11.

The only way to fix attacks that damage a unit at the same instant is to use attack indexing : p, so I'll prob work on that system after I release Team Manager... so many systems to write, so little time.

edit
Ok, this requires a few major fixes so that UnitDamageTarget works properly... also, this needs some extra safety... if a unit is removed and then created in the middle of a damage event and that unit happens to be an event unit (somewhere), the pointers are going to get messed up. What this means is that either the pointers need to be locked (lock method in UnitIndexer, which I really don't want to do) or... actually, when I think about it, a lock method is the best solution. Whenever a lib with Events in it ends up using pointers, it'll end up needing the safety that DamageEvent and AdvDamageEvent now require... so I'll be adding a lock method to UnitIndexer. Don't worry, the implementation will be cool good (will still fire deindex events at right times and so on, the pointers just won't be recycled until nothing is using them, no extra events or anything, they just won't be reused until later ^_^).
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
As far as I see, it works perfectly now.

Test script using a canon tower and 3 units that can't move. The canon tower continued to be damaged by 10 JASS damage every time it hit the units (30 damage total).
JASS:
struct tester extends array
    private static integer ran = 0
    private static integer ran2 = 3
    private static method test takes nothing returns boolean
        if (ran < 3 and ran2 > 0 and GetUnitTypeId(DamageEvent.source) == 'hctw') then
            set ran = ran + 1
            call UnitDamageTarget(DamageEvent.target, DamageEvent.source, 10, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_ACID, WEAPON_TYPE_AXE_MEDIUM_CHOP)
        elseif (ran2 > 0) then
            set ran2 = ran2 - 1
        else
            set ran2 = 3
            set ran = 0
            call UnitDamageTarget(DamageEvent.target, DamageEvent.source, 10, true, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_ACID, WEAPON_TYPE_AXE_MEDIUM_CHOP)
        endif
        call Print(GetUnitName(DamageEvent.source) + " damaged " + GetUnitName(DamageEvent.target) + " for " + R2S(DamageEvent.amount))
        return false
    endmethod
    
    private static method run takes nothing returns nothing
        call DamageEvent.PHYSICAL.register(Condition(function thistype.test))
        call DamageEvent.JASS.register(Condition(function thistype.test))
    endmethod

    implement Test
endstruct

Next thing I plan on doing is telling between ranged and melee attacks as well as letting people see whether the damage came from a missile, an artillery, or a regular attack.
 
Level 10
Joined
Jul 12, 2009
Messages
318
I don't care to futz around with ObjectMerger or your lua libraries (I just use JassHelper via command-line, not JNGP), so I'm not going to test this myself, but:

Have you tried this with a Missile (Bounce) attack? I don't personally know of any ability that applies a buff to every unit hit by one, and for that matter, when I gave a Huntress 'AIob', its attack did absolutely nothing (no missile, no damage).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
So back to the drawing board with this one.

not at all, it's done. It's impossible to get around wc3 bugs.

The ability is 100% required in order to detect physical attacks, but adding that ability to a unit with a bouncing missile attac makes it so that the unit can't attack... that's a major blunder on the part of Blizzard.
 
Level 10
Joined
May 27, 2009
Messages
494
i'm having a problem with multiple units attacking a single unit
the dummy ability (orb of frost ) and the buff (volcano) is appearing on the unit. More often is the dummy ability because you can see the color of the unit changing to frost in a short period of time while the buff can be seen especially if the target unit received a buff before that attack.

this usually happens to multiple units attacking a single unit.
hmm, maybe indexing the attack? but may have the problem especially when a player spams it with stop or changing orb of frost ability like barrage (not tested though) or other abilities for attack testing that may work on bounce abilities ('cause orb of frost is not really working with bounce attacks)

or a workaround :D
 
Level 19
Joined
Feb 4, 2009
Messages
1,313
just use orb of corruption instead and you can make the icon black so nobody will see it unless someone clicks on it
but this will most-likely not work with bounce either
triggering the whole ranged attack is much move convenient anyway (giving every unit instant attack, healing the target when it takes damage, making a missile to deal the damage instead etc.)

You're right, units with bounce missile will not even attack.

Another wonderful wc3 bug =D.

and this is why you can't buy orb effect items for luna at dota
life steal works but it does not place a buff so...(maybe it is possible to check for heal on unit takes damage event and use it for attack indexing somehow?)
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
This thing also makes units with the Missile(line) weapon type unable attack, just like those with Missile(bounce).
 
Top