Bladedance

This bundle is marked as pending. It has not been reviewed by a staff member yet.

Blade Dance​

"One warrior becomes twelve, and twelve become a storm of blades."

After a brief channel, the Hero creates 12 untargetable clones of himself. 6 clones are placed within a 750 AoE, while 6 additional clones travel outward in all directions to half that distance before stopping.

Each clone gains +50 Attack Range and automatically attacks nearby enemy Heroes. Clones inherit the Hero's items, level, attributes, stats, and learned passive abilities, allowing them to mirror the Hero's attacking power with devastating efficiency.

The clones remain active for 20 seconds before vanishing. Clones are removed and thus do not trigger on-death effects from copied items / abilities.

Level Details​

Level 1 – Clones inherit 33% of the Hero's attributes and stats.
Level 2 – Clones inherit 66% of the Hero's attributes and stats.
Level 3 – Clones inherit 100% of the Hero's attributes and stats.
390957-92688a5ca46eea2a26cfb4dba8c35e91.webp

Features​

  • Creates 12 fully functional combat clones
  • Dynamic clone deployment pattern
  • Automatic enemy Hero targeting
  • Inherits items, level, attributes, stats, and passive abilities
  • Scales with the Hero's attributes and spells
  • Leak-free (i believe)
  • Highly configurable through the variable trigger
  • BladedanceVariables
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Hashtable created for storing and setting up some stuff with Clones, dont touch --------
      • Hashtable - Create a hashtable
      • Set VariableSet BladedanceHash = (Last created hashtable)
      • -------- set this to the Bladedance ability --------
      • Set VariableSet BladedanceAbility = Bladedance
      • -------- This sets what unit is the main caster, and what unit is the clone --------
      • -------- You need to create an exact copy of your hero who will be casting this in the object editor --------
      • -------- But you need to make some changes to the copied version. Written in ReadMe Trigger --------
      • Set VariableSet BladedanceCasterUnitType = Blackrock of Blademaster Clan
      • Set VariableSet BladedanceCloneUnitType = Blackrock of Blademaster Clan (UltiClone)
      • -------- Duration of how long clones last --------
      • Set VariableSet BladedanceDuration = 20.00
      • -------- ---- --------
      • -------- These 2 variables dictate how far the individual circles of clones will go from the caster --------
      • -------- If you want the circle smaller, set outer distance to 600, and inner to 300 for example --------
      • -------- by default the clones are always meant to go Distance (x) and Distance (x / 2) --------
      • Set VariableSet BladedanceInnerDistance = 375.00
      • Set VariableSet BladedanceOuterDistance = 750.00
      • -------- ---- --------
      • -------- Clones get +50 bonus range. Its better to set it here than in object editor imo --------
      • Set VariableSet BladedanceBonusAttackRange = 50.00
      • -------- This is the movespeed. When you make your cloned unit in Object Editor, the movement speed of that unit and this variable must match --------
      • -------- So for example if your hero has native MS of 270, and you copypaste it to make the clone unit, you need to set this variable to 270 as well --------
      • Set VariableSet BladedanceCloneMoveSpeed = 320.00
      • -------- Special effect that plays when clones timeout. If you want to change the effect, the \ dash from object editor paths must always be doubled \\ in code --------
      • Set VariableSet BladedanceExpireEffect = Abilities\\Spells\\Orc\\FeralSpirit\\feralspiritdone.mdl
      • -------- ---------- --------
      • -------- Inheritable Spells setup. Check ReadMe text trigger for more info --------
      • Set VariableSet BladedancePassives[1] = Critical Strike
      • Set VariableSet BladedancePassivesMax = 1
      • -------- ---------- --------
      • -------- This variable sets weather or not Clones inherit hero items --------
      • -------- if set to False, Clones will not get hero items, only Attributes and passive spells --------
      • Set VariableSet BladedanceCopyItemsBool = True

JASS:
library Bladedance initializer Init
private function Conditions takes nothing returns boolean
//this sets which ability triggers the cast
//to change this, just change the ability code type variable
//in the GUI init, this updates automatically to that
    return GetSpellAbilityId() == udg_BladedanceAbility
endfunction

private function Expire takes nothing returns nothing
//Local Timer functions used to remove the Clones without triggering Unit - Dies effects
    local timer t = GetExpiredTimer()
    local integer timerId = GetHandleId(t)
    local integer i = 0
    local unit clone
    local effect startEffect

    loop
        exitwhen i >= 12

        set clone = LoadUnitHandle(udg_BladedanceHash, timerId, i)

        if clone != null then
            call DestroyEffect(AddSpecialEffect(udg_BladedanceExpireEffect, GetUnitX(clone), GetUnitY(clone)))
            call RemoveUnit(clone)
        endif

        set i = i + 1
    endloop

    //destroy persistent aura effect created on cast
    set startEffect = LoadEffectHandle(udg_BladedanceHash, timerId, 200)

    if startEffect != null then
        call BlzSetSpecialEffectAlpha(startEffect, 0)
        call BlzSetSpecialEffectScale(startEffect, 0.01)
        call DestroyEffect(startEffect)
    endif

    call FlushChildHashtable(udg_BladedanceHash, timerId)
    call DestroyTimer(t)

    set startEffect = null
    set clone = null
    set t = null
endfunction

private function StopClones takes nothing returns nothing
//This function makes clones stop when they reach their designed position
//To change those positions, use the premade GUI variables
//dont mess with this unless you know what you are doing
    local timer t = GetExpiredTimer()
    local integer timerId = GetHandleId(t)
    local integer startIndex = LoadInteger(udg_BladedanceHash, timerId, 100)
    local integer endIndex = LoadInteger(udg_BladedanceHash, timerId, 101)
    local integer spellTimerId = LoadInteger(udg_BladedanceHash, timerId, 102)
    local integer i = startIndex
    local unit clone

    loop
        exitwhen i > endIndex

        set clone = LoadUnitHandle(udg_BladedanceHash, spellTimerId, i)

        if clone != null then
            call IssueImmediateOrder(clone, "holdposition")
            call BlzSetUnitRealField(clone, UNIT_RF_SPEED, 0.00)
        endif

        set i = i + 1
    endloop

    call FlushChildHashtable(udg_BladedanceHash, timerId)
    call DestroyTimer(t)

    set clone = null
    set t = null
endfunction

private function CopyInventory takes unit caster, unit clone returns nothing
//Function that copies the casting heros inventory
//items given to clones that (drop on death) do not create dropped copies
//all on-hit functions are inherited by clones too
//to disable clones copying inventory, change BladedanceCopyItemsBool to False in variable init
    local integer i = 0
    local item casterItem
    local item clonesItem

    loop
        exitwhen i >= bj_MAX_INVENTORY

        set casterItem = UnitItemInSlot(caster, i)

        if casterItem != null then
            set clonesItem = CreateItem(GetItemTypeId(casterItem), GetUnitX(clone), GetUnitY(clone))
            call UnitAddItem(clone, clonesItem)
        endif

        set i = i + 1
    endloop

    set casterItem = null
    set clonesItem = null
endfunction

private function CopyPassiveAbilities takes unit caster, unit clone returns nothing
//this function copies passibe abilieties of heroes such as Critical Strike, bash, cleave, poison on hit etc...
//to add or remove abilities that the clone inherits, use GUI variables "BladedancePassives" in the variable init
    local integer i = 1
    local integer abilityId
    local integer abilityLevel

    loop
        exitwhen i > udg_BladedancePassivesMax

        set abilityId = udg_BladedancePassives[i]
        set abilityLevel = GetUnitAbilityLevel(caster, abilityId)

        if abilityLevel > 0 then
            call UnitAddAbility(clone, abilityId)
            call SetUnitAbilityLevel(clone, abilityId, abilityLevel)
        endif

        set i = i + 1
    endloop
endfunction

private function Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    local player owner
    local timer t
    local integer timerId
    local timer stopTimer
    local integer stopTimerId
    local effect startEffect

    local integer spellLevel
    local integer casterLevel
    local real multiplier
    local integer strength
    local integer agility
    local integer intelligence

    local integer i
    local unit clone

    local real casterX
    local real casterY
    local real facing

    local real angle
    local real targetX
    local real targetY

//This event triggers when the casting of the ability begins to play an animation
//This is done because blizzard is fucking dogshit and the object editor natives doet work properly
    if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
        call TriggerSleepAction(0.00)
        call SetUnitAnimationByIndex(caster, 4)

        set caster = null
        return
    endif

    set owner = GetOwningPlayer(caster)
    set t = CreateTimer()
    set timerId = GetHandleId(t)
    set i = 0

    set spellLevel = GetUnitAbilityLevel(caster, udg_BladedanceAbility)
    set casterLevel = GetHeroLevel(caster)

    set casterX = GetUnitX(caster)
    set casterY = GetUnitY(caster)
    set facing = GetUnitFacing(caster)

//persistent aura effect that lasts as long as the clones
    set startEffect = AddSpecialEffect("ManaAura_SpinEdit.mdx", casterX, casterY)
    call BlzSetSpecialEffectAlpha(startEffect, 55)
    call BlzSetSpecialEffectTimeScale(startEffect, 0.15)
    call BlzSetSpecialEffectScale(startEffect, 8.75)
    call SaveEffectHandle(udg_BladedanceHash, timerId, 200, startEffect)

    if spellLevel == 1 then
        set multiplier = 0.33
    elseif spellLevel == 2 then
        set multiplier = 0.66
    else
        set multiplier = 1.00
    endif

//This part copies the heroes Str + Agi + Int and then gives it to the clone
    set strength = R2I(GetHeroStr(caster, true) * multiplier)
    set agility = R2I(GetHeroAgi(caster, true) * multiplier)
    set intelligence = R2I(GetHeroInt(caster, true) * multiplier)

    loop
        exitwhen i >= 6

        set angle = (90.00 - I2R(i) * 60.00) * bj_DEGTORAD

        set targetX = casterX + udg_BladedanceOuterDistance * Cos(angle)
        set targetY = casterY + udg_BladedanceOuterDistance * Sin(angle)
//clone creation (unit type of created clone is set in GUI Variable)
        set clone = CreateUnit(owner, udg_BladedanceCloneUnitType, casterX, casterY, facing)
//disables XP for clone (important, dont touch)
        call SuspendHeroXP(clone, true)
//Declares weather units should copy items or not from the hero based on Bool variable
        if udg_BladedanceCopyItemsBool then
            call CopyInventory(caster, clone)
        endif

        call SetHeroLevel(clone, casterLevel, false)
        call SetHeroStr(clone, strength, true)
        call SetHeroAgi(clone, agility, true)
        call SetHeroInt(clone, intelligence, true)
//runs the function that copies passive abilities after checking for learned / level
        call CopyPassiveAbilities(caster, clone)
//add 50 range to clones. Its ran for both 0 and 1 index because idk, it just doesnt work when i do 0 or 1 only...
        call BlzSetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 0, BlzGetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 0) + udg_BladedanceBonusAttackRange)
        call BlzSetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 1, BlzGetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 1) + udg_BladedanceBonusAttackRange)
//this function adds the cloned unit transparency. I didnt bother to variable this, but maybe i should?
//anyway if you want the clones to be MORE ghosty or LEES ghosty, just change the 200 at the end to 0-255 (255 is 0% transparent)
        call SetUnitVertexColor(clone, 100, 100, 100, 200)

        call IssuePointOrder(clone, "move", targetX, targetY)
        call SaveUnitHandle(udg_BladedanceHash, timerId, i, clone)
//And now is just does the exact same thing again, for the second row of clones
        set targetX = casterX + udg_BladedanceInnerDistance * Cos(angle)
        set targetY = casterY + udg_BladedanceInnerDistance * Sin(angle)

        set clone = CreateUnit(owner, udg_BladedanceCloneUnitType, casterX, casterY, facing)
//disables XP for clone (important, dont touch)
        call SuspendHeroXP(clone, true)

        if udg_BladedanceCopyItemsBool then
            call CopyInventory(caster, clone)
        endif

        call SetHeroLevel(clone, casterLevel, false)
        call SetHeroStr(clone, strength, true)
        call SetHeroAgi(clone, agility, true)
        call SetHeroInt(clone, intelligence, true)
        call CopyPassiveAbilities(caster, clone)

        call BlzSetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 0, BlzGetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 0) + udg_BladedanceBonusAttackRange)
        call BlzSetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 1, BlzGetUnitWeaponRealField(clone, UNIT_WEAPON_RF_ATTACK_RANGE, 1) + udg_BladedanceBonusAttackRange)
        call SetUnitVertexColor(clone, 100, 100, 100, 200)

        call IssuePointOrder(clone, "move", targetX, targetY)
        call SaveUnitHandle(udg_BladedanceHash, timerId, i + 6, clone)

        set i = i + 1
    endloop
//timer starting for expiration
    set stopTimer = CreateTimer()
    set stopTimerId = GetHandleId(stopTimer)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 100, 6)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 101, 11)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 102, timerId)
    call TimerStart(stopTimer, udg_BladedanceInnerDistance / udg_BladedanceCloneMoveSpeed, false, function StopClones)

    set stopTimer = CreateTimer()
    set stopTimerId = GetHandleId(stopTimer)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 100, 0)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 101, 5)
    call SaveInteger(udg_BladedanceHash, stopTimerId, 102, timerId)
    call TimerStart(stopTimer, udg_BladedanceOuterDistance / udg_BladedanceCloneMoveSpeed, false, function StopClones)

    call TimerStart(t, udg_BladedanceDuration, false, function Expire)
//locals cleaup
    set startEffect = null
    set caster = null
    set owner = null
    set clone = null
    set t = null
    set stopTimer = null
endfunction

//Function registering. DO NOT MESS WITH THIS unless you know what you are doing
private function Init takes nothing returns nothing
    local trigger trig = CreateTrigger()

    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
    call TriggerRegisterAnyUnitEventBJ(trig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(trig, Condition(function Conditions))
    call TriggerAddAction(trig, function Actions)

    set trig = null
endfunction
endlibrary


Uses several great models from the amazing people at Hive Workshop
Floor Texture: ManaAura (edited to Spin + Color change)
Clones Walk Effect: Windwalk
Clones Circle Effect: Domination Aura
Contents

Bladedance Test (Map)

Back
Top