System Damage

Jesus4Lyf

Good Idea™
Reaction score
397
Damage​
Version 1.0.5​

Requirements:
- Jass NewGen
- AIDS
- Event

JASS:
//  
//      ___   _     __  __   _   ___  ____    _______________________________
//     |   \ /_\   /  |/  | /_\ /  _\|  __|   ||      D E A L   I T ,      ||
//     | |) / _ \ / / | / |/ _ \| |/||  __|   ||    D E T E C T   I T ,    ||
//     |___/_/ \_/_/|__/|_|_/ \_\___/|____|   ||     B L O C K   I T .     ||
//                            By Jesus4Lyf    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//                                                                    v 1.0.5
//      What is Damage?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Damage is a damage dealing, detection and blocking system. It implements
//          all such functionality. It also provides a means to detect what type
//          of damage was dealt, so long as all damage in your map is dealt using
//          this system's deal damage functions (except for basic attacks).
//
//          It is completely recursively defined, meaning if you deal damage on
//          taking damage, the type detection and other features like blocking
//          will not malfunction.
//          
//      How to implement?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Create a new trigger object called Damage, go to 'Edit -> Convert to
//          Custom Text', and replace everything that's there with this script.
//
//          At the top of the script, there is a '//! external ObjectMerger' line.
//          Save your map, close your map, reopen your map, and then comment out this
//          line. Damage is now implemented. This line creates a dummy ability used
//          in the system in some circumstances with damage blocking.
//
//      Functions:
//     ¯¯¯¯¯¯¯¯¯¯¯¯
//          function Damage_RegisterEvent takes trigger whichTrigger returns nothing
//              - This registers a special "any unit takes damage" event.
//              - This event supports dynamic trigger use.
//              - Only triggers registered on this event may block damage.
//              - Only fires when the damage dealt is not 0.
//
//          function Damage_RegisterZeroEvent takes trigger whichTrigger returns nothing
//              - The same as Damage_RegisterEvent, but for only zero damage events
//                (which are excluded from Damage_RegisterEvent).
//              - Note that getting the damage type may be unreliable, since spells
//                like faerie fire trigger 0 damage, but it will count as an attack.
//
//          function Damage_GetType takes nothing returns damagetype
//              - This will get the type of damage dealt, like an event response,
//                for when using a unit takes damage event (or the special event above).
//
//          function Damage_IsPhysical takes nothing returns boolean
//          function Damage_IsSpell takes nothing returns boolean
//          function Damage_IsPure takes nothing returns boolean
//              - Wrappers to simply check if Damage_GetType is certain types.
//
//          function Damage_IsAttack takes nothing returns boolean
//              - Checks if the damage is from a physical attack (so you can deal
//                physical damage without it being registered as an actual attack).
//
//          function Damage_Block takes real amount returns nothing
//          function Damage_BlockAll takes nothing returns nothing
//              - For use only with Damage_RegisterEvent.
//              - Blocks 'amount' of the damage dealt.
//              - Multiple blocks at once work correctly.
//              - Blocking more than 100% of the damage will block 100% instead.
//              - Damage_BlockAll blocks 100% of the damage being dealt.
//
//          function Damage_EnableEvent takes boolean enable returns nothing
//              - For disabling and re-enabling the special event.
//              - Use it to deal damage which you do not want to be detected by
//                the special event.
//
//          function UnitDamageTargetEx takes lots of things returns boolean
//              - Replaces UnitDamageTarget in your map, with the same arguments.
//
//          function Damage_Physical takes unit source, unit target, real amount,
//            attacktype whichType, boolean attack, boolean ranged returns boolean
//              - A clean wrapper for physical damage.
//              - 'attack' determines if this is to be treated as a real physical
//                attack or just physical type damage.
//              - 'ranged' determines if this is to be treated as a ranged or melee
//                attack.
//
//          function Damage_Spell takes unit source, unit target, real amount returns boolean
//              - A clean wrapper for spell damage.
//
//          function Damage_Pure takes unit source, unit target, real amount returns boolean
//              - A clean wrapper for pure type damage (universal type, 100% damage).
//          
//      Thanks:
//     ¯¯¯¯¯¯¯¯¯
//          - Romek, for helping me find a better way to think about damage blocking.
//
library Damage uses AIDS, Event
    //============================================================
    //! external ObjectMerger w3a AIlz dprv anam "Life Bonus" ansf "(Damage System)" Ilif 1 500000 aite 0
    globals
        private constant integer LIFE_BONUS_ABIL='dprv'
    endglobals
    
    //============================================================
    globals
        private Event OnDamageEvent
        private Event OnZeroDamageEvent
        private boolean EventEnabled=true
    endglobals
    
    public function RegisterEvent takes trigger whichTrigger returns nothing
        call OnDamageEvent.register(whichTrigger)
    endfunction
    
    public function RegisterZeroEvent takes trigger whichTrigger returns nothing
        call OnZeroDamageEvent.register(whichTrigger)
    endfunction
    
    public function EnableEvent takes boolean enable returns nothing
        set EventEnabled=enable
    endfunction
    
    //============================================================
    globals
        private integer TypeStackLevel=0
        private damagetype array TypeStackValue
        private boolean array TypeStackAttack
        private real array ToBlock
    endglobals
    
    public function GetType takes nothing returns damagetype
        return TypeStackValue[TypeStackLevel]
    endfunction
    
    public function IsAttack takes nothing returns boolean
        return TypeStackAttack[TypeStackLevel]
    endfunction
    
    public function Block takes real amount returns nothing
        set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+amount
    endfunction
    
    public function BlockAll takes nothing returns nothing
        set ToBlock[TypeStackLevel]=ToBlock[TypeStackLevel]+GetEventDamage()
    endfunction
    
    //============================================================
    globals
        private integer BlockNum=0
        private unit array BlockUnit
        private real array BlockUnitLife
        private real array BlockRedamage
        private unit array BlockDamageSource
        
        private timer BlockTimer=CreateTimer()
    endglobals
    
    //============================================================
    globals
        private unit array RemoveBoosted
        private integer RemoveBoostedMax=0
        
        private timer RemoveBoostedTimer=CreateTimer()
    endglobals
    
    globals//locals
        private real BoostedLifeTemp
        private unit BoostedLifeUnit
    endglobals
    private function RemoveBoostedTimerFunc takes nothing returns nothing
        loop
            exitwhen RemoveBoostedMax==0
            set BoostedLifeUnit=RemoveBoosted[RemoveBoostedMax]
            set BoostedLifeTemp=GetWidgetLife(BoostedLifeUnit)
            call UnitRemoveAbility(BoostedLifeUnit,LIFE_BONUS_ABIL)
            if BoostedLifeTemp>0.405 then
                call SetWidgetLife(BoostedLifeUnit,BoostedLifeTemp)
            endif
            set RemoveBoostedMax=RemoveBoostedMax-1
        endloop
    endfunction
    
    //============================================================
    private keyword Detector // Darn, I actually had to do this. XD
    globals//locals
        private unit ForUnit
        private real NextHealth
    endglobals
    private function OnDamageActions takes nothing returns boolean
        if EventEnabled then
            if GetEventDamage()==0. then
                call OnZeroDamageEvent.fire()
            else
                call OnDamageEvent.fire()
            endif
            
            if ToBlock[TypeStackLevel]!=0. then
                //====================================================
                // Blocking
                set ForUnit=GetTriggerUnit()
                
                set NextHealth=GetEventDamage()
                if ToBlock[TypeStackLevel]>=NextHealth then
                    set NextHealth=GetWidgetLife(ForUnit)+NextHealth
                else
                    set NextHealth=GetWidgetLife(ForUnit)+ToBlock[TypeStackLevel]
                endif
                
                call SetWidgetLife(ForUnit,NextHealth)
                if GetWidgetLife(ForUnit)<NextHealth then
                    // NextHealth is over max health.
                    call UnitAddAbility(ForUnit,LIFE_BONUS_ABIL)
                    call SetWidgetLife(ForUnit,NextHealth)
                    
                    set RemoveBoostedMax=RemoveBoostedMax+1
                    set RemoveBoosted[RemoveBoostedMax]=ForUnit
                    call ResumeTimer(RemoveBoostedTimer)
                endif
                //====================================================
                set ToBlock[TypeStackLevel]=0.
            endif
        endif
        return false
    endfunction
    
    //============================================================
    function UnitDamageTargetEx takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
        local boolean result
        set TypeStackLevel=TypeStackLevel+1
        set TypeStackValue[TypeStackLevel]=damageType
        set TypeStackAttack[TypeStackLevel]=attack
        set result=UnitDamageTarget(whichUnit,target,amount,attack,ranged,attackType,damageType,weaponType)
        set TypeStackLevel=TypeStackLevel-1
        return result
    endfunction
    //! textmacro Damage__DealTypeFunc takes NAME, TYPE
        public function $NAME$ takes unit source, unit target, real amount returns boolean
            return UnitDamageTargetEx(source,target,amount,false,false,ATTACK_TYPE_NORMAL,$TYPE$,WEAPON_TYPE_WHOKNOWS)
        endfunction
        public function Is$NAME$ takes nothing returns boolean
            return GetType()==$TYPE$
        endfunction
    //! endtextmacro
    
    //! runtextmacro Damage__DealTypeFunc("Pure","DAMAGE_TYPE_UNIVERSAL")
    //! runtextmacro Damage__DealTypeFunc("Spell","DAMAGE_TYPE_MAGIC")
    
    // Uses different stuff, but works much the same way.
    public function Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
        return UnitDamageTargetEx(source,target,amount,attack,ranged,whichType,DAMAGE_TYPE_NORMAL,WEAPON_TYPE_WHOKNOWS)
    endfunction
    public function IsPhysical takes nothing returns boolean
        return GetType()==DAMAGE_TYPE_NORMAL
    endfunction
    
    //============================================================
    private struct Detector extends array // Uses AIDS.
        //! runtextmacro AIDS()
        
        private static conditionfunc ACTIONS_COND
        
        private trigger t
        
        private method AIDS_onCreate takes nothing returns nothing
            set this.t=CreateTrigger()
            call TriggerAddCondition(this.t,thistype.ACTIONS_COND)
            call TriggerRegisterUnitEvent(this.t,this.unit,EVENT_UNIT_DAMAGED)
        endmethod
        
        private method AIDS_onDestroy takes nothing returns nothing
            call DestroyTrigger(this.t)
        endmethod
        
        private static method AIDS_onInit takes nothing returns nothing
            set thistype.ACTIONS_COND=Condition(function OnDamageActions)
        endmethod
    endstruct
    
    //============================================================
    private module InitModule
        private static method onInit takes nothing returns nothing
            local unit abilpreload=CreateUnit(Player(15),'uloc',0,0,0)
            call UnitAddAbility(abilpreload,LIFE_BONUS_ABIL)
            call RemoveUnit(abilpreload)
            set abilpreload=null
            
            set OnDamageEvent=Event.create()
            set OnZeroDamageEvent=Event.create()
            set TypeStackValue[TypeStackLevel]=DAMAGE_TYPE_NORMAL
            set TypeStackAttack[TypeStackLevel]=true
            call TimerStart(RemoveBoostedTimer,0.0,false,function RemoveBoostedTimerFunc)
        endmethod
    endmodule
    private struct InitStruct extends array
        implement InitModule
    endstruct
endlibrary

TESH Highlighting:
JASS:
//
//      _____ ___ ___ _   _ 
//     |_   _| __/ __| |_| |
//       | | | _|\__ \  _  |
//       |_| |___|___/_| |_| Highlighting:
//                            - Damage by Jesus4Lyf.
//
//      What is this?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Implementing this allows the TESH syntax highlighter in Jass NewGen
//          to highlight the syntax for this system (and autocomplete the functions).
//          
//      How to import?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Copy this into a blank text file, and save it at:
//          <Your Jass NewGen folder>\tesh\includes\Damage.j
//
function Damage_RegisterEvent takes trigger whichTrigger returns nothing
    // Registers an event: Any unit is damaged.
endfunction
function Damage_RegisterZeroEvent takes trigger whichTrigger returns nothing
    // Registers an event: Any unit is dealt zero damage.
endfunction
function Damage_GetType takes nothing returns damagetype
    // Requires all damage in the map (except basic attacks) to be dealt with one of:
    //  - UnitDamageTargetEx
    //  - Damage_Physical
    //  - Damage_Spell
    //  - Damage_Pure
endfunction
function Damage_IsPhysical takes nothing returns boolean
    // Requires all damage in the map (except basic attacks) to be dealt with one of:
    //  - UnitDamageTargetEx
    //  - Damage_Physical
    //  - Damage_Spell
    //  - Damage_Pure
endfunction
function Damage_IsSpell takes nothing returns boolean
    // Requires all damage in the map (except basic attacks) to be dealt with one of:
    //  - UnitDamageTargetEx
    //  - Damage_Physical
    //  - Damage_Spell
    //  - Damage_Pure
endfunction
function Damage_IsPure takes nothing returns boolean
    // Requires all damage in the map (except basic attacks) to be dealt with one of:
    //  - UnitDamageTargetEx
    //  - Damage_Physical
    //  - Damage_Spell
    //  - Damage_Pure
endfunction
function Damage_IsAttack takes nothing returns boolean
    // Requires all damage in the map (except basic attacks) to be dealt with one of:
    //  - UnitDamageTargetEx
    //  - Damage_Physical
    //  - Damage_Spell
    //  - Damage_Pure
endfunction
function Damage_Block takes real amount returns nothing
    // Must only be used from a trigger with the event from:
    //  - Damage_RegisterEvent
endfunction
function Damage_BlockAll takes nothing returns nothing
    // Must only be used from a trigger with the event from:
    //  - Damage_RegisterEvent
endfunction
function Damage_EnableEvent takes boolean enable returns nothing
endfunction
function UnitDamageTargetEx takes unit source, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
    // The same parameters as the native UnitDamageTarget, but allows type detection.
endfunction
function Damage_Physical takes unit source, unit target, real amount, attacktype whichType, boolean attack, boolean ranged returns boolean
    // "attack" is whether or not the damage is an attack.
    // "ranged" is whether or not the damage is a ranged attack (set "attack" to true also).
endfunction
function Damage_Spell takes unit source, unit target, real amount returns boolean
endfunction
function Damage_Pure takes unit source, unit target, real amount returns boolean
endfunction

"Damage" is a full damage system for any map. It doesn't leak events internally, supports damage-on-damage functions without bugging (so it supports recursion), etc.

The main features are:
  • An AnyUnitDamaged event (which supports dynamic triggers just fine)
  • Instant damage blocking (only for triggers registered on the above event)
  • Get dealt damage type (as long as all damage is dealt with the functions from this system - except for basic attacks - which means the map's spells must damage through this system and not use damage from things like frost nova... so this feature is only for maps where the map is built to run on this system).
Demo map is a simple demonstration of stopping everything from dealing damage on the map. :)

Updates:
- Version 1.0.5: Added Damage_RegisterZeroEvent(trig).
- Version 1.0.4: Changed initialiser to module/struct to make it fire first.
- Version 1.0.3: Stopped detection of 0.0 damage events.
- Version 1.0.2: Added Damage_IsPure(), Damage_IsSpell(), Damage_IsPhysical() and Damage_IsAttack() (so damage can be physical but not count as an attack).
- Version 1.0.1: Changed the syntax from EnableEvent and DisableEvent to EnableEvent(boolean).
- Version 1.0.0: Release.
 

Attachments

  • Damage.w3x
    38.3 KB · Views: 1,673

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
Finally you submit this, once you told me about it on the chat I couldn't wait. :thup:
Looks good.
 

Viikuna

No Marlo no game.
Reaction score
265
It would be better to use use one trigger with multiple events to detect damage and recycle units to avoid event leaks, but I guess this is ok too...
 

Troll-Brain

You can change this now in User CP.
Reaction score
85
Recycle units is not easy, and i guess a good universal system can't be done without any Warcraft III 3rd party tools like GMSI .
If you want a good recycle system you should use many instant morph abilities and there are many side effect to play around them ...
 

Steel

Software Engineer
Reaction score
109
It would be a good idea to benchmark your system with a small, medium, large and very large number of units at once. I know from experience that having too many units in a system like this that has too many function calls or conditions can cause noticeable slowdowns. You may want to examine this because the slowdowns can get to the point where players can see what is happening and you always want to mask what your system is doing.
 

T.s.e

Wish I was old and a little sentimental
Reaction score
133
Who cares if this is one hundredth of a milisecond slower than another system, as long as it's easily used and actually works, that's enough for me.
 

Hatebreeder

So many apples
Reaction score
381
JASS:
    globals//locals
        private real BoostedLifeTemp
        private unit BoostedLifeUnit
    endglobals


What do you mean with Globals/Locals?
I don't see how they behave like locals, actually... Since they ARE globals, or?
I havn't used vJASS for a while now...
Many things changed :X
 

Romek

Super Moderator
Reaction score
963
He just uses global variables instead of local variables in the instant functions.
They're about 0.000000000001 nanoseconds faster, and don't need nulling. :p
 

Jesus4Lyf

Good Idea™
Reaction score
397
Yay, replies.

>It would be better to use use one
I'm not going to release a system that assumes players took the ridiculous measure to recycle units, and if they did, I believe the triggers in this would never actually get destroyed, so this supports unit recycling just fine anyway. :thup:

>It would be a good idea to benchmark your system
Benchmark -what- part of my system? Every single part of this system is O(1) complexity (in relation to its interface) so nothing should slow down anything at all. I don't know what you're talking about with units slowing things down, can you give me an example of why it might slow?

Looking at that alternative (no offense to the author, but these are the things I noticed):
IDDS:
  • Doesn't work: If you have a damage on damage action, the damage type and stuff screws up completely. It isn't recursively defined, which is VITAL to a system like this actually being reliable. I would never use IDDS for that reason - it breaks.
  • Uses TriggerUnregisterDamageEvent which isn't a nice interface, so this isn't behaving like a real event anyway, because you can't just destroy triggers.
  • Damage type is an integer (how is that intuitive?) which is actually one of the reasons I wrote this.
  • Uses H2I (Table) which will break on 1.24, etc. I do not want any of my maps to break when new patches come out. Purely personal choice, but I wrote this system out of necessity.
  • Has an O(n) complexity trigger sorting mechanism for some reason.
That was just at a quick glance.

>Who cares if this is one hundredth of a milisecond slower than another system
No, this system should be faster than other systems which try to accomplish the same thing. AND actually work. :) :thup:
 

Renendaru

(Evol)ution is nothing without love.
Reaction score
309
I've updated most triggers to use Damage and most things are running faster/better than they did. For example my trigger used to be delayed since I had to use a wait in a trigger for my old system. But now, it works perfectly without delays.
 

lep

Active Member
Reaction score
8
>Uses H2I (Table) which will break on 1.24, etc. I do not want any of my maps to break >when new patches come out. Purely personal choice, but I wrote this system out of >necessity.

Hell, no. Since he uses Table as a abstraction everyone using IDDS just have to update
Table.

>Has an O(n) complexity trigger sorting mechanism for some reason.

An O(n) sorting algorithm wouls be awesome....


>>Who cares if this is one hundredth of a milisecond slower than another system
>No, this system should be faster than other systems which try to accomplish the >same thing. AND actually work. :) :thup:

Ahh, that kind of mapper...
And don't use Dusks System but i would prefer his to this, because those priorities are actualy quite nice.
 

Jesus4Lyf

Good Idea™
Reaction score
397
>Hell, no. Since he uses Table as a abstraction everyone using IDDS just have to update Table.
Actually, he incorporated his own H2I and didn't use HandleTables. Besides (and this is irrelevant), even using HandleTables in your map will make it break for 1.24. I want my 1.23 RELEASE to work on 1.24.

>An O(n) sorting algorithm wouls be awesome....
Actually, his is O(n^2).

>Ahh, that kind of mapper...
Yes, that kind of mapper. One who makes things which work. The fact that most of my stuff is highly efficient is a side bonus.

By the way, you can do sequencing just fine. Just register multiple conditions or actions on one trigger in the order you want them run...? <_<

By all means, use whatever you like. I wrote this because I needed it, so I figured it would be useful for others. :)
 

Kenny

Back for now.
Reaction score
202
Awesome, finally a good all-purpose full damage system for maps. Looks very cool, and interface is simple as, love it.

Once again, if I actually needed damage detection, I think I found the system to use.

Nice work. :D
 

D.V.D

Make a wish
Reaction score
73
JASS:
function UnitDamageTargetEx takes lots of things returns boolean


What if a GUI user wanted to use this? He would have to search your code to find out what function calls he needs to do and even if he found it, he still might not know how to call the function.
 

Romek

Super Moderator
Reaction score
963
This system wasn't designed for GUI users.
 

D.V.D

Make a wish
Reaction score
73
But what if a GUI user want to use it? You could atleast just wright what the function takes.
 

uberfoop

~=Admiral Stukov=~
Reaction score
177
'But what if a foreign medical aid service wanted to use the Topol-M missile launch vehicle? You should at least include instructions on how to load medical supplies into the missile tube.'


Using AIDS within GUI would be horrifying to consider even if you were a master of both Jass and GUI. There's really no point facilitating it. I mean, if you're so beastly that you can actually use AIDS within GUI's structure, you'll have no trouble figuring out what that function takes.
 

Darthfett

Aerospace/Cybersecurity Software Engineer
Reaction score
615
But what if a GUI user want to use it?

Then he can spend the day or two learning JASS, instead of the week or two trying to figure out how to use a custom event system within GUI.

There are very few systems that should actually be used within GUI, and those are the one-function-call do-it-all-for-you systems, such as a knockback system.

Since there is no native way to add GUI function calls, every part of a system like this would need to deal with custom script. There are even conditional functions in here. There isn't any *native* custom script for conditions.

I would recommend adding in documentation about what the returned booleans mean for the functions, but other than that, good job. :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top