• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!

SetUnitAttackSpeedBonus

Level 20
Joined
May 16, 2012
Messages
635
Hey guys, i just created a small function to control the attack speed bonus of a unit via code instead of abilities.

You need to create a hashtable at map initialization called UnitHash and use Bribe's Unit Indexer or Unit Event. Why? because its amazing and i'm using the on index event to set up a few thinks.

So in this first piece, i just save every indexed unit base attack cooldown and their current bonus (0%)

JASS:
scope Snippet initializer Init

private function Actions takes nothing returns nothing
    call SaveReal(udg_UnitHash, GetHandleId(udg_UDexUnits[udg_UDex]), 1, BlzGetUnitAttackCooldown(udg_UDexUnits[udg_UDex], 0)) // Original Base Attack CD
    call SaveReal(udg_UnitHash, GetHandleId(udg_UDexUnits[udg_UDex]), 2, 0) // Current Attack Speed Bonus
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    set gg_trg_Snippet = CreateTrigger()
    call TriggerRegisterVariableEvent(gg_trg_Snippet, "udg_UnitIndexEvent", EQUAL, 1.00)
    call TriggerAddAction(gg_trg_Snippet, function Actions)
endfunction

endscope

why is it necessary? well to be able to remove bonusses i need to know the unit base attack time (bat) and i also need to know if its current bonus is not lower than -1 (-100% or no attacks).

now the snippet:

JASS:
function SetUnitAttackSpeedBonus takes unit whichUnit, real bonus returns nothing
    local real bat = LoadReal(udg_UnitHash, GetHandleId(whichUnit), 1)
    local real currentBonus = LoadReal(udg_UnitHash, GetHandleId(whichUnit), 2)
    local real newBonus = currentBonus + bonus
    local real newAS

    set newAS = bat/(1 + newBonus)

    if bonus < 0 then
        if newBonus > -1 then
            call SaveReal(udg_UnitHash, GetHandleId(whichUnit), 2, newBonus)
            call BlzSetUnitAttackCooldown(whichUnit, newAS, 0)
        else
            set newAS = bat/(1 - 0.9999)//capped at -99.99% AS
            call SaveReal(udg_UnitHash, GetHandleId(whichUnit), 2, -1)
            call BlzSetUnitAttackCooldown(whichUnit, newAS, 0)
        endif
    else
        if newAS >= 0.022 then
            call SaveReal(udg_UnitHash, GetHandleId(whichUnit), 2, newBonus)
            call BlzSetUnitAttackCooldown(whichUnit, newAS, 0)
        else
            call SaveReal(udg_UnitHash, GetHandleId(whichUnit), 2, newBonus)
            call BlzSetUnitAttackCooldown(whichUnit, 0.022, 0)
        endif
    endif
endfunction

function GetUnitAttackSpeedBonus takes unit whichUnit returns real
    return LoadReal(udg_UnitHash, GetHandleId(whichUnit), 2)
endfunction

so to use these functions you only need to do:

JASS:
call SetUnitAttackSpeedBonus(unit, 3)

that will give the "unit" a bonus of 300% attack speed. the max bonus is capped on a attack cooldown of 0.022 or 45.45 attacks per seconds and -99.99% for the lower bound.

Its not neccessary to do:

JASS:
call SetUnitAttackSpeedBonus(unit, GetUnitAttackSpeedBonus(unit) + 3)

as its already taken in consideration.

this is cool because by using it you not chained to the 400% max bonus or -80%.]

Note that you can do:

JASS:
call SetUnitAttackSpeedBonus(unit, -3)

this you remove 300% bonus attack speed ffrom the unit if its current bonus is greater than or equal to 3 and if its lower will stop at the cap (-99.99%) and save -1 as the current bonus, so you are safe to keep adding negative values because its never going to be lower than -1.

Hope you like it.
 
Level 15
Joined
Mar 25, 2016
Messages
1,327
You should use a library for your functions and a global block, like Bribe said.

I would suggest to provide a public function InitUnit or RegisterUnit that initializes a unit's base attack speed and current attack speed bonus. Then it is easier for people to use this snippet with other unit indexers than Bribe's.

I think it would be nice to have lower and upper bound as constants instead of hardcoded values.

SetUnitAttackSpeedBonus(...) should be renamed to UnitAddAttackSpeedBonus() or something similar. Set implies that it removes all other bonuses.
Then you could add an additional function SetUnitAttackSpeedBonus(...) that does remove all other bonuses.

If you stay with hashtables, you should add a function to clear data for a unit, if the unit is removed.

How does this interact with attack speed bonuses from abilities or upgrades?

f its lower will stop at the cap (-99.99%) and save -1
I don't think that is good. A typical use case for your snippet is timed buffs/debuffs. So what you would do is:

1. Apply effect: e.g. -50% attack speed
2. Wait buff duration
3. Revert effect +50% attack speed

Now if you use this effect 3 times, you would be at -150% and then it is reverted 3 times and you are back at 0%. However if you save -150% as -1, then it will first go to -100% and the reverted 3 times and you are at +50% even though all 3 effects are over.

It seems it only modifies the first attack index. A parameter which attack index to modify would be nice.
 
Level 20
Joined
May 16, 2012
Messages
635
You should use a library for your functions and a global block, like Bribe said.

I would suggest to provide a public function InitUnit or RegisterUnit that initializes a unit's base attack speed and current attack speed bonus. Then it is easier for people to use this snippet with other unit indexers than Bribe's.

I think it would be nice to have lower and upper bound as constants instead of hardcoded values.

SetUnitAttackSpeedBonus(...) should be renamed to UnitAddAttackSpeedBonus() or something similar. Set implies that it removes all other bonuses.
Then you could add an additional function SetUnitAttackSpeedBonus(...) that does remove all other bonuses.

If you stay with hashtables, you should add a function to clear data for a unit, if the unit is removed.

How does this interact with attack speed bonuses from abilities or upgrades?


I don't think that is good. A typical use case for your snippet is timed buffs/debuffs. So what you would do is:

1. Apply effect: e.g. -50% attack speed
2. Wait buff duration
3. Revert effect +50% attack speed

Now if you use this effect 3 times, you would be at -150% and then it is reverted 3 times and you are back at 0%. However if you save -150% as -1, then it will first go to -100% and the reverted 3 times and you are at +50% even though all 3 effects are over.

It seems it only modifies the first attack index. A parameter which attack index to modify would be nice.

Hey, thanks for the feedback. Initially i was trying to make this snippet be able to substitute all of the attack speed provided by the game, meaning that i tried to set agility, item and other sources of attack speed bonus to 0 and every time i needed to add attack speed to a unit, i would call this function. It kind of worked until i tried to set the constant attack speed bonus per agility point to 0, which made the attack animation of units weird, so i decided to drop this snippet for now and focus on finishing my map and when i got a little more time i will try to make a complete library, but you are absolutely right about everythink you mentioned.
 
Top