• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[Extension] Advanced Damage Event

Level 31
Joined
Jul 10, 2007
Messages
6,306
Installation Code- Save, Reopen Map, Delete
LUA_GET_VAR_OBJECT
LUA_FILE_HEADER
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! runtextmacro LUA_FILE_HEADER()
    
    //! i dofile("GetVarObject")
    
    //! i local saveBuff = getvarobject("AIlf", "abilities", "ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY", true)
    //! i createobject("AIlf", saveBuff)
    //! i makechange(current, "Ilif", "1", "2000000")
    
    //! i updateobjects()
//! endexternalblock

delete settings if running Lua

JASS:
library AdvDamageEvent /* v1.1.2.0
*************************************************************************************
*
*   Able to modify damage
*
*************************************************************************************
*
*   */uses/*
*
*       */ DamageEvent      /*          hiveworkshop.com/forums/jass-resources-412/snippet-damageevent-186829/
*
************************************************************************************
*
*   SETTINGS
*/
globals
    constant integer ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY = 'A001'
endglobals
/*
*************************************************************************************
*
*   struct AdvDamageEvent extends DamageEvent
*       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.
*
*************************************************************************************/
    //! textmacro ADV_DAMAGE_EVENT_EXT_CODE
        /*
        *   Stores information for processing when the timer expires
        *   Life is what life the target currently has
        *   If the life is <= 0, the source will kill the target via UnitDamageTarget and a massive amount of damage
        *   If the life is > 0, the target will have its life set via SetWidgetLife
        */
        private struct DamageEventStats extends array
            private static thistype count = 0
            static method operator last takes nothing returns thistype
                return count
            endmethod
            static method operator first takes nothing returns thistype
                return 1
            endmethod
            method operator next takes nothing returns thistype
                return this + 1
            endmethod
            
            readonly UnitIndex source
            readonly UnitIndex target
            method operator sourceUnit takes nothing returns unit
                return GetUnitById(source)
            endmethod
            method operator targetUnit takes nothing returns unit
                return GetUnitById(target)
            endmethod
            
            real life
            real actualLife
            
            static method create takes UnitIndex source, UnitIndex target returns thistype
                set count = count + 1
                
                set count.source = source
                set count.target = target
                
                set count.life = thistype(target).actualLife
                
                /*
                *   Not having the targets locked could corrupt timesSaved upon deindex
                */
                call source.lock()
                call target.lock()
                
                return count
            endmethod
            
            static method clear takes nothing returns nothing
                set count = 0
            endmethod
        endstruct
        
        /*
        *   Handles removing/adding abilities
        */
        private module AdvDamageEventMod
            private static delegate DamageEventStats openDamageStats = 0
            
            private static timer saveTimer
            private integer timesSaved
            
            static real modifiedDamage = 0
            
            private method addSave takes nothing returns nothing
                if (0 == timesSaved) then
                    set DamageEventStats(this).actualLife = GetWidgetLife(GetUnitById(this)) - DamageEvent.amount
                else
                    set DamageEventStats(this).actualLife = DamageEventStats(this).actualLife - DamageEvent.amount
                endif
                
                set timesSaved = timesSaved + 1
            endmethod
            private static method removeSave takes nothing returns nothing
                /*
                *   Need this check in case the unit was killed before all of its damage was applied
                */
                if (0 == thistype(target).timesSaved) then
                    return
                endif
                
                set thistype(target).timesSaved = thistype(target).timesSaved - 1
                
                if (0 < life) then
                    /*
                    *   This is nested to make the else statement only run if the life is <= 0
                    */
                    if (0 == thistype(target).timesSaved) then
                        call UnitRemoveAbility(targetUnit, ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY)
                        /*
                        *   The life may very well be higher than the original life. There is no way to check for that from here, but then
                        *   again there is no need. The user can set modifiedDamage to 0 if they don't want it to go into the negatives in the
                        *   after event.
                        */
                        call SetWidgetLife(targetUnit, life)
                    endif
                else
                    /*
                    *   set timesSaved to 0 so that the unit will not be processed anymore. There is no point
                    *   to processing dead units.
                    */
                    set thistype(target).timesSaved = 0
                endif
            endmethod
            
            private static method saveUnits takes nothing returns nothing
                local integer last = DamageEventStats.last
                
                /*
                *   DamageEvent has to be disabled because UnitDamageTarget is used when a unit is to be killed
                *   This native would end up running DamageEvent
                */
                set DamageEvent.enabled = false
                
                /*
                *   Iterate over all units to be saved
                */
                set openDamageStats = DamageEventStats.first
                loop
                    call removeSave()
                    
                    /*
                    *   The target and source were locked in the DamageEventStats create method
                    *   Need to unlock them or they will remain locked. This is done in this loop
                    *   as its the most efficient way to do it.
                    */
                    call target.unlock()
                    call source.unlock()
                    
                    /*
                    *   Rather than looping backwards and comparing to 0, loop forwards, this way the
                    *   damage order is maintained.
                    */
                    exitwhen last == openDamageStats
                    set openDamageStats = openDamageStats.next
                endloop
                
                call DamageEventStats.clear()
                
                set DamageEvent.enabled = true
            endmethod
            
            private static method startSaveTimer takes nothing returns nothing
                if (0 == DamageEventStats.last) then
                    call TimerStart(saveTimer, 0, false, function thistype.saveUnits)
                endif
            endmethod
            
            static method onDamage_p_core takes nothing returns nothing
                local thistype target = DamageEvent.targetId
                local thistype source = DamageEvent.sourceId
                
                /*
                *   Need to store two damage amounts, the original amount and the modified amount
                *   With this, users will be able to apply % bonuses using the original amount
                */
                set modifiedDamage = DamageEvent.amount
                
                call startSaveTimer()
                call target.addSave()
                set openDamageStats = DamageEventStats.create(source, target)
            endmethod
            
            private static method onInit takes nothing returns nothing
                set saveTimer = CreateTimer()
            endmethod
        endmodule
        
        private struct AdvDamageEvent_p extends array
            implement AdvDamageEventMod
        endstruct
        
        /*
        *   A separate struct is needed for the API to ensure proper encapsullation
        */
        struct AdvDamageEvent extends array
            private static delegate DamageEvent damageEvent = 0
            
            static method operator life takes nothing returns real
                return AdvDamageEvent_p.life
            endmethod
            static method operator modifiedAmount takes nothing returns real
                return AdvDamageEvent_p.modifiedDamage
            endmethod
            static method operator modifiedAmount= takes real modifiedAmount returns nothing
                /*
                *   The modified damage is only active while the damage event is running. It is
                *   static. When the DamageEvent finishes running, the modified amount is lost.
                *   The only thing that the DamageEvent needs is the life.
                */
                set AdvDamageEvent_p.life = AdvDamageEvent_p.life - (modifiedAmount - thistype.modifiedAmount)
                set DamageEventStats(DamageEvent.targetId).actualLife = DamageEventStats(DamageEvent.targetId).actualLife - (modifiedAmount - thistype.modifiedAmount)
                set AdvDamageEvent_p.modifiedDamage = modifiedAmount
            endmethod
        endstruct
    //! endtextmacro
    
    //! textmacro ADV_DAMAGE_EVENT_LOC_BEFORE
        local real prevModifiedDamage = AdvDamageEvent_p.modifiedDamage
    //! endtextmacro
    //! textmacro ADV_DAMAGE_EVENT_EXT
        call AdvDamageEvent_p.onDamage_p_core()
    //! endtextmacro
    //! textmacro ADV_DAMAGE_EVENT_LOC_AFTER
        set AdvDamageEvent_p.modifiedDamage = prevModifiedDamage
        
        if (GetWidgetLife(GetUnitById(targetId)) - amount < .5 and AdvDamageEvent_p.life > 0) then
            call UnitAddAbility(GetUnitById(targetId), ADV_DAMAGE_EVENT_SAVE_UNIT_ABILITY)
            call SetWidgetLife(GetUnitById(targetId), GetUnitState(GetUnitById(targetId), UNIT_STATE_MAX_LIFE))
        endif
    //! endtextmacro
endlibrary
 

Attachments

  • Advanced Damage Event.w3x
    54.7 KB · Views: 116
Last edited:
JASS:
struct Test extends array
    
    private static method onInit takes nothing returns nothing
        if ATTACK_TYPE_NORMAL == null then
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "ATTACK_TYPE_NORMAL == null")
        endif
        if WEAPON_TYPE_WHOKNOWS == null then
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "WEAPON_TYPE_WHOKNOWS == null")
        endif
    endmethod
    
endstruct


attachment.php


Is that proof enough for you? :p
 

Attachments

  • atwtscreen.PNG
    atwtscreen.PNG
    35.7 KB · Views: 738
Level 31
Joined
Jul 10, 2007
Messages
6,306
Another bug fix ;o


Also, this won't explode units... but neither will DamageControl by purge or the stuff by j4l.

No way for me to check to explode units as no abilities are applied to the units >.<.

I'll try Envenomed Spears though to see if that works with artillery/splash (I doubt it does though). If it does, then I'll be able to implement old AdvDamageEvent features in order to fix the explode unit stuff.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
mag, I know.

To shadow
#1, Bribe's is in GUI.
#2, mine is more accurate as it uses unit's original hp for calculating life rather than unit's health + ability.
#3, my ability adds 2,000,000 health : P
#4, DamageModificationEffect uses this and DamageModificationEffect is totally awesome, lol
#5, this has damage priorities, Bribe's does not ; p
#6, this is an extension, meaning that you can either use simple DDS or advanced DDS
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
mag, I know.

To shadow
#1, Bribe's is in GUI.
#2, mine is more accurate as it uses unit's original hp for calculating life rather than unit's health + ability.
#3, my ability adds 2,000,000 health : P
#4, DamageModificationEffect uses this and DamageModificationEffect is totally awesome, lol
#5, this has damage priorities, Bribe's does not ; p
#6, this is an extension, meaning that you can either use simple DDS or advanced DDS

1. yes that i noticed, but its mean your faster or dont have too much speed difference between DDS systems?
3. is related with 2., its not possible without adding ability? because in Bribe system too that was annoying when u used spellbook and reicived damage (so hp ability was added removed) then spellbook was unusable coz closed with each add/remove ability thing, basically if u are on heavy attack u cant even open the spellbook.
5. what u mean under damage priority?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
1. yes, faster
3. yea, sadly, spellbooks can't be used with this : |
5. you can decide damage order. For example, you can make damaging effects run before defending effects.


Try this instead... make the items spellbooks and then do an ability style inventory: when you get an item, rather than putting it into the inventory, add a passive ability that represents that item or something (actually active, that way you can click the item to use it). Furthermore, to remove items, well, I'll think about that ;D.

Anyways, if you do the item spellbook idea, I don't think the spellbook will close, but I'm not sure... it might still close, we'll see.. I'll try it myself ^)^.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
5. i dont got what u mean in this part :D defending effects related with ability what give damage reduction? or its related with armor?

@off
this is why u created the floating text snippet?
coz i also noticed the floating texts in gui if have heavy attack cause serious fps drop, this is why u want make it faster?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok...

let's say you have a damage modification that adds 20 to damage. So attacker attacks, they get +20 damage. Now you have a defensive modification that reduces damage by 5.

You can, with priority, decide which one runs first. The one with highest priority is applied first. This means that you can make the 20 damage amplification always run before the 5 damage reduction. This can be useful for, when example, you want to reduce by a % amount of the total. It can also help you group up damage types (magical damage etc). Furthermore, it can help you display final damage by giving the display method a priority of 0 : ).

edit
Try placing active abilities outside of the spellbook and passives inside ;p.
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Ah, work with this is soo strangely comfortable xD, extremely nice work here nes.

But, I'm curious about the name, why Advance Damage Event, why not DamageModificator or something like that?.

because it extends off of Damage Event
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
attached is a release candidate for update

this fixes explosion so that it works

it also correctly keeps explosion even with layered unit damage target uses

using artillery, this will still explode the units (notice the the unit is dying from UnitDamageTarget, not from the actual attack). The units will also receive 1 damage per hit =).
JASS:
struct Test extends array
    private static method onDamage takes nothing returns nothing
        if (damageSourceType == DamageSourceType.SPELL) then
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Spell")
        elseif (damageSourceType == DamageSourceType.PHYSICAL) then
            set modifiedAmount = 0
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Physical")
        else
            call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Code")
            //set modifiedAmount = 0
        endif
        
        //call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,R2S(amount))
        
        if (damageSourceType != DamageSourceType.CODE) then
            call allocateAttack(1, 0, 0)
            call UnitDamageTarget(source, target, 1, true, false, null, DAMAGE_TYPE_UNIVERSAL, null)
        endif
    endmethod
    
    implement DamageEvent
    
    private static method onInit takes nothing returns nothing
        call SetPlayerState(Player(11), PLAYER_STATE_GIVES_BOUNTY, 1)
    endmethod
endstruct

edit
accounted for damage factor

thanks to looking_for_help for seeing that there was a problem : p
 

Attachments

  • DamageType.w3x
    56.9 KB · Views: 45
Last edited:
Level 2
Joined
May 4, 2009
Messages
11
Hi Nestharus, getDamageSrourceType system is realy cool! But sometime i need more advanced function like get attacktype and damagetype from source to target, can it be possible ?
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Ok, decided to overhaul this system with some new cool innovative designs.

There are now a lot of DDS systems out there.

Which one is the best to use?
What are the pros/cons to each?


The new design of this system will be the following

1. Take out the trigger refresh code and make it a separate resource so that anyone can use it. This is the one thing that really makes this system shine more than the others.

2. Make the extension design work with anything rather than targeted extensions through the use of modules.


4 extensions will be supported (DamageEvent will not know what's in them)

Damage Event Core - the core event handling of Damage Event

Advanced Damage Event - for damage modification and timer delay for damage application

Damage Type - retrieving whether damage was from a spell, physical attack, or code

Unit Damage Modification - unit specific damage modification

Damage Event will essentially be 4 implemented modules and the trigger refreshing as well as the common API with the struct architecture =). Doing this will allow anyone to plug into this perfect super lightweight framework. This will also deprecate the currently approved DDS resources other than this one as they will be able to plug into this framework and get performance benefits + a shared common API. The code will be entirely their own, DamageEvent will not be intrusive =).

And now that I think about it, rather than modules, textmacros should be used >.<. The reason for this is so that cohadar's version of vjass is supported as well =).


This will also deprecate Damage Modification Effect and all of its related resources : (. A new plugin design will need to be created.

My own implementations of the plugins will feature improved damage handling to prevent flashing life, improved calculation of damage reduction when the damage type plugin is enabled to remove flashing life (life flashed to keep the unit from dying), and of course improved damage modification effects with damage queues. I really don't know if I want to code all of this as I'm starting to get into other things now, but at least the framework will be up for people to plug stuff into. I'll also be sure to update the essentials: Advanced Damage Event and Damage Source Type.
 
Top