Cooldown system issues

Nexor

...
Reaction score
74
I don't know where it gets messed up, that's why I'm here to ask for some help and some fix that I can't do on my own.

Okay I try to explain my problem. There are two units, A and B, they can learn skills, A can learn ability X and B can learn ability Y.

The two abilities have the same slot number (see code for use of slots).

When A learns X and after that B learns Y, it will show to A's owner that A has learnt Y. That's the main problem. The units have the learned abilities( A the X and B the Y) but they got messed up somehow.
 

Nexor

...
Reaction score
74
AAARGH!

I completely rewrote some parts of this system but the variables got overwritten again :S:S
And the Fake abilities too :/ I don't know what to do now :(

It may help you if you could test this map in multiplayer.
Please help me, I know that I'm not that far away from the solution.
Here's the map:

View attachment CooldownSystem.w3x
 

SanKakU

Member
Reaction score
21
hey kingybird guy, could you put that system you put here in a test map and show it to us? or at least explain more the pros and cons of it...

oh and nexor...why doesn't banish have a lower cooldown? it's annoyingly hard to micro without a proper cooldown for banish...haha...i can't even banish my mage and my attacker together...
which makes me wonder...

would it be possible to do like you cast a spell a certain number of times, and THEN after that number of times, it goes into cooldown? like cast banish twice, or maybe three times...and then it goes into a ten second cooldown, just for an example.
 

Nexor

...
Reaction score
74
The skils' cooldowns are low because of testing. And this IS working in singleplayer, the problems come if you play it in multi, and I want it make it work in multiplayer.
Try somehow out in multiplayer you'll see the issues.
 

SanKakU

Member
Reaction score
21
oh i would but right now i don't have internet...anyway...sorry i didn't read all the code but what exactly does your system do man? i tried asking if it can do such and such but you didn't respond. i'm a bit too clueless as to what your system does...i need to know that before i can attempt to figure out what's wrong with it...

apparently you didn't read my post right, because i was complaining that the cooldown for banish was far too high...not that it was low...

all i can figure what your system does is display cooldowns in an alternate fashion...and it's like it's own cooldown system...can you explain are there any ways to enhance the system to make it better than the normal blizzard cooldown system?

i myself have wondered about making better cooldown system which is why i'm posting here so please explain what are the ramifications of what you're trying to do please...
 

Nexor

...
Reaction score
74
The cooldown system of the regular Warcraft uses constant values for each level. I decided to make it changeable during game, add to it substract from it even multiply the value. In the map I posted is a trigger that will decrease a random ability's cooldown on killing an enemy unit. That's just an example how it could be used. The problem is that the values get overwritten in multiplayer and couldn't figure out a solution.
 

Nexor

...
Reaction score
74
I recreated the whole system but I can't get it working :S:S

The instances got crossed somehow, but I got no clue how and why, ABC writes out a message too.
Code:
Warning SetTimerStructA(1048718, 0) - index: 270, collision: 1

and here's the whole code of the system:

JASS:
library DynamicCooldown initializer Init_DynamicCooldown uses ABC,DynamicCooldownDatabase

globals
    private constant integer MAX_ABILITY = 4
    private group             DC_Group    = CreateGroup()
endglobals


private function Fake takes integer whichslot returns integer
    if      whichslot == 0 then
        return 'A000'
    elseif  whichslot == 1 then
        return 'A001'
    elseif  whichslot == 2 then
        return 'A002'
    elseif  whichslot == 3 then
        return 'A003'
    endif
    return 0
endfunction

struct Skill
    integer abid
    integer slot
    integer fake
    real cd
    unit u
    boolean ready
endstruct

struct CD
    Skill array S [MAX_ABILITY]
    timer array T [MAX_ABILITY]
    unit u
    
    static method remove takes nothing returns nothing
        local Skill skill = GetTimerStructA(GetExpiredTimer())
        call PauseTimer(GetExpiredTimer())
        call SetPlayerAbilityAvailable(GetOwningPlayer(skill.u),skill.abid,true)
        call SetPlayerAbilityAvailable(GetOwningPlayer(skill.u),Fake(skill.slot),false)
        call UnitResetCooldown(skill.u)
        call BJDebugMsg("|cffffcc00"+GetAbilityName(skill.abid)+"|r is ready!")
        set skill.ready=true
        call ClearTimerStructA(GetExpiredTimer())
    endmethod
    
    method add takes integer abid returns nothing
        local integer level = GetUnitAbilityLevel(this.u,abid)
        local integer whichslot = slot[abid]
        
        set this.S[whichslot].cd         =   DC_Base(abid,level)
        set this.S[whichslot].ready      =   false
        call BJDebugMsg("Player|cffffcc00"+I2S(GetPlayerId(GetOwningPlayer(this.u)))+"'|rs |cffffcc00"+GetUnitName(this.u)+"|r has casted the skill: |cffffcc00"+GetAbilityName(abid))
        call BJDebugMsg("Skill's level is: |cffffcc00"+I2S(level))
        call BJDebugMsg("Skill's cooldown: |cffffcc00"+R2S(this.S[whichslot].cd))
        call BJDebugMsg("Testskill's level is: |cffffcc00"+I2S(GetUnitAbilityLevel(this.u,'TEST')))
        
        call SetPlayerAbilityAvailable(GetOwningPlayer(this.u),abid,false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(this.u),Fake(whichslot),true)
        
        call SetTimerStructA(this.T[whichslot],this.S[whichslot])
        call TimerStart(this.T[whichslot],this.S[whichslot].cd,false,function CD.remove)
    endmethod
    
    method make takes integer abid returns nothing
        local integer level = GetUnitAbilityLevel(this.u,abid)
        local integer whichslot = slot[abid]
        
        set this.S[whichslot].u          =   this.u
        set this.S[whichslot].slot       =   whichslot
        set this.S[whichslot].abid       =   abid
        set this.S[whichslot].fake       =   Fake(whichslot)
        
        if this.T[whichslot] == null then
            set this.T[whichslot]=CreateTimer()
        endif
        
    endmethod
    
    method reset takes integer whichslot returns nothing
        if not (this.T[whichslot] == null and this.S[whichslot].cd ==0 ) and this.S[whichslot].ready== false  then
            call PauseTimer(this.T[whichslot])
            call TimerStart(this.T[whichslot],0.0,false,function CD.remove)
        endif
    endmethod
    method resetall takes nothing returns nothing
        local integer i=0
        loop
            exitwhen i>MAX_ABILITY
            call this.reset(i)
            set i = i + 1
        endloop
    endmethod
endstruct

globals
    CD array DC
endglobals

function SkillCast takes nothing returns nothing
    local integer abid=GetSpellAbilityId()
    local unit caster = GetTriggerUnit()
    local integer level =GetUnitAbilityLevel(caster,abid)
    set DC[GetPlayerId(GetOwningPlayer(caster))].u=caster
    call DC[GetPlayerId(GetOwningPlayer(caster))].add(abid)
    
    set caster = null
endfunction

function SkillLearn takes nothing returns nothing
    local integer abid=GetLearnedSkill()
    local unit caster = GetTriggerUnit()
    local integer level =GetUnitAbilityLevel(caster,abid)
    set DC[GetPlayerId(GetOwningPlayer(caster))].u=caster
    call DC[GetPlayerId(GetOwningPlayer(caster))].make(abid)
    if GetUnitAbilityLevel(caster,'A000') == 0 then
        call UnitAddAbility(caster,'A000')
        call UnitAddAbility(caster,'A001')
        call UnitAddAbility(caster,'A002')
        call UnitAddAbility(caster,'A003')
        call SetPlayerAbilityAvailable(GetOwningPlayer(caster),'A000',false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(caster),'A001',false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(caster),'A002',false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(caster),'A003',false)
    endif
    set caster = null
endfunction


private function Heroes takes nothing returns boolean
    return (IsUnitType(GetEnumUnit(),UNIT_TYPE_HERO) == true and IsUnitInGroup(GetEnumUnit(), DC_Group) == false )
endfunction
private function GetHeroes takes nothing returns nothing
    call GroupEnumUnitsInRect(DC_Group,bj_mapInitialPlayableArea,Condition(function Heroes))
endfunction
//===========================================================================
function Init_DynamicCooldown takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    local trigger t2= CreateTrigger(  )
    local trigger t3= CreateTrigger(  )
    call GroupEnumUnitsInRect(DC_Group,bj_mapInitialPlayableArea,Condition(function Heroes))
    call TriggerRegisterEnterRectSimple(t,bj_mapInitialPlayableArea)
    call TriggerAddAction(t,function GetHeroes)
    call TriggerRegisterAnyUnitEventBJ(t2,EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddAction(t2,function SkillCast)
    call TriggerRegisterAnyUnitEventBJ(t3,EVENT_PLAYER_HERO_SKILL)
    call TriggerAddAction(t3,function SkillLearn)
    set t=null
    set t2=null
    set t3=null
endfunction

endlibrary


Please help me about this issue :)
 

tooltiperror

Super Moderator
Reaction score
231
J4L sounds correct, I find it helpful to use prefixes and double prefixes. The first would be Cooldown, the other would be what it really does. So CooldownAddCooldown would be a good name.
 

Viikuna

No Marlo no game.
Reaction score
265
Its pretty damn cool actually.

I think it would be neat to get some custom icons with numbers or something, one dummy ability for each second, so you can see the cooldown counting down from 5 to 4 and to 3 and to 2 and so on.

Its just that if theres long cooldowns in your map, you would need to make pretty damn many icons. ( Generating abilities is no problem, because that can be done automaticly with object merger, but theres no tool to do that icon art as far as I know. )

Anyways, Ive always wanted to have some neat cooldown/ability system to manipulate stuff like mana cost and cooldown.

edit. System bugged when I had 2 cooldowns going on, and I hit esc few times. It gave me some ABC hash collinsion error. I guess you should use TimerUtils anyways. Its better and kinda standard stuff, if you are planning to submit something like this.
 

Nexor

...
Reaction score
74
edit. System bugged when I had 2 cooldowns going on, and I hit esc few times. It gave me some ABC hash collinsion error. I guess you should use TimerUtils anyways. Its better and kinda standard stuff, if you are planning to submit something like this.

That's the main problem, casting two units the ability makes some overlapping thingie. That's why I'm writing here, for some help on this :S Because I don't know what the solution is.

Anyways, Ive always wanted to have some neat cooldown/ability system to manipulate stuff like mana cost and cooldown.

Oh yay, that was the idea :D

I think it would be neat to get some custom icons with numbers or something, one dummy ability for each second, so you can see the cooldown counting down from 5 to 4 and to 3 and to 2 and so on.

I made the previous ones with multiboards but I'd like to make it working first, then the other optional things will come.

Its pretty damn cool actually.

Thank you :)
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
JASS:
library Cooldown initializer Init requires PUI, ABC
    
    globals
        private constant integer MAX_ABILITY = 4
        private integer array DisabledAbilities
        private constant integer MaxAbilityPlusOne = MAX_ABILITY + 1
    endglobals
    
    struct Slot extends array
        private static integer array Data
        
        static method operator []= takes integer id, integer value returns nothing
            set thistype.Data[id-(id/8191)*8191] = value
        endmethod
        
        static method operator[] takes integer id returns integer
            return thistype.Data[id-(id/8191)*8191]
        endmethod
        
    endstruct

    struct Cooldown extends array
        private static hashtable ht = InitHashtable()
        
        static method operator [] takes integer id returns thistype
            return id
        endmethod
                
        method operator [] takes integer level returns real
            return LoadReal(thistype.ht,this,level)
        endmethod
                
        method operator []= takes integer level, real value returns nothing
            call SaveReal(thistype.ht,this,level,value)
        endmethod
    endstruct
    
    struct HeroAbilities extends array
        private static hashtable ht = InitHashtable()
        
        static method operator [] takes integer pos returns thistype
            return pos
        endmethod
        
        method operator [] takes integer pos returns integer
            return LoadInteger(thistype.ht,this,pos)
        endmethod
        
        method operator []= takes integer pos, integer value returns nothing
            call SaveInteger(thistype.ht,this,pos,value)
        endmethod
    endstruct
    
    public struct Data
        unit hero
        timer array t [MaxAbilityPlusOne]
        boolean array ready [MaxAbilityPlusOne]
        
        method reset takes integer pos returns nothing
            set .ready[pos] = true
            call PauseTimer(.t[pos])
            call UnitRemoveAbility(.hero,DisabledAbilities[pos])
            call UnitAddAbility(.hero,HeroAbilities[GetUnitTypeId(.hero)][pos])
        endmethod
        
        static method removeCooldown takes nothing returns nothing
            call thistype(GetTimerStructA(GetExpiredTimer())).reset(GetTimerStructB(GetExpiredTimer()))
        endmethod
        
        method add takes nothing returns nothing
            local integer abil_id = GetSpellAbilityId()
            local integer pos = Slot[abil_id]
            local integer lv = 0
            if .t[pos] == null then
                set .t[pos] = CreateTimer()
                call SetTimerStructA(.t[pos],this)
                call SetTimerStructB(.t[pos],pos)
            endif
            set .ready[pos] = false
            set lv = GetUnitAbilityLevel(.hero,abil_id)
            call UnitRemoveAbility(.hero,abil_id)
            call UnitAddAbility(.hero,DisabledAbilities[pos])
            call TimerStart(.t[pos],Cooldown[abil_id][lv],false,function thistype.removeCooldown)
            call BJDebugMsg("Slot : " + I2S(pos))
            call BJDebugMsg("Cooldown : " + R2S(Cooldown[abil_id][lv]))
            
        endmethod
        
        method resetall takes nothing returns nothing
            local integer i = 1
            loop
                call .reset(i)
            exitwhen i == MAX_ABILITY
            endloop
        endmethod
        
        static method create takes unit hero returns thistype
            local thistype this = thistype.allocate()
            set .hero = hero
            return this
        endmethod
        //! runtextmacro PUI()
    endstruct
    
    private function Cond takes nothing returns boolean
        if Data[GetTriggerUnit()] == 0 then
            set Data[GetTriggerUnit()] = Data.create(GetTriggerUnit())
        endif
        call Data(Data[GetTriggerUnit()]).add()
        return false
    endfunction
    
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(trig,Condition(function Cond))
        
        //Declare the disabled abilities here.
        set DisabledAbilities[1] = 'A000'
        set DisabledAbilities[2] = 'A001'
        set DisabledAbilities[3] = 'A002'
        set DisabledAbilities[4] = 'A003'
        //
    endfunction
    
endlibrary


Try this?
 

Nexor

...
Reaction score
74
Could you explain your code? how do I set the initial values for the abilities and how can I change the values per level?
 

Viikuna

No Marlo no game.
Reaction score
265
I cleaned up your code a bit and did some restructuring and stuff.

I didnt test this, because I didnt write proper testing triggers yet, but it at least looks better like this, IMO.

( lols, :D, did I just say that it looks better, but doesnt neccesarily work? )

Anyways, read it if you like. Id like to do some more than just making your code nicer looking, but I gotta go sleep now. I help you more later, if you still need help.

JASS:
library DynamicCooldowns initializer Init uses TimerUtils, AutoIndex, DynamicCooldownDatabase
//===============================================================================
//===============================================================================
globals

    private constant integer  MAX_SKILL = 4
    
    
//===============================================================================
//===============================================================================
    private code              RemovingFunction
endglobals


private function Fake takes integer whichslot returns integer
    if      whichslot == 0 then
        return 'A000'
    elseif  whichslot == 1 then
        return 'A001'
    elseif  whichslot == 2 then
        return 'A002'
    elseif  whichslot == 3 then
        return 'A003'
    endif
    return 0
endfunction

struct Skill

    readonly integer level
    readonly integer abid
    readonly integer slot
    readonly integer fake
    readonly boolean ready
    readonly real    cd
    readonly unit    unit
    readonly timer   timer
    
    method startCooldown takes real duration returns nothing
        local player  owner       = GetOwningPlayer(.unit)
        
        set .cd         =   duration
        set .ready      =   false
        
        debug call BJDebugMsg("Player|cffffcc00"+I2S(GetPlayerId(GetOwningPlayer(.unit)))+"'|rs |cffffcc00"+GetUnitName(.unit)+"|r has casted the skill: |cffffcc00"+GetAbilityName(abid))
        debug call BJDebugMsg("Skill's level is: |cffffcc00"+I2S(level))
        debug call BJDebugMsg("Skill's cooldown: |cffffcc00"+R2S(.cd))
        debug call BJDebugMsg("Testskill's level is: |cffffcc00"+I2S(GetUnitAbilityLevel(.unit,'TEST')))
        
        call SetPlayerAbilityAvailable(owner,.abid,false)
        call SetPlayerAbilityAvailable(owner,Fake(.slot),true)
        
        set .timer=NewTimer()
        call SetTimerData(.timer,this)
        call TimerStart(.timer,duration,false,RemovingFunction)
    endmethod
    
    method stopCooldown takes nothing returns nothing
        local player owner=GetOwningPlayer(.unit)
        call SetPlayerAbilityAvailable(owner,.abid,true)
        call SetPlayerAbilityAvailable(owner,Fake(.slot),false)
        call UnitResetCooldown(.unit)
        set .ready=true
        
        debug call BJDebugMsg("|cffffcc00"+GetAbilityName(.abid)+"|r is ready!")
        
    endmethod
    
    static method create takes unit u, integer whichslot, integer abid returns Skill
        local Skill this=Skill.allocate()
        
        set .unit       =   u
        set .slot       =   whichslot
        set .abid       =   abid
        set .fake       =   Fake(whichslot)
        set .level      =   GetUnitAbilityLevel(u,abid)
        
        return this
    endmethod
    
    static method Expire takes nothing returns nothing
        local timer t=GetExpiredTimer()
        local Skill skill=GetTimerData(t)
        call ReleaseTimer(t)
        call skill.stopCooldown()
    endmethod
    
endstruct

struct UnitSkillData

    readonly       unit                         unit
    
    private        Skill         array          S [MAX_SKILL]
    private static UnitSkillData array          array
    
    
    method startCooldownById takes integer abid returns nothing
        local integer level       = GetUnitAbilityLevel(this.unit,abid)
        local integer whichslot   = slot[abid]
        local Skill   skill       = .S[whichslot]
        local real    duration    = DC_Base(abid,level)
        
        call skill.startCooldown( duration )
        
    endmethod
    
    // When hero learns a new skill, call this
    method addSkill takes integer abid returns nothing
        local integer whichslot = slot[abid]
    
        set .S[whichslot]    =   Skill.create(.unit,whichslot,abid)
        
    endmethod
    
    method resetCooldownById takes integer whichslot returns nothing
        local Skill skill=.S[whichslot]
        if skill.cd > 0 and not skill.ready then
            call PauseTimer(skill.timer)
            call skill.stopCooldown()
        endif
    endmethod
    
    method resetCooldowns takes nothing returns nothing
        local integer i=0
        loop
            exitwhen i>MAX_SKILL
            call .S<i>.stopCooldown()
            set i = i + 1
        endloop
    endmethod
    
    // When new hero is created, this is used to create cooldown data for it
    static method create takes unit u returns UnitSkillData
        local UnitSkillData this=UnitSkillData.allocate()
        
        set .unit=u
        set .array[ GetUnitId(u) ] = this

        call UnitAddAbility(u,&#039;A000&#039;)
        call UnitAddAbility(u,&#039;A001&#039;)
        call UnitAddAbility(u,&#039;A002&#039;)
        call UnitAddAbility(u,&#039;A003&#039;)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u),&#039;A000&#039;,false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u),&#039;A001&#039;,false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u),&#039;A002&#039;,false)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u),&#039;A003&#039;,false)
        
        return this
    endmethod
    
    // this is used to get units cooldown data
    static method getFromUnit takes unit u returns UnitSkillData
        return .array[ GetUnitId( u ) ]
    endmethod
    
endstruct

private function Init takes nothing returns nothing
    set RemovingFunction=function Skill.Expire
endfunction

endlibrary
</i>
 

Nexor

...
Reaction score
74
Thanks Viikuna for your code, I don't understand some parts of it, but it looks good imo.
I can't test it now because I'm sleepy as well :)

But tomorrow I will try it out and then I write if someone isn't working or something.
One thing though:
How shall I make the database so I could set the slot number for a spell and the cooldown duration of it for each level?
The slot isnt a problem, but the other one, that's a hard one for me.

It's handwritten but something like this:
set CD[ability id][level] = duration
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top