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

Damage System Help

Status
Not open for further replies.
Level 8
Joined
Feb 17, 2007
Messages
368
This is the jass code I'm using for my damage system:

JASS:
library PlayerDamage initializer init requires Damage

    //##################################################################################
    globals
    
        public      real array          playerDamage

        public      constant integer    PLAYER_COUNT      = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            set s = udg_CounterColor[GetPlayerId(p[i])+1]+GetPlayerName(p[i])+":    "+R2S(r[i])
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        
        if GetEventDamage() > GetWidgetLife(GetTriggerUnit()) then
        set playerDamage[id] = playerDamage[id] + GetWidgetLife(GetTriggerUnit())
        //if the dealt damage exceeds the unit's life then the "real" amount of
        //damage would be the unit's current life, because that's how much damage
        //is required to kill it.
    else
        set playerDamage[id] = playerDamage[id] + GetEventDamage()
        //in any other situation we can just add the damage of the event to the
        //counter as "full-damage" was dealt.
    endif
endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set i = i + 1
        endloop
    endfunction
endlibrary

I don't really understand Jass as I use GUI, and had Berb make this for me on this site. I implemented it and thought it worked, but there are a couple problems now that I've come back to this script and taken a look at it. Firstly it register's more damage than is possible; i.e. when a boss has 20,000 hp it can register a single players damage as higher than that; which shouldn't be possible. I thought this was fixed by the function Berb implemented, but it still seems possible to have greater damage than the bosses hp. Also I'm not sure how to use the call ResetAllDamage function as when I try it, it gives me an error. Can anyone help me with these problems and look over the code? Berb if your here then obviously it would be great if u could look at it. :thumbs_up:
 
Level 8
Joined
Feb 17, 2007
Messages
368
Thx for that WarManta, but I want to have something more functional at the moment than what you have there. ;) If anyone can help me with this Jass code I would really appreciate it. Although I hesitate to use jass and avoid it at all possible times, as long as the functions are working then I don't have a problem with this system.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay so what I've gathered is that for some reason a player is capable of inflicting more damage than a unit has life points?

In other words, let's say a boss has 2000 hit points; are you saying that the script seems to detect more than 2000 points of damage?

Also I'm not sure how to use the call ResetAllDamage function as when I try it, it gives me an error.

What error exactly? All that function does is iterate from 0 to PLAYER_COUNT (which by default is 8) and set an array value to 0. Can you give me any more details?
 
Last edited:
Level 8
Joined
Feb 17, 2007
Messages
368
Okay so what I've gathered is that for some reason a player is capable of inflicting more damage than a unit has life points?

In other words, let's say a boss has 2000 hit points; are you saying that the script seems to detect more than 2000 points of damage?



What error exactly? All that function does is iterate from 0 to PLAYER_COUNT (which by default is 8) and set an array value to 0. Can you give me any more details?

I'm saying that when a unit uses his abilities on the boss, or uses items which deal damage, the unit is capable of inflicting more damage than the bosses hp, which shouldn't be possible. Because if you did more damage than the bosses hp, the boss would already be dead and you would not be able to inflict anymore damage than the bosses hp.

Well I'm getting an error because I'm not sure how to call the function. All I did was call ResetAllDamage(8) . But I'm not sure if thats how you do it. I will never need to reset the damage for just one player, so all I want is the function to reset all the damage, for every single player.
 
Level 40
Joined
Dec 14, 2005
Messages
10,532
Damage detection systems don't care about how much damage it is possible to inflict, just how much damage is inflicted. That is, if I hit a unit with 3 life remaining for 50 damage, the system will tell me I did 50 damage. This is necessary if you are coding any sort of damage reduction system, texttags to note damage, etc, and if you aren't then live with it; it should be pretty easy to work around (just take min(unit life, damage dealt)).

--

If you want to reset damage for just one player, call ResetPlayerDamage(PlayerToReset). ResetAllDamage takes no parameters and is essentially the "All Players" version of ResetPlayerDamage. Note that PlayerToReset is a player object and not a number.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
This is necessary if you are coding any sort of damage reduction system, texttags to note damage, etc, and if you aren't then live with it; it should be pretty easy to work around (just take min(unit life, damage dealt)).

I believe I do this here:

JASS:
    if GetEventDamage() > GetWidgetLife(GetTriggerUnit()) then
        set playerDamage[id] = playerDamage[id] + GetWidgetLife(GetTriggerUnit())
        //if the dealt damage exceeds the unit's life then the "real" amount of
        //damage would be the unit's current life, because that's how much damage
        //is required to kill it.
    else
        set playerDamage[id] = playerDamage[id] + GetEventDamage()
        //in any other situation we can just add the damage of the event to the
        //counter as "full-damage" was dealt.
    endif

Is it a possibility that I am using this Damage library wrong in some way? Does anybody have more experience using this particular library?

I use the library to register an any unit takes damage event and then add function onDamage to its event response actions. The owning player of the damaging unit is converted to an index value and then the associated playerDamage[] array element is incremented in one of two possibilities.

1.
If the damage dealt (the final damage amount) is larger than the hit points of the damaged unit then the hit points of this unit is the magnitude of the increment.

2.
If the damage dealt is smaller than the hit points of the damaged then the damage dealt is used as the magnitude of the increment.

I'm saying that when a unit uses his abilities on the boss, or uses items which deal damage, the unit is capable of inflicting more damage than the bosses hp, which shouldn't be possible.

I agree, it shouldn't be possible for more damage to be inflicted than a unit has HP - I cannot seem to see a reason as to why this would ever be the case though because in the onDamage function I always ensure that damage exceeding the amount of life a unit has (lethal damage) does not cause the playerDamage array element to increment by more than the total possible amount of damage a unit could receive.

There are other possibilities that come to mind in this case; are you sure that there isn't miscellaneous or arbitrary damage being dealt that could potentially be caused by the players? This may explain why it seems more damage is being added to a player's damage counter than it seems should be. If you think this may be the case then I can fix up the script a bit so that only certain unit types (in your case I suppose "bosses") are recognized as "player damage".
 
Level 8
Joined
Feb 17, 2007
Messages
368
There are other possibilities that come to mind in this case; are you sure that there isn't miscellaneous or arbitrary damage being dealt that could potentially be caused by the players? This may explain why it seems more damage is being added to a player's damage counter than it seems should be. If you think this may be the case then I can fix up the script a bit so that only certain unit types (in your case I suppose "bosses") are recognized as "player damage".

Yes! I realized that there's other units owned by the Boss which must be adding to the player's damage.... hahaha. XD There are multiple bosses, so can you set it up so I can easily add boss types?

Also how do I use the call ResetAllDamage function? What does PurplePoot mean by player object? I just say
  • Custom script: call ResetAllDamage
and then what do I put in parenthesis after??
 
Last edited:
Level 40
Joined
Dec 14, 2005
Messages
10,532
If you want to reset player N's damage, where N is 0 for red, 1 for blue, 2 for teal etc, you can do:

  • Custom script: call ResetPlayerDamage(Player(N))
From a Player variable called myPlayer:

  • Custom script: call ResetPlayerDamage(udg_myPlayer)
From the owner of a unit called myUnit (note you can replace udg_myUnit with GetTriggerUnit() if you want to use the Triggering Unit, for example):

  • Custom script: call ResetPlayerDamage(GetOwningPlayer(udg_myUnit))
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Here's some new code; if you read the comments near the top (Utility Functions section) it explains how you would go about adding a unit type to the boss-filter. I give examples with the Footman and the Paladin - you can use whatever raw-codes you may have for the bosses.

If there are any errors please say something, I do not have the Damage library and I do not know where udg_CounterColor[] came from, but I am assuming it's something you use in the actual map but I can't really check for these errors.

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public integer array        bossType
        public integer              bosses          = 0

        public constant integer     PLAYER_COUNT    = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            set s = udg_CounterColor[GetPlayerId(p[i])+1]+GetPlayerName(p[i])+":    "+R2S(r[i])
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(GetTriggerUnit()) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(GetTriggerUnit())
                
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                
            endif
        endif
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set i = i + 1
        endloop
    endfunction
endlibrary
 
Level 8
Joined
Feb 17, 2007
Messages
368
It works perfectly man, your awesome. =D 1 thing, is it possible to add a damage overall print function? So that the damage done in total overall is displayed? How would I go about doing this if I want to reset every players damage at the end of the boss fight, but still want the overall damage done by each player recorded and able to be printed? Do you see what I'm saying?
 
Level 8
Joined
Feb 17, 2007
Messages
368
The exact same way it looks now except that the printed damages for each player are the overall damages that they've done in total as multiple boss fights go on, and they reach the end of the game. But would this be possible since I want to reset every players damage to 0 after each boss fight, but I still want a seperate command called 'dmgo' that displays the overall damage done by all players.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay; I know what you mean. It'll be a moment.

Okay so I've got a new array setup that stores the total amount of damage that has been dealt by a player. This is referenced as GetPlayerDamageTotal() (it works the same as GetPlayerDamage).

Now to talk about the printing; I'm really not sure how this looks it's been awhile since I made this script so could you be a little more specific (maybe a picture) to describe what it should look like - in the mean time I'll try to figure it out by reading the code.

Tell me how this looks:

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            set s = udg_CounterColor[GetPlayerId(p[i]) + 1] + GetPlayerName(p[i]) + /*
                    */ ":    " + R2S(r[i]) + " (" + R2S(playerDamageTotal[i]) + ")"
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay yea in the private function isBossType is missing a return statement at the end. Here's a fix:

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            set s = udg_CounterColor[GetPlayerId(p[i]) + 1] + GetPlayerName(p[i]) + /*
                    */ ":    " + R2S(r[i]) + " (" + R2S(playerDamageTotal[i]) + ")"
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay, one second; I need to add that.

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function ResetPlayerDamageTotal takes player p returns nothing
        set playerDamageTotal[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamageTotal takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i == PLAYER_COUNT
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            set s = udg_CounterColor[GetPlayerId(p[i]) + 1] + GetPlayerName(p[i]) + /*
                    */ ":    " + R2S(r[i]) + " (" + R2S(playerDamageTotal[i]) + ")"
            call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary

I added two functions that are similar to ones already available.

JASS:
function ResetPlayerDamageTotal takes player p returns nothing

This works the exact same as ResetPlayerDamage except it resets the total damage counter.

JASS:
function ResetAllDamageTotal takes nothing returns nothing

This works the exact same as ResetAllDamage except it resets every player's total damage counter.
 
Level 8
Joined
Feb 17, 2007
Messages
368
I just realized I need to make it so it only appears for players that are in the game, and not just for all 8 players. And when a player leaves, it should remove them from the damage table. Then after that I am definitely positive it'll be a final draft, haha. =)

1 other thing, the color code for the print damage function seems to be messing up, and I'm really not sure why. All the players names are in red... lol.
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Okay; I'm going to look over it I think that what you're asking should be fairly simple. I will have an if-statement that makes sure the player who's score is being displayed is actually playing in the game.

The color-code seems like it may be a problem on your end, I simply reference an array udg_CounterColor which would be CounterColor defined in GUI.

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    function PrintDamage takes real time returns nothing
        local integer i = 0
        local integer j
        local integer h
        local string  s
        
        local real rt
        local player pt
        
        local real array r
        local player array p
        loop
            exitwhen(i == PLAYER_COUNT)
            set r[i]    = playerDamage[i]
            set p[i]    = Player(i)
            set i = i + 1
        endloop
        
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set h = i
            set j = i
            loop
                exitwhen(j == PLAYER_COUNT)
                if (h != j) and (r[j] > r[h]) then
                    set h = j
                        
                endif
                set j = j + 1
            endloop            
            
            set rt      = r[h]
            set pt      = p[h]
            set p[h]    = p[i]
            set r[h]    = r[i]
            set p[i]    = pt
            set r[i]    = rt
            
            // --
            // if the player is vacant from the game, his/her score will not be displayed
            // amongst the other players.
            //
            if (GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(p[i]) + 1] + GetPlayerName(p[i]) + /*
                        */ ":    " + R2S(r[i]) + " (" + R2S(playerDamageTotal[i]) + ")"
                call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            endif
            
            set i = i + 1
        endloop
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary

Tell me how this looks when you use it.
 
Level 8
Joined
Feb 17, 2007
Messages
368
Works perfectly! Thanks Berb! =) And yeah for the color code problem, it's stemming from me changing a players name to the class of the unit they have in parenthesis. Here are all the triggers associated with the color codes:

  • Actions
    • Set CounterColor[1] = |CFFFF0303
    • Set CounterColor[2] = |CFF0042FF
    • Set CounterColor[3] = |CFF1CB619
    • Set CounterColor[4] = |CFF540081
    • Set CounterColor[5] = |CFFFFFF01
    • Set CounterColor[6] = |CFFFE8A0E
    • Set CounterColor[7] = |CFF20C000
    • Set CounterColor[8] = |CFFE55BB0
    • Player - Set name of Player 1 (Red) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 1 (Red)))
    • Player - Set name of Player 2 (Blue) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 2 (Blue)))
    • Player - Set name of Player 3 (Teal) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 3 (Teal)))
    • Player - Set name of Player 4 (Purple) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 4 (Purple)))
    • Player - Set name of Player 5 (Yellow) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 5 (Yellow)))
    • Player - Set name of Player 6 (Orange) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 6 (Orange)))
    • Player - Set name of Player 7 (Green) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 7 (Green)))
    • Player - Set name of Player 8 (Pink) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 8 (Pink)))
    • Player Group - Pick every player in (All players) and do (Actions)
      • Loop - Actions
        • Set player_name_raw[(Player number of (Picked player))] = (Name of (Picked player))
When a player chooses a hero it changes to this:

  • Player - Set name of (Triggering player) to (player_name_raw[(Player number of (Triggering player))] + (( ( + (Proper name of (Last created unit))) + (CounterColor[(Player number of (Triggering player))] + ))))
Maybe you can help me get it straight, haha. I'm not sure why it's messing up, getting this straight is a little confusing. I want the color of the players name to stay the same, but the heroes proper name color to be in parenthesis as is in this screenshot for the deathknights name:

 
Level 18
Joined
Jan 21, 2006
Messages
2,552
  • Player - Set name of Player 1 (Red) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 1 (Red)))
  • Player - Set name of Player 2 (Blue) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 2 (Blue)))
  • Player - Set name of Player 3 (Teal) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 3 (Teal)))
  • Player - Set name of Player 4 (Purple) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 4 (Purple)))
  • Player - Set name of Player 5 (Yellow) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 5 (Yellow)))
  • Player - Set name of Player 6 (Orange) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 6 (Orange)))
  • Player - Set name of Player 7 (Green) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 7 (Green)))
  • Player - Set name of Player 8 (Pink) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 8 (Pink)))

Shouldn't you be using the player numbers instead of Triggering player here? I imagine this is run on initialization, so what is this supposed to do?
 
Level 8
Joined
Feb 17, 2007
Messages
368
Yeah it's run on initialization. It sets and saves the player's names into color array strings. And no I think triggering player is the appropriate action. After I set all there names to this I save the names for each player as player_name_raw.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
You're not thinking this through. You're using the color-code of Triggering player for every single player who's color you're changing. This is why they are all one color.

Also, what exactly are you expecting the Triggering player to be on map initialization? There is no triggering player on map initialization, that makes about as much sense as using "Triggering unit" on the "Player leaves game" event.

How about you look at it like this; take the color-code of Triggering player and let's assign it to a variable name. Let's call it trigColor.

Your GUI looks like:
  • Player - Set name of Player 1 (Red) to (trigColor + (Name of Player 1 (Red)))
  • Player - Set name of Player 2 (Blue) to (trigColor + (Name of Player 2 (Blue)))
  • Player - Set name of Player 3 (Teal) to (trigColor + (Name of Player 3 (Teal)))
  • Player - Set name of Player 4 (Purple) to (trigColor + (Name of Player 4 (Purple)))
  • Player - Set name of Player 5 (Yellow) to (trigColor + (Name of Player 5 (Yellow)))
  • Player - Set name of Player 6 (Orange) to (trigColor + (Name of Player 6 (Orange)))
  • Player - Set name of Player 7 (Green) to (trigColor + (Name of Player 7 (Green)))
  • Player - Set name of Player 8 (Pink) to (trigColor + (Name of Player 8 (Pink)))
Looking at this, how exactly are you expecting these players to obtain different color names when they're all being assigned the color of trigColor.
 
Level 8
Joined
Feb 17, 2007
Messages
368
No no, I think your confused. It's an array, thats why lol. Here is where I set all the colors up based on the players number:

  • Set CounterColor[1] = |CFFFF0303
  • Set CounterColor[2] = |CFF0042FF
  • Set CounterColor[3] = |CFF1CB619
  • Set CounterColor[4] = |CFF540081
  • Set CounterColor[5] = |CFFFFFF01
  • Set CounterColor[6] = |CFFFE8A0E
  • Set CounterColor[7] = |CFF20C000
  • Set CounterColor[8] = |CFFE55BB0
The numbers correspond to the player numbers. Player number of triggering player matches these numbers with the correct players number.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
That's not the part of the code that I'm talking about. I realize that is where you initialize the array, but that is not where you set the color of player's names.

You said earlier that you were having problems because all their names showed up as the same color. This is why, you're setting all their colors to the color of Triggering player. Not to mention you shouldn't even be using the Triggering player event-response in map initialization, it makes no sense.

There is no such thing as Triggering player on map initialization, where this code is located.

  • Set CounterColor[1] = |CFFFF0303
  • Set CounterColor[2] = |CFF0042FF
  • Set CounterColor[3] = |CFF1CB619
  • Set CounterColor[4] = |CFF540081
  • Set CounterColor[5] = |CFFFFFF01
  • Set CounterColor[6] = |CFFFE8A0E
  • Set CounterColor[7] = |CFF20C000
  • Set CounterColor[8] = |CFFE55BB0
  • Player - Set name of Player 1 (Red) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 1 (Red)))
  • Player - Set name of Player 2 (Blue) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 2 (Blue)))
  • Player - Set name of Player 3 (Teal) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 3 (Teal)))
  • Player - Set name of Player 4 (Purple) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 4 (Purple)))
  • Player - Set name of Player 5 (Yellow) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 5 (Yellow)))
  • Player - Set name of Player 6 (Orange) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 6 (Orange)))
  • Player - Set name of Player 7 (Green) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 7 (Green)))
  • Player - Set name of Player 8 (Pink) to (CounterColor[(Player number of (Triggering player))] + (Name of Player 8 (Pink)))

This is all in your initialization trigger. You set every single player's name to the color of Triggering player. I don't understand how you cannot see this; and I'm not confused I know exactly what I'm talking about.

If you replace Player number of (Triggering player) with the number associated with each player it will work fine.
 
Level 8
Joined
Feb 17, 2007
Messages
368
No I see what you're saying, but I thought that particular action would work because of the Player Number part. I'll try it under another event, however, you do understand that Player Number of Triggering Player is different than triggering player, right? The Number part refers to the number corresponding to the color in the array.

EDIT: All right hold on, I'm reconfiguring it.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
you do understand that Player Number of Triggering Player is different than triggering player, right? The Number part refers to the number corresponding to the color in the array.

Yea but do you understand that using Player number of (Triggering player) refers to the same number each time you use it in the same block of code, it's not going to know that you want to increment the number by 1 for each time you call it.

Let's say Triggering player was player 1 (red). The number of triggering player will return 1. So, let's look at what the code will be given Triggering player refers to player 1.

  • Player - Set name of Player 1 (Red) to (CounterColor[1] + (Name of Player 1 (Red)))
  • Player - Set name of Player 2 (Blue) to (CounterColor[1] + (Name of Player 2 (Blue)))
  • Player - Set name of Player 3 (Teal) to (CounterColor[1] + (Name of Player 3 (Teal)))
  • Player - Set name of Player 4 (Purple) to (CounterColor[1] + (Name of Player 4 (Purple)))
  • Player - Set name of Player 5 (Yellow) to (CounterColor[1] + (Name of Player 5 (Yellow)))
  • Player - Set name of Player 6 (Orange) to (CounterColor[1] + (Name of Player 6 (Orange)))
  • Player - Set name of Player 7 (Green) to (CounterColor[1] + (Name of Player 7 (Green)))
  • Player - Set name of Player 8 (Pink) to (CounterColor[1] + (Name of Player 8 (Pink)))
So how are you expecting this to give players different colors?
 
Level 8
Joined
Feb 17, 2007
Messages
368
Ok I fixed it by putting in manually the numbers for each player. :) Yeah I get what your saying though Berb, I just thought that the Set Name of Player part would automatically assign each player the number for that specific player, combined with the player number of triggering player. But ok I get it now! Thanks for being patient, as always. Don't know if you noticed but I +reped you. :D
 
Level 8
Joined
Feb 17, 2007
Messages
368
Heh, alright well thanks for the lesson. :p One more thing, how can I get the Print Damage function to display for the triggering player? The function is GetTriggerPlayer correct? But how would I insert this into the call PrintDamage function?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
I'm just looking at the PrintDamage function now and it seems to take real time - I'm not really sure what this is for and it really isn't used in the function. Do you know what the purpose of it is?

It seems the PrintDamage function is used to display all the players' scores, so I'm not sure how you want to display only a specific player's score. Can you explain a little more?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
That is a pickle, the function is currently setup to simply display it to every player. There are a few ways we could go about solving this problem though.

I could add a player parameter to the PrintDamage function so that you can specify which player specifically you want to show the text to. This would also require you modify all your references to PrintDamage with this extra parameter (and it would no longer display it to every player).

I could just copy and paste the function and change it so you have one for all players and one for one specific player.

These solutions all seem to be really ugly, so why don't you tell me how you would like to interface with it and then I'll just make it accustomed to that.
 
Level 8
Joined
Feb 17, 2007
Messages
368
Lol! A pickle. =) Well the latter sounds good actually, haha. =p Because I need to have the option to show it to the specific player who says the dmg command, and then I want the function to also be able to be displayed to everyone after the end of a boss fight.
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Eh, I'll just do some complex shit and see how well that comes out.

-- In looking through the code I finally figured out why PrintDamage takes real time, and that is for how much time the scores will be displayed for :D

I'm re-working it so that the damage-counter can be separate from the print function so that it can be sorted independently. I'll be able to then have two functions that work at fairly optimal speeds, without having to duplicate too much code.

-- Can you post the Damage library? I need it so that I can test my code otherwise it just gives errors.

-- I just made a dud library named Damage that had the function I use in it so I was able to by-pass that and there aren't any syntax errors but I'm not sure if this is going to work properly, so try it out and tell me how it plays.

The PrintDamage function has remained intact, so you shouldn't have to change anything. I added a struct to store scores and do the sorting which I also use in a secondary PrintDamageToPlayer which takes an additional player parameter (this is the player you want to display it to).

In order to use it with something like GetTriggerPlayer() (let's say for 1 second) it would look like this:

JASS:
call PrintDamageToPlayer(1, GetTriggerPlayer())
 
Last edited:
Level 8
Joined
Feb 17, 2007
Messages
368
Sounds good! And sure, here it is:

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
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Sorry I forgot to post the code:

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    private struct damagecounter
        real array score[PLAYER_COUNT]
        player array scorePlayer[PLAYER_COUNT]
        
        method sort takes nothing returns nothing
            local integer i = 0
            local integer j
            local integer l
            local real t
            local player p
            
            loop
                exitwhen (i == PLAYER_COUNT)
                set l = i
                set j = i
                loop
                    exitwhen (j == PLAYER_COUNT)
                    if (score[j] < score[l]) then
                        set l = j
                    endif
                    set j = j + 1
                endloop
                set p = scorePlayer[i]                  // This should keep the list of players
                set scorePlayer[i] = scorePlayer[l]     // associated with each score sorted along
                set scorePlayer[l] = p                  // with the score so that the associated
                set t = score[i]                        // player can later be referenced.
                set score[i] = score[l]
                set score[l] = t
                
                set i = i + 1
            endloop
        endmethod
    endstruct
    
    function PrintDamage takes real time returns nothing
        local damagecounter data = damagecounter.create()
        local string s
        local integer i = 0
        
        loop
            exitwhen (i == PLAYER_COUNT)
            set data.score[i] = playerDamage[i]
            set data.scorePlayer[i] = Player(i)
            set i = i + 1
        endloop
    
        call data.sort()
        
        set i = PLAYER_COUNT
        loop
            exitwhen (i < 0)
                
            if (GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(data.scorePlayer[i]) + 1] + /*
                        */ GetPlayerName(data.scorePlayer[i]) + ":    " + R2S(data.score[i]) /*
                        */ + " (" + R2S(playerDamageTotal[GetPlayerId(data.scorePlayer[i])]) + ")"
                        
                call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            endif
            
            set i = i - 1
        endloop
        
        call data.destroy()
    endfunction
    
    function PrintDamageToPlayer takes real time, player to returns nothing
        local damagecounter data = damagecounter.create()
        local string s
        local integer i = 0
        
        loop
            exitwhen (i == PLAYER_COUNT)
            set data.score[i] = playerDamage[i]
            set data.scorePlayer[i] = Player(i)
            set i = i + 1
        endloop
    
        call data.sort()
        
        set i = PLAYER_COUNT
        loop
            exitwhen (i < 0)
                
            if (GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(data.scorePlayer[i]) + 1] + /*
                        */ GetPlayerName(data.scorePlayer[i]) + ":    " + R2S(data.score[i]) /*
                        */ + " (" + R2S(playerDamageTotal[GetPlayerId(data.scorePlayer[i])]) + ")"
                        
                call DisplayTimedTextToPlayer(to, 0, 0, time, s)
            endif
            
            set i = i - 1
        endloop
        
        call data.destroy()
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary

-- In order to use the Damage library I also need AIDS and Event. It may be easier if you just test out the code and see what works/doesn't work first.
 
Level 8
Joined
Feb 17, 2007
Messages
368
AIDS:

JASS:
//  
//        _   ___ ___  ___    _______________________________________________
//       /_\ |_ _|   \/ __|   ||     A D V A N C E D   I N D E X I N G     ||
//      / _ \ | || |) \__ \   ||                  A N D                    ||
//     /_/ \_\___|___/|___/   ||         D A T A   S T O R A G E           ||
//            By Jesus4Lyf    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//                                                                    v 1.0.5
//      What is AIDS?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          AIDS assigns unique integers between 1 and 8191 to units which enter
//          the map. These can be used for arrays and data attaching.
//          
//          AIDS also allows you to define structs which are created automatically
//          when units enter the map, and filtering which units should be indexed
//          as well as for which units these structs should be created.
//          
//      How to implement?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Simply create a new trigger object called AIDS, go to 'Edit -> Convert
//          to Custom Text', and replace everything that's there with this script.
//
//          Save the map, close it, reopen it, and then delete the "!" from the
//          FAR left side of the next lines (so "external" will line up with this line):
//!          external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
//          At the top of the script, there is a 'UnitIndexingFilter' constant
//          function. If the function returns true for the unit, then that unit
//          will be automatically indexed. Setting this to true will automatically
//          index all units. Setting it to false will disable automatic indexing.
//
//      Functions:
//     ¯¯¯¯¯¯¯¯¯¯¯¯
//          function GetUnitId takes unit u returns integer
//              - This returns the index of an indexed unit. This will return 0
//                if the unit has not been indexed.
//              - This function inlines. It does not check if the unit needs an
//                index. This function is for the speed freaks.
//              - Always use this if 'UnitIndexingFilter' simply returns true.
//
//          function GetUnitIndex takes unit u returns integer
//              - This will return the index of a unit if it has one, or assign
//                an index if the unit doesn't have one (and return the new index).
//              - Use this if 'UnitIndexingFilter' doesn't return true.
//
//          function GetIndexUnit takes integer index returns unit
//              - This returns the unit which has been assigned the 'index'.
//
//      AIDS Structs:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - Insert: //! runtextmacro AIDS() at the top of a struct to make it
//            an AIDS struct.
//          - AIDS structs cannot be created or destroyed manually. Instead, they
//            are automatically created when an appropriate unit enters the map.
//          - You cannot give members default values in their declaration.
//            (eg: private integer i=5 is not allowed)
//          - You cannot use array members.
//          - AIDS structs must "extend array". This will remove some unused
//            functions and enforce the above so there will be no mistakes.
//          - There are four optional methods you can use in AIDS structs:
//              - AIDS_onCreate takes nothing returns nothing
//                  - This is called when the struct is 'created' for the unit.
//                  - In here you can assign members their default values, which
//                    you would usually assign in their declarations.
//                    (eg: set this.i=5)
//              - AIDS_onDestroy takes nothing returns nothing
//                  - This is called when the struct is 'destroyed' for the unit.
//                  - This is your substitute to the normal onDestroy method.
//              - AIDS_filter takes unit u returns boolean
//                  - This is similar to the constant filter in the main system.
//                  - Each unit that enters the map will be tested by each AIDS
//                    struct filter. If it returns true for that unit, that unit
//                    will be indexed if it was not already, the AIDS struct will
//                    have its AIDS_onCreate method called, and later have its
//                    AIDS_onDestroy method called when the index is recycled.
//                  - Not declaring this will use the default AIDS filter instead.
//              - AIDS_onInit takes nothing returns nothing
//                  - This is because I stole your onInit function with my textmacro.
//          - You can use '.unit' from any AIDS struct to get the unit for which
//            the struct is for.
//          - The structs id will be the units index, so getting the struct for
//            a unit inlines to a single native call, and you can typecast between
//            different AIDS structs. This is the premise of AIDS.
//          - Never create or destroy AIDS structs directly.
//          - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
//            decrease the lock level on the struct. If a struct's lock level is
//            not 0, it will not be destroyed until it is reduced to 0. Locks just
//            put off AIDS struct destruction in case you wish to attach to a timer
//            or something which must expire before the struct data disappears.
//            Hence, not freeing all locks will leak the struct (and index).
//
//      PUI and AutoIndex:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - AIDS includes the PUI textmacros and the AutoIndex module, because
//            these systems are not compatible with AIDS but have valid and distinct
//            uses.
//          - The PUI textmacros are better to use for spells than AIDS structs,
//            because they are not created for all units, just those targetted by
//            the spell (or whatever else is necessary).
//          - The AutoData module is good for very simple array syntax for data
//            attachment (although I don't recommend that people actually use it,
//            it's here mostly for compatability). Note that unlike the PUI textmacros,
//            units must pass the AIDS filter in order for this module to work with
//            them. This is exactly as the same as in AutoIndex itself (AutoIndex
//            has a filter too).
//          
//      Thanks:
//     ¯¯¯¯¯¯¯¯¯
//          - Romek, for writing 90% of this user documentation, challenging my
//            interface, doing some testing, suggesting improvements and inspiring
//            me to re-do my code to include GetUnitIndex as non-inlining.
//          - grim001, for writing the AutoData module, and AutoIndex. I used the
//            on-enter-map method that he used. Full credits for the AutoData module.
//          - Cohadar, for writing his PUI textmacros. Full credits to him for these,
//            except for my slight optimisations for this system.
//            Also, I have used an optimised version of his PeriodicRecycler from
//            PUI in this system to avoid needing a RemoveUnitEx function.
//          - Vexorian, for helping Cohadar on the PUI textmacro.
//          - Larcenist, for suggesting the AIDS acronym. Originally he suggested
//            'Alternative Index Detection System', but obviously I came up with
//            something better. In fact, I'd say it looks like the acronym was
//            an accident. Kinda neat, don't you think? :P
//
//      Final Notes:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          - With most systems, I wouldn't usually add substitutes for alternative
//            systems. However, UnitData systems are an exception, because they
//            are incompatible with eachother. Since using this system forbids
//            people from using the real PUI or AutoIndex, and a lot of resources
//            use either of these, it made sense not to break them all.
//
//          - If this documentation confused you as to how to use the system, just
//            leave everything as default and use GetUnitId everywhere.
//
//          - To use this like PUI (if you don't like spamming indices) simply
//            make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS
    //==============================================================================
    // Configurables
    //
    globals
        private constant boolean USE_PERIODIC_RECYCLER = false
        private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
                                               // Lower to be able to recycle faster.
                                               // Only used if USE_PERIODIC_RECYCLER
                                               // is set to true.
        
        private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
    endglobals
    
    private function UnitIndexingFilter takes unit u returns boolean
        return true
    endfunction
    
    //==============================================================================
    // System code
    //
    globals
        // The unit stored at an index.
        private unit array IndexUnit
        private integer array LockLevel
    endglobals
    
    //==============================================================================
    globals
        // Recycle stack
        private integer array RecycledIndex
        private integer MaxRecycledIndex = 0
        
        // Previous highest index
        private integer MaxIndex = 0
    endglobals
    
    //==============================================================================
    globals
        private integer array DecayingIndex
        private integer MaxDecayingIndex=0
        private integer DecayChecker=0
    endglobals
    
    globals
        private timer UndefendTimer=CreateTimer()
        private integer array UndefendIndex
        private integer UndefendStackIndex=0
    endglobals
    globals
        private integer array UndefendExpiringIndex
        private integer UndefendExpiringIndexLevel=0
    endglobals
    
    //==============================================================================
    globals
        // The Add/Remove stack (or assign/recycle stack).
        // 
        // Indexing can become recusive since units can be created on index
        // assignment or deallocation.
        // To support this, a stack is used to store the event response results.
        private integer ARStackLevel=0
        private integer array ARStackIndex
        private unit    array ARStackUnit
        
        // A later discovery revealed that the Add/Remove stack did not need to be
        // used for deallocation. The alternative used works fine...
    endglobals
    
    public constant function GetEnteringIndexUnit takes nothing returns unit
        return ARStackUnit[ARStackLevel]
    endfunction
    
    public function GetIndexOfEnteringUnit takes nothing returns integer
        // Called in AIDS structs when units do not pass the initial AIDS filter.
        
        if ARStackIndex[ARStackLevel]==0 then
            // Get new index, from recycler first, else new.
            // Store the current index on the (new) top level of the AR stack.
            if MaxRecycledIndex==0 then // Get new.
                set MaxIndex=MaxIndex+1
                set ARStackIndex[ARStackLevel]=MaxIndex
            else // Get from recycle stack.
                set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
                set MaxRecycledIndex=MaxRecycledIndex-1
            endif
            
            // Store index on unit.
            call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
            set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
            
            // Add index to recycle list.
            set MaxDecayingIndex=MaxDecayingIndex+1
            set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
        endif
        
        return ARStackIndex[ARStackLevel]
    endfunction
    
    public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
        // Called in AIDS structs when units have passed the initial AIDS filter.
        return ARStackIndex[ARStackLevel]
    endfunction
    public constant function GetDecayingIndex takes nothing returns integer
        static if USE_PERIODIC_RECYCLER then
            return DecayingIndex[DecayChecker]
        else
            return UndefendExpiringIndex[UndefendExpiringIndexLevel]
        endif
    endfunction
    
    //==============================================================================
    globals
        // For structs and such which need to do things on unit index assignment.
        private trigger OnEnter=CreateTrigger()
        // The same, but for when units pass the initial filter anyway.
        private trigger OnEnterAllocated=CreateTrigger()
        // For structs and such which need to do things on unit index deallocation.
        private trigger OnDeallocate=CreateTrigger()
    endglobals
    
    public function RegisterOnEnter takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnEnter,b)
    endfunction
    public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnEnterAllocated,b)
    endfunction
    public function RegisterOnDeallocate takes boolexpr b returns triggercondition
        return TriggerAddCondition(OnDeallocate,b)
    endfunction
    
    //==============================================================================
    function GetIndexUnit takes integer index returns unit
        debug if index==0 then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
        debug elseif IndexUnit[index]==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
        debug endif
        
        return IndexUnit[index]
    endfunction
    
    function GetUnitId takes unit u returns integer
        debug if u==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
        debug elseif GetUnitUserData(u)==0 then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
        debug endif
        
        return GetUnitUserData(u)
    endfunction
    
    globals//locals
        private integer getindex
    endglobals
    function GetUnitIndex takes unit u returns integer // Cannot be recursive.
        debug if u==null then
        debug   call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
        debug endif
        
        set getindex=GetUnitUserData(u)
        
        if getindex==0 then
            // Get new index, from recycler first, else new.
            // Store the current index in getindex.
            if MaxRecycledIndex==0 then // Get new.
                set MaxIndex=MaxIndex+1
                set getindex=MaxIndex
            else // Get from recycle stack.
                set getindex=RecycledIndex[MaxRecycledIndex]
                set MaxRecycledIndex=MaxRecycledIndex-1
            endif
            
            // Store index on unit.
            call SetUnitUserData(u,getindex)
            set IndexUnit[getindex]=u
            
            static if USE_PERIODIC_RECYCLER then
                
                // Add index to recycle list.
                set MaxDecayingIndex=MaxDecayingIndex+1
                set DecayingIndex[MaxDecayingIndex]=getindex
                
            else
            
                // Add leave detection ability.
                call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
                call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
                
            endif
            
            // Do not fire things here. No AIDS structs will be made at this point.
        endif
        
        return getindex
    endfunction
    
    //==============================================================================
    public function AddLock takes integer index returns nothing
        set LockLevel[index]=LockLevel[index]+1
    endfunction
    public function RemoveLock takes integer index returns nothing
        set LockLevel[index]=LockLevel[index]-1
        
        static if not USE_PERIODIC_RECYCLER then
            if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
                
                // Increment stack for recursion.
                set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
                set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
                
                // Fire things.
                call TriggerEvaluate(OnDeallocate)
                
                // Decrement stack for recursion.
                set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
                
                // Add the index to the recycler stack.
                set MaxRecycledIndex=MaxRecycledIndex+1
                set RecycledIndex[MaxRecycledIndex]=index
                
                // Null the unit.
                set IndexUnit[index]=null
                
            endif
        endif
    endfunction
    
    //==============================================================================
    static if USE_PERIODIC_RECYCLER then
        
        private function PeriodicRecycler takes nothing returns nothing
            if MaxDecayingIndex>0 then
                set DecayChecker=DecayChecker+1
                if DecayChecker>MaxDecayingIndex then
                    set DecayChecker=1
                endif
                if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
                if LockLevel[DecayingIndex[DecayChecker]]==0 then
                    
                    // Fire things.
                    call TriggerEvaluate(OnDeallocate)
                    
                    // Add the index to the recycler stack.
                    set MaxRecycledIndex=MaxRecycledIndex+1
                    set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
                    
                    // Null the unit.
                    set IndexUnit[DecayingIndex[DecayChecker]]=null
                    
                    // Remove index from decay list.
                    set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
                    set MaxDecayingIndex=MaxDecayingIndex-1
                    
                endif
                endif
            endif
        endfunction
        
    else
        
        private function UndefendFilter takes nothing returns boolean
            return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
        endfunction
        
        private function OnUndefendTimer takes nothing returns nothing
            loop
                exitwhen UndefendStackIndex==0
                
                set UndefendStackIndex=UndefendStackIndex-1
                set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
                
                if IndexUnit[UndefendExpiringIndex[0]]!=null then
                if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
                if LockLevel[UndefendExpiringIndex[0]]==0 then
                    
                    // Fire things.
                    call TriggerEvaluate(OnDeallocate)
                    
                    // Add the index to the recycler stack.
                    set MaxRecycledIndex=MaxRecycledIndex+1
                    set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
                    
                    // Null the unit.
                    set IndexUnit[UndefendExpiringIndex[0]]=null
                    
                endif
                endif
                endif
                
            endloop
        endfunction
        
        globals//locals
            private integer UndefendFilterIndex
        endglobals
        private function OnUndefend takes nothing returns boolean
            if GetIssuedOrderId()==852056 then // If undefend then...
                set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
                
                if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
                    set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
                    set UndefendStackIndex=UndefendStackIndex+1
                    
                    call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
                endif
            endif
            
            return false
        endfunction
        
    endif
    
    //==============================================================================
    public function IndexEnum takes nothing returns boolean // Can be recursive...
        // Start by adding another level on the AR stack (for recursion's sake).
        set ARStackLevel=ARStackLevel+1
        
        // Store the current unit on the (new) top level of the AR stack.
        set ARStackUnit[ARStackLevel]=GetFilterUnit()
        
        if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
            
            if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
                
                // Get new index, from recycler first, else new.
                // Store the current index on the (new) top level of the AR stack.
                if MaxRecycledIndex==0 then // Get new.
                    set MaxIndex=MaxIndex+1
                    set ARStackIndex[ARStackLevel]=MaxIndex
                else // Get from recycle stack.
                    set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
                    set MaxRecycledIndex=MaxRecycledIndex-1
                endif
                
                // Store index on unit.
                call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
                set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
                
                static if USE_PERIODIC_RECYCLER then
                    
                    // Add index to recycle list.
                    set MaxDecayingIndex=MaxDecayingIndex+1
                    set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
                    
                else
                    
                    // Add leave detection ability.
                    call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
                    call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
                    
                endif
                
                // Fire things.
                call TriggerEvaluate(OnEnter)
                
            else
                
                // The unit did not pass the filters, so does not need to be auto indexed.
                // However, for certain AIDS structs, it may still require indexing.
                // These structs may index the unit on their creation.
                // We flag that an index must be assigned by setting the current index to 0.
                set ARStackIndex[ARStackLevel]=0
                
                // Fire things.
                call TriggerEvaluate(OnEnter)
                
            endif
            
        endif
        
        // Decrement the stack.
        set ARStackLevel=ARStackLevel-1
        
        return false
    endfunction
    
    //==============================================================================
    private function InitAIDS takes nothing returns nothing
        local region r=CreateRegion()
        
        local group g=CreateGroup()
        local integer n=15
        
        static if USE_PERIODIC_RECYCLER then
            
            call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
            
        else
            
            local trigger t=CreateTrigger()
            
            loop
                call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
                call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
                // Capture "undefend" orders.
                exitwhen n==0
                set n=n-1
            endloop
            set n=15
            
            call TriggerAddCondition(t,Filter(function OnUndefend))
            set t=null
            
        endif
        
        // This must be done first, due to recursion. :)
        call RegionAddRect(r,GetWorldBounds())
        call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
        set r=null
        
        loop
            call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
            //Enum every non-filtered unit on the map during initialization and assign it a unique
            //index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
            exitwhen n==0
            set n=n-1
        endloop
        call DestroyGroup(g)
        set g=null
    endfunction
    
    //==============================================================================
    public struct DEFAULT extends array
        method AIDS_onCreate takes nothing returns nothing
        endmethod
        method AIDS_onDestroy takes nothing returns nothing
        endmethod
        
        static method AIDS_filter takes unit u returns boolean
            return UnitIndexingFilter(u)
        endmethod
        
        static method AIDS_onInit takes nothing returns nothing
        endmethod
    endstruct
    
    //===========================================================================
    //  Never create or destroy AIDS structs directly.
    //  Also, do not initialise members except by using the AIDS_onCreate method.
    //===========================================================================
    //! textmacro AIDS
        // This magic line makes default methods get called which do nothing
        // if the methods are otherwise undefined.
        private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
        
        //-----------------------------------------------------------------------
        // Gotta know whether or not to destroy on deallocation...
        private boolean AIDS_instanciated
        
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns thistype
            return GetUnitId(whichUnit)
        endmethod
        
        method operator unit takes nothing returns unit
            // Allows structVar.unit to return the unit.
            return GetIndexUnit(this)
        endmethod
        
        //-----------------------------------------------------------------------
        method AIDS_addLock takes nothing returns nothing
            call AIDS_AddLock(this)
        endmethod
        method AIDS_removeLock takes nothing returns nothing
            call AIDS_RemoveLock(this)
        endmethod
        
        //-----------------------------------------------------------------------
        private static method AIDS_onEnter takes nothing returns boolean
            // At this point, the unit might not have been assigned an index.
            if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
                // Flag it for destruction on deallocation.
                set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
                // Can use inlining "Assigned" function now, as it must be assigned.
                call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
            endif
            
            return false
        endmethod
        
        private static method AIDS_onEnterAllocated takes nothing returns boolean
            // At this point, the unit must have been assigned an index.
            if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
                // Flag it for destruction on deallocation. Slightly faster!
                set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
                // Can use inlining "Assigned" function now, as it must be assigned.
                call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
            endif
            
            return false
        endmethod
        
        private static method AIDS_onDeallocate takes nothing returns boolean
            if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
                call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
                // Unflag destruction on deallocation.
                set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
            endif
            
            return false
        endmethod
        
        //-----------------------------------------------------------------------
        private static method onInit takes nothing returns nothing
            call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
            call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
            call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
            
            // Because I robbed you of your struct's onInit method.
            call thistype.AIDS_onInit()
        endmethod
    //! endtextmacro
endlibrary

library PUI uses AIDS
    //===========================================================================
    //  Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
    //  Do NOT put handles that need to be destroyed here (timer, trigger, ...)
    //  Instead put them in a struct and use PUI textmacro
    //===========================================================================
    //! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
    $VISIBILITY$ struct $NAME$
        private static unit   array pui_unit
        private static $TYPE$ array pui_data
        
        //-----------------------------------------------------------------------
        //  Returns default value when first time used
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns $TYPE$
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            if .pui_unit[pui] != whichUnit then
                set .pui_unit[pui] = whichUnit
                set .pui_data[pui] = $DEFAULT$
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
        endmethod
    endstruct
    //! endtextmacro

    //===========================================================================
    //  Never destroy PUI structs directly.
    //  Use .release() instead, will call .destroy()
    //===========================================================================
    //! textmacro PUI
        private static unit    array pui_unit
        private static integer array pui_data
        private static integer array pui_id
        
        //-----------------------------------------------------------------------
        //  Returns zero if no struct is attached to unit
        //-----------------------------------------------------------------------
        static method operator[] takes unit whichUnit returns integer
            local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
            // Switched the next two lines for optimisation.
            if .pui_unit[pui] != whichUnit then
                if .pui_data[pui] != 0 then
                    // recycled index detected
                    call .destroy(.pui_data[pui])
                    set .pui_unit[pui] = null
                    set .pui_data[pui] = 0            
                endif
            endif
            return .pui_data[pui]
        endmethod
        
        //-----------------------------------------------------------------------
        //  This will overwrite already attached struct if any
        //-----------------------------------------------------------------------
        static method operator[]= takes unit whichUnit, integer whichData returns nothing
            local integer pui = GetUnitIndex(whichUnit)
            if .pui_data[pui] != 0 then
                call .destroy(.pui_data[pui])
            endif
            set .pui_unit[pui] = whichUnit
            set .pui_data[pui] = whichData
            set .pui_id[whichData] = pui
        endmethod

        //-----------------------------------------------------------------------
        //  If you do not call release struct will be destroyed when unit handle gets recycled
        //-----------------------------------------------------------------------
        method release takes nothing returns nothing
            local integer pui= .pui_id[integer(this)]
            call .destroy()
            set .pui_unit[pui] = null
            set .pui_data[pui] = 0
        endmethod
    //! endtextmacro
endlibrary

library AutoIndex uses AIDS
    module AutoData
        private static thistype array data
        
        // Fixed up the below to use thsitype instead of integer.
        static method operator []= takes unit u, thistype i returns nothing
            set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
        endmethod                       //using the module's thistype array.
        
        static method operator [] takes unit u returns thistype
            return .data[GetUnitId(u)] //Just returning the attached struct.
        endmethod
    endmodule
endlibrary

EVENT:

JASS:
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//  ~~    Event     ~~    By Jesus4Lyf    ~~    Version 1.02    ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//  What is Event?
//         - Event simulates Warcraft III events. They can be created,
//           registered for, fired and also destroyed.
//         - Event, therefore, can also be used like a trigger "group".
//         - This was created when there was an influx of event style systems 
//           emerging that could really benefit from a standardised custom
//           events snippet. Many users were trying to achieve the same thing
//           and making the same kind of errors. This snippet aims to solve that.
//
//  Functions:
//         - Event.create()     --> Creates a new Event.
//         - .chainDestroy()    --> Destroys an Event.
//                                  DO NOT use .destroy().
//         - .fire()            --> Fires all triggers which have been
//                                  registered on this Event.
//         - .register(trigger) --> Registers another trigger on this Event.
//
//  Details:
//         - Event is extremely efficient and lightweight.
//         - It is safe to use with dynamic triggers.
//         - Internally, it is just a singularly linked list. Very simple.
//
//  How to import:
//         - Create a trigger named Event.
//         - Convert it to custom text and replace the whole trigger text with this.
//
//  Thanks:
//         - Builder Bob for the trigger destroy detection method.
//         - Azlier for inspiring this by ripping off my dodgier code.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library Event
    ///////////////
    // EventRegs //
    ////////////////////////////////////////////////////////////////////////////
    // For reading this far, you can learn one thing more.
    // Unlike normal Warcraft III events, you can attach to Event registries.
    // 
    // Event Registries are registrations of one trigger on one event.
    // These cannot be created or destroyed, just attached to.
    //
    // It is VERY efficient for loading and saving data.
    // 
    //  Functions:
    //         - set eventReg.data = someStruct --> Store data.
    //         - eventReg.data                  --> Retreive data.
    //         - Event.getTriggeringEventReg()  --> Get the triggering EventReg.
    // 
    struct EventReg
        integer data
        method clear takes nothing returns nothing
            set this.data=0
        endmethod
    endstruct
    
    struct Event
        private trigger trig
        private Event next
        static method create takes nothing returns Event
            local Event this=Event.allocate()
            set this.next=0
            return this
        endmethod
        private static Event current
        static method getTriggeringEventReg takes nothing returns EventReg
            return .current
        endmethod
        private static trigger t
        method fire takes nothing returns nothing
            // this = last.
            loop
                set .current=this.next
                exitwhen .current==0
                set .t=.current.trig
                if IsTriggerEnabled(.t) then
                    if TriggerEvaluate(.t) then
                        call TriggerExecute(.t)
                    endif
                    set this=.current
                else
                    call EnableTrigger(.t) // Was trigger destroyed?
                    if IsTriggerEnabled(.t) then
                        call DisableTrigger(.t)
                        set this=.current
                    else // If trigger destroyed...
                        set .current.trig=null
                        set this.next=.current.next
                        call .current.destroy()
                    endif
                endif
            endloop
        endmethod
        method register takes trigger t returns EventReg
            local Event new=Event.allocate()
            set new.next=this.next
            set new.trig=t
            set this.next=new
            call EventReg(new).clear()
            return new
        endmethod
        method chainDestroy takes nothing returns nothing
            loop
                call this.destroy()
                set this=this.next
                exitwhen this==0
                set this.trig=null
            endloop
        endmethod
    endstruct
    
    /////////////////////////////////////////////////////
    // Demonstration Functions & Alternative Interface //
    ////////////////////////////////////////////////////////////////////////////
    // What this would look like in normal WC3 style JASS (should all inline).
    // 
    function CreateEvent takes nothing returns Event
        return Event.create()
    endfunction
    function DestroyEvent takes Event whichEvent returns nothing
        call whichEvent.chainDestroy()
    endfunction
    function FireEvent takes Event whichEvent returns nothing
        call whichEvent.fire()
    endfunction
    function TriggerRegisterEvent takes trigger whichTrigger, Event whichEvent returns EventReg
        return whichEvent.register(whichTrigger)
    endfunction
    
    // And for EventRegs...
    function SetEventRegData takes EventReg whichEventReg, integer data returns nothing
        set whichEventReg.data=data
    endfunction
    function GetEventRegData takes EventReg whichEventReg returns integer
        return whichEventReg.data
    endfunction
    function GetTriggeringEventReg takes nothing returns integer
        return Event.getTriggeringEventReg()
    endfunction
endlibrary

Your missing parts of the code so I had to reorganize your new functions with mine, lol. :p

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    function ResetPlayerDamageTotal takes player p returns nothing
        set playerDamageTotal[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamageTotal takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen i == PLAYER_COUNT
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    private struct damagecounter
        real array score[PLAYER_COUNT]
        player array scorePlayer[PLAYER_COUNT]
        
        method sort takes nothing returns nothing
            local integer i = 0
            local integer j
            local integer l
            local real t
            local player p
            
            loop
                exitwhen (i == PLAYER_COUNT)
                set l = i
                set j = i
                loop
                    exitwhen (j == PLAYER_COUNT)
                    if (score[j] < score[l]) then
                        set l = j
                    endif
                    set j = j + 1
                endloop
                set p = scorePlayer[i]                  // This should keep the list of players
                set scorePlayer[i] = scorePlayer[l]     // associated with each score sorted along
                set scorePlayer[l] = p                  // with the score so that the associated
                set t = score[i]                        // player can later be referenced.
                set score[i] = score[l]
                set score[l] = t
                
                set i = i + 1
            endloop
        endmethod
    endstruct
    
    function PrintDamage takes real time returns nothing
        local damagecounter data = damagecounter.create()
        local string s
        local integer i = 0
        
        loop
            exitwhen (i == PLAYER_COUNT)
            set data.score[i] = playerDamage[i]
            set data.scorePlayer[i] = Player(i)
            set i = i + 1
        endloop
    
        call data.sort()
        
        set i = PLAYER_COUNT
        loop
            exitwhen (i < 0)
    
    //##################################################################################
            // if the player is vacant from the game, his/her score will not be displayed
            // amongst the other players.
            //
            if (GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(data.scorePlayer[i]) + 1] + /*
                        */ GetPlayerName(data.scorePlayer[i]) + ":    " + R2S(data.score[i]) /*
                        */ + " (" + R2S(playerDamageTotal[GetPlayerId(data.scorePlayer[i])]) + ")"
                        
                call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            endif
            
            set i = i - 1
        endloop
        
        call data.destroy()
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary

I can't really test to see if the damage displays for only the player that types the command since it's only me testing, haha. Is the function command the same? Call PrintDamage ()?
 
Level 18
Joined
Jan 21, 2006
Messages
2,552
Yea for displaying the score to all players you'll call it the same way you did before, that is:

JASS:
call PrintDamage(2)

2 can be any positive real number, it is the amount of time that the message will be displayed for.

-- God I hate it when people release libraries with their own fame in mind... AIDS has to be one of the worst unit indexers I have ever seen. In my otherwise perfectly functioning map, simply copying the AIDS script made half of it compile syntax errors because of stupid shit that AIDS does.

He declares a library named AutoIndex (which makes it incompatible with AutoIndex) for the sole purpose of poorly mimicking the AutoData module that is provided by the real AutoIndex.

Single handedly, AIDS prevented 8 practically essential libraries from functioning properly. Good thing Jesus4Lyfe doesn't just code things for the sake of putting his name on something.

Okay, here; this should work:

JASS:
library PlayerDamageCounter initializer init requires Damage

    //##################################################################################
    globals
    
        public real array           playerDamage
        public real array           playerDamageTotal
        public integer array        bossType
        public integer              bosses              = 0

        public constant integer     PLAYER_COUNT        = 8
        
    endglobals
    
    //##################################################################################
    // Utility Functions
    // ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    //  These are functions that can be used easily to gather special case values or
    //  perform special actions.
    //
    //  function PrintDamage will print a list of player ID's along with each associated
    //  damage-dealt value, all in a sorted order.
    //
    //  function AddBossUnitType will take an integer value (this should represent the
    //  raw-code of the unit that you want to be considered. For example, if a "Footman"
    //  was to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'hfoo' )
    //
    //  If a "Paladin" were to be added to the filter it would look like this:
    //
    //      call AddBossUnitType( 'Hpal' )
    //
    function ResetPlayerDamage takes player p returns nothing
        set playerDamage[GetPlayerId(p)] = 0
    endfunction
    
    function ResetAllDamage takes nothing returns nothing
        local integer i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set i = i + 1
        endloop
    endfunction
    
    private function isBossType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return true
            endif
            set i = i + 1
        endloop
        return false
    endfunction
    
    function AddBossUnitType takes integer id returns boolean
        local integer i = 0
        loop
            exitwhen i == bosses
            if bossType[i] == id then
                return false
            endif
            set i = i + 1
        endloop
        set bossType[bosses] = id
        set bosses = bosses + 1
        return true
    endfunction
    
    function GetPlayerDamage takes player p returns real
        return playerDamage[GetPlayerId(p)]
    endfunction
    
    function GetPlayerDamageTotal takes player p returns real
        return playerDamageTotal[GetPlayerId(p)]
    endfunction
    
    private struct damagecounter
        real array score[PLAYER_COUNT]
        player array scorePlayer[PLAYER_COUNT]
        
        method sort takes nothing returns nothing
            local integer i = 0
            local integer j
            local integer l
            local real t
            local player p
            
            loop
                exitwhen (i == PLAYER_COUNT)
                set l = i
                set j = i
                loop
                    exitwhen (j == PLAYER_COUNT)
                    if (score[j] < score[l]) then
                        set l = j
                    endif
                    set j = j + 1
                endloop
                set p = scorePlayer[i]                  // This should keep the list of players
                set scorePlayer[i] = scorePlayer[l]     // associated with each score sorted along
                set scorePlayer[l] = p                  // with the score so that the associated
                set t = score[i]                        // player can later be referenced.
                set score[i] = score[l]
                set score[l] = t
                
                set i = i + 1
            endloop
        endmethod
        
        static method create takes nothing returns thistype
            local thistype d = allocate()
            local integer i = 0
            loop
                exitwhen (i == PLAYER_COUNT)
                set d.score[i] = playerDamage[i]
                set d.scorePlayer[i] = Player(i)
                set i = i + 1
            endloop
            return d
        endmethod
    endstruct
    
    function PrintDamage takes real time returns nothing
        local damagecounter data = damagecounter.create()
        local string s
        local integer i = 0
    
        call data.sort()
        
        set i = PLAYER_COUNT
        loop
            exitwhen (i < 0)
                
            if (GetPlayerSlotState(data.scorePlayer[i]) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(data.scorePlayer[i]) + 1] + /*
                        */ GetPlayerName(data.scorePlayer[i]) + ":    " + R2S(data.score[i]) /*
                        */ + " (" + R2S(playerDamageTotal[GetPlayerId(data.scorePlayer[i])]) + ")"
                        
                call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, time, s)
            endif
            
            set i = i - 1
        endloop
        
        call data.destroy()
    endfunction
    
    function PrintDamageToPlayer takes real time, player to returns nothing
        local damagecounter data = damagecounter.create()
        local string s
        local integer i = 0
        
        call data.sort()
        
        set i = PLAYER_COUNT
        loop
            exitwhen (i < 0)
                
            if (GetPlayerSlotState(data.scorePlayer[i]) == PLAYER_SLOT_STATE_PLAYING) then
                set s = udg_CounterColor[GetPlayerId(data.scorePlayer[i]) + 1] + /*
                        */ GetPlayerName(data.scorePlayer[i]) + ":    " + R2S(data.score[i]) /*
                        */ + " (" + R2S(playerDamageTotal[GetPlayerId(data.scorePlayer[i])]) + ")"
                        
                call DisplayTimedTextToPlayer(to, 0, 0, time, s)
            endif
            
            set i = i - 1
        endloop
        
        call data.destroy()
    endfunction
    
    //##################################################################################
    public function onDamage takes nothing returns nothing
        local integer id = GetPlayerId(GetOwningPlayer(GetEventDamageSource()))
        local unit t = GetTriggerUnit()
        
        if isBossType(GetUnitTypeId(GetTriggerUnit())) then
            if GetEventDamage() > GetWidgetLife(t) then
                set playerDamage[id] = playerDamage[id] + GetWidgetLife(t)
                set playerDamageTotal[id] = playerDamageTotal[id] + GetWidgetLife(t)
            else
                set playerDamage[id] = playerDamage[id] + GetEventDamage()
                set playerDamageTotal[id] = playerDamageTotal[id] + GetEventDamage()
            endif
        endif
        set t = null
    endfunction
    
    //##################################################################################
    public function init takes nothing returns nothing
        local trigger t = CreateTrigger()
        local integer i
        
        //This is where knowing what damage-detection system you use comes into play
        call Damage_RegisterEvent(t) //This is not a specific example
        
        //The rest is pretty standard, though not guaranteed (again, depending on damage-
        //detection protocol)
        call TriggerAddAction(t, function onDamage)
        
        //Ensure that the values in the playerDamage array are initialized to 0 so that
        //there are no problems with addition
        set i = 0
        loop
            exitwhen(i == PLAYER_COUNT)
            set playerDamage[i] = 0
            set playerDamageTotal[i] = 0
            set i = i + 1
        endloop
    endfunction
endlibrary
 
Last edited:
Level 8
Joined
Feb 17, 2007
Messages
368
Right yeah. And how about the function for displaying it for the specific player who types the command?

EDIT: Haha, really?.. So it's not stable in other words and I should use another? I might sometime in the future, but for now I'll keep it since this whole system is setup with that. :p
 
Last edited:
Level 18
Joined
Jan 21, 2006
Messages
2,552
Well the only reference I use to the Damage library is once in the initialization function when I register the trigger.

I find it ridiculous how two enormous additional libraries are required in order to get such basic functionality for damage-detection.

-- Eh, the event library isn't all that big but it's quite useless. Just a proprietary gag no doubt.

I think that if Jesus4Lyfe wanted to implement AIDS and Event in his Damage library he could have greatly expanded compatibility with other unit indexers, or not made them a requirement at all. You really do not need a unit-indexer to have a properly functioning damage-detection system, in fact separating the two allows the user more capability in combining the systems themselves.

But I've successfully tested the code and the most recent code I've posted (final draft v2) should be fully functional.
 
Status
Not open for further replies.
Top