• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Handles from trigger events

Status
Not open for further replies.
Level 5
Joined
Feb 17, 2011
Messages
95
In my code, spell I am using native TriggerRegisterUnitInRange takes trigger whichTrigger, unit whichUnit, real range, boolexpr filter returns event to add 5 events to the trigger on each cast. And after some testing i realized that number of handles is also increasing by 5.


I have only basic, limited knowledge about handles... So, is this considered normal, or as something that should be avoided/fixed? If anyone has some advice, or can steer me in the right direction, it would be much appreciated.

The only thing i can think of is destroying and recreating trigger, with which I am inexperienced.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
why not make a trigger for each spel lcast and add the events than remove the trigger when spell wears off.

yes its normal and yes it should be fixed
 
Level 5
Joined
Feb 17, 2011
Messages
95
What about showing your whole trigger.
If only you didn't have to ask every time: :)
JASS:
//===================================================================================================================================== 
//                                                              About
// Spirits v1.6
//  Created by bajaist
//  This spell: [url]http://www.hiveworkshop.com/forums/spells-569/spirits-v1-6-a-250320/[/url]  
//
//  For more resources visit:
//      [url]www.thehelper.net[/url]
//      [url]www.hiveworkshop.com[/url]
//      [url]www.wc3c.net[/url]
//
//
//
//      MUI: true
//      Language: JASS
//      Editor: vanilla Editor
//      Import difficulty: low
//      Spell type: no target 
//
//          Io summons 5 ancient Spirits over the course of 3 seconds. The Spirits dance around Io in a circle to protect him. 
// If an enemy hero moves close enough to touch a Spirit, the Spirit releases its life energy in a burst, 
// dealing damage to all enemies in a 300 area of effect. Non hero units only take minor damage upon touching a spirit 
// and do not cause them to explode.
// ===================================================================================================================================
//                                                              Importing
//  These settings are optional, user should set them to their liking. You can look at the provided map for all settings.
// --------------------------------------------------------
//  External requirements: none
// --------------------------------------------------------
//  Triggers: 
// Simple copy and paste. Remember to allow pasting of variables: 
// File/Preferences/Automatically create unknown variables while pasting trigger data (English version)
// If not already, set initial value for IS_maxIndex to -1.
// --------------------------------------------------------
//  Units: 
// A hero or heroes with Spirits ability.
// A Spirit unit:            
//      Scaling Value: 0.5          damageUnit[0] should be relative to scaling value        
//      Model File: Wisp
//      Sight Radius (Day): 0
//      Sight Radius (Night): 0
// --------------------------------------------------------
//  Abilities:
// This spell requires 3 dummy abilities: Spirits, Push Out and Pull In.
// If you don't want them to interrupt orders use Mana Shield, Wind Walk and Berserk, clear all unneeded values. For all 3: 
//      Duration: 0.01
// Or 3 spells based off of channel. 
// 
// ===================================================================================================================================

function SpiritsInitialization takes nothing returns nothing
    // This function is used to set/configure spell values    
    
    local integer l = 1                        // l; integer; counter, helper variable
    local integer rotation                     // rotation; integer; sets clockwise/anticlockwise rotation for Spirits
    local integer nLevels                      // levels; integer; number of spell levels
    local real ang                             // ang; real; angle added on every shift, for Spirits rotation
    
    // Note: changing any value too much may cause malfunctions                                                                             
    // ======================== Spell Configuration ================================================================================== 
    // ============== Stats ==============
    
    set udg_IS_nSpirits = 5                               // Set number of Spirits
    
    // Note: duration countdown timer always starts when Push Out and Pull In abilities are added
    set udg_IS_durTime = 19                               // Set duration time

    set udg_IS_perTime = 0.025                            // Periodic time for Spirits rotation 
    
    // Note: Use ctrl + D in Object Editor to see raw data 
    set udg_IS_abiUnitID[0] = 'A007'                      // Spirits ability
    set udg_IS_abiUnitID[1] = 'A006'                      // Push Out ability
    set udg_IS_abiUnitID[2] = 'A004'                      // Pull In ability
    set udg_IS_abiUnitID[3] = 'e000'                      // Spirit unit
    set udg_IS_abiUnitID[4] = 'Aloc'                      // Locust ability    
    
    // ============== Damage ==============
    
    set nLevels = 4                                       // Number of spell levels
    
    // Damage calculator per level
    loop
        exitwhen l > nLevels                      
        // ------------------------------
        set udg_IS_dmgUnit[l] = l * 6 + 2                 // damage to units
        set udg_IS_dmgHero[l] = l * 25                    // damage to heroes
        // ------------------------------
        set l = l + 1                                     // Note: for heroes and for end of spell, damage is AOE
    endloop                                 
    
    set udg_IS_dmgHero[0] = 300                           // Area of effect
    set udg_IS_dmgUnit[0] = 40                            // Detection radius of Spirit, Spirits Damage trigger event
    
    set udg_IS_dmgAttackType = ATTACK_TYPE_HERO           // Attack type
    set udg_IS_dmgType = DAMAGE_TYPE_MAGIC                // Damage type

    set udg_IS_dmgUnitType[1] = false                     // Damage to flying
    set udg_IS_dmgUnitType[2] = true                      // Damage to mechanical
    set udg_IS_dmgUnitType[3] = true                      // Damage to structure

    // ============== Visuals ==============

    // There are 2, dota style - Spirits are created at minimal oscillation distance
    // and customized - Spirits are created at position of caster and are sent one by one to minimal oscillation distance
    set udg_IS_creationStyle = true                       // Set to false for Dota styled creation of Spirits
     
    set udg_IS_distOscMin = 100                           // Minimal oscillation distance
    set udg_IS_distOscMax = 875                           // Maximal oscillation distance
    
    set udg_IS_distAng[1] = 8                             // Set "speed" of oscillation, distance traveled from/to caster, while pushing out/pulling in
    set udg_IS_distAng[3] = 0                             // Starting angle at which Spirits are created
                                                                        
    set rotation = 1                                      // Set to -1 for clockwise rotation
    
    set udg_IS_sfxAnim = "Stand Work"                                                  // Queued Spirits animation 
    set udg_IS_sfxModel = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"        // Effect played upon Spirits explosion
    
    set ang = 3                                           // Angle added on every shift, for Spirits rotation
    
    // ======================== End of Spell Configuration ===========================================================================
    
    set udg_IS_maxIndex = 0                                                     // Setting maxIndex from -1 to 0
    set udg_IS_distAng[0] = rotation * ang                                      // Anticlockwise/clockwise rotation, angle while rotated is increased by 3 
    
    set udg_IS_distAng[2] = udg_IS_distOscMin / (360 / udg_IS_nSpirits / ang)   // Distance traveled on each iteration for SpiritsCreate function (customized creation) 
    
    if udg_IS_creationStyle then                                                // If creation style is customized
        set udg_IS_durTime = udg_IS_durTime + udg_IS_perTime * (360 / ang)      // Extending duration time for customized creation of Spirits
    endif
    
    set udg_IS_spiritHash = InitHashtable()                                     // spiritHash; hashtable; stores spirits units
    set udg_IS_dmgGroup = CreateGroup()                                         // damageGroup; unit group; unit froup for AOE damage
    set udg_IS_perTimer = CreateTimer()                                         // perTimer; timer; periodic timer for Spirits rotation 
endfunction

// ----------------------------------------------------------
function SpiritsUnitTypeFilter takes unit filt returns boolean
    // This function is used to filter unit types for damage
    // filt; unit; filter unit
    
    if IsUnitType(filt, UNIT_TYPE_FLYING) then                                  // Damage to flying
        return udg_IS_dmgUnitType[1]
    elseif IsUnitType(filt, UNIT_TYPE_MECHANICAL) then                          // Damage to mechanical
        return udg_IS_dmgUnitType[2]   
    elseif IsUnitType(filt, UNIT_TYPE_STRUCTURE) then                           // Damage to structure
        return udg_IS_dmgUnitType[3]
    endif
    return  true
endfunction

// ----------------------------------------------------------
function SpiritsIsAliveFilter takes unit filt returns boolean
    // This function is used to filter alive units
    // filt; unit; filter unit

    return GetWidgetLife(filt) > 0.405 and not IsUnitType(filt, UNIT_TYPE_DEAD) and GetUnitTypeId(filt) != 0
endfunction

// ----------------------------------------------------------
function SpiritsUnitGroupFilter takes nothing returns boolean
    // This function is filter for damage group
    
    return SpiritsUnitTypeFilter(GetFilterUnit()) and SpiritsIsAliveFilter(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), udg_IS_ownPlayer)
endfunction

// ----------------------------------------------------------
function SpiritsDynamicIndex takes integer curIndex returns nothing
    // This function is used to change indexes
    // curIndex; integer; current index
    
    call FlushChildHashtable(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]))           // Clearing child hashtable

    call UnitRemoveAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                        // Removes Push Out and
    call UnitRemoveAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                        // Pull In abilities
    
    // =================== Max index to current index =======================================
    set udg_IS_durTimer[curIndex] = udg_IS_durTimer[udg_IS_maxIndex]                            // Duration timer
    set udg_IS_caster[curIndex] = udg_IS_caster[udg_IS_maxIndex]                                // Caster
    set udg_IS_angMove[curIndex] = udg_IS_angMove[udg_IS_maxIndex]                              // Angle of Spirits while moving
    set udg_IS_distMove[curIndex] = udg_IS_distMove[udg_IS_maxIndex]                            // Distance of Spirits while moving 
    set udg_IS_angCreate[curIndex] = udg_IS_angCreate[udg_IS_maxIndex]                          // Angle of Spirits while created
    set udg_IS_distCreate[curIndex] = udg_IS_distCreate[udg_IS_maxIndex]                        // Distance of Spirits while created
    set udg_IS_distAngC[curIndex] = udg_IS_distAngC[udg_IS_maxIndex]                            // Counts number of iterations
    set udg_IS_distOscPushOut[curIndex] = udg_IS_distOscPushOut[udg_IS_maxIndex]                // Push Out
    set udg_IS_distOscPullIn[curIndex] = udg_IS_distOscPullIn[udg_IS_maxIndex]                  // Pull In
    set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[udg_IS_maxIndex]              // Number of created Spirits 
    set udg_IS_nSpiritsCurrent[curIndex] = udg_IS_nSpiritsCurrent[udg_IS_maxIndex]              // Current number of Spirits  
        
    set udg_IS_maxIndex = udg_IS_maxIndex - 1                                                   // Lowering max index

    if udg_IS_maxIndex == 0 then                                                                // If no one is using the spell
        call PauseTimer(udg_IS_perTimer)                                                        // pause periodic timer
    endif

endfunction

// ----------------------------------------------------------
function SpiritsEndSpell takes integer curIndex returns nothing
    // This function is used to end spell if caster dies
    // or if spell is used before duration expire
    // or if duration expire
    // curIndex; integer; current index
    
    local integer c = 1          // c; integer; counter, helper variable  
    local unit fog               // fog; unit; first of group; used for damageGroup unit group
    local unit spirit            // spirit; unit; current Spirit, helper variable
 
    // =========== Reamaining Spirits burst, at the end of spell, releasing damage ================
    loop
        exitwhen c > udg_IS_nSpirits
        set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)   
        // If Spirit is alive
        if SpiritsIsAliveFilter(spirit) then
            // Picking units in group for aoe damage around each Spirit
            set udg_IS_ownPlayer = GetOwningPlayer(spirit)
            call GroupEnumUnitsInRange(udg_IS_dmgGroup, GetUnitX(spirit), GetUnitY(spirit), udg_IS_dmgHero[0], Filter(function SpiritsUnitGroupFilter))
            loop
                set fog = FirstOfGroup(udg_IS_dmgGroup)
                exitwhen fog == null
                // Damaging unit in group
                call UnitDamageTarget(udg_IS_caster[curIndex], fog, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
                call GroupRemoveUnit(udg_IS_dmgGroup, fog)
            endloop
            call DestroyEffect(AddSpecialEffect(udg_IS_sfxModel, GetUnitX(spirit), GetUnitY(spirit)))       // Adding special effect at position of spirit
            call RemoveUnit(spirit)                                                                         // Spirit explode at the end of spell
        endif
        set c = c + 1
    endloop    
    
    call SpiritsDynamicIndex(curIndex)          // Managing indexes 
    
    // fog is already nulled
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsCreate takes integer curIndex returns nothing
    // This function is used for customized creation of Spirits
    // Spirits are created at position of caster
    // and moved, over time, one by one to minimal oscillation distance
    // curIndex; integer; current index
    
    local unit spirit                                              // spirit; unit; created Spirit, helper variable

    if udg_IS_distCreate[curIndex] == 0 then                       // If angle is reset, Spirit reached minimal oscillation distance, and another is created
        // Creating Spirit for caster, at position of caster 
        set spirit = CreateUnit(GetOwningPlayer(udg_IS_caster[curIndex]), udg_IS_abiUnitID[3], GetUnitX(udg_IS_caster[curIndex]), GetUnitY(udg_IS_caster[curIndex]), 0) 
        call UnitAddAbility(spirit, udg_IS_abiUnitID[4])           // add Locust to Spirit
        call SaveUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex], spirit)
        call QueueUnitAnimation(spirit, udg_IS_sfxAnim)
    else
        set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex])
    endif
    
    // =================== Moving Spirit towards minimal oscillation distance ======================================================================          
    call SetUnitX(spirit, GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distCreate[curIndex] * Cos(udg_IS_angCreate[curIndex] * bj_DEGTORAD))
    call SetUnitY(spirit, GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distCreate[curIndex] * Sin(udg_IS_angCreate[curIndex] * bj_DEGTORAD))
    set udg_IS_distCreate[curIndex] = udg_IS_distCreate[curIndex] + udg_IS_distAng[2]              // Always increases distance
    set udg_IS_angCreate[curIndex] = udg_IS_angCreate[curIndex] + udg_IS_distAng[0]                // Always increases angle
        
    // =================== Checking if all Spirits are created or next Spirit should be ============================================================
    // If distance of Spirits while created is same as minimimal oscillation distance
    if udg_IS_distCreate[curIndex] == udg_IS_distOscMin then          
        // Adding "unit comes within event", for each Spirit
        call TriggerRegisterUnitInRange(gg_trg_Spirits, spirit, udg_IS_dmgUnit[0], null)                 
        set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[curIndex] + 1             // Counts current number of Spirits, from 1 - 5 since creation
        set udg_IS_angCreate[curIndex] = udg_IS_distAng[3]                                      // Resets angle of Spirits while created
        set udg_IS_distCreate[curIndex] = 0                                                     // Resets distance of Spirits while created
                                                                                                // Note: All 5 Spirits are meant to use the same path from caster to minimal oscillation distance        
        if udg_IS_nSpiritsCreated[curIndex] == udg_IS_nSpirits + 1 then                         // If all Spirits are created:
            call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                   // add Push Out to Caster
            call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                   // add Pull In to Caster                       
        endif
    endif
    
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsCreateDota takes integer curIndex returns nothing
    // This function is used to create Spirits as in Dota
    // Spirits are created one by one at minimal oscillation distance
    // curIndex; integer; current index
    
    local unit spirit                                                                        // spirit; unit; created Spirit, helper variable

    // Creating Spirit for caster, at position of caster
    // Spirit is immediately moved to minimal oscialtion distance by MoveSpirit function
    set spirit = CreateUnit(GetOwningPlayer(udg_IS_caster[curIndex]), udg_IS_abiUnitID[3], GetUnitX(udg_IS_caster[curIndex]), GetUnitY(udg_IS_caster[curIndex]), 0) 
    call UnitAddAbility(spirit, udg_IS_abiUnitID[4])               // add Locust to Spirit
    call SaveUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex], spirit)
    
    // Adding unit comes within event, for each Spirit, to Spirits Damage trigger
    call TriggerRegisterUnitInRange(gg_trg_Spirits, spirit, udg_IS_dmgUnit[0], null)     
    call QueueUnitAnimation(spirit, udg_IS_sfxAnim)
    
    
    if udg_IS_nSpiritsCreated[curIndex] == 1 then                                            // If all Spirits are created:
        call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                    // add Push Out to Caster
        call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                    // add Pull In to Caster                       
    endif
    set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[curIndex] + 1              // Increasing number of created spirits
    
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsMove takes nothing returns nothing
    // This function is used to move Spirits, 
    // check which creation style is used,
    // manage oscillation distance
    // and check if caster is dead or duration expired  
    
    local integer c                                                 // c; integer; counter, helper variable
    local integer curIndex = 1                                      // curIndex; integer; current index
    local unit spirit                                               // spirit; unit; current Spirit, helper variable
    local unit fog                                                  // fog; unit; first of group; used for damageGroup unit group
    local real x                                                    // x; real; new x coordinate, helper variable
    local real y                                                    // y; real; new y coordiante, helper variable
    local real a = 360 / udg_IS_nSpirits                            // a; real; angle

    // =================== 
    loop
        exitwhen curIndex > udg_IS_maxIndex
        // =================== Checking if duration time expired =====================================================
        if udg_IS_durTimer[curIndex] >= udg_IS_durTime or not SpiritsIsAliveFilter(udg_IS_caster[curIndex]) then
            call SpiritsEndSpell(curIndex)     
        else
            // =================== Checking if all Spirits are created ===================================
            if udg_IS_nSpiritsCreated[curIndex] < udg_IS_nSpirits + 1 then                     
                if udg_IS_creationStyle then                              
                    call SpiritsCreate(curIndex)                                                     // Create another Spirit 
                elseif  udg_IS_distAngC[udg_IS_maxIndex] >= (360 / udg_IS_nSpirits) then             // Reseting counter
                    set udg_IS_distAngC[udg_IS_maxIndex] = 0
                    call SpiritsCreateDota(curIndex)                                                 // Create another Spirit
                else
                    set udg_IS_distAngC[udg_IS_maxIndex] = udg_IS_distAngC[udg_IS_maxIndex] + RAbsBJ(udg_IS_distAng[0])   // Adding "ang degrees" to counter 
                endif
            endif
    
            // ================ Setting up oscialtion distance ===========================================
            if udg_IS_distMove[curIndex] < udg_IS_distOscMin + 1  then                               // If oscillation distance is minimal:            
                set udg_IS_distOscPullIn[curIndex] = false                                           // turn off Pull in    
                set udg_IS_distMove[curIndex] = udg_IS_distOscMin                                    // set oscillation distance to minimal oscillation distance
            elseif udg_IS_distMove[curIndex] > udg_IS_distOscMax - 1 then                            // If oscillation distance is maximal:          
                set udg_IS_distOscPushOut[curIndex] = false                                          // turn off Push Out    
                set udg_IS_distMove[curIndex] = udg_IS_distOscMax                                    // set oscillation distance to maximal oscillation distance
            elseif udg_IS_distOscPushOut[curIndex] then                                              // If Push Out is on:    
                set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] + udg_IS_distAng[1]        // Increase oscillation distance    
            elseif udg_IS_distOscPullIn[curIndex] then                                               // If Pull In is on:
                set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] - udg_IS_distAng[1]        // decrease oscillation distance
            endif
    
            set udg_IS_angMove[curIndex] = udg_IS_angMove[curIndex] + udg_IS_distAng[0]              // Always increase angle
            set udg_IS_distAngC[curIndex] = udg_IS_distAngC[curIndex] + 1                            // Iteration counter
            
            // ================ Moving Spirits ===========================================================
            set c = 1
            loop
                // current number of Spirits; transfering from create to move function
                exitwhen c == udg_IS_nSpiritsCreated[curIndex]
                // Loading spirits
                set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)
                if udg_IS_distAng[0] > 0 then
                    // anticlockwise, (1 - c) * a sets it to the last created Spirit
                    set x = GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Cos((udg_IS_angMove[curIndex] + (1 - c) * a) * bj_DEGTORAD)
                    set y = GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Sin((udg_IS_angMove[curIndex] + (1 - c) * a) * bj_DEGTORAD)
                else
                    // cloclwise, (c - 1) * a sets it to the last created Spirit
                    set x = GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Cos((udg_IS_angMove[curIndex] + (c - 1) * a) * bj_DEGTORAD)
                    set y = GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Sin((udg_IS_angMove[curIndex] + (c - 1) * a) * bj_DEGTORAD)
                endif
                call SetUnitX(spirit, x)                    // Moving Spirit to new coordinates
                call SetUnitY(spirit, y)
                set c = c + 1
            endloop
            
            set udg_IS_durTimer[curIndex] = udg_IS_durTimer[curIndex] + udg_IS_perTime        // Increasing duration time
            set curIndex = curIndex + 1                                                       // Increasing index
        endif    
    endloop
    
    // fog is already nulled
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsDamage takes integer curIndex, unit triggeredSpirit, unit triggerUnit returns nothing
    // This function is used to deal damage
    // triggeredSpirit; unit; triggered spirit
    // triggerUnit; unit; triggering unit
    // curIndex; integer; current index 
    
    local unit fog                          // fog; unit; first of group; used for damageGroup unit group
    
    // ================= Dealing damage ====================================================================================
    // If Spirit is alive
    if SpiritsIsAliveFilter(triggeredSpirit) then
        // If triggerting unit is alive, and an enemy
        if SpiritsIsAliveFilter(triggerUnit) and SpiritsUnitTypeFilter(triggerUnit) and IsUnitEnemy(triggerUnit, GetOwningPlayer(udg_IS_caster[curIndex])) then
            if IsUnitType(triggerUnit, UNIT_TYPE_HERO) then                                                     // If enemy unit is a hero
                set udg_IS_nSpiritsCurrent[curIndex] = udg_IS_nSpiritsCurrent[curIndex] - 1                     // Lowering number of Spirits                
                // Picking units in group for aoe damage around enemy hero
                set udg_IS_ownPlayer = GetOwningPlayer(triggeredSpirit)
                call GroupEnumUnitsInRange(udg_IS_dmgGroup, GetUnitX(triggerUnit), GetUnitY(triggerUnit), udg_IS_dmgHero[0], Filter(function SpiritsUnitGroupFilter))
                loop
                    set fog = FirstOfGroup(udg_IS_dmgGroup)
                    exitwhen fog == null
                    // Damaging unit in group
                    call UnitDamageTarget(udg_IS_caster[curIndex], fog, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
                    call GroupRemoveUnit(udg_IS_dmgGroup, fog)
                endloop
                call DestroyEffect(AddSpecialEffect(udg_IS_sfxModel, GetUnitX(triggeredSpirit), GetUnitY(triggeredSpirit)))          // Adding special effect at position of spirit
                call RemoveUnit(triggeredSpirit)                                                                                     // Spirit explode at the end of spell
            else
                // If enemy unit is not a hero
                call UnitDamageTarget(udg_IS_caster[curIndex], triggerUnit, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
            endif
        endif
    endif
    if udg_IS_nSpiritsCurrent[curIndex] == 0 then           // If there are no Spirits left
        call SpiritsDynamicIndex(curIndex)                  // Lowering index
    endif 
    
    // fog is already nulled
    set triggeredSpirit = null
    set triggerUnit = null
endfunction
                
// ----------------------------------------------------------
function SpiritsManage takes nothing returns nothing
    // This function is used to detect which ability is being used,
    // to set/reset spell values
    // to detect if unit is in range of spirit
    
    local integer c                                                          // c; integer; counter, helper variable
    local integer curIndex = 1                                               // curIndex; integer; counter, helper variable
    local unit spirit                                                        // spirit; unit; current Spirit, helper variable
    local unit triggerUnit = GetTriggerUnit()                                // triggerUnit; unit; helper variable  
    
    // =========== Initializating spell values, 0.01s event =======================================
    if udg_IS_maxIndex == -1 then                                            // maxIndex is used as a helper variable
        call SpiritsInitialization()                                         // setting spell values
    
    // =========== Ability being used is Spirits ==================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[0] then             
        loop 
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then
                call SpiritsEndSpell(curIndex)                                              // Ending spell if activated before duration expire 
            endif    
            set curIndex = curIndex + 1
        endloop
        
        set udg_IS_maxIndex = udg_IS_maxIndex + 1                                           // Incereasing max index
        
        // ========== Reseting spell values =======================================================                
        set udg_IS_caster[udg_IS_maxIndex] = triggerUnit                                    // caster
        set udg_IS_durTimer[udg_IS_maxIndex] = 0                                            // duration time
         
        set udg_IS_angMove[udg_IS_maxIndex] = udg_IS_distAng[3]                             // angle of Spirits while moving
        set udg_IS_distMove[udg_IS_maxIndex] = udg_IS_distOscMin                            // distance of Spirits while moving 
        set udg_IS_angCreate[udg_IS_maxIndex] = udg_IS_distAng[3]                           // angle of Spirits while created
        set udg_IS_distCreate[udg_IS_maxIndex] = 0                                          // distance of Spirits while created
        set udg_IS_distAngC[udg_IS_maxIndex] = (360 / udg_IS_nSpirits)                      // counts number of iterations
        set udg_IS_distOscPushOut[udg_IS_maxIndex] = false                                  // Push Out
        set udg_IS_distOscPullIn[udg_IS_maxIndex] = false                                   // Pull In
        set udg_IS_nSpiritsCreated[udg_IS_maxIndex] = 1                                     // Number of created Spirits 
        set udg_IS_nSpiritsCurrent[udg_IS_maxIndex] = udg_IS_nSpirits                       // Current number of Spirits

        if udg_IS_maxIndex == 1 then                                                        // If only one hero is using the spell and is first to start
            call TimerStart(udg_IS_perTimer, udg_IS_perTime, true, function SpiritsMove)    // turn on periodic timer for SpiritsMove function 
        endif
        
    // =========== Ability being used is Push Out =================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[1] then
        loop                                                                                         // Looping through units
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then
                if udg_IS_distMove[curIndex] == udg_IS_distOscMax then                               // If distance of oscillation is maximal:
                    set udg_IS_distOscPushOut[curIndex] = false                                      // turn off Push Out
                elseif udg_IS_distOscPushOut[curIndex] then                                          // Sets Spirits oscillation distance at current if
                    set udg_IS_distOscPushOut[curIndex] = false                                      // casted while pushing out 
                else
                    set udg_IS_distOscPullIn[curIndex] = false                                       // Turn off Pull In
                    set udg_IS_distOscPushOut[curIndex] = true                                       // Turn on Push Out
                    set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] + udg_IS_distAng[1]    // Incerease oscillation distance
                endif
            endif
            set curIndex = curIndex + 1
        endloop
    
    // ========= Ability being used is Pull In ====================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[2] then
        loop                                                                                         // Looping through units
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then        
                if udg_IS_distMove[curIndex] == udg_IS_distOscMin then                               // If distance of oscillation is minimal: 
                    set udg_IS_distOscPullIn[curIndex] = false                                       // turn off Pull In
                elseif udg_IS_distOscPullIn[curIndex] then                                           // Sets Spirits oscillation distance at current if
                    set udg_IS_distOscPullIn[curIndex] = false                                       // casted while pulling in
                else
                    set udg_IS_distOscPushOut[curIndex] = false                                      // Turn off Push Out
                    set udg_IS_distOscPullIn[curIndex] = true                                        // Turn on Pull In
                    set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] - udg_IS_distAng[1]    // Decrease oscillation distance
                endif
            endif
            set curIndex = curIndex + 1               
        endloop   
    // ================= Checking which Spirit enemy unit came in contact with =============================================
    elseif GetSpellAbilityId() == 0 then                                 
        loop                                                                    // Looping through units
            exitwhen curIndex > udg_IS_maxIndex
            set c = 1
            loop
                exitwhen c > udg_IS_nSpirits  
                // Loading spirits
                set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)
                // Checking if triggering unit is in range of each Spirit
                if IsUnitInRangeXY(triggerUnit,  GetUnitX(spirit), GetUnitY(spirit), udg_IS_dmgUnit[0] + 1) then
                    call SpiritsDamage(curIndex, spirit, triggerUnit)
                endif                                                                                 
                set c = c + 1 
            endloop
            set curIndex = curIndex + 1
        endloop
    endif    
    
    set triggerUnit = null
    set spirit = null    
endfunction

// ======================= Init Trigger Spirits Initialization =======================================================================
function InitTrig_Spirits takes nothing returns nothing
    set gg_trg_Spirits = CreateTrigger()
    
    call TriggerRegisterTimerEvent(gg_trg_Spirits, 0.01, false)
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Spirits, EVENT_PLAYER_UNIT_SPELL_EFFECT) 
    call TriggerAddCondition(gg_trg_Spirits, Condition(function SpiritsManage))
endfunction
 
Last edited:
Level 5
Joined
Feb 17, 2011
Messages
95
Bump, any ideas?
I don't expect anyone to go through the whole trigger, so here's a simplified version of what i thought would solve this:
JASS:
            //.....
function SpiritsDynamicIndex takes integer curIndex returns nothing
            //...  
    set udg_IS_maxIndex = udg_IS_maxIndex - 1          // Lowering max index
    
    if udg_IS_maxIndex == 0 then                       // If no one is using the spell
        call PauseTimer(udg_IS_perTimer)               // Pausing periodic timer
        
        call DestroyTrigger(udg_IS_trig)
        set udg_IS_trig = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(udg_IS_trig, EVENT_PLAYER_UNIT_SPELL_EFFECT) 
        // Can't add conditions
        call TriggerAddCondition(udg_IS_trig, Condition(function SpiritsManage))       
    endif

endfunction
            //.....
function SpiritsCreate takes integer curIndex returns nothing
            //...          
        call TriggerRegisterUnitInRange(udg_IS_trig, spirit, udg_IS_dmgUnit[0], null)                 
            //... 
endfunction
            //.....
function SpiritsManage takes nothing returns nothing
            //...
endfunction
            //.....
function InitTrig_Spirits takes nothing returns nothing
    set udg_IS_trig = CreateTrigger()
    
    call TriggerRegisterTimerEvent(udg_IS_trig, 0.01, false)   // This event doesn't need to be added on recreation
    
    call TriggerRegisterAnyUnitEventBJ(udg_IS_trig, EVENT_PLAYER_UNIT_SPELL_EFFECT) 
    call TriggerAddCondition(udg_IS_trig, Condition(function SpiritsManage))
endfunction

To sum it all up, i am struggling to find a way to clear unused events from trigger, which are stacking over time. 5 events are added on each spell cast and become useless after the duration expire.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
I don't like this leak clear stuff much, but what I heard from guys here is destroying a trigger also destroys events attached to it.Also I heard destroying trigger also destroy conditions so you don't need extra clear conditions.Hope I am not wrong :thumbs_up:

I think there might be something more to improve spell code, nvm now.

And since you are a vanilla jass coder I will try help you,

Add a trigger array variable with name SpiritCollisionTrigger

What we will try to do is, use a dynamic trigger(trigger created in-game) to detect wisp collisions.

JASS:
//===================================================================================================================================== 
//                                                              About
// Spirits v1.6
//  Created by bajaist
//  This spell: [url]http://www.hiveworkshop.com/forums/spells-569/spirits-v1-6-a-250320/[/url]  
//
//  For more resources visit:
//      [url]www.thehelper.net[/url]
//      [url]www.hiveworkshop.com[/url]
//      [url]www.wc3c.net[/url]
//
//
//
//      MUI: true
//      Language: JASS
//      Editor: vanilla Editor
//      Import difficulty: low
//      Spell type: no target 
//
//          Io summons 5 ancient Spirits over the course of 3 seconds. The Spirits dance around Io in a circle to protect him. 
// If an enemy hero moves close enough to touch a Spirit, the Spirit releases its life energy in a burst, 
// dealing damage to all enemies in a 300 area of effect. Non hero units only take minor damage upon touching a spirit 
// and do not cause them to explode.
// ===================================================================================================================================
//                                                              Importing
//  These settings are optional, user should set them to their liking. You can look at the provided map for all settings.
// --------------------------------------------------------
//  External requirements: none
// --------------------------------------------------------
//  Triggers: 
// Simple copy and paste. Remember to allow pasting of variables: 
// File/Preferences/Automatically create unknown variables while pasting trigger data (English version)
// If not already, set initial value for IS_maxIndex to -1.
// --------------------------------------------------------
//  Units: 
// A hero or heroes with Spirits ability.
// A Spirit unit:            
//      Scaling Value: 0.5          damageUnit[0] should be relative to scaling value        
//      Model File: Wisp
//      Sight Radius (Day): 0
//      Sight Radius (Night): 0
// --------------------------------------------------------
//  Abilities:
// This spell requires 3 dummy abilities: Spirits, Push Out and Pull In.
// If you don't want them to interrupt orders use Mana Shield, Wind Walk and Berserk, clear all unneeded values. For all 3: 
//      Duration: 0.01
// Or 3 spells based off of channel. 
// 
// ===================================================================================================================================

function SpiritsInitialization takes nothing returns nothing
    // This function is used to set/configure spell values    
    
    local integer l = 1                        // l; integer; counter, helper variable
    local integer rotation                     // rotation; integer; sets clockwise/anticlockwise rotation for Spirits
    local integer nLevels                      // levels; integer; number of spell levels
    local real ang                             // ang; real; angle added on every shift, for Spirits rotation
    
    // Note: changing any value too much may cause malfunctions                                                                             
    // ======================== Spell Configuration ================================================================================== 
    // ============== Stats ==============
    
    set udg_IS_nSpirits = 5                               // Set number of Spirits
    
    // Note: duration countdown timer always starts when Push Out and Pull In abilities are added
    set udg_IS_durTime = 19                               // Set duration time

    set udg_IS_perTime = 0.025                            // Periodic time for Spirits rotation 
    
    // Note: Use ctrl + D in Object Editor to see raw data 
    set udg_IS_abiUnitID[0] = 'A007'                      // Spirits ability
    set udg_IS_abiUnitID[1] = 'A006'                      // Push Out ability
    set udg_IS_abiUnitID[2] = 'A004'                      // Pull In ability
    set udg_IS_abiUnitID[3] = 'e000'                      // Spirit unit
    set udg_IS_abiUnitID[4] = 'Aloc'                      // Locust ability    
    
    // ============== Damage ==============
    
    set nLevels = 4                                       // Number of spell levels
    
    // Damage calculator per level
    loop
        exitwhen l > nLevels                      
        // ------------------------------
        set udg_IS_dmgUnit[l] = l * 6 + 2                 // damage to units
        set udg_IS_dmgHero[l] = l * 25                    // damage to heroes
        // ------------------------------
        set l = l + 1                                     // Note: for heroes and for end of spell, damage is AOE
    endloop                                 
    
    set udg_IS_dmgHero[0] = 300                           // Area of effect
    set udg_IS_dmgUnit[0] = 40                            // Detection radius of Spirit, Spirits Damage trigger event
    
    set udg_IS_dmgAttackType = ATTACK_TYPE_HERO           // Attack type
    set udg_IS_dmgType = DAMAGE_TYPE_MAGIC                // Damage type

    set udg_IS_dmgUnitType[1] = false                     // Damage to flying
    set udg_IS_dmgUnitType[2] = true                      // Damage to mechanical
    set udg_IS_dmgUnitType[3] = true                      // Damage to structure

    // ============== Visuals ==============

    // There are 2, dota style - Spirits are created at minimal oscillation distance
    // and customized - Spirits are created at position of caster and are sent one by one to minimal oscillation distance
    set udg_IS_creationStyle = true                       // Set to false for Dota styled creation of Spirits
     
    set udg_IS_distOscMin = 100                           // Minimal oscillation distance
    set udg_IS_distOscMax = 875                           // Maximal oscillation distance
    
    set udg_IS_distAng[1] = 8                             // Set "speed" of oscillation, distance traveled from/to caster, while pushing out/pulling in
    set udg_IS_distAng[3] = 0                             // Starting angle at which Spirits are created
                                                                        
    set rotation = 1                                      // Set to -1 for clockwise rotation
    
    set udg_IS_sfxAnim = "Stand Work"                                                  // Queued Spirits animation 
    set udg_IS_sfxModel = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"        // Effect played upon Spirits explosion
    
    set ang = 3                                           // Angle added on every shift, for Spirits rotation
    
    // ======================== End of Spell Configuration ===========================================================================
    
    set udg_IS_maxIndex = 0                                                     // Setting maxIndex from -1 to 0
    set udg_IS_distAng[0] = rotation * ang                                      // Anticlockwise/clockwise rotation, angle while rotated is increased by 3 
    
    set udg_IS_distAng[2] = udg_IS_distOscMin / (360 / udg_IS_nSpirits / ang)   // Distance traveled on each iteration for SpiritsCreate function (customized creation) 
    
    if udg_IS_creationStyle then                                                // If creation style is customized
        set udg_IS_durTime = udg_IS_durTime + udg_IS_perTime * (360 / ang)      // Extending duration time for customized creation of Spirits
    endif
    
    
    set udg_IS_spiritHash = InitHashtable()                                     // spiritHash; hashtable; stores spirits units
    set udg_IS_dmgGroup = CreateGroup()                                         // damageGroup; unit group; unit froup for AOE damage
    set udg_IS_perTimer = CreateTimer()                                         // perTimer; timer; periodic timer for Spirits rotation 
endfunction

// ----------------------------------------------------------
function SpiritsUnitTypeFilter takes unit filt returns boolean
    // This function is used to filter unit types for damage
    // filt; unit; filter unit
    
    if IsUnitType(filt, UNIT_TYPE_FLYING) then                                  // Damage to flying
        return udg_IS_dmgUnitType[1]
    elseif IsUnitType(filt, UNIT_TYPE_MECHANICAL) then                          // Damage to mechanical
        return udg_IS_dmgUnitType[2]   
    elseif IsUnitType(filt, UNIT_TYPE_STRUCTURE) then                           // Damage to structure
        return udg_IS_dmgUnitType[3]
    endif
    return  true
endfunction

// ----------------------------------------------------------
function SpiritsIsAliveFilter takes unit filt returns boolean
    // This function is used to filter alive units
    // filt; unit; filter unit

    return GetWidgetLife(filt) > 0.405 and not IsUnitType(filt, UNIT_TYPE_DEAD) and GetUnitTypeId(filt) != 0
endfunction

// ----------------------------------------------------------
function SpiritsUnitGroupFilter takes nothing returns boolean
    // This function is filter for damage group
    
    return SpiritsUnitTypeFilter(GetFilterUnit()) and SpiritsIsAliveFilter(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), udg_IS_ownPlayer)
endfunction

// ----------------------------------------------------------
function SpiritsDynamicIndex takes integer curIndex returns nothing
    // This function is used to change indexes
    // curIndex; integer; current index
    
    call FlushChildHashtable(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]))           // Clearing child hashtable
    
    // Clears both events and conditions of trigger, flush data inside trigger
    call FlushChildHashtable(udg_IS_spiritHash, GetHandleId(udg_SpiritCollisionTrigger[curIndex]))
    call DestroyTrigger(udg_SpiritCollisionTrigger[curIndex])
    set udg_SpiritCollisionTrigger[curIndex] = null

    call UnitRemoveAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                        // Removes Push Out and
    call UnitRemoveAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                        // Pull In abilities
    
    // =================== Max index to current index =======================================
    set udg_IS_durTimer[curIndex] = udg_IS_durTimer[udg_IS_maxIndex]                            // Duration timer
    set udg_IS_caster[curIndex] = udg_IS_caster[udg_IS_maxIndex]                                // Caster
    set udg_IS_angMove[curIndex] = udg_IS_angMove[udg_IS_maxIndex]                              // Angle of Spirits while moving
    set udg_IS_distMove[curIndex] = udg_IS_distMove[udg_IS_maxIndex]                            // Distance of Spirits while moving 
    set udg_IS_angCreate[curIndex] = udg_IS_angCreate[udg_IS_maxIndex]                          // Angle of Spirits while created
    set udg_IS_distCreate[curIndex] = udg_IS_distCreate[udg_IS_maxIndex]                        // Distance of Spirits while created
    set udg_IS_distAngC[curIndex] = udg_IS_distAngC[udg_IS_maxIndex]                            // Counts number of iterations
    set udg_IS_distOscPushOut[curIndex] = udg_IS_distOscPushOut[udg_IS_maxIndex]                // Push Out
    set udg_IS_distOscPullIn[curIndex] = udg_IS_distOscPullIn[udg_IS_maxIndex]                  // Pull In
    set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[udg_IS_maxIndex]              // Number of created Spirits 
    set udg_IS_nSpiritsCurrent[curIndex] = udg_IS_nSpiritsCurrent[udg_IS_maxIndex]              // Current number of Spirits  

    set udg_IS_maxIndex = udg_IS_maxIndex - 1                                                   // Lowering max index

    if udg_IS_maxIndex == 0 then                                                                // If no one is using the spell
        call PauseTimer(udg_IS_perTimer)                                                        // pause periodic timer
    endif

endfunction

// ----------------------------------------------------------
function SpiritsEndSpell takes integer curIndex returns nothing
    // This function is used to end spell if caster dies
    // or if spell is used before duration expire
    // or if duration expire
    // curIndex; integer; current index
    
    local integer c = 1          // c; integer; counter, helper variable  
    local unit fog               // fog; unit; first of group; used for damageGroup unit group
    local unit spirit            // spirit; unit; current Spirit, helper variable
 
    // =========== Reamaining Spirits burst, at the end of spell, releasing damage ================
    loop
        exitwhen c > udg_IS_nSpirits
        set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)   
        // If Spirit is alive
        if SpiritsIsAliveFilter(spirit) then
            // Picking units in group for aoe damage around each Spirit
            set udg_IS_ownPlayer = GetOwningPlayer(spirit)
            call GroupEnumUnitsInRange(udg_IS_dmgGroup, GetUnitX(spirit), GetUnitY(spirit), udg_IS_dmgHero[0], Filter(function SpiritsUnitGroupFilter))
            loop
                set fog = FirstOfGroup(udg_IS_dmgGroup)
                exitwhen fog == null
                // Damaging unit in group
                call UnitDamageTarget(udg_IS_caster[curIndex], fog, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
                call GroupRemoveUnit(udg_IS_dmgGroup, fog)
            endloop
            call DestroyEffect(AddSpecialEffect(udg_IS_sfxModel, GetUnitX(spirit), GetUnitY(spirit)))       // Adding special effect at position of spirit
            call RemoveUnit(spirit)                                                                         // Spirit explode at the end of spell
        endif
        set c = c + 1
    endloop    
    
    call SpiritsDynamicIndex(curIndex)          // Managing indexes 
    
    // fog is already nulled
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritCollision takes nothing returns boolean
    local integer curIndex = LoadInteger(udg_IS_spiritHash, GetHandleId(GetTriggeringTrigger()), 0)
    local unit spirit                                         // spirit; unit; current Spirit, helper variable
    local integer c = 1                                       // c; integer; counter, helper variable
    // ================= Checking which Spirit enemy unit came in contact with =============================================
    loop
        exitwhen c > udg_IS_nSpirits  
        // Loading spirits
        set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)
        // Checking if triggering unit is in range of each Spirit
        if IsUnitInRangeXY(triggerUnit,  GetUnitX(spirit), GetUnitY(spirit), udg_IS_dmgUnit[0] + 1) then
            call SpiritsDamage(curIndex, spirit, triggerUnit)
        endif                                                                                 
        set c = c + 1 
    endloop

    return false
endfunction

// ----------------------------------------------------------
function SpiritsCreate takes integer curIndex returns nothing
    // This function is used for customized creation of Spirits
    // Spirits are created at position of caster
    // and moved, over time, one by one to minimal oscillation distance
    // curIndex; integer; current index
    
    local unit spirit                                              // spirit; unit; created Spirit, helper variable

    if udg_IS_distCreate[curIndex] == 0 then                       // If angle is reset, Spirit reached minimal oscillation distance, and another is created
        // Creating Spirit for caster, at position of caster 
        set spirit = CreateUnit(GetOwningPlayer(udg_IS_caster[curIndex]), udg_IS_abiUnitID[3], GetUnitX(udg_IS_caster[curIndex]), GetUnitY(udg_IS_caster[curIndex]), 0) 
        call UnitAddAbility(spirit, udg_IS_abiUnitID[4])           // add Locust to Spirit
        call SaveUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex], spirit)
        call QueueUnitAnimation(spirit, udg_IS_sfxAnim)
    else
        set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex])
    endif
    
    // =================== Moving Spirit towards minimal oscillation distance ======================================================================          
    call SetUnitX(spirit, GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distCreate[curIndex] * Cos(udg_IS_angCreate[curIndex] * bj_DEGTORAD))
    call SetUnitY(spirit, GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distCreate[curIndex] * Sin(udg_IS_angCreate[curIndex] * bj_DEGTORAD))
    set udg_IS_distCreate[curIndex] = udg_IS_distCreate[curIndex] + udg_IS_distAng[2]              // Always increases distance
    set udg_IS_angCreate[curIndex] = udg_IS_angCreate[curIndex] + udg_IS_distAng[0]                // Always increases angle
        
    // =================== Checking if all Spirits are created or next Spirit should be ============================================================
    // If distance of Spirits while created is same as minimimal oscillation distance
    if udg_IS_distCreate[curIndex] == udg_IS_distOscMin then          
        // Adding "unit comes within event", for each Spirit
        call TriggerRegisterUnitInRange(udg_SpiritCollisionTrigger[curIndex], spirit, udg_IS_dmgUnit[0], null)   
                
        set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[curIndex] + 1             // Counts current number of Spirits, from 1 - 5 since creation
        set udg_IS_angCreate[curIndex] = udg_IS_distAng[3]                                      // Resets angle of Spirits while created
        set udg_IS_distCreate[curIndex] = 0                                                     // Resets distance of Spirits while created
                                                                                                // Note: All 5 Spirits are meant to use the same path from caster to minimal oscillation distance        
        if udg_IS_nSpiritsCreated[curIndex] == udg_IS_nSpirits + 1 then                         // If all Spirits are created:
            call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                   // add Push Out to Caster
            call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                   // add Pull In to Caster                       
        endif
    endif
    
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsCreateDota takes integer curIndex returns nothing
    // This function is used to create Spirits as in Dota
    // Spirits are created one by one at minimal oscillation distance
    // curIndex; integer; current index
    
    local unit spirit                                                                        // spirit; unit; created Spirit, helper variable

    // Creating Spirit for caster, at position of caster
    // Spirit is immediately moved to minimal oscialtion distance by MoveSpirit function
    set spirit = CreateUnit(GetOwningPlayer(udg_IS_caster[curIndex]), udg_IS_abiUnitID[3], GetUnitX(udg_IS_caster[curIndex]), GetUnitY(udg_IS_caster[curIndex]), 0) 
    call UnitAddAbility(spirit, udg_IS_abiUnitID[4])               // add Locust to Spirit
    call SaveUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), udg_IS_nSpiritsCreated[curIndex], spirit)
    
    // Adding unit comes within event, for each Spirit, to Spirits Damage trigger
    call TriggerRegisterUnitInRange(udg_SpiritCollisionTrigger[curIndex], spirit, udg_IS_dmgUnit[0], null)     
    
    call QueueUnitAnimation(spirit, udg_IS_sfxAnim)
    
    
    if udg_IS_nSpiritsCreated[curIndex] == 1 then                                            // If all Spirits are created:
        call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[1])                    // add Push Out to Caster
        call UnitAddAbility(udg_IS_caster[curIndex], udg_IS_abiUnitID[2])                    // add Pull In to Caster                       
    endif
    set udg_IS_nSpiritsCreated[curIndex] = udg_IS_nSpiritsCreated[curIndex] + 1              // Increasing number of created spirits
    
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsMove takes nothing returns nothing
    // This function is used to move Spirits, 
    // check which creation style is used,
    // manage oscillation distance
    // and check if caster is dead or duration expired  
    
    local integer c                                                 // c; integer; counter, helper variable
    local integer curIndex = 1                                      // curIndex; integer; current index
    local unit spirit                                               // spirit; unit; current Spirit, helper variable
    local unit fog                                                  // fog; unit; first of group; used for damageGroup unit group
    local real x                                                    // x; real; new x coordinate, helper variable
    local real y                                                    // y; real; new y coordiante, helper variable
    local real a = 360 / udg_IS_nSpirits                            // a; real; angle

    // =================== 
    loop
        exitwhen curIndex > udg_IS_maxIndex
        // =================== Checking if duration time expired =====================================================
        if udg_IS_durTimer[curIndex] >= udg_IS_durTime or not SpiritsIsAliveFilter(udg_IS_caster[curIndex]) then
            call SpiritsEndSpell(curIndex)     
        else
            // =================== Checking if all Spirits are created ===================================
            if udg_IS_nSpiritsCreated[curIndex] < udg_IS_nSpirits + 1 then                     
                if udg_IS_creationStyle then                              
                    call SpiritsCreate(curIndex)                                                     // Create another Spirit 
                elseif  udg_IS_distAngC[udg_IS_maxIndex] >= (360 / udg_IS_nSpirits) then             // Reseting counter
                    set udg_IS_distAngC[udg_IS_maxIndex] = 0
                    call SpiritsCreateDota(curIndex)                                                 // Create another Spirit
                else
                    set udg_IS_distAngC[udg_IS_maxIndex] = udg_IS_distAngC[udg_IS_maxIndex] + RAbsBJ(udg_IS_distAng[0])   // Adding "ang degrees" to counter 
                endif
            endif
    
            // ================ Setting up oscialtion distance ===========================================
            if udg_IS_distMove[curIndex] < udg_IS_distOscMin + 1  then                               // If oscillation distance is minimal:            
                set udg_IS_distOscPullIn[curIndex] = false                                           // turn off Pull in    
                set udg_IS_distMove[curIndex] = udg_IS_distOscMin                                    // set oscillation distance to minimal oscillation distance
            elseif udg_IS_distMove[curIndex] > udg_IS_distOscMax - 1 then                            // If oscillation distance is maximal:          
                set udg_IS_distOscPushOut[curIndex] = false                                          // turn off Push Out    
                set udg_IS_distMove[curIndex] = udg_IS_distOscMax                                    // set oscillation distance to maximal oscillation distance
            elseif udg_IS_distOscPushOut[curIndex] then                                              // If Push Out is on:    
                set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] + udg_IS_distAng[1]        // Increase oscillation distance    
            elseif udg_IS_distOscPullIn[curIndex] then                                               // If Pull In is on:
                set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] - udg_IS_distAng[1]        // decrease oscillation distance
            endif
    
            set udg_IS_angMove[curIndex] = udg_IS_angMove[curIndex] + udg_IS_distAng[0]              // Always increase angle
            set udg_IS_distAngC[curIndex] = udg_IS_distAngC[curIndex] + 1                            // Iteration counter
            
            // ================ Moving Spirits ===========================================================
            set c = 1
            loop
                // current number of Spirits; transfering from create to move function
                exitwhen c == udg_IS_nSpiritsCreated[curIndex]
                // Loading spirits
                set spirit = LoadUnitHandle(udg_IS_spiritHash, GetHandleId(udg_IS_caster[curIndex]), c)
                if udg_IS_distAng[0] > 0 then
                    // anticlockwise, (1 - c) * a sets it to the last created Spirit
                    set x = GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Cos((udg_IS_angMove[curIndex] + (1 - c) * a) * bj_DEGTORAD)
                    set y = GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Sin((udg_IS_angMove[curIndex] + (1 - c) * a) * bj_DEGTORAD)
                else
                    // cloclwise, (c - 1) * a sets it to the last created Spirit
                    set x = GetUnitX(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Cos((udg_IS_angMove[curIndex] + (c - 1) * a) * bj_DEGTORAD)
                    set y = GetUnitY(udg_IS_caster[curIndex]) + udg_IS_distMove[curIndex] * Sin((udg_IS_angMove[curIndex] + (c - 1) * a) * bj_DEGTORAD)
                endif
                call SetUnitX(spirit, x)                    // Moving Spirit to new coordinates
                call SetUnitY(spirit, y)
                set c = c + 1
            endloop
            
            set udg_IS_durTimer[curIndex] = udg_IS_durTimer[curIndex] + udg_IS_perTime        // Increasing duration time
            set curIndex = curIndex + 1                                                       // Increasing index
        endif    
    endloop
    
    // fog is already nulled
    set spirit = null
endfunction

// ----------------------------------------------------------
function SpiritsDamage takes integer curIndex, unit triggeredSpirit, unit triggerUnit returns nothing
    // This function is used to deal damage
    // triggeredSpirit; unit; triggered spirit
    // triggerUnit; unit; triggering unit
    // curIndex; integer; current index 
    
    local unit fog                          // fog; unit; first of group; used for damageGroup unit group
    
    // ================= Dealing damage ====================================================================================
    // If Spirit is alive
    if SpiritsIsAliveFilter(triggeredSpirit) then
        // If triggerting unit is alive, and an enemy
        if SpiritsIsAliveFilter(triggerUnit) and SpiritsUnitTypeFilter(triggerUnit) and IsUnitEnemy(triggerUnit, GetOwningPlayer(udg_IS_caster[curIndex])) then
            if IsUnitType(triggerUnit, UNIT_TYPE_HERO) then                                                     // If enemy unit is a hero
                set udg_IS_nSpiritsCurrent[curIndex] = udg_IS_nSpiritsCurrent[curIndex] - 1                     // Lowering number of Spirits                
                // Picking units in group for aoe damage around enemy hero
                set udg_IS_ownPlayer = GetOwningPlayer(triggeredSpirit)
                call GroupEnumUnitsInRange(udg_IS_dmgGroup, GetUnitX(triggerUnit), GetUnitY(triggerUnit), udg_IS_dmgHero[0], Filter(function SpiritsUnitGroupFilter))
                loop
                    set fog = FirstOfGroup(udg_IS_dmgGroup)
                    exitwhen fog == null
                    // Damaging unit in group
                    call UnitDamageTarget(udg_IS_caster[curIndex], fog, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
                    call GroupRemoveUnit(udg_IS_dmgGroup, fog)
                endloop
                call DestroyEffect(AddSpecialEffect(udg_IS_sfxModel, GetUnitX(triggeredSpirit), GetUnitY(triggeredSpirit)))          // Adding special effect at position of spirit
                call RemoveUnit(triggeredSpirit)                                                                                     // Spirit explode at the end of spell
            else
                // If enemy unit is not a hero
                call UnitDamageTarget(udg_IS_caster[curIndex], triggerUnit, udg_IS_dmgUnit[GetUnitAbilityLevel(udg_IS_caster[curIndex], udg_IS_abiUnitID[0])], false, false, udg_IS_dmgAttackType, udg_IS_dmgType, WEAPON_TYPE_WHOKNOWS)
            endif
        endif
    endif
    if udg_IS_nSpiritsCurrent[curIndex] == 0 then           // If there are no Spirits left
        call SpiritsDynamicIndex(curIndex)                  // Lowering index
    endif 
    
    // fog is already nulled
    set triggeredSpirit = null
    set triggerUnit = null
endfunction
                
// ----------------------------------------------------------
function SpiritsManage takes nothing returns nothing
    // This function is used to detect which ability is being used,
    // to set/reset spell values
    // to detect if unit is in range of spirit
    
    local integer c                                                          // c; integer; counter, helper variable
    local integer curIndex = 1                                               // curIndex; integer; counter, helper variable
    local unit triggerUnit = GetTriggerUnit()                                // triggerUnit; unit; helper variable  
    
    // =========== Initializating spell values, 0.01s event =======================================
    if udg_IS_maxIndex == -1 then                                            // maxIndex is used as a helper variable
        call SpiritsInitialization()                                         // setting spell values
    
    // =========== Ability being used is Spirits ==================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[0] then             
        loop 
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then
                call SpiritsEndSpell(curIndex)                                              // Ending spell if activated before duration expire 
            endif    
            set curIndex = curIndex + 1
        endloop
        
        set udg_IS_maxIndex = udg_IS_maxIndex + 1                                           // Incereasing max index
        
        // ========== Reseting spell values =======================================================                
        set udg_IS_caster[udg_IS_maxIndex] = triggerUnit                                    // caster
        set udg_IS_durTimer[udg_IS_maxIndex] = 0                                            // duration time
         
        set udg_IS_angMove[udg_IS_maxIndex] = udg_IS_distAng[3]                             // angle of Spirits while moving
        set udg_IS_distMove[udg_IS_maxIndex] = udg_IS_distOscMin                            // distance of Spirits while moving 
        set udg_IS_angCreate[udg_IS_maxIndex] = udg_IS_distAng[3]                           // angle of Spirits while created
        set udg_IS_distCreate[udg_IS_maxIndex] = 0                                          // distance of Spirits while created
        set udg_IS_distAngC[udg_IS_maxIndex] = (360 / udg_IS_nSpirits)                      // counts number of iterations
        set udg_IS_distOscPushOut[udg_IS_maxIndex] = false                                  // Push Out
        set udg_IS_distOscPullIn[udg_IS_maxIndex] = false                                   // Pull In
        set udg_IS_nSpiritsCreated[udg_IS_maxIndex] = 1                                     // Number of created Spirits 
        set udg_IS_nSpiritsCurrent[udg_IS_maxIndex] = udg_IS_nSpirits                       // Current number of Spirits

        set udg_SpiritCollisionTrigger[udg_IS_maxIndex] = CreateTrigger()
        // Save curIndex inside trigger so we can retrieve data from fired trigger
        call SaveInteger(udg_IS_spiritHash, GetHandleId(udg_SpiritCollisionTrigger[udg_IS_maxIndex]), 0, udg_IS_maxIndex) 
        call TriggerAddCondition(udg_SpiritCollisionTrigger[udg_IS_maxIndex], Condition(function SpiritCollision))     

        if udg_IS_maxIndex == 1 then                                                        // If only one hero is using the spell and is first to start
            call TimerStart(udg_IS_perTimer, udg_IS_perTime, true, function SpiritsMove)    // turn on periodic timer for SpiritsMove function 
        endif
        
    // =========== Ability being used is Push Out =================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[1] then
        loop                                                                                         // Looping through units
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then
                if udg_IS_distMove[curIndex] == udg_IS_distOscMax then                               // If distance of oscillation is maximal:
                    set udg_IS_distOscPushOut[curIndex] = false                                      // turn off Push Out
                elseif udg_IS_distOscPushOut[curIndex] then                                          // Sets Spirits oscillation distance at current if
                    set udg_IS_distOscPushOut[curIndex] = false                                      // casted while pushing out 
                else
                    set udg_IS_distOscPullIn[curIndex] = false                                       // Turn off Pull In
                    set udg_IS_distOscPushOut[curIndex] = true                                       // Turn on Push Out
                    set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] + udg_IS_distAng[1]    // Incerease oscillation distance
                endif
            endif
            set curIndex = curIndex + 1
        endloop
    
    // ========= Ability being used is Pull In ====================================================
    elseif GetSpellAbilityId() == udg_IS_abiUnitID[2] then
        loop                                                                                         // Looping through units
            exitwhen curIndex > udg_IS_maxIndex
            if udg_IS_caster[curIndex] == triggerUnit then        
                if udg_IS_distMove[curIndex] == udg_IS_distOscMin then                               // If distance of oscillation is minimal: 
                    set udg_IS_distOscPullIn[curIndex] = false                                       // turn off Pull In
                elseif udg_IS_distOscPullIn[curIndex] then                                           // Sets Spirits oscillation distance at current if
                    set udg_IS_distOscPullIn[curIndex] = false                                       // casted while pulling in
                else
                    set udg_IS_distOscPushOut[curIndex] = false                                      // Turn off Push Out
                    set udg_IS_distOscPullIn[curIndex] = true                                        // Turn on Pull In
                    set udg_IS_distMove[curIndex] = udg_IS_distMove[curIndex] - udg_IS_distAng[1]    // Decrease oscillation distance
                endif
            endif
            set curIndex = curIndex + 1               
        endloop   
    endif    
    
    set triggerUnit = null   
endfunction

// ======================= Init Trigger Spirits Initialization =======================================================================
function InitTrig_Spirits takes nothing returns nothing
    set gg_trg_Spirits = CreateTrigger()
    
    call TriggerRegisterTimerEvent(gg_trg_Spirits, 0.01, false)
    call TriggerRegisterAnyUnitEventBJ(gg_trg_Spirits, EVENT_PLAYER_UNIT_SPELL_EFFECT) 
    call TriggerAddCondition(gg_trg_Spirits, Condition(function SpiritsManage))
endfunction


Edit: SpiritsDamage needs to be above SpiritCollision function.
 
Level 5
Joined
Feb 17, 2011
Messages
95
Also I heard destroying trigger also destroy conditions so you don't need extra clear conditions.Hope I am not wrong :thumbs_up:
You're right. :thumbs_up:

Ok, i went through what you wrote, and i like how simple it is. I didn't know that dynamically creating/destroying trigger could be done this way. There were few simple changes i made, and it works nicely.

Only thing i don't get is why SpiritsCollision function needs to return false?
Edit: It works fine as: function SpiritsCollision takes nothing returns nothing

It's always a nice feeling to learn something new. Thank you for taking time to go through the whole spell, and for helping me. :thumbs_up:
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
You're right. :thumbs_up:
Yeah :grin:

Only thing i don't get is why SpiritsCollision function needs to return false?

SpiritsCollision function needs to be a triggercondition function so it must return true or false, I chose false because....?(I assumed if it returns false it doesn't check any trigger actions)

Oh wait...

You can use functions takes nothing returns nothing as Condition() parameter with vanilla editor?

I think newgenpack doesn't let you do this, so that is why it seemed weird to me.
 
Level 5
Joined
Feb 17, 2011
Messages
95
SpiritsCollision function needs to be a triggercondition function so it must return true or false, I chose false because....?(I assumed if it returns false it doesn't check any trigger actions)

Oh wait...

You can use functions takes nothing returns nothing as Condition() parameter with vanilla editor?
Yeah, I've used the same method for SpiritsManage function.
I think newgenpack doesn't let you do this, so that is why it seemed weird to me.
I guess it wouldn't make much difference either way. :)

Here is the spell if you want to see it in action. :grin:
 
Status
Not open for further replies.
Top