VERSION 1
This library allows you to manage units' speed for a set amount of time (you chose the amount and the time), without needing a dummy with 'slow'.
I made this library, because before it - I was using dummies, casting "slow" on the targets to slow them. However - this wasn't really MUI, since a unit can have only 1 dummy slow at once (if all of the slows are based on the sorceresses slow).
However, this system, besides stacking, is also more efficient. All the slows are done via triggers.
This library can also apply buffs for the duration of the speed modification.
The only drawback of the system is that the speed modifiers, applied by it can't be dispelled with ingame dispells (if you don't trigger the dispell).
Now the system has only 3 limitations:
1) there can be up to 8190 speed modifiers on the map at once. Where with "normal" type (see below) - each stack counts as 1, and with the other types - each buff (buff_ability id), applied on every unit counts as 1.
2) different types of speed modifiers must have different buff_ability (IDs): if you are using 'A00F' for a "normal" type of modifier, you shouldn't use 'A00F' for any other type.
3) It may cause bugs, if used along with "SetUnitMoveSpeed": if a unit has its speed changed by this function, used OUTSIDE of the library - the library will "ignore" it. The unit will have its speed changed by the proper amount, but later, if a speed modification is applied by this library - it will act as if the unit didn't have it. However, it will work "normally" if the unit had its speed altered by this library before using the "SetUnitMoveSpeed", but then the unit's speed will return to a weird value...
To use the system - you need just 1 line of custom text. And the functions take:
1) unit - the unit that is going to have its speed modified
2.a) modifier - the relative amount of speed the unit should have after the modification. 1 = 100%, 0.5 = 50%, 1.5 = 150% (simply divide the % by 100)
2.b) amount - the amount of speed, added (reduced - if a negative number is given) to the unit by the modification. If a unit has 250 speed, and 40 is given as amount - the unit will end up with 290 speed.
3) duration - how long should the modifier last (in seconds).
4.a) linear - should the amount be taken from the unit's current speed (false), or from the unit's max speed (true). "true" would look like: 100% - 80% - 60% - 40%; While "false" will be: 100% - 80% - 64% - 51.2%
4.b) type (only 'ChangeUnitSpeedLinear' has this) - chose the type of modification you want. It can be (it's a string): "normal", "time_stacking", "time_refreshing", "not_stacking" and "special".
5) buff - (buff ability) the ID of the ability, applying the buff. If you don't want to add a buff - put a value between 1 and 1,000,000 (different modification types should have different buff ability ids).
(6) building_up (only 'ChangeUnitSpeed2' has this) - if set to true, the remaining time of the previously applied modifier is added to the duration, which is being currently applied. If false - the time is simply set to the wanted duration ( can't go above it ).
There are 5 main types of slowing, and 3 sub-types. The main types are:
1) "normal" - every time a modifier of this type is applied - the unit's move speed is changed further. However - each stack has its own time:
If you cast a 20% slow for 5 seconds (80% speed remaining), and after 2 seconds - you re-cast it (60% speed remaining), after 3 more seconds (5 total) - the unit will get back the 1-st 20%, and will have 80% speed again. Then after 2 more seconds (7 total) the unit will go back to 100%.
To get this type of modification you need to use
2) "time_stacking" - every time a modifier of this type is applied - the unit's speed isn't changed further, but the time remaining (until the modifier expires) is increased by the 'duration' you have set:
If you cast a 25% slow for 5 seconds (75% speed remaining), and after 2 seconds - you re-cast it, the unit will still have 75% speed, but the time remaining will become 8 seconds.
To get this type of modification you need to use
3) "time_refreshing" - every time a modifier of this type is applied - the unit's speed isn't changed further, but the time remaining (until the modifier expires) is set to the 'duration' you have set:
If you cast a 25% slow for 5 seconds (75% speed remaining), and after 2 seconds - you re-cast it, the unit will still have 75% speed, but the time remaining will go back to 5 seconds.
To get this type of modification you need to use
4) "not_stacking" - if a unit has this type of modifier - you need to wait until it has expired before you can re-apply it, otherwise nothing happens:
A unit is slowed (with this type of modifier) by 40% (60% speed remaining) for 5 seconds, after 2 seconds the slow is re-casted - the speed remains 60%, and the time remaining is 3 seconds. Neither changes.
To get this type of modification you need to use
5) "special" - (kind of a combination between (1) and (3)) with every stack - the speed is modified further (than the previous) and the time remaining is refreshed.
If a unit is slowed by 20% for 3 seconds (80% remaining), and after 2 seconds the slow is re-cast - the unit will have 60% speed remaining, and it will expire in 3 seconds (kind a like the Batrider from dota's annoying slow).
To get this type of modification you need to use
NOTE: With (4) if you apply a 20% slow, with buff = ~(some number) let's say..~ 3 - if you cast a slow with 0.75 modifier (25% slow), and the same buff (3 in this example) - the unit will keep the 20%.
However, if you have a 20% slow with buff ability and buff = 3, and then another 20% slow is applied, with buff = 4. Then the unit will be slowed FURTHER.
The sub-types are:
1) hyperbolic - upon stacking, the speed changes like this: 100% - 80% - 64% - 51.2%
To get this type of sub-type - use 'false' as 'linear'
2) linear - upon stacking, the speed changes like this: 100% - 80% - 60% - 40%
To get this type of sub-type - use 'true' as 'linear'
3) by fixed amount - always reduces the same amount (value) of speed, no matter what's the unit's default speed. It looks like this: 320 - 280 - 240.. or 250 - 210 - 170 (the default speed doesn't matter)
To get this type of sub-type, you need to use
You can also forcefully remove speed modifications (it takes a 0.00 timer until they expire, so you can't re-add them in the same trigger, in which you remove them). You just need to call
Or you could use
Development until now:
This library allows you to manage units' speed for a set amount of time (you chose the amount and the time), without needing a dummy with 'slow'.
I made this library, because before it - I was using dummies, casting "slow" on the targets to slow them. However - this wasn't really MUI, since a unit can have only 1 dummy slow at once (if all of the slows are based on the sorceresses slow).
However, this system, besides stacking, is also more efficient. All the slows are done via triggers.
This library can also apply buffs for the duration of the speed modification.
The only drawback of the system is that the speed modifiers, applied by it can't be dispelled with ingame dispells (if you don't trigger the dispell).
Now the system has only 3 limitations:
1) there can be up to 8190 speed modifiers on the map at once. Where with "normal" type (see below) - each stack counts as 1, and with the other types - each buff (buff_ability id), applied on every unit counts as 1.
2) different types of speed modifiers must have different buff_ability (IDs): if you are using 'A00F' for a "normal" type of modifier, you shouldn't use 'A00F' for any other type.
3) It may cause bugs, if used along with "SetUnitMoveSpeed": if a unit has its speed changed by this function, used OUTSIDE of the library - the library will "ignore" it. The unit will have its speed changed by the proper amount, but later, if a speed modification is applied by this library - it will act as if the unit didn't have it. However, it will work "normally" if the unit had its speed altered by this library before using the "SetUnitMoveSpeed", but then the unit's speed will return to a weird value...
To use the system - you need just 1 line of custom text. And the functions take:
1) unit - the unit that is going to have its speed modified
2.a) modifier - the relative amount of speed the unit should have after the modification. 1 = 100%, 0.5 = 50%, 1.5 = 150% (simply divide the % by 100)
2.b) amount - the amount of speed, added (reduced - if a negative number is given) to the unit by the modification. If a unit has 250 speed, and 40 is given as amount - the unit will end up with 290 speed.
3) duration - how long should the modifier last (in seconds).
4.a) linear - should the amount be taken from the unit's current speed (false), or from the unit's max speed (true). "true" would look like: 100% - 80% - 60% - 40%; While "false" will be: 100% - 80% - 64% - 51.2%
4.b) type (only 'ChangeUnitSpeedLinear' has this) - chose the type of modification you want. It can be (it's a string): "normal", "time_stacking", "time_refreshing", "not_stacking" and "special".
5) buff - (buff ability) the ID of the ability, applying the buff. If you don't want to add a buff - put a value between 1 and 1,000,000 (different modification types should have different buff ability ids).
(6) building_up (only 'ChangeUnitSpeed2' has this) - if set to true, the remaining time of the previously applied modifier is added to the duration, which is being currently applied. If false - the time is simply set to the wanted duration ( can't go above it ).
There are 5 main types of slowing, and 3 sub-types. The main types are:
1) "normal" - every time a modifier of this type is applied - the unit's move speed is changed further. However - each stack has its own time:
If you cast a 20% slow for 5 seconds (80% speed remaining), and after 2 seconds - you re-cast it (60% speed remaining), after 3 more seconds (5 total) - the unit will get back the 1-st 20%, and will have 80% speed again. Then after 2 more seconds (7 total) the unit will go back to 100%.
To get this type of modification you need to use
call ChangeUnitSpeed( unit, modifier, duration, linear, buff )
2) "time_stacking" - every time a modifier of this type is applied - the unit's speed isn't changed further, but the time remaining (until the modifier expires) is increased by the 'duration' you have set:
If you cast a 25% slow for 5 seconds (75% speed remaining), and after 2 seconds - you re-cast it, the unit will still have 75% speed, but the time remaining will become 8 seconds.
To get this type of modification you need to use
call ChangeUnitSpeed2( unit, modifier, duration, linear, buff, true )
3) "time_refreshing" - every time a modifier of this type is applied - the unit's speed isn't changed further, but the time remaining (until the modifier expires) is set to the 'duration' you have set:
If you cast a 25% slow for 5 seconds (75% speed remaining), and after 2 seconds - you re-cast it, the unit will still have 75% speed, but the time remaining will go back to 5 seconds.
To get this type of modification you need to use
call ChangeUnitSpeed2( unit, modifier, duration, linear, buff, false )
4) "not_stacking" - if a unit has this type of modifier - you need to wait until it has expired before you can re-apply it, otherwise nothing happens:
A unit is slowed (with this type of modifier) by 40% (60% speed remaining) for 5 seconds, after 2 seconds the slow is re-casted - the speed remains 60%, and the time remaining is 3 seconds. Neither changes.
To get this type of modification you need to use
call ChangeUnitSpeedNS( unit, modifier, duration, linear, buff )
5) "special" - (kind of a combination between (1) and (3)) with every stack - the speed is modified further (than the previous) and the time remaining is refreshed.
If a unit is slowed by 20% for 3 seconds (80% remaining), and after 2 seconds the slow is re-cast - the unit will have 60% speed remaining, and it will expire in 3 seconds (kind a like the Batrider from dota's annoying slow).
To get this type of modification you need to use
call ChangeUnitSpeedSpecial( unit, modifier, duration, linear, buff )
NOTE: With (4) if you apply a 20% slow, with buff = ~(some number) let's say..~ 3 - if you cast a slow with 0.75 modifier (25% slow), and the same buff (3 in this example) - the unit will keep the 20%.
However, if you have a 20% slow with buff ability and buff = 3, and then another 20% slow is applied, with buff = 4. Then the unit will be slowed FURTHER.
The sub-types are:
1) hyperbolic - upon stacking, the speed changes like this: 100% - 80% - 64% - 51.2%
To get this type of sub-type - use 'false' as 'linear'
call ChangeUnitSpeed( u, m, d, false, id )
2) linear - upon stacking, the speed changes like this: 100% - 80% - 60% - 40%
To get this type of sub-type - use 'true' as 'linear'
call ChangeUnitSpeed( u, m, d, true, id )
3) by fixed amount - always reduces the same amount (value) of speed, no matter what's the unit's default speed. It looks like this: 320 - 280 - 240.. or 250 - 210 - 170 (the default speed doesn't matter)
To get this type of sub-type, you need to use
call ChangeUnitSpeedLinear( unit, amount, duration, type, buff )
You can also forcefully remove speed modifications (it takes a 0.00 timer until they expire, so you can't re-add them in the same trigger, in which you remove them). You just need to call
call EndSpeedModifByType( unit, modification_type )
, where unit is the unit, which modifications you want to remove, and the modification_type is a string, which can be "slow" = removes only the slows from this unit, "boost" = removes only the boosts from this unit, or "all" = removes both slows, and boosts.Or you could use
call EndSpeedModifById( unit, buff_ability )
, which would remove only a speed modifier, using a specific buff_ability id (and leave the rest).Development until now:
Version 0.1:
This version had a limit of 250 units having their speed being modified at once. However - there was no limit about the modifications. They could be anything.
However, I was dynamically assigning hashtables to units, which wasn't very efficient.
Version 0.2:
Pretty much the same as (0.1), but the only limit was that 250 units can have their speed modified by the "normal" type of modification, and unlimited amount of units could have the rest of the types. (Was still dynamically assigning hashtables, but only for the "normal" type of modification.
Version 0.3:
Stopped dynamically assigning hashtables to units. Started saving a struct into a static hashtable. With this version I had a limit of 8190 modifiers, with different buff_ability ids (or applied on different units) of the "normal" type could exsist at the same time, but each could have only up to 32 stacks per buff_ability id. (Had a ridiculous struct with 33 agents, and 2 methods with ~70 rows each)
Version 0.4:
Drasticly reduced the library's lenght, but also increased its limits: Up to 1023 modifiers with different buff_ability ids (or applied on different units) of the "normal" type could exsist at the same time, and each unit could have up to 48 stacks of a modifier with the same buff_ability id.
Version 0.5:
Completely changed the way the system works. I removed the limit how many units can be affected by the normal type of modifier, and how many stacks can each unit have. However, I created another limit - now 8190 modifiers are allowed to exist on the map at once (each modifier counts as 1).
This system changed the way slowing is calculated: In previous versions - if a unit is slowed by 100% with some ability, and before this modification expires - the unit is slowed by 20% - when the mega slow expires - the unit was going back to 100% speed, no matter that it has a 20% slow remaining.
With this version - this 'bug' was removed. Now the units have exactly as much movespeed as they should have at any given time.
This version created 1 more limit - each buff_ability was allowed to have only 1 speed_modifier amount - '0' could hold only 1 value at once.
Version 0.9 (test period):
In this version - there is no more limit how many buff_modifier values can a buff_ability id 'hold'. Everything seems to work just fine.
The only limit now is: 8190 modifiers allowed at once, where each "normal" type stack counts as 1, and each modifier, of the other types (with different buff_ability, or applied on a different unit) counts as 1.
Version 0.9.1:
Prevented a potential bug - before, if a "special" modifier (linear) was used on a unit, and then another "special" modifier (hyperbolic), with the same buff_ability ID was used on the same unit - its speed wouldn't have returned to normal.
Also did some other changes to make the script shorter, and I made the structs private.
b) Got rid of some unneeded variables, which I had missed to remove since updating to version 0.5+ (reduced trigger lenght further). Also the 'getEndSpeed' method now requires one variable less.
Version 0.9.2:
Added a function, which can end the speed modification effect, before the time is over.
However, it doesn't work for the "normal" speed modification type.
b) Prevented a potential bug in the force ending function.
Made it work with the "normal" type of speed modification too.
And added a condition (string), does it remove only slows, only boosts, or all the modifications.
c) Renamed the function "ForceEnd" to "ForceEndByType", and added a "ForceEndById" function, which removes only a modifier, with a specific buff_ability id.
Version 0.9.3:
1) (Since I learned that reals have 37 diggits after the decimal point) - I stopped using 10000 as default value for the current.hyperbolic. Now it's 1.
2) Changed the way the system handles "overwriting" modifications with the same buff_ability (for the "time_stacking" and the "time_refreshing" types). Before if 20% slow was applied to the unit, and another 40% slow was applied to it, before the time of the 1-st one was over - it would've refreshed the time of the 1-st slow, but the speed modification wouldn't have changed. While now - it changes the speed modification to the "last applied" one.
3) Added a bit more documentation.
Version 0.9.3B:
1) Added an explanation why am I doing the "buff_ability <= 0x100000" check, at the bottom of the library.
2) Changed the names of the "ForceEnd" functions to "EndSpeedModifByType" and "EndSpeedModifById"
3) Added MoveSpeedX library to the map to show the compatibility between the 2.
C) Fixed a bug - the EndById function wasn't working with modifiers, which didn't have a buff.
This version had a limit of 250 units having their speed being modified at once. However - there was no limit about the modifications. They could be anything.
However, I was dynamically assigning hashtables to units, which wasn't very efficient.
Version 0.2:
Pretty much the same as (0.1), but the only limit was that 250 units can have their speed modified by the "normal" type of modification, and unlimited amount of units could have the rest of the types. (Was still dynamically assigning hashtables, but only for the "normal" type of modification.
Version 0.3:
Stopped dynamically assigning hashtables to units. Started saving a struct into a static hashtable. With this version I had a limit of 8190 modifiers, with different buff_ability ids (or applied on different units) of the "normal" type could exsist at the same time, but each could have only up to 32 stacks per buff_ability id. (Had a ridiculous struct with 33 agents, and 2 methods with ~70 rows each)
Version 0.4:
Drasticly reduced the library's lenght, but also increased its limits: Up to 1023 modifiers with different buff_ability ids (or applied on different units) of the "normal" type could exsist at the same time, and each unit could have up to 48 stacks of a modifier with the same buff_ability id.
Version 0.5:
Completely changed the way the system works. I removed the limit how many units can be affected by the normal type of modifier, and how many stacks can each unit have. However, I created another limit - now 8190 modifiers are allowed to exist on the map at once (each modifier counts as 1).
This system changed the way slowing is calculated: In previous versions - if a unit is slowed by 100% with some ability, and before this modification expires - the unit is slowed by 20% - when the mega slow expires - the unit was going back to 100% speed, no matter that it has a 20% slow remaining.
With this version - this 'bug' was removed. Now the units have exactly as much movespeed as they should have at any given time.
This version created 1 more limit - each buff_ability was allowed to have only 1 speed_modifier amount - '0' could hold only 1 value at once.
Version 0.9 (test period):
In this version - there is no more limit how many buff_modifier values can a buff_ability id 'hold'. Everything seems to work just fine.
The only limit now is: 8190 modifiers allowed at once, where each "normal" type stack counts as 1, and each modifier, of the other types (with different buff_ability, or applied on a different unit) counts as 1.
Version 0.9.1:
Prevented a potential bug - before, if a "special" modifier (linear) was used on a unit, and then another "special" modifier (hyperbolic), with the same buff_ability ID was used on the same unit - its speed wouldn't have returned to normal.
Also did some other changes to make the script shorter, and I made the structs private.
b) Got rid of some unneeded variables, which I had missed to remove since updating to version 0.5+ (reduced trigger lenght further). Also the 'getEndSpeed' method now requires one variable less.
Version 0.9.2:
Added a function, which can end the speed modification effect, before the time is over.
However, it doesn't work for the "normal" speed modification type.
b) Prevented a potential bug in the force ending function.
Made it work with the "normal" type of speed modification too.
And added a condition (string), does it remove only slows, only boosts, or all the modifications.
c) Renamed the function "ForceEnd" to "ForceEndByType", and added a "ForceEndById" function, which removes only a modifier, with a specific buff_ability id.
Version 0.9.3:
1) (Since I learned that reals have 37 diggits after the decimal point) - I stopped using 10000 as default value for the current.hyperbolic. Now it's 1.
2) Changed the way the system handles "overwriting" modifications with the same buff_ability (for the "time_stacking" and the "time_refreshing" types). Before if 20% slow was applied to the unit, and another 40% slow was applied to it, before the time of the 1-st one was over - it would've refreshed the time of the 1-st slow, but the speed modification wouldn't have changed. While now - it changes the speed modification to the "last applied" one.
3) Added a bit more documentation.
Version 0.9.3B:
1) Added an explanation why am I doing the "buff_ability <= 0x100000" check, at the bottom of the library.
2) Changed the names of the "ForceEnd" functions to "EndSpeedModifByType" and "EndSpeedModifById"
3) Added MoveSpeedX library to the map to show the compatibility between the 2.
C) Fixed a bug - the EndById function wasn't working with modifiers, which didn't have a buff.
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* -------------------- MOVESPEED MANAGING LIBRARY BY WereElf ----------------------------
Describtion: This library slows down or speeds up units by certain amount for a set amount of time.
GUI users need to use '1 row' of custom script to use it. Using it is really easy.
New: Added a function for ending the speed modification before the timer expires.
call EndSpeedModifByType( unit, string )
the string can be:
"all" - removes all the modifications
"slow" - removes only slows
"boost" - removes only boosts
You can also use
call EndSpeedModifById( unit, integer )
this one removes only the effect of a modifier, with specific buff_ability
The library offers the following types of speed management:
1) Stacking, independant - called "normal" - this type can slow 1 unit multiple times, and the unit will become slower with every stack.
However - each stack has its own time. The unit's speed would go like this: 100% - 80% - 60% - 80% - 100%
call ChangeUnitSpeed( unit, modifier, duration, linear, buff_ability )
2) Refreshing - called "time_refreshing" - this type slows the unit by a fixed %, and when cast again - the unit's speed doesn't change any further.
But the time remaining of the speed modification is reset.
call ChangeUnitSpeed2( unit, modifier, duration, linear, buff_ability, false )
3) Time stacking - called "time_stacking" - this one also slows the unit by a fixed %. However, if you cast a slow on unit,
which lasts for 5 seconds, and after 2 seconds you re-cast it - the remaining time will become 8 seconds (5-2 + 5).
call ChangeUnitSpeed2( unit, modifier, duration, linear, buff_ability, true )
4) Not stacking - called "not_stacking" - this type slows the unit by a fixed amount and re-using this function, while the
unit is still under the effect of the previous use - nothing happens
call ChangeUnitSpeedNS( unit, modifier, duration, linear, buff_ability )
5) Stacking, finishing together - called "special" - this type is like (1) and (2) combined - each usage changes the speed
further than the previous, and the time is refreshed. When the duration is over - all the stacks are removed at once.
The speed of a unit changes like: 100% - 90% - 80% - 70% - 100%
call ChangeUnitSpeedSpecial( unit, modifier, duration, linear, buff_ability )
Speed can be managed in 3 ways:
1) Speed changes by a % of the unit's current speed: 100% - 80% - 64% - 51.2% .....
use "false" as value for the "linear"
2) Speed changes by a % of the unit's default speed: 100% - 80% - 60% - 40% .....
use "true" as value for the "linear"
3) Speed changes by a fixed amount: 320 - 280 - 240 // OR // 300 - 260 - 220; The unit's maximum speed doesn't matter.
call ChangeUnitSpeedLinear( unit, amount, duration, type, buff_id )
type can be "normal" / "time_stacking" / "time_refreshing" / "not_stacking" / "special"
How to use:
The system provides 2 options:
1) Speed management without a buff:
When you call the functions, that manage the unit's speed - just put a number from 0 to (1 048 570) in the "buff_ability" field.
2) Speed management with a buff:
This one is a bit more tricky. Essentially it's the same, but you need to create an item ability (I based the ones in this sample
map on Item Imollation), and in the buff_ability field - put the id of that ability.
Don't forget to remove any unwanted effects from the ability, and to give it the buff that you want to be getting.
Do have in mind that you need to set the ability's Y to -268435456 for the ability button to be hidden.
IMPORTANT!!! - USE DIFFERENT BUFF_ABILITY (ID) FOR EVERY TYPE OF SPEED MANAGEMENT THAT YOU HAVE ON YOUR MAP.
IF YOU USE '0' FOR A "NORMAL" TYPE - DON'T USE IT FOR "SPECIAL" TYPE TOO, USE SOMETHING ELSE!
Once the preperations have been done - you simply need to call one of the slowing functions, and as parameters put:
1 - unit = the unit that you want to slow down
2.a - modifier = the % you want to change the unit's speed, where 1 is 100%, 0.5 is 50% and 1.5 is 150% (simply divide the % you want by 100)
2.b - amount (for the Linear function = the amount of speed you want to add. You can use negative values too, if you want to slow down the unit.
3 - duration = how long (seconds) do you want the speed modification to last.
4 - linear = do you want the % to be calculated from the unit's default speed ( true ), or from its current speed ( false )
5 - buff_ability = the buff applying ability, that you want to add to the unit during the speed modification. As I already mentioned
use different values for this for different types of speed modification. Two different abilities can still use the same value,
but only as long as their speed modification TYPE is the same too.
(6) - building_up (only the ChangeUnitSpeed2) = does the remaining time get summed up upon refreshing the speedmodification (true)
or does it simply reset the time to the default (false)
Extra: You can also use this system for something, that's not "speed managment". If you use a normal ability's id in the buff_ability
field - you will end up with adding that ability to the unit for the 'duration' seconds.
Note: In this version the limitations of the system is 8190 speed modifiers on the map at once.
With "normal" modifiers - each stack counts as one.
While with the other modifiers - each modifier (not stack) counts as one.
*/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
library SpeedChange
globals
private hashtable Table = InitHashtable() // This table is used to save data into the timers and to link structs to units.
constant real MinMoveSpeed = 1.00 // If you set this to a higher value than the minimum Gameplay constant - the system wouldn't be able to slow the units to the minimum allowed by the game.
//This number can be lower than the Minimum speed allowed in the Gameplay constants. Do not set it to 0 (or negative values) tho if you want to avoid bugs.
endglobals
private struct CurrentSpeed
integer count = 0
real default = 0.00
real speed = 0.00
real hyperbolic = 1.00
real linear = 1.00
static method create takes real d returns CurrentSpeed
local CurrentSpeed c = CurrentSpeed.allocate()
set c.default = d
set c.speed = d
return c
endmethod
method getActualSpeed takes nothing returns real
local real r = this.hyperbolic*this.linear
if r < MinMoveSpeed/100.00 then
return MinMoveSpeed
endif
return (r*this.default)
endmethod
endstruct
private struct Data
unit u
timer t
integer buff_ab
real linear = 0.00
real hyperbolic = 1.00
integer number
boolean normal = false
static method create takes unit u, integer i returns Data
local Data d = Data.allocate()
set d.t = CreateTimer()
set d.u = u
set d.buff_ab = i
return d
endmethod
method onDestroy takes nothing returns nothing
call FlushChildHashtable( Table, GetHandleId(this.t) )
call DestroyTimer(this.t)
endmethod
method getEndSpeed takes real modif, CurrentSpeed c, boolean b returns real
local real result
if b then
set result = c.speed - c.default*(1-modif)
set this.linear = this.linear + (modif - 1)
set c.linear = c.linear + (modif - 1)
else
set result = c.speed*modif
set this.hyperbolic = this.hyperbolic*modif
set c.hyperbolic = c.hyperbolic*modif
endif
if result < MinMoveSpeed then
return MinMoveSpeed
endif
return result
endmethod
// If a modifier of the "time_stacking" or "time_refreshing" type, with the same buff, but different modification is applied to the unit,
// The 'last' applied modifier will replace the old one. If the type is "time_stacking", the the new modifier will still take the time from the old one.
method refreshSpeed takes real modif, CurrentSpeed c, boolean b returns real
local real result
set c.linear = c.linear - this.linear
set c.hyperbolic = c.hyperbolic / this.hyperbolic
if b then
set result = c.getActualSpeed() - c.default*(1-modif)
set this.linear = modif - 1
set this.hyperbolic = 1
set c.linear = c.linear + (modif - 1)
else
set result = c.getActualSpeed()*modif
set this.hyperbolic = modif
set this.linear = 0
set c.hyperbolic = c.hyperbolic*modif
endif
if result < MinMoveSpeed then
return MinMoveSpeed
endif
return result
endmethod
// When a speed modifier expires - this function sets the unit's "current" speed(s) to the values they should have, without the modifier.
method getCurrentSpeed takes CurrentSpeed c returns real
set c.linear = c.linear - this.linear
set c.hyperbolic = c.hyperbolic / this.hyperbolic
return c.getActualSpeed()
endmethod
endstruct
// This function runs when a timer of the "normal" type expires. It simply returns the unit's speed. (And removes the extra buff, if any)
private function OnExpire takes nothing returns nothing
local timer t = GetExpiredTimer()
local Data data = LoadInteger(Table, GetHandleId(t), 0)
local integer id = GetHandleId(data.u)
local CurrentSpeed current = LoadInteger( Table, id, 0 )
local integer count = LoadInteger( Table, id, data.buff_ab )
local timer temp_timer
local Data temp_data
call SetUnitMoveSpeed(data.u, data.getCurrentSpeed(current))
set current.speed = current.getActualSpeed()
if current.count == 1 then
call CurrentSpeed.destroy(current)
call FlushChildHashtable(Table, GetHandleId(data.u))
call UnitRemoveAbility( data.u, data.buff_ab )
else
set temp_timer = LoadTimerHandle(Table, id, current.count)
set temp_data = LoadInteger(Table, GetHandleId(temp_timer), 0)
call SaveTimerHandle(Table, id, data.number, temp_timer)
set temp_data.number = data.number
call RemoveSavedHandle(Table, id, current.count)
set current.count = current.count - 1
if count == 1 then
call RemoveSavedInteger(Table, GetHandleId(data.u), data.buff_ab)
call UnitRemoveAbility( data.u, data.buff_ab )
else
call SaveInteger( Table, GetHandleId(data.u), data.buff_ab, count - 1 )
endif
endif
call Data.destroy(data)
set t = null
endfunction
// Next function runs when a "special"/"time_stacking"/"time_refreshing"/"not_stacking" timer expires. It returns the unit's old speed.
private function OnExpire2 takes nothing returns nothing
local integer i
local timer t = GetExpiredTimer()
local Data data = LoadInteger(Table, GetHandleId(t), 0)
local integer id = GetHandleId(data.u)
local CurrentSpeed current = LoadInteger(Table, id, 0)
local timer temp_timer
local Data temp_data
call SetUnitMoveSpeed( data.u, data.getCurrentSpeed(current) )
call UnitRemoveAbility( data.u, data.buff_ab )
if current.count == 1 then
call FlushChildHashtable(Table, id)
call CurrentSpeed.destroy(current)
else
set temp_timer = LoadTimerHandle(Table, id, current.count)
set temp_data = LoadInteger(Table, GetHandleId(temp_timer), 0)
call SaveTimerHandle(Table, id, data.number, temp_timer)
set temp_data.number = data.number
call RemoveSavedHandle(Table, id, current.count)
set current.speed = current.getActualSpeed()
call RemoveSavedInteger(Table, id, data.buff_ab)
set current.count = current.count - 1
endif
call Data.destroy(data)
set t = null
endfunction
// Changes the unit's speed. Each re-use stacks with the previous, but each stack has its own time.
function ChangeUnitSpeed takes unit u, real speed_modifier, real duration, boolean linear, integer buff_ability returns nothing
local integer id = GetHandleId(u)
local Data data
local CurrentSpeed current = LoadInteger(Table, id, 0)
if buff_ability <= 0x100000 - 2 then //* See why at the bottom.
set buff_ability = buff_ability + 1
else
call UnitAddAbility( u , buff_ability )
endif
if current == 0 then
set current = CurrentSpeed.create(GetUnitDefaultMoveSpeed(u))
call SaveInteger(Table, id, 0, current)
endif
call SaveInteger(Table, id, buff_ability, LoadInteger(Table, id, buff_ability) + 1)
set data = Data.create(u, buff_ability)
set current.speed = data.getEndSpeed( speed_modifier, current, linear )
call SetUnitMoveSpeed( u , current.speed )
set current.count = current.count + 1
set data.number = current.count
set data.normal = true
call SaveInteger(Table, GetHandleId(data.t), 0 , data)
call SaveTimerHandle(Table, id, current.count, data.t)
call TimerStart( data.t, duration, false, function OnExpire )
set u = null
endfunction
// Changes the unit's speed. More casts don't change the unit's speed further, but the time remaining is increased or refreshed.
function ChangeUnitSpeed2 takes unit u, real speed_modifier, real duration, boolean linear, integer buff_ability, boolean building_up returns nothing
local integer id = GetHandleId(u)
local Data data
local CurrentSpeed current = LoadInteger( Table, id, 0 )
if buff_ability <= 0x100000 - 2 then //* See why at the bottom.
set buff_ability = buff_ability + 1
else
call UnitAddAbility( u , buff_ability )
endif
set data = LoadInteger(Table, id, buff_ability)
if current == 0 then
set current = CurrentSpeed.create(GetUnitDefaultMoveSpeed(u))
call SaveInteger(Table, id, 0, current)
endif
if data == 0 then
set data = Data.create(u, buff_ability)
call SaveInteger(Table, id, buff_ability, data)
set current.speed = data.getEndSpeed( speed_modifier, current, linear )
set current.count = current.count + 1
set data.number = current.count
call SaveTimerHandle(Table, id, current.count, data.t)
call SetUnitMoveSpeed(u, current.speed)
call SaveInteger(Table, GetHandleId(data.t), 0, data)
else
if linear then
if speed_modifier - 1 != data.linear then
set current.speed = data.refreshSpeed(speed_modifier, current, linear)
call SetUnitMoveSpeed(u, current.speed)
endif
else
if speed_modifier != data.hyperbolic then
set current.speed = data.refreshSpeed(speed_modifier, current, linear)
call SetUnitMoveSpeed(u, current.speed)
endif
endif
endif
if building_up then
call TimerStart( data.t, duration + TimerGetRemaining(data.t), false, function OnExpire2 )
else
call TimerStart( data.t, duration, false, function OnExpire2 )
endif
set u = null
endfunction
// Changes the unit's speed. The 'current' effect from this type must end before it can be re-used. Otherwise nothing happens.
// If a unit has its speed modified by the non-stacking type, and gets another stack from the same type, with a different buff_ability
// They do stack. Only stacks with the same buff_ability don't.
function ChangeUnitSpeedNS takes unit u, real speed_modifier, real duration, boolean linear, integer buff_ability returns nothing
local integer id = GetHandleId(u)
local Data data
local CurrentSpeed current = LoadInteger( Table, id, 0 )
if buff_ability <= 0x100000 - 2 then //* See why at the bottom.
set buff_ability = buff_ability + 1
else
call UnitAddAbility( u , buff_ability )
endif
set data = LoadInteger(Table, id, buff_ability)
if data == 0 then
if current == 0 then
set current = CurrentSpeed.create(GetUnitDefaultMoveSpeed(u))
call SaveInteger(Table, id, 0, current)
endif
set data = Data.create(u, buff_ability)
call SaveInteger(Table, id, buff_ability, data)
set current.speed = data.getEndSpeed( speed_modifier, current, linear )
set current.count = current.count + 1
set data.number = current.count
call SaveTimerHandle(Table, id, current.count, data.t)
call SetUnitMoveSpeed(u, current.speed)
call SaveInteger(Table, GetHandleId(data.t), 0, data)
call TimerStart( data.t, duration, false, function OnExpire2 )
endif
set u = null
endfunction
// Changes the unit's speed. Each stack changes the unit's speed further than the previous, but once the timer expires - all are removed at once.
function ChangeUnitSpeedSpecial takes unit u, real speed_modifier, real duration, boolean linear, integer buff_ability returns nothing
local integer id = GetHandleId(u)
local Data data
local CurrentSpeed current = LoadInteger( Table, id, 0 )
if buff_ability <= 0x100000 - 2 then //* See why at the bottom.
set buff_ability = buff_ability + 1
else
call UnitAddAbility( u , buff_ability )
endif
set data = LoadInteger(Table, id, buff_ability)
if current == 0 then
set current = CurrentSpeed.create(GetUnitDefaultMoveSpeed(u))
call SaveInteger(Table, id, 0, current)
endif
if data == 0 then
set data = Data.create(u, buff_ability)
call SaveInteger(Table, id, buff_ability, data)
set current.count = current.count + 1
set data.number = current.count
call SaveTimerHandle(Table, id, current.count, data.t)
endif
set current.speed = data.getEndSpeed( speed_modifier, current, linear )
call SetUnitMoveSpeed(u, current.speed)
call SaveInteger(Table, GetHandleId(data.t), 0, data)
call TimerStart( data.t, duration, false, function OnExpire2 )
set u = null
endfunction
// Calculates how much does the unit's speed need to be changed by for it to increase/decrease by a fixed value.
// After that it calls a function from the already declared ones, depending on the type chosen (by a string).
function ChangeUnitSpeedLinear takes unit u, real amount, real duration, string change_type, integer abil returns nothing
local real def = GetUnitDefaultMoveSpeed(u)
local real modif = (def + amount)/def
if change_type == "normal" then
call ChangeUnitSpeed(u, modif, duration, true, abil)
elseif change_type == "time_stacking" then
call ChangeUnitSpeed2(u, modif, duration, true, abil, true)
elseif change_type == "time_refreshing" then
call ChangeUnitSpeed2(u, modif, duration, true, abil, false)
elseif change_type == "not_stacking" then
call ChangeUnitSpeedNS(u, modif, duration, true, abil)
elseif change_type == "special" then
call ChangeUnitSpeedSpecial(u, modif, duration, true, abil)
endif
endfunction
// If unit 'u' has the 'buff_ab' modifier applied - it gets removed after a 0.00 timer.
// Beware, because you can't end a buff, and then re-add it, right after calling the "ForceEnd" function due to the 0.00 timer.
function EndSpeedModifById takes unit u, integer buff_ab returns nothing
local integer id = GetHandleId(u)
local CurrentSpeed current = LoadInteger(Table, id, 0)
local integer i = 1
local timer t
local Data data
if current != 0 then
if buff_ab <= 0x100000 then
set buff_ab = buff_ab + 1
endif
loop
exitwhen i > current.count
set t = LoadTimerHandle(Table, id, i)
set data = LoadInteger(Table, GetHandleId(t), 0)
if data.buff_ab == buff_ab then
if data.normal then
call TimerStart(t, 0.00, false, function OnExpire)
else
call TimerStart(t, 0.00, false, function OnExpire2)
set i = current.count
endif
endif
set i = i + 1
endloop
endif
set u = null
endfunction
// Pretty much like "ForceEndById", but this one removes all the modifiers of a chosen type.
// Types can be "slow" - removes all the slows from the unit; "boost" - removes all the boosts from the unit;
// or "all" removes both boosts and slows from the unit.
function EndSpeedModifByType takes unit u, string s returns nothing
local integer id = GetHandleId(u)
local CurrentSpeed current = LoadInteger(Table, id, 0)
local integer i = 1
local timer t
local Data data
if current != 0 then
loop
exitwhen i > current.count
set t = LoadTimerHandle(Table, id, i)
set data = LoadInteger(Table, GetHandleId(t), 0)
if s == "all" then
if data.normal then
call TimerStart(t, 0.00, false, function OnExpire)
else
call TimerStart(t, 0.00, false, function OnExpire2)
endif
elseif s == "boost" and (data.hyperbolic >= 1 or data.linear > 0) then
if data.normal then
call TimerStart(t, 0.00, false, function OnExpire)
else
call TimerStart(t, 0.00, false, function OnExpire2)
endif
elseif s == "slow" and (data.hyperbolic <= 1 or data.linear < 0) then
if data.normal then
call TimerStart(t, 0.00, false, function OnExpire)
else
call TimerStart(t, 0.00, false, function OnExpire2)
endif
endif
set i = i + 1
endloop
endif
set u = null
endfunction
endlibrary
/* This "if buff_ability <= 0x100000 - 2 then" check is done in order to see if the id given is an object or simply an integer (without a buff).
The -2 is there, because I'm using the '0' parent of the hashtable to to store the unit's default speed, and if 0 is given as id - I'll use 1 instead,
but then if 1 is given I'd need to use 2, so what this does is simply checking if the given number is an object or not, and if it isn't - it increases it by 1.
But if someone uses 0x100000 - 1 as buff id, it wouldn't get converted to 0x100000, which is a potential object :P */
Attachments
Last edited: