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

[Spell] Cluster Rocket

Level 14
Joined
Jan 24, 2017
Messages
246
I have an ability based on Cluster Rocket and I noticed that on the higher levels the missiles come out faster. This seems to be a vanillia behaviour. Is there a way to have them come out at the same rate on all levels?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
They are the same for all levels, was hopeing that makes it behave the same on all levels.
I assume the spell uses a Timer which fires 1 missile every X seconds.

X is most likely Effect Duration/Missile Count.

If you increase the Effect Duration to let's say 1/2/3 seconds then it will fire the 6/12/18 missiles at the same rate.
 
Level 14
Joined
Jan 24, 2017
Messages
246
I assume the spell uses a Timer which fires 1 missile every X seconds.

X is most likely Effect Duration/Missile Count.

If you increase the Effect Duration to let's say 1/2/3 seconds then it will fire the missiles at the same rate.
Do you mean multiple abilites with each 1 level so they fire at the same rate? And then just adjust the damage. That sounds like a lot of object data :/
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
Do you mean multiple abilites with each 1 level so they fire at the same rate? And then just adjust the damage. That sounds like a lot of object data :/
No, you just increase the Effect Duration per level. Scale it along with the Missile Count so the rate stays the same.

The unfortunate side effect is that the cast time or channel time or whatever you want to call it will last longer at higher levels.

At least I assume this is the case, I haven't tested it myself.
 
Last edited:
Level 14
Joined
Jan 24, 2017
Messages
246
No, you just increase the Effect Duration per level. Scale it along with the Missile Count so the rate stays the same.

The unfortunate side effect is that the cast time or channel time or whatever you want to call it will last longer at higher levels.
I tried changing the effect duration without any noticeable difference.
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
I tried changing the effect duration without any noticeable difference.
Did it affect how long the Stun lasted? Cluster Rockets is a weird spell...

 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
Here's a custom triggered Cluster Rockets ability if you'd like. It doesn't function exactly like the original Cluster Rockets and is missing things like Building Damage Factor and the Stun debuff, but those two effects could be added fairly easily.

It requires three systems which are provided in the demo map, GUI Simple Timers, GUI Unit Indexer, and Relativistic Missiles. You may already have these systems in your map. If so, do NOT import copies of them. Also, the Missile system comes with it's own set of Dependencies which are other systems like TimerUtils, which again you do not want multiple copies of. These are all pretty lightweight and shouldn't make any impact on map performance if that's a concern.

Map requires the latest patch to open.
 

Attachments

  • New Cluster Rockets 1.w3x
    1 MB · Views: 3
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,564
No, I cannot provide a 1.31 version, but I can show you how to create it yourself.

First, you'll need to create a new custom script (Control + U) that will contain the code for my GUI Simple Timers system:
Create these variables in your trigger editor:
GST_RestartKey (Integer - Initial Value of -1)
GST_Trigger (Trigger)
GST_Unit1 (Unit)
GST_Unit2 (Unit)
GST_Unit3 (Unit)
GST_Integer1 (Integer)
GST_Integer2 (Integer)
GST_Integer3 (Integer)
GST_Real1 (Real)
GST_Real2 (Real)
GST_Real3 (Real)
vJASS:
library GUISimpleTimers
// created by Uncle

globals
    hashtable GST_Hash = InitHashtable()
    timer GST_Timer
    integer GST_NewKey = 0
    integer GST_Index = 0
endglobals


// Call this function to destroy and clean up an expired repeating timer.
// Example: Custom script: call GST_DestroyTimer()
// You would call this inside of your GST_Trigger.

function GST_DestroyTimer takes nothing returns nothing
    local timer t = GetExpiredTimer()
    call FlushChildHashtable(GST_Hash, GetHandleId(t))
    call PauseTimer(t)
    call DestroyTimer(t)
    set t = null
endfunction


// Call this function to destroy and clean up a specific repeating timer.
// Example: Custom script: call GST_ClearTimer(timer)

function GST_DestroyTimerById takes timer t returns nothing
    call FlushChildHashtable(GST_Hash, GetHandleId(t))
    call PauseTimer(t)
    call DestroyTimer(t)
    set t = null
endfunction


// Call this function to get the timer that was just created or just expired.
// Example: Custom script: call GST_GetLastTimer()

function GST_GetLastTimer takes nothing returns timer
    return GST_Timer
endfunction


// Do NOT call this function yourself.
// This is used with the Key system to "restart" a timer.
// It destroys the previously created timer (if one is found).
// Then the new timer that was just created replaces it.
function GST_RestartTimer takes nothing returns nothing
    local integer trigId = GetHandleId(udg_GST_Trigger)
    set GST_NewKey = (-1000000000 + (100000 * LoadInteger(GST_Hash, -1, trigId)))
    if (GST_NewKey != -1000000000) then
        // key already exists
        set GST_NewKey = GST_NewKey + udg_GST_RestartKey
    else
        // key doesn't exist, initialize it
        set GST_Index = GST_Index + 1
        set GST_NewKey = (-1000000000 + (100000 * GST_Index)) + udg_GST_RestartKey
        call SaveInteger(GST_Hash, -1, trigId, GST_Index)
    endif

    set GST_Timer = LoadTimerHandle(GST_Hash, GST_NewKey, trigId)

    // remove the old timer stored at these keys
    if (GST_Timer != null) then
        call PauseTimer(GST_Timer)
        call DestroyTimer(GST_Timer)
        call RemoveSavedInteger(GST_Hash, GST_NewKey, trigId)
    endif
endfunction


// Callback functions which run when a timer expires:
function GST_Int_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 2)
    local boolean repeating = LoadBoolean(GST_Hash, id, 1)
    set udg_GST_Integer1 = LoadInteger(GST_Hash, id, 0)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_IntInt_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Integer1 = LoadInteger(GST_Hash, id, 0)
    set udg_GST_Integer2 = LoadInteger(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_IntIntInt_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 4)
    local boolean repeating = LoadBoolean(GST_Hash, id, 3)
    set udg_GST_Integer1 = LoadInteger(GST_Hash, id, 0)
    set udg_GST_Integer2 = LoadInteger(GST_Hash, id, 1)
    set udg_GST_Integer3 = LoadInteger(GST_Hash, id, 2)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_Real_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 2)
    local boolean repeating = LoadBoolean(GST_Hash, id, 1)
    set udg_GST_Real1 = LoadReal(GST_Hash, id, 0)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_RealReal_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Real1 = LoadReal(GST_Hash, id, 0)
    set udg_GST_Real2 = LoadReal(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_RealRealReal_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 4)
    local boolean repeating = LoadBoolean(GST_Hash, id, 3)
    set udg_GST_Real1 = LoadReal(GST_Hash, id, 0)
    set udg_GST_Real2 = LoadReal(GST_Hash, id, 1)
    set udg_GST_Real2 = LoadReal(GST_Hash, id, 2)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_IntReal_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Integer1 = LoadInteger(GST_Hash, id, 0)
    set udg_GST_Real1 = LoadReal(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_Unit_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 2)
    local boolean repeating = LoadBoolean(GST_Hash, id, 1)
    set udg_GST_Unit1 = LoadUnitHandle(GST_Hash, id, 0)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_UnitUnit_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Unit1 = LoadUnitHandle(GST_Hash, id, 0)
    set udg_GST_Unit2 = LoadUnitHandle(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_UnitUnitUnit_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 4)
    local boolean repeating = LoadBoolean(GST_Hash, id, 3)
    set udg_GST_Unit1 = LoadUnitHandle(GST_Hash, id, 0)
    set udg_GST_Unit2 = LoadUnitHandle(GST_Hash, id, 1)
    set udg_GST_Unit3 = LoadUnitHandle(GST_Hash, id, 2)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_UnitReal_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Unit1 = LoadUnitHandle(GST_Hash, id, 0)
    set udg_GST_Real1 = LoadReal(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_UnitInt_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 3)
    local boolean repeating = LoadBoolean(GST_Hash, id, 2)
    set udg_GST_Unit1 = LoadUnitHandle(GST_Hash, id, 0)
    set udg_GST_Integer1 = LoadInteger(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction

function GST_Simple_Expire takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local trigger trig = LoadTriggerHandle(GST_Hash, id, 0)
    local boolean repeating = LoadBoolean(GST_Hash, id, 1)

    // run the GUI trigger
    call TriggerExecute(trig)

    // the timer is not repeating so destroy it
    if (repeating == false) then
        call FlushChildHashtable(GST_Hash, id)
        call PauseTimer(t)
        call DestroyTimer(t)
    endif

    // clean up memory leaks
    set t = null
    set trig = null
endfunction


// Functions to start timers:
function GST_Real takes real r1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveReal(GST_Hash, id, 0, r1)
    call SaveBoolean(GST_Hash, id, 1, repeating)
    call SaveTriggerHandle(GST_Hash, id, 2, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_Real_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_RealReal takes real r1, real r2, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveReal(GST_Hash, id, 0, r1)
    call SaveReal(GST_Hash, id, 1, r2)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_RealReal_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_RealRealReal takes real r1, real r2, real r3, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveReal(GST_Hash, id, 0, r1)
    call SaveReal(GST_Hash, id, 1, r2)
    call SaveReal(GST_Hash, id, 2, r3)
    call SaveBoolean(GST_Hash, id, 3, repeating)
    call SaveTriggerHandle(GST_Hash, id, 4, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_RealRealReal_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_IntReal takes integer i1, real r1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveInteger(GST_Hash, id, 0, i1)
    call SaveReal(GST_Hash, id, 1, r1)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_IntReal_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_Int takes integer i1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveInteger(GST_Hash, id, 0, i1)
    call SaveBoolean(GST_Hash, id, 1, repeating)
    call SaveTriggerHandle(GST_Hash, id, 2, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_Int_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_IntInt takes integer i1, integer i2, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveInteger(GST_Hash, id, 0, i1)
    call SaveInteger(GST_Hash, id, 1, i2)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_IntInt_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_IntIntInt takes integer i1, integer i2, integer i3, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveInteger(GST_Hash, id, 0, i1)
    call SaveInteger(GST_Hash, id, 1, i2)
    call SaveInteger(GST_Hash, id, 2, i3)
    call SaveBoolean(GST_Hash, id, 3, repeating)
    call SaveTriggerHandle(GST_Hash, id, 4, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_IntIntInt_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_UnitInt takes unit u1, integer i1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveUnitHandle(GST_Hash, id, 0, u1)
    call SaveInteger(GST_Hash, id, 1, i1)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_UnitInt_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_UnitReal takes unit u1, real r1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveUnitHandle(GST_Hash, id, 0, u1)
    call SaveReal(GST_Hash, id, 1, r1)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_UnitReal_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_UnitUnitUnit takes unit u1, unit u2, unit u3, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveUnitHandle(GST_Hash, id, 0, u1)
    call SaveUnitHandle(GST_Hash, id, 1, u2)
    call SaveUnitHandle(GST_Hash, id, 2, u2)
    call SaveBoolean(GST_Hash, id, 3, repeating)
    call SaveTriggerHandle(GST_Hash, id, 4, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_UnitUnitUnit_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_UnitUnit takes unit u1, unit u2, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveUnitHandle(GST_Hash, id, 0, u1)
    call SaveUnitHandle(GST_Hash, id, 1, u2)
    call SaveBoolean(GST_Hash, id, 2, repeating)
    call SaveTriggerHandle(GST_Hash, id, 3, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_UnitUnit_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_Unit takes unit u1, boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveUnitHandle(GST_Hash, id, 0, u1)
    call SaveBoolean(GST_Hash, id, 1, repeating)
    call SaveTriggerHandle(GST_Hash, id, 2, udg_GST_Trigger)

    // start timer
    call TimerStart(t, interval, repeating, function GST_Unit_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif

    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

function GST_Simple takes boolean repeating, real interval returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)

    // save to hashtable
    call SaveTriggerHandle(GST_Hash, id, 0, udg_GST_Trigger)
    call SaveBoolean(GST_Hash, id, 1, repeating)

    // start timer
    call TimerStart(t, interval, repeating, function GST_Simple_Expire)

    // check for keys which are used to restart existing timers
    if udg_GST_RestartKey >= 0 then
        call GST_RestartTimer()
        // keep track of the new timer stored at these keys
        call SaveTimerHandle(GST_Hash, GST_NewKey, GetHandleId(udg_GST_Trigger), t)
        set udg_GST_RestartKey = -1
    endif
 
    // clean up memory leaks
    set GST_Timer = t
    set t = null
endfunction

endlibrary
^ Make sure to create the variables listed at the top of the spoiler!

Then the other 2 systems can be downloaded on Hive and should have 1.31 compatible versions.

After setting up all 3 systems in your map, create the following triggers:
  • New Cluster Rockets Cast
    • Events
      • Unit - A unit Starts the effect of an ability
    • Conditions
      • (Ability being cast) Equal to Cluster Rockets (New)
    • Actions
      • Set VariableSet NCR_Unit = (Triggering unit)
      • Set VariableSet NCR_CV = (Custom value of NCR_Unit)
      • -------- --------
      • -------- The (Target point of ability being cast) needs to be tied to the Missile System which is done using this Index variable: --------
      • Set VariableSet NCR_Index = (NCR_Index + 1)
      • -------- --------
      • -------- This Condition is to help keep the Array size smaller. You may need to increase it if you're casting this spell very frequently! A safe value is 32000. --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • NCR_Index Greater than or equal to 100
        • Then - Actions
          • Set VariableSet NCR_Index = 1
        • Else - Actions
      • -------- --------
      • Set VariableSet NCR_Point[NCR_Index] = (Target point of ability being cast)
      • Set VariableSet NCR_Point[NCR_CV] = NCR_Point[NCR_Index]
      • -------- --------
      • -------- Define the behavior of the Cluster Rockets ability: --------
      • Set VariableSet NCR_Interval = 0.10
      • Set VariableSet NCR_Damage[NCR_Index] = 10.00
      • Set VariableSet NCR_MaxDamage[NCR_Index] = 300.00
      • Set VariableSet NCR_AreaOfEffect[NCR_Index] = 200.00
      • -------- You can customize the Missile properties in New Cluster Rockets Interval --------
      • -------- --------
      • -------- Create a timer that will periodically launch the missiles: --------
      • Set VariableSet GST_Trigger = New Cluster Rockets Interval <gen>
      • Custom script: call GST_UnitInt(udg_NCR_Unit, udg_NCR_Index, true, udg_NCR_Interval)
      • -------- --------
      • -------- Track the created timer so we can later destroy it: --------
      • Custom script: set udg_NCR_Timer[udg_NCR_CV] = GST_GetLastTimer()
  • New Cluster Rockets Stop
    • Events
      • Unit - A unit Stops casting an ability
    • Conditions
      • (Ability being cast) Equal to Cluster Rockets (New)
    • Actions
      • Set VariableSet NCR_Unit = (Triggering unit)
      • Set VariableSet NCR_CV = (Custom value of NCR_Unit)
      • -------- --------
      • Custom script: call RemoveLocation(udg_NCR_Point[udg_NCR_CV])
      • -------- --------
      • -------- Destroy the timer that was launching the missiles: --------
      • Custom script: call GST_DestroyTimerById(udg_NCR_Timer[udg_NCR_CV])
  • New Cluster Rockets Interval
    • Events
    • Conditions
    • Actions
      • -------- GST_Unit1 = Caster --------
      • -------- GST_Integer1 = NCR_Index (used to track the data fields for the spell) --------
      • -------- --------
      • -------- Get a random point inside of the Area of Effect of the spell: --------
      • Set VariableSet NCR_Point[0] = (NCR_Point[GST_Integer1] offset by (Random real number between 0.00 and NCR_AreaOfEffect[GST_Integer1]) towards (Random angle) degrees.)
      • -------- --------
      • -------- SETTING UP MEMBERS --------
      • Set VariableSet MissileStart = (Position of GST_Unit1)
      • Set VariableSet MissileStartZ = 50.00
      • Set VariableSet MissileFinish = NCR_Point[0]
      • Set VariableSet MissileFinishZ = 0.00
      • Set VariableSet MissileModel = Abilities\Spells\Other\TinkerRocket\TinkerRocketMissile.mdl
      • Set VariableSet MissileSpeed = 700.00
      • Set VariableSet MissileData = GST_Integer1
      • Set VariableSet MissileSource = GST_Unit1
      • Set VariableSet MissileOwner = (Owner of MissileSource)
      • Set VariableSet MissileVision = 0.00
      • Set VariableSet MissileArc = 45.00
      • -------- SETTING UP EVENTS --------
      • Set VariableSet Missile_onFinish = New Cluster Rockets OnMissileFinish <gen>
      • -------- LAUNCH THE MISSILE --------
      • Trigger - Run MissileCreate <gen> (ignoring conditions)
      • -------- --------
      • Custom script: call RemoveLocation(udg_NCR_Point[0])
  • New Cluster Rockets OnMissileFinish
    • Events
    • Conditions
    • Actions
      • Set VariableSet NCR_UnitGroup = (Units within NCR_AreaOfEffect[MissileData] of NCR_Point[MissileData].)
      • -------- --------
      • Unit Group - Pick every unit in NCR_UnitGroup and do (Actions)
        • Loop - Actions
          • Set VariableSet NCR_Unit = (Picked unit)
          • -------- --------
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (NCR_Unit is alive) Equal to True
              • (NCR_Unit belongs to an enemy of MissileOwner.) Equal to True
              • (NCR_Unit is A ground unit) Equal to True
              • (NCR_Unit is invulnerable) Equal to False
            • Then - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • NCR_MaxDamage[MissileData] Greater than 0.00
                • Then - Actions
                  • Set VariableSet NCR_MaxDamage[MissileData] = (NCR_MaxDamage[MissileData] - NCR_Damage[MissileData])
                  • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                    • If - Conditions
                      • NCR_MaxDamage[MissileData] Greater than 0.00
                    • Then - Actions
                      • Unit - Cause MissileSource to damage NCR_Unit, dealing NCR_Damage[MissileData] damage of attack type Spells and damage type Normal
                    • Else - Actions
                      • Unit - Cause MissileSource to damage NCR_Unit, dealing (NCR_Damage[MissileData] + NCR_MaxDamage[MissileData]) damage of attack type Spells and damage type Normal
                • Else - Actions
            • Else - Actions
      • -------- --------
      • Custom script: call DestroyGroup(udg_NCR_UnitGroup)
Variables used in these NCR triggers:
NCR_Point (Point)
NCR_Unit (Unit)
NCR_CV (Integer)
NCR_UnitGroup (Unit Group)
NCR_Interval (Real)
NCR_Damage (Real - Array)
NCR_AreaOfEffect (Real - Array)
NCR_MaxDamage (Real - Array)
NCR_Timer (Timer - Array)

The Cluster Rockets (New) ability is based on Death and Decay with it's Targets Allowed set to None. The channel Duration of the ability determines how long the Hero will continue to launch missiles. Play with that value and NCR_Interval to get your desired results.
 
Top