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

GUI Spell System v1.8.0.0

This bundle is marked as director's cut. It exceeds all expectations and excels in every regard.
GUI Spell System
version 1.8.0.0

About:

Spell System revolutionizes the spell making process. It handles spell indexing, end-cast events, timed triggers, configurable unit-in-range filters, memory leak management, gives you access to a part of a global hashtable and reduces your variable and handle count drastically per spell. This system unifies all spell events and runs your triggers for you, directly. The result is an efficient, fully-featured and clean spell coding experience, and my personal favorite contribution to the GUI community.

Included in the demo map is the first spell I've made in 5 years: The Big Dipper. I am pleased to present this project to the Hive community!

Features:
  • Automatic event handling of spells. The system runs your triggers for you once you have configured your spell with a couple of variables.
  • Automatic creation and destruction of spell indices.
  • Automatic variable setting: sets the cast ability, the ability level, caster, target, caster position and target position and more into Spell__ variables.
  • Automatically runs a spell instance periodically if you specify an OnLoop trigger from a spell.
  • Automatic memory management of caster/target locations.
  • Unified common variables to reduce user's need to constantly create the same, redudant variables each time per spell.
  • Automatically gets all units in a range matching pre-specified conditions.
  • You can configure which units in range are chosen from the configuration trigger of your spell.


Spell Preview: Spell System GIF - Find & Share on GIPHY


Required variables

Spell__Ability (ability)

Set in each spell's Config trigger to the ability you want a trigger for.​

Spell System <gen> (trigger)

Run this trigger, ignoring conditions, once all Config variables are set.​

At least one event trigger is required

Spell__Trigger_OnEffect (trigger)

Trigger runs when a unit starts the effect of the specified ability.​

Spell__Trigger_OnFinish (trigger)

When a unit is done casting the ability (completed/canceled).​

Spell__Trigger_OnCast/Channel (trigger)

Usually unused. For more information about what these do, see Casting events guide

Event Responses

Spell__Caster (unit)

The unit casting the spell.​

Spell__Target (unit)

The target unit of the spell, if applicable. Will be set to (no unit) if it was a point-target or no-target spell.​

Spell__CastPoint (point)

The position of the casting unit. If the caster moves, this point moves with it.​

Spell__TargetPoint (point)

The target point of the spell. If the target was a unit and the unit moves, this point moves with it.​

Commonly-extracted variables

Spell__Level (integer)

The level of the ability being cast.​

Spell__CasterOwner (player)

The caster owner is also commonly used to determine friend from foe, or to specify the owner of a dummy unit.​

MUI variables

Spell__Index (integer)

The unique index which is used in an array to store specific stuff with your spell. Using this variable as an array index ensures your spell is fully-MUI.​

Spell__Hash (hashtable)

You can store anything extra you want or need for your spell into this hashtable as long as you only use Spell__Index as the parent key.​

Common Time variables

Does your spell do something over time? If not, skip this section.

Spell__Trigger_OnLoop (trigger)

Usually set from the config trigger, but can be set or changed at any point in the spell's lifespan. This trigger runs after a specified period of time.​

Spell__Time (real)

How long (in seconds) to wait before the OnLoop trigger runs (or runs again, if used sequentially). It must be set to a value above 0.00 to be registered with the system.

Spell__Duration (real)

Specified during Config or during any phase of the spell. Will keep repeating the OnLoop trigger every Spell__Time seconds until the duration is expired.

If you've already set a duration and wish to terminate it early, simply set the Duration to 0.00.

If you need to know if the duration has ended, check if it's equal to or less than 0.00. If it will still run, it will be greater than 0.00.​

Units-in-range essential variables

Spell__InRangePoint (point)

Set to the center of where you want to pick units from.​

Spell__InRange (real)

How wide should the radius be?​

Spell__InRangeGroup (unit group)

Filled with all units in range of the given point who matched the spell filter criteria.​

Spell__Filter_AllowX (boolean)

Specify what kind of targets you allow to be added to Spell__InRangeGroup. Specify any of these filters from your spell's Config trigger if you want custom ones. What you leave unspecified defaults to whatever the filters are set to in Spell System Config <gen>​



Spell__DurationPerLevel (real)

You can specify a duration per level from the Config trigger. The formula is Spell__Duration + (Spell__Level x Spell__DurationPerLevel).​

Spell__StartDuration (boolean)

Set to True if you specified a duration from the Config trigger so the system knows when you intend to have the duration start from.​

Spell__LevelMultiplier (real)

Made available as a real so you don't have to convert the spell level into a real yourself.​



Spell__UseTargetGroup (boolean)

Set to True from the Config trigger if you want a group to keep track of your target units.​

Spell__TargetGroup (unit group)

If you specified Spell__UseTargetGroup, this group is available to you throughout the duration of the spell. It is empty until you manually add units to it.​

Spell__InRangeCount (integer)

The number of units added to Spell__InRangeGroup.​

Spell__InRangeUnits[] (unit array)

An array with indices between 1 and Spell__InRangeCount listing the units in Spell__InRangeGroup.​

Spell__Trigger_InRange (integer)

Set from your spell's Config trigger (if desired), It is used to add additional things to the unit filter that couldn't be covered otherwise.​

Spell__InRangeMax (integer)

Set before setting Spell__InRange. This integer limits the number of units added to Spell__InRangeGroup. The extra units are removed at random.​



Want to detect if a caster has finished channeling or a spell was canceled early?

Spell__Channeling (boolean)

True if the caster is still channeling the ability. Useful from an OnLoop trigger if you want to interrupt the loop if the caster stops.​

Spell__Completed (boolean)

True if the caster successfully completed the channeling of the spell without it getting interrupted. Invaluable if you want to do bonus effects upon successful execution of the ability.​



Inspiration:

  • SpellEvent by Anitarf - Without his system, GUI Spell System wouldn't be nearly what it is now. Almost all the fundamentals of this system are encapsulated by SpellEvent.
  • vJass structs by Vexorian - Create a unique integer variable which will act as an array index. Has OnDestroy functionality which is run automatically and is double-free safe.
  • Timer32 by Jesus4Lyf - One timer for all periodic events; handles iteration of instances for you behind-the-scenes.
  • Constant Timer Loop 32 by Nestharus - A combination of Timer32 and vJass struct creation/destruction which pauses timers/removes system triggers when completed.
  • GroupUtils by Rising_Dusk - Factors in a unit's collision size into the InRange check and recycles unit groups.
  • Table by Vexorian - The idea to use one hashtable with unique indexes as parent keys so it can be used for many different things.


  • Spell System Config
    • Events
    • Conditions
    • Actions
      • -------- Only one dummy unit type is needed as you can attach an effect to it of any kind --------
      • -------- --------
      • Set Spell__DummyType = Dummy (Vexorian, Anitarf, Infrane)
      • Set Spell__DummyOwner = Neutral Extra
      • Set Spell__Interval = (1.00 / 32.00)
      • -------- --------
      • -------- Configure default values for the unit filter: --------
      • -------- --------
      • Set Spell__Filter_AllowEnemy = True
      • Set Spell__Filter_AllowLiving = True
      • Set Spell__Filter_AllowHero = True
      • Set Spell__Filter_AllowNonHero = True
      • Set Spell__Filter_AllowAlly = False
      • Set Spell__Filter_AllowDead = False
      • Set Spell__Filter_AllowFlying = False
      • Set Spell__Filter_AllowMechanical = False
      • Set Spell__Filter_AllowStructure = False
      • -------- --------
      • -------- Magic immunity is a great thing to block, as it also discludes invulnerable units from being picked --------
      • -------- --------
      • Set Spell__Filter_AllowMagicImmune = False
      • -------- --------
      • -------- Normal WC3 abilities, like Channel, wake sleeping creeps - even if they don't deal damage or apply buffs. --------
      • -------- Because of this, I provided an option to wake up creeps when they are enumerated by an InRange command. --------
      • -------- --------
      • Set Spell__WakeTargets = True


JASS:
function SpellIndexGetVars takes integer i returns nothing
    set udg_Spell__Ability = udg_Spell_i_Abil[udg_Spell_i_Head[i]]
    set udg_Spell__Index = i
    set udg_Spell__Caster = udg_Spell_i_Caster[i]
    set udg_Spell__CasterOwner = GetOwningPlayer(udg_Spell__Caster)
    set udg_Spell__Level = udg_Spell_i_Level[i]
    set udg_Spell__LevelMultiplier = udg_Spell__Level //Spell__LevelMultiplier is a real variable.
    set udg_Spell__Target = udg_Spell_i_Target[i]
   
    //Magic to ensure the locations never leak.
    call MoveLocation(udg_Spell__CastPoint, GetUnitX(udg_Spell__Caster), GetUnitY(udg_Spell__Caster))
    if udg_Spell__Target == null then
        call MoveLocation(udg_Spell__TargetPoint, udg_Spell_i_TargetX[i], udg_Spell_i_TargetY[i])
    else
        call MoveLocation(udg_Spell__TargetPoint, GetUnitX(udg_Spell__Target), GetUnitY(udg_Spell__Target))
    endif
    set udg_Spell__TargetGroup = udg_Spell_i_TargetGroup[i]
    set udg_Spell__Completed = udg_Spell_i_Completed[i]
    set udg_Spell__Channeling = udg_Spell_i_Channeling[i]
endfunction
function SpellSetFilters takes integer i returns nothing
    set udg_Spell_i_AllowEnemy[i]       = udg_Spell__Filter_AllowEnemy
    set udg_Spell_i_AllowAlly[i]        = udg_Spell__Filter_AllowAlly
    set udg_Spell_i_AllowDead[i]        = udg_Spell__Filter_AllowDead
    set udg_Spell_i_AllowLiving[i]      = udg_Spell__Filter_AllowLiving
    set udg_Spell_i_AllowMagicImmune[i] = udg_Spell__Filter_AllowMagicImmune
    set udg_Spell_i_AllowMechanical[i]  = udg_Spell__Filter_AllowMechanical
    set udg_Spell_i_AllowStructure[i]   = udg_Spell__Filter_AllowStructure
    set udg_Spell_i_AllowFlying[i]      = udg_Spell__Filter_AllowFlying
    set udg_Spell_i_AllowHero[i]        = udg_Spell__Filter_AllowHero
    set udg_Spell_i_AllowNonHero[i]     = udg_Spell__Filter_AllowNonHero
endfunction
function SpellIndexDestroy takes integer i returns nothing
    local integer indexOf
    local integer index
    if udg_Spell_i_RecycleList[i] >= 0 then
        return
    endif
    //If the caster is still channeling on the spell, don't destroy until it's finished:
    if not udg_Spell_i_Channeling[i] then
        set index = udg_Spell_i_Head[i]
        set udg_Spell_i_RecycleList[i] = udg_Spell_i_Recycle
        set udg_Spell_i_Recycle = i
       
        //Reset things to defaults:
        set udg_Spell_i_Time[i] = 0.00
        set udg_Spell_i_LastTime[i] = 0.00
        set udg_Spell_i_Duration[i] = 0.00
        set udg_Spell_i_Completed[i] = false
        set udg_Spell_i_Caster[i] = null
        set udg_Spell_i_Target[i] = null
        set udg_Spell_i_OnLoopStack[i] = null
       
        //Recycle any applicable target unit group.
        if udg_Spell_i_TargetGroup[i] != null then
            call GroupClear(udg_Spell_i_TargetGroup[i])
            set udg_Spell_i_GroupStack[udg_Spell_i_GroupN] = udg_Spell_i_TargetGroup[i]
            set udg_Spell_i_GroupN = udg_Spell_i_GroupN + 1
            set udg_Spell_i_TargetGroup[i] = null
        endif
       
        //Clear any user-specified data in the hashtable:
        call FlushChildHashtable(udg_Spell__Hash, i)
        //call BJDebugMsg("Destroying index: " + I2S(i))
    endif
   
    set indexOf = udg_Spell_i_StackRef[i]
    if indexOf >= 0 then
        set index = udg_Spell_i_StackN - 1
        set udg_Spell_i_StackN = index
       
        set udg_Spell_i_StackRef[udg_Spell_i_Stack[index]] = indexOf
        set udg_Spell_i_Stack[indexOf] = udg_Spell_i_Stack[index]
        if index == 0 then
            //If no more spells require the timer, pause it.
            call PauseTimer(udg_Spell_i_Timer)
        endif
        set udg_Spell_i_StackRef[i] = -1
    endif
endfunction
function SpellTriggerExecute takes integer i, trigger t returns real
    local real d = udg_Spell_i_Duration[i]
    local boolean b = false
    set udg_Spell__Duration = d
    set udg_Spell__Time = 0.00
    if t != null then
        set udg_Spell__Trigger_OnLoop = null
        set udg_Spell__Expired = d <= 0.00 //If the duration is <= 0, the spell has expired.
        call SpellIndexGetVars(i)
        if TriggerEvaluate(t) then
            call TriggerExecute(t)
        endif
        if udg_Spell__Trigger_OnLoop != null then
            set udg_Spell_i_OnLoopStack[i] = udg_Spell__Trigger_OnLoop
        endif
        //The remaining lines in this function process the duration specified by the user.
        if udg_Spell__StartDuration then
            set udg_Spell__StartDuration = false
            set udg_Spell__Duration = udg_Spell_i_Duration[udg_Spell_i_Head[i]] + udg_Spell_i_LastTime[udg_Spell_i_Head[i]]*udg_Spell__LevelMultiplier
        elseif (udg_Spell__Expired and d > 0.00) or (udg_Spell__Duration <= 0.00) then
            set udg_Spell__Duration = 0.00
            return udg_Spell__Time
            //The user manually expired the spell or the spell duration ended on its own.
        endif
        if d != udg_Spell__Duration then
            //A new duration has been assigned
            set d = udg_Spell__Duration
            set b = true
        endif
        set udg_Spell__Duration = 0.00
        if udg_Spell__Time == 0.00 then
            if udg_Spell_i_LastTime[i] == 0.00 then
                if udg_Spell_i_Time[udg_Spell_i_Head[i]] > 0.00 then
                    //The user specified a default interval to follow:
                    set udg_Spell__Time = udg_Spell_i_Time[udg_Spell_i_Head[i]]
                else
                    //Set the spell time to the minimum.
                    set udg_Spell__Time = udg_Spell__Interval
                endif
            else
                //Otherwise, set it to what it was before.
                set udg_Spell__Time = udg_Spell_i_LastTime[i]
            endif
        //else, the user is specifying a new time for the spell.
        endif
        set udg_Spell_i_LastTime[i] = udg_Spell__Time //Whatever the case, remember this time for next time.
        if b then
            //The duration was just assigned
            set udg_Spell_i_Duration[i] = d
        else
            //The duration has been ongoing
            set udg_Spell_i_Duration[i] = d - udg_Spell__Time
        endif
    endif
    return udg_Spell__Time
endfunction
//===========================================================================
// Runs every Spell__Interval seconds and handles all of the timed events.
//
function SpellTimerLoop takes nothing returns nothing
    local integer i = udg_Spell_i_StackN
    local integer node
    local real time
    set udg_Spell__Running = true
   
    //Run stack top to bottom to avoid skipping slots when destroying.
    loop
        set i = i - 1
        exitwhen i < 0
        set node = udg_Spell_i_Stack[i]
        set time = udg_Spell_i_Time[node] - udg_Spell__Interval
        if time <= 0.00 then
            set time = SpellTriggerExecute(node, udg_Spell_i_OnLoopStack[node])
        endif
        if time <= 0.00 then
            call SpellIndexDestroy(node)
        else
            set udg_Spell_i_Time[node] = time
        endif
    endloop
    set udg_Spell__Running = false
endfunction
//===========================================================================
// This is the meat of the system as it handles the event responses.
//
function RunSpellEvent takes nothing returns boolean
    local boolean b
    local integer aid = GetSpellAbilityId()
    local integer head = LoadInteger(udg_Spell__Hash, 0, aid)
    local integer i
    local integer id
    local trigger t
    local playerunitevent eid
    if head == 0 then
        //Nothing for this ability has been registered. Skip the sequence.
        return false
    endif
    set eid = ConvertPlayerUnitEvent(GetHandleId(GetTriggerEventId()))
    set udg_Spell__Caster = GetTriggerUnit()
    set id = GetHandleId(udg_Spell__Caster)
    set i = LoadInteger(udg_Spell__Hash, aid, id)
    if i == 0 then
        //This block will almost always happen with the OnChannel event. In the
        //case of Charge Gold and Lumber, only an OnEffect event will run.
        set i = udg_Spell_i_Recycle
        if i == 0 then
            //Create a new, unique index
            set i = udg_Spell_i_Instances + 1
            set udg_Spell_i_Instances = i
        else
            //Repurpose an existing one
            set udg_Spell_i_Recycle = udg_Spell_i_RecycleList[i]
        endif
        //call BJDebugMsg("Creating index: " + I2S(i))
        set udg_Spell_i_RecycleList[i] = -1
        set udg_Spell_i_StackRef[i] = -1
        set udg_Spell_i_Head[i] = head
       
        if eid == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
            set udg_Spell_i_Channeling[i] = true
            call SaveInteger(udg_Spell__Hash, aid, id, i)
            set t = udg_Spell_i_OnChannelStack[head]
        else //eid == EVENT_PLAYER_UNIT_SPELL_EFFECT
            set t = udg_Spell_i_OnEffectStack[head]
        endif
        set udg_Spell_i_Caster[i] = udg_Spell__Caster
        set udg_Spell_i_Level[i] = GetUnitAbilityLevel(udg_Spell__Caster, aid)
        set udg_Spell_i_Target[i] = GetSpellTargetUnit()
        set udg_Spell_i_TargetX[i] = GetSpellTargetX()
        set udg_Spell_i_TargetY[i] = GetSpellTargetY()
       
        set udg_Spell_i_OnLoopStack[i] = udg_Spell_i_OnLoopStack[head]
        if udg_Spell_i_UseTG[head] then
            //Get a recycled unit group or create a new one.
            set id = udg_Spell_i_GroupN - 1
            if id >= 0 then
                set udg_Spell_i_GroupN = id
                set udg_Spell_i_TargetGroup[i] = udg_Spell_i_GroupStack[id]
            else
                set udg_Spell_i_TargetGroup[i] = CreateGroup()
            endif
        endif
    elseif eid == EVENT_PLAYER_UNIT_SPELL_CAST then
        set t = udg_Spell_i_OnCastStack[head]
    elseif eid == EVENT_PLAYER_UNIT_SPELL_EFFECT then
        set t = udg_Spell_i_OnEffectStack[head]
    elseif eid == EVENT_PLAYER_UNIT_SPELL_FINISH then
        set udg_Spell_i_Completed[i] = true
        return true
    else //eid == EVENT_PLAYER_UNIT_SPELL_ENDCAST
        set udg_Spell_i_Channeling[i] = false
        call RemoveSavedInteger(udg_Spell__Hash, aid, id)
        set t = udg_Spell_i_OnFinishStack[head]
    endif
    if SpellTriggerExecute(i, t) > 0.00 then
        //Set the spell time to the user-specified one.
        set udg_Spell_i_Time[i] = udg_Spell__Time
        if udg_Spell_i_StackRef[i] < 0 then
            //Allocate the spell index onto the loop stack.
            set aid = udg_Spell_i_StackN
            set udg_Spell_i_Stack[aid] = i
            set udg_Spell_i_StackRef[i] = aid
            set udg_Spell_i_StackN = aid + 1
            if aid == 0 then
                //If this is the first spell index using the timer, start it up:
                call TimerStart(udg_Spell_i_Timer, udg_Spell__Interval, true, function SpellTimerLoop)
            endif
        endif
    elseif (not udg_Spell_i_Channeling[i]) and (t != null or udg_Spell_i_Time[i] <= 0.00) then
        call SpellIndexDestroy(i)
    endif
    set t = null
    return true
endfunction
//This function is invoked if an event was launched recursively by another event's callback.
function RunPreSpellEvent takes nothing returns nothing
    local integer i = udg_Spell__Index
    local real time = udg_Spell__Time
    local real d = udg_Spell__Duration
    local boolean expired = udg_Spell__Expired
    if udg_Spell__Trigger_OnLoop != null then
        set udg_Spell_i_OnLoopStack[i] = udg_Spell__Trigger_OnLoop
    endif
    if RunSpellEvent() then
        set udg_Spell__Time = time
        set udg_Spell__Duration = d
        set udg_Spell__Expired = expired
        call SpellIndexGetVars(i)
    endif
endfunction
//===========================================================================
// Base function of the system: runs when an ability event does something.
//
function SpellSystemEvent takes nothing returns boolean
    if udg_Spell__Running then
        call RunPreSpellEvent()
    else
        set udg_Spell__Running = true
        call RunSpellEvent()
        set udg_Spell__Running = false
    endif
    return false
endfunction
//===========================================================================
// Set Spell__Ability to your spell's ability
// Set Spell__Trigger_OnChannel/Cast/Effect/Finish/Loop to any trigger(s) you
// want to automatically run.
//
// GUI-friendly: Run Spell System <gen> (ignoring conditions)
//
function SpellSystemRegister takes nothing returns nothing
    local integer aid = udg_Spell__Ability
    local integer head = udg_Spell_i_Instances + 1
   
    if HaveSavedInteger(udg_Spell__Hash, 0, aid) or aid == 0 then
        //The system rejects duplicate or unassigned abilities.
        return
    endif
    set udg_Spell_i_Instances = head
    set udg_Spell_i_Abil[head] = aid
   
    //Preload the ability on dummy unit to help prevent first-instance lag
    call UnitAddAbility(udg_Spell_i_PreloadDummy, aid)
   
    //Save head index to the spell ability so it be referenced later.
    call SaveInteger(udg_Spell__Hash, 0, aid, head)
   
    //Set any applicable event triggers.
    set udg_Spell_i_OnChannelStack[head]= udg_Spell__Trigger_OnChannel
    set udg_Spell_i_OnCastStack[head]   = udg_Spell__Trigger_OnCast
    set udg_Spell_i_OnEffectStack[head] = udg_Spell__Trigger_OnEffect
    set udg_Spell_i_OnFinishStack[head] = udg_Spell__Trigger_OnFinish
    set udg_Spell_i_OnLoopStack[head]   = udg_Spell__Trigger_OnLoop
    set udg_Spell_i_InRangeFilter[head] = udg_Spell__Trigger_InRangeFilter
   
    //Set any customized filter variables:
    call SpellSetFilters(head)
   
    //Tell the system to automatically create target groups, if needed
    set udg_Spell_i_AutoAddTargets[head] = udg_Spell__AutoAddTargets
    set udg_Spell_i_UseTG[head] = udg_Spell__UseTargetGroup or udg_Spell__AutoAddTargets
   
    //Handle automatic buff assignment
    set udg_Spell_i_BuffAbil[head] = udg_Spell__BuffAbility
    set udg_Spell_i_BuffOrder[head] = udg_Spell__BuffOrder
   
    //Set the default time sequences if a duration is used:
    set udg_Spell_i_Time[head]     = udg_Spell__Time
    set udg_Spell_i_Duration[head] = udg_Spell__Duration
    set udg_Spell_i_LastTime[head] = udg_Spell__DurationPerLevel
   
    //Set variables back to their defaults:
    set udg_Spell__Trigger_OnChannel    = null
    set udg_Spell__Trigger_OnCast       = null
    set udg_Spell__Trigger_OnEffect     = null
    set udg_Spell__Trigger_OnFinish     = null
    set udg_Spell__Trigger_OnLoop       = null
    set udg_Spell__Trigger_InRangeFilter= null
    set udg_Spell__AutoAddTargets       = false
    set udg_Spell__UseTargetGroup       = false
    set udg_Spell__Time                 = 0.00
    set udg_Spell__Duration             = 0.00
    set udg_Spell__DurationPerLevel     = 0.00
    set udg_Spell__BuffAbility          = 0
    set udg_Spell__BuffOrder            = 0
   
    set udg_Spell__Filter_AllowEnemy        = udg_Spell_i_AllowEnemy[0]
    set udg_Spell__Filter_AllowAlly         = udg_Spell_i_AllowAlly[0]
    set udg_Spell__Filter_AllowDead         = udg_Spell_i_AllowDead[0]
    set udg_Spell__Filter_AllowMagicImmune  = udg_Spell_i_AllowMagicImmune[0]
    set udg_Spell__Filter_AllowMechanical   = udg_Spell_i_AllowMechanical[0]
    set udg_Spell__Filter_AllowStructure    = udg_Spell_i_AllowStructure[0]
    set udg_Spell__Filter_AllowFlying       = udg_Spell_i_AllowFlying[0]
    set udg_Spell__Filter_AllowHero         = udg_Spell_i_AllowHero[0]
    set udg_Spell__Filter_AllowNonHero      = udg_Spell_i_AllowNonHero[0]
    set udg_Spell__Filter_AllowLiving       = udg_Spell_i_AllowLiving[0]
endfunction
function SpellFilterCompare takes boolean is, boolean yes, boolean no returns boolean
    return (is and yes) or ((not is) and no)
endfunction
//===========================================================================
// Before calling this function, set Spell__InRangePoint to whatever point
// you need, THEN set Spell__InRange to the radius you need. The system will
// enumerate the units matching the configured filter and fill them into
// Spell_InRangeGroup.
//
function SpellGroupUnitsInRange takes nothing returns boolean
    local integer i = udg_Spell_i_Head[udg_Spell__Index]
    local integer j = 0
    local unit u
    local real padding = 64.00
    if udg_Spell_i_AllowStructure[i] then
        //A normal unit can only have up to size 64.00 collision, but if the
        //user needs to check for structures we need a padding big enough for
        //the "fattest" ones: Tier 3 town halls.
        set padding = 197.00
    endif
    call GroupEnumUnitsInRangeOfLoc(udg_Spell__InRangeGroup, udg_Spell__InRangePoint, udg_Spell__InRange + padding, null)
    loop
        set u = FirstOfGroup(udg_Spell__InRangeGroup)
        exitwhen u == null
        call GroupRemoveUnit(udg_Spell__InRangeGroup, u)
        loop
            exitwhen udg_Spell_i_AutoAddTargets[i] and IsUnitInGroup(u, udg_Spell__TargetGroup)
            exitwhen not IsUnitInRangeLoc(u, udg_Spell__InRangePoint, udg_Spell__InRange)
            exitwhen not SpellFilterCompare(IsUnitType(u, UNIT_TYPE_DEAD), udg_Spell_i_AllowDead[i], udg_Spell_i_AllowLiving[i])
            exitwhen not SpellFilterCompare(IsUnitAlly(u, udg_Spell__CasterOwner), udg_Spell_i_AllowAlly[i], udg_Spell_i_AllowEnemy[i])
            exitwhen not SpellFilterCompare(IsUnitType(u, UNIT_TYPE_HERO) or IsUnitType(u, UNIT_TYPE_RESISTANT), udg_Spell_i_AllowHero[i], udg_Spell_i_AllowNonHero[i])
            exitwhen IsUnitType(u, UNIT_TYPE_STRUCTURE) and not udg_Spell_i_AllowStructure[i]
            exitwhen IsUnitType(u, UNIT_TYPE_FLYING) and not udg_Spell_i_AllowFlying[i]
            exitwhen IsUnitType(u, UNIT_TYPE_MECHANICAL) and not udg_Spell_i_AllowMechanical[i]
            exitwhen IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and not udg_Spell_i_AllowMagicImmune[i]
            set udg_Spell__InRangeUnit = u
            //Run the user's designated filter, if one exists.
            exitwhen udg_Spell_i_InRangeFilter[i] != null and not TriggerEvaluate(udg_Spell_i_InRangeFilter[i])
            set j = j + 1
            set udg_Spell__InRangeUnits[j] = u
            exitwhen true
        endloop
    endloop
    if j > udg_Spell__InRangeMax and udg_Spell__InRangeMax > 0 then
        //The user has defined a maximum number of units allowed in the group.
        //Remove a random unit until the total does not exceed capacity.
        loop
            set i = GetRandomInt(1, j)
            set udg_Spell__InRangeUnits[i] = udg_Spell__InRangeUnits[j]
            set j = j - 1
            exitwhen j == udg_Spell__InRangeMax
        endloop
    endif
    set udg_Spell__InRangeCount = j
    set udg_Spell__InRangeMax = 0
    set udg_Spell__InRange = 0.00
    set i = udg_Spell_i_Head[udg_Spell__Index]
    loop
        exitwhen j == 0
        set u = udg_Spell__InRangeUnits[j]
        call GroupAddUnit(udg_Spell__InRangeGroup, u)
        if udg_Spell_i_AutoAddTargets[i] then
            call GroupAddUnit(udg_Spell__TargetGroup, u)
        endif
        if udg_Spell__WakeTargets and UnitIsSleeping(u) then
            call UnitWakeUp(u)
        endif
        if udg_Spell_i_BuffAbil[i] != 0 and udg_Spell_i_BuffOrder[i] != 0 then
            //Auto-buff units added to group:
            call UnitAddAbility(udg_Spell_i_PreloadDummy, udg_Spell_i_BuffAbil[i])
            call IssueTargetOrderById(udg_Spell_i_PreloadDummy, udg_Spell_i_BuffOrder[i], u)
            call UnitRemoveAbility(udg_Spell_i_PreloadDummy, udg_Spell_i_BuffAbil[i])
        endif
        set j = j - 1
    endloop
    set u = null
    return false
endfunction
function SpellPreloadEnd takes nothing returns nothing
    local integer i = udg_Spell_i_Instances
    loop
        exitwhen i == 0
        //Remove preloaded abilities so they don't interfere with orders
        call UnitRemoveAbility(udg_Spell_i_PreloadDummy, udg_Spell_i_Abil[udg_Spell_i_Head[i]])
        set i = i - 1
    endloop
endfunction
//===========================================================================
function InitTrig_Spell_System takes nothing returns nothing
    local integer i = bj_MAX_PLAYER_SLOTS
    local player p
    local trigger t
   
    if gg_trg_Spell_System != null then
        //A JASS function call already initialized the system.
        return
    endif
   
    //This runs before map init events so the hashtable is ready before then.
    set udg_Spell__Hash = InitHashtable()
   
    //Initialize these two locations which will never get removed
    set udg_Spell__CastPoint = Location(0, 0)
    set udg_Spell__TargetPoint = Location(0, 0)
   
    //Recycle existing unit groups into the recycle stack to avoid needing to destroy any extras.
    set udg_Spell_i_GroupStack[2] = udg_Spell__TargetGroup
    set udg_Spell_i_GroupStack[3] = udg_Spell_i_TargetGroup[0]
    set udg_Spell_i_GroupStack[4] = udg_Spell_i_TargetGroup[1]
    set udg_Spell_i_GroupN = 5 //There are already five valid unit groups thanks to Variable Editor.
   
    set t = CreateTrigger()
    call TriggerRegisterVariableEvent(t, "udg_Spell__InRange", GREATER_THAN, 0.00)
    call TriggerAddCondition(t, Filter(function SpellGroupUnitsInRange))
   
    set t = CreateTrigger()
    call TriggerAddCondition(t, Filter(function SpellSystemEvent))
    loop
        set i = i - 1
        set p = Player(i)
        call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_CHANNEL, null)
        call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_CAST, null)
        call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
        call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_FINISH, null)
        call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
        exitwhen i == 0
    endloop
    set p = null
    set t = null
   
    //Run the configuration trigger so its variables are ready before the
    //map initialization events run.
    call TriggerExecute(gg_trg_Spell_System_Config)
    call SpellSetFilters(0)
   
    //Create this trigger so it's GUI-friendly.
    set gg_trg_Spell_System = CreateTrigger()
    call TriggerAddAction(gg_trg_Spell_System, function SpellSystemRegister)
    set gg_trg_Spell_System_Config = gg_trg_Spell_System //In case the user accidentally picks this one
   
    //Create a dummy unit for preloading abilities and casting buffs.
    set udg_Spell_i_PreloadDummy = CreateUnit(udg_Spell__DummyOwner, udg_Spell__DummyType, 0, 0, 0)
   
    //Start the timer to remove its abilities:
    call TimerStart(udg_Spell_i_Timer, 0.00, false, function SpellPreloadEnd)
    call UnitRemoveAbility(udg_Spell_i_PreloadDummy, 'Amov') //Force it to never move to cast spells
endfunction


  • SS Holy Light Demo Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set Spell__Ability = Holy Light
      • -------- When holy light is cast on a unit, heal it each second for 4/6/8 seconds. --------
      • Set Spell__Time = 1.00
      • Set Spell__Duration = 2.00
      • Set Spell__DurationPerLevel = 2.00
      • -------- --------
      • Set Spell__Trigger_OnEffect = SS Holy Light Demo Effect <gen>
      • Set Spell__Trigger_OnLoop = SS Holy Light Demo Loop <gen>
      • Trigger - Run Spell System <gen> (ignoring conditions)
  • SS Holy Light Demo Effect
    • Events
    • Conditions
    • Actions
      • Set Spell__StartDuration = True
      • Special Effect - Create a special effect attached to the origin of Spell__Target using Abilities\Spells\NightElf\Rejuvenation\RejuvenationTarget.mdl
      • Set HolyLightBuff[Spell__Index] = (Last created special effect)
  • SS Holy Light Demo Loop
    • Events
    • Conditions
    • Actions
      • -------- Heals for 10/15/20 each second --------
      • Unit - Set life of Spell__Target to ((Life of Spell__Target) + (5.00 + (5.00 x Spell__LevelMultiplier)))
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • -------- When the heal-over-time has ended, destroy the rejuvenation effect --------
          • Special Effect - Destroy HolyLightBuff[Spell__Index]
        • Else - Actions



JASS:
// This function is run every 1.00 for each active divine shield
function SSDivineShieldDemoLoop takes nothing returns nothing
    local integer i = 1
    set udg_Spell__InRangePoint = udg_Spell__CastPoint
    set udg_Spell__InRange = 600.00
    //Spell__CastPoint will always be the Caster's own location, even if he moves from where he originally casted
    loop
        exitwhen i > udg_Spell__InRangeCount
        call IssuePointOrderLoc(udg_Spell__InRangeUnits[i], "move", udg_Spell__CastPoint)
        set i = i + 1
    endloop
endfunction

// The next function will be run when the effect of Divine Shield starts.
function SSDivineShieldDemoEffect takes nothing returns nothing
    //Set the time until next expiration
    set udg_Spell__Time = 1.00
    //Set how long the buff will last
    set udg_Spell__Duration = 15.00*udg_Spell__Level
endfunction

//===========================================================================
function InitTrig_SS_Divine_Shield_Demo takes nothing returns nothing
    //Use of this spell requires you to copy the SpellSystem_Register function from the
    //map header or use the vJass library which contains it.
    call SpellSystem_Register('AHds', function SSDivineShieldDemoEffect, function SSDivineShieldDemoLoop)
endfunction


Keywords:
Spell, system, gui, anitarf, vexorian, jesus4lyf, timer32, spellevent, nestharus, struct
Contents

GUI Spell System (Map)

Reviews
Approved on the 11th Jan 2016 Last review update on the 4th Apr 2016 This resource has been approved by THW moderators BPower and IcemanBo. In consultation with the spell moderation team we agreed on a rating of 6/5, Director's Cut. Criticism...
Thanks a lot @Bribe !

EDIT :
That doesn't seem to solve the problem. My problem is InRange having 0 value despite me setting them to Radius value (both are checked with text to see their values). A test using hardcode value still returns 0 as opposed to the hardcoded value.

EDIT 2 :
To be clear, I'm referring to Spell__InRange (Real)

EDIT 3 :
Now it makes sense, just noticed after reading the script for the system.
Problem solved. Sorry for the trouble.
 
Last edited:
Level 3
Joined
Jan 23, 2014
Messages
32
Hello @Bribe! i really like your Spell System! but i have 2 problems...

1) Why every time i edit the triggers, does the trigger "Spell System (JASS)" create leaks?
I have to delete it from my map, save the map, open your map, copy and paste it back on my map so that i do not believe leaks!!! it happens to me so much that i had to create a trigger that would "bridge" with Spell System using the action "Trigger run" to not be reactivating it spell per spell...

2) How do i make the DPS not accumulate?
I am using a custom passive Poison, and the DPS is accumulated for each attack, then 4 attacks = 7 + 7 + 7 + 7... and i want 4 attacks to do only = 7.

If you help me, i will be eternally grateful!

Sorry for my bad english!
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
1) No leaks there. Please describe what you mean by "leaks" as it doesn't make sense so far.

2) Are you using Damage Engine to catch the times the unit takes damage, and then applying the bonus damage?

ie. what are your amounts you want each extra attack to add to the unit's damage taken...

1 attack, 7? Or 4 attack, 7? This one is really not clear.
 
Level 3
Joined
Jan 23, 2014
Messages
32
OK, thanks for answering

1) When i start the game, everything starts to slow down! it's like a trigger with an "Event - Every time" but without destroying points and groups, do you understand me? But with Spell System that is JASS(Should not have leaks)...
Process: Then have to delete it(My Spell System trigger)>Save map>Open Spell System map>Copy/paste Spell System trigger to my map so that the game runs without leaks!
But if i modify any trigger, they return leaks and i have to do it again the same process! it's strange, i know but it happens to me!

2) I want it to do only 7 damage per second, no matter how many attacks i make! as if it were the Poison Ability (Default) but in triggers, because when i attack, it creates a dummy that cast Poison Ability(Spell System) and if i attack again, create another dummy that creates another different Poison Ability(Spell System) causing it to do 7 damage for each trigger and not per buff...

Example:
1st attack = poisoned enemy (7 DPS)
2nd attack = already poisoned enemy (7 and 7 DPS)
3rd attack = already poisoned enemy (7, 7 and 7 DPS)
4th attack = already poisoned enemy (7, 7, 7 and 7 DPS)

I want:
1st attack = poisoned enemy (7 DPS)
2nd attack = already poisoned enemy (7 DPS)
3rd attack = already poisoned enemy (7 DPS)
4th attack = already poisoned enemy (7 DPS)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Not unless someone wants to make a functional cooldown system (using 1.29 natives).

I have a neglected, but mostly-complete version of it which does integrate a cooldown event. The issue it faces is that it has an issue with the timer right now, and I have a feeling that Reforged is going to introduce StarCraft 2 style "actor" behavior where you can build spells without triggers.

Lastly, if I update the resource now, it's going to require everyone to be on 1.31 - which may mean a separate map like I do with Damage Engine.
 
Hey Bribe, I found that in a channeling spell, after the channeling ends (Spell__Trigger_OnFinish is run), the looping trigger (Spell__Trigger_OnLoop) still keeps looping until the duration in Spell__Trigger_OnEffect times out. Is this normal behavior or do I have to set Spell__Duration = 0 in every Spell__Trigger_OnFinish trigger for the channeling?
I'm using patch 1.31.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Hey Bribe, I found that in a channeling spell, after the channeling ends (Spell__Trigger_OnFinish is run), the looping trigger (Spell__Trigger_OnLoop) still keeps looping until the duration in Spell__Trigger_OnEffect times out. Is this normal behavior or do I have to set Spell__Duration = 0 in every Spell__Trigger_OnFinish trigger for the channeling?
I'm using patch 1.31.
The OnFinish event is meant to run whenever the spell is actually done channeling. If you are using an OnLoop trigger, whatever the original duration you set it to would keep going if it exceeds the actual channel duration.

I designed the system to allow for it, but yes if you want it to stop the loop as soon as channeling ends you'll need to set the Spell__Time to 0.00.

Hoping with Reforged that systems like this one will no longer be desirable.
 
Can you identify what's wrong in this trigger? I made a spell (Furion with stag's ultimate) in the test map where he begins channeling to transfer life every second to an ally. But even when Furion reaches under minimum health (LifeShared) and triggers the finish phase, the health drain in the On loop trigger still continues and Furion kills himself. The test map is in patch 1.31.
  • Druid zeal cfg
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set Spell__Ability = ED - Zeal
      • Set Spell__Trigger_OnEffect = Druid zeal fx <gen>
      • Set Spell__Trigger_OnFinish = Druid zeal end <gen>
      • Set Spell__Trigger_OnLoop = Druid zeal loop <gen>
      • Trigger - Run Spell System <gen> (ignoring conditions)
  • Druid zeal fx
    • Events
    • Conditions
    • Actions
      • Custom script: if GetUnitState(udg_Spell__Caster,UNIT_STATE_LIFE) > (udg_Spell__Level+1)*16 then
      • Set Spell__Duration = (10.00 x Spell__LevelMultiplier)
      • Set Spell__Time = Spell__Interval
      • Set A_DruidZealDamage[Spell__Index] = (((Base Damage of Spell__Caster for weapon index 0) + 5) / 2)
      • Unit - Add ED - Negative Damage Self to Spell__Caster
      • Unit - Set Base Damage of Spell__Target to ((Base Damage of Spell__Target for weapon index 0) + A_DruidZealDamage[Spell__Index]) for weapon index: 0
      • Set A_DruidZealArmor[Spell__Index] = ((Armor of Spell__Caster) / 2.00)
      • Unit - Set Armor of Spell__Caster to ((Armor of Spell__Caster) - A_DruidZealArmor[Spell__Index])
      • Unit - Set Armor of Spell__Target to ((Armor of Spell__Target) + A_DruidZealArmor[Spell__Index])
      • Custom script: set udg_A_DruidZealFX[udg_Spell__Index] = AddSpecialEffectTarget("Abilities\\Spells\\Human\\InnerFire\\InnerFireTarget.mdl",udg_Spell__Target,"overhead")
      • Custom script: endif
  • Druid zeal loop
    • Events
    • Conditions
    • Actions
      • Custom script: local real LifeShared = (udg_Spell__LevelMultiplier+1)/2
      • Custom script: if GetUnitState(udg_Spell__Caster,UNIT_STATE_LIFE) <= LifeShared then
      • Unit - Order Spell__Caster to Stop
      • Custom script: else
      • Custom script: call SetWidgetLife(udg_Spell__Target,GetWidgetLife(udg_Spell__Target)+LifeShared)
      • Custom script: call SetWidgetLife(udg_Spell__Caster,GetWidgetLife(udg_Spell__Caster)-LifeShared)
      • Custom script: endif
  • Druid zeal end
    • Events
    • Conditions
    • Actions
      • Set Spell__Duration = 0.00
      • Unit - Remove ED - Negative Damage Self from Spell__Caster
      • Unit - Set Base Damage of Spell__Target to ((Base Damage of Spell__Target for weapon index 0) - A_DruidZealDamage[Spell__Index]) for weapon index: 0
      • Unit - Set Armor of Spell__Caster to ((Armor of Spell__Caster) + A_DruidZealArmor[Spell__Index])
      • Unit - Set Armor of Spell__Target to ((Armor of Spell__Target) - A_DruidZealArmor[Spell__Index])
      • Unit - Remove Zeal Damage Loss buff from Spell__Caster
      • Custom script: call DestroyEffect(udg_A_DruidZealFX[udg_Spell__Index])
 

Attachments

  • My project 2.w3x
    3.3 MB · Views: 18
Unfortunately, the situation didn't change (so the problem didn't lie in Spell__Duration or Spell__Time), but I think I found the cause of the problem anyway: The health drain can pass the minimum health check yet kill Furion at the same time if after the drain he would have less than 0.41 HP (HP threshold for unit death). So setting the minimum health check so that after the drain, Furion must have at least 1 HP should fix it.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
Glad I've checked this resource of yours Bribe, this is very useful and deserves DC. It has the vast of majority features a GUI user would benefit. The system already handles the tiresome indexing, deindexing, and filtering methods which is a great time saver. It also comes with a hashtable which is handy for more complex data structure. It already provides you the global variables that are commonly found in every spells which will reduce you tons of work in variable setups.

However, there's a weird behavior on your big dipper spell where one of the dummy missiles float but instantly goes down and up. I don't know if there's something wrong with the transitions, intervals or one of the systems fault... so I'm quiet concerned about this.

Overall, I'm convinced to use this and might use it for my future submissions as I also recommend using it. I just really hope you respond regarding that weird behavior.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Glad I've checked this resource of yours Bribe, this is very useful and deserves DC. It has the vast of majority features a GUI user would benefit. The system already handles the tiresome indexing, deindexing, and filtering methods which is a great time saver. It also comes with a hashtable which is handy for more complex data structure. It already provides you the global variables that are commonly found in every spells which will reduce you tons of work in variable setups.

However, there's a weird behavior on your big dipper spell where one of the dummy missiles float but instantly goes down and up. I don't know if there's something wrong with the transitions, intervals or one of the systems fault... so I'm quiet concerned about this.

Overall, I'm convinced to use this and might use it for my future submissions as I also recommend using it. I just really hope you respond regarding that weird behavior.
Thanks for the feedback! Unfortunately, I am not seeing anything in the code that would make one of those dummy units refuse to move. I'll have to experiment and see what the cause could be.
 
Level 20
Joined
Aug 13, 2013
Messages
1,696
Thanks for the feedback! Unfortunately, I am not seeing anything in the code that would make one of those dummy units refuse to move. I'll have to experiment and see what the cause could be.

The strange behavior was caused by KB2D, not this system by the way.
One unit acts strangely because the KnockbackOnDestroy influences its height, referring to a unit variable that's been overwritten?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
The strange behavior was caused by KB2D, not this system by the way.
One unit acts strangely because the KnockbackOnDestroy influences its height, referring to a unit variable that's been overwritten?
That could be. I really don't like Knockback 2D as it is the most ugly and cumbersome of all my stuff. I'm pretty sure there is at least one better GUI Knockback resource by now.
 
Level 8
Joined
Feb 20, 2020
Messages
200
how can i make the spell damage to use the caster attribute??
and i need it to be DAMAGE not SET LIFE, because i want the unit that uses the spell take the credit for the kill.
I already tryed to set it : "unit cause spell_caster to damage spell_target dealling spell_caster(atribute x 1.00) damage of chaos type and normal"
but it's not working...
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
how can i make the spell damage to use the caster attribute??
and i need it to be DAMAGE not SET LIFE, because i want the unit that uses the spell take the credit for the kill.
I already tryed to set it : "unit cause spell_caster to damage spell_target dealling spell_caster(atribute x 1.00) damage of chaos type and normal"
but it's not working...
Please share the trigger you're using.

That approach should work, provided that the target isn't ethereal.
 
Level 8
Joined
Feb 20, 2020
Messages
200
Here is the first one i have tryed

  • Burn config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Spell__Ability = |cffff0000Flames|r [|cffffff00W|r]
      • -------- When holy light is cast on a unit, heal it each second for 4/6/8 seconds. --------
      • Set VariableSet Spell__Time = 1.00
      • Set VariableSet Spell__Duration = 3.00
      • Set VariableSet Spell__DurationPerLevel = 2.00
      • -------- --------
      • Set VariableSet Spell__Trigger_OnEffect = Burn Effect <gen>
      • Set VariableSet Spell__Trigger_OnLoop = Burn Loop <gen>
      • Trigger - Run Spell System <gen> (ignoring conditions)
  • Burn Effect
    • Events
    • Conditions
    • Actions
      • Set VariableSet Spell__StartDuration = True
      • Special Effect - Create a special effect attached to the chest of Spell__Target using Abilities\Spells\Other\BreathOfFire\BreathOfFireDamage.mdl
      • Set VariableSet BurnBuff[Spell__Index] = (Last created special effect)
  • Burn Loop
    • Events
    • Conditions
    • Actions
      • -------- Heals for 10/15/20 each second --------
      • Unit - Cause Spell__Caster to damage Spell__Target, dealing (3.00 + (0.20 x (Real((Intelligence of Spell__Caster (Include bonuses)))))) damage of attack type Spells and damage type Normal
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • -------- When the heal-over-time has ended, destroy the rejuvenation effect --------
          • Special Effect - Destroy HolyLightBuff[Spell__Index]
        • Else - Actions
And here the second one i have tryed

  • Blizzard Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Set the ability from Object Editor which represents the spell --------
      • Set VariableSet Spell__Ability = Blizzard
      • -------- Set the duration at least as high as the maximum duration in Object Editor. It will stop as soon as the caster is done, anyway. --------
      • Set VariableSet Spell__Duration = 5.00
      • -------- The Spell__Time is the time between waves. --------
      • Set VariableSet Spell__Time = 0.90
      • -------- Configure the types of units to be damaged by Blizzard --------
      • Set VariableSet Spell__Filter_AllowAlly = False
      • Set VariableSet Spell__Filter_AllowFlying = True
      • Set VariableSet Spell__Filter_AllowMechanical = True
      • Set VariableSet Spell__Filter_AllowStructure = True
      • -------- Set the trigger to run when Blizzard is cast --------
      • Set VariableSet Spell__Trigger_OnCast = Blizzard Start <gen>
      • -------- Set the trigger to run each interval: --------
      • Set VariableSet Spell__Trigger_OnLoop = Blizzard Wave <gen>
      • -------- Lock in the settings with Spell System by running the system trigger: --------
      • Trigger - Run Spell System <gen> (ignoring conditions)
      • -------- --------
      • -------- Configure the AoE of Blizzard and the number & type of effects: --------
      • Set VariableSet Blizzard_AoE = 300.00
      • Set VariableSet Blizzard_Shards[1] = 4
      • Set VariableSet Blizzard_FX = Abilities\Spells\Human\Blizzard\BlizzardTarget.mdl
  • Blizzard Start
    • Events
    • Conditions
    • Actions
      • -------- Tell the system to run the wave trigger each Spell__Time until the caster is done channeling the ability. --------
      • Set VariableSet Spell__StartDuration = True
      • -------- Create shards in advance so they hit the ground at the same time the damage is dealt --------
      • Trigger - Run Blizzard Shards <gen> (ignoring conditions)
  • Blizzard Wave
    • Events
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Channeling Equal to False
        • Then - Actions
          • -------- Stop future triggered effects if the caster is no longer channeling the ability --------
          • Set VariableSet Spell__Duration = 0.00
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Spell__Duration Greater than 0.00
            • Then - Actions
              • -------- Only create new shards if the spell will still continue for at least one more wave --------
              • Trigger - Run Blizzard Shards <gen> (ignoring conditions)
            • Else - Actions
      • -------- Pick all units within designated range of the target point --------
      • Set VariableSet Spell__InRangePoint = Spell__TargetPoint
      • Set VariableSet Spell__InRange = Blizzard_AoE
      • Unit Group - Pick every unit in Spell__InRangeGroup and do (Actions)
        • Loop - Actions
          • Unit - Cause Spell__Caster to damage (Picked unit), dealing (8.00 + (0.35 x (Real((Intelligence of Spell__Caster (Include bonuses)))))) damage of attack type Chaos and damage type Magic
  • Blizzard Shards
    • Events
    • Conditions
    • Actions
      • -------- Create shards at random points in the AoE --------
      • For each (Integer A) from 1 to Blizzard_Shards[Spell__Level], do (Actions)
        • Loop - Actions
          • Set VariableSet TempPoint = (Spell__TargetPoint offset by (Random real number between 0.00 and Blizzard_AoE) towards (Random angle) degrees.)
          • Special Effect - Create a special effect at TempPoint using Blizzard_FX
          • Special Effect - Destroy (Last created special effect)
          • Custom script: call RemoveLocation(udg_TempPoint)
 
Level 1
Joined
Jun 18, 2020
Messages
3
Hello,
I'm using the spell system but one of my spells appears to break the system, sometimes causing other spells' loops to rapidly fire, with varying intervals depending on different factors (usually approx. 20-40 times a second), and sometimes fires those spells' loops as if they were completing their duration ... I was wondering what in this trigger could be causing this break. I'll post the relevant triggers below:

Additionally, the area of effect damage does not fire for this spell in particular. It's base ability is Stormbolt. Not sure if these are related.

  • Zygan Thunderstep Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Spell__Ability = Thunderstep
      • -------- --------
      • Set VariableSet Spell__Trigger_OnEffect = Zygan Thunderstep Effect <gen>
      • Set VariableSet Spell__Trigger_OnLoop = Zygan Thunderstep Loop <gen>
      • -------- --------
      • Set VariableSet Spell__Time = 1.00
      • Set VariableSet Spell__Duration = 8.00
      • -------- --------
      • Set VariableSet Spell__Filter_AllowEnemy = True
      • Set VariableSet Spell__Filter_AllowLiving = True
      • Set VariableSet Spell__Filter_AllowHero = True
      • Set VariableSet Spell__Filter_AllowNonHero = True
      • Set VariableSet Spell__Filter_AllowAlly = False
      • Set VariableSet Spell__Filter_AllowDead = False
      • Set VariableSet Spell__Filter_AllowFlying = False
      • Set VariableSet Spell__Filter_AllowMechanical = True
      • Set VariableSet Spell__Filter_AllowStructure = True
      • Set VariableSet Spell__Filter_AllowMagicImmune = False
      • -------- --------
      • Trigger - Run Spell System <gen> (ignoring conditions)

  • Zygan Thunderstep Effect
    • Events
    • Conditions
    • Actions
      • Set VariableSet Spell__StartDuration = True
      • Set VariableSet Thunder_Fork_Empowered = 1
      • Set VariableSet Zyg_Stats[1] = (Real((Unit: Spell__Caster's Integer Field: Agility (with Bonus) ('uagb'))))
      • Set VariableSet Thunder_Fork_Multiplier = (1.00 + (((0.01 x 0.30) x Zyg_Stats[1]) + 0.25))
      • Set VariableSet Zyg_Thunderstep_Location = ((Position of Spell__Target) offset by 125.00 towards (Facing of Spell__Caster) degrees.)
      • Set VariableSet Zyg_Thunderstep_Location_2 = (Position of Spell__Caster)
      • Special Effect - Create a special effect attached to the origin of Spell__Target using Abilities\Spells\Human\Thunderclap\ThunderClapCaster.mdl
      • Special Effect - Set Scale of (Last created special effect) to 1.20
      • Special Effect - Destroy (Last created special effect)
      • Special Effect - Create a special effect attached to the origin of Spell__Caster using Abilities\Spells\Human\Thunderclap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Lightning - Create a Chain Lightning - Primary lightning effect from source Zyg_Thunderstep_Location_2 to target Zyg_Thunderstep_Location
      • Set VariableSet Zyg_Thunderstep_Lightning = (Last created lightning effect)
      • Unit - Move Spell__Caster instantly to Zyg_Thunderstep_Location, facing (Facing of Spell__Target) degrees
      • Unit - Cause Spell__Caster to damage Spell__Target, dealing (50.00 + (Spell__LevelMultiplier x (0.80 x Zyg_Stats[1]))) damage of attack type Spells and damage type Lightning
      • Unit - Set Unit: Spell__Target's Real Field: Defense ('udfc') to Value: ((Armor of Spell__Target) - Spell__LevelMultiplier)
      • Unit - Pause Spell__Target
      • Set VariableSet Spell__InRangePoint = (Zyg_Thunderstep_Location offset by 125.00 towards (Facing of Spell__Caster) degrees.)
      • Set VariableSet Spell__InRange = 250.00
      • Unit Group - Add all units of Spell__InRangeGroup to Spell__TargetGroup
      • Unit Group - Pick every unit in Spell__TargetGroup and do (Actions)
        • Loop - Actions
          • Unit - Cause Spell__Caster to damage (Picked unit), dealing (((0.50 x Spell__LevelMultiplier) x Zyg_Stats[1]) + 25.00) damage of attack type Spells and damage type Lightning
      • Custom script: call RemoveLocation (udg_Zyg_Thunderstep_Location)
      • Custom script: call RemoveLocation (udg_Zyg_Thunderstep_Location_2)
      • Wait 0.35 game-time seconds
      • Unit - Unpause Spell__Target
      • Lightning - Destroy Zyg_Thunderstep_Lightning

  • Zygan Thunderstep Loop
    • Events
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • Set VariableSet Thunder_Fork_Empowered = 0
          • Set VariableSet Thunder_Fork_Multiplier = 1.00
        • Else - Actions

Edit: Further testing suggests that
  • Unit - Move Spell__Caster instantly to Zyg_Thunderstep_Location, facing (Facing of Spell__Target) degrees
in the effect trigger causes the main problem. Why? And what would be the solution?
 
Last edited:
Level 20
Joined
Aug 13, 2013
Messages
1,696
And what would be the solution?
I didn't comprehend your script entirely as you already pointed out the main problem,
if you believe that's the culprit then you can subtitute that to SetUnitX/Y functions. (which takes real coordinates)

However, to consider the level of your scripting, just replace it with these instead;
  • Custom script: call SetUnitX( udg_Spell__Caster , GetLocationX(udg_Zyg_Thunderstep_Location) )
  • Custom script: call SetUnitY( udg_Spell__Caster , GetLocationY(udg_Zyg_Thunderstep_Location) )
Then just set the facing of unit via GUI function: 'Unit - Set Facing To' (or something)

If the problem persists, it might be something else. (which would require me to analyze your script)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Hello,
I'm using the spell system but one of my spells appears to break the system, sometimes causing other spells' loops to rapidly fire, with varying intervals depending on different factors (usually approx. 20-40 times a second), and sometimes fires those spells' loops as if they were completing their duration ... I was wondering what in this trigger could be causing this break. I'll post the relevant triggers below:

Additionally, the area of effect damage does not fire for this spell in particular. It's base ability is Stormbolt. Not sure if these are related.

  • Zygan Thunderstep Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Spell__Ability = Thunderstep
      • -------- --------
      • Set VariableSet Spell__Trigger_OnEffect = Zygan Thunderstep Effect <gen>
      • Set VariableSet Spell__Trigger_OnLoop = Zygan Thunderstep Loop <gen>
      • -------- --------
      • Set VariableSet Spell__Time = 1.00
      • Set VariableSet Spell__Duration = 8.00
      • -------- --------
      • Set VariableSet Spell__Filter_AllowEnemy = True
      • Set VariableSet Spell__Filter_AllowLiving = True
      • Set VariableSet Spell__Filter_AllowHero = True
      • Set VariableSet Spell__Filter_AllowNonHero = True
      • Set VariableSet Spell__Filter_AllowAlly = False
      • Set VariableSet Spell__Filter_AllowDead = False
      • Set VariableSet Spell__Filter_AllowFlying = False
      • Set VariableSet Spell__Filter_AllowMechanical = True
      • Set VariableSet Spell__Filter_AllowStructure = True
      • Set VariableSet Spell__Filter_AllowMagicImmune = False
      • -------- --------
      • Trigger - Run Spell System <gen> (ignoring conditions)

  • Zygan Thunderstep Effect
    • Events
    • Conditions
    • Actions
      • Set VariableSet Spell__StartDuration = True
      • Set VariableSet Thunder_Fork_Empowered = 1
      • Set VariableSet Zyg_Stats[1] = (Real((Unit: Spell__Caster's Integer Field: Agility (with Bonus) ('uagb'))))
      • Set VariableSet Thunder_Fork_Multiplier = (1.00 + (((0.01 x 0.30) x Zyg_Stats[1]) + 0.25))
      • Set VariableSet Zyg_Thunderstep_Location = ((Position of Spell__Target) offset by 125.00 towards (Facing of Spell__Caster) degrees.)
      • Set VariableSet Zyg_Thunderstep_Location_2 = (Position of Spell__Caster)
      • Special Effect - Create a special effect attached to the origin of Spell__Target using Abilities\Spells\Human\Thunderclap\ThunderClapCaster.mdl
      • Special Effect - Set Scale of (Last created special effect) to 1.20
      • Special Effect - Destroy (Last created special effect)
      • Special Effect - Create a special effect attached to the origin of Spell__Caster using Abilities\Spells\Human\Thunderclap\ThunderClapCaster.mdl
      • Special Effect - Destroy (Last created special effect)
      • Lightning - Create a Chain Lightning - Primary lightning effect from source Zyg_Thunderstep_Location_2 to target Zyg_Thunderstep_Location
      • Set VariableSet Zyg_Thunderstep_Lightning = (Last created lightning effect)
      • Unit - Move Spell__Caster instantly to Zyg_Thunderstep_Location, facing (Facing of Spell__Target) degrees
      • Unit - Cause Spell__Caster to damage Spell__Target, dealing (50.00 + (Spell__LevelMultiplier x (0.80 x Zyg_Stats[1]))) damage of attack type Spells and damage type Lightning
      • Unit - Set Unit: Spell__Target's Real Field: Defense ('udfc') to Value: ((Armor of Spell__Target) - Spell__LevelMultiplier)
      • Unit - Pause Spell__Target
      • Set VariableSet Spell__InRangePoint = (Zyg_Thunderstep_Location offset by 125.00 towards (Facing of Spell__Caster) degrees.)
      • Set VariableSet Spell__InRange = 250.00
      • Unit Group - Add all units of Spell__InRangeGroup to Spell__TargetGroup
      • Unit Group - Pick every unit in Spell__TargetGroup and do (Actions)
        • Loop - Actions
          • Unit - Cause Spell__Caster to damage (Picked unit), dealing (((0.50 x Spell__LevelMultiplier) x Zyg_Stats[1]) + 25.00) damage of attack type Spells and damage type Lightning
      • Custom script: call RemoveLocation (udg_Zyg_Thunderstep_Location)
      • Custom script: call RemoveLocation (udg_Zyg_Thunderstep_Location_2)
      • Wait 0.35 game-time seconds
      • Unit - Unpause Spell__Target
      • Lightning - Destroy Zyg_Thunderstep_Lightning

  • Zygan Thunderstep Loop
    • Events
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • Set VariableSet Thunder_Fork_Empowered = 0
          • Set VariableSet Thunder_Fork_Multiplier = 1.00
        • Else - Actions

Edit: Further testing suggests that
  • Unit - Move Spell__Caster instantly to Zyg_Thunderstep_Location, facing (Facing of Spell__Target) degrees
in the effect trigger causes the main problem. Why? And what would be the solution?

I see several problems:

You need to use indexing. This won't work with Waits unless you use local variables to store the necessities.

I'm not sure why you are using loops when you only do stuff after 8 seconds. I would suggest to just use Spell__Time.

Additionally, Zyg_Thunderstep_Lightning should be an array indexed to Spell__Index. This will make your spell fully MUI.

You can unify the spell timing by having one Spell__Time for 0.35 seconds and the next for 7.65.

You are leaking a position when you do the polar offset.

You don't need to use (Position of Caster/Target). Just use Spell__CastPoint/Target point which DO NOT leak. Therefore DO NOT remove them.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
How to utilize Spell__AutoAddTarget?

You would use it from your spell's Config trigger. If you check in the "Big Dipper Loop", I could have taken advantage of that feature instead of manually adding units to Spell__TargetGroup myself.

One of the things it does differently than just adding to the target group is that it only adds NEW units in range to the target group, so that when you pick the units in Spell__InRangeGroup, it only has the units that weren't already picked at an earlier point.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
You would use it from your spell's Config trigger. If you check in the "Big Dipper Loop", I could have taken advantage of that feature instead of manually adding units to Spell__TargetGroup myself.

One of the things it does differently than just adding to the target group is that it only adds NEW units in range to the target group, so that when you pick the units in Spell__InRangeGroup, it only has the units that weren't already picked at an earlier point.

Here is the first one i have tryed

  • Burn config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set VariableSet Spell__Ability = |cffff0000Flames|r [|cffffff00W|r]
      • -------- When holy light is cast on a unit, heal it each second for 4/6/8 seconds. --------
      • Set VariableSet Spell__Time = 1.00
      • Set VariableSet Spell__Duration = 3.00
      • Set VariableSet Spell__DurationPerLevel = 2.00
      • -------- --------
      • Set VariableSet Spell__Trigger_OnEffect = Burn Effect <gen>
      • Set VariableSet Spell__Trigger_OnLoop = Burn Loop <gen>
      • Trigger - Run Spell System <gen> (ignoring conditions)
  • Burn Effect
    • Events
    • Conditions
    • Actions
      • Set VariableSet Spell__StartDuration = True
      • Special Effect - Create a special effect attached to the chest of Spell__Target using Abilities\Spells\Other\BreathOfFire\BreathOfFireDamage.mdl
      • Set VariableSet BurnBuff[Spell__Index] = (Last created special effect)
  • Burn Loop
    • Events
    • Conditions
    • Actions
      • -------- Heals for 10/15/20 each second --------
      • Unit - Cause Spell__Caster to damage Spell__Target, dealing (3.00 + (0.20 x (Real((Intelligence of Spell__Caster (Include bonuses)))))) damage of attack type Spells and damage type Normal
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • -------- When the heal-over-time has ended, destroy the rejuvenation effect --------
          • Special Effect - Destroy HolyLightBuff[Spell__Index]
        • Else - Actions
And here the second one i have tryed

  • Blizzard Config
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Set the ability from Object Editor which represents the spell --------
      • Set VariableSet Spell__Ability = Blizzard
      • -------- Set the duration at least as high as the maximum duration in Object Editor. It will stop as soon as the caster is done, anyway. --------
      • Set VariableSet Spell__Duration = 5.00
      • -------- The Spell__Time is the time between waves. --------
      • Set VariableSet Spell__Time = 0.90
      • -------- Configure the types of units to be damaged by Blizzard --------
      • Set VariableSet Spell__Filter_AllowAlly = False
      • Set VariableSet Spell__Filter_AllowFlying = True
      • Set VariableSet Spell__Filter_AllowMechanical = True
      • Set VariableSet Spell__Filter_AllowStructure = True
      • -------- Set the trigger to run when Blizzard is cast --------
      • Set VariableSet Spell__Trigger_OnCast = Blizzard Start <gen>
      • -------- Set the trigger to run each interval: --------
      • Set VariableSet Spell__Trigger_OnLoop = Blizzard Wave <gen>
      • -------- Lock in the settings with Spell System by running the system trigger: --------
      • Trigger - Run Spell System <gen> (ignoring conditions)
      • -------- --------
      • -------- Configure the AoE of Blizzard and the number & type of effects: --------
      • Set VariableSet Blizzard_AoE = 300.00
      • Set VariableSet Blizzard_Shards[1] = 4
      • Set VariableSet Blizzard_FX = Abilities\Spells\Human\Blizzard\BlizzardTarget.mdl
  • Blizzard Start
    • Events
    • Conditions
    • Actions
      • -------- Tell the system to run the wave trigger each Spell__Time until the caster is done channeling the ability. --------
      • Set VariableSet Spell__StartDuration = True
      • -------- Create shards in advance so they hit the ground at the same time the damage is dealt --------
      • Trigger - Run Blizzard Shards <gen> (ignoring conditions)
  • Blizzard Wave
    • Events
    • Conditions
    • Actions
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Channeling Equal to False
        • Then - Actions
          • -------- Stop future triggered effects if the caster is no longer channeling the ability --------
          • Set VariableSet Spell__Duration = 0.00
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • Spell__Duration Greater than 0.00
            • Then - Actions
              • -------- Only create new shards if the spell will still continue for at least one more wave --------
              • Trigger - Run Blizzard Shards <gen> (ignoring conditions)
            • Else - Actions
      • -------- Pick all units within designated range of the target point --------
      • Set VariableSet Spell__InRangePoint = Spell__TargetPoint
      • Set VariableSet Spell__InRange = Blizzard_AoE
      • Unit Group - Pick every unit in Spell__InRangeGroup and do (Actions)
        • Loop - Actions
          • Unit - Cause Spell__Caster to damage (Picked unit), dealing (8.00 + (0.35 x (Real((Intelligence of Spell__Caster (Include bonuses)))))) damage of attack type Chaos and damage type Magic
  • Blizzard Shards
    • Events
    • Conditions
    • Actions
      • -------- Create shards at random points in the AoE --------
      • For each (Integer A) from 1 to Blizzard_Shards[Spell__Level], do (Actions)
        • Loop - Actions
          • Set VariableSet TempPoint = (Spell__TargetPoint offset by (Random real number between 0.00 and Blizzard_AoE) towards (Random angle) degrees.)
          • Special Effect - Create a special effect at TempPoint using Blizzard_FX
          • Special Effect - Destroy (Last created special effect)
          • Custom script: call RemoveLocation(udg_TempPoint)

Sorry I didn't see this until now. You need to use debug messages in order to isolate where the issue is. Your script looks fine. My guess is that your spell isn't being caught correctly by the system or the timer portion is expiring prematurely.
 
Level 12
Joined
May 16, 2020
Messages
660
Hi Bribe, I just found this system which you say is your favorite one (even more so than Damage Engine). This statement alone makes me consider your system, but I have some questions:

1) can you please explain in simple terms why this system exponentially improves performance? I understand it nulls the need to create your own global variables over and over again, but I know this is not the main reason - there must be a bigger gain.

2) since I started in Reforged I have like +200 spells... is it worth switching now after all this work...? I currently experience very little lag when the spell triggers fire, so I’m wondering if this system is mostly useful for people who struggle already with performance?
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Hi Bribe, I just found this system which you say is your favorite one (even more so than Damage Engine). This statement alone makes me consider your system, but I have some questions:

1) can you please explain in simple terms why this system exponentially improves performance? I understand it nulls the need to create your own global variables over and over again, but I know this is not the main reason - there must be a bigger gain.

2) since I started in Reforged I have like +200 spells... is it worth switching now after all this work...? I currently experience very little lag when the spell triggers fire, so I’m wondering if this system is mostly useful for people who struggle already with performance?

I would suggest waiting a bit for your spells because I will be creating an update for this system to make transitioning to it a bit easier.

Spell System was my favorite for a long time but Damage Engine has gotten really cool lately so I think I have a new favorite now.
 
Level 2
Joined
Jul 1, 2020
Messages
22
I would suggest waiting a bit for your spells because I will be creating an update for this system to make transitioning to it a bit easier.

Spell System was my favorite for a long time but Damage Engine has gotten really cool lately so I think I have a new favorite now.
Can you tell me how to increase the Perodic time on the Spells?

The units that moves thru triggers is moving kinda slow so i would like to increase the Perodic timer if possible..
 
Level 2
Joined
Jul 1, 2020
Messages
22
Then you're doing something else that's weird. I didn't think the interval would fix things up because it's pretty fast and smooth as-is.

Please post your trigger actions here so I can take a look.
2fb9aac4975443c40b73ccac709bbe6a.png



9d485a3f27402e58fee35ae926ed462b.png


i have Spell__Interval = 0.02 on the config and i noticed a bit different..
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Ok I can see that there are a number of things here that I should clarify:

1) Sorry I meant change Interval from the Spell System Config trigger. In your own Spell's Config trigger you'd only change the Spell__Time. But this is not the issue.

2) you're moving the unit 55.0 per interval and that is going to put that unit REALLY far away REALLY fast. You want to probably lower that to 5 or 10.

3) You have a lot of memory leaks there. When you set a location variable, you need to remove it before you set it to something else. In your trigger you have set GunnersArrowPoint twice in a row. You'll need to use a second variable there.

Aside from the four instances where you are leaking locations, you are also leaking a Group variable.

4. You are picking all units in the Arrow group twice. You need only do this once. Just move the actions from one into the other.

5. Your Custom Scripts to remove the locations need to be within the Unit Group actions.

6. How to easily post triggers
 
Level 2
Joined
Jul 1, 2020
Messages
22
Ok I can see that there are a number of things here that I should clarify:

1) Sorry I meant change Interval from the Spell System Config trigger. In your own Spell's Config trigger you'd only change the Spell__Time. But this is not the issue.

2) you're moving the unit 55.0 per interval and that is going to put that unit REALLY far away REALLY fast. You want to probably lower that to 5 or 10.

3) You have a lot of memory leaks there. When you set a location variable, you need to remove it before you set it to something else. In your trigger you have set GunnersArrowPoint twice in a row. You'll need to use a second variable there.

Aside from the four instances where you are leaking locations, you are also leaking a Group variable.

4. You are picking all units in the Arrow group twice. You need only do this once. Just move the actions from one into the other.

5. Your Custom Scripts to remove the locations need to be within the Unit Group actions.

6. How to easily post triggers
I'm almost positive that the Spell__Interval does nothing... no speed increase at all..
 
Level 2
Joined
Jul 1, 2020
Messages
22
Ok I can see that there are a number of things here that I should clarify:

1) Sorry I meant change Interval from the Spell System Config trigger. In your own Spell's Config trigger you'd only change the Spell__Time. But this is not the issue.

2) you're moving the unit 55.0 per interval and that is going to put that unit REALLY far away REALLY fast. You want to probably lower that to 5 or 10.

3) You have a lot of memory leaks there. When you set a location variable, you need to remove it before you set it to something else. In your trigger you have set GunnersArrowPoint twice in a row. You'll need to use a second variable there.

Aside from the four instances where you are leaking locations, you are also leaking a Group variable.

4. You are picking all units in the Arrow group twice. You need only do this once. Just move the actions from one into the other.

5. Your Custom Scripts to remove the locations need to be within the Unit Group actions.

6. How to easily post triggers
Also 55.0 offset is too little for this spell too.. i tried 5 and 10 and it didn't move 100 yards xd
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
Also 55.0 offset is too little for this spell too.. i tried 5 and 10 and it didn't move 100 yards xd
Please share the full triggers you are using via link I shared: How to easily post triggers

If Spell__Time is not filled in during the Config it will default to Spell__Interval. If you set Spell__Time elsewhere it may be getting stuck.
 
Level 2
Joined
Jul 1, 2020
Messages
22
Please share the full triggers you are using via link I shared: How to easily post triggers

If Spell__Time is not filled in during the Config it will default to Spell__Interval. If you set Spell__Time elsewhere it may be getting stuck.
  • BurningShotConfig
    • Events
      • Map initialization
    • Conditions
    • Actions
      • Set Spell__Ability = |cffff7d7dBurning Shot|r
      • Set Spell__Duration = 2.50
      • Set Spell__Time = 0.15
      • Set Spell__Trigger_OnEffect = BurningShotEffect <gen>
      • Set Spell__Trigger_OnLoop = BurningShotLoop <gen>
      • Trigger - Run Spell System <gen> (ignoring conditions)
  • BurningShotEffect
    • Events
    • Conditions
    • Actions
      • -------- Creation Trigger.. --------
      • Set Spell__StartDuration = True
      • Set GunnersArrowPoint = ((Position of Spell_i_Caster[Spell__Index]) offset by 60.00 towards (Facing of Spell_i_Caster[Spell__Index]) degrees)
      • Unit - Create 1 Burning Shot for Player 1 (Red) at GunnersArrowPoint facing (Facing of Spell_i_Caster[Spell__Index]) degrees
      • Set BurningShotUnit2[Spell__Index] = (Last created unit)
      • Unit - Add a 3.00 second Generic expiration timer to (Last created unit)
      • Custom script: call RemoveLocation(udg_GunnersArrowPoint)
  • BurningShotLoop
    • Events
    • Conditions
    • Actions
      • -------- This is the Loop Trigger, which moves it --------
      • Set BurningShotPoint2 = (Position of BurningShotUnit2[Spell__Index])
      • Set BurningShotPoint2 = (BurningShotPoint2 offset by 60.00 towards (Facing of BurningShotUnit2[Spell__Index]) degrees)
      • Unit - Move BurningShotUnit2[Spell__Index] instantly to BurningShotPoint2, facing (Facing of BurningShotUnit2[Spell__Index]) degrees
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
        • Else - Actions
It moves too slow perodic i don't know why.
 
Level 2
Joined
Jul 1, 2020
Messages
22
Remove this line: Set Spell__Time = 0.15

.15 is several times slower than the 1/32 (0.03125) timeout used by the system.
Removed it Still too slow [Same Speed as Before]

  • Spell System Config
    • Events
    • Conditions
    • Actions
      • -------- Only one dummy unit type is needed as you can attach an effect to it of any kind --------
      • -------- --------
      • Set Spell__DummyType = Dummy/Missile
      • Set Spell__DummyOwner = Neutral Extra
      • Set Spell__Interval = (1.00 / 32.00)
      • -------- --------
      • -------- Configure default values for the unit filter: --------
      • -------- --------
      • Set Spell__Filter_AllowEnemy = True
      • Set Spell__Filter_AllowLiving = True
      • Set Spell__Filter_AllowHero = True
      • Set Spell__Filter_AllowNonHero = True
      • Set Spell__Filter_AllowAlly = False
      • Set Spell__Filter_AllowDead = False
      • Set Spell__Filter_AllowFlying = False
      • Set Spell__Filter_AllowMechanical = False
      • Set Spell__Filter_AllowStructure = False
      • -------- --------
      • -------- Magic immunity is a great thing to block, as it also discludes invulnerable units from being picked --------
      • -------- --------
      • Set Spell__Filter_AllowMagicImmune = False
      • -------- --------
      • -------- Normal WC3 abilities, like Channel, wake sleeping creeps - even if they don't deal damage or apply buffs. --------
      • -------- Because of this, I provided an option to wake up creeps when they are enumerated by an InRange command. --------
      • -------- --------
      • Set Spell__WakeTargets = True
      • -------- --------
      • -------- Do not enable the following lines as they are variable declarations which make copying this system easier --------
      • -------- --------
      • Set Spell_i_Level[(Integer(Spell__LevelMultiplier))] = (Spell__Level + (Player number of Spell__CasterOwner))
      • Set Spell_i_TargetX[(Integer(Spell_i_TargetY[(Integer(Spell_i_Time[(Integer(Spell__Time))]))]))] = ((X of Spell__CastPoint) + (X of Spell__TargetPoint))
      • Set Spell__Caster = Spell_i_Caster[(Custom value of Spell_i_Target[(Custom value of Spell__Target)])]
      • Unit Group - Add all units of Spell_i_TargetGroup[0] to Spell__TargetGroup
      • Set Spell__Running = Spell__UseTargetGroup
      • Set Spell__Completed = Spell_i_UseTG[Spell_i_Level[(Integer((Elapsed time for Spell_i_Timer)))]]
      • Set Spell_i_Completed[0] = Spell_i_Linked[Spell_i_EventType[0]]
      • Set Spell__Trigger_OnLoop = Spell_i_OnLoopStack[(Load 0 of 0 from Spell__Hash)]
      • Set Spell__Trigger_OnFinish = Spell_i_OnFinishStack[Spell__Index]
      • Set Spell__Trigger_OnEffect = Spell_i_OnEffectStack[0]
      • Set Spell__Trigger_OnChannel = Spell_i_OnChannelStack[0]
      • Set Spell__Trigger_OnCast = Spell_i_OnCastStack[Spell_i_Recycle]
      • Set Spell_i_Instances = Spell_i_Head[Spell_i_RecycleList[Spell_i_Stack[Spell_i_StackRef[Spell_i_StackN]]]]
      • Set Spell_i_PreloadDummy = No unit
      • Set Spell__Ability = Spell_i_Abil[0]
      • Set Spell__InRangeGroup = (Units within Spell__InRange of Spell__InRangePoint)
      • Set Spell_i_AllowAlly[0] = Spell__Filter_AllowAlly
      • Set Spell_i_AllowDead[0] = Spell__Filter_AllowDead
      • Set Spell_i_AllowEnemy[0] = Spell__Filter_AllowEnemy
      • Set Spell_i_AllowFlying[0] = Spell__Filter_AllowFlying
      • Set Spell_i_AllowHero[0] = Spell__Filter_AllowHero
      • Set Spell_i_AllowMagicImmune[0] = Spell__Filter_AllowMagicImmune
      • Set Spell_i_AllowMechanical[0] = Spell__Filter_AllowMechanical
      • Set Spell_i_AllowNonHero[0] = Spell__Filter_AllowNonHero
      • Set Spell_i_AllowStructure[0] = Spell__Filter_AllowStructure
      • Set Spell_i_AllowLiving[0] = Spell__Filter_AllowLiving
      • Set Spell__Channeling = Spell_i_Channeling[0]
      • Set Spell_i_GroupN = (Number of units in Spell_i_GroupStack[0])
      • Set Spell__InRangeUnits[Spell__InRangeCount] = No unit
      • Set Spell__Duration = (Spell_i_Duration[(Integer(Spell__DurationPerLevel))] + Spell_i_LastTime[0])
      • Set Spell__Expired = Spell__StartDuration
      • Set Spell__InRangeMax = ((Supply used by Spell__InRangeUnit) + ((Evaluation count of Spell__Trigger_InRangeFilter) + (Evaluation count of Spell_i_InRangeFilter[0])))
      • Set Spell__AutoAddTargets = Spell_i_AutoAddTargets[0]
      • Set Spell__BuffOrder = Spell_i_BuffOrder[0]
      • Set Spell__BuffAbility = Spell_i_BuffAbil[0]
Here is my SpellCreator Config..
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
That i cannot do.. you got any solutions to that? or any possible debugs..
Not sure why you can't share your map privately, but for debugging what you can do is the following:

  • BurningShotLoop
    • Events
    • Conditions
    • Actions
      • -------- This is the Loop Trigger, which moves it --------
      • Set BurningShotPoint2 = (Position of BurningShotUnit2[Spell__Index])
      • Set BurningShotPoint2 = (BurningShotPoint2 offset by 60.00 towards (Facing of BurningShotUnit2[Spell__Index]) degrees)
      • Unit - Move BurningShotUnit2[Spell__Index] instantly to BurningShotPoint2, facing (Facing of BurningShotUnit2[Spell__Index]) degrees
      • Set MyInteger = MyInteger + 1
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Spell__Duration Less than or equal to 0.00
        • Then - Actions
          • Game - Display message to (All Players) the text (MyInteger)
        • Else - Actions
That would tell you how many times the loop actually ran. If it ran 64 times, you probably have an issue with framerate. If it ran less, then there is another trigger somewhere in your map messing things up.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,464
With the update to 1.32 and more efficient enumeration natives I think this system deserves a revision to make it more efficient.
Natives such as the BlzIsUnitSelectable() boolean can be very effective in filtering as well.

Sure, I am working on 1.5 projects right now related to Damage Engine and will queue this one up.
 
Top