Spellpack The Dark Lady

Ayanami

칼리
Reaction score
288
Introduction
After playing The Dark Lady from Heroes of Newerth, I was inspired by her cool spells. Thus, I decided to recreate her spells in Warcraft. The spells may not be exactly the same, but it should be very similar. Here's the actual information on The Dark Lady's spells.

The spells:
  • Dark Blades [Active]
  • Taint Soul [Active]
  • Charging Strikes [Active]
  • Cover of Darkness [Active]

Details
- The spells are vJASS.
- The spells should be leakless. Feedback will be appreciated if otherwise.
- It is MUI, meaning that the spell can run multiple times at the same instance.
- A hero from Heroes of Newerth


Requirements


Other Trigger
Caster Dummy
JASS:

library CasterDummy initializer InitTrig

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer DUMMY_ID = 'dDCD' // raw code of Caster Dummy
endglobals

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//

globals
    private unit dummy
endglobals

public function TargetUnit takes unit owner, unit target, integer abil, integer level, string order returns nothing
    call UnitAddAbility(dummy, abil)
    call SetUnitAbilityLevel(dummy, abil, level)
    call SetUnitOwner(dummy, GetOwningPlayer(owner), false)
    call IssueTargetOrder(dummy, order, target)
    call UnitRemoveAbility(dummy, abil)
endfunction

public function TargetPoint takes unit owner, real x, real y, integer abil, integer level, string order returns nothing
    call UnitAddAbility(dummy, abil)
    call SetUnitAbilityLevel(dummy, abil, level)
    call SetUnitOwner(dummy, GetOwningPlayer(owner), false)
    call IssuePointOrder(dummy, order, x, y)
    call UnitRemoveAbility(dummy, abil)
endfunction

private function InitTrig takes nothing returns nothing
    local trigger t = CreateTrigger()
    set dummy = CreateUnit(Player(0), DUMMY_ID, 0.00, 0.00, 0.00)
endfunction

endlibrary


Spells

Dark Blades
darkblades.jpg


Description:
The Dark Lady activates the malicious energy in her Wrath Blades, causing her attacks to silence foes for 2.5 seconds, preventing them from using spells and abilities. The malicious energy in her blades also increase her base damage temporarily.

Level 1 - Increases base damage by 50%. Lasts 3 seconds.
Level 2 - Increases base damage by 70%. Lasts 4 seconds.
Level 3 - Increases base damage by 90%. Lasts 5 seconds.
Level 4 - Increases base damage by 110%. Lasts 6 seconds.

Cast Range - Self
Target Type - Instant
Cooldown - 22/18/14/10 seconds

Screenshot:

darkbladesss.jpg

Code:
JASS:

library DarkBlades initializer InitTrig uses Damage

//=====================================================================//
//                           Dark Blades                               //
//                      by iAyanami aka Glenphir                       //
//=====================================================================//
//                                                                     //
//=====================================================================//
//                     Implementation Instructions                     //
//---------------------------------------------------------------------//
// Copy these objects into your map                                    //
// [Abilities]                                                         //
// 1) Dark Blades                                                      //
// 2) Dark Blades (Spell Book)                                         //
// 3) Dark Blades (Damage - Level 1)                                   //
// 4) Dark Blades (Damage - Level 2)                                   //
// 5) Dark Blades (Damage - Level 3)                                   //
// 6) Dark Blades (Damage - Level 4)                                   //
// 7) Dark Blades (Silence)                                            //
// Ensure that 3), 4), 5), and 6) are inside the Spell Book, 2)        //
//                                                                     //
// [Buffs]                                                             //
// 1) Dark Blades (Caster)                                             //
// 2) Dark Blades (Target)                                             //
//                                                                     //
//=====================================================================//

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer ABIL_ID1 = 'ABDB' // raw code of Ability "Dark Blades"
    private constant integer ABIL_ID2 = 'ADB5' // raw code of Ability "Dark Blades (Spell Book)"
    private constant integer ABIL_ID3 = 'ADB6' // raw code of Ability "Dark Blades (Silence)"
    private constant integer BUFF_ID = 'BDB1' // raw code of Buff "Dark Blades (Caster)"
    private constant boolean PLAYSOUND = true // true if sound is played upon casting
endglobals

private function GetDuration takes unit caster returns real
    return 2.00 + (GetUnitAbilityLevel(caster, ABIL_ID1)) // duration of the spell
endfunction

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//

private struct db
    unit u
    real r
endstruct
    
public function GetDarkBlades takes unit u returns integer
    return GetUnitAbilityLevel(u, ABIL_ID1)
endfunction

public function GetSilenceAbil takes nothing returns integer
    return ABIL_ID3
endfunction

public function GetSilence takes unit u returns integer
    return GetUnitAbilityLevel(u, ABIL_ID1)
endfunction

private function Expire takes nothing returns boolean
    local db d = KT_GetData()
    set d.r = d.r - 0.10
    if d.r <= 0.00 then
        call UnitRemoveAbility(d.u, ABIL_ID2)
        call UnitRemoveAbility(d.u, BUFF_ID)
        call d.destroy()
        return true
    elseif GetUnitAbilityLevel(d.u, ABIL_ID2) == 0 then
        call UnitAddAbility(d.u, ABIL_ID2)
        call SetUnitAbilityLevel(d.u, ABIL_ID2, GetUnitAbilityLevel(d.u, ABIL_ID1))
    endif
    return false
endfunction

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

private function Actions takes nothing returns nothing
    local db d = db.create()
    set d.u = GetTriggerUnit()
    set d.r = GetDuration(d.u)
    if PLAYSOUND then
        call StartSound(gg_snd_DarkBlades)
    endif
    call UnitAddAbility(d.u, ABIL_ID2)
    call SetUnitAbilityLevel(d.u, ABIL_ID2, GetUnitAbilityLevel(d.u, ABIL_ID1))
    call KT_Add(function Expire, d, 0.10)
endfunction

private function Silence takes nothing returns boolean
    local unit c = GetEventDamageSource()
    local unit t = GetTriggerUnit()
    if Damage_IsAttack() and GetUnitAbilityLevel(c, BUFF_ID) > 0 then
        call CasterDummy_TargetPoint(c, GetUnitX(t), GetUnitY(t), ABIL_ID3, GetUnitAbilityLevel(c, ABIL_ID1), "silence")
    endif
    set c = null
    set t = null
    return false
endfunction

private function InitTrig takes nothing returns nothing
    local trigger t = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local integer i = 0
    loop
        exitwhen i == 12
        call SetPlayerAbilityAvailable(Player(i), ABIL_ID2, false)
        set i = i + 1
    endloop
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Conditions))
    call TriggerAddAction(t, function Actions)
    call TriggerAddCondition(t2, Condition(function Silence))
    call Damage_RegisterEvent(t2)
endfunction

endlibrary


Taint Soul
taintsoul.jpg


Description:
The Dark Lady forces her influence onto an enemy unit's soul, tainting it. The target enemy is weakened, taking damage and having intitial 60% reduced movement speed for a brief duration.

Level 1 - Deals 60 damage and slow wears off over 2 seconds.
Level 2 - Deals 90 damage and slow wears off over 3 seconds.
Level 3 - Deals 120 damage and slow wears off over 4 seconds.
Level 4 - Deals 150 damage and slow wears off over 5 seconds.

Cast Range - 1200
Target Type - Unit
Cooldown - 12 seconds

Screenshot:
taintsoulss.jpg

Code:

JASS:

scope TaintSoul initializer InitTrig

//=====================================================================//
//                           Taint Soul                                //
//                      by iAyanami aka Glenphir                       //
//=====================================================================//
//                                                                     //
//=====================================================================//
//                     Implementation Instructions                     //
//---------------------------------------------------------------------//
// Copy these objects into your map                                    //
// [Abilities]                                                         //
// 1) Taint Soul                                                       //
//                                                                     //
// [Buffs]                                                             //
// 1) Taint Soul                                                       //
//                                                                     //
//=====================================================================//

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer ABIL_ID1 = 'ABTS' // raw code of ability "Taint Soul"
    
    private constant string EFFECT = "Abilities//Spells//Undead//AnimateDead//AnimateDeadTarget.mdl" // special effect on target
endglobals

private function GetDamage takes unit u returns real
    return 30.00 + (30.00 * GetUnitAbilityLevel(u, ABIL_ID1)) // damage dealt
endfunction

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//

private function Actions takes nothing returns boolean
    local unit c = GetTriggerUnit()
    local unit t = GetSpellTargetUnit()
    if GetSpellAbilityId() == ABIL_ID1 then
        call UnitDamageTargetEx(c, t, GetDamage(c), true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
        call AddSpecialEffectTarget(EFFECT, t, "origin")
    endif
    set c = null
    set t = null
    return false
endfunction

private function InitTrig takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Actions))
endfunction

endscope


Charging Strikes
chargingstrikes.jpg


Description:
The Dark Lady launches herself toward a target location at incredible speed, attacking enemies as she passes by them. Charges toward target location, dealing base damage once to every enemy. Each consecutive attack during the charge will deal 15% less damage than the previous. If Dark Blades is activated, the damage increases and it will silence the enemies when hit. Once the charge is completed, The Dark Lady gains attack speed for 6 seconds.

Level 1 - 600 cast range. 30% increased attack speed.
Level 2 - 700 cast range. 45% increased attack speed.
Level 3 - 800 cast range. 60% increased attack speed.
Level 4 - 900 cast range. 75% increased attack speed.

Cast Range - 600/700/800/900
Target Type - Point
Cooldown - 21/17/13/9 seconds

Note - In Heroes of Newerth, when hit by Charging Strikes, it will place any orb effects that The Dark Lady possesses. In this version, it doesn't do that.

Screenshot:
chargingstrikesss.jpg
Code:
JASS:

scope ChargingStrikes initializer InitTrig

//=====================================================================//
//                         Charging Strikes                            //
//                      by iAyanami aka Glenphir                       //
//=====================================================================//
//                                                                     //
//=====================================================================//
//                     Implementation Instructions                     //
//---------------------------------------------------------------------//
// Copy these objects into your map                                    //
// [Abilities]                                                         //
// 1) Charging Strikes                                                 //
// 2) Charging Strikes (Attack Speed)                                  //
//                                                                     //
//=====================================================================//

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer ABIL_ID1 = 'ABCS' // raw code of Ability "Charging Strikes"
    private constant integer ABIL_ID2 = 'ACS1' // raw code of Ability "Charging Strikes (Attack Speed)"
    
    private constant real MINDMG = 23.00 // minimum damage dealt
    private constant real MAXDMG = 25.00 // maximum damage dealt
    private constant real TIME = 0.25 // time taken for charge
    private constant real AOE = 150.00 // area of effect of the damage
    
    private constant string SLIDE_EFFECT = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl" // special effect of the charge. set to "none.mdl" if no effects are desired
    private constant string HIT_EFFECT = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl" // special effect on the targets when they are hit. set to "none.mdl" if no effects are desired.
    private constant string HIT_ATTACH = "chest" // attachment point of HIT_EFFECT
    
    private constant boolean STR = false // true if the hero's primary attribute is strength
    private constant boolean AGI = true // true if the hero's primary attribute is agility
    private constant boolean INT = false // true if the hero's primary attribute is intelligence
    // if all is set to false, damage dealt will be random number between MINDMG and MAXDMG
endglobals

private function GetDuration takes unit caster returns real
    return 6.00 // duration of attack speed bonus 
endfunction

globals
    private constant boolean EXTRADMG = true // set to true to deal extra damage and silence enemies if dark blades are activated
endglobals

private function GetExtraDamage takes unit caster, real damage returns real
    local integer i = DarkBlades_GetDarkBlades(caster)
    if i == 0 then
        return damage
    endif
    return damage * (1.30 + (0.20 * DarkBlades_GetDarkBlades(caster))) // new damage dealt
endfunction

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//

private struct cs
    real offsetx
    real offsety
    real dist
    real speed
    real damage
    real r
    group g
    unit u
endstruct

private function GetDamage takes unit caster returns real
    if STR then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroStr(caster, true)) 
    elseif AGI then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroAgi(caster, true)) 
    elseif INT then
        return GetRandomReal(MINDMG, MAXDMG) + I2R(GetHeroInt(caster, true)) 
    endif
    return GetRandomReal(MINDMG, MAXDMG)
endfunction

private function AttackSpeed takes nothing returns boolean
    local cs d = KT_GetData()
    set d.r = d.r - 0.10
    if d.r <= 0 then
        call UnitRemoveAbility(d.u, ABIL_ID2)
        call d.destroy()
        return true
    elseif GetUnitAbilityLevel(d.u, ABIL_ID2) == 0 then
        call UnitAddAbility(d.u, ABIL_ID2)
        call SetUnitAbilityLevel(d.u, ABIL_ID2, GetUnitAbilityLevel(d.u, ABIL_ID1))
    endif
    return false
endfunction

private function Slide takes nothing returns boolean
    local cs d = KT_GetData()
    local group g = Group.get()
    local real x = GetUnitX(d.u) + d.offsetx
    local real y = GetUnitY(d.u) + d.offsety
    local real damage
    local unit u
    if d.dist <= 0 then
        call SetUnitPathing(d.u, true)
        call UnitAddAbility(d.u, ABIL_ID2)
        call SetUnitAbilityLevel(d.u, ABIL_ID2, GetUnitAbilityLevel(d.u, ABIL_ID1))
        call KT_Add(function AttackSpeed, d, 0.10)
        call Group.release(d.g)
        return true
    elseif not IsTerrainPathable(x, y, PATHING_TYPE_WALKABILITY) then
        call SetUnitPathing(d.u, false)
        call SetUnitX(d.u, x)
        call SetUnitY(d.u, y)
    endif
    call AddSpecialEffect(SLIDE_EFFECT, x, y)
    call DestroyEffect(bj_lastCreatedEffect)
    set d.dist = d.dist - d.speed
    call GroupEnumUnitsInRange(g, x, y, AOE, null)
    loop
        set u = FirstOfGroup(g)
        exitwhen u == null
        if not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(u, GetOwningPlayer(d.u)) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitInGroup(u, d.g) then
            if EXTRADMG then
                call CasterDummy_TargetPoint(d.u, GetUnitX(u), GetUnitY(u), DarkBlades_GetSilenceAbil(), DarkBlades_GetDarkBlades(d.u), "silence")
            endif
            call UnitDamageTargetEx(d.u, u, d.damage, true, false, ATTACK_TYPE_MELEE, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            call AddSpecialEffectTarget(HIT_EFFECT, u, HIT_ATTACH)
            call GroupAddUnit(d.g, u)
            set d.damage = d.damage * 0.85
        endif
        call GroupRemoveUnit(g, u)
    endloop
    call Group.release(g)
    set g = null
    set u = null
    return false
endfunction

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

private function Actions takes nothing returns nothing
    local cs d = cs.create()
    local real dx = GetSpellTargetX()
    local real dy = GetSpellTargetY()
    local real angle
    local real x
    local real y
    set d.damage = GetDamage(d.u)
    if EXTRADMG then
        set d.damage = GetExtraDamage(d.u, d.damage)
    endif
    set d.g = Group.get()
    set d.u = GetTriggerUnit()
    set x = GetUnitX(d.u)
    set y = GetUnitY(d.u)
    set angle = Atan2(dy - y, dx - x)
    set d.dist = SquareRoot(((x - dx) * (x - dx)) + ((y - dy) * (y - dy)))
    set d.speed = d.dist / TIME * 0.03
    set d.offsetx = d.speed * Cos(angle)
    set d.offsety = d.speed * Sin(angle)
    set d.r = GetDuration(d.u)
    call KT_Add(function Slide, d, 0.03)
endfunction

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

endscope

Cover of Darkness
coverofdarkness.jpg


Description:
Using the depths of her Dark Powers, The Dark Lady poisons the minds of all enemy heroes in an area. For the duration, affected enemy heroes move slower by 15%, lose all allied vision and have a reduced sight range.

Level 1 - 1000 reduced sight range. Lasts 4 seconds.
Level 2 - 1200 reduced sight range. Lasts 6 seconds.
Level 3 - 1400 reduced sight range. Lasts 8 seconds.

Cast Range - Global
Target Type - AoE, 1000
Cooldown - 120 seconds

Screenshot:
coverofdarknessss.jpg

Code:
JASS:

scope CoverofDarkness initializer InitTrig

//=====================================================================//
//                        Cover of Darkness                           //
//                      by iAyanami aka Glenphir                       //
//=====================================================================//
//                                                                     //
//=====================================================================//
//                     Implementation Instructions                     //
//---------------------------------------------------------------------//
// Copy these objects into your map                                    //
// [Abilities]                                                         //
// 1) Cover of Darkness                                                //
// 2) Cover of Darkness (Movement Speed)                               //
// 3) Cover of Darkness (Sight - Level 1)                              //
// 4) Cover of Darkness (Sight - Level 2)                              //
// 5) Cover of Darkness (Sight - Level 3)                              //
//                                                                     //
// [Buffs]                                                             //
// 1) Cover of Darkness                                                //
//                                                                     //
//=====================================================================//

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer ABIL_ID1 = 'ABCD' // raw code of Ability "Cover of Darkness"
    private constant integer ABIL_ID2 = 'ACD4' // raw code of Ability "Charging Strikes (Movement Speed)"
    private constant integer ABIL_ID3 = 'ACD1' // raw code of Ability "Charging Strikes (Sight - Level 1)"
    private constant integer ABIL_ID4 = 'ACD2' // raw code of Ability "Charging Strikes (Sight - Level 2)"
    private constant integer ABIL_ID5 = 'ACD3' // raw code of Ability "Charging Strikes (Sight - Level 3)"

    private constant real AOE = 1000.00 // area of effect
    
    private constant boolean HERO = true // set to true to only affect enemy heroes
endglobals

private function GetDuration takes unit caster returns real
    return 2.00 + (2.00 * GetUnitAbilityLevel(caster, ABIL_ID1)) // duration of blind
endfunction

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//

globals
    private player PLAYER
    private unit UNIT
endglobals

private struct cod
    real r
    group g
    unit u
endstruct

private function RemoveVision takes nothing returns nothing
    local unit u = GetEnumUnit()
    local player ou = GetOwningPlayer(u)
    local integer i = 0
    if GetUnitAbilityLevel(UNIT, ABIL_ID2) == 0 then
        call UnitAddAbility(u, ABIL_ID2)
        if GetUnitAbilityLevel(UNIT, ABIL_ID1) == 1 then
            call UnitAddAbility(u, ABIL_ID3)
        elseif GetUnitAbilityLevel(UNIT, ABIL_ID1) == 2 then
            call UnitAddAbility(u, ABIL_ID4)
        elseif GetUnitAbilityLevel(UNIT, ABIL_ID1) == 3 then
            call UnitAddAbility(u, ABIL_ID4)
        endif
    endif
    loop
        if GetPlayerAlliance(Player(i), ou, ALLIANCE_SHARED_VISION) then
            call SetPlayerAlliance(Player(i), ou, ALLIANCE_SHARED_VISION, false)
        endif
        exitwhen i == 15
        set i = i + 1
    endloop
    set u = null
    set ou = null
endfunction

private function Restore takes nothing returns nothing
    local unit u = GetEnumUnit()
    local player ou = GetOwningPlayer(u)
    local integer i = 0
    call UnitRemoveAbility(GetEnumUnit(), ABIL_ID2)
    call UnitRemoveAbility(GetEnumUnit(), ABIL_ID3)
    call UnitRemoveAbility(GetEnumUnit(), ABIL_ID4)
    call UnitRemoveAbility(GetEnumUnit(), ABIL_ID5)
    loop
        if IsPlayerAlly(ou, Player(i)) and not GetPlayerAlliance(Player(i), ou, ALLIANCE_SHARED_VISION) then
            call SetPlayerAlliance(Player(i), ou, ALLIANCE_SHARED_VISION, true)
        endif
        exitwhen i == 15
        set i = i + 1
    endloop
    set u = null
    set ou = null
endfunction

private function Periodic takes nothing returns boolean
    local cod d = KT_GetData()
    local integer i
    set d.r = d.r - 0.10
    if d.r <= 0 then
        call ForGroup(d.g, function Restore)
        call Group.release(d.g)
        call d.destroy()
        return true
    endif
    set PLAYER = GetOwningPlayer(d.u)
    set UNIT = d.u
    call ForGroup(d.g, function RemoveVision)
    return false
endfunction

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

private function Actions takes nothing returns nothing
    local cod d = cod.create()
    local real x = GetSpellTargetX()
    local real y = GetSpellTargetY()
    local boolean check
    local integer i
    local unit u
    set d.g = Group.get()
    set d.u = GetTriggerUnit()
    set d.r = GetDuration(d.u)
    set i = CountUnitsInGroup(d.g)
    call GroupEnumUnitsInRange(d.g, x, y, AOE, null)
    loop
        exitwhen i == 0
        set u = FirstOfGroup(d.g)
        set i = i - 1
        if HERO then
            set check = not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(u, GetOwningPlayer(d.u)) and IsUnitType(u, UNIT_TYPE_HERO)
        else
            set check = not IsUnitType(u, UNIT_TYPE_STRUCTURE) and IsUnitEnemy(u, GetOwningPlayer(d.u))
        endif
        if check then
            if GetUnitAbilityLevel(d.u, ABIL_ID1) == 1 then
                call UnitAddAbility(u, ABIL_ID3)
            elseif GetUnitAbilityLevel(d.u, ABIL_ID1) == 2 then
                call UnitAddAbility(u, ABIL_ID4)
            elseif GetUnitAbilityLevel(d.u, ABIL_ID1) == 3 then
                call UnitAddAbility(u, ABIL_ID4)
            endif
        else
            call GroupRemoveUnit(d.g, u)
        endif
    endloop
    call KT_Add(function Periodic, d, 0.10)
    set u = null
endfunction

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

endscope



Credits
Code:
[COLOR="Red"]Event, AIDS, Key Timers 2, Damage[/COLOR] - [URL="http://www.thehelper.net/forums/member.php?u=15813"]Jesus4Lyf[/URL]
[COLOR="Red"]Recycle[/COLOR] - [URL="http://www.thehelper.net/forums/member.php?u=9767"]Nestharus[/URL]
[COLOR="Red"]GDD[/COLOR] - [URL="http://www.thehelper.net/forums/member.php?u=26460"]Weep[/URL]
[COLOR="Red"]Dark Blades Model[/COLOR] - shamanyouranus @ [url]www.hiveworkshop.com[/url]
[COLOR="Red"]Taint Soul Model[/COLOR] - WILL THE ALMIGHTY @ [url]www.hiveworkshop.com[/url]
[COLOR="Red"]Cover of Darkness Model[/COLOR] - shamanyouranus @ [url]www.hiveworkshop.com[/url]
[COLOR="Red"]Spell Icons[/COLOR] - [url]www.heroesofnewerth.com[/url]



Changelogs
Code:
[B]Version 1.0[/B]
- Initial release
[B]Version 1.1[/B]
- Fixed the problem of Cover of Darkness (Spell Book) from showing up
[B]Version 1.2[/B]
- Changed Dark Blades to a Buff Placer to improve performance.
- Changed Taint Soul's base spell from Chain Lightning to Shadow Strike to remove unecessary triggers.
- Removed the IsUnitGroupEmptyBJ function from the trigger "Charging Strikes Periodic" and used an integer to check the units instead.
- Fixed a lag issue in charging strikes where dummies were spawning for every enemy unit hit.
- Changed Cover of Darkness' slow aura to Slow Aura (Tornado) from Endurance Aura so that a Spell Book will not be needed.
- Slightly reworked Cover of Darkness' trigger.
[B]Version 1.3[/B]
- Fixed Taint Soul's lag issues.
[B]Version 1.4[/B]
- Added the actual Dark Lady's soundset.
[B]Version 1.5[/B]
- Rewrote the spell triggers into vJASS


Any type of feedback will be appreciated. Please post constructive criticisms if you're intending to criticize this spell pack. Thank you.
 

Attachments

  • The Dark Lady Spellpack v1.5.w3x
    1.4 MB · Views: 515

Ayanami

칼리
Reaction score
288
Uploaded. I think I encountered an error when uploading just now. Sorry about that.
 

Carnerox

The one and only.
Reaction score
84
Nice spells. :thup:

Tho with the Ultimate, when you cast it shows the spellbook and doesn't go away.
 

INCINERATE

New Member
Reaction score
12
very nice pack, i might put this to use in my map , + rep dude .. cool unique hero

anybody got a link for a nice hero model to use ? (other than warden)
 

Chaos_Knight

New Member
Reaction score
39
Shit. Wow. Wow. AWSOME! LeaKLesS, LaGLesS, and what i can see MUI. Awsome. I will use it i hope.

Did you make it in normal WE or NewGen?
 

Angel_Island

Much long, many time, wow
Reaction score
56
When spamming Cover of Darkness, the effect disaappears and doesn't work after some seconds.

How did you get the sound effects?
 

Weep

Godspeed to the sound of the pounding
Reaction score
400
README said:
Dark Blades (MUI) - Active
- Used "Berserk" as the base spell.
- 6 dummy spells required.
- No dummy units required.
Dark Blades Silence said:
Trigger:
  • Unit - Create 1 CasterDummy for (Owner of GDD_DamageSource) at TempPoint facing Default building facing degrees
:p

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

• General comments
You're using 4 (!) hashtables? Each map is limited to having 256 - having 1 per spell is bad enough. Try to use only one hashtable for all spells. It should be possible by using a unique child key (eg. StringHash("darkbladesduration")) for each spell's properties, instead of the same StringHash("duration") or whatever for all of them.

It probably doesn't matter much, but it would be a tiny bit more efficient to use explicit integers rather than the StringHash function (eg. replace all instances of StringHash("duration") with 0, all instances of StringHash("target") with 1, etc.)

• Dark Blades
Dark Blades is way over-triggered, IMO. Using (Current order of GDD_DamageSource) Equal to (Order(attack)) in your conditions is terrible, as it requires the other few triggers which force the hero to attack, which will cancel Hold Position, Patrol, and queued orders. It's also unreliable: in case the hero has any items which allow it to deal delayed damage (such as a slow-moving projectile or a poison), it will trigger the silence-on-attack if the hero is presently ordered to attack, regardless of target or range.

And worst, it seems unreliable in that it does not always cause the target to be silenced; if the hero is already attacking a target via a "smart" order and I cast Dark Blades, the silence effect won't happen, for some reason...

If you don't mind the base spell having a casting animation, I'd recommend basing it on Roar, which can handle the duration (the periodic trigger needs only check for the buff; no hashtable required) and the damage increase. In place of the spell book, use a traditional buff-placer/orb ability for attack detection, and consider using Soul Burn as the dummy spell.

• Taint Soul
Tainted soul could be made using shadow strike IMO.
I agree. It does, in fact, have decaying movement speed reduction - I just tested to be sure. It won't show green damage numbers over its duration if the decaying damage is 0 (which is the case with Taint Soul), only on initial hit, which ought to be OK. No triggering is required, then.

I didn't use Shadow Strike due to stacking issues. Plus, didn't want the floating text.
Avoiding stacking issues? Yet, you used a modified Slow spell on a dummy, instead of a self-only Tornado Slow aura (no spell book needed to hide that ability)... Plus, if it's really important to not have any floating text, Shadow Strike could be used as a dummy spell exclusively for the decaying slow effect to reduce the dependence on triggers, using some other spell to deal the initial damage. (Shadow Strike produces no floating text if the damage is 0.)

I could only see your current method being ideal if you want 1. no Shadow Strike, to play well with other abilities that might use Shadow Strike, and 2. the ability to dispel the buff, which you can't do with an aura.

BTW, why do you use custom text for your hashtable saving/loading? Those actions are available in the GUI...

• Charging Strikes
I would suggest completely omitting the initial If-statement in Charging Strikes Periodic. If you look at the JASS code behind the function IsUnitGroupEmptyBJ, you'll see that it works by running through all units in the group, setting a boolean to true for each unit it finds (and if there are none, the boolean is of course left false). This is pretty inefficient, meaning that you are looping through the group twice in that trigger - it would be better to keep track of how many units are actively casting the spell with a simple integer. At the least, you could perform the same kind of boolean-check in the trigger, with the turn-off action at the end, if the boolean has been set to true.

In the readme, you should make a note that CSCheckGroup needs to be set to a size of 100, since the World Editor doesn't automatically do that when copying variables.

• Cover of Darkness
You could base the movement speed penalty aura on Tornado Slow, so you won't need to use a spell book.

In Cover of Darkness Cast, you might want to use Players Matching Condition (Boolean: Player - Player Alliance Toward Player), which allows you to specifically pick players with shared vision, as opposed to All Allies Of Player, which assumes shared vision for any ally. In turn, it would probably be better to use Player - Set Aspect Of Alliance instead of Player - Set Alliance. The periodic trigger should also probably re-set this aspect, since a player could undo that setting manually before the duration is over, if the map does not prevent players from selecting alliances.

As with Charging Strikes, I'd recommend against using Unit Group Is Empty in any periodic triggers, tracking it yourself instead.

The Cover of Darkness Test trigger in the Other Unrelated Triggers category had me confused, thinking the spell was mistakenly also targeting the caster. :p You might want to remove that.

[off-topic] LOL, "Taste my moon-beam!" It sounds like the same voice actress that did the Dryad in WC3...
 

Angel_Island

Much long, many time, wow
Reaction score
56
Can you explain in more detail? Got the sound effect via Audacity.

When you cast the cover of darkness 2 times, the first one will, after it ends, make the vision normal again and the second effect will not reduce vision for the rest of its duration.
 

Ayanami

칼리
Reaction score
288
:p

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

• General comments
You're using 4 (!) hashtables? Each map is limited to having 256 - having 1 per spell is bad enough. Try to use only one hashtable for all spells. It should be possible by using a unique child key (eg. StringHash("darkbladesduration")) for each spell's properties, instead of the same StringHash("duration") or whatever for all of them.

It probably doesn't matter much, but it would be a tiny bit more efficient to use explicit integers rather than the StringHash function (eg. replace all instances of StringHash("duration") with 0, all instances of StringHash("target") with 1, etc.)

• Dark Blades
Dark Blades is way over-triggered, IMO. Using (Current order of GDD_DamageSource) Equal to (Order(attack)) in your conditions is terrible, as it requires the other few triggers which force the hero to attack, which will cancel Hold Position, Patrol, and queued orders. It's also unreliable: in case the hero has any items which allow it to deal delayed damage (such as a slow-moving projectile or a poison), it will trigger the silence-on-attack if the hero is presently ordered to attack, regardless of target or range.

And worst, it seems unreliable in that it does not always cause the target to be silenced; if the hero is already attacking a target via a "smart" order and I cast Dark Blades, the silence effect won't happen, for some reason...

If you don't mind the base spell having a casting animation, I'd recommend basing it on Roar, which can handle the duration (the periodic trigger needs only check for the buff; no hashtable required) and the damage increase. In place of the spell book, use a traditional buff-placer/orb ability for attack detection, and consider using Soul Burn as the dummy spell.

• Taint Soul

I agree. It does, in fact, have decaying movement speed reduction - I just tested to be sure. It won't show green damage numbers over its duration if the decaying damage is 0 (which is the case with Taint Soul), only on initial hit, which ought to be OK. No triggering is required, then.


Avoiding stacking issues? Yet, you used a modified Slow spell on a dummy, instead of a self-only Tornado Slow aura (no spell book needed to hide that ability)... Plus, if it's really important to not have any floating text, Shadow Strike could be used as a dummy spell exclusively for the decaying slow effect to reduce the dependence on triggers, using some other spell to deal the initial damage. (Shadow Strike produces no floating text if the damage is 0.)

I could only see your current method being ideal if you want 1. no Shadow Strike, to play well with other abilities that might use Shadow Strike, and 2. the ability to dispel the buff, which you can't do with an aura.

BTW, why do you use custom text for your hashtable saving/loading? Those actions are available in the GUI...

• Charging Strikes
I would suggest completely omitting the initial If-statement in Charging Strikes Periodic. If you look at the JASS code behind the function IsUnitGroupEmptyBJ, you'll see that it works by running through all units in the group, setting a boolean to true for each unit it finds (and if there are none, the boolean is of course left false). This is pretty inefficient, meaning that you are looping through the group twice in that trigger - it would be better to keep track of how many units are actively casting the spell with a simple integer. At the least, you could perform the same kind of boolean-check in the trigger, with the turn-off action at the end, if the boolean has been set to true.

In the readme, you should make a note that CSCheckGroup needs to be set to a size of 100, since the World Editor doesn't automatically do that when copying variables.

• Cover of Darkness
You could base the movement speed penalty aura on Tornado Slow, so you won't need to use a spell book.

In Cover of Darkness Cast, you might want to use Players Matching Condition (Boolean: Player - Player Alliance Toward Player), which allows you to specifically pick players with shared vision, as opposed to All Allies Of Player, which assumes shared vision for any ally. In turn, it would probably be better to use Player - Set Aspect Of Alliance instead of Player - Set Alliance. The periodic trigger should also probably re-set this aspect, since a player could undo that setting manually before the duration is over, if the map does not prevent players from selecting alliances.

As with Charging Strikes, I'd recommend against using Unit Group Is Empty in any periodic triggers, tracking it yourself instead.

The Cover of Darkness Test trigger in the Other Unrelated Triggers category had me confused, thinking the spell was mistakenly also targeting the caster. :p You might want to remove that.

[off-topic] LOL, "Taste my moon-beam!" It sounds like the same voice actress that did the Dryad in WC3...

Thanks for the constructive feedback. Well I do see no point for using 4 hashtables. Will change to 1. I'm using custom script for hashtables because my hashtables are bugged in GUI for some reason. This includes both the NewGen Editor and the Default World Editor.

Dark Blades
My actual aim was to keep this a non-buff placer and non-orb effect. So I've been trying means to put it this way.

Taint Soul
Actually you do have a point. Using slow or shadow strike would be the same thing as they both can't stack if used again. I'll change it to an aura instead.

Charging Strikes
Didn't know about the "Unit Group is Empty" condition. Thanks for pointing that out.

Cover of Darkness
Noted. Will change the slow to Tornado Slow aura. Well the purpose of the test trigger was to actually see the effect of the Cover of Darkness.
 

INCINERATE

New Member
Reaction score
12
one thing i found and its pretty evident ( did a testing of this hero in my map)

the charge spell can get VEry laggy if you cast it into a LARGE group of units e.g (mass footmens). thats basically the only issue i have with this pack , i guess ill change the tainted soul to shadow strike to boost some performance also ( yes i am currently using this hero :D )
 

Ayanami

칼리
Reaction score
288
one thing i found and its pretty evident ( did a testing of this hero in my map)

the charge spell can get VEry laggy if you cast it into a LARGE group of units e.g (mass footmens). thats basically the only issue i have with this pack , i guess ill change the tainted soul to shadow strike to boost some performance also ( yes i am currently using this hero :D )

It's probably because there are mass dummy units being summoned. Will fix this so that the dummy units will only be spawned if the unit being damaged has mana.
 

Viikuna

No Marlo no game.
Reaction score
265
Most of abilities, like slow for example, can be casted instantly, so you dont need more than one dummy for all that spell casting.

That should make it much less laggy. Creating units is, after all, one of the slowest operations in wc3.
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top