[v]Jass - Poly Slash v0.2

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Poly Slash
The hero divides up into several copies of himself, which will dash towards any enemy in the selected area to deliver a furious strike. Each enemy will only be striked once.

Level 1
Level 2Level 3

Damage: 70
Damage: 110Damage: 150

Copies: 3
Copies: 4Copies: 5

Area of Effect: 250
Area of Effect: 350Area of Effect: 450

This spell requires Jass New Gen Pack. Download it from here. Credit is given to Risin_Dusk for Group Utils.

Spell Code:
library PolySlash initializer Init requires TNTK, GroupUtils

        private constant integer SID = 'pLsL'
        //The rawcode of the ability
        private constant real TRAVEL_SPEED = 8.43
        //The movement speed of the "mirror images"
        private constant real MOVE_ANIMATION_TIME = 1.133
        //The duration of the animation which a mirror image performes
        //while it is moving around
        private constant real MOVE_ANIMATION_TIME_MOD = 1.5
        //The animation de- or acceleration. Having a value of 1.0 means
        //that the animation is played with regualr speed, a value of 2.0
        //means the animation is played twice as fast as normal
        private constant boolean FORCE_REPICK = true
        //When the mirror images are reuniting back into the caster, it
        //will be automatically picked for the controlling player
        //if set to true
        private constant string MOVE_ANIMATION_NAME = "Attack Slam"
        //The name of the animation a mirror image performs while moving around
        private constant string CASTER_SFX = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl"
        //The path of a special effect which is being attached to the mirror images
        private constant string TARGET_SFX = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
        //The path of a special effect which is being displayed when a target unit gets hit
        private constant string CASTER_SFX_ATTACHMENT = "weapon"
        //Where should the special effect be attached to the mirror image
        private constant string TARGET_SFX_ATTACHMENT = "origin"
        //Where should the special effect be attached to the target
        //Selfexplaining damage options...
        private constant attacktype ATTACK = ATTACK_TYPE_NORMAL
        private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
        private constant weapontype WEAPON = null
    //The amount of damage depending of the ability level being inflicted to a target when hit 
    private constant function GetDamage takes real level returns real
        return 40 * level + 30
    //The amount of mirror images spawned per level
    private constant function GetCopies takes integer level returns integer
        return 1 * level + 2
    //The radius of the Area of Effect
    private constant function GetAoERange takes real level returns real
        return 100 * level + 150
    //Spellcode begins here. PLEASE DO NOT TOUCH ! Thank you !
    //Forward struct declaration
    private keyword polyslash_data
        private filterfunc ENUM_FILTER //Used to acquire valid targets
        private player     TMP_PLAYER  //Used as paramter substitue
        private group      TMP_GROUP   //Used filter invalid units off the selection
                                       //Ratio between speed modifier an animation time
        private integer    TMP_COUNTER //Used to count the number of targets

    //Customized knockback for this spell
    private struct polyslash_move extends TNTKnock
        effect sfx
        real   anim_time = .0
        method operator psd takes nothing returns polyslash_data
            return polyslash_data(.data)
        method operator psd= takes polyslash_data psd returns nothing
            set .data = integer(psd)
        //Responsible for movement and animations
        method onKnock takes nothing returns nothing
            call SetUnitX( .knocker, .x )
            call SetUnitY( .knocker, .y )
            call SetUnitFacing( .knocker, .radiants*bj_RADTODEG )
            if anim_time <= .0 then
                call SetUnitAnimation( .knocker, MOVE_ANIMATION_NAME )
                set .anim_time = TM_RATIO
                set .anim_time = .anim_time - .runtime

        //Manages the target selection and ends a knockback
        method onBreak takes nothing returns boolean
            //If true a mirror image reached its target unit
            if super.onBreak() then
                //If the caster was the target this instance can be destroyed
                if .target == .psd.caster then
                    return true
                //Damage the current target and play the effect
                call UnitDamageTarget( .psd.caster, .target, .psd.damage, false, false, ATTACK, DAMAGE, WEAPON )
                call DestroyEffect( AddSpecialEffectTarget( TARGET_SFX, .target, TARGET_SFX_ATTACHMENT ))
                //Leave the old target behind ang acquire a new one if possible
                if .psd.target_count > 0 then
                    call .psd.UpdateGroupState()
                    set .target = FirstOfGroup(.psd.targets)
                    call GroupRemoveUnit(.psd.targets, .target )
                    set .psd.target_count = .psd.target_count -1
                    //If all targets recieved their damage return to the caster
                    set .target = .psd.caster
            //The mirror image is still on its way
            return false
        //Customized knockback dstructor method
        method onDestroy takes nothing returns nothing
            //Clean up stuff
            call RemoveUnit(.knocker)
            call DestroyEffect(.sfx)
            call SetUnitVertexColor( .psd.caster, 255, 255, 255, 255 / .psd.copy_count )
            //If this is the first unit which should return to the caster then we must
            //unhide the caster again
            if .psd.target_count == 0 then
                call ShowUnit( .psd.caster, true )
                static if FORCE_REPICK then 
                    if GetLocalPlayer() == GetOwningPlayer(.psd.caster) then
                        call SelectUnit( .psd.caster, true )
            //If there are no mirror images left we can destroy the remaining instances
            set .psd.copy_count = .psd.copy_count -1
            if .psd.copy_count == 0 then
                call .psd.destroy()
    //Sorta managing struct
    private struct polyslash_data
        unit caster
        group targets
        integer target_count 
        integer copy_count
        real damage
        method onDestroy takes nothing returns nothing
            call ReleaseGroup(.targets)
            //call ShowUnit(.caster, true)
        //Loops through the selection and removes invalid units
        static method UpdateGroupState_enum takes nothing returns nothing
            local unit u = GetEnumUnit()
            if (u == null) or (GetWidgetLife(u) < 0.405) then
                call GroupRemoveUnit( TMP_GROUP, u )
                set TMP_COUNTER = TMP_COUNTER +1
            set u = null
        //Starts the check loop
        method UpdateGroupState takes nothing returns nothing
            set TMP_COUNTER = 0
            set TMP_GROUP = .targets
            call ForGroup(.targets, function thistype.UpdateGroupState_enum )
            set .target_count = TMP_COUNTER
        //Creates and configures a mirror image
        method setup_copy takes real x, real y, unit t returns nothing
            local unit u = CreateUnit( Player(13), GetUnitTypeId(.caster), x, y, 0. )
            local polyslash_move psm = polyslash_move.create()
            //Create the unit and its knockback instance
            set psm.psd = this
            set psm.sfx = AddSpecialEffectTarget( CASTER_SFX, u, CASTER_SFX_ATTACHMENT )
            call psm.StartHomingTimed(u, t, x, y, TRAVEL_SPEED )
            //Setup the knockback
            call UnitAddAbility( u, 'Aloc' )
            call SetUnitTimeScale( u, MOVE_ANIMATION_TIME_MOD )
            call SetUnitColor( u, GetPlayerColor( TMP_PLAYER ))
            call SetUnitVertexColor( u, 255, 255, 255, 50 + 255 / .copy_count )
            call UnitShareVision( u, TMP_PLAYER , true )
            //Setup the unit
        //Setup for the spell
        static method create takes unit ca, real x, real y returns thistype
            local thistype this = thistype.allocate()
            local unit t
            local integer i = GetUnitAbilityLevel(ca, SID)
            local real cx = GetUnitX(ca)
            local real cy = GetUnitY(ca)
            set .caster = ca
            set .targets = NewGroup()
            set .damage = GetDamage(i)
            set .copy_count = GetCopies(i)
            set TMP_PLAYER = GetOwningPlayer(ca)
            set TMP_COUNTER = 0
            call GroupEnumUnitsInArea( .targets, x, y, GetAoERange(i), ENUM_FILTER )
            call GroupRemoveUnit( .targets, .caster )
            set .target_count = TMP_COUNTER
            //Acquire target selection, level depeing stuff, coords, etc...
            call SetUnitX( ca, x )
            call SetUnitY( ca, y )
            call ShowUnit( ca, false )
            //Setup the caster
            //In case there is no valid target we will just send a mirror image to the
            //target location
            if .target_count == 0 then
                set .copy_count = 1
                call .setup_copy( cx, cy, ca )

                //In case there would be more mirror images than targets
                //we will just leave out the obsolete ones
                if .copy_count > .target_count then
                    set .copy_count = .target_count
                //Creates the required mirror images
                set i = 0
                    exitwhen i == .copy_count                    
                    call .UpdateGroupState()
                    set t = FirstOfGroup( .targets )
                    call GroupRemoveUnit( .targets, t)
                    call .setup_copy( cx, cy, t )
                    set i = i+1
                call .UpdateGroupState()
            set t = null
            return this
    //Filter which specifies if a unit is a target or not
    private function TargetFilter takes nothing returns boolean
        local unit u = GetFilterUnit()
        if IsUnitEnemy(u,TMP_PLAYER) and not (      /*
      */   GetWidgetLife(u) < 0.405              or /*
      */   IsUnitType(u, UNIT_TYPE_STRUCTURE)    or /*
      */   IsUnitType(u, UNIT_TYPE_FLYING)       or /*
      */   IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) or /*
      */   IsUnitType(u, UNIT_TYPE_ANCIENT)      or /*
      */   IsUnitInvisible(u, TMP_PLAYER)        )  /*
            set TMP_COUNTER = TMP_COUNTER +1
            set u = null
            return true
        set u = null
        return true
    //Launches the spell
    private function onCast takes nothing returns boolean
        if GetSpellAbilityId() == SID then
            call polyslash_data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY() )
        return false
    private function Init takes nothing returns nothing
        local trigger trig = CreateTrigger()
        set ENUM_FILTER = Filter( function TargetFilter )
        call Preload(CASTER_SFX)
        call Preload(TARGET_SFX)
        call PreloadStart()
        call TriggerAddCondition( trig, Filter( function onCast ) )
        call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )


v 0.1
-Initial Release

v 0.2
  • Adjusted the Damage settings.
  • Removed the obsolete onDestroy from polyslash_data
  • Replaced IsUnitType() with GetWidgetLife()
  • Replaced TriggerAction with TriggerCondition.

Poly, Slash, Sword, Sowrdmaster, mirror image, Polyslash, jump, moving

Poly Slash Test Enviorment (Map)

9th Nov 2011 Bribe: It's just another knockback. There are hundreds of these. Yours uses lots of function interfaces, bad API and for what benefit over the others? Your "onLoop" method is also called via function interface making this much slower...




9th Nov 2011
Bribe: It's just another knockback. There are hundreds of these. Yours uses lots of function interfaces, bad API and for what benefit over the others?

Your "onLoop" method is also called via function interface making this much slower and bulkier than it needs to be.

Rising_Dusk's knockback is better and I consider this redundant in its favor.
Reading the code.
First of all, good job on using a library ;)



private constant attacktype ATTACK = null
private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON = WEAPON_TYPE_WHOKNOWS


private constant attacktype ATTACK = ATTACK_TYPE_NORMAL
private constant damagetype DAMAGE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WEAPON = null

Another thing.. don't use onDestroy..

Also, to check if a unit is alive, you could simply use this: GetWidgetLife(u)>=0.405

Finally, don't use TriggerAddAction because TriggerAddCondition is better and faster :)
You just have to return false at the end ^^

Oh and:

call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )

Also, when it comes to locals, try to make the names as short as possible for efficieny :D
Level 9
Aug 2, 2008
Thanks for your hints Magtheridon96 :)

Regarding the damage options my memory didnt serve me very well, i might change that later.

Since i'm trying to exploit OOP i need the onDestroy in the polyslash_move struct but i might get rid of the onDestroy in polyslash_data.

If i recall correctly there was once an issue with GetWidgetLife(u)>=0.405 and thats why i prefer GetUnitState(). And i think technically there is not much different about those two natives.

In future i will prefer TriggerConditions over TriggerActions :)

Is there a big difference between EVENT_PLAYER_UNIT_SPELL_CAST and EVENT_PLAYER_UNIT_SPELL_EFFECT ? I will change it anyway.

Im going to update this as soon as a mod reviewd this.
Level 17
Mar 17, 2009
Nice to see you still active Thanathos :)
& I see your back baassee ;)

Yeah the GetWidgetLife is preferable although GetUnitState would work... it's just better coding (speedwise it BARELY differs, but we tend to squeeze as much speed as possible :p). But in my opinion, not much of a big deal.

But the spell cast is an issue you need to fix ;)

& in my opinion, start fixing before the mod checks it out... why wait? :p
Last edited:
Level 9
Aug 2, 2008
Updated the spell to v0.2

Fixed all issues mentioned by Magtheridon96, except the onDestroy problem (For more information see changelog).
I've tried to do it the way Luorax said, but i somehow got malfunctions and errors, which is probably because of the interface or the multiple struct inheritations. However i will repair this as fast as possible.

Further thanks and rep+ to you all for your hints :)

And its also nice to see you again seed :)
Level 16
Aug 7, 2009
I've tried to do it the way Luorax said, but i somehow got malfunctions and errors, which is probably because of the interface or the multiple struct inheritations. However i will repair this as fast as possible.

Exactly that's what causes errors :D I'll check the code again when the TSM vs Fnatic match ends and will come back with some suggestions.

EDIT: okay, so that's how I'd do this:

1, Add this to the basic interface (Ext): method doDestroy takes nothing returns nothing defaults nothing

2, rename onDestroy() in struct "Indexable" to destroy, call this.doDestroy() and then call this.deallocate()

3, rename onDestroy() in struct "TNTKnock" and in the move struct to doDestroy()

4, call super.doDestroy() at the end of the doDestroy() method in struct move.

I think that's all.

EDIT2: however I just realised that the "doDestroy" method in TNTKnockback is useless. You're initializing those members in the create method(s) and nulling them is useless. You should simply remove that method and you don't have to call super.doDestroy() in your move struct.
And a hotkey would be fine :p Like E or R
Last edited:
Level 9
Aug 2, 2008
Well that would do it but its kinda pointless.

The idea of replacing onDestroy with destroy and calling deallocate instead is to boost the performance by getting rid of the trigger mechanism introduced Vex to manage the destruction of a struct instance. If this works successfully you avoid the slower triggers and have just some nested function calls instead.

If i now would use an interface method for destroying my structs JassHelper will add an trigger mechanism (similar to that one introduced by Vex) that takes care of the calls of that certain method. So i would just change the names of the methods while the desired performance boost will simply not occur.

Further i think that Vex's built in destructor would be still more efficient than a custom mechanism, since Vex's destructor is optimized while the custom mechanism i just an "all purpose" interface method.

All i know is that your way would be at least that efficient than Vex's method, to make a definite statement i need to compare the compiled version of your mechanism and Vex's destructor.

Besides, have you ever seen a vJass script which has been turned into normal jass by JassHelper ? It looks horrible xD
Level 16
Aug 7, 2009
I did not know that interfaces use triggers :p But you know, we learn something every day :D (However I should have known, it's just simple logic)
But there's still no need for the Knockback onDestroy method.
Yea, vJASS looks horrible in normal JASS. However array structs are okayish.