• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Vorpal Extermination v1.0.0.8

Realized recently how all my spells lack eye-candy, I decided to play with my imagination a bit and finally came up with this spell.

For obvious reasons, should you decide to use this spell, give it to a boss. This is far too overkill for a normal hero spell.

213181-albums6672-picture78826.gif

213181-albums6672-picture78827.gif

VORPAL EXTERMINATION

The Hero throws a magical shuriken that deals damage to all enemies it comes in contact with. If the shuriken comes in contact with an enemy hero or reaches its maximum range, a spinning glaive will be released for 5 seconds to deal damage in an AoE and slow enemies by 8% for every 10% of health they are missing. Once the duration is up, the glaive will split into slicing blades that explode in a caustic finale after a short period.

Level 1 - 150 main damage, 75 dps/slow, 30 slice damage and 150 explosion damage.
Level 2 - 250 main damage, 125 dps/slow, 50 slice damage and 200 explosion damage.
Level 3 - 350 main damage, 175 dps/slow, 70 slice damage and 250 explosion damage.
Level 4 - 450 main damage, 225 dps/slow, 90 slice damage and 300 explosion damage.

Required Libraries:

Optional Library:

Note: You should be able to find all the required libraries from Nestharus in this archive.

Credit:
  • Codings:
    • Dirac for Missile, Linked List Module, Loc, Adv Loc.
    • PurgeandFire for SpecialEffectZ.
    • Garfield1337 for GetTerrainZ | UnitZ.
    • Maker for ArcingFloatingText, TimedLightnings.
    • Nestharus for Alloc Alternative, UnitIndexer, Event, World Bounds, Dummy, CTL, DummyCaster, ErrorMessage.
    • Bribe for SpellEffectEvent.
    • Magtheridon96 for RegisterPlayerUnitEvent.
    • Vexorian for TimerUtils, JassHelper.
  • Models:
    • Infrisios for ArcaneGlaive_2.mdx.
    • WILL THE ALMIGHTY for EMPBomb.mdx.
    • Suselishe for Cyclon Explosion.mdx.
    • Everyone who contributed to dummy.mdx.
  • Effect Feedback:
    • Lambdadelta.
    • Tank-Commander.
    • watermelon_1234.
    • Crigges.
    • UngThanhNhan.
    • hayaku1412.
    • nhocklanhox6.


  • Step 1: Copy all required libraries to your map.
  • Step 2: Copy al library-related objects in the test map (2 dummies) to your map. Readjust the raw codes in the Dummy and DummyCaster library.
  • Step 3: Copy the Slow dummy ability, the custom Slow buff and the Vorpal Extermination ability and configure them as you want to.
  • Step 4: ???
  • Step 5: Profit!!!

Script:

JASS:
library VorpalExtermination /* v1.0.0.8 by Doomlord
*************************************************************************************
*
*   The Hero throws a magical shuriken that deals damage to all enemies it comes in contact with. 
*   If the shuriken comes in contact with an enemy hero or reaches its maximum range,
*   a spinning glaive will be released for 5 seconds to deal damage in an AoE and slow enemies. 
*   Once the duration is up, the glaive will split into slicing blades that explode 
*   in a caustic finale after a short period.
*
*************************************************************************************
*
*   Credits
*
*       Dirac
*       -----------------------
*
*           Missile library
*
*       Maker
*       -----------------------
*
*           TimedLightnings, FloatingTextArc library
*
*       Garfield1337
*       -----------------------
*
*           UnitZ library
*
*       Nestharus
*       -----------------------
*
*           Alloc, DummyCaster library
*
*       PurgeandFire
*       -----------------------
*
*           SpecialEffectZ library
*
*       Bribe
*       -----------------------
*
*           SpellEffectEvent library
*
*       Infrisios
*       -----------------------
*
*           ArcaneGlaive_2.mdx
*
*       WILL THE ALMIGHTY
*       -----------------------
*
*           EMPBomb.mdx
*
*       Suselishe
*       -----------------------
*
*           Cyclon Explosion.mdx
*
*************************************************************************************
*
*   */ uses /*
*   
*       */ Missile                          /* hiveworkshop.com/forums/jass-resources-412/system-missile-207854/
*       */ SpecialEffectZ                   /* wc3jass.com/5007/snippet-addspecialeffectz/
*       */ UnitZ                            /* hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
*       */ Alloc                            /* hiveworkshop.com/forums/jass-resources-412/snippet-alloc-alternative-221493/
*       */ TimedLightnings                  /* hiveworkshop.com/forums/spells-569/system-timed-lightnings-v1-0-1-1-a-205105/
*       */ DummyCaster                      /* hiveworkshop.com/forums/jass-resources-412/snippet-dummy-caster-197087/
*       */ DelayedDummyRecycler             /* hiveworkshop.com/forums/submissions-414/snippet-delayeddummyrecycler-239327/
*       */ optional FloatingTextArc         /* hiveworkshop.com/forums/spells-569/arcing-floating-text-1-0-0-3-a-228710/
*       */ optional SpellEffectEvent        /* hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*
************************************************************************************
*
*   SETTINGS
*
*/

    globals
        // Ability raw code
        private constant integer ABI_ID = 'A001'
        // Slow ability raw code
        private constant integer SLOW_ID = 'A002'
        // Slow order id
        private constant integer SLOW_ORDER_ID = 852075
        // Height difference for missiles
        private constant real HEIGHT_DIFFERENCE = 100.
        
        // FIRST PHASE
        // Primary missile model (throw phase)
        private constant string PRIMARY_MISSILEFX = "Abilities\\Weapons\\GlaiveMissile\\GlaiveMissile.mdl"
        // On collide model for main missile
        private constant string PRIMARY_COLLIDEFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
        // Primary attachment point
        private constant string PRIMARY_ATTACHPOINT = "chest"
        // Primary missile scaling
        private constant real PRIMARY_MISSILE_SCALE = 1.5
        // Area of Effect for throw phase
        private constant real PRIMARY_AOE = 200.
        
        // SECOND PHASE
        // Secondary missile model (spin phase)
        private constant string SECONDARY_MISSILEFX = "war3mapImported\\ArcaneGlaive_2.mdx"
        // On collide model for spin phase
        private constant string SECONDARY_COLLIDEFX = "Objects\\Spawnmodels\\Human\\HumanBlood\\HeroBloodElfBlood.mdl"
        // Secondary attachment point
        private constant string SECONDARY_ATTACHPOINT = "chest"
        // Glaive expiration effect
        private constant string SECONDARY_EXPFX = "war3mapImported\\Cyclon Explosion.mdx"
        // Secondary missile scaling
        private constant real SECONDARY_MISSILE_SCALE = 3.
        // Glaive expiration time
        private constant real EXPIRATION_TIME = 5.
        /* Damage fraction for spin phase. The unit will be
        damaged by main spell damage multiplied by SECONDARY_FRACTION */
        private constant real SECONDARY_FRACTION = 0.25
        // Damage fidelity for spin phase. Units will be damaged every DAMAGE_FIDELITY(s)
        private constant real DAMAGE_FIDELITY = 0.5
        // Area of Effect for spin phase
        private constant real SECONDARY_AOE = 300.
        
        // THIRD PHASE
        // Tertiary missile model (spread phase)
        private constant string TERTIARY_MISSILEFX = "Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
        // On collide model for spread phase
        private constant string TERTIARY_COLLIDEFX = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
        // Tertiary attachment point
        private constant string TERTIARY_ATTACHPOINT = "chest"
        // Final explosion model
        private constant string EXPLOSIONFX = "war3mapImported\\EMPBomb.mdx"
        // Explosion hit model
        private constant string EXPLOSION_HITFX = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
        // Explosion hit attachment point
        private constant string EXPLOSION_ATTACHPOINT = "origin"
        // Bonus effect
        private constant string BONUSFX = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl"
        // Tertiary missile scaling
        private constant real TERTIARY_MISSILE_SCALE = 1.5
        // Glaive spread count
        private constant integer SPREAD_COUNT = 12
        /* Damage fraction for spread phase. The unit will be 
        damaged by main spell damage multiplied by TERTIARY_FRACTION */
        private constant real TERTIARY_FRACTION = 0.2
        // Glaive spread max range
        private constant real SPREAD_MAX_RANGE = 400.
        // Inactive time for spread glaives
        private constant real INACTIVE_PERIOD = 1.5
        // Area of Effect for spread phase slicing
        private constant real TERTIARY_AOE = 200.
        // Explosion AoE for spread phase
        private constant real EXPLOSION_AOE = 300.
        /* Some explosion effect needs scaling and 
        thus we need a special technique for it */
        private constant boolean SPECIAL_EXPLOSION_FX_USAGE = true
        // Explosion effect scaling. Only use this if the above boolean is true.
        private constant real EXPLOSION_SCALE = 5.
        
        // ENERGY GATHER EFFECT
        // Effect model
        private constant string ENERGY_GATHER_FX = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
        // Effect model variant
        private constant string ENERGY_GATHER_FX_VAR = "Abilities\\Weapons\\VengeanceMissile\\VengeanceMissile.mdl"
        // Effect when lightning orbs reach the glaive
        private constant string ENERGY_GATHER_FX_FINISH = "Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl"
        /* Effect orbs spawn rate per second. There must be a remainder 
        of 0 from the division 60/ENERGY_GATHER_SPAWN_OFFSET */
        private constant integer ENERGY_GATHER_SPAWN_RATE = 12
        // Effect spawn offset
        private constant real ENERGY_GATHER_SPAWN_OFFSET = 400.
        // Effect orbs move speed
        private constant real ENERGY_GATHER_SPEED = 900.
        
        // DAMAGE CONFIGURATION
        // SLICE DAMAGE
        // Attack type
        private constant attacktype SLICE_ATT = ATTACK_TYPE_HERO
        // Damage type
        private constant damagetype SLICE_DAM = DAMAGE_TYPE_NORMAL
        // Weapon type
        private constant weapontype SLICE_WEA = WEAPON_TYPE_METAL_HEAVY_SLICE
        
        // EXPLOSION DAMAGE
        // Attack type
        private constant attacktype EXP_ATT = ATTACK_TYPE_NORMAL
        // Damage type
        private constant damagetype EXP_DAM = DAMAGE_TYPE_MAGIC
        // Weapon type
        private constant weapontype EXP_WEA = null
    endglobals

/************************************************************************************
*
*   END SETTINGS
*
*/

    globals
        private group g = CreateGroup()
        private string array s
    endglobals
    
    // Primary missile speed
    private constant function GetPrimarySpeed takes integer lvl returns real
        return 1440.
    endfunction
    
    // Tertiary missile speed
    private constant function GetTertiarySpeed takes integer lvl returns real
        return 1120.
    endfunction
    
    // Main spell damage
    private constant function GetDamage takes integer level returns real
        return 50. + level*100.
    endfunction
    
    // Explosion damage
    private constant function GetExplosionDamage takes integer level returns real
        return 100. + level*50
    endfunction
    
    // Filter out some targets
    private function FilterUnit takes unit u, player casterOwner returns boolean
        return /*
        
        */ IsUnitEnemy(u, casterOwner) and /* Target is an enemy of caster
        */ not IsUnitType(u, UNIT_TYPE_DEAD) and /* Target is not dead
        */ not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) and /* Target is not immune to magic
        */ not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* Target is not a structure
        */
    endfunction
    
    // Filter target for glaive release
    private function FilterActivationUnit takes unit u returns boolean
        return /*
        */ IsUnitType(u, UNIT_TYPE_HERO) /* Target is a Hero
        */
    endfunction
    
    // Lightning settings - set lightning types for randomizer
    private function LightningSettings takes nothing returns nothing
        set s[0] = "AFOD"
        set s[1] = "FORK"
        set s[2] = "MBUR"
        set s[3] = "SPLK"
        set s[4] = "DRAL"
        set s[5] = "DRAM"
    endfunction
    
    // Lightning type randomizer
    private function LightningRandomizer takes nothing returns string
        return s[GetRandomInt(0, 5)]
    endfunction
    
    // As usual, skip this part
    private struct GlaiveSpread extends array
        implement Alloc
        
        private boolean b
        private real timeCounter
        private real x
        private real y
        private real z
        private Missile m

        static method onPeriod takes Missile m returns boolean
            local thistype this = thistype(m.data)
            local unit u
            local unit v
            
            if this.b then
            
                // Is inactive period over?
                if this.timeCounter >= INACTIVE_PERIOD then
                    call DestroyEffect(AddSpecialEffectZ(TERTIARY_MISSILEFX, m.impact.x, m.impact.y, m.impact.z))
                    
                    static if SPECIAL_EXPLOSION_FX_USAGE then
                        set v = Dummy.create(m.impact.x, m.impact.y, 270).unit
                        call SetUnitScale(v, EXPLOSION_SCALE, 0, 0)
                        call SetUnitZ(v, m.impact.z)
                        call RecycleDummyDelayedEx(v, 3, AddSpecialEffectTarget(EXPLOSIONFX, v, "origin"), false)
                        set v = null
                        call DestroyEffect(AddSpecialEffectZ(ENERGY_GATHER_FX, m.impact.x, m.impact.y, m.impact.z))
                    else
                        call DestroyEffect(AddSpecialEffectZ(EXPLOSIONFX, m.impact.x, m.impact.y, m.impact.z))
                    endif
                    
                    call GroupEnumUnitsInRange(g, m.x, m.y, EXPLOSION_AOE, null)
                    
                    loop
                        set u = FirstOfGroup(g)
                        exitwhen u == null
                        
                        if FilterUnit(u, GetOwningPlayer(m.source)) then
                            call DestroyEffect(AddSpecialEffectTarget(EXPLOSION_HITFX, u, EXPLOSION_ATTACHPOINT))
                            call UnitDamageTarget(m.source, u, GetDamage(GetUnitAbilityLevel(m.source, ABI_ID)), false, false, EXP_ATT, EXP_DAM, EXP_WEA)
                        endif
                        
                        call GroupRemoveUnit(g, u)
                    endloop
                    
                    call this.deallocate()
                    return true
                else
                
                    // Create lightning links to center
                    if this.timeCounter == 0 then
                        set bj_lastCreatedLightning = AddLightningEx(LightningRandomizer(), true, this.x, this.y, this.z, m.impact.x, m.impact.y, m.impact.z)
                        call TimedL.P2P(bj_lastCreatedLightning, INACTIVE_PERIOD, 1, 0)
                        call DestroyEffect(AddSpecialEffectZ(BONUSFX, m.impact.x, m.impact.y, m.impact.z))
                    endif
                    
                    set this.timeCounter = this.timeCounter + .031250000
                endif
                
            endif
            
            return false
        endmethod
        
        static method onFinish takes Missile m returns boolean
            set thistype(m.data).b = true
            // Stop the slicing blades
            set m.speed = 0
            return false
        endmethod
        
        static method onCollide takes Missile m, unit justHit returns boolean
            if FilterUnit(justHit, GetOwningPlayer(m.source)) then
                // Damage enemies and play some extra blood effects
                call UnitDamageTarget(m.source, justHit, GetDamage(GetUnitAbilityLevel(m.source, ABI_ID))*TERTIARY_FRACTION, false, false, SLICE_ATT, SLICE_DAM, SLICE_WEA)
                call DestroyEffect(AddSpecialEffectTarget(TERTIARY_COLLIDEFX, justHit, TERTIARY_ATTACHPOINT))
            endif
            
            return false
        endmethod
        
        implement MissileStruct
        
        static method onActivate takes unit caster, real x, real y, real x1, real y1 returns thistype
            local thistype this = thistype.allocate()
            local real r = GetTerrainZ(x, y)
            local integer i = GetUnitAbilityLevel(caster, ABI_ID)

            set this.timeCounter = 0
            set this.b = false
            set this.x = x
            set this.y = y
            set this.z = GetTerrainZ(x, y) + HEIGHT_DIFFERENCE
            set this.m = Missile.create(x, y, this.z, Atan2(y1 - y, x1 - x), SPREAD_MAX_RANGE, GetTerrainZ(x1, y1) + HEIGHT_DIFFERENCE)
            set m.source = caster
            set m.model = TERTIARY_MISSILEFX
            set m.scale = TERTIARY_MISSILE_SCALE
            set m.collision = TERTIARY_AOE
            set m.speed = GetTertiarySpeed(i)/32
            set m.data = this
            
            call launch(m)
            
            return this
        endmethod
    endstruct
    
    private struct EnergyGatherFxInstance extends array
        implement Alloc
        
        private Missile m
        private integer i
        private real alphaStep
        
        static method onFinish takes Missile m returns boolean
            local unit v
        
            if thistype(m.data).i == 0 then
                call DestroyEffect(AddSpecialEffectZ(ENERGY_GATHER_FX, m.impact.x, m.impact.y, m.impact.z))
            else
                call DestroyEffect(AddSpecialEffectZ(ENERGY_GATHER_FX_VAR, m.impact.x, m.impact.y, m.impact.z))
            endif
            
            set v = Dummy.create(m.impact.x, m.impact.y, 270).unit
            call SetUnitZ(v, m.impact.z)
            call SetUnitScale(v, 1.25, 0, 0)
            call RecycleDummyDelayedEx(v, 2, AddSpecialEffectTarget(ENERGY_GATHER_FX_FINISH, v, "origin"), false)
            
            set v = null
            call SetUnitVertexColor(m.dummy, 255, 255, 255, 255)
            call thistype(m.data).deallocate()

            return true
        endmethod
        
        static method onPeriod takes Missile m returns boolean
            set thistype(m.data).alphaStep = thistype(m.data).alphaStep + 255/(ENERGY_GATHER_SPAWN_OFFSET/(ENERGY_GATHER_SPEED/32))
            call SetUnitVertexColor(m.dummy, 255, 255, 255, R2I(thistype(m.data).alphaStep))
            return false
        endmethod
        
        implement MissileStruct
        
        static method onActivate takes real x, real y returns thistype
            local thistype this = thistype.allocate()
            local real r = GetTerrainZ(x, y) + HEIGHT_DIFFERENCE
            local real s = GetRandomReal(0, 2*bj_PI)
            local real x1 = x + ENERGY_GATHER_SPAWN_OFFSET*Cos(s)
            local real y1 = y + ENERGY_GATHER_SPAWN_OFFSET*Sin(s)

            set this.m = Missile.create(x1, y1, r, Atan2(y - y1, x - x1), ENERGY_GATHER_SPAWN_OFFSET, r)
            set this.i = GetRandomInt(0, 1)
            set this.alphaStep = 0
            
            if this.i == 0 then
                set m.model = ENERGY_GATHER_FX
                set m.scale = 1
            else
                set m.model = ENERGY_GATHER_FX_VAR
                set m.scale = 1.5
            endif
            
            set m.speed = ENERGY_GATHER_SPEED/32
            set m.data = this
            
            call launch(m)
            
            return this
        endmethod
    endstruct
    
    private struct EnergyGatherFx extends array
        private integer counter
        private real dur
        private real x
        private real y

        implement CTL
        implement CTLExpire
        
            if this.dur <= 0 then
                call this.destroy()
            else
                if this.counter == 0 or ModuloInteger(this.counter, 60/ENERGY_GATHER_SPAWN_RATE) == 0 and this.dur >= 0.5 then 
                    call EnergyGatherFxInstance.onActivate(this.x, this.y)
                endif

                set this.counter = this.counter + 1
                set this.dur = this.dur - .031250000
            endif
            
        implement CTLNull
        implement CTLEnd
        
        static method onStart takes real x, real y, real duration returns thistype
            local thistype this = create()
            
            set this.counter = 0
            set this.dur = duration
            set this.x = x
            set this.y = y
            
            return this
        endmethod
    endstruct
    
    private struct GlaiveSpin extends array
        implement Alloc

        private real baseDamage
        private real smallDamage
        private real timeCounter
        private Missile m
        
        static method onPeriod takes Missile m returns boolean
            local unit u
            local thistype this = thistype(m.data)
            local real i = 0
        
            // Is the duration up yet?
            if this.timeCounter >= EXPIRATION_TIME then

                call DestroyEffect(AddSpecialEffectZ(SECONDARY_MISSILEFX, m.impact.x, m.impact.y, m.impact.z))
                call DestroyEffect(AddSpecialEffectZ(SECONDARY_EXPFX, m.impact.x, m.impact.y, m.impact.z))
                
                // Splits the glaive into slicing blades
                loop
                    exitwhen i >= 2*bj_PI
                    call GlaiveSpread.onActivate(m.source, m.impact.x, m.impact.y, m.impact.x + SPREAD_MAX_RANGE*Cos(i), m.impact.y + SPREAD_MAX_RANGE*Sin(i))
                    set i = i + 2*bj_PI/SPREAD_COUNT
                endloop
                
                call this.deallocate()
                return true
                
            else
                // Accumulate damage for the next damage instance
                set this.smallDamage = this.smallDamage + this.baseDamage*.031250000
                
                // Do we acquire enough damage yet?
                if this.smallDamage >= this.baseDamage*DAMAGE_FIDELITY then
                
                    call GroupEnumUnitsInRange(g, m.x, m.y, SECONDARY_AOE, null)
                    
                    loop
                        set u = FirstOfGroup(g)
                        exitwhen u == null
                        
                        if FilterUnit(u, GetOwningPlayer(m.source)) then
                            call DestroyEffect(AddSpecialEffectTarget(SECONDARY_COLLIDEFX, u, SECONDARY_ATTACHPOINT))
                            call UnitDamageTarget(m.source, u, this.smallDamage, false, false, SLICE_ATT, SLICE_DAM, SLICE_WEA)
                            call DummyCaster[SLOW_ID].castTarget(GetOwningPlayer(m.source), 10 - R2I((GetWidgetLife(u)/GetUnitState(u, UNIT_STATE_MAX_LIFE))*10), SLOW_ORDER_ID, u)
                        endif
                        
                        call GroupRemoveUnit(g, u)
                    endloop
                    
                    set this.smallDamage = 0
                endif
                
                set this.timeCounter = this.timeCounter + .031250000
            endif
            
            return false
        endmethod
        
        implement MissileStruct
        
        static method onActivate takes unit caster, real x, real y returns thistype
            local thistype this = thistype.allocate()
            local real r = GetTerrainZ(x, y)
            local integer i = GetUnitAbilityLevel(caster, ABI_ID)

            set this.baseDamage = GetDamage(i)*SECONDARY_FRACTION
            set this.timeCounter = 0
            set this.smallDamage = 0
            set this.m = Missile.create(x, y, r + HEIGHT_DIFFERENCE, 0, 1, r + HEIGHT_DIFFERENCE)
            set m.source = caster
            set m.model = SECONDARY_MISSILEFX
            set m.scale = SECONDARY_MISSILE_SCALE
            set m.speed = 0
            set m.data = this
            call EnergyGatherFx.onStart(x, y, EXPIRATION_TIME)
            
            call launch(m)
            
            return this
        endmethod
    endstruct
    
    private struct GlaiveThrow extends array
        implement Alloc
    
        private unit caster
        private Missile m
        
        // Shuriken at max range, activate buzzsaw
        static method onFinish takes Missile m returns boolean
            call DestroyEffect(AddSpecialEffectZ(PRIMARY_MISSILEFX, m.impact.x, m.impact.y, m.impact.z))
            call GlaiveSpin.onActivate(m.source, m.impact.x, m.impact.y)
            call thistype(m.data).deallocate()
            set thistype(m.data).caster = null
            return true
        endmethod
        
        static method onCollide takes Missile m, unit justHit returns boolean
            if FilterUnit(justHit, GetOwningPlayer(m.source)) then

                call UnitDamageTarget(m.source, justHit, GetDamage(GetUnitAbilityLevel(m.source, ABI_ID)), false, false, SLICE_ATT, SLICE_DAM, SLICE_WEA)
                call DestroyEffect(AddSpecialEffectTarget(PRIMARY_COLLIDEFX, justHit, PRIMARY_ATTACHPOINT))
            
                // Hero collision. Release buzzsaw
                if FilterActivationUnit(justHit) then
                    call DestroyEffect(AddSpecialEffectZ(PRIMARY_MISSILEFX, m.x, m.y, m.z))
                    call GlaiveSpin.onActivate(m.source, m.x, m.y)
                    call thistype(m.data).deallocate()
                    set thistype(m.data).caster = null
                    return true
                endif
                
            endif
            
            return false
        endmethod
        
        implement MissileStruct
        
        private static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            local integer i
            local real x = GetSpellTargetX()
            local real y = GetSpellTargetY()
            local real x1
            local real y1
            local real a
            
            set this.caster = GetTriggerUnit()
            set i = GetUnitAbilityLevel(this.caster, ABI_ID)
            set x1 = GetUnitX(this.caster)
            set y1 = GetUnitY(this.caster)
            set a = Atan2(y - y1, x - x1)
            
            set this.m = Missile.create(x1 + 100*Cos(a), y1 + 100*Sin(a), GetUnitZ(this.caster) + HEIGHT_DIFFERENCE, a, SquareRoot((x - x1)*(x - x1) + (y - y1)*(y - y1)) - 100, GetTerrainZ(x, y) + HEIGHT_DIFFERENCE)
            set m.source = this.caster
            set m.speed = GetPrimarySpeed(i)/32
            set m.model = PRIMARY_MISSILEFX
            set m.collision = PRIMARY_AOE
            set m.scale = PRIMARY_MISSILE_SCALE
            set m.data = this
            
            static if LIBRARY_FloatingTextArc then
                call ArcingTextTag.create("|c00ECEC00 VORPAL EXTERMINATION!|r", this.caster)
            endif

            call launch(m)
            
            return this
        endmethod
        
        static if not LIBRARY_SpellEffectEvent then
            private static method onCast takes nothing returns boolean
            
                if GetSpellAbilityId() == ABI_ID then
                    call thistype.create()
                endif
                
                return false
            endmethod
        endif
        
        private static method onInit takes nothing returns nothing
            local trigger t
        
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(ABI_ID, function thistype.create)
            else
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Condition(function thistype.onCast))
                set t = null
            endif
            
            call LightningSettings()
        endmethod
    endstruct
endlibrary


+ v1.0.0.0: Initial release.
+ v1.0.0.1: Removed null check for instant unit enumeration.
+ v1.0.0.2: Improved minor coding flaws. Added some code comments.
+ v1.0.0.3: Some misc improvements. Not the major update I am talking about though.
  • Now main glaive will travel to target point instead of max range.
  • Main glaive now spawn slightly ahead of caster to avoid damaging units behind.
  • Readjust spin aoe to fit model.
  • Change main glaive model to fit the GIF.
  • Improve description.
+ v1.0.0.4: Allow users to modify used lightning types.
+ v1.0.0.5: Major update. Various changes are implemented.
+ v1.0.0.6: Added configuration for glaive-release activation unit.
+ v1.0.0.7: Fixed two minor bugs, improved the energy gather effect and updated a required library.
+ LibUpdate: Updated the DelayedDummyRecycler lib with the approved version. Some alterations are made to the test map also.
+ v1.0.0.8: Minor code and effect improvements. DelayedDummyRecycler lib updated to newest version.


Keywords:
shuriken, glaive, explosion, swag, yolo, magtheridon96, vJASS
Contents

Vorpal Extermination (Map)

Reviews
05:59, 13th Aug 2013 Magtheridon96: Approved. 5/5 Good work ;)

Moderator

M

Moderator

05:59, 13th Aug 2013
Magtheridon96: Approved. 5/5
Good work ;)
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
You don't need to check unit_type_id == null when enumerating units instantly. Only when the same group is looped through over time.

Ah yes of course. Thanks Maker. Spell updated :)

Try using SpellStruct too, and maybe even Spell ;)

I tried using Spell but it seems to require SpellStruct for onEndCast. Thus I ended up using DummyCaster because it is lighter. I had 19 libraries already and I am afraid people will hit me for using more >_<

Thank you very much for your suggestion :)
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Don't be afraid to use systems ;). Use as many as you need.

I recommend SpellStruct so that you get a performance improvement and so that your code is a bit simpler.

All of this will turn into a 1 line module

JASS:
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(ABI_ID, function thistype.create)
            else
                set t = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddCondition(t, Condition(function thistype.onCast))
                set t = null
            endif

And you can work off of other events if you want too by just adding more methods.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I haven't checked the code thoroughly but it looks nice.

I feel that the spell is contrived.
Basing by looks alone, it doesn't feel cohesive to have spinning blades shoot random lightning blasts and make huge explosions. I find it difficult to assign a theme to this spell.

If you're looking to practice eye candy, try making the blade spinning for 5 seconds look more active than just standing in the same spot. Ex: Make the blade look like it's gathering energy while it spins before it does the explosion. You could show pulses of energy moving into the blade.
Remember, having a spell with good eye-candy doesn't necessarily mean having a lot of effects.
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
I haven't checked the code thoroughly but it looks nice.

I feel that the spell is contrived.
Basing by looks alone, it doesn't feel cohesive to have spinning blades shoot random lightning blasts and make huge explosions. I find it difficult to assign a theme to this spell.

If you're looking to practice eye candy, try making the blade spinning for 5 seconds look more active than just standing in the same spot. Ex: Make the blade look like it's gathering energy while it spins before it does the explosion. You could show pulses of energy moving into the blade.
Remember, having a spell with good eye-candy doesn't necessarily mean having a lot of effects.

Ah thanks a lot. That is definitely what I am looking for.

I am currently working on an update of the spell. Hope you can come back later to take a look at it again :)
 
ArcingFloatingText by Maker. (for more swag)
#SWAG RULES!!

I tried using Spell but it seems to require SpellStruct for onEndCast. Thus I ended up using DummyCaster because it is lighter. I had 19 libraries already and I am afraid people will hit me for using more >_<
I usually hit people when they uses more than 10 libraries :ogre_hurrhurr:



Honestly, I can't find anything wrong and the spell looks great.
Don't whack me, but is there a way to configure the time and the target type that must be hit when the shuriken enlarge?
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
only thing i can see that you should fix is this.
GetOwningPlayer(caster)
It's in your filter unit function.
You should make the function take the owning player instead.
Anything that gets used twice or more should be stored.
If you have to loop through 10 units that gets called 10 times.

Why do you use this ?
JASS:
set bj_lastCreatedLightning = AddLightningEx(LightningRandomizer(), true, this.x, this.y, this.z, m.impact.x, m.impact.y, m.impact.z)
                        call TimedL.P2P(bj_lastCreatedLightning, INACTIVE_PERIOD, 1, 0)

Even with those i have to give it a 5/5
This is a great looking spell and well executed
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
only thing i can see that you should fix is this.
GetOwningPlayer(caster)
It's in your filter unit function.
You should make the function take the owning player instead.
Anything that gets used twice or more should be stored.
If you have to loop through 10 units that gets called 10 times.

Why do you use this ?
JASS:
set bj_lastCreatedLightning = AddLightningEx(LightningRandomizer(), true, this.x, this.y, this.z, m.impact.x, m.impact.y, m.impact.z)
                        call TimedL.P2P(bj_lastCreatedLightning, INACTIVE_PERIOD, 1, 0)

Even with those i have to give it a 5/5
This is a great looking spell and well executed

I don't know really. I think I will just leave it there. What if a person wanna add some kind of wacky condition for the caster to damage the target? You can never know :p

Eh about the 2 or more show me where I didn't do that.

The lightning? Well that is how Maker's system works. Besides bj_lastCreatedLightning is a global, why bother creating a local and nulling it later?

Thanks for the rating ^^
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
u can still pass caster and the owning player of caster through to the function so that they can do this.

The 2 or more is for GetOwningPlayer(caster)
think about it this way you look through 10 units. That means that you have to call the function to get the owning player 10 times.

I never used Maker's system it just looked odd to me. That's good then.

No problem this spell deserves it.
 
Level 16
Joined
Dec 15, 2011
Messages
1,423
u can still pass caster and the owning player of caster through to the function so that they can do this.

The 2 or more is for GetOwningPlayer(caster)
think about it this way you look through 10 units. That means that you have to call the function to get the owning player 10 times.

I never used Maker's system it just looked odd to me. That's good then.

No problem this spell deserves it.

Yeah well...alright fine I will add it.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I really think all of the lightning at the end is way over the top. It would have been better if it was just a spinning blade ;p. Better yet, if the unit threw the spinning blade and it flew and landed and stuff, that mighta been cool. Or if the blade came out of the ground and was like a propeller on top of something etc, that woulda been interesting, but don't think that 2nd one can really be done in wc3.
 
Level 29
Joined
Oct 24, 2012
Messages
6,543
I really think all of the lightning at the end is way over the top. It would have been better if it was just a spinning blade ;p. Better yet, if the unit threw the spinning blade and it flew and landed and stuff, that mighta been cool. Or if the blade came out of the ground and was like a propeller on top of something etc, that woulda been interesting, but don't think that 2nd one can really be done in wc3.

The 2nd one could most likely be done by simulating it using the terrain height and a set distance from terrain height for the shuriken.

also the lightning at the end looks fitting to this since it looks like the shuriken is pulling in the lightning ( like it's charging up). Then it gives a big discharge.
 
Top