• 🏆 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!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Move Speed Modifying Library

Level 12
Joined
Jan 2, 2016
Messages
973
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 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.


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

  • Speed Change Sample Map.w3x
    45.2 KB · Views: 172
Last edited:
@Almia: This resource is intended to simulate stacking effects rather than extending the move speed limit.

@WereElf: I like the resource. I made a similar one for my map a while back, and it is definitely useful when you have a lot of slows/snares.

But the points you've listed in the update are gibberish to me. The next step would be to document the code; and in addition to those points you have listed, you should probably give an example situation where you would want that behavior. :)
 
@Almia: I just have a method SetUnitMoveSpeedX that allows you to set a unit's ms any positive movement speed value. The goal of my system (or rather, Jesus4Lyf's system) is to provide that.

The goal of this system is to address a different problem:
- Let's say you have a footman that has a 50% slow on him for 5 seconds. 1 second into that, you have a 25% slow for 5 seconds. How should this stack?

At t = 0, this system will reduce the footman's movement speed (assume it is 300) to 150. At t = 1, it will reduce it to 112.5. At t = 5, it will restore it to 225. At t = 6, it will restore it back to 300.

There are a lot of ways to approach this problem, though. Some people might prefer to use "dominance" instead of actual stacking, i.e. the unit would have 50% movement reduction from t = 0 to t = 5, and then 25% reduction from t = 5 to t = 6. i.e. only the strongest slow is applied. This is what WoW does.

Things can also get a bit complicated when you want to reduce movement speed by a flat number. Let's say you have an effect that reduces ms by 100 for 5 seconds, so it goes 300 -> 200. At t = 1, you apply a 50% reduction for 6 seconds to get it from 200 -> 100. At t = 5 seconds, you'll restore the first effect to get ms = 200. After that, you'll restore the 50% reduction, but you'll mistakingly get ms = 400. I don't think that is a feature of this system in particular, but it is a potential problem that can happen if you don't always use multiplicative reductions.
 
Level 12
Joined
Jan 2, 2016
Messages
973
The next step would be to document the code; and in addition to those points you have listed, you should probably give an example situation where you would want that behavior. :)

Yeah, I was just making a sample map, when I discovered a problem - the buff version isn't dispellable. I couldn't figure out why is that since on my map 1 buff, applied trough an item was getting dispelled.
Just now, after conducing some tests, I found out that on my map it wasn't the creeps, that were dispelling it, but 1 of my triggers.

So in the end, I guess I'll just let it be undispellable. I tried triggering a dispell, but didn't work either =/

(This kind a makes the Periodc check functions pointless, but I will keep them this way in case there IS a way to make buffs, applied this way dispellable)
~I could make a dummy cast a spell with an endless buff. But that would make the system a little bit heavier xP~

EDIT: I uploaded a sample map, so you can see what can different functions be used for, and how exactly do they work :)
I could've made 3 more "example spells", but I didn't see the point.

EDIT 2: I added some describtions how (for what) to use different functions.
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
This might be only my subjective opinion, but doing the stacking with addition would be better.

Also this will fail horribly if I use something that adds flat move speed, like +50, because:

I want the unit to go 1.1x faster, and its speed is 200.

That will give the unit 20 more speed, so you actually store 220/200 into database, which is 1.1(that calculation is actually useless[the one storing to hashtable]), and then I give the unit boots of speed that give flat +50.

Now the unit has 270 move speed.

Time has come around, you have to remove the added bonus, what you do is you load the 1.1 from hashtable, and divide the move speed by that, which gives you 245,45 move speed, now I drop my boots, get -50, and voila, Im 4.5 move speed down.

If you want to make it correct, you have to not store the multiplication, but the difference, and then substract it from the unit at the end, so the process looks like this:

Start: 200
Want 1.1, do 200*1.1, get 220, store 20 in hashtable.
Add boots: 220 + 50 = 270
Time has come, you actually substract 20(the exact amount you added), gives you 250.
Boots drop: 250 - 50 = 200, back to original.

For double stacking:
Init: 200, want 1.1 and 1.1 again.
First 1.1 = 220 and 20 stored.
Second 1.1 = 242 and 22 is stored.

Now it doesnt matter in which way you remove them from the unit, if you remove both of them at some point, you are back to 200.

If my math is wrong, feel free to correct me.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Yeah, I knew that this system wouldn't work with flat bonuses.
Initially I wanted to make it work with both, but I figured it wouldn't stack.
I gave up on the flat speed change, and made it with %s
Didn't think of the 'boots of speed example' myself, since I was thinking, that normal slows in the game use % difference, and such "bug" can't occure if someone doesn't manually change a unit's speed by a fixed amount :p

Anyways, I was already thinking to change it to save the difference, but I wasn't sure if I should do it since this will happen:
A unit with 300 speed is slowed by 20% - 240 speed remaining (60 saved)
then it's slowed by another 20% - 192 speed remaining (48 saved)
then it's slowed by another 20% - 153,6 speed remaining (38,4 saved)

Then the 1-st slow expires, and the unit re-gains 60 speed - it has 213,6 speed now.
The way it is now - it will go trough the values in reverse order.

I'll make it "linear" as soon as I think of a way to avoid that.
At the moment I'm thinking of either some kind of indexing (I'm not sure how would I make that work), either making a hashtable for every unit, that's been slowed (and recycle them somehow), and in that hashtable I'll store the right order the unit should be regaining its speed by.
1 hashtable per unit, instead of 1 hashtable for all the units, cuz as parent I'll have the buff ID, and as child - 0 will store how many times has the buff been stacked, and from 1 to ~the value in 0~ - the slow's amounts.
I'm just not sure if that wouldn't be an overkill.

And 1 more thing:
If I slow a unit with 300 speed by 20% = 240 remaining (60 saved)
and then a normal (not triggered) slow is applied - 20% more = 192 speed remaining (nothing saved, since it's not triggered)
If the triggered slow expires 1-st - will it make the unit's speed 252, or will it make it 240 (as it's supposed to)?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
it doesnt matter imo in what order he regains the movement, as long as he has the same movement at the end of the last timeout as he had before applying first.

The order should not be fixed, because if I slow you by 5% and then by 90%, and the 5% slow is meant to run out first, I do not want to restore 90% of your move speed back.

I have no idea how this would work with the inbuild slow, that is up to testing. I can test it sometime today or tomorrow if you cant/dont/wont :D

But actually yes, storing the slows in some way per unit, and then recalculating the value on each timeout could do the trick(in your case, that should make it be 300*0,8*0,8 which is 192 instead of 213,6), but it is a little bit more work. I dont think the code speed would change too much, and if you use Table or something, arent even going to spam hashtables.

Changing movement speed is really tricky as you can see :D
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, I did the test myself.
It didn't work as I expected:
Unit with 320 speed, slowed by 40% - 192 speed remaining (128 saved)
Then I slowed it with the inbuild slow for 60% - 76.8 speed remaining (nothing saved)
Then, when the unit regained its speed from the 1-st slow, it got back only 5.12 speed = 81.92 total speed.
And when the slow ends - the unit has 204.8 speed.

The way it works is:
A unit has 192 speed (when slowed with trigger), when slowed again - it's slowed by 60% of its current speed.
But when I use call SetUnitMoveSpeed( u , GetUnitMoveSpeed(u) + LoadReal(Table, id, 'sped') ) - it does 76.8 + 128 = 204.8, but since the slow is still there - it recuces the the speed by 60% = 81.92.

So basically "GetUnitMoveSpeed" includes slows, but "SetUnitMoveSpeed" doesn't - it changes only the "base speed" (not the default speed either).
And even the way my triggers used to be before wouldn't have worked with inbuild slows

However, I think I have a solution to this.
May be done with it until tonight.

And by the way
The order should not be fixed, because if I slow you by 5% and then by 90%, and the 5% slow is meant to run out first, I do not want to restore 90% of your move speed back.
If I wanted to slow a unit by 90% and by 5%, I'd use different buffs for each slow. This way each buff will hold its own value :p

Okay! It works!
I made it work with both items and abilities!
I will update the original post with the incomplete (for now) system, and will update it later, when it's finished :p

Turns out it doesn't matter if I'm saving the linear speed change, or the speed % change, none of them works with items/abilities... Unless some clever hacks are used (and that's what I'm doing :D)

And now.. I can add some functions that change units' speed by fixed values :D
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
You should just use Table by Bribe, not your table recycler. Table by bribe only uses 1 hashtable, your system can potentially run out of hashtables(there is a hard-coded limit of 255 or 256 hashtables in one map initialized at once, and you cant destroy hashtables)
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, I got the "Table" resource you suggested.
I didn't know about it before you mentioned it.
Took me a while reading comments and stuff about it.
I have a question tho...
Does it matter if I flush or destroy the tables?
From what I understood, it's using one hashtable, and it's split into different parts or something.
So Flushing and Destroying should be the same thing? Yet they seem to be different from the way the system was described.

And how exactly would my script look like?
 
Last edited:
Level 23
Joined
Apr 16, 2012
Messages
4,041
flush basically calls FlushChildHashtable on 'this'(the id of the caller), while destroy will also call flush, but it will also deallocate the instance, which means its id can be assigned to another resource that wishes to use Table too.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Well, I feel insecure to make my system work with Bribe's "Table" (since I haven't really used structs before + I'm not really sure if I'll be able to save the unit's hashtable into the library hashtable's field, or how to do it). I asked Bribe to assist me incorporate my system to use his (awaiting for response).

Meanwhile I did a little upgrade - added Linear speed changes :D
Now you can change the unit's speed by a fixed value, rather than %
And now you can keep slowing the unit by some %, but now that % CAN be based on the unit's default speed, instead of on its current speed (thus the slowing can be both linear or hyperbolic).

EDIT: And I improved the system even further :)
Now the only limitation is when people use the normal type of speed changing - they are limited to 250 units AT ONCE.
However, there is no limit how many units can be affected by this system, with buffs from the other types (special, type 2, and not-stacking type)
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, I updated it.
It no loger dynamically assignes hashtables to units.
It saves the speed list as a struct, and saves that struct into the static SpeedTable.

The problem now is that each speed modifier is allowed to have 32 stacks (which is PLENTY for practical purposes, unless you want to change the unit's speed with a modifier between 0.98 and 1.02). However, you can still stack 32 * X number of modifiers to one unit. You just need to use different "buff_ablility" (for example 0, 1, 2, 3, 4, 5..... until 0x100000, if you don't want a buff applied).

I guess my system will need 1-2 more updates before I feel that its ready.
I need to learn how to use structs better than this :D

EDIT: Okay, I learned a bit more about structs, so I drasticly decreased the struct's lenght, and the amount of "elseif"s. By doing that - I decreased the maximum amount of units that can have their speed managed by this system at once. But who needs 8000 units slowed at the same time?!
Now the maximum amount of units is 1023, and the amount of stacks each unit can have is 48.

I added a variable, that allows an easy re-distributing of these amounts. People can set it to 511 units at once, and 96 stacks each unit, or 2047 units at once, and 24 stacks each. Or whatever they want, as long as (stacks * units) = 8190 * 6, and the amount of stacks can be divided by 6.

EDIT 2:
Increased the overall functionality of the library - now I'm using structs, and I'm only saving up to 3 integers per speed modification usage (2 if the unit's speed is modified, and 1 if the unit's speed is already modified, with the same buff ability id)

I think I will update the system only 1 more time, and it will be ready.
At the moment:
A unit is slowed by 99% with 'slow 1' (3 seconds remaining)
The same unit is slowed again (with 'slow 2') by 60%. The unit's speed doesn't change (it simply can't) (for 6 seconds)
Then the 99% slow expires, and the unit regains its base speed (100%), instead of going to 40% (due to the 2-nd slow)

I'll try to fix that with the next update.

EDIT 3: I thought of a way how to fix the thing I pointed out in 2.
I tested the method (with debuf msges, in a test function) and it works better than I could imagine.
Now I only need to change my working functions to use it :p

EDIT 4: Actually, when I'm doing it this way - I don't need the SpeedList o.o
The limit how many units can have their speed modifier at once will fall, and the limit how many normal type stacks of slow can each unit have also disapears.
However, nothing good is 100% good. With the new system you need different buff id for different modifiers:
if you have a slow with modifier 0.8, and buff_ability = 0, you'd need to put some other buff_ability id if you want a 0.75 modifier (again of normal type).
Before it didn't care about the modifiers.
However, people can use "level of ability" as buff_ability if they want different levels of the same ability to slow by different %.
if they want buff applied by it tho - it becomes a bit more complicated xP

EDIT 5: Okay, I uploaded the updated version. And I already have an idea how to make it work flawlessly, without any limits.
I just need some more time.
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, it's finally done!
Now it feels like a complete version, with (almost) no limits.
Completely refreshed the original post :)
Uploaded the updated sameple map, and updated trigger.

However, it'd be nice if you could test it and tell me if you find any bugs, so I can fix them :p
(It will stay version 0.9 until it's confirmed there are no bugs)
 
Level 12
Joined
Jan 2, 2016
Messages
973
I don't understand what do you mean...
You ask if you can dispell the slow before the time is over?
Sorry, you can't xP
I removed that option when I found out that buffs, applied trough item abilities can't be dispelled.
I could make some functions to remove the slow before the time is over, if you want?

Or did you mean something completely different?
 
Level 10
Joined
Jun 17, 2014
Messages
236
I don't understand what do you mean...
You ask if you can dispell the slow before the time is over?
Sorry, you can't xP
I removed that option when I found out that buffs, applied trough item abilities can't be dispelled.
I could make some functions to remove the slow before the time is over, if you want?

Or did you mean something completely different?

yes you're right, remove the slow before the duration end
Loll, can you add that function?
i need it xD
 
Level 12
Joined
Jan 2, 2016
Messages
973
Alright, I updated the original post to version 0.9.2 - I added the "ForceEnd" function.
It DOESN'T work for the "normal" type of speed modification tho. Works for all the others.
And it doesn't care if the speed is being increased or decreased xP

Should I make it care about is it boost or is it slow?

EDIT:
Actually I thought of a way how to make it work with the "normal" type of modification.
And I figured that it may bug out the way I've made it work now, so let me edit it some more :p

EDIT 2:
Okay, fixed the potential bug, and made it work with the "normal" modifier as well.
And now you choose do you remove only the "slow" (s), "boost" (s), or simply "all" :)

EDIT 3:
Okay, now you can remove a specific buff (and leave the rest) as well :)
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
I would love to see this integrate with MoveSpeedX by PurgeAndFire so you can work outside of the limitations of normal WC3 movespeed.

Why are you comparing the buff ID with 0x1000000 -2? Some comments explaining your code will help others to understand it and you to debug it in the future.

Why not loop through a stack every 0.1 seconds instead of using dynamic timers? It seems it would remove the need for the hashtable.

ForceEndById/Type are not good function names.
 
Level 12
Joined
Jan 2, 2016
Messages
973
I would love to see this integrate with MoveSpeedX by PurgeAndFire so you can work outside of the limitations of normal WC3 movespeed.
I'll see what can I do about it.
Hmm, his library is "hooked" to "SetUnitMoveSpeed", thus I don't really need to do anything...
It will automatically work as long as you have MoveSpeedX on your map :p

Why are you comparing the buff ID with 0x1000000 -2? Some comments explaining your code will help others to understand it and you to debug it in the future.
This 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
(I guess I could give that explanation in a coment, inside the script too...)

Why not loop through a stack every 0.1 seconds instead of using dynamic timers? It seems it would remove the need for the hashtable.
When I made this - I still had no knowledge of linked lists.
I guess I could rework it now to use one of these.
However, I can't really imagine getting completely rid of the hashtable, without making it too complex. I'd like to store the unit's "CurrentSpeed" struct there.
As well as the modifiers, applied to the unit.. Since I wouldn't be able to manipulate the time remaining otherwise.

EDIT: Hmm, if you wanted this, just so I can get rid of the hashtable - I don't think it's really worth it. According to this post, Using 1-shot timers is better for situations like this, where I need a single update, after a longer duration.

ForceEndById/Type are not good function names.
How should I call them then?
"EndSpeedModificationType", "EndSpeedModificationId"??
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Well, the functions could be removed entirely. The user can use their own iterators to end modifiers for multiple units at once. The only thing you need to be responsible for is to make sure the user can remove a single unit.

Also, using a unit indexer would eliminate the hashtable at that point. Otherwise, you could index your stuff in a Table.
 
Level 12
Joined
Jan 2, 2016
Messages
973
I did a little update:
added MoveSpeedX
changed the force end functions names to "EndSpeedModif..."
explained why do I do the check for <= 0x100000

And... I added the Force end functions, cuz userid907 asked me to :p
I think it's good to have them.

But yeah, do I really need to change the way it works to use a single looping timer?
It wouldn't improve functionality, will (possibly) make the system heavier, and harder to read from people, who aren't too good with vJASS.

And about the unit indexer.. I really aim to make my systems "stand alone", don't really like putting "requirements", unless they are vital for the system's functionality. Yet this system can work, even without Unit indexer, so why force people have a unit indexer if they want to use this sytem.
(I understand that many people have it, and use it anyways, and wouldn't be a problem for them, but there are still some people who don't...)
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, tomorrow I'm boarding a ship for 4 months and wouldn't be able to update any of my resources during that time.
I'll just throw in few more lines why will I keep it the way it is:
- I've testet my code by applying over 8000 (near the limit) modifiers at once.
The game screen froze for 0.1 seconds, and then for 0.1 seconds more when the modifiers expired. it's possbile that I've reached the operation limit, and not all of the modifiers were applied, but still...
So even if hashtables are slow - they are fast enough to just cause a 0.1 sec freeze when this system is used at its limits.
- On my map I'm using the Custom values of some units for other stuff, and I don't really want to add Unit Indexer to it, yet I AM using this library. That's why I don't want to make Unit Indexer a requirement.

Disclaimer: I know that using unit's custom values is forbiden for public resources, however - my map is not a public resource, so I can do whatever I want on it (as long as it surves its purpose)
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
Oh, well, it's kind a complete.
I was on a ship for 4 months (got home just yesterday, after 2 days of flying, so I was too tired to log on here then), so I couldn't update the title.
The "test period" is/was there just because I wasn't sure it's 100% bug free. But since nobody noticed any malfunctions for 4 months, I guess it's good to go. Changing the title to "VERSION 1" :p
 
Sorry for my ignorance, but it seems a bit bloated and a bit complicated to use.

After reading this [vJASS] - Movespeed --I'm not very sure I'm missing a necessary feature there, and he broke it down to the pretty much the minimal but still useful structures and API. Could you point out if/what Flux's system is missing, what is really required, and what your system has?
 
Level 12
Joined
Jan 2, 2016
Messages
973
Well, I didn't really know this Movespeed library exsisted.
However, my library offers few extra things:
1) It takes care of buff adding and removing, so people wouldn't need to get a buff system, or create one theirselves.
2) It has some pre-set "behaviours". Using Flux's Movespeed library - people would be limited to what I've offered as "normal" type of modifier, but the other types aren't covered. For example, making a time refreshing, but non-stacking slow is way simpler with my library, while if using Flux's - people would have to do it manually...
 
Buffs is not stricly required imo in a speed modifying system. An important goal for me often is to focus on the system's main purpose, and let different features to different systems.

Indeed, in Flux's system one has not control over the remaining time of an instance. However it would be fairly simple to extend the interface to manage the time. Hm...

I read Flux's again, and I actually really like the simple concept. But you say, your point are certain presets-- might you maybe imagine to define presets based on his system, or are you convinved your system should be standalone? Honest question.
 
Level 12
Joined
Jan 2, 2016
Messages
973
I suppose I could make something like an add-on library, that would add the other presets to Flux's library.
Would just need to change the hashtable he's using to public (from private), or I'd need to put my functions (or maybe methods) inside his library, which I don't think would be appropriate.

EDIT: Actually I'd also need to save the timer he's creating somewhere...
 

Submission:
MoveSpeed Modifying Library v1

Date:
23 Jan 2017

Status:
Graveyard
Note:

I think when using structs, no buff is needed. I might miss your point, though.
Anyways, then just ping us, when you again have time to work on it. For now I move this to graveyard,
and later we will just move it back. Or we can also have it in the Lab for now, if you prefer.

 
Top