• 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.

[vJASS] Damage tracking

Status
Not open for further replies.
JASS:
 // unit indexer and weep's DDS already in map, 8 heroes 
hero[0] .to. hero[7] // hero[0] belongs ro red player, hero[1]-blue etc
// 8 players. Im looking for easy way to get last 7 seconds damage dealt by given player
// to specific hero. This 7 sec is not critical, it may be 6-8sec
// So when calling anytime function like :
function GetDamage takes integer playerVictim, integer playerAttacker returns real
// it return whole damage dealt by Player(playerAttacker) to hero[playerVictim] 
// during last 7 seconds.
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Nice way of describing your problem... not preferred though.

The best way is to have a linked list for each combination.
Everytime you damage a hero, you add one on the end with the current timestamp.
GetDamage would loop through the entire list and removing the components that are too long ago and counting all other components and return the sum.

Pretty much that.
 
Thanks for your answer Wietlol,
from somewhere on Hive:
A linked list is a list containing a set of pointers that point to each other.
can you please show small example how your idea should work with linked list
I can imagine for now that this requires for about ~60 array variables, one variable for timeStamp
And this will grow to "system" but ok, if there's no easier way that's fine.
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
I guess this should work.

Dont be surprised if it doesnt work, just made it :D

edit: ow and GetElapsedGameTime() just returns the elapsed time of a timer with a practically infinite duration that is started at map init.

JASS:
module LinkedListNode

    readonly static thistype array first
    readonly static thistype array last
    readonly thistype next
    readonly thistype prev
    readonly integer list

    public static method addNode takes integer list returns thistype
        local thistype this
    
        if list == 0 then
            return 0
        endif
    
        set this = .allocate()
    
        set .list = list
        set .last[list].next = this
        set .prev = .last[list]
        set .last[list] = this
    
        if .first[list] == 0 then
            set .first[list] = this
        endif
    
        return this
    endmethod

    public static method insertNode takes integer list, thistype node returns thistype
        local thistype this
    
        if list == 0 then
            return 0
        elseif node == 0 then
            return .addNode(list)
        elseif node.list != list then
            return 0
        endif
    
        set this = .allocate()
    
        set .list = list
        set .prev = node.prev
        set node.prev.next = this
        set node.prev = this
        set .next = node
    
        if .first[list] == node then
            set .first[list] = this
        endif
    
        return this
    endmethod

    public method removeNode takes nothing returns nothing
        set .next.prev = .prev
        set .prev.next = .next
    
        if this == .last[list] then
            set .last[list] = .prev
        endif
        if this == .first[list] then
            set .first[list] = .next
        endif
    
        set .list = 0
        set .next = 0
        set .prev = 0
    
        call .deallocate()
    endmethod

endmodule

struct DamageTrackingNode
    implement LinkedListNode

    readonly real value
    readonly real timestamp

    public static method add takes integer list, real value returns thistype
        local thistype this = .addNode(list)
        if this == 0 then
            return this
        endif
    
        set .value = value
        set .timestamp = GetElapsedGameTime()
    
        return this
    endmethod
    public static method insert takes integer list, thistype node, real value returns thistype
        local thistype this = .insertNode(list, node)
        if this == 0 then
            return this
        endif
    
        set .value = value
        set .timestamp = GetElapsedGameTime()
    
        return this
    endmethod
    public method remove takes nothing returns nothing
        call .removeNode()
    
        //set .value = 0.
        //set .timestamp = 0.
    endmethod

endstruct

struct DamageTrackingList

    public static method create takes nothing returns thistype
        return .allocate()
    endmethod

    public method add takes real value returns DamageTrackingNode
        return DamageTrackingNode.add(this, value)
    endmethod
    public method insert takes thistype node, real value returns DamageTrackingNode
        return DamageTrackingNode.insert(this, node, value)
    endmethod

    public method first takes nothing returns DamageTrackingNode
        return DamageTrackingNode.first[this]
    endmethod

endstruct

struct DamageTracking

    private static constant integer PLAYER_MAX = 8
    private static constant real TIMEOUT = 7.00
    readonly static thistype array get

    private DamageTrackingList array damagesList[.PLAYER_MAX]

    private static method create takes nothing returns thistype
        local thistype this = .allocate()
        local integer i = 0
    
        loop
            exitwhen i >= .PLAYER_MAX
            set .damagesList[i] = DamageTrackingList.create()
            set i = i +1
        endloop
    
        return this
    endmethod

    public method addDamage takes integer playerId, real damage returns nothing
        call .damagesList[playerId].add(damage)
    endmethod

    public method getDamage takes integer playerId returns real
        local DamageTrackingNode curr
        local DamageTrackingNode next = .damagesList[playerId].first()
        local real result = 0
        local real timestamp = GetElapsedGameTime() - .TIMEOUT
    
        loop
            exitwhen next == 0
            set curr = next
            set next = curr.next
        
            if curr.timestamp < timestamp then
                call curr.remove()
            else
                set result = result + curr.value
            endif
        endloop
    
        return result
    endmethod

    private static method onInit takes nothing returns nothing
        local integer i = 0
    
        loop
            exitwhen i >= .PLAYER_MAX
            set .get[i] = .create()
            set i = i +1
        endloop
    endmethod

endstruct
 
Last edited:
wow, you're so advanced in coding, thanks for this Wietlol!
after reading I realized that what is for me, as user, is: addDamage and getDamage methods.
But does your code public method getDamage takes integer playerId returns real allows to pass damage source and victim's id? I see only one integer variable in "add" and "get" methods
if you please look at 1st initial post I described function with 2 parameters, is what I need
Or maybe I completelly didnt understand how to use your functions ;]
 
Level 24
Joined
Aug 1, 2013
Messages
4,658
Well, I dont blame you for not knowing, because it is kinda going against how vanilla jass (which is pretty much the only thing you have used in it).
In structs, you have static content or instance content.
Static content is the same as normal functions, but instance content are functions and variables bound to... well... an instance, an object.

In this struct, you have only a few instances and they dont get created during the game.
You create them at map init and use those the entire game.

To get one of the instances (which are stored in the array variable "get"), you use the struct name followed by the variable name (separated by a dot).
local DamageTracking tracker = DamageTracking.get[attackedPlayerId]
Then you can use the instance methods because you have an instance.
call tracker.addDamage(attackingPlayerId, damageValue)

The same for getting the damage.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,287
Log all damage events in a sorted list based by occurrence time. You can then filter this list when you desire to know how much damage a unit has done to another unit over a period of time. For every damage event found you add the damage to an accumulator which then is returned for the total damage over a specified time.

Periodically all events older than 7 seconds can be discarded, keeping the list small and efficient. Using a balanced binary search tree can allow for iteration to occur over the specified time domain very efficiently compared with linear search. Multiple such damage event lists can be created for separate players or even separate units to further reduce the number of elements to search through.
 
Status
Not open for further replies.
Top