Spellpack The Invoker

Reaction score
91
http://www.dota-allstars.com/hero/2809/index.html <--- The Hero.
http://www.dota-allstars.com/wiki/Invoker <--- Check this for full information about him.
If you haven't played DotA then just download the testmap at the bottom of this post and see the spells. The screenshots will not show you much, after all the spellpack can be fully seen in-game.

GUI/JASS/vJASS? vJASS.
MUI? Yes.
Lagless? Not so much since it's a huge spellpack. But I've included an optional script that can preload all these resources.
Requires?
- Jass NewGen Pack
- PUI
- GroupUtils
- TimerUtils
- Line Segments

After you have read the two links that give full information on this DotA Hero, you will notice that there are a lot of object editor data that needs to be used and every spell needs to be triggered.

Read me:
Code:
Spellpack created by Tyrande_ma3x. Credits are not necessary but would be appreciated. 

Please do not claim this to be your own.

HOW TO IMPORT:
- First, at the top of each trigger in this folder (The Invoker) there is a green comment that shows the requirements for it. 
You must have all those in your map, otherwise it would give you errors. The full resources required can be found in the folder 
"Systems". Copy everything from there (if you do not have it) to your map and return back to this folder.
- The main library that is needed for all spells to function normally (AND IS THE MAIN CONFIGURATION) is the trigger InvokerConfig.
If you take a closer look you will see lots of constant integers that represent raw codes of abilities/units/buffs. There are also strings
that contain the order string of a specific ability. To understand easier the naming of the constants I've sorted them with a prefix in front of them:
AID (ability id) - this is a raw code of an ability.
UID (ability id) - this is a raw code of a unit.
OID (order id) - this is an order string.
BID (buff id) - this is a raw code of a buff. 
You will also see a few functions at the bottom which might also need configuring depending on your needs. Read the comments and change if you'd like.
It will be quite easy for you to configure the raw codes because I've made the variables exactly as in the object editor. For example:
Ice Wall - UID_ICE_WALL = unit raw code of the Ice Wall unit in Object Editor.
Chaos Meteor Falling - UID_CHAOS_METEOR_FALLING = unit raw code of the Chaos Meteor Falling unit in Object Editor. etc.

Ok, you know how to edit the main configuration script. The next step is quite simple:
Copy EVERYTHING in Object Editor/Units under categorization Custom Units (you may skip the Invoker if you don't want him)! 
That's a total of 16 (15) units.
Copy EVERYTHING in Object Editor/Abilities under categorization Custom Abilities/UNDEAD! The Human part is for the testmap.

You can now copy this whole folder (The Invoker) but there are two triggers you can skip:
- InvokerPreload - this is a script that will preload all those Invoker's resources at map initialization.
- Invokelist - a script that gives you a command that will display a message for the triggering player. Usually this message would be the list of the Invoker's spells. 

After you have copied the whole folder, go through every trigger and check the configuration menu. EDIT WHAT NEEDS EDITING!

This is a lot of work to do and it cannot be done really fast. Enjoy your Invoker!

//========================================================
InvokerConfig: (this is the configuration library that contains all resources such as raw codes, order strings, etc.)
JASS:

library InvokerConfig initializer Init 
// Requires nothing.

//===============================================================================
// C O N F I G U R E    E V E R Y T H I N G    H E R E    I F     N E C E S S A R Y
//===============================================================================

globals
// Raw code of your Hero that is going to have all of the Invoker&#039;s spells.
    constant integer UID_INVOKER = &#039;H000&#039;
// Raw code of your generic dummy unit.
    constant integer UID_DUMMY = &#039;e000&#039;
// The EMP unit that is created at the target point of an EMP cast.
    constant integer UID_EMP = &#039;e001&#039;
// The EMP Blast unit that is created when the ball explodes, drains mana and deals damage.
// It uses an unit because this way it can get scaled to a much bigger size.
    constant integer UID_EMP_BLAST = &#039;e002&#039;
// Raw code of the Chaos Meteor unit (the unit that is supposed to &quot;roll&quot;).
    constant integer UID_CHAOS_METEOR = &#039;e003&#039;
// Raw code of the Chaos Meteor Falling unit (the meteor that falls from the sky).
    constant integer UID_CHAOS_METEOR_FALLING = &#039;e004&#039;
// Raw code of the Tornado unit.
    constant integer UID_TORNADO = &#039;e005&#039;
// Raw code of the Deafening Blast unit.
    constant integer UID_DEAFENING_BLAST = &#039;e006&#039;
// Raw code of the Ice Wall unit.
    constant integer UID_ICE_WALL = &#039;e007&#039;
    
//===============================================================================
// Order string of casting the ability Cold Snap Ministun.
    constant string OID_COLD_SNAP_MINISTUN = &quot;thunderbolt&quot;
// Order string of casting the ability Tornado Cyclone.
    constant string OID_TORNADO_CYCLONE = &quot;cyclone&quot;
// Order string for casting the ability Deafening Blast Silence.
    constant string OID_DEAFENING_BLAST_SILENCE = &quot;soulburn&quot;
// Order string for casting the ability Ghost Walk Self Slow.
    constant string OID_GHOST_WALK_SELF_SLOW = &quot;slow&quot;
//===============================================================================
// These are the raw codes of the abilities that give the sphere effect.
// Reference them as Quas Sphere First/Second/Third and fill in their raw code accordingly.
// Raw code of the spell Quas Sphere First 
    constant integer AID_QUAS_FIRST = &#039;A008&#039;
// Raw code of Quas Sphere Second... etc.
    constant integer AID_QUAS_SECOND = &#039;A009&#039;
    constant integer AID_QUAS_THIRD = &#039;A00A&#039;

// Reference these as Wex Sphere First/Second/Third and fill in their raw codes accordingly.
    constant integer AID_WEX_FIRST = &#039;A005&#039;
    constant integer AID_WEX_SECOND = &#039;A006&#039;
    constant integer AID_WEX_THIRD = &#039;A007&#039;

// Reference these as Exort Sphere First/Second/Third and fill their raw codes accordingly.
    constant integer AID_EXORT_FIRST = &#039;A002&#039;
    constant integer AID_EXORT_SECOND = &#039;A003&#039;
    constant integer AID_EXORT_THIRD = &#039;A004&#039;

// Raw code of the Hero ability Quas.
    constant integer AID_QUAS = &#039;A00D&#039;
// Raw code of the Hero ability Wex.
    constant integer AID_WEX = &#039;A00C&#039;
// Raw code of the Hero ability Exort.
    constant integer AID_EXORT = &#039;A00B&#039;
// Raw code of the Hero ability Invoke.
    constant integer AID_INVOKE = &#039;A00E&#039;
    
//===============================================================================
// Buff raw code of the ability Ghost Walk.
    constant integer BID_GHOST_WALK = &#039;B004&#039;
// Buff raw code of the ability Ghost Walk Self Slow.
    constant integer BID_GHOST_WALK_SELF_SLOW = &#039;B003&#039;
//===============================================================================

// Raw codes of the abilities that gives movement/attack speed bonuses when Wex is in use.
    constant integer AID_WEX_MOVEMENT_SPEED_BONUS = &#039;A00I&#039;
    constant integer AID_WEX_ATTACK_SPEED_BONUS = &#039;A00J&#039;
// Raw code of the spellbook that contains the movement speed bonus by Wex.
    constant integer AID_WEX_SPELLBOOK = &#039;A00K&#039;
// Raw code of the ability that gives damage bonuses when Exort is in use. 
    constant integer AID_EXORT_BONUS = &#039;A00F&#039;

// Raw code of the Cold Snap ability.
    constant integer AID_COLD_SNAP = &#039;A00G&#039;
// Raw code of the dummy ability that is used to ministun and deal damage (Cold Snap Ministun).
    constant integer AID_COLD_SNAP_MINISTUN = &#039;A00H&#039;
// Raw code of the EMP ability.
    constant integer AID_EMP = &#039;A00L&#039;
// Raw code of the Sun Strike ability.
    constant integer AID_SUN_STRIKE = &#039;A00N&#039;
// Raw code of the Chaos Meteor ability.
    constant integer AID_CHAOS_METEOR = &#039;A00M&#039;
// Raw code of the Alacrity ability.
    constant integer AID_ALACRITY = &#039;A00O&#039;
// Raw code of the ability that gives damage (Alacrity Damage Bonus).
    constant integer AID_ALACRITY_DAMAGE_BONUS = &#039;A00P&#039;
// Raw code of the ability that gives attack speed (Alacrity Speed Bonus).
    constant integer AID_ALACRITY_SPEED_BONUS = &#039;A00Q&#039;
// Raw code of the ability Tornado.
    constant integer AID_TORNADO = &#039;A00R&#039;
// Raw code of the ability Tornado Cyclone that cyclones affected units.
    constant integer AID_TORNADO_CYCLONE = &#039;A00S&#039;
// Raw code of the ability Deafening Blast.
    constant integer AID_DEAFENING_BLAST = &#039;A00W&#039;
// Raw code of the ability Deafening Blast Disable Attack that is supposed to disable a unit&#039;s attack.
// Based on Cargo Hold (Orc Burrow).
    constant integer AID_DEAFENING_BLAST_DISABLE_ATTACK = &#039;A00T&#039;
// Raw code of the ability Deafening Blast Silence.
    constant integer AID_DEAFENING_BLAST_SILENCE = &#039;A00V&#039;
// Raw code of the Ice Wall ability.
    constant integer AID_ICE_WALL = &#039;A00X&#039;
// Raw code of the Ice Wall Slow Aura ability. 
    constant integer AID_ICE_WALL_SLOW_AURA = &#039;A00Y&#039;
// Raw code of the Ghost Walk ability.
    constant integer AID_GHOST_WALK = &#039;A00Z&#039;
// Raw code of the Ghost Walk Aura ability.
    constant integer AID_GHOST_WALK_AURA = &#039;A010&#039;
// Raw code of the ability Ghost Walk Self Slow.
    constant integer AID_GHOST_WALK_SELF_SLOW = &#039;A011&#039;
// Raw code of the ability Forge Spirit.
    constant integer AID_FORGE_SPIRIT = &#039;A012&#039;
// Raw code of the ability Forge Spirit Armor Melt.
    constant integer AID_FORGE_SPIRIT_ARMOR_MELT = &#039;A013&#039;
// Raw code of the ability Forge Spirit Armor Bonus.
    constant integer AID_FORGE_SPIRIT_ARMOR_BONUS = &#039;A014&#039;
// Raw code of the ability Forge Spirit Mana Bonus.
    constant integer AID_FORGE_SPIRIT_MANA_BONUS = &#039;A015&#039;
endglobals

//===============================================================================

// Since real numbers cannot be used in life regeneration spells, Quas&#039; regeneration
// has to be triggered for special cases like these. 
public constant function QUAS_REGENERATION takes integer level returns real
    return 0.75 * level 
    // Each level gives 0.75 regeneration PER ONE INSTANCE.
    // If you have 3 instances you would get 2.25 hp/sec.
    // And 15.75 for 3 instances and maximum level.
endfunction

// Choose a limitation to which levels to make it invoke only 1 spell.
// If you, for example, returned 3 it will allow 2 invokes only at level 4 and above.
public constant function LIMIT takes integer level returns integer
    return 1
endfunction

globals
// DO NOT TOUCH OR CHANGE THIS INTEGER ARRAY!
    integer array SPIRIT
endglobals

// Edit this by adding or changing raw codes and array indexes. 
// REMEMBER TO ALWAYS START FROM SPIRIT[1] AND INCREASE BY ONE WITHOUT SKIPPING AN INDEX!
// For example: SPIRIT[1], SPIRIT[3], SPIRIT[4] &lt;---- WRONG.
// The index represents what spirit will be spawned at that level.
// For example: Spirit[3] - A spirit with raw code &#039;n002&#039; will be spawned.
private function SetForgeSpiritData takes nothing returns nothing
// Level 1.
    set SPIRIT[1] = &#039;n000&#039;
// Level 2.
    set SPIRIT[2] = &#039;n001&#039;
// Level 3.
    set SPIRIT[3] = &#039;n002&#039;
// etc.
    set SPIRIT[4] = &#039;n003&#039;
    set SPIRIT[5] = &#039;n004&#039;
    set SPIRIT[6] = &#039;n005&#039;
    set SPIRIT[7] = &#039;n006&#039;
endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

private function Init takes nothing returns nothing
    local integer a = 0
    // Make the spellbook hidden so the bonuses&#039; icons can&#039;t be seen.
    loop
        exitwhen a &gt; 11
        call SetPlayerAbilityAvailable(Player(a), AID_WEX_SPELLBOOK, false)
        set a = a + 1
    endloop
    call SetForgeSpiritData()
endfunction

endlibrary



InvokerPreload (optional library that preloads all of the Invoker's resources)
JASS:

library InvokerPreload initializer Init requires InvokerConfig, AbilityPreload
// Look above what it requires.

//===============================================================================
// THIS IS AN OPTIONAL LIBRARY THAT WILL PRELOAD ALL OF THE INVOKER&#039;S RESOURCES
// AT MAP INITIALIZATION. IF YOU WANT YOU CAN COPY IT IN YOUR MAP HAVING IN MIND
// THAT IT WOULD CAUSE A SPIKE JUST AT THE START DUE TO THE BIG AMOUNTS OF DATA.
// IT IS OPTIONAL BECAUSE YOU MAY WANT TO PRELOAD YOUR STUFF IN A DIFFERENT WAY.
//===============================================================================

private function Init takes nothing returns nothing
    local integer a = 0
    call AbilityPreload(AID_QUAS_FIRST)
    call AbilityPreload(AID_QUAS_SECOND)
    call AbilityPreload(AID_QUAS_THIRD)
    call AbilityPreload(AID_WEX_FIRST)
    call AbilityPreload(AID_WEX_SECOND)
    call AbilityPreload(AID_WEX_THIRD)
    call AbilityPreload(AID_EXORT_FIRST)
    call AbilityPreload(AID_EXORT_SECOND)
    call AbilityPreload(AID_EXORT_THIRD)
    call AbilityPreload(AID_QUAS)
    call AbilityPreload(AID_WEX)
    call AbilityPreload(AID_EXORT)
    call AbilityPreload(AID_INVOKE)
    call AbilityPreload(AID_WEX_ATTACK_SPEED_BONUS)
    // Uknown why but preloading these at Map Initialization crashes with fatal error.
    //call AbilityPreload(AID_WEX_SPELLBOOK) &lt;--- Causes Fatal Error.
    //call AbilityPreload(AID_WEX_MOVEMENT_SPEED_BONUS) &lt;--- Causes Fatal Error.
    call AbilityPreload(AID_EXORT_BONUS)
    call AbilityPreload(AID_COLD_SNAP)
    call AbilityPreload(AID_COLD_SNAP_MINISTUN)
    call AbilityPreload(AID_EMP)
    call AbilityPreload(AID_SUN_STRIKE)
    call AbilityPreload(AID_CHAOS_METEOR)
    call AbilityPreload(AID_TORNADO)
    call AbilityPreload(AID_TORNADO_CYCLONE)
    call AbilityPreload(AID_DEAFENING_BLAST)
    call AbilityPreload(AID_DEAFENING_BLAST_DISABLE_ATTACK)
    call AbilityPreload(AID_ICE_WALL)
    call AbilityPreload(AID_ICE_WALL_SLOW_AURA)
    call AbilityPreload(AID_GHOST_WALK)
    call AbilityPreload(AID_GHOST_WALK_AURA)
    call AbilityPreload(AID_FORGE_SPIRIT)
    call AbilityPreload(AID_FORGE_SPIRIT_ARMOR_MELT)
    call AbilityPreload(AID_FORGE_SPIRIT_ARMOR_BONUS)
    call AbilityPreload(AID_FORGE_SPIRIT_MANA_BONUS)
    
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_INVOKER, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_DUMMY, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_EMP, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_EMP_BLAST, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_CHAOS_METEOR, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_CHAOS_METEOR_FALLING, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_TORNADO, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    call UnitApplyTimedLife(CreateUnit(Player(13), UID_ICE_WALL, 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
    loop
        exitwhen SPIRIT[a] == null
        call UnitApplyTimedLife(CreateUnit(Player(13), SPIRIT[a], 9999999., 999999., 0.), &#039;BTLF&#039;, 2.)
        set a = a + 1
    endloop
endfunction

endlibrary



Invokelist (optional library that displays a text [usually the list of the Invoker's spells] to a specific player)
Screenshot: http://i42.tinypic.com/2wrovhl.jpg
JASS:

scope Invokelist initializer Init 
// Requires nothing.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Should a message appear at Map Initialization reminding what is the command?
    private constant boolean REMINDER = true
// If yes, then this message will be displayed:
    private constant string REMINDER_MESSAGE = &quot;Type |cffffcc00-invokelist|r to see what spells you can invoke.&quot;
// Should previous text messages be cleared so only the list be visible for the player? 
// True or false.
    private constant boolean CLEAR_MESSAGES = true
// Duration of the list that is going to be displayed for the player (in seconds).
    private constant real LIST_DURATION = 30.
    
// Don&#039;t touch this string array!!!
    private string array SPELL
endglobals

// You can add your list here by filling the value SPELL[x] (x is a number) from 0 to
// as much as you like. Then all these will be displayed after the user types -invokelist.
// Remember to always start at 0 and NEVER have a missing index inbetween 
// (for example SPELL[0], SPELL[1], SPELL[3] &lt;--- that is WRONG and will not display the whole list).
private function Set takes nothing returns nothing
// All those FF03535 etc. symbols are colour codes.
    set SPELL[0] = &quot;Sun Strike [|CFFFF0303EEE|r] - [|cffffcc00T|r]&quot;
    set SPELL[1] = &quot;Chaos Meteor [|CFFFF0303EE|r|cff93ffc9W|r] - [|cffffcc00D|r]&quot;
    set SPELL[2] = &quot;Forge Spirit [|CFFFF0303EE|r|CFF0042FFQ|r] - [|cffffcc00F|r]&quot;
    set SPELL[3] = &quot;Cold Snap [|CFF0042FFQQQ|r] - [|cffffcc00Y|r]&quot;
    set SPELL[4] = &quot;Ghost Walk [|CFF0042FFQQ|r|cff93ffc9W|r] - [|cffffcc00V|r]&quot;
    set SPELL[5] = &quot;Ice Wall [|CFF0042FFQQ|r|cff93ffc9E|r] - [|cffffcc00G|r]&quot;
    set SPELL[6] = &quot;Alacrity [|cff93ffc9WW|r|CFFFF0303E|r] - [|cffffcc00Z|r]&quot;
    set SPELL[7] = &quot;Tornado [|cff93ffc9WW|r|CFF0042FFQ|r] - [|cffffcc00X|r]&quot;
    set SPELL[8] = &quot;EMP [|cff93ffc9WWW|r] - [|cffffcc00C|r]&quot;
    set SPELL[9] = &quot;Deafening Blast [|CFF0042FFQ|r|cff93ffc9W|r|CFFFF0303E|r] - [|cffffcc00B|r]&quot;
endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================
    
private function Actions takes nothing returns nothing
    local integer a = 0
    if CLEAR_MESSAGES then
        if GetLocalPlayer() == GetTriggerPlayer() then
            call ClearTextMessages()
        endif
    endif
    loop
        exitwhen SPELL[a] == null
        call DisplayTimedTextToPlayer(GetTriggerPlayer(), 0., 0., LIST_DURATION, SPELL[a])
        set a = a + 1
    endloop
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    local integer a = 0
    loop
        exitwhen a &gt; 11
        call TriggerRegisterPlayerChatEvent(trig, Player(a), &quot;-invokelist&quot;, true)
        set a = a + 1
    endloop
    call TriggerAddAction(trig, function Actions)
    if REMINDER then
        call BJDebugMsg(REMINDER_MESSAGE)
    endif
    call Set()
endfunction

endscope


Spheres: (handles all bonuses from Quas/Wex/Exort and displays correctly the spheres above the Invoker)
Screenshot: http://i39.tinypic.com/5wz3mw.jpg
JASS:
 
scope Spheres initializer Init 
// Requires PUI and InvokerConfig.

//===============================================================================
// * This script handles all orb combination and changes and keeps track of them.
// * It also takes care of the bonuses given by Quas/Wex/Exort and of invoked spells.
//===============================================================================

//========================
// No configuration here.=
//========================

globals
// Group for enumerating.
    private group gr = CreateGroup()
// Unit used for FirstOfGroup().
    private unit fir = null
endglobals

// Even if this isn&#039;t private, it shouldn&#039;t be touched.
// Do not edit it unless you know what you are doing!
struct Spheres
    //! runtextmacro PUI()
    integer quas = 0
    integer wex = 0
    integer exort = 0
    
    integer count = 0
    integer invokes = 0
    
    integer invoke1 = 0
    integer invoke2 = 0
endstruct

private function Conditions takes nothing returns boolean
    // One of the three spells should be casted.
    return GetSpellAbilityId() == AID_QUAS or GetSpellAbilityId() == AID_WEX or GetSpellAbilityId() == AID_EXORT
endfunction

// The bonuses given by the orbs should be updated when a new level of Exort/Wex is learned.
// There is no need for Quas since it&#039;s triggered.
private function FixBonuses takes nothing returns boolean
    local unit cast = GetTriggerUnit()
    local Spheres d = Spheres[cast]
    if GetLearnedSkill() == AID_EXORT and GetUnitAbilityLevel(cast, AID_EXORT_BONUS) &gt; 0 then
        call SetUnitAbilityLevel(cast, AID_EXORT_BONUS, GetUnitAbilityLevel(cast, AID_EXORT) * d.exort)
    elseif GetLearnedSkill() == AID_WEX and GetUnitAbilityLevel(cast, AID_WEX_MOVEMENT_SPEED_BONUS) &gt; 0 then
        call SetUnitAbilityLevel(cast, AID_WEX_MOVEMENT_SPEED_BONUS, GetUnitAbilityLevel(cast, AID_WEX) * d.wex)
        call SetUnitAbilityLevel(cast, AID_WEX_ATTACK_SPEED_BONUS, GetUnitAbilityLevel(cast, AID_WEX) * d.wex)
    endif
    set cast = null
    return false
endfunction

private function GetInvoker takes nothing returns boolean
    return GetUnitTypeId(GetFilterUnit()) == UID_INVOKER and GetUnitAbilityLevel(GetFilterUnit(), AID_QUAS) &gt; 0 and GetWidgetLife(GetFilterUnit()) &gt; 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
    // Double check. 
endfunction

private function Callback takes nothing returns nothing
    local real heal
    local Spheres d
    // Get the Invokers on the map (there can be more than one of them). 
    call GroupEnumUnitsInRange(gr, 0., 0., 999999., Condition(function GetInvoker))
    loop
        set fir = FirstOfGroup(gr)
        exitwhen fir == null
        set d = Spheres[fir]
        // Is the struct created and attached?
        if d != 0 then 
            // Is a Quas orb activated?
            if d.quas &gt; 0 then
                set heal = InvokerConfig_QUAS_REGENERATION(GetUnitAbilityLevel(fir, AID_QUAS)) * d.quas
                call SetWidgetLife(fir, GetWidgetLife(fir) + heal)
            endif
        endif
        call GroupRemoveUnit(gr, fir)
    endloop
endfunction

private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local Spheres d = Spheres[cast]
    // Was the struct already created? If no, create it and &quot;attach&quot; with PUI.
    if d == 0 then
        set d = Spheres.create()
        set Spheres[cast] = d
    endif
    // Used for counting the spheres... If it gets over 3 then return it back
    // to 1 so the cycle can begin once again and orbs can get replaced.
    if d.count &lt; 3 then
        set d.count = d.count + 1
    else
        set d.count = 1
    endif
    // Was Quas casted?
    if GetSpellAbilityId() == AID_QUAS then
        // Maybe the first orb must be replaced?
        if d.count == 1 then
            // Remove all attached orb effects.
            call UnitRemoveAbility(cast, AID_QUAS_FIRST)
            call UnitRemoveAbility(cast, AID_WEX_FIRST)
            call UnitRemoveAbility(cast, AID_EXORT_FIRST)
            // And add the one that is really needed.
            call UnitAddAbility(cast, AID_QUAS_FIRST)
        // Or maybe the second sphere needs to be replaced&gt;
        elseif d.count == 2 then
            // Remove again.
            call UnitRemoveAbility(cast, AID_QUAS_SECOND)
            call UnitRemoveAbility(cast, AID_WEX_SECOND)
            call UnitRemoveAbility(cast, AID_EXORT_SECOND)
            // Add the necessary one.
            call UnitAddAbility(cast, AID_QUAS_SECOND)
        // Nothing left, it must be the 3rd orb.
        elseif d.count == 3 then
            // Remove.
            call UnitRemoveAbility(cast, AID_QUAS_THIRD)
            call UnitRemoveAbility(cast, AID_WEX_THIRD)
            call UnitRemoveAbility(cast, AID_EXORT_THIRD)
            // Add.
            call UnitAddAbility(cast, AID_QUAS_THIRD)
        endif
    // Was Wex casted instead?
    elseif GetSpellAbilityId() == AID_WEX then
        // Same cycle as in Exort.
        if d.count == 1 then
            // Remove.
            call UnitRemoveAbility(cast, AID_QUAS_FIRST)
            call UnitRemoveAbility(cast, AID_WEX_FIRST)
            call UnitRemoveAbility(cast, AID_EXORT_FIRST)
            // Add.
            call UnitAddAbility(cast, AID_WEX_FIRST)
        // Is it second sphere? 
        elseif d.count == 2 then
            // Remove.
            call UnitRemoveAbility(cast, AID_QUAS_SECOND)
            call UnitRemoveAbility(cast, AID_WEX_SECOND)
            call UnitRemoveAbility(cast, AID_EXORT_SECOND)
            // Add.
            call UnitAddAbility(cast, AID_WEX_SECOND)
        elseif d.count == 3 then
            call UnitRemoveAbility(cast, AID_QUAS_THIRD)
            call UnitRemoveAbility(cast, AID_WEX_THIRD)
            call UnitRemoveAbility(cast, AID_EXORT_THIRD)
            call UnitAddAbility(cast, AID_WEX_THIRD)
        endif
// Exort was casted if it wasn&#039;t the other two: 
    else
        if d.count == 1 then
            call UnitRemoveAbility(cast, AID_QUAS_FIRST)
            call UnitRemoveAbility(cast, AID_WEX_FIRST)
            call UnitRemoveAbility(cast, AID_EXORT_FIRST)
            // Add first exort orb effect.
            call UnitAddAbility(cast, AID_EXORT_FIRST)
        elseif d.count == 2 then
            call UnitRemoveAbility(cast, AID_QUAS_SECOND)
            call UnitRemoveAbility(cast, AID_WEX_SECOND)
            call UnitRemoveAbility(cast, AID_EXORT_SECOND)
            // Add second exort orb effect.
            call UnitAddAbility(cast, AID_EXORT_SECOND)
        elseif d.count == 3 then
            call UnitRemoveAbility(cast, AID_QUAS_THIRD)
            call UnitRemoveAbility(cast, AID_WEX_THIRD)
            call UnitRemoveAbility(cast, AID_EXORT_THIRD)
            // Add third exort orb effect.
            call UnitAddAbility(cast, AID_EXORT_THIRD)
        endif
    endif
    // Set the counting for the orbs to 0.
    set d.wex = 0
    set d.exort = 0
    set d.quas = 0
    // And start checking one by one the number of activated instances.
    if GetUnitAbilityLevel(cast, AID_WEX_FIRST) &gt; 0 then
        set d.wex = d.wex + 1
    endif
    if GetUnitAbilityLevel(cast, AID_WEX_SECOND) &gt; 0 then
        set d.wex = d.wex + 1
    endif
    if GetUnitAbilityLevel(cast, AID_WEX_THIRD) &gt; 0 then
        set d.wex = d.wex + 1
    endif
    
    if GetUnitAbilityLevel(cast, AID_EXORT_FIRST) &gt; 0 then
        set d.exort = d.exort + 1
    endif
    if GetUnitAbilityLevel(cast, AID_EXORT_SECOND) &gt; 0 then
        set d.exort = d.exort + 1
    endif
    if GetUnitAbilityLevel(cast, AID_EXORT_THIRD) &gt; 0 then
        set d.exort = d.exort + 1
    endif

    if GetUnitAbilityLevel(cast, AID_QUAS_FIRST) &gt; 0 then
        set d.quas = d.quas + 1
    endif
    if GetUnitAbilityLevel(cast, AID_QUAS_SECOND) &gt; 0 then
        set d.quas = d.quas + 1
    endif
    if GetUnitAbilityLevel(cast, AID_QUAS_THIRD) &gt; 0 then
        set d.quas = d.quas + 1
    endif

    // First remove the abilities that give bonuses.
    call UnitRemoveAbility(cast, AID_WEX_SPELLBOOK)
    call UnitRemoveAbility(cast, AID_WEX_ATTACK_SPEED_BONUS)
    call UnitRemoveAbility(cast, AID_EXORT_BONUS)
    // And re-add them if it&#039;s necessary.
    if d.wex &gt; 0 then
        call UnitAddAbility(cast, AID_WEX_SPELLBOOK)
        call UnitAddAbility(cast, AID_WEX_ATTACK_SPEED_BONUS)
    endif
    if d.exort &gt; 0 then
        call UnitAddAbility(cast, AID_EXORT_BONUS)
    endif
    
    // Modify their level depending on the main abilities&#039; level and the number of activated orbs. 
    call SetUnitAbilityLevel(cast, AID_WEX_MOVEMENT_SPEED_BONUS, GetUnitAbilityLevel(cast, AID_WEX) * d.wex)
    call SetUnitAbilityLevel(cast, AID_WEX_ATTACK_SPEED_BONUS, GetUnitAbilityLevel(cast, AID_WEX) * d.wex)
    call SetUnitAbilityLevel(cast, AID_EXORT_BONUS, GetUnitAbilityLevel(cast, AID_EXORT) * d.exort)
    
    set cast = null
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    set trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_HERO_SKILL)
    call TriggerAddCondition(trig, Condition(function FixBonuses))
    
    call TimerStart(CreateTimer(), 1., true, function Callback)
endfunction

endscope


Invoke: (the Invoker's ultimate - Invokes a spell based on a combination between Quas/Wex/Exort)
JASS:

scope Invoke initializer Init 
// Requires PUI, Spheres, InvokerConfig.

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_INVOKE
endfunction

private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local integer temp
    local integer lvl = GetUnitAbilityLevel(cast, AID_INVOKE)
    local Spheres d = Spheres[cast]
    // Some checks to prevent more than 2 invokes (and a limit check, ofc).
    if d.invokes &lt; 2 and lvl &gt; InvokerConfig_LIMIT(lvl) then
        set d.invokes = d.invokes + 1
    else
        set d.invokes = 1
    endif
// Next are some checks to see what combinations were made:
    // 3 times Exort - Sun Strike.
    if d.exort == 3 then
        set temp = AID_SUN_STRIKE
    // 3 times Wex - EMP.
    elseif d.wex == 3 then
        set temp = AID_EMP
    // 3 times Quas - Cold Snap.
    elseif d.quas == 3 then
        set temp = AID_COLD_SNAP
    // 2 times Exort and 1 time Wex - Chaos Meteor.
    elseif d.exort == 2 and d.wex == 1 then
        set temp = AID_CHAOS_METEOR
    // 2 times Wex and 1 time Exort - Alacrity.
    elseif d.wex == 2 and d.exort == 1 then
        set temp = AID_ALACRITY
    // 2 times Wex and 1 time Quas - Tornado.
    elseif d.wex == 2 and d.quas == 1 then
        set temp = AID_TORNADO
    // 1 time Quas, 1 time Wex and 1 time Exort - Deafening Blast.
    elseif d.quas == 1 and d.wex == 1 and d.exort == 1 then
        set temp = AID_DEAFENING_BLAST
    // 2 times Quas, 1 time Exort - Ice Wall.
    elseif d.quas == 2 and d.exort == 1 then
        set temp = AID_ICE_WALL
    // 2 times Quas, 1 time Wex - Ghost Walk.
    elseif d.quas == 2 and d.wex == 1 then
        set temp = AID_GHOST_WALK
    // 2 times Exort, 1 time Quas - Forge Spirit.
    elseif d.exort == 2 and d.quas == 1 then
        set temp = AID_FORGE_SPIRIT
    endif
    
    // Other checks to see which ability needs to be replaced.
    // Of course, it shouldn&#039;t be the previous invoked one because that would
    // make you have one invoked spell forever. A cycle is needed.
    // And a check must be done to see if the spell that is going to be replaced 
    // is the same (if it just replaced then cooldown could be reseted and thus abused).
    if d.invokes == 1 and temp != d.invoke1 then
        call UnitRemoveAbility(cast, d.invoke1)
        set d.invoke1 = temp
        call UnitAddAbility(cast, d.invoke1)
    elseif d.invokes == 2 and temp != d.invoke2 then
        call UnitRemoveAbility(cast, d.invoke2)
        set d.invoke2 = temp
        call UnitAddAbility(cast, d.invoke2)
    endif
    set cast = null
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
endfunction

endscope


Cold Snap: (created by having 3 quas orbs and using Invoke)
Screenshot: http://i42.tinypic.com/2d77imp.jpg
JASS:

scope ColdSnap initializer Init 
// Requires PUI, TimerUtils, LightLeaklessDamageDetect, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Special effect attached to the target while Cold Snap is active.
    private constant string SFX_ONE = &quot;Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl&quot;
// Attachment point of the special effect (SFX_ONE).
    private constant string AP_ONE = &quot;overhead&quot;
endglobals
// Duration of the ability.
    private constant function DURATION takes integer level returns real
        return 2.5 + (level * 0.5)
    endfunction
// Interval in which affected enemies cannot get ministunned. 
    private constant function MINISTUN_INTERVAL takes integer level returns real
        return 0.8 - (0.028 * level)
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

// PUI properties for checking whether a unit was hit and prevent endless
// ministuns and passing the level of Quas. 
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;boolean&quot;, &quot;AFFECTED&quot;, &quot;false&quot;)
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;integer&quot;, &quot;LEVEL&quot;, &quot;0&quot;)

globals
// Group that contains all units that have Cold Snap on them.
    private group gr = CreateGroup()
// A pointer for a dummy unit.
    private unit dum = null
endglobals
    
private struct Data
    unit targ
    timer tim 
    effect sfx 
    
    static method create takes unit targ returns Data
        local Data d = Data.allocate()
        set d.targ = targ
        set d.tim = NewTimer()
        call SetTimerData(d.tim, d)
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.sfx)
        call ReleaseTimer(.tim)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_COLD_SNAP 
endfunction

private function Refresh takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    // Allow the target to be affected by a ministun next time it gets damaged.
    set AFFECTED[d.targ] = false 
    call d.destroy()
    set tim = null
endfunction

private function OnDamage takes nothing returns boolean
    local unit cast = GetEventDamageSource()
    local unit targ = GetTriggerUnit()
    local Data d 
    // Is the unit allowed to get ministunned? Does it have Cold Snap on it? 
    if AFFECTED[targ] == false and IsUnitInGroup(targ, gr) and GetEventDamage() &gt; 1. then
        // No more ministun activating until the timer expires.
        set AFFECTED[targ] = true
        set d = Data.create(targ)
        call TimerStart(d.tim, MINISTUN_INTERVAL(LEVEL[targ]), false, function Refresh)
        // Creating the dummy and cause it to cast the ministun.
        set dum = CreateUnit(GetOwningPlayer(cast), UID_DUMMY, GetUnitX(targ), GetUnitY(targ), 0.)
        call UnitAddAbility(dum, &#039;Aloc&#039;)
        call UnitAddAbility(dum, AID_COLD_SNAP_MINISTUN)
        call IssueTargetOrder(dum, OID_COLD_SNAP_MINISTUN, targ)
        call UnitApplyTimedLife(dum, &#039;BTLF&#039;, 0.5)
        set dum = null
    endif
    set cast = null
    set targ = null
    return false
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    // Remove the affected unit from the Cold Snap group because the duration has ended.
    call GroupRemoveUnit(gr, d.targ)
    call d.destroy()
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local integer lvl = GetUnitAbilityLevel(cast, AID_QUAS)
    local Data d = Data.create(GetSpellTargetUnit())
    // Pass the level of Quas for the casting unit to the target.
    // This value gets accessed when the target gets damaged.
    set LEVEL[d.targ] = lvl 
    // Add an effect.
    set d.sfx = AddSpecialEffectTarget(SFX_ONE, d.targ, AP_ONE)
    // Add the unit to the Cold Snap group.
    call GroupAddUnit(gr, d.targ)
    call TimerStart(d.tim, DURATION(lvl), false, function Callback)
    set cast = null
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call AddOnDamageFunc(Condition(function OnDamage))
    call Preload(SFX_ONE)
endfunction

endscope


EMP: (see the wiki link about the spell)
Screenshot 1: http://i40.tinypic.com/wre73t.jpg
Screenshot 2: http://i44.tinypic.com/2q07qdk.jpg
JASS:

scope EMP initializer Init 
// Requires TimerUtils, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Period used for changing the size of the EMP unit. 
// Keep it in range 0.10 - 0.20.
    private constant real PERIOD = 0.1
// Attack type of the damaging when EMP explodes.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
// Damage type of the damaging when EMP explodes.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
// Weapon type of the damaging when EMP explodes.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// The duration of the wait between casting EMP and the explosion.
    private constant function DELAY takes integer level returns real
        return 4. - (0.29 * level)
    endfunction
// Mana drained when the ball explodes.
    private constant function MANA_DRAINED takes integer level returns real
        return 57. * level
    endfunction
// Damage dealt when the ball explodes (in this case - 50% of mana drained).
    private constant function DAMAGE takes integer level returns real
        return 0.5 * (MANA_DRAINED(level))
    endfunction
// Area of effect in which enemies get damaged/mana drained.
    private constant function AREA_OF_EFFECT takes integer level returns real
        return 700.
    endfunction
// Scaling of the EMP unit when it&#039;s created.
    private constant function STARTING_SCALING takes integer level returns real
        return 2.5
    endfunction
// The maximum scaling the EMP unit reaches (it grows) before it explodes.
    private constant function MAXIMUM_SCALING takes integer level returns real
        return 5.
    endfunction
// Animation speed of the EMP unit.
    private constant function BALL_ANIMATION_SPEED takes integer level returns real
        return 0.35
    endfunction
// Animation speed of the EMP Blast unit (might want to be a little slower?).
    private constant function BLAST_ANIMATION_SPEED takes integer level returns real
        return 0.25
    endfunction
// Duration of the EMP Blast unit (if you chose a faster effect you could decrease this 
// so it doesn&#039;t get created multiple times).
    private constant function BLAST_DURATION takes integer level returns real
        return 4.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

globals
// Group used for enumerations.
    private group gr = CreateGroup()
// Unit used for FirstOfGroup() and global caster passing for filters.
    private unit fir = null
endglobals

private struct Data
    unit cast
    unit emp
    integer lvl
    location loc
    timer tim 
    integer ticks
    real change 
    real scale
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        set d.cast = cast
        set d.lvl = GetUnitAbilityLevel(d.cast, AID_WEX)
        set d.loc = GetSpellTargetLoc()
        set d.emp = CreateUnitAtLoc(GetOwningPlayer(cast), UID_EMP, d.loc, GetRandomReal(0., 359.))
        set d.tim = NewTimer()
        set d.ticks = R2I(DELAY(d.lvl) / PERIOD)
        set d.change = (MAXIMUM_SCALING(d.lvl) - STARTING_SCALING(d.lvl)) / d.ticks
        set d.scale = STARTING_SCALING(d.lvl)
        call SetUnitScale(d.emp, d.scale, d.scale, d.scale)
        call SetUnitTimeScale(d.emp, BALL_ANIMATION_SPEED(d.lvl))
        //call UnitApplyTimedLife(d.emp, &#039;BTLF&#039;, DELAY(d.lvl))
        call SetTimerData(d.tim, d)
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call RemoveLocation(.loc)
        call ReleaseTimer(.tim)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_EMP
endfunction

private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and GetUnitState(GetFilterUnit(), UNIT_STATE_MANA) &gt; 0. and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local unit blast
    set d.scale = d.scale + d.change
    call SetUnitScale(d.emp, d.scale, d.scale, d.scale)
    set d.ticks = d.ticks - 1
    if d.ticks &lt;= 0 then
        call RemoveUnit(d.emp) // Please don&#039;t bug. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />
        set blast = CreateUnitAtLoc(GetOwningPlayer(d.cast), UID_EMP_BLAST, d.loc, GetRandomReal(0., 359.))
        call SetUnitTimeScale(blast, BLAST_ANIMATION_SPEED(d.lvl))
        call UnitApplyTimedLife(blast, &#039;BTLF&#039;, BLAST_DURATION(d.lvl))
        set blast = null
        set fir = d.cast
        call GroupEnumUnitsInRangeOfLoc(gr, d.loc, AREA_OF_EFFECT(d.lvl), Condition(function GetEnemies))
        loop
            set fir = FirstOfGroup(gr)
            exitwhen fir == null
            if GetWidgetLife(fir) &gt; DAMAGE(d.lvl) then
                // It should &quot;damage&quot; through Tornado and other spells. 
                call SetWidgetLife(fir, GetWidgetLife(fir) - DAMAGE(d.lvl)) 
            else
                call UnitDamageTarget(d.cast, fir, DAMAGE(d.lvl), true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            endif
            call SetUnitState(fir, UNIT_STATE_MANA, GetUnitState(fir, UNIT_STATE_MANA) - MANA_DRAINED(d.lvl))
            call GroupRemoveUnit(gr, fir)
        endloop
        call d.destroy()
    endif
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
endfunction

endscope


Sun Strike: (see the wiki link)
Screenshot: http://i39.tinypic.com/14aydk7.jpg
JASS:

scope SunStrike initializer Init 
// Requires TimerUtils, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
    
// Special effect created at the targeted location.
    private constant string SFX_ONE = &quot;Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl&quot;
endglobals
// Time to wait before Sun Strike damages the targeted area.
    private constant function DELAY takes integer level returns real
        return 1.7
    endfunction
// Damage dealt in the area (which is spread amongst affected foes).
    private constant function DAMAGE takes integer level returns real
        return 12.5 + (level * 62.5)
    endfunction
// Area of effect of the damaging.
    private constant function AREA_OF_EFFECT takes integer level returns real
        return 200.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Group used for enumerations.
    private group gr = CreateGroup()
// Unit used for FirstOfGroup() and global passing of the caster for the filter.
    private unit fir = null
endglobals

private struct Data
    unit cast
    location loc 
    timer tim 
    integer lvl
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        set d.cast = cast
        set d.loc = GetSpellTargetLoc()
        set d.tim = NewTimer()
        set d.lvl = GetUnitAbilityLevel(d.cast, AID_EXORT)
        call SetTimerData(d.tim, d)
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(.tim)
        call RemoveLocation(.loc)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_SUN_STRIKE
endfunction

private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local integer temp
    call GroupEnumUnitsInRangeOfLoc(gr, d.loc, AREA_OF_EFFECT(d.lvl), Condition(function GetEnemies))
    set temp = CountUnitsInGroup(gr)
    if temp &gt; 0 then
        loop
            set fir = FirstOfGroup(gr)
            exitwhen fir == null
            call UnitDamageTarget(d.cast, fir, DAMAGE(d.lvl) / temp, true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            call GroupRemoveUnit(gr, fir)
        endloop
    endif
    call DestroyEffect(AddSpecialEffectLoc(SFX_ONE, d.loc))
    call d.destroy()
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, DELAY(d.lvl), false, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call Preload(SFX_ONE)
endfunction

endscope


(Continues bellow...)
 

Attachments

  • Invoker v0[1].1.w3x
    219.6 KB · Views: 911
Reaction score
91
Chaos Meteor: (see the wiki link)
Screenshot 1: http://i44.tinypic.com/11jrwrd.jpg
Screenshot 2: http://i43.tinypic.com/2rcmq6a.jpg
JASS:

scope ChaosMeteor initializer Init 
// Requires PUI, TimerUtils, BoundSentinel (optional but recommended), InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// This period is used for moving the meteor so keep it in range of 0.02 to 0.05,
// otherwise the movement would look quite choppy and ugly!
    private constant real PERIOD = 0.03125
// Special effect created bellow the meteor while it&#039;s moving. 
// It is NOT created each PERIOD!
    private constant string SFX_ONE = &quot;Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl&quot;
// Special effect created on enemies affected by the dps effect of the meteor.
    private constant string SFX_TWO = &quot;Environment\\LargeBuildingFire\\LargeBuildingFire1.mdl&quot;
// Attachment point of the above effect (SFX_TWO).
    private constant string AP_TWO = &quot;chest&quot;
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// Animation speed of the unit Chaos Meteor Falling. 
    private constant function METEOR_FALLING_ANIMATION_SPEED takes integer wexLvl, integer exortLvl returns real
        return 0.7
    endfunction
// This DELAY time is waited so the Chaos Meteor Falling unit can play it&#039;s animation.
// Of course, it shouldn&#039;t just be a random number because it has to be high or low enough
// so the Chaos Meteor Falling can play it&#039;s full animation. In this case, we wait 1.05 seconds
// so the meteor can hit the ground and THEN create the rolling meteor with all its damaging and such.
    private constant function DELAY takes integer wexLvl, integer exortLvl returns real
        return 1.05
    endfunction
// Speed of the normal meteor (Chaos Meteor). This isn&#039;t &quot;Movement Speed&quot; (as in Object Editor)!
    private constant function METEOR_SPEED takes integer wexLvl, integer exortLvl returns real
        return 8.5
    endfunction
// How much range should the rolling meteor travel until it gets destroyed. 
    private constant function METEOR_TRAVEL_DISTANCE takes integer wexLvl, integer exortLvl returns real
        return 7.5 * wexLvl * wexLvl + 127.500 * wexLvl + 330 // Lol, formula finder.
    endfunction
// Period of damaging of the main meteor (in this case every half second).
    private constant function DAMAGE_PERIOD takes integer wexLvl, integer exortLvl returns real
        return 0.5
    endfunction
// Primary damage dealt by the rolling meteor. I multiply it by the DAMAGE_PERIOD (cut it in half) 
// on purpouse because this way it will deal the half damage the first 0.5 of the second and the
// other half at the last 0.5 second.
    private constant function METEOR_DAMAGE takes integer wexLvl, integer exortLvl returns real
        return (30 * exortLvl + 50) * DAMAGE_PERIOD(wexLvl, exortLvl) 
        // This is supposed to deal the full damage every second.
        // That&#039;s why I multiply it by the period because it shouldn&#039;t deal damage every 0.5 second.
    endfunction
// Area of effect of the Chaos Meteor (in this AoE enemies will get damaged).
    private constant function AREA_OF_EFFECT takes integer wexLvl, integer exortLvl returns real
        return 300.
    endfunction
// Damage per second that is dealt to enemies as a secondary damage. Cannot be stacked.
    private constant function DAMAGE_PER_SECOND takes integer wexLvl, integer exortLvl returns real
        return (30 * exortLvl + 50) * 0.2 // 20% of the main damage.
    endfunction
// Duration of the dps damaging. Staying in the meteor after 3 seconds will renew the damaging.
    private constant function DPS_DURATION takes integer wexLvl, integer exortLvl returns real
        return 3.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

globals
// Group used for enumerating.
    private group gr = CreateGroup()
// Unit used for FirstOfGroup() and global passing of the caster for the filter.
    private unit fir = null
// Group containing all units affected by the dps of the meteor.
    private group dmgGroup = CreateGroup()
endglobals

// Properties attached to affected units. 
// Dps.
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;real&quot;, &quot;DAMAGE&quot;, &quot;0.00&quot;)
// Counter for the duration.
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;integer&quot;, &quot;TIME_LEFT&quot;, &quot;0&quot;)
// Damaging unit... No other way to pass it and still be MUI. Or maybe there is another way.
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;unit&quot;, &quot;DAMAGER&quot;, &quot;null&quot;)
// Effect attached on the damaged unit.
//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;effect&quot;, &quot;EFFECT&quot;, &quot;null&quot;)

private struct Data
    unit cast
    unit meteor
    location loc
    timer tim 
    real angle
    integer ticks
    integer dmgTicks
    integer wex  // Level of Wex.
    integer exort // Level of Exort.
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        local unit meteorFalling 
        set d.cast = cast
        set d.wex = GetUnitAbilityLevel(d.cast, AID_WEX)
        set d.exort = GetUnitAbilityLevel(d.cast, AID_EXORT)
        set d.loc = GetSpellTargetLoc()
        // I can&#039;t work with radians, sorry. Not so high level of maths at school. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite3" alt=":(" title="Frown    :(" loading="lazy" data-shortname=":(" />
        set d.angle = bj_RADTODEG * Atan2(GetLocationY(d.loc) - GetUnitY(d.cast), GetLocationX(d.loc) - GetUnitX(d.cast))
        set meteorFalling = CreateUnitAtLoc(GetOwningPlayer(d.cast), UID_CHAOS_METEOR_FALLING, d.loc, d.angle)
        call SetUnitTimeScale(meteorFalling, METEOR_FALLING_ANIMATION_SPEED(d.wex, d.exort))
        call UnitApplyTimedLife(meteorFalling, &#039;BTLF&#039;, DELAY(d.wex, d.exort))
        set d.tim = NewTimer()
        set d.ticks = R2I(METEOR_TRAVEL_DISTANCE(d.wex, d.exort) / METEOR_SPEED(d.wex, d.exort))
        set d.dmgTicks = R2I(DAMAGE_PERIOD(d.wex, d.exort) / PERIOD)
        call SetTimerData(d.tim, d)
        set meteorFalling = null
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call RemoveLocation(.loc)
        call ReleaseTimer(.tim)
        call KillUnit(.meteor)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_CHAOS_METEOR
endfunction

private function ForDPSGroup takes nothing returns nothing
    local unit first = GetEnumUnit()
    call UnitDamageTarget(DAMAGER[first], first, DAMAGE[first], true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
    set TIME_LEFT[first] = TIME_LEFT[first] - 1
    if TIME_LEFT[first] &lt;= 0 then
        call GroupRemoveUnit(dmgGroup, first)
        call DestroyEffect(EFFECT[first])
        set EFFECT[first] = null
        set DAMAGER[first] = null
        // The other properties don&#039;t need changing since they&#039;ll be 
        // replaced next time the unit is hit by the meteor.
    endif
    set first = null
endfunction

private function DamagePerSecond takes nothing returns nothing
    call ForGroup(dmgGroup, function ForDPSGroup)
endfunction
    
private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), 

UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function MoveMeteor takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local real x = GetUnitX(d.meteor) + METEOR_SPEED(d.wex, d.exort) * Cos(d.angle * bj_DEGTORAD)
    local real y = GetUnitY(d.meteor) + METEOR_SPEED(d.wex, d.exort) * Sin(d.angle * bj_DEGTORAD)
    call SetUnitX(d.meteor, x)
    call SetUnitY(d.meteor, y)
    set d.dmgTicks = d.dmgTicks - 1
    if d.dmgTicks &lt;= 0 then
        set d.dmgTicks = R2I(DAMAGE_PERIOD(d.wex, d.exort) / PERIOD)
        call DestroyEffect(AddSpecialEffect(SFX_ONE, x, y))
        // Just for passing, we don&#039;t need another global variable just for that.
        set fir = d.cast
        call GroupEnumUnitsInRange(gr, x, y, AREA_OF_EFFECT(d.wex, d.exort), Condition(function GetEnemies))
        loop
            set fir = FirstOfGroup(gr)
            exitwhen fir == null
            call UnitDamageTarget(d.cast, fir, METEOR_DAMAGE(d.wex, d.exort), true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            if not IsUnitInGroup(fir, dmgGroup) then
                call GroupAddUnit(dmgGroup, fir)
                set TIME_LEFT[fir] = R2I(DPS_DURATION(d.wex, d.exort))
                set DAMAGE[fir] = DAMAGE_PER_SECOND(d.wex, d.exort)
                set DAMAGER[fir] = d.cast
                set EFFECT[fir] = AddSpecialEffectTarget(SFX_TWO, fir, AP_TWO)
            endif
            call GroupRemoveUnit(gr, fir)
        endloop
    endif
    set d.ticks = d.ticks - 1
    if d.ticks &lt;= 0 then
        // Okay, the meteor travelled it&#039;s maximum range. 
        call d.destroy()
    endif
    set tim = null
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    set d.meteor = CreateUnitAtLoc(GetOwningPlayer(d.cast), UID_CHAOS_METEOR, d.loc, d.angle)
    call DestroyEffect(AddSpecialEffectLoc(SFX_ONE, d.loc))
    call TimerStart(d.tim, PERIOD, true, function MoveMeteor)
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, DELAY(d.wex, d.exort), false, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call TimerStart(CreateTimer(), 1., true, function DamagePerSecond)
    call Preload(SFX_ONE)
    call Preload(SFX_TWO)
endfunction

endscope


Alacrity: (see the wiki link)
Screenshot: http://i43.tinypic.com/j6u712.jpg
JASS:

scope Alacrity initializer Init 
// Requires TimerUtils, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Special effect attached on unit affected with Alacrity.
    private constant string SFX_ONE = &quot;Abilities\\Spells\\Items\\AIsp\\SpeedTarget.mdl&quot;
// Attachment point of the above effect (SFX_ONE)
    private constant string AP_ONE = &quot;overhead&quot;
endglobals
// Duration of the bonuses from Alacrity.
    private constant function DURATION takes integer wexLvl, integer exortLvl returns real
        return 6.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

private struct Data
    unit targ
    effect sfx
    timer tim 
    integer wex
    integer exort
    
    static method create takes unit targ returns Data
        local Data d = Data.allocate()
        local unit cast = GetTriggerUnit()
        // Get Exort and Wex&#039;s levels.
        set d.wex = GetUnitAbilityLevel(cast, AID_WEX)
        set d.exort = GetUnitAbilityLevel(cast, AID_EXORT)
        set d.targ = targ
        set d.sfx = AddSpecialEffectTarget(SFX_ONE, d.targ, AP_ONE)
        // Add the abilities that give bonuses and set their level depending
        // on the level of Wex and Exort.
        call UnitAddAbility(d.targ, AID_ALACRITY_DAMAGE_BONUS)
        call UnitAddAbility(d.targ, AID_ALACRITY_SPEED_BONUS)
        call SetUnitAbilityLevel(d.targ, AID_ALACRITY_DAMAGE_BONUS, d.exort)
        call SetUnitAbilityLevel(d.targ, AID_ALACRITY_SPEED_BONUS, d.wex)
        set d.tim = NewTimer()
        call SetTimerData(d.tim, d)
        set cast = null
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        // Remove the bonuses when the spell ends.
        call UnitRemoveAbility(.targ, AID_ALACRITY_DAMAGE_BONUS)
        call UnitRemoveAbility(.targ, AID_ALACRITY_SPEED_BONUS)
        call DestroyEffect(.sfx)
        call ReleaseTimer(.tim)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ALACRITY
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    call d.destroy()
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetSpellTargetUnit())
    call TimerStart(d.tim, DURATION(d.wex, d.exort), false, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call Preload(SFX_ONE)
endfunction

endscope


Tornado: (see the wiki link)
Screenshot: http://i39.tinypic.com/10ynfgz.jpg
JASS:

scope Tornado initializer Init 
// Requires TimerUtils, GroupUtils, BoundSentinel (optional but recommended), InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// This period is used for moving the tornado so keep it in range of 0.02 to 0.04,
// otherwise the movement would look quite choppy and ugly!
    private constant real PERIOD = 0.03125
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// Speed of the tornado (this is NOT &quot;Movement Speed&quot; as in Object Editor).
    private constant function SPEED takes integer quasLvl, integer wexLvl returns real
        return 28.
    endfunction
// Distance travelled by the tornado.
    private constant function DISTANCE takes integer quasLvl, integer wexLvl returns real 
        return 410. * wexLvl + 230.
    endfunction
// Area of effect of the tornado.
    private constant function AREA_OF_EFFECT takes integer quasLvl, integer wexLvl returns real
        return 200.
    endfunction
// Duration of the cycloning. REMEMBER, IT MUST MATCH THE DURATION FROM THE ABILITY
// TORNADO CYCLONE, OTHERWISE IT WOULDN&#039;T DEAL DAMAGE AT ALL. 
    private constant function CYCLONE_DURATION takes integer quasLvl, integer wexLvl returns real
        return 0.000 * quasLvl * quasLvl + 0.250 * quasLvl + 0.350 // Lol, formula finder.
    endfunction
// Damage dealt by the tornado when the units get back on the ground.
    private constant function DAMAGE takes integer quasLvl, integer wexLvl returns real
        return 42.5 * wexLvl + 77.5
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

globals
// Unit used for FirstOfGroup() and global passing of the caster for the filter.
    private unit fir = null
endglobals

private struct Damage
    unit cast
    unit targ
    real dmg
    timer tim 
    
    static method create takes unit cast, unit targ, real dmg returns Damage
        local Damage g = Damage.allocate()
        set g.cast = cast
        set g.targ = targ
        set g.dmg = dmg
        set g.tim = NewTimer()
        call SetTimerData(g.tim, g)
        return g
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(.tim)
    endmethod
endstruct

private struct Data
    unit cast
    unit tornado
    real angle
    integer ticks 
    timer tim
    group gr
    integer quas
    integer wex
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        local location loc = GetSpellTargetLoc()
        set d.cast = cast
        set d.quas = GetUnitAbilityLevel(d.cast, AID_QUAS)
        set d.wex = GetUnitAbilityLevel(d.cast, AID_WEX)
        // I can&#039;t work with radians, sorry. Not so high level of maths at school. <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite3" alt=":(" title="Frown    :(" loading="lazy" data-shortname=":(" />
        set d.angle = bj_RADTODEG * Atan2(GetLocationY(loc) - GetUnitY(d.cast), GetLocationX(loc) - GetUnitX(d.cast))
        set d.tornado = CreateUnit(GetOwningPlayer(d.cast), UID_TORNADO, GetUnitX(d.cast), GetUnitY(d.cast), d.angle)
        set d.ticks = R2I(DISTANCE(d.quas, d.wex) / SPEED(d.quas, d.wex))
        set d.tim = NewTimer()
        set d.gr = NewGroup()
        call SetTimerData(d.tim, d)
        call RemoveLocation(loc)
        set loc = null
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseGroup(.gr)
        call ReleaseTimer(.tim)
        call SetUnitTimeScale(.tornado, 3.) // &lt;-- Because the tornado dies too slow...
        call KillUnit(.tornado)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_TORNADO
endfunction

private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), 

UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function DamageAfterCyclone takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Damage g = GetTimerData(tim)
    call UnitDamageTarget(g.cast, g.targ, g.dmg, true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
    call g.destroy()
    set tim = null
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local real x = GetUnitX(d.tornado) + SPEED(d.quas, d.wex) * Cos(d.angle * bj_DEGTORAD)
    local real y = GetUnitY(d.tornado) + SPEED(d.quas, d.wex) * Sin(d.angle * bj_DEGTORAD)
    local unit dum
    local Damage g
    call SetUnitX(d.tornado, x)
    call SetUnitY(d.tornado, y)
    set fir = d.cast
    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, AREA_OF_EFFECT(d.quas, d.wex), Condition(function GetEnemies))
    loop
        set fir = FirstOfGroup(ENUM_GROUP)
        exitwhen fir == null
        if not IsUnitInGroup(fir, d.gr) then
            call GroupAddUnit(d.gr, fir)
            set dum = CreateUnit(GetOwningPlayer(d.cast), UID_DUMMY, x, y, 0.)
            call UnitAddAbility(dum, AID_TORNADO_CYCLONE)
            call UnitAddAbility(dum, &#039;Aloc&#039;)
            call SetUnitAbilityLevel(dum, AID_TORNADO_CYCLONE, d.quas)
            call IssueTargetOrder(dum, OID_TORNADO_CYCLONE, fir)
            call UnitApplyTimedLife(dum, &#039;BTLF&#039;, 0.5)
            set g = Damage.create(d.cast, fir, DAMAGE(d.quas, d.wex))
            // Requires a tiny little wait so the cyclone can fully finish.
            // (Because you can&#039;t damage invulnerable units)
            call TimerStart(g.tim, CYCLONE_DURATION(d.quas, d.wex) + 0.2, false, function DamageAfterCyclone)
            set dum = null
        endif
        call GroupRemoveUnit(ENUM_GROUP, fir)
    endloop
    set d.ticks = d.ticks - 1
    if d.ticks &lt;= 0 then
        // The tornado reached its maximum distance.
        call d.destroy()
    endif
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
endfunction

endscope


Deafening Blast: (see the wiki link)
Screenshot: http://i43.tinypic.com/2iloc0.jpg
JASS:

scope DeafeningBlast initializer Init 
// Requires TimerUtils, GroupUtils, BoundSentinel (optional but recommended), InvokerConfig

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// This period is used for moving the blast and for the knockback so keep it 
// in range of 0.02 to 0.04, otherwise the movement would look quite choppy and ugly!
    private constant real PERIOD = 0.03125
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
// Effect attached to units that get affected by the deafening blast.
    private constant string SFX_ONE = &quot;Abilities\\Spells\\Other\\TalkToMe\\TalkToMe.mdl&quot;
// Attachment point of the above effect (SFX_ONE).
    private constant string AP_ONE = &quot;overhead&quot;
endglobals
// Speed of the Deafening Blast (this is NOT &quot;Movement Speed&quot; as in Object Editor).
    private constant function SPEED takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 30.
    endfunction
// Distance travelled by the Deafening Blast.
    private constant function DISTANCE takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 1000.
    endfunction
// Starting area of effect of the Deafening Blast (it gets increased).
    private constant function STARTING_AREA_OF_EFFECT takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 200.
    endfunction
// End area of effect of the Deafening Blast (it gets increased to this value).
    private constant function END_AREA_OF_EFFECT takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 250.
    endfunction
// Damage dealt by the Deafening Blast to each unit hit by it. 
    private constant function DAMAGE takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return exortLvl * 40.
    endfunction
// Duration of the attack prevention of affected units.
    private constant function ATTACK_PREVENTION_DURATION takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 0.5 * wexLvl + 0.5
    endfunction
// Duration of the knockback when a unit is hit by the Deafening Blast.
    private constant function KNOCKBACK_DURATION takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return quasLvl * 0.25
    endfunction
// Knockback speed of units hit by the Deafening Blast (this is NOT &quot;Movement Speed&quot; as in Object Editor).
    private constant function KNOCKBACK_SPEED takes integer quasLvl, integer wexLvl, integer exortLvl returns real
        return 6.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

globals
// Unit used for FirstOfGroup() and global passing of the caster for the filter.
    private unit fir = null
endglobals

private struct Attack
    unit targ
    timer tim 
    effect sfx
    
    static method create takes unit targ returns Attack
        local Attack a = Attack.allocate()
        set a.targ = targ
        set a.tim = NewTimer()
        set a.sfx = AddSpecialEffectTarget(SFX_ONE, a.targ, AP_ONE)
        call SetTimerData(a.tim, a)
        return a
    endmethod
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect(.sfx)
        call ReleaseTimer(.tim)
    endmethod
endstruct

private struct Knockback
    unit targ
    real angle
    timer tim
    integer quas
    integer wex
    integer exort
    integer ticks
    
    static method create takes unit blast, unit targ, integer quas, integer wex, integer exort returns Knockback
        local Knockback b = Knockback.allocate()
        set b.targ = targ
        set b.quas = quas
        set b.wex = wex
        set b.exort = exort
        set b.angle = bj_RADTODEG * Atan2(GetUnitY(b.targ) - GetUnitY(blast), GetUnitX(b.targ) - GetUnitX(blast))
        set b.ticks = R2I(KNOCKBACK_DURATION(b.quas, b.wex, b.exort) / PERIOD)
        set b.tim = NewTimer()
        call SetTimerData(b.tim, b)
        return b
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(.tim)
    endmethod
endstruct
        
private struct Data
    unit cast
    unit blast
    timer tim
    group gr
    real angle
    real currentAoe
    real increment
    integer ticks
    integer quas
    integer wex
    integer exort
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        local location loc = GetSpellTargetLoc()
        set d.cast = cast
        set d.quas = GetUnitAbilityLevel(d.cast, AID_QUAS)
        set d.wex = GetUnitAbilityLevel(d.cast, AID_WEX)
        set d.exort = GetUnitAbilityLevel(d.cast, AID_EXORT)
        set d.angle = bj_RADTODEG * Atan2(GetLocationY(loc) - GetUnitY(d.cast), GetLocationX(loc) - GetUnitX(d.cast))
        set d.blast = CreateUnit(GetOwningPlayer(d.cast), UID_DEAFENING_BLAST, GetUnitX(d.cast), GetUnitY(d.cast), d.angle)
        set d.ticks = R2I(DISTANCE(d.quas, d.wex, d.exort) / SPEED(d.quas, d.wex, d.exort))
        // Keeping track of the area of effect so it can get increased.
        set d.currentAoe = STARTING_AREA_OF_EFFECT(d.quas, d.wex, d.exort)
        // The growing of the area of effect each tick.
        set d.increment = (END_AREA_OF_EFFECT(d.quas, d.wex, d.exort) - STARTING_AREA_OF_EFFECT(d.quas, d.wex, d.exort)) / d.ticks
        set d.tim = NewTimer()
        set d.gr = NewGroup()
        call SetTimerData(d.tim, d)
        call RemoveLocation(loc)
        set loc = null
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(.tim)
        call ReleaseGroup(.gr)
        call KillUnit(.blast)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_DEAFENING_BLAST
endfunction

private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), 

UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function KnockCallback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Knockback b = GetTimerData(tim)
    local real x = GetUnitX(b.targ) + KNOCKBACK_SPEED(b.quas, b.wex, b.exort) * Cos(b.angle * bj_DEGTORAD)
    local real y = GetUnitY(b.targ) + KNOCKBACK_SPEED(b.quas, b.wex, b.exort) * Sin(b.angle * bj_DEGTORAD)
    call SetUnitPosition(b.targ, x, y)
    set b.ticks = b.ticks - 1
    if b.ticks &lt;= 0 or IsUnitType(b.targ, UNIT_TYPE_DEAD) == true then
        call b.destroy()
    endif
    set tim = null
endfunction

private function EnableAttack takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Attack a = GetTimerData(tim)
    call UnitRemoveAbility(a.targ, AID_DEAFENING_BLAST_DISABLE_ATTACK)
    call a.destroy()
    set tim = null
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local real x = GetUnitX(d.blast) + SPEED(d.quas, d.wex, d.exort) * Cos(d.angle * bj_DEGTORAD)
    local real y = GetUnitY(d.blast) + SPEED(d.quas, d.wex, d.exort) * Sin(d.angle * bj_DEGTORAD)
    local Attack a 
    local Knockback b
    local unit dum
    call SetUnitX(d.blast, x)
    call SetUnitY(d.blast, y)
    set fir = d.cast
    set d.currentAoe = d.currentAoe + d.increment
    call GroupEnumUnitsInRange(ENUM_GROUP, x, y, d.currentAoe, Condition(function GetEnemies))
    loop
        set fir = FirstOfGroup(ENUM_GROUP)
        exitwhen fir == null
        if not IsUnitInGroup(fir, d.gr) then
            call GroupAddUnit(d.gr, fir)
            set a = Attack.create(fir)
            call UnitAddAbility(fir, AID_DEAFENING_BLAST_DISABLE_ATTACK)
            call TimerStart(a.tim, ATTACK_PREVENTION_DURATION(d.quas, d.wex, d.exort), false, function EnableAttack)
            set b = Knockback.create(d.blast, fir, d.quas, d.wex, d.exort)
            call TimerStart(b.tim, PERIOD, true, function KnockCallback)
            call UnitDamageTarget(d.cast, fir, DAMAGE(d.quas, d.wex, d.exort), true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            if IsUnitType(fir, UNIT_TYPE_DEAD) == false then
                set dum = CreateUnit(GetOwningPlayer(d.cast), UID_DUMMY, x, y, 0.)
                call UnitAddAbility(dum, AID_DEAFENING_BLAST_SILENCE)
                call UnitAddAbility(dum, &#039;Aloc&#039;)
                call SetUnitAbilityLevel(dum, AID_DEAFENING_BLAST_SILENCE, d.quas)
                call IssueTargetOrder(dum, OID_DEAFENING_BLAST_SILENCE, fir)
                call UnitApplyTimedLife(dum, &#039;BTLF&#039;, 0.5)
            endif
        endif
        call GroupRemoveUnit(ENUM_GROUP, fir)
    endloop
    set d.ticks = d.ticks - 1
    if d.ticks &lt;= 0 then
        call d.destroy()
    endif
    set tim = null
    set dum = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call Preload(SFX_ONE)
endfunction

endscope


Ice Wall: (see the wiki link)
Screenshot: http://i41.tinypic.com/iz84ft.jpg
JASS:

scope IceWall initializer Init 
// Requires TimerUtils, LineSegment, xebasic, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// This period is used for checking units around the wall so use a low period around 0.1 and 0.3,
// otherwise it wouldn&#039;t work as good as you want.
    private constant real PERIOD = 0.1
// Attack type of the damaging.
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
// Damage type of the damaging.
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
// Weapon type of the damaging.
    private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// How much range in front of the caster should the wall be created.
    private constant function CREATION_RANGE takes integer quasLvl, integer exortLvl returns real
        return 200.
    endfunction
// Area of effect of the Ice Wall. In this area units will be damaged. 
// THE AREA OF EFFECT OF THE SLOW DEPENDS ON THE AREA OF EFFECT OF THE AURA IN OBJECT EDITOR
// SO THAT&#039;S WHY YOU WOULD NEED TO MAKE BOTH THE SAME.
    private constant function AREA_OF_EFFECT takes integer quasLvl, integer exortLvl returns real
        return 150.
    endfunction
// Number of Ice Wall units created (ice blocks, since that is their current model).
    private constant function NUMBER_OF_WALL_UNITS takes integer quasLvl, integer exortLvl returns integer
        return 9
    endfunction
// Distance between the wall units (shouldn&#039;t be really high so it could be visually good).
    private constant function DISTANCE_BETWEEN_WALL_UNITS takes integer quasLvl, integer exortLvl returns real
        return 95.
    endfunction
// Duration of the wall.
    private constant function WALL_DURATION takes integer quasLvl, integer exortLvl returns real
        return 1.5 * quasLvl + 1.5
    endfunction
// Period between damaging units in the wall.
    private constant function DAMAGE_PERIOD takes integer quasLvl, integer exortLvl returns real
        // DPS - damage per second. You can change it to something like 0.65
        // and it will damage units every 0.65 second.
        return 1.
    endfunction
// Damage dealt every DAMAGE_PERIOD().
    private constant function DAMAGE takes integer quasLvl, integer exortLvl returns real
        return exortLvl * 6.
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

globals
// Group used for enumerating untis.
    private group gr = CreateGroup()
// Unit used for FirstOfGroup() and global passing of the caster for the filter.
    private unit fir = null
endglobals

private struct Data
    unit cast
    real x1
    real y1
    real x2
    real y2
    timer tim 
    integer ticks
    integer dmgTicks
    integer quas
    integer exort
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        set d.cast = cast
        set d.quas = GetUnitAbilityLevel(d.cast, AID_QUAS)
        set d.exort = GetUnitAbilityLevel(d.cast, AID_EXORT)
        set d.ticks = R2I(WALL_DURATION(d.quas, d.exort) / PERIOD)
        set d.dmgTicks = R2I(DAMAGE_PERIOD(d.quas, d.exort) / PERIOD)
        set d.tim = NewTimer()
        call SetTimerData(d.tim, d)
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        call ReleaseTimer(.tim)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_ICE_WALL
endfunction

private function GetEnemies takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(fir)) and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), 

UNIT_TYPE_GROUND) == true and IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    local real dd = NUMBER_OF_WALL_UNITS(d.quas, d.exort) * DISTANCE_BETWEEN_WALL_UNITS(d.quas, d.exort)
    set d.dmgTicks = d.dmgTicks - 1
    if d.dmgTicks &lt;= 0 then
        set d.dmgTicks = R2I(DAMAGE_PERIOD(d.quas, d.exort) / PERIOD)
        set fir = d.cast
        // Get units around the wall.
        call GroupEnumUnitsInRangeOfSegment(gr, d.x1, d.y1, d.x2, d.y2, AREA_OF_EFFECT(d.quas, d.exort), Condition(function GetEnemies))
        loop
            set fir = FirstOfGroup(gr)
            exitwhen fir == null
            call UnitDamageTarget(d.cast, fir, 50., true, true, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
            call GroupRemoveUnit(gr, fir)
        endloop
    endif
    set d.ticks = d.ticks - 1
    if d.ticks &lt;= 0 then
        call d.destroy()
    endif
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local Data d = Data.create(cast)
    local real x = GetUnitX(cast)
    local real y = GetUnitY(cast)
    local real face = GetUnitFacing(cast)
    local real tx = x + CREATION_RANGE(d.quas, d.exort) * Cos(face * bj_DEGTORAD)
    local real ty = y + CREATION_RANGE(d.quas, d.exort) * Sin(face * bj_DEGTORAD)
    local real dd = NUMBER_OF_WALL_UNITS(d.quas, d.exort) * DISTANCE_BETWEEN_WALL_UNITS(d.quas, d.exort)
    local real angle = Atan2(ty - y, tx - x)
    local integer a = 0
    local unit block
    set x = Cos(angle + bj_PI / 2) * dd
    set y = Sin(angle + bj_PI / 2) * dd
    set tx = tx + x / 2
    set ty = ty + y / 2
    set x = x / NUMBER_OF_WALL_UNITS(d.quas, d.exort)
    set y = y / NUMBER_OF_WALL_UNITS(d.quas, d.exort)
    loop
        //exitwhen a &gt; NUMBER_OF_WALL_UNITS(d.quas, d.exort)
        set tx = tx - x
        set ty = ty - y
        set block = CreateUnit(GetOwningPlayer(cast), UID_ICE_WALL, tx, ty, GetRandomReal(0., 359.))
        if a == 0 then
            set d.x1 = tx
            set d.y1 = ty
        endif
        // I think it&#039;s useless but I&#039;ve seen other people do it in their wall spells.
        // Maybe it has something to do with pathing, I don&#039;t know exactly. Still, it doesn&#039;t hurt.
        call SetUnitX(block, tx) 
        call SetUnitY(block, ty)
        call UnitAddAbility(block, AID_ICE_WALL_SLOW_AURA)
        call SetUnitAbilityLevel(block, AID_ICE_WALL_SLOW_AURA, d.quas)
        call UnitApplyTimedLife(block, &#039;BTLF&#039;, WALL_DURATION(d.quas, d.exort))
        exitwhen a == NUMBER_OF_WALL_UNITS(d.quas, d.exort)
        set a = a + 1
    endloop
    set d.x2 = tx
    set d.y2 = ty
    call TimerStart(d.tim, PERIOD, true, function Callback)
    set block = null
    set cast = null
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
endfunction

endscope


Ghost Walk:
Screenshot: http://i44.tinypic.com/v5aa9s.jpg
JASS:

scope GhostWalk initializer Init 
// Requires TimerUtils, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// This period is used for checking whether the Hero is in Ghost Walk
// So keep it in range 0.02-0.10 for maximum accuracies.
    private constant real PERIOD = 0.03125
endglobals
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================
private struct Data
    unit cast
    unit dum
    integer quas
    integer wex
    timer tim
    
    static method create takes unit cast returns Data
        local Data d = Data.allocate()
        set d.cast = cast
        set d.quas = GetUnitAbilityLevel(d.cast, AID_QUAS)
        set d.wex = GetUnitAbilityLevel(d.cast, AID_WEX)
        set d.dum = CreateUnit(GetOwningPlayer(d.cast), UID_DUMMY, GetUnitX(d.cast), GetUnitY(d.cast), 0.)
        set d.tim = NewTimer()
        call UnitAddAbility(d.dum, &#039;Aloc&#039;)
        call UnitAddAbility(d.dum, AID_GHOST_WALK_AURA)
        call UnitAddAbility(d.dum, AID_GHOST_WALK_SELF_SLOW)
        call SetUnitAbilityLevel(d.dum, AID_GHOST_WALK_AURA, d.quas)
        call SetUnitAbilityLevel(d.dum, AID_GHOST_WALK_SELF_SLOW, d.wex)
        call IssueTargetOrder(d.dum, OID_GHOST_WALK_SELF_SLOW, d.cast)
        call SetTimerData(d.tim, d)
        return d
    endmethod
    
    method onDestroy takes nothing returns nothing
        // Remove the self-slow.
        call UnitRemoveAbility(.cast, BID_GHOST_WALK_SELF_SLOW)
        call KillUnit(.dum)
        call ReleaseTimer(.tim)
    endmethod
endstruct

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_GHOST_WALK
endfunction

private function Callback takes nothing returns nothing
    local timer tim = GetExpiredTimer()
    local Data d = GetTimerData(tim)
    call SetUnitX(d.dum, GetUnitX(d.cast))
    call SetUnitY(d.dum, GetUnitY(d.cast))
    // Did caster break invisibility or did he die?
    if GetUnitAbilityLevel(d.cast, BID_GHOST_WALK) &lt; 1 or IsUnitType(d.cast, UNIT_TYPE_DEAD) == true then
        call d.destroy()
    endif
    set tim = null
endfunction

private function Actions takes nothing returns nothing
    local Data d = Data.create(GetTriggerUnit())
    call TimerStart(d.tim, PERIOD, true, function Callback)
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
endfunction

endscope


Forge Spirit: (see the wiki link)
Screenshot: http://i41.tinypic.com/1z566ms.jpg
JASS:

scope ForgeSpirit initializer Init 
// Requires PUI, LightLeaklessDamageDetect, InvokerConfig.

//===============================================================================
// C O N F I G U R A T I O N     M E N U
//===============================================================================
globals
// Effect created upon forged spirits when they are spawned.
    private constant string SFX_ONE = &quot;Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl&quot;
// Maximum armor reduction EVER by the melting armor ability. MUST MATCH OBJECT EDITOR! 
    private constant integer MAX_ARMOR_MELT_LEVEL = 10
endglobals
// Level needed for Quas and for Exort so 2 Spirits can be forged.
// For example: quas = 3 and exort = 4 --&gt; 1 spirit.
//              quas = 4 (and above) and exort = 4 (and above) --&gt; 2 spirits.
    private constant function LEVEL_NEEDED takes integer quasLvl, integer exortLvl returns integer
        return 4
    endfunction
// Duration of the spirits.
    private constant function SPIRIT_DURATION takes integer quasLvl, integer exortLvl returns real
        return 10. + (quasLvl * 10.)
    endfunction
//===============================================================================
// E N D     O F     C O N F I G U R A T I O N     M E N U
//===============================================================================

//! runtextmacro PUI_PROPERTY(&quot;private&quot;, &quot;integer&quot;, &quot;TIME&quot;, &quot;0&quot;)

globals
// Group that contains all units 
    private group gr = CreateGroup()
// Group used for enumerating units.
    private group temp = CreateGroup()
// Unit used for FirstOfGroup() loops.
    private unit fir = null
endglobals

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AID_FORGE_SPIRIT
endfunction

private function ForTimeHandlerGroup takes nothing returns nothing
    local unit fir = GetEnumUnit()
    if TIME[fir] &gt; 0 then
        set TIME[fir] = TIME[fir] - 1
    else
        call GroupRemoveUnit(gr, fir)
        call UnitRemoveAbility(fir, AID_FORGE_SPIRIT_ARMOR_MELT)
    endif
    set fir = null
endfunction

private function TimeHandler takes nothing returns nothing
    call ForGroup(gr, function ForTimeHandlerGroup)
endfunction

private function OnDamage takes nothing returns boolean
    local unit spirit = GetEventDamageSource()
    local unit targ = GetTriggerUnit()
    local integer a = 1
    loop
        exitwhen SPIRIT[a] == null
        if GetUnitTypeId(spirit) == SPIRIT[a] and IsUnitType(targ, UNIT_TYPE_HERO) == true then
            set TIME[targ] = 5
            if not IsUnitInGroup(targ, gr) then
                call GroupAddUnit(gr, targ)
            endif
            // Don&#039;t have the spell that reduces armor?
            if GetUnitAbilityLevel(targ, AID_FORGE_SPIRIT_ARMOR_MELT) &lt; 1 then
                call UnitAddAbility(targ, AID_FORGE_SPIRIT_ARMOR_MELT)
            else
                // If the unit has it, then check 
                if GetUnitAbilityLevel(targ, AID_FORGE_SPIRIT_ARMOR_MELT) &lt; MAX_ARMOR_MELT_LEVEL then
                    call IncUnitAbilityLevel(targ, AID_FORGE_SPIRIT_ARMOR_MELT)
                endif
            endif
        endif
        set a = a + 1
    endloop
    set spirit = null
    set targ = null
    return false
endfunction

// Funny filter, but that&#039;s the easiest way to check if the enumed unit is a Spirit,
// otherwise that line would be come a little long with all those OR operations...
private function GetSpirits takes nothing returns boolean
    local integer a = 1
    if GetOwningPlayer(GetFilterUnit()) == GetOwningPlayer(GetTriggerUnit()) then
        loop
            exitwhen SPIRIT[a] == null
            if GetUnitTypeId(GetFilterUnit()) == SPIRIT[a] then
                return true
            endif
            set a = a + 1
        endloop
    endif
    return false
endfunction

private function Actions takes nothing returns nothing
    local unit cast = GetTriggerUnit()
    local integer exort = GetUnitAbilityLevel(cast, AID_EXORT)
    local integer quas = GetUnitAbilityLevel(cast, AID_QUAS)
    // Get and remove all currently existing spirits.
    call GroupEnumUnitsInRange(temp, GetUnitX(cast), GetUnitY(cast), 999999., Condition(function GetSpirits))
    loop
        set fir = FirstOfGroup(temp)
        exitwhen fir == null
        call KillUnit(fir)
        call GroupRemoveUnit(temp, fir)
    endloop
    // Create a spirit.
    set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(cast), SPIRIT[exort], GetUnitX(cast), GetUnitY(cast), GetRandomReal(0., 359.))
    call UnitApplyTimedLife(bj_lastCreatedUnit, &#039;BTLF&#039;, SPIRIT_DURATION(quas, exort))
    call UnitAddAbility(bj_lastCreatedUnit, AID_FORGE_SPIRIT_ARMOR_BONUS)
    call UnitAddAbility(bj_lastCreatedUnit, AID_FORGE_SPIRIT_MANA_BONUS)
    call SetUnitAbilityLevel(bj_lastCreatedUnit, AID_FORGE_SPIRIT_MANA_BONUS, quas)
    call SetUnitAbilityLevel(bj_lastCreatedUnit, AID_FORGE_SPIRIT_ARMOR_BONUS, quas)
    call DestroyEffect(AddSpecialEffect(SFX_ONE, GetUnitX(bj_lastCreatedUnit), GetUnitY(bj_lastCreatedUnit)))
    // If Quas and Exort are both at/above the level needed for 2 spirits, then create another spirit.
    if exort &gt;= LEVEL_NEEDED(quas, exort) and quas &gt;= LEVEL_NEEDED(quas, exort) then
        set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(cast), SPIRIT[exort], GetUnitX(cast), GetUnitY(cast), GetRandomReal(0., 359.))
        call UnitApplyTimedLife(bj_lastCreatedUnit, &#039;BTLF&#039;, SPIRIT_DURATION(quas, exort))
        call UnitAddAbility(bj_lastCreatedUnit, AID_FORGE_SPIRIT_ARMOR_BONUS)
        call UnitAddAbility(bj_lastCreatedUnit, AID_FORGE_SPIRIT_MANA_BONUS)
        call SetUnitAbilityLevel(bj_lastCreatedUnit, AID_FORGE_SPIRIT_MANA_BONUS, quas)
        call SetUnitAbilityLevel(bj_lastCreatedUnit, AID_FORGE_SPIRIT_ARMOR_BONUS, quas)
        call DestroyEffect(AddSpecialEffect(SFX_ONE, GetUnitX(bj_lastCreatedUnit), GetUnitY(bj_lastCreatedUnit)))
    endif
    set cast = null
endfunction

private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)
    call AddOnDamageFunc(Condition(function OnDamage))
    call TimerStart(CreateTimer(), 1., true, function TimeHandler)
    call Preload(SFX_ONE)
endfunction

endscope


Some random screenshots with combinations:
http://i42.tinypic.com/1ou96w.jpg
http://i43.tinypic.com/2vkz1hu.jpg
 
Reaction score
91
> How long have you been making him? =P
Dunno, about two-three days. Didn't think it would be that huge in the end (mostly the object editor part).
 

Furby

Current occupation: News poster
Reaction score
144
Really nice job! :)
It was really a lot of work. :rolleyes:
Well done, keep it up. :thup:
 

Romek

Super Moderator
Reaction score
963
Seems like I can't post a funny message without generating tonnes of spam.
I deleted my message, as well as the spam, so if you're wondering where they went, there's your answer.

As for the spell, I'll look over it later.
Unless some other mod is up for the job? :p
 
Reaction score
91
> I deleted my message, as well as the spam,
Spam? Didn't see anything, I just entered the site again.

> As for the spell, I'll look over it later.
Okay, I guess they aren't that poorly written ...
 

BlackRose

Forum User
Reaction score
239
Wow! So much! Was it boring? Anyways DotA should use this instead of their own coding style XD....

Offtopic:
Jesus4Lyf you are from Australia also?
 
Reaction score
91
> May I quietly encourage you to be more creative instead of recreating DotA spells?
What's wrong with that? I'm not really creative when it comes to new spells and such so I rarely submit anything custom.

> Was it boring?
No, not at all since I wanted to submit it as soon as possible. :p
 

BlackRose

Forum User
Reaction score
239
One thing, not everyone knows DotA, so a screenshot would help. Just my opinion. Even though there are links -.-
 

kingkingyyk3

Visitor (Welcome to the Jungle, Baby!)
Reaction score
216
One thing, not everyone knows DotA, so a screenshot would help. Just my opinion. Even though there are links -.-

So, go to find and try DotA yourself. You should find many hard coded spell and challenge yourself to make it.:p
 
Reaction score
91
> One thing, not everyone knows DotA, so a screenshot would help.
True, I'll make some but it wouldn't really show the full effects of the spells - it's better to see the test map.

EDIT: Added screenshots.
 

wraithseeker

Tired.
Reaction score
122
For the converting of angles, you don't need RADTODEG, just remove that line and it'll work the same unless you are setting a unit's facing.

Although I am not sure why you used PUI_PROPERTY or how good it is, why don't you make a member in the struct and do //! runtextmacro PUI() at the top of your struct members.
 

Romek

Super Moderator
Reaction score
963
> integer array SPIRIT
public integer array SPIRIT would be preferable.

> struct Spheres
public struct Spheres would be preferable.

-------------------------------------------
JASS:
//
    if CLEAR_MESSAGES then
        if GetLocalPlayer() == GetTriggerPlayer() then
            call ClearTextMessages()
        endif
    endif

Could be:
JASS:
if CLEAR_MESSAGES and GetLocalPlayer() == GetTriggerPlayer() then
    call ClearTextMessages()
endif


-------------------------------------------
JASS:
//
        if d != 0 then 
            // Is a Quas orb activated?
            if d.quas &gt; 0 then
                set heal = InvokerConfig_QUAS_REGENERATION(GetUnitAbilityLevel(fir, AID_QUAS)) * d.quas
                call SetWidgetLife(fir, GetWidgetLife(fir) + heal)
            endif
        endif

Could be:
JASS:
//
        if d != 0 and d.quas &gt; 0 then
                set heal = InvokerConfig_QUAS_REGENERATION(GetUnitAbilityLevel(fir, AID_QUAS)) * d.quas
                call SetWidgetLife(fir, GetWidgetLife(fir) + heal)
        endif


-------------------------------------------

That's as far as I got before getting bored. This is going to be a nightmare to read through. =|
 
Reaction score
91
> This is going to be a nightmare to read through.
I know, wasn't intended but is better than submitting each spell as a standalone resource... Should I do the fixes now or wait for a bigger review?
 
Reaction score
91
23jsxth.gif
 

Viikuna

No Marlo no game.
Reaction score
265
You should add some nice ObjectMerger macros for generating all those needed object editor abilities.

Would be cool.
 
Reaction score
91
> You should add some nice ObjectMerger macros for generating all those needed object editor abilities.
Good idea, will consider that for the next update. Now I have to find some free time to do it...
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top