• 🏆 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!

Movement Modifier 1.07

Library MovementModifier ver 1.07 is a simple library that allows to change unit's movement speed (up to 522) for given duration (or permanent). All Object Editor abilities [that changing move speed] will work with this system - they stack. System's bonuses also stack.
Requires Unit Event: GUI Unit Event v2.5.2.0


Optional:
If you want to set unit move speeds beyond the 522 limit import library MoveSpeedXGUI:
MoveSpeedX for GUI v1.1.0.0
Purge's system does nothing on speed below 522. It only activates for higher speeds. Movement Modifier properly adds/removes flat and percent speed bonuses which is a problem to do without this system. Native SetUnitMoveSpeed is not very easy to use. Explained by Ezekiel12 here: Movement Speed Issues
Movement Modifier and MoveSpeedXGUI can work together. Purge's hook catches SetUnitMoveSpeed calls (used in this system) and only for speed>522 activates (basicly it uses SetUnitXY).

Alternative:
There is library Movespeed that uses hashtable for those users who do not want to use Unit Indexer:
[vJASS] - Movespeed

library
JASS:
library MovementModifier initializer Init //ver 1.07 by ZibiTheWand3r3r, 18-08-2018

// Allows to change unit's movement speed for given duration (or permanent)
// All Object Editor abilities [that changing move speed] will work with this system - they stack.
// System's bonuses also stack.

// ********************************
// ==> Requires Unit Event
// https://www.hiveworkshop.com/threads/gui-unit-event-v2-5-2-0.201641/
// ********************************

// ==> Applying move speed bonus:
// UnitAddMoveSpeedBonus(unit u, real percentBonus, real flatBonus, real duration)
// u                        - unit whos move speed will be changed
// percentBonus             - positive to increase speed, negative to slow a target (use 0.00 to not apply percent bonus)
//                           bonus will be added as percent (like Endurance Aura for example)
// flatBonus                - positive to increase speed, negative to slow a target (use 0.00 to not apply flat bonus)
//                           bonus will be added like item's Boots of Speed for example
// duration                 - how long bonus will be on unit, [use duration = 0.00 to make bonus permanent]
// both percentBonus and flatBonus can be used in one function call -- or use one chosen bonus only

// ==> Removing timed movespeed bonuses:
// UnitRemoveMoveSpeedBonuses(unit u, boolean removePositiveBonuses, boolean removeNegativeBonuses)
// u                        - unit whos timed bonuses will be removed
// removePositiveBonuses    - if "true" _all_ positive timed bonuses (bonus>0.00) will be removed
// removeNegativeBonuses    - if "true" _all_ negative timed bonuses (bonus<0.00) will be removed

// ==> Usage examples:
// Speed up unit u: +45% move speed for 12sec:              call UnitAddMoveSpeedBonus(u, 0.45, 0.00, 12.00)
// Speed up unit u: +120 flat move speed for ever:          call UnitAddMoveSpeedBonus(u, 0.00, 120, 0.00)
// Slow unit u: -15%(percent) and -20(flat) for 7sec:       call UnitAddMoveSpeedBonus(u, -0.15, -20.00, 7.00)
// Remove *all negative* move speed bonuses:                call UnitRemoveMoveSpeedBonuses(u, false, true)
// Remove *all positive* move speed bonuses:                call UnitRemoveMoveSpeedBonuses(u, true, false)
// Remove _all_ (negative and positive) move speed bonuses: call UnitRemoveMoveSpeedBonuses(u, true, true)

// how bonuses are calculated:
// moveSpeed = (GetUnitDefaultMoveSpeed(u) + flatBonus) * (1 + percentageBonus1 + percentageBonus2 + etc)

globals
    // configuration:
    private constant real       INTERVAL = 0.50 //How precise is timed applied movespeed change, for greater accuracy use lower value like 0.20 or 0.10
    private constant boolean    PERSISTS_THROUGH_DEATH = true // if "true" then unit/hero's death will not end up applied bonus
    // Set to "false" to end bonus-instance upon unit's death. Above boolean will affect only timed bonuses.
    // end of configuration ----------------------------------------------------------------------------
    private real array          totalFlat               // total flat bonus [targetId] => binded to target unit
    private real array          totalPercent            //  total percent bonus [targetId]
    private boolean array       instanceIsBonusPositive // [id] => connected to spell instance
    private real array          instanceBonus           // bonus [id]
    private real array          instanceDuration        // duration [id]
    private unit array          instanceTarget          // target [id]
    private boolean array       instanceIsBonusFlat     // [id]
    private integer             instanceId = 0
    private timer               t = CreateTimer()
endglobals
//--------------------------------------------------------------------
native UnitAlive takes unit id returns boolean
//--------------------------------------------------------------------
private function Loop takes nothing returns nothing
    local integer id=1
    local integer targetId
    loop
        exitwhen id>instanceId    
        set instanceDuration[id] = instanceDuration[id] - INTERVAL //when spell expires:
        if instanceDuration[id] <= 0.00 or ((not PERSISTS_THROUGH_DEATH) and (not UnitAlive(instanceTarget[id]))) then
            //revert:
            set targetId = GetUnitUserData(instanceTarget[id])
            if instanceIsBonusFlat[id] then
                set totalFlat[targetId] = totalFlat[targetId] - instanceBonus[id] //minus specific flat bonus
            else
                set totalPercent[targetId] = totalPercent[targetId] - instanceBonus[id] //minus specific % bonus
            endif
            call SetUnitMoveSpeed(instanceTarget[id], (GetUnitDefaultMoveSpeed(instanceTarget[id]) + totalFlat[targetId])*(1+totalPercent[targetId]))

            //dealloc: /move data from max to current/
            set instanceIsBonusPositive[id] = instanceIsBonusPositive[instanceId]
            set instanceBonus[id] = instanceBonus[instanceId]
            set instanceDuration[id] = instanceDuration[instanceId]
            set instanceTarget[id] = instanceTarget[instanceId]
            set instanceIsBonusFlat[id]  = instanceIsBonusFlat[instanceId]
            set instanceTarget[instanceId] = null
        
            set instanceId=instanceId-1
            set id=id-1
            if instanceId==0 then
                call PauseTimer(t)
            endif
        endif
        set id=id+1
    endloop
endfunction
//--------------------------------------------------------------------
private function UnitAddMoveSpeedBonus_Ex takes unit u, real bonus, boolean isFlatBonus, real duration returns nothing
    local integer targetId = GetUnitUserData(u)
    //common part:
    if isFlatBonus then
        set totalFlat[targetId] = totalFlat[targetId] + bonus //add flat bonus
    else
        set totalPercent[targetId] = totalPercent[targetId] + bonus //add % bonus    
    endif
    call SetUnitMoveSpeed(u, (GetUnitDefaultMoveSpeed(u) + totalFlat[targetId])*(1+totalPercent[targetId]))
    //permanent:
    if duration==0.00 then
        return
    endif
    //timed:
    set instanceId = instanceId + 1
    set instanceTarget[instanceId] = u
    set instanceDuration[instanceId] = duration
    set instanceIsBonusFlat[instanceId] = isFlatBonus
    set instanceIsBonusPositive[instanceId] = bonus>0.00
    set instanceBonus[instanceId] = bonus
    if instanceId==1 then
        call TimerStart(t, INTERVAL, true, function Loop)
    endif
endfunction
//--------------------------------------------------------------------
//--------------------------------------------------------------------
function UnitAddMoveSpeedBonus takes unit u, real percentBonus, real flatBonus, real duration returns nothing
    if not (u==null or (not UnitAlive(u)) or duration<0.00 or (percentBonus==0.00 and flatBonus==0.00)) then //<=protection
        if percentBonus != 0.00 then
            call UnitAddMoveSpeedBonus_Ex(u, percentBonus, false, duration)
        endif
        if flatBonus != 0.00 then
            call UnitAddMoveSpeedBonus_Ex(u, flatBonus, true, duration)
        endif
    endif
endfunction
//-----------------------------------------------------------------------------------
// ==> Removing timed movespeed bonuses
function UnitRemoveMoveSpeedBonuses takes unit u, boolean removePositiveBonuses, boolean removeNegativeBonuses returns nothing
    local integer id=1
    loop
        exitwhen id>instanceId
        if u == instanceTarget[id] then
            if removePositiveBonuses and instanceIsBonusPositive[id] then
                set instanceDuration[id] = 0.00
            endif
            if removeNegativeBonuses and (not instanceIsBonusPositive[id]) then
                set instanceDuration[id] = 0.00
            endif
        endif
        set id=id+1
    endloop
    call Loop()
endfunction
//-----------------------------------------------------------------------------------
// Units that changing their form (metamorphosis, chamical rage, destroyer form, bear form etc)
// will lose their move speed bonuses comes from SetUnitMoveSpeed.
// To prevent that: after unit transforms to new unit-type this function must be called:
function Trig_UnitUpdateMoveSpeedBonusAfterMorph takes nothing returns boolean
    local integer targetId = GetUnitUserData(udg_UDexUnits[udg_UDex])
    call SetUnitMoveSpeed(udg_UDexUnits[udg_UDex], (GetUnitDefaultMoveSpeed(udg_UDexUnits[udg_UDex]) + totalFlat[targetId])*(1+totalPercent[targetId]))
    return false
endfunction

private function Init takes nothing returns nothing
    local trigger t=CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_UnitTypeEvent", EQUAL, 1.00)
    call TriggerAddCondition(t, function Trig_UnitUpdateMoveSpeedBonusAfterMorph)
    set t=null
endfunction
endlibrary

examples:

  • HeavyItem1
    • Events
      • Unit - A unit Acquires an item
    • Conditions
      • (Item-type of (Item being manipulated)) Equal to Crown of Kings +5
    • Actions
      • Custom script: call UnitAddMoveSpeedBonus(GetManipulatingUnit(), 0.00, -50.00, 0.00)
  • HeavyItem2
    • Events
      • Unit - A unit Loses an item
    • Conditions
      • (Item-type of (Item being manipulated)) Equal to Crown of Kings +5
    • Actions
      • Custom script: call UnitAddMoveSpeedBonus(GetManipulatingUnit(), 0.00, 50.00, 0.00)
  • OrThis
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Chain Lightning
    • Actions
      • Set tempUnit = (Target unit of ability being cast)
      • Set tempReal = (Random real number between 1.00 and 2.50)
      • Game - Display to (All players) the text: ( + ((String((tempReal x 100.00))) + % speed for 9 sec.))
      • Custom script: call UnitAddMoveSpeedBonus(udg_tempUnit, udg_tempReal, 0.00, 9.00)
Contents

Movement Modifier 1.07 (Map)

Reviews
KILLCIDE
Useful system with easy to use API. Needs Fixed Nothing Suggestions function UnitAddMoveSpeedBonus & function UnitAddMoveSpeedBonus_Ex could be combined It would be much better if you used linked list over dynamic indexing. You can easily remove...

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Needs Fix:
  • Please follow the Jass conventions for variable naming. For instance constant variables should be all CAPS
    JASS:
        private constant boolean           useEffects = true
        private constant string              positiveBuff = "Abilities\\Spells\\Undead\\UnholyFrenzy\\UnholyFrenzyTarget.mdl"
        private constant string              negativeBuff = "Abilities\\Spells\\Human\\slow\\slowtarget.mdl"
Others:
  • JASS:
            if (GetUnitTypeId(ms_targ[id])==0 or IsUnitType(ms_targ[id], UNIT_TYPE_DEAD)) then //if target is dead
                set ms_dur[id] = 0.00
            endif
            ...
            if ms_dur[id] <= 0.00  then
    =>
    JASS:
    if ms_dur[id] <= 0.00  or not UnitAlive(ms_targ[id]) then
  • set ms_dur[id] = ms_dur[id] - 1.00 //when spell expires:
    1.00 can be added to configuration called INTERVAL or something
  • if useEffects => static if USE_EFFECT
    use static ifs for constant conditions
  • Attachment points can be added to configuration.
    JASS:
    set g_effPos[targetId] = AddSpecialEffectTarget(positiveBuff, ms_targ[id_max], "overhead")
    set g_effNeg[targetId] = AddSpecialEffectTarget(negativeBuff, ms_targ[id_max], "origin")
  • Effects should only be created once:
    JASS:
            set g_countPos[targetId] = g_countPos[targetId] + 1    
            call DestroyEffect(g_effPos[targetId])
            set g_effPos[targetId] = null
            set g_effPos[targetId] = AddSpecialEffectTarget(positiveBuff, ms_targ[id_max], "overhead")
    =>
    JASS:
       set g_countPos[targetId] = g_countPos[targetId] + 1
        g_countPos[targetId] == 1 then
            set g_effPos[targetId] = AddSpecialEffectTarget(positiveBuff, ms_targ[id_max], "overhead")
    The same applies to negative effect too.
  • Merge _permanent and _timed functions. If duration is less than or equal to zero then it means the bonus is permanent.
  • UnitRemoveMoveSpeedBonuses function requires to iterate twice which is inefficient.
  • Remove private group ms_g = CreateGroup() because you don't use it.
  • Please delete unrelated object data.
Set to Awaiting Update. It's minor but rule is rule.
 
Level 18
Joined
Nov 21, 2012
Messages
835
So if you set a unit's move speed to <= 522, then it destroys their instance (if it exists) or just does nothing. Hooks still ultimately call the function, so it'll still end up calling SetUnitMoveSpeed(u, num) for that unit.
Purge's system does nothing on speed below 522. It only activates for higher speeds.

My system properly adds/removes flat and percent speed bonuses which is a real pain to do that without it. SetUnitMoveSpeed is not very easy to use. Explained for example by
Ezekiel12 here Movement Speed Issues

Both systems (Purge's an mine) can work together. Purge's hook catches SetUnitMoveSpeed calls (used in this system) and only if speed>522 detected it activates (basicly it uses SetUnitXY).


I would appreciate it if you can follow JPAG conventions
Previously I fixed globals names as Quilnez suggested. What did I miss?
 
Level 18
Joined
Nov 21, 2012
Messages
835
I changed private integer "id_max" to "ms_id", fixed intendation, I really dont understand what constant vars you refering to. All constants are (and was) CAPS.
edit
Im not sure if I should null variable ms_targ in function Loop in dealloc part before this: set ms_targ[id] = ms_targ[ms_id]
 
Last edited:
Level 37
Joined
Jul 22, 2015
Messages
3,485
I really dont understand what constant vars you refering to.
Sorry I didn't mean to quote his text about constants. I was referring to your other variable names. Quil mentioned constats an example, but variable names should be likeThis.

Im not sure if I should null variable ms_targ in function Loop in dealloc part before this
It is not really necessary for your data structure because they will easily be overwritten by the next instance. I personally only really care if its like linked list because of the way recycling works for that situation. However, if you just want to be consistent, you would do this:
JASS:
set ms_target[id] = ms_target[ms_id]
set ms_target[ms_id] = null
 
Level 18
Joined
Nov 21, 2012
Messages
835
It is not really necessary for your data structure because they will easily be overwritten by the next instance.
Thank you, so I will left it as is. Variable names changed.

edit
I'm curious about keeping UnitAlive check in: if instanceDuration[id] <= 0.00 or (not UnitAlive(instanceTarget[id])) then inside Loop function. Should unit's (hero's) death ends duration of movespeed bonus? what you think? User may instantly revive hero and wants to keep bonus until duration expires.

edit2 - updated to v1.05
  • Abandoned special effects as they can be handled in external way, as user wishes.
  • Also decided to exclude condition UnitAlive(u) for ending bonus instance (only duration matters now for timed bonuses).
  • Added set instanceTarget[instanceId] = null in dealloc part inside Loop function.
 
Last edited:
Level 37
Joined
Jul 22, 2015
Messages
3,485
Should unit's (hero's) death ends duration of movespeed bonus? what you think? User may instantly revive hero and wants to keep bonus until duration expires.
Logically I think so. You could always have a boolean called "PERSISTS_THROUGH_DEATH" or some sort to alloe people to decide. It will require some clever work to get it to fit in a single if statement.
 
Level 37
Joined
Jul 22, 2015
Messages
3,485
Useful system with easy to use API.

Needs Fixed

  • Nothing

Suggestions

  • function UnitAddMoveSpeedBonus & function UnitAddMoveSpeedBonus_Ex could be combined
  • It would be much better if you used linked list over dynamic indexing. You can easily remove certain instances instead of removing all the bonuses of a unit

Status


Approved
 
Level 18
Joined
Nov 21, 2012
Messages
835
I will update this thread in the future by adding one function.
Units that changing their form (metamorphosis, chamical rage, destroyer form, bear form etc) will lose their move speed bonuses comes from SetUnitMoveSpeed. To prevent that: after unit transforms to new unit-type this function must be called:
JASS:
function UnitUpdateMoveSpeedBonusAfterMorph takes unit u returns nothing
    local integer targetId = GetUnitUserData(u)
    call SetUnitMoveSpeed(u, (GetUnitDefaultMoveSpeed(u) + totalFlat[targetId])*(1+totalPercent[targetId]))
endfunction
I will add it to the library soon.

Updated to 1.07.
 
Last edited:
Level 8
Joined
May 24, 2016
Messages
296
I will update this thread in the future by adding one function.
Units that changing their form (metamorphosis, chamical rage, destroyer form, bear form etc) will lose their move speed bonuses comes from SetUnitMoveSpeed. To prevent that: after unit transforms to new unit-type this function must be called:
JASS:
function UnitUpdateMoveSpeedBonusAfterMorph takes unit u returns nothing
    local integer targetId = GetUnitUserData(u)
    call SetUnitMoveSpeed(u, (GetUnitDefaultMoveSpeed(u) + totalFlat[targetId])*(1+totalPercent[targetId]))
endfunction
I will add it to the library soon.

Updated to 1.07.
The function listed in the message takes unit u, while in the demo map, there's actually no unit.

It looks like that

JASS:
//-----------------------------------------------------------------------------------
// Units that changing their form (metamorphosis, chamical rage, destroyer form, bear form etc) 
// will lose their move speed bonuses comes from SetUnitMoveSpeed. 
// To prevent that: after unit transforms to new unit-type this function must be called:
function Trig_UnitUpdateMoveSpeedBonusAfterMorph takes nothing returns boolean
    local integer targetId = GetUnitUserData(udg_UDexUnits[udg_UDex])
    call SetUnitMoveSpeed(udg_UDexUnits[udg_UDex], (GetUnitDefaultMoveSpeed(udg_UDexUnits[udg_UDex]) + totalFlat[targetId])*(1+totalPercent[targetId]))
    return false
endfunction

Can anyone help to handle this function properly?
 
Level 18
Joined
Oct 17, 2012
Messages
818
You can simply add the function that ZiBitheWand3r3r provided to the library and then use it. As the library stands though, you don't need to touch that function because transformations are automatically covered when UnitTypeEvent equals to 1, which is the event id for unit transformations in Bribe's GUI Unit Event.
 
Last edited:
Level 8
Joined
May 24, 2016
Messages
296
You can simply add the function that ZiBitheWand3r3r provided to the library and then use it. As the library stands though, you don't need to touch that function because transformations are automatically covered when UnitTypeEvent equals to 1.
So, library already detects unit who getting transformed and then transfer all the movespeed modifier back and forth?

Will it work for this trick too since it's basically transformation too? Hero passive transformation
 
Level 8
Joined
May 24, 2016
Messages
296
As long as Bribe's GUI Unit Event can catch the transformation, the move speed bonuses should be transferred from one form to the next.
My friend there's another question from a fool (me).

Does unit event replace unit indexer? I believe the last version of 1.26 unit event is 2.5.2.0 (the movement system itself requires it). I have been simple Unit Indexer the one that is implemented into Damage Engine 3A.0.0.0 and 3.8.0.0 bribe's damage system for 1.26.

If I replace unit indexer with unit event 2.5.2.0 so I can use movement modifier, the same damage engine Damage Engine 3A.0.0.0 and movement modfiier 1.07, is there's any chance I'd get some bugs and conflicts? Are they compatible with each other without any additional actions?
 
Top