System Damage

Discussion in 'Systems and Snippets' started by Jesus4Lyf, Jul 6, 2009.

  1. Jesus4Lyf

    Jesus4Lyf Good Idea™

    Ratings:
    +394 / 0 / -0
    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.
     

    Attached Files:

    • Like Like x 6
  2. Renendaru

    Renendaru (Evol)ution is nothing without love.

    Ratings:
    +309 / 0 / -0
    Finally you submit this, once you told me about it on the chat I couldn't wait. :thup:
    Looks good.
     
  3. Viikuna

    Viikuna No Marlo no game.

    Ratings:
    +265 / 0 / -0
    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...
     
  4. Troll-Brain

    Troll-Brain You can change this now in User CP.

    Ratings:
    +85 / 0 / -0
    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 ...
     
  5. Steel

    Steel Software Engineer

    Ratings:
    +109 / 0 / -0
    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.
     
  6. T.s.e

    T.s.e Wish I was old and a little sentimental

    Ratings:
    +133 / 0 / -0
    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.
     
  7. Hatebreeder

    Hatebreeder So many apples

    Ratings:
    +383 / 0 / -0
    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
     
  8. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    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
     
  9. wraithseeker

    wraithseeker Tired.

    Ratings:
    +122 / 0 / -0
    It seems similar to Dusk's IDDS

    The other optional ones are here and here
     
  10. Jesus4Lyf

    Jesus4Lyf Good Idea™

    Ratings:
    +394 / 0 / -0
    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:
     
  11. Renendaru

    Renendaru (Evol)ution is nothing without love.

    Ratings:
    +309 / 0 / -0
    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.
     
  12. lep

    lep Active Member

    Ratings:
    +8 / 0 / -0
    >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.
     
  13. Jesus4Lyf

    Jesus4Lyf Good Idea™

    Ratings:
    +394 / 0 / -0
    >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. :)
     
  14. Kenny

    Kenny Back for now.

    Ratings:
    +202 / 0 / -0
    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
     
    • Like Like x 1
  15. Jesus4Lyf

    Jesus4Lyf Good Idea™

    Ratings:
    +394 / 0 / -0
    Thanks. :thup:

    Updated with some Damage_IsType() wrappers, and added Damage_IsAttack() so physical damage can be physical without being registered as an attack. :)

    That's v1.0.2. :D
     
  16. D.V.D

    D.V.D Make a wish

    Ratings:
    +73 / 0 / -0
    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.
     
  17. Romek

    Romek Super Moderator Staff Member

    Ratings:
    +961 / 0 / -0
    This system wasn't designed for GUI users.
     
  18. D.V.D

    D.V.D Make a wish

    Ratings:
    +73 / 0 / -0
    But what if a GUI user want to use it? You could atleast just wright what the function takes.
     
  19. uberfoop

    uberfoop ~=Admiral Stukov=~

    Ratings:
    +176 / 0 / -0
    '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.
     
    • Like Like x 1
  20. Darthfett

    Darthfett Super Mod

    Ratings:
    +614 / 0 / -0
    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. :)
     

Share This Page