Spellpack Shadow Sting Spellpack

Ayanami

칼리
Reaction score
288
Introduction
Made another spellpack, because, why not?

Requirements
- JNGP
- AIDS
- Damage
- GroupUtils
- GTrigger
- Table
- Timer32

The spells:
  • Eagle Eye [Active]
  • Heartbreaker [Active]
  • Bolt Sequence [Active]


Details
- The spells are vJASS
- They should be leak-less and lag-less
- It is MUI, meaning can be cast many times at the same instance


Implementation
JASS:

//==============================================================================
//                       SHADOW STING SPELLPACK v1.3                       
//                              BY Ayanami                             
//==============================================================================

//==============================================================================
//                            REQUIREMENTS                                      
//==============================================================================
// - JNGP
// - AIDS
// - Damage
//   * Event
// - GroupUtils
// - GTrigger                                                             
// - Timer32       
// - Table                                                              
//==============================================================================

//==============================================================================
//                           IMPLEMENTATION                                     
//==============================================================================
// 1) Copy the whole "Required Systems" Trigger folder 
// 2) Save the map, it will take a bit longer than usual
// 3) Close the map and re-open it, then disable or delete the trigger "Objects"
// 4) Go to the Import Manager and export all resources, then import into map
// 5) Copy all 3 abilities under "Night Elf - Heroes"    
// 6) Copy the whole "Shadow Sting Spellpack" Trigger folder                                         
//==============================================================================



Spells

eagleeye.png

Eagle Eye

Description:
Utilizing the shadows within the hero, the hero temporarily has sharper vision. This allows the hero to deal armor shattering blows for the duration. Any damage received from the hero during this duration will result in an armor penalty to the target for 5 seconds. The amount of armor shattered is dependant to the damage dealt. Maximum of 15 armor penalty.

Level 1 - 0.4% of damage as armor penalty. Buff lasts 8 seconds.
Level 2 - 0.6% of damage as armor penalty. Buff lasts 9 seconds.
Level 3 - 0.8% of damage as armor penalty. Buff lasts 10 seconds.
Level 4 - 1.0% of damage as armor penalty. Buff lasts 11 seconds.

Cast Range: Self
Target Type: Instant
Cooldown: 22 seconds

Screenshot:

eagleeyess.jpg

Code:
JASS:

library EagleEye uses AIDS, Damage, GT

globals
    private integer array ARMORID
endglobals

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABILID = 'ABEE' // raw code of ability "Eagle Eye"
    private constant integer DUMABILID = 'AEE0' // raw code of ability "Eagle Eye (Buff)"
    private constant integer BUFFID = 'BEE0' // raw code of buff "Eagle Eye"
    private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
    
    private constant string FX = "Abilities\\Spells\\Items\\SpellShieldAmulet\\SpellShieldCaster.mdl" // effect on target when damaged
    private constant string FX_AT = "chest" // attachment point of FX
    
    private constant real PERIOD = 0.10 // timer update frequency for buff checking
endglobals

private function SetUp takes nothing returns nothing
    set ARMORID[0] = 'dEE0' // raw code of ability "Eagle Eye (Armor - -1's)"
    set ARMORID[1] = 'dEE1' // raw code of ability "Eagle Eye (Armor - -10's)"
    set ARMORID[2] = 'dEE2' // raw code of ability "Eagle Eye (Armor - -100's)"
    set ARMORID[3] = 'dEE3' // raw code of ability "Eagle Eye (Armor - -1000's)"
    set ARMORID[4] = 'dEE4' // raw code of ability "Eagle Eye (Armor - -10000's)"
endfunction

// duration of buff on caster
private constant function GetDuration takes integer level returns real
    return 7. + level
endfunction

// duration of armor penalty
private constant function GetTargetDuration takes integer level returns real
    return 5.
endfunction

// percentage of damage taken as armor penalty, where 1.00 = 100%
private constant function GetArmorPenalty takes integer level returns real
    return 0.002 + (0.002 * level)
endfunction

// maximum armor penalty (supports up to 99999 armor reduction)
private constant function GetMaxPenalty takes integer level returns integer
    return 15
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

private struct Data
    thistype next
    thistype prev
    unit u
    real dur
    static timer linkTimer = CreateTimer()
    static integer linkCount = 0
    static integer array store
    
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        
        loop
            set this = this.next
            exitwhen this == 0
        
            if this.dur <= 0 then
                set this.next.prev = this.prev
                set this.prev.next = this.next
                
                set linkCount = linkCount - 1
                if linkCount == 0 then
                    call PauseTimer(linkTimer)
                endif
            
                call UnitRemoveAbility(this.u, DUMABILID)
                call UnitRemoveAbility(this.u, BUFFID)
                
                set store[GetUnitId(this.u)] = 0

                call this.deallocate()
            else
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod
    
    private static method onCast takes nothing returns boolean
        local thistype this
        local unit u = GetTriggerUnit()
        local integer id = GetUnitId(u)
        
        if store[id] > 0 then
            set this = store[id]
        else
            set this = thistype.allocate()
            set thistype(0).next.prev = this
            set this.next = thistype(0).next
            set thistype(0).next = this
            set this.prev = thistype(0)
            
            if linkCount == 0 then
                call TimerStart(linkTimer, PERIOD, true, function thistype.iterate)
            endif
            set linkCount = linkCount + 1
            
            set this.u = GetTriggerUnit()
            
            call UnitAddAbility(this.u, DUMABILID)
        endif
                
        set this.dur = GetDuration(GetUnitAbilityLevel(this.u, ABILID))

        set u = null
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        
        call GT_RegisterStartsEffectEvent(t, ABILID)
        call TriggerAddCondition(t, Condition(function thistype.onCast))
        set t = null
    endmethod
endstruct

private struct Damage
    thistype next
    thistype prev
    unit u
    real amount
    real dur
    static timer linkTimer = CreateTimer()
    static integer linkCount = 0
    static integer array store
    
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        local integer i
        
        loop
            set this = this.next
            exitwhen this == 0
        
            if this.dur <= 0 then
                set this.next.prev = this.prev
                set this.prev.next = this.next
                
                set linkCount = linkCount - 1
                if linkCount == 0 then
                    call PauseTimer(linkTimer)
                endif
            
                set i = 0
                loop
                    exitwhen i > 4
                    call SetUnitAbilityLevel(this.u, ARMORID<i>, 1)
                    set i = i + 1
                endloop

                set store[GetUnitId(this.u)] = 0
                
                call this.deallocate()
            else
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod

    private static method onDamage takes nothing returns boolean
        local thistype this
        local unit u = GetEventDamageSource()
        local integer level
        local integer id
        local integer max
        local integer value
        local integer factor
        local integer i
        
        if GetUnitAbilityLevel(u, DUMABILID) == 1 then
            set level = GetUnitAbilityLevel(u, ABILID)
            set id = GetUnitId(GetTriggerUnit())
            set max = GetMaxPenalty(level)
            if store[id] &gt; 0 then
                set this = store[id]
                set this.amount = this.amount + (GetEventDamage() * GetArmorPenalty(level))
            else
                set this = thistype.allocate()
                set thistype(0).next.prev = this
                set this.next = thistype(0).next
                set thistype(0).next = this
                set this.prev = thistype(0)
                
                if linkCount == 0 then
                    call TimerStart(linkTimer, PERIOD, true, function thistype.iterate)
                endif
                set linkCount = linkCount + 1
                
                set this.u = GetTriggerUnit()
                set this.amount = GetEventDamage() * GetArmorPenalty(level)
                set store[id] = this
            endif
            set this.dur = GetTargetDuration(level)
            if this.amount &gt; max then
                set this.amount = max
            endif
            call DestroyEffect(AddSpecialEffectTarget(FX, this.u, FX_AT))
            
            set value = R2I(this.amount)
            set factor = 10000
            set i = 4
            loop
                exitwhen i &lt; 0
                if GetUnitAbilityLevel(this.u, ARMORID<i>) == 0 then
                    call UnitAddAbility(this.u, ARMORID<i>)
                endif
                set level = value / factor
                call SetUnitAbilityLevel(this.u, ARMORID<i>, level + 1)
                set value = value - (level * factor)
                set factor = factor / 10
                set i = i - 1
            endloop
        endif

        set u = null
        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local unit u = CreateUnit(Player(13), PRELOADID, 0, 0, 0)
        local integer i = 4
        
        call Damage_RegisterEvent(t)
        call TriggerAddCondition(t, Condition(function thistype.onDamage))
        call SetUp()
        call UnitAddAbility(u, DUMABILID)
        loop
            exitwhen i &lt; 0
            call UnitAddAbility(u, ARMORID<i>)
            set i = i - 1
        endloop
        call RemoveUnit(u)
        
        set t = null
        set u = null
    endmethod
endstruct

endlibrary
</i></i></i></i></i>




heartbreaker.png

Heartbreaker

Description:
Fires a bolt that pierces through the soul of the target, dealing damage. A shadow curse is also applied to the target and nearby enemy units. The curse takes effect after 3 seconds, which then enemies are stunned based on the damage taken. Enemies are immediately stunned if the maximum damage taken is reached.

Level 1 - 50 damage. 100 curse damage limit, 1 second stun limit.
Level 2 - 100 damage. 200 curse damage limit, 2 seconds stun limit.
Level 3 - 150 damage. 300 curse damage limit, 3 seconds stun limit.
Level 4 - 200 damage. 400 curse damage limit, 4 seconds stun limit.

Cast Range: 600
Target Type: Unit
Cooldown: 16 seconds

Screenshot:
heartbreakerss.jpg

Code:
JASS:

library Heartbreaker uses Damage, GroupUtils, T32, Table

constant native UnitAlive takes unit id returns boolean

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABILID = &#039;ABHe&#039; // raw code of ability &quot;Heartbreaker&quot;
    private constant integer DUM_ABILID_1 = &#039;AHe0&#039; // raw code of ability &quot;Heartbreaker (Stun)&quot;
    private constant integer DUM_ABILID_2 = &#039;AHe1&#039; // raw code of ability &quot;Heartbreaker (Buff)&quot;
    private constant integer BUFFID_1 = &#039;BHe0&#039; // raw code of buff &quot;Heartbreaker (Stun)&quot;
    private constant integer BUFFID_2 = &#039;BHe1&#039; // raw code of buff &quot;Heartbreaker (Buff)&quot;
    private constant integer DUMMYID = &#039;dHEA&#039; // raw code of unit &quot;Heartbreaker Dummy&quot;
    private constant integer CASTERID = &#039;cAST&#039; // raw code of unit &quot;Caster Dummy&quot;
    private constant integer PRELOADID = &#039;prel&#039; // raw code of unit &quot;Preloader&quot;
    
    private constant string FX = &quot;Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl&quot; // effect used upon being stunned
    private constant string FX_AT = &quot;origin&quot; // attachment point of FX
    private constant string PROJ_FX = &quot;Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl&quot; // effect used upon projectile impact
    
    private constant boolean SHOWTEXT = true // true if floating text (displaying stun duration) is displayed
    private constant string COLOR = &quot;|cffff0000&quot; // color code for floating text
    private constant real FLOAT_ANGLE = bj_PI / 2 // floating angle of the floating text (radians)
    
    private constant real SPEED = 1000.0 // distance traveled by projectile per second
    private constant real TRUESPEED = SPEED * T32_PERIOD // distance per interval
    private constant real COL_SIZE = 50. // collision size of projectile
    private constant real PERIOD = 0.1 // timer update frequency for stun
    
    private constant attacktype ATK = ATTACK_TYPE_NORMAL // attack type of damage
    private constant damagetype DMG = DAMAGE_TYPE_MAGIC // damage type of damage
endglobals

// damage dealt
private constant function GetDamage takes integer level returns real
    return 50. * level
endfunction

// damage limit for curse
private constant function GetDamageLimit takes integer level returns real
    return 100. * level
endfunction

// stun limit for curse
private constant function GetStunLimit takes integer level returns real
    return 1. * level
endfunction

// delay time before curse takes effect
private constant function GetDelayTime takes integer level returns real
    return 3.
endfunction

// curse area of effect
private constant function GetArea takes integer level returns real
    return 400.
endfunction

// target filter
private constant function GetFilter takes unit c, unit u returns boolean
    return /*
    */ UnitAlive(u) /* // target is alive
    */ and IsUnitEnemy(u, GetOwningPlayer(c)) /* // target is an enemy
    */ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* // target is not a structure
    */
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

globals
    private constant real TRUE_COL_SIZE = COL_SIZE * COL_SIZE
endglobals

private struct Curse
    thistype next
    thistype prev
    unit u
    unit tar
    trigger trig
    integer level
    real damage
    real damagelimit
    real dur
    static unit castDummy
    static Table table
    static timer linkTimer = CreateTimer()
    static integer linkCount = 0
    
    private static method iterate takes nothing returns nothing
        local thistype this = thistype(0)
        
        loop
            set this = this.next
            exitwhen this == 0
            
            if this.dur &lt;= 0 or not UnitAlive(this.tar) then
                set this.next.prev = this.prev
                set this.prev.next = this.next
                
                set linkCount = linkCount - 1
                if linkCount == 0 then
                    call PauseTimer(linkTimer)
                endif
            
                call UnitRemoveAbility(this.tar, BUFFID_1)

                call this.deallocate()
            else
                if GetUnitAbilityLevel(this.tar, BUFFID_1) == 0 then
                    call IssueTargetOrder(castDummy, &quot;firebolt&quot;, this.tar)
                endif
                set this.dur = this.dur - PERIOD
            endif
        endloop
    endmethod
    
    private method periodic takes nothing returns nothing
        local boolean b = UnitAlive(this.tar)
        local integer id
        static if SHOWTEXT then
            local texttag t
            local integer digitone
            local integer digittwo
        endif
        
        if this.dur &lt;= 0 or this.damage &gt;= this.damagelimit or not b then
            if b and this.damage &gt; 0 then
                if this.damage &gt; this.damagelimit then
                    set this.damage = this.damagelimit
                endif
                set this.dur = (this.damage / this.damagelimit) * GetStunLimit(this.level)
                
                static if SHOWTEXT then
                    set digitone = R2I(this.dur)
                    set digittwo = R2I((this.dur - digitone) * 10)
                    set t = CreateTextTag()
                    
                    call SetTextTagText(t, COLOR + I2S(digitone) + &quot;.&quot; + I2S(digittwo) + &quot;!|r&quot;, 0.0253)
                    call SetTextTagPosUnit(t, this.tar, 0)
                    call SetTextTagColor(t, 255, 255, 255, 255)
                    call SetTextTagVelocity(t, 0.0355 * Cos(FLOAT_ANGLE), 0.0355 * Sin(FLOAT_ANGLE))
                    call SetTextTagPermanent(t, false)
                    call SetTextTagLifespan(t, 2.50)
                endif
                
                call IssueTargetOrder(castDummy, &quot;firebolt&quot;, this.tar)
                call DestroyEffect(AddSpecialEffectTarget(FX, this.tar, FX_AT))
                
                set thistype(0).next.prev = this
                set this.next = thistype(0).next
                set thistype(0).next = this
                set this.prev = thistype(0)
                    
                if linkCount == 0 then
                    call TimerStart(linkTimer, PERIOD, true, function thistype.iterate)
                endif
                set linkCount = linkCount + 1
            endif
            
            call UnitRemoveAbility(this.tar, DUM_ABILID_2)
            call UnitRemoveAbility(this.tar, BUFFID_2)
            call table.remove(GetHandleId(this.trig))
            call DestroyTrigger(this.trig)
            
            call this.stopPeriodic()
            
            if not b then
                call this.deallocate()
            endif
        else
            set this.dur = this.dur - T32_PERIOD
        endif
        
        static if SHOWTEXT then
            set t = null
        endif
    endmethod
    implement T32x
    
    private static method onDamage takes nothing returns boolean
        local thistype this = table[GetHandleId(GetTriggeringTrigger())]
        
        if this.tar == GetTriggerUnit() then
            set this.damage = this.damage + GetEventDamage()
        endif

        return false
    endmethod

    public static method create takes unit u, unit tar, integer level returns thistype
        local thistype this = thistype.allocate()

        set this.u = u
        set this.tar = tar
        set this.trig = CreateTrigger()
        set this.level = level
        set this.damage = 0
        set this.damagelimit = GetDamageLimit(this.level)
        set this.dur = GetDelayTime(this.level)
        
        call UnitAddAbility(this.tar, DUM_ABILID_2)
        call Damage_RegisterEvent(this.trig)
        call TriggerAddCondition(this.trig, Condition(function thistype.onDamage))
        set table[GetHandleId(this.trig)] = this
        
        call this.startPeriodic()
        
        return this
    endmethod
    
    private static method onInit takes nothing returns nothing
        set table = Table.create()
        
        set castDummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), CASTERID, 0, 0, 0)
        call UnitAddAbility(castDummy, DUM_ABILID_1)
    endmethod
endstruct

private struct Data
    unit u
    unit tar
    unit dummy
    integer level
    static thistype tempData
    
    private static method filterFunc takes nothing returns boolean
        local thistype this = tempData
        local unit u = GetFilterUnit()
        
        if GetFilter(this.u, u) then
            call Curse.create(this.u, u, this.level)
        endif
        
        set u = null
        return false
    endmethod
    
    private method periodic takes nothing returns nothing
        local real x = GetUnitX(this.dummy)
        local real y = GetUnitY(this.dummy)
        local real dx = GetUnitX(this.tar) - x
        local real dy = GetUnitY(this.tar) - y
        local real a
        
        if dx * dx + dy * dy &lt;= TRUE_COL_SIZE then
            call DestroyEffect(AddSpecialEffect(PROJ_FX, x, y))
            call UnitDamageTargetEx(this.u, this.tar, GetDamage(this.level), true, false, ATK, DMG, null)
            set tempData = this
            call GroupEnumUnitsInArea(ENUM_GROUP, dx + x, dy + y, GetArea(this.level), Filter(function thistype.filterFunc))
            
            call KillUnit(this.dummy)
            call this.stopPeriodic()
            call this.deallocate()
        else
            set a = Atan2(dy, dx)
            call SetUnitX(this.dummy, x + TRUESPEED * Cos(a))
            call SetUnitY(this.dummy, y + TRUESPEED * Sin(a))
            call SetUnitFacing(this.dummy, a * bj_RADTODEG)
        endif
    endmethod
    implement T32x

    private static method onCast takes nothing returns boolean
        local thistype this
        local real x
        local real y
        
        set this = thistype.allocate()
        set this.u = GetTriggerUnit()
        set this.tar = GetSpellTargetUnit()
        set x = GetUnitX(this.u)
        set y = GetUnitY(this.u)
        set this.dummy = CreateUnit(GetOwningPlayer(this.u), DUMMYID, x, y, Atan2(GetUnitY(this.tar) - y, GetUnitX(this.tar) - x) * bj_RADTODEG)
        set this.level = GetUnitAbilityLevel(this.u, ABILID)
            
        call this.startPeriodic()

        return false
    endmethod

    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        local unit u
        
        call GT_RegisterStartsEffectEvent(t, ABILID)
        call TriggerAddCondition(t, Condition(function thistype.onCast))
        
        // preload
        set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), CASTERID, 0, 0, 0)
        call UnitAddAbility(u, DUM_ABILID_1)
        call UnitAddAbility(u, DUM_ABILID_2)
        call RemoveUnit(u)
        call RemoveUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMYID, 0, 0, 0))
        
        set t = null
        set u = null
    endmethod
endstruct

endlibrary


boltsequence.png

Bolt Sequence

Description:
Fires a series of arrows at all enemies within the area, dealing physical damage. Every wave of arrows are released at 0.5 seconds intervals. Channels for 3 seconds.

Level 1 - 35 damage.
Level 2 - 50 damage.
Level 3 - 65 damage.
Level 4 - 80 damage.

Cast Range: 400
Target Type: Unit Area (400)
Cooldown: 20/19/18/17 seconds
Channeling

Screenshot:
boltsequencess.jpg

Code:
JASS:

library BoltSequence uses Damage, GroupUtils, T32

constant native UnitAlive takes unit id returns boolean

//===========================================================================
//                           CONFIGURABLES                        
//===========================================================================

globals
    private constant integer ABILID = &#039;ABBS&#039; // raw code of ability &quot;Bolt Sequence&quot;
    private constant string ORDERID = &quot;blight&quot; // order ID of &quot;Bolt Sequence&quot;
    private constant integer DUMMYID = &#039;dBOL&#039; // raw code of unit &quot;Bolt Sequence Dummy&quot;
    
    private constant real ANIMTIME = 1.334 // this value is the attack animation time of the model
    
    private constant real COLLISION = 50.0 // collision check for arrow impact
    private constant real TRUECOL = COLLISION * COLLISION // square of COLLISION
    
    private constant real SPEED = 1000.0 // distance traveled by projectile per second
    private constant real TRUESPEED = SPEED * T32_PERIOD // distance per interval
    
    private constant attacktype ATK = ATTACK_TYPE_CHAOS // attack type of damage
    private constant damagetype DMG = DAMAGE_TYPE_NORMAL // damage type of damage
endglobals

// area of effect
private constant function GetArea takes integer level returns real
    return 400.
endfunction

// damage per wave
private constant function GetDamage takes integer level returns real
    return 15. * level + 20.
endfunction

// interval per wave
private constant function GetWaves takes integer level returns real
    return 0.5
endfunction

// target filter
private constant function GetFilter takes unit c, unit u returns boolean
    return /*
    */ UnitAlive(u) /* // target is alive
    */ and IsUnitEnemy(u, GetOwningPlayer(c)) /* // target is an enemy
    */ and not IsUnitType(u, UNIT_TYPE_STRUCTURE) /* // target is not a structure
    */
endfunction

//===========================================================================
//                          END CONFIGURABLES                        
//===========================================================================

private struct Projectile
    unit u
    unit tar
    unit dummy
    real damage
    
    private method periodic takes nothing returns nothing
        local real x = GetUnitX(this.dummy)
        local real y = GetUnitY(this.dummy)
        local real dx = GetUnitX(this.tar) - x
        local real dy = GetUnitY(this.tar) - y
        local real a
        
        if dx * dx + dy * dy &lt;= TRUECOL then
            call UnitDamageTargetEx(this.u, this.tar, this.damage, true, false, ATK, DMG, null)
            call KillUnit(this.dummy)
            
            call this.stopPeriodic()
            
            call this.deallocate()
        else
            set a = Atan2(dy, dx)
            call SetUnitX(this.dummy, x + TRUESPEED * Cos(a))
            call SetUnitY(this.dummy, y + TRUESPEED * Sin(a))
            call SetUnitFacing(this.dummy, a * bj_RADTODEG)
        endif
    endmethod
    implement T32x
    
    static method create takes unit u, unit tar, real damage returns thistype
        local thistype this = thistype.allocate()
        local real x = GetUnitX(u)
        local real y = GetUnitY(u)
        
        set this.u = u
        set this.tar = tar
        set this.dummy = CreateUnit(GetOwningPlayer(this.u), DUMMYID, x, y, Atan2(GetUnitY(tar) - y, GetUnitX(tar) - x) * bj_RADTODEG)
        set this.damage = damage
        
        call this.startPeriodic()
        
        return this
    endmethod
endstruct

private struct Data
    unit u
    real area
    real damage
    real interval
    real count
    real x
    real y
    static thistype tempData
    
    private static method filterFunc takes nothing returns boolean
        local thistype this = tempData
        local unit u = GetFilterUnit()
        
        if GetFilter(this.u, u) then
            call Projectile.create(this.u, u, this.damage)
        endif
        
        set u = null
        return false
    endmethod
    
    private method periodic takes nothing returns nothing
        if this.count &lt;= 0 then
            set tempData = this
            call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.area, Filter(function thistype.filterFunc))
            set this.count = this.interval - T32_PERIOD
        else
            set this.count = this.count - T32_PERIOD
        endif
        if GetUnitCurrentOrder(this.u) != OrderId(ORDERID) then
            call SetUnitTimeScale(this.u, 1.)
            call this.stopPeriodic()
            call this.deallocate()
        endif
    endmethod
    implement T32x

    private static method onCast takes nothing returns boolean
        local thistype this
        local integer level
        
        set this = thistype.allocate()
        set this.u = GetTriggerUnit()
        set level = GetUnitAbilityLevel(this.u, ABILID)
        set this.area = GetArea(level)
        set this.damage = GetDamage(level)
        set this.interval = GetWaves(level)
        set this.count = this.interval
        set this.x = GetSpellTargetX()
        set this.y = GetSpellTargetY()
            
        set tempData = this
        call GroupEnumUnitsInArea(ENUM_GROUP, this.x, this.y, this.area, Filter(function thistype.filterFunc))
            
        call SetUnitTimeScale(this.u, ANIMTIME / this.interval)
        call this.startPeriodic()

        return false
    endmethod
    
    private static method onInit takes nothing returns nothing
        local trigger t = CreateTrigger()
        
        call GT_RegisterStartsEffectEvent(t, ABILID)
        call TriggerAddCondition(t, Condition(function thistype.onCast))
        
        //preload
        call RemoveUnit(CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMYID, 0, 0, 0))
        
        set t = null
    endmethod
endstruct

endlibrary


Credits
Code:
[B]Jesus4Lyf[/B]
- [URL=http://www.thehelper.net/forums/showthread.php/130752-Advanced-Indexing-Data-Storage]AIDS[/URL]
- [URL=http://www.thehelper.net/forums/showthread.php/131287-Damage]Damage[/URL]
- [URL=http://www.thehelper.net/forums/showthread.php/123288-GTrigger-Event-System]GTrigger[/URL]
- [URL="http://www.thehelper.net/forums/showthread.php/132538-Timer32?highlight=Timer32"]Timer32[/URL]
[B]Rising_Dusk[/B] - [URL=http://www.wc3c.net/showthread.php?t=104464]GroupUtils[/URL]
[B]Bribe[/B] - [URL=http://www.hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/]Table[/URL]
[B]JetFangInferno[/B] - [URL="http://www.hiveworkshop.com/forums/models-530/brilliance-50443/?prev=search%3DGrudge%26d%3Dlist%26r%3D20"]Eagle Eye Model[/URL]
[B]Granado Espada[/B] - [URL="http://ge.hanbiton.com/Home/Home.aspx"]Skill Icons[/URL]


Changelogs
Code:
[B]Version 1.0[/B]
- Initial release

[B]Version 1.1[/B]
- Rewrote the triggers using Timer32 and TimerUtils instead of KeyTimers2
- Optimized code

[B]Version 1.2[/B]
- Removed use of TimerUtils
- Optimized code

[B]Version 1.3[/B]
- Optimized code

Feedback will be appreciated.
 

Attachments

  • Shadow Sting Spellpack v1.2.w3x
    167.1 KB · Views: 538
  • Shadow Sting Spellpack v1.3.w3x
    167.6 KB · Views: 681
Eagle Eye
  1. The preloading part of [ljass]function SetUp[/ljass] should be moved to the initialization, but keep the rawcode setting up there. It is also faster to loop backwards from 4 to 0, but make sure you call SetUp() before looping.
  2. [LJASS]function GetDuration, function GetTargetDuration, function GetArmorPenalty, function GetMaxPenalty[/LJASS] could all be constant functions so that they will be inlined by JASSHelper (saving a function call).
  3. It may be wise to get Timer32 rather than TimerUtils in [ljass]struct Data[/ljass], because you're dealing with rather high frequencies for TimerUtils.
  4. In [ljass]private static method onInit[/ljass] from Data, you do [ljass]set t = null[/ljass], which isn't necessary for an initialization function.
  5. Again, in [LJASS]struct Damage[/LJASS] you are using TimerUtils when T32 would be better suited for the job.
  6. You are using a hashtable for Damage, but a Table would keep the hashtable limit low while providing the same functionality.
  7. Again in damageFunc, you could save time on Line 182 by looping down instead of up.
  8. In [ljass]private static method onInit[/LJASS] you do [ljass]set t = null[/ljass] which you don't have to do for an initialization function.

Heartbreaker
  1. [ljass]function GetDamage, function GetDamageLimit, function GetStunLimit, function GetDelayTime, function GetArea[/ljass] can be constant functions and be inlined by JASSHelper.
  2. You could probably add a constant boolean for if someone wants to use [LJASS]native UnitAlive[/LJASS], in case they want to use the optimizer.
  3. Again, as in your last spell, you should use Table rather than a hashtable. If you're using [LJASS]InitHashtable[/LJASS] four times you lower the total allowed hashtables to 252.
  4. The "Unix Rule of 3" applies in [ljass]struct Curse[/ljass], in [ljass]private static method countDown[/ljass]. You should just inline [ljass]GetExpiredTimer()[/ljass] twice, rather than declare a local variable and null it. This applies for all native calls, as they're so fast anyways.
  5. You use [ljass]call this.deallocate[/ljass] in countDown. Why not just destroy it? That way if you want to override destroy later you may save yourself a headache.
  6. Add in a static if by your locals so that you do not declare a useless local.
    JASS:
    
    static if SHOWTEXT then
        local texttag t
    endif
    ...
    static if SHOWTEXT then
        set t = null
    endif
  7. You call [ljass]UnitAlive[/ljass] three times in [ljass]method periodic[/ljass], so you should just set it to a boolean. This also makes your code more manageable when you make a static if for checking if the unit is alive.
  8. [ljass](bj_PI / 2)[/ljass] is redundant, it's just going to come out to [LJASS]1.57[/LJASS] anyways, because reals in WC3 only have accuracy to three decimal points, so replace it with [LJASS]Cos(1.57)[/LJASS] and [LJASS]Sin(1.57)[/LJASS].
  9. t and u do not need to be nulled in [LJASS]onInit[/LJASS] from [LJASS]struct Data[/LJASS]
The comments about UnitAlive are optional :)

Bolt Sequence
  1. [LJASS]function GetArea, function GetDamage, function GetWaves[/LJASS] can all be constant functions so JASSHelper will inline them.
  2. You could follow my instructions for [LJASS]UnitAlive[/LJASS] from the above spell if you'd like.
  3. You do [LJASS]call this.deallocate[/LJASS] again in [ljass]method periodic[/ljass]. You may as well use .destroy.
  4. On the If-statement on Line 117 you have an extra pair of parenthesis around GetUnitCurrentOrder.
  5. You do [ljass]set t = null[/ljass] in an initialization function.

Just fix that up and you should be good to go :)
 
>(bj_PI / 2) is redundant, it's just going to come out to 1.57 anyways, because reals in WC3 only have accuracy to three decimal points, so replace it with Cos(1.57) and Sin(1.57).

attachment.php

JASS:
local real r = Cos(bj_PI/2)
local real halfPi = bj_PI/2
local real r2 = Cos(1.57)
call BJDebugMsg(&quot;Cos(bj_PI/2) = &quot; + R2SW(r,5,10))
call BJDebugMsg(&quot;Cos(1.57) = &quot; + R2SW(r2,5,10))
call BJDebugMsg(&quot;bj_PI/2 = &quot; + R2SW(halfPi,5,10))


Cos(bj_PI/2) is not equal to Cos(1.57).
Even using R2S, rather than R2SW, Cos(bj_PI/2) comes to 0.000 while Cos(1.57) comes to 0.001.
 

Attachments

  • halfpivs157.bmp
    100.5 KB · Views: 1,237
I stand corrected.

But I would assume using 1.57079480 would be quicker, unless JASS is really that horrendous...
 
bj_PI / 2 is better than 1.57079480, because that's just a garbage value without any meaning.
 
Just stash 1.5708 into a constant variable and reference it by name.

The optimizer will inline & destroy that constant when done anyway,
so no worries.
 
Updated.

For the nulling onInit part, does it really matter? I though nulling is okay in onInit. I just have a habit of nulling them local variables :p

I just prefer using [ljass]deallocate[/ljass] directly. I don't think that should be a problem for this spell, right?
 
>For the nulling onInit part, does it really matter? I though nulling is okay in onInit. I just have a habit of nulling them local variables :p
It's unnecessary, the ref count doesn't matter if the function is only being run once.

>I just prefer using deallocate directly. I don't think that should be a problem for this spell, right?
It doesn't really make a difference.

Approved.

PS: I like what you did with the Changelogs.
 
Nulling onInit/player handles should be a matter of preference and not a requirement.
 
Thank you sir, you may find it helpful to read the entire thread.
 
Read the thread, at no point did you clarify whether or not
your approval depended on whether perpetual handles were
nulled or not.
 
>For the nulling onInit part, does it really matter? I though nulling is okay in onInit. I just have a habit of nulling them local variables :p
>It's unnecessary, the ref count doesn't matter if the function is only being run once.
If you want to discuss this any more you can take it to the Private Messaging system.
 
Hi I am relatively new to maping,today I found out about AIDS,do I download and use AIDS to import your spells to my map or is jass enough to import them?
 
You can find everything on the test map; their documentations state it cleary how to import them.

You'll need the JASS NewGen Pack, which contains the JASSHelper BTW.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • The Helper The Helper:
    News portal has been retired. Main page of site goes to Headline News forum now
  • The Helper The Helper:
    I am working on getting access to the old news portal under a different URL for those that would rather use that for news before we get a different news view.
  • Ghan Ghan:
    Easily done
    +1
  • The Helper The Helper:
    https://www.thehelper.net/pages/news/ is a link to the old news portal - i will integrate it into the interface somewhere when i figure it out
  • Ghan Ghan:
    Need to try something
  • Ghan Ghan:
    Hopefully this won't cause problems.
  • Ghan Ghan:
    Hmm
  • Ghan Ghan:
    I have converted the Headline News forum to an Article type forum. It will now show the top 20 threads with more detail of each thread.
  • Ghan Ghan:
    See how we like that.
  • The Helper The Helper:
    I do not see a way to go past the 1st page of posts on the forum though
  • The Helper The Helper:
    It is OK though for the main page to open up on the forum in the view it was before. As long as the portal has its own URL so it can be viewed that way I do want to try it as a regular forum view for a while
  • Ghan Ghan:
    Yeah I'm not sure what the deal is with the pagination.
  • Ghan Ghan:
    It SHOULD be there so I think it might just be an artifact of having an older style.
  • Ghan Ghan:
    I switched it to a "Standard" article forum. This will show the thread list like normal, but the threads themselves will have the first post set up above the rest of the "comments"
  • The Helper The Helper:
    I don't really get that article forum but I think it is because I have never really seen it used on a multi post thread
  • Ghan Ghan:
    RpNation makes more use of it right now as an example: https://www.rpnation.com/news/
  • The Helper The Helper:
  • The Helper The Helper:
    What do you think Tom?
  • tom_mai78101 tom_mai78101:
    I will have to get used to this.
  • tom_mai78101 tom_mai78101:
    The latest news feed looks good
  • The Helper The Helper:
    I would like to see it again like Ghan had it the first time with pagination though - without the pagination that view will not work but with pagination it just might...
  • The Helper The Helper:
    This drink recipe I have had more than a few times back in the day! Mind Eraser https://www.thehelper.net/threads/cocktail-mind-eraser.194720/

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top