Eraser v1.1

Description

Code

How to Import

Reference


Description
Summons orbs of lightning that will shoot lightning towards a random point, dealing damage and applying a short stun to effected enemy units. The caster will have its armor increased throughout the duration of the spell.

Effect
Level 1
- deals 60 damage + (100% Intelligence) per lightning strike, grants 2 armor
Level 2 - deals 70 damage + (100% Intelligence) per lightning strike, grants 3 armor
Level 3 - deals 80 damage + (100% Intelligence) per lightning strike, grants 4 armor

Others
Area of Effect:500
Duration: 10 seconds
Number of Orbs: 11 orbs
Stun Duration: 0.1 seconds


JASS:
library Eraser requires DummyCaster, TreeDestruction, Preload
    //-------------------------------------------------------------------------
    // Eraser v1.1
    // Author: Blightsower
    //
    // Description:
    // Summons orbs of lightning that will shoot lightning towards a random point,
    // dealing damage and applying a short stun to effected enemy units. The caster
    // will have its armor increased throughout the duration of the spell.
    //
    // Requirements:
    //  * Enable JassHelper
    //
    // How to enable JassHelper
    //  * In the Trigger Editor window, look for the menu labeled JassHelper
    //    and click Enable JassHelper
    //-------------------------------------------------------------------------
    // How to Import:
    // 1. Copy and paste the objects from this map to your map namely: 
    //    * Eraser
    //    * Eraser (Ministun)
    //    * Eraser (Bonuses)
    //    * Eraser (Swap)
    //    * Eraser (Disabled)
    // 2. Copy and paste the Eraser Category into your map
    // 3. Configure the spell so that everything points to the correct 
    //    variables. Additional information about the variables are 
    //    provided for clarity.
    //-------------------------------------------------------------------------
    // Note:
    // This spell includes some optional libraries to handle tree destruction, dummy casting
    // and preloading. The spell is arranged so that you can easily get rid of those libraries without
    // going into the code. You might want to get rid of those libraries if you already have a system 
    // that handles those functionalities or you just feel like writing your own. Should you choose to keep
    // the libraries, there are a few things that you need to configure to fit your needs. Check the libraries
    // individually to have the variables point to the correct objects.
    // 
    // How to remove optional libraries:
    // To remove DummyCaster:
    //     Remove DummyCaster from the requires and delete whats inside the onDummyCast function found
    //     at the bottom of configurables. Delete DummyCaster script
    // To remove TreeDestruction:
    //     Remove TreeDestrcution from the requires and delete whats inside the onCastDestroyTree and onBeamDestroyTree
    //     function found at the bottom of configurables. Delete TreeDestruction script
    // To remove Preload:
    //     Remove Preload from the requires and delete whats inside the onPreload function found
    //     at the bottom of configurables. Delete Preload script
    //
    // While most of the functions and variable names are intuitive, it will also help to read what each 
    // of the values do to affect the spell.
    //-------------------------------------------------------------------------

    //-------------------------------------------------------------------------
    // CONFIGURABLES
    //-------------------------------------------------------------------------
    globals
        // The interval used by the timer which is responsible for executing the spell
        private constant real TIMER_SPEED = 0.03
    endglobals
    native UnitAlive takes unit id returns boolean
    module EraserConfiguration
        //-------------------------------------------------------------
        // Hint:
        // Press ctrl + d to check the object ids. 
        //-------------------------------------------------------------

        //-------------------------------------------------------------
        // ERASER_ID - it should point to Eraser
        // Note: the ability is based off of Monsoon
        //-------------------------------------------------------------
        readonly static constant integer ERASER_ID = 'A000'

        //-------------------------------------------------------------
        // CAST_SAFETY - it should point to Eraser (Swap)
        // Note: the ability is based off of Engineering Upgrade to prevent the caster 
        // from casting the spell while there is an ongoing instance of the spell
        //-------------------------------------------------------------
        readonly static constant integer CAST_SAFETY = 'A002'

        //-------------------------------------------------------------
        // BONUSES - it should point to Eraser (Bonuses)
        // Note: this is a spell book responsible for providing the armor bonus while
        // casting
        //-------------------------------------------------------------
        readonly static constant integer BONUSES = 'A004'

        //-------------------------------------------------------------
        // DUMMY_ABILITY_ID - it should point to Eraser (Ministun)
        // Note: this spell is what the dummy caster will cast during the spell
        //-------------------------------------------------------------
        readonly static constant integer DUMMY_ABILITY_ID = 'A001'

        //-------------------------------------------------------------
        // DAMAGE
        //-------------------------------------------------------------
        readonly static attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
        readonly static damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
        readonly static weapontype WEAPON_TYPE = null

        static constant method getAbsoluteDamage takes integer level returns real
            return 50. + (10*level)
        endmethod

        //-------------------------------------------------------------
        // Hint:
        // 1.0 = 100% of the attribute
        //-------------------------------------------------------------
        static constant method getStrengthDamage takes integer level returns real
            return 0. + (0*level)
        endmethod
        static constant method getAgilityDamage takes integer level returns real
            return 0. + (0*level)
        endmethod
        static constant method getIntelligenceDamage takes integer level returns real
            return 1.0 + (0*level)
        endmethod

        //-------------------------------------------------------------
        // getAoe - returns the size of the targeting circle which determines the area 
        // where the lightning orbs will appear
        //-------------------------------------------------------------
        static constant method getAoe takes integer level returns real 
            return 300.
        endmethod

        //-------------------------------------------------------------
        // getBeamAoe - returns the size of the area in which enemies are damaged by an
        // individual lightning beam.
        //-------------------------------------------------------------
        static constant method getBeamAoE takes integer level returns real
            return 150.
        endmethod
        
        //-------------------------------------------------------------
        // getSpellDuration - returns the maximum duration of the spell can be channeled.
        // the main ability has 3600 sec channel. this function will break the channel of the unit
        // which will end the spell
        //-------------------------------------------------------------
        static constant method getSpellDuration takes integer level returns real
            return 10.
        endmethod

        //-------------------------------------------------------------
        // getOrbCount - returns the number of lightning orbs summoned during the spell
        //-------------------------------------------------------------
        static constant method getOrbCount takes integer level returns integer
            return 11
        endmethod

        //-------------------------------------------------------------
        // getOrbDelay - returns the delay before the first orb appears after casting the spell
        //-------------------------------------------------------------
        static constant method getOrbDelay takes integer level returns real
            return .75
        endmethod

        //-------------------------------------------------------------
        // getNextOrbDelay - returns the delay before the next orb appears after casting the spell
        //-------------------------------------------------------------
        static constant method getNextOrbDelay takes integer level returns real
            return .25
        endmethod

        //-------------------------------------------------------------
        // getOrbAttackInterval - returns the delay between an orb's attack and its next attack
        //-------------------------------------------------------------
        static constant method getOrbAttackInterval takes integer level returns real
            return 3.
        endmethod

        //-------------------------------------------------------------
        // getLaserDuration - returns the delay before the lightning strikes the ground
        //-------------------------------------------------------------
        static constant method getLaserDuration takes integer level returns real
            return .15
        endmethod

        //-------------------------------------------------------------
        // MODELS
        //-------------------------------------------------------------
        //-------------------------------------------------------------
        // ERASER_RANGE_MODEL - the range indicator of the spell. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string ERASER_RANGE_MODEL = "" // "Abilities\\Spells\\Human\\Brilliance\\Brilliance.mdl"
        static constant method getRangeIndicatorSize takes integer level returns real
            return 3.
        endmethod

        // controls the elevation of the range indicator
        readonly static constant real ERASER_RANGE_HEIGHT = 0

        //-------------------------------------------------------------
        // ORB_MODEL - the model used by the floating orbs of lightning. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string ORB_MODEL = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBase.mdl" 
        static constant method getOrbModelSize takes integer level returns real
            return 1.
        endmethod

        //-------------------------------------------------------------
        // ORB_ATTACK_MODEL - the model used whenever the orb is about to attack. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string ORB_ATTACK_MODEL = "Abilities\\Spells\\Human\\Feedback\\SpellBreakerAttack.mdl" 
        static constant method getAttackModelSize takes integer level returns real
            return 1.
        endmethod


        //-------------------------------------------------------------
        // BEAM_IMPACT_MODEL - the model used whenever the lightning hits the ground. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string BEAM_IMPACT_MODEL = "Abilities\\Weapons\\AncestralGuardianMissile\\AncestralGuardianMissile.mdl" 
        static constant method getImpactModelSize takes integer level returns real
            return 1.
        endmethod

        //-------------------------------------------------------------
        // HIT_MODEL - the model attached to the damaged unit. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string HIT_MODEL = "Abilities\\Weapons\\AncestralGuardianMissile\\AncestralGuardianMissile.mdl" 
        readonly static constant string HIT_ATTACHMENT = "chest"
        static constant method getHitModelSize takes integer level returns real
            return 1.
        endmethod

        //-------------------------------------------------------------
        // CASTER_MODEL  - the model attached to the caster. the function right below it controls
        // the size
        //-------------------------------------------------------------
        readonly static constant string CASTER_MODEL = "Abilities\\Spells\\Items\\StaffOfSanctuary\\Staff_Sanctuary_Target.mdl"
        readonly static constant string CASTER_ATTACHMENT = "chest"
        static constant method getCasterModelSize takes integer level returns real
            return 1.
        endmethod

        //-------------------------------------------------------------
        // Orbs' height distribution
        //-------------------------------------------------------------
        readonly static constant real ORB_MIN_HEIGHT = 200.
        readonly static constant real ORB_MAX_HEIGHT = 350.

        //-------------------------------------------------------------
        // Dummy Caster
        // If true, the dummy will cast 852095 (storm bolt) to affected enemies
        //-------------------------------------------------------------
        readonly static constant boolean USES_DUMMY_CASTER = true
        readonly static constant integer DUMMY_ABILITY_ORDER = 852095

        //-------------------------------------------------------------
        // If true, individual beams can destroy trees
        //-------------------------------------------------------------
        readonly static constant boolean BEAM_DESTROYS_TREES = true

        //-------------------------------------------------------------
        // If true, destroys the trees at the target point
        //-------------------------------------------------------------
        readonly static constant boolean CAST_DESTROYS_TREES = false

        //-------------------------------------------------------------
        // Lightning type of the spell "CLPB" - chain lightning primary
        //-------------------------------------------------------------
        readonly static constant string ERASER_LIGHTNING = "MBUR"

        //-------------------------------------------------------------
        // Coordinates where SFX are moved before destruction. Find some unused corner in your map and hover
        // your mouse while using the terrain editor to see the coordinates. it will be found at the lower left
        // portion of your screen. 
        //-------------------------------------------------------------
        readonly static constant real SFX_CLEANUP_X = -1231.7
        readonly static constant real SFX_CLEANUP_Y = 988.4

        //-------------------------------------------------------------
        // API
        //-------------------------------------------------------------

        //-------------------------------------------------------------
        // isErasable - is the damage filter of the spell.
        // unit u - is the unit in question
        // player p - is the owner of the spell
        // Original Conditions:
        //  - u is alive
        //  - u is not a structure
        //  - u is not magic immune
        //  - u is is an enemy of player p
        //-------------------------------------------------------------
        static method isErasable takes unit u, player p returns boolean
            return UnitAlive(u) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and IsUnitEnemy(u,p)
        endmethod

        //-------------------------------------------------------------
        // onDamage - is the damage event of the spell.
        // unit caster - is the caster of the spell
        // unit target - is the enemy about to get damaged by the spell
        // real damage - is the amount of damage about to be dealt
        // attacktype attackType - attacktype of the spell
        // damagetype damageType - damagetype of the spell
        // weapontype weaponType - weapontype of the spell
        //-------------------------------------------------------------
        static method onDamage takes unit caster, unit target, real damage, attacktype attackType, damagetype damageType, weapontype weaponType returns nothing
            call UnitDamageTarget(caster, target, damage, true, false, attackType, damageType, weaponType)
        endmethod

        //-------------------------------------------------------------
        // onDummyCast - is the ministun event of the spell
        // unit u - target of the ministun
        // integer l - level of the ministun
        // uses the OrderCaster function from the optional library
        //-------------------------------------------------------------
        static method onDummyCast takes unit u, integer l returns nothing
            call OrderCaster(u, l, DUMMY_ABILITY_ID, DUMMY_ABILITY_ORDER)
        endmethod

        //-------------------------------------------------------------
        // onCastDestroyTree - tree destruction at the target point
        // (x, y) - target point
        // aoe - radius of the destruction
        // uses the ClearTrees function from the optional library
        //-------------------------------------------------------------
        static method onCastDestroyTree takes real x, real y, real aoe returns nothing
            call ClearTrees(x, y, aoe)
        endmethod

        //-------------------------------------------------------------
        // onBeamDestroyTree - tree destruction where the beam lands
        // (x, y) - target point
        // aoe - radius of the destruction
        // uses the ClearTreesAbility/Effect function from the optional library
        //-------------------------------------------------------------
        static method onBeamDestroyTree takes real x, real y, real aoe returns nothing
            call ClearTrees(x, y, aoe)
        endmethod

        //-------------------------------------------------------------
        // onPreload - runs at map initialization
        // uses the preload function from the optional library
        //-------------------------------------------------------------
        static method onPreload takes nothing returns nothing
            call Initialize.preloadAbility(ERASER_ID)
            call Initialize.preloadAbility(DUMMY_ABILITY_ID)
            call Initialize.preloadAbility(CAST_SAFETY)
            call Initialize.preloadAbility(BONUSES)
            call Initialize.preloadEffect(ERASER_RANGE_MODEL)
            call Initialize.preloadEffect(ORB_MODEL)
            call Initialize.preloadEffect(ORB_ATTACK_MODEL)
            call Initialize.preloadEffect(BEAM_IMPACT_MODEL)
            call Initialize.preloadEffect(HIT_MODEL)
            call Initialize.preloadEffect(CASTER_MODEL)
        endmethod
    endmodule
    //-------------------------------------------------------------------------
    // END OF CONFIGURABLES
    //-------------------------------------------------------------------------


    //-------------------------------------------------------------------------
    // Eraser starts here
    //-------------------------------------------------------------------------
    scope Eraser

        //-------------------------------------------------------------------------
        // Singly Linked List set up where the end node is 0
        //-------------------------------------------------------------------------
        module SinglyLinkedList
            thistype next
            thistype prev

            static method insertAtHead takes thistype this returns nothing
                set this.next = 0
                set this.prev = thistype(0).prev
                set thistype(0).prev.next = this
                set thistype(0).prev = this
            endmethod
  
            static method pop takes thistype this returns nothing
                set this.next.prev = this.prev
                set this.prev.next = this.next
            endmethod
        endmodule


        //-------------------------------------------------------------------------
        // Module which handles vectors
        //-------------------------------------------------------------------------
        module Transformer
            readonly real x1
            readonly real y1
            readonly real z1
            readonly real x2
            readonly real y2
            readonly real z2

            real t
            
       
            method setEndingPoint takes real x, real y, real r returns nothing
                // generates the random offset for x and y
                local real randomRadius = r * SquareRoot(GetRandomReal(0, 1))
                local real angle = GetRandomDirectionDeg() * bj_DEGTORAD 
                set .x2 = x + randomRadius * Cos(angle)
                set .y2 = y + randomRadius * Sin(angle)
              
                // accounts for elevation
                call MoveLocation(Eraser.loc, .x2, .y2)
                set .z2 = GetLocationZ(Eraser.loc)
  
            endmethod

            method setStartingPoint takes real x, real y, real r, real h1, real h2 returns nothing

                // generates the random offset for x and y
                local real randomRadius = r * SquareRoot(GetRandomReal(0, 1))
                local real angle = GetRandomDirectionDeg() * bj_DEGTORAD 
                set .x1 = x + randomRadius * Cos(angle)
                set .y1 = y + randomRadius * Sin(angle)
 
                // adjusts height
                set .z1 = GetRandomReal(h1, h2)
            endmethod
        
            method lerp takes real a, real b returns real
                return a + (b - a) * .t
            endmethod
        endmodule

        //-------------------------------------------------------------------------
        // Struct used to represent the floating orbs
        //-------------------------------------------------------------------------
        struct Orb extends array
            implement Transformer

            // the unit group from the optimized group enumeration thread
            private static group g = CreateGroup()
            private static integer instanceCount = 0
            private static integer recycleCount = 0
            private static integer array recycle

            // privates
            private real x              
            private real y         
            private unit caster 
            private integer level
            private player owner
            private real speed
            private real damage
            private real aoe
            private real attackTimeLeft
            private real formationDelay
            private boolean isWaiting
            private boolean isAttacking
            private effect orb
            private lightning beam

            static method createOrb takes Eraser instance returns thistype
                local thistype this

                // struct extends array set up
                if (recycleCount > 0) then 
                    set recycleCount = recycleCount - 1
                    set this = recycle[recycleCount] 
                else
                    set instanceCount = instanceCount + 1
                    set this = instanceCount
                endif
                
                // load the values from the instance
                set .x = instance.x
                set .y = instance.y
                set .caster = instance.caster
                set .level = instance.level
                set .owner = instance.owner
                set .damage = instance.damage
                set .formationDelay = instance.formationDelay
                set .attackTimeLeft = Eraser.getOrbAttackInterval(instance.level)
                set .aoe = instance.aoe
                set .speed = 1. / (Eraser.getLaserDuration(instance.level) / TIMER_SPEED)
                set .isWaiting = true

                // apply random points
                call setStartingPoint(.x, .y, .aoe, Eraser.ORB_MIN_HEIGHT, Eraser.ORB_MAX_HEIGHT)
                call setEndingPoint(.x, y, .aoe)

                // reassign the aoe after the random
                set .aoe = instance.beamAoe
                return this
            endmethod

            method update takes nothing returns nothing
                local effect e = null
                local unit u = null

                // waiting for the orbs to appear
                if .isWaiting then
                    if .formationDelay > 0 then
                        set .formationDelay = .formationDelay - TIMER_SPEED
                    else
                        set .isWaiting = false
                        set .orb = AddSpecialEffect(Eraser.ORB_MODEL, .x1, .y1)
                        call BlzSetSpecialEffectZ(.orb, z1)
                        call BlzSetSpecialEffectScale(.orb, Eraser.getOrbModelSize(.level))
                    endif
                else
                    // waiting for the orbs to attack
                    if .attackTimeLeft > 0 then
                        set .attackTimeLeft = .attackTimeLeft - TIMER_SPEED
                    else
                        set .attackTimeLeft = Eraser.getOrbAttackInterval(.level)
                        set .isAttacking = true

                        set e = AddSpecialEffect(Eraser.ORB_ATTACK_MODEL, .x1, .y1)
                        call BlzSetSpecialEffectZ(e, z1)  
                        call BlzSetSpecialEffectScale(e, Eraser.getAttackModelSize(.level))
                        call DestroyEffect(e)
                        set .beam = AddLightningEx(Eraser.ERASER_LIGHTNING, true, .x1, .y1, .z1, .x1, .y1, .z1)
                    endif
                endif

                // when orbs are attacking
                if .isAttacking then
                    if .t <= 1 then
                        call MoveLightningEx(.beam, true, .x1, .y1, .z1, lerp(.x1, .x2), lerp(.y1, .y2), lerp(.z1, .z2))
                        set .t = .t + .speed
                    else

                        // resets everything so that orbs may attack again
                        set.isAttacking = false
                        set .t = 0
                        call DestroyLightningBJ(.beam)
                        set e = AddSpecialEffect(Eraser.BEAM_IMPACT_MODEL, .x2, .y2)
                        call BlzSetSpecialEffectScale(e, Eraser.getImpactModelSize(.level))
                        call DestroyEffect(e)
                        
                        // tree destruction
                        if Eraser.BEAM_DESTROYS_TREES then
                            call Eraser.onBeamDestroyTree(.x2, .y2, .aoe)
                        endif

                        // optimized group enumeration
                        call GroupEnumUnitsInRange(g, .x2, .y2, .aoe, null)
                        
                        loop
                            set u = FirstOfGroup(g)
                            exitwhen u == null

                            if Eraser.isErasable(u, .owner) then      
                                // hit special effects
                                set e = AddSpecialEffectTarget(Eraser.HIT_MODEL, u, Eraser.HIT_ATTACHMENT)
                                call BlzSetSpecialEffectScale(e, Eraser.getHitModelSize(.level))
                                call DestroyEffect(e)
                               
                                // damage
                                call Eraser.onDamage(caster, u, .damage, Eraser.ATTACK_TYPE, Eraser.DAMAGE_TYPE, Eraser.WEAPON_TYPE)
         
                                // dummy caster
                                if Eraser.USES_DUMMY_CASTER then
                                    call Eraser.onDummyCast(u, .level)
                                endif
                            endif
                            call GroupRemoveUnit(g,u)
                         endloop
                    endif
                endif
            endmethod

            method destroy takes nothing returns nothing

                // takes the effect to the corner before execu-- i mean clean up
                call BlzSetSpecialEffectX(.orb, Eraser.SFX_CLEANUP_X)
                call BlzSetSpecialEffectY(.orb, Eraser.SFX_CLEANUP_Y)
                call DestroyEffect(.orb)

                // manually resetting the values
                call DestroyLightning(.beam)
                set .x = 0
                set .y = 0
                set .caster = null
                set .owner = null
                set .speed = 0
                set .damage = 0
                set .aoe = 0
                set .attackTimeLeft = 0
                set .formationDelay = 0
                set .isWaiting = true
                set .isAttacking = false

                // struct extends array set up
                set recycle[recycleCount] = this
                set recycleCount = recycleCount + 1
            endmethod

        endstruct

        //-------------------------------------------------------------------------
        // Struct used to represent the instance of the spell
        //-------------------------------------------------------------------------
        struct Eraser
            implement EraserConfiguration
            implement SinglyLinkedList

            // dynamic indexing set up
            private static timer eraserTimer = CreateTimer()
            private static integer index = 0

            // privates
            readonly unit caster
            readonly integer level
            readonly player owner
            readonly real x
            readonly real y
            readonly real damage
            readonly real aoe
            readonly real beamAoe
            readonly real formationDelay
            private real channelTimeLeft
            private effect range
            private effect casterEffect
            private Orb orb
            static location loc = Location(0,0)

            // used to adjust the elevation of the range indicator relative to the z of the target point
            private static method updateSFX takes real x, real y, real z returns real
                call MoveLocation(loc, x, y)
                return GetLocationZ(loc) + z
            endmethod

            // damage calculation
            private static method getDamageFromAttribute takes unit u, real s, real a, real i returns real
                local real damage_agi = I2R(GetHeroStatBJ(bj_HEROSTAT_AGI, u, true)) * a
                local real damage_str = I2R(GetHeroStatBJ(bj_HEROSTAT_STR, u, true)) * s
                local real damage_int = I2R(GetHeroStatBJ(bj_HEROSTAT_INT, u, true)) * i
                return damage_agi + damage_str + damage_int
            endmethod

            // clean up
            private method clear takes nothing returns nothing
                call this.deallocate()

                // remove from linked list
                call pop(this)
                
                // destroy special effects
                call BlzSetSpecialEffectScale(.range, 0.001)
                call DestroyEffect(.range)
                                             
                call BlzSetSpecialEffectX(.casterEffect, SFX_CLEANUP_X)
                call BlzSetSpecialEffectY(.casterEffect, SFX_CLEANUP_Y)
                call DestroyEffect(.casterEffect)

                // removing the abilities added
                call UnitRemoveAbility(.caster, CAST_SAFETY)
                call UnitRemoveAbility(.caster, BONUSES)

                // destroy the orb
                call this.orb.destroy()

                set .caster = null
                set .owner = null

                // dynamic indexing
                set index = index - 1
                if index == 0 then
                    call PauseTimer(.eraserTimer)
                endif  
            endmethod 

            // spell loop
            private static method onEffect takes nothing returns nothing
                local integer i = 0                      
                local thistype this = thistype(0).next

                loop 
                    exitwhen this == 0

                    // checks the order
                    if GetUnitCurrentOrder(.caster) == String2OrderIdBJ("monsoon") then

                        // spell cancels the order when the duration expires
                        if .channelTimeLeft <= 0 then
                            call IssueImmediateOrder(.caster, "stop")
                        endif 

                        set .channelTimeLeft = .channelTimeLeft - TIMER_SPEED
                        call .orb.update()
                    else
                        // clean up
                        call .clear()
                    endif

                    set this = this.next
                endloop
            endmethod

            // cast function
            private static method onCast takes nothing returns nothing
                local thistype this 
                local integer i    // loops orb count

                local real x       // x of target
                local real y       // y of target          
                local unit c       // caster
                local integer l    // level
                local player p     // player
                local real t       // time - spell's duration
                local real a       // AOE
                local real d       // damage
                local real f       // formation delay
                local real b       // beam AOE

                if GetSpellAbilityId() == ERASER_ID then
                    set i = 1

                    set x = GetSpellTargetX()
                    set y = GetSpellTargetY()                
                    set c = GetTriggerUnit()
                    set l = GetUnitAbilityLevel(c, ERASER_ID)
                    set p = GetTriggerPlayer()
                    set t = getSpellDuration(l)
                    set a = getAoe(l)
                    set d = getAbsoluteDamage(l) + getDamageFromAttribute(c, getStrengthDamage(l), getAgilityDamage(l), getIntelligenceDamage(l))
                    set f = 0
                    set b = getBeamAoE(l)

                    // add cast safety and bonuses
                    call UnitAddAbility(c, CAST_SAFETY)
                    call UnitAddAbility(c, BONUSES)
                    call SetUnitAbilityLevel(c, BONUSES, l)

                    // spawn orbs but do not show the effects yet
                    loop 
                        exitwhen i > getOrbCount(l)

                        set this = thistype.allocate()
                        call insertAtHead(this)
                        
                        set .x = x
                        set .y = y
                        set .caster = c
                        set .level = l
                        set .owner = p 
                        set .aoe = a
                        set .damage = d
                        set .channelTimeLeft = t
                        set .formationDelay = getOrbDelay(.level) + f
                        set .beamAoe = b

                        // only the first instance has the range indicator and tree destruction
                        if i == 1 then
                            if CAST_DESTROYS_TREES then
                                call onCastDestroyTree(.x, .y, .aoe)
                            endif
                            
                            // range indicator
                            set .casterEffect = AddSpecialEffectTarget(CASTER_MODEL, .caster, CASTER_ATTACHMENT)
                            call BlzSetSpecialEffectScale(.casterEffect, Eraser.getCasterModelSize(.level))

                            // caster effects
                            set .range = AddSpecialEffect(ERASER_RANGE_MODEL, .x, .y)
                            call BlzSetSpecialEffectScale(.range, getRangeIndicatorSize(.level))
                            call BlzSetSpecialEffectZ(.range, updateSFX(.x, .y, ERASER_RANGE_HEIGHT))
                        endif

                        // get orb specs
                        set .orb = Orb.createOrb(this)

                        // dynamic indexing
                        set index = index + 1
                        if index == 1 then                   
                            call TimerStart(eraserTimer, TIMER_SPEED, true, function thistype.onEffect)
                        endif
 
                        // increment the formation delay
                        set f = f + getNextOrbDelay(.level)
                        set i = i + 1
                    endloop

                    // clean up
                    set p = null
                    set c = null
                endif
            endmethod

            static method onInit takes nothing returns nothing
                local integer i = 1
                local trigger t = CreateTrigger()

                // preload
                call onPreload()

                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Condition(function thistype.onCast))
                set t = null

                // disable the bonuses spell book
                loop
                    exitwhen i > 24
                    call SetPlayerAbilityAvailableBJ( false, CAST_SAFETY, ConvertedPlayer(i))
                    call SetPlayerAbilityAvailableBJ( false, BONUSES, ConvertedPlayer(i))
                    set i = i + 1
                endloop
            endmethod
        endstruct
    endscope
endlibrary

1. Copy and paste the objects from this map to your map namely:
* Eraser
* Eraser (Ministun)
* Eraser (Bonuses)
* Eraser (Swap)
* Eraser (Disabled)
2. Copy and paste the Eraser Category into your map
3. Configure the spell so that everything points to the correct
variables. Additional information about the variables are
provided for clarity.

Important Tip: The cooldown, tooltip, and icons of Eraser and Eraser (Disabled) must be identical to produce intended results.

The spell is heavily inspired by Eraser (I don't own this video) from Dragon Nest. The screenshots don't fully depict what it looks like in game.

Credits: Vexorian for the dummy.mdx
Previews
Contents

Blightsower Eraser v1.1 (Map)

Reviews
Wrda
The keyword "optional" exists for declaring optional libraries being used by others. And then you can use static if to check if the library exists and declare the onDummyCast inside that: static if LIBRARY_DummyCaster then //dosomething endif Same...

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
The keyword "optional" exists for declaring optional libraries being used by others. And then you can use static if to check if the library exists and declare the onDummyCast inside that:
JASS:
static if LIBRARY_DummyCaster then
//dosomething
endif
Same thing for the part:
JASS:
if Eraser.USES_DUMMY_CASTER then
    call Eraser.onDummyCast(u, .level)
endif
Equivalent for the other optional libraries.


JASS:
local unit c       // caster
                local integer l    // level
                local player p     // player
                local real t       // time - spell's duration
                local real a       // AOE
                local real d       // damage
                local real f       // formation delay
                local real b       // beam AOE
Some, to avoid wasting paper, abbreviate terms, now while coding you're saving characters, it's a bit insane :D It's better to make more meaningful names.
The spell is very nice, that's for sure.

Approved
 
Top