Spellpack Shadow Sting Spellpack

Discussion in 'Spells' started by Ayanami, Feb 5, 2011.

  1. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    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

    [​IMG]
    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:

    [​IMG]

    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] > 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 > 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 < 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 < 0
                call UnitAddAbility(u, ARMORID[i])
                set i = i - 1
            endloop
            call RemoveUnit(u)
            
            set t = null
            set u = null
        endmethod
    endstruct
    
    endlibrary


    [​IMG]
    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:
    [​IMG]

    Code:
    JASS:
    
    library Heartbreaker uses Damage, GroupUtils, T32, Table
    
    constant native UnitAlive takes unit id returns boolean
    
    //===========================================================================
    //                           CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABHe' // raw code of ability "Heartbreaker"
        private constant integer DUM_ABILID_1 = 'AHe0' // raw code of ability "Heartbreaker (Stun)"
        private constant integer DUM_ABILID_2 = 'AHe1' // raw code of ability "Heartbreaker (Buff)"
        private constant integer BUFFID_1 = 'BHe0' // raw code of buff "Heartbreaker (Stun)"
        private constant integer BUFFID_2 = 'BHe1' // raw code of buff "Heartbreaker (Buff)"
        private constant integer DUMMYID = 'dHEA' // raw code of unit "Heartbreaker Dummy"
        private constant integer CASTERID = 'cAST' // raw code of unit "Caster Dummy"
        private constant integer PRELOADID = 'prel' // raw code of unit "Preloader"
        
        private constant string FX = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" // effect used upon being stunned
        private constant string FX_AT = "origin" // attachment point of FX
        private constant string PROJ_FX = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl" // effect used upon projectile impact
        
        private constant boolean SHOWTEXT = true // true if floating text (displaying stun duration) is displayed
        private constant string COLOR = "|cffff0000" // 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 <= 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, "firebolt", 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 <= 0 or this.damage >= this.damagelimit or not b then
                if b and this.damage > 0 then
                    if this.damage > 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) + "." + I2S(digittwo) + "!|r", 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, "firebolt", 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 <= 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


    [​IMG]
    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:
    [​IMG]

    Code:
    JASS:
    
    library BoltSequence uses Damage, GroupUtils, T32
    
    constant native UnitAlive takes unit id returns boolean
    
    //===========================================================================
    //                           CONFIGURABLES                        
    //===========================================================================
    
    globals
        private constant integer ABILID = 'ABBS' // raw code of ability "Bolt Sequence"
        private constant string ORDERID = "blight" // order ID of "Bolt Sequence"
        private constant integer DUMMYID = 'dBOL' // raw code of unit "Bolt Sequence Dummy"
        
        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 <= 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 <= 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.
     

    Attached Files:

  2. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Eagle Eye
    1. The preloading part of function SetUp 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. function GetDuration, function GetTargetDuration, function GetArmorPenalty, function GetMaxPenalty 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 struct Data, because you're dealing with rather high frequencies for TimerUtils.
    4. In private static method onInit from Data, you do set t = null, which isn't necessary for an initialization function.
    5. Again, in struct Damage 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 private static method onInit you do set t = null which you don't have to do for an initialization function.

    Heartbreaker
    1. function GetDamage, function GetDamageLimit, function GetStunLimit, function GetDelayTime, function GetArea can be constant functions and be inlined by JASSHelper.
    2. You could probably add a constant boolean for if someone wants to use native UnitAlive, 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 InitHashtable four times you lower the total allowed hashtables to 252.
    4. The "Unix Rule of 3" applies in struct Curse, in private static method countDown. You should just inline GetExpiredTimer() 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 call this.deallocate 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 UnitAlive three times in method periodic, 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. (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).
    9. t and u do not need to be nulled in onInit from struct Data
    The comments about UnitAlive are optional :)

    Bolt Sequence
    1. function GetArea, function GetDamage, function GetWaves can all be constant functions so JASSHelper will inline them.
    2. You could follow my instructions for UnitAlive from the above spell if you'd like.
    3. You do call this.deallocate again in method periodic. 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 set t = null in an initialization function.

    Just fix that up and you should be good to go :)
     
  3. trb92

    trb92 Throwing science at the wall to see what sticks

    Ratings:
    +142 / 0 / -0
    >(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).

    [​IMG]
    JASS:
    local real r = Cos(bj_PI/2)
    local real halfPi = bj_PI/2
    local real r2 = Cos(1.57)
    call BJDebugMsg("Cos(bj_PI/2) = " + R2SW(r,5,10))
    call BJDebugMsg("Cos(1.57) = " + R2SW(r2,5,10))
    call BJDebugMsg("bj_PI/2 = " + 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.
     

    Attached Files:

  4. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    I stand corrected.

    But I would assume using 1.57079480 would be quicker, unless JASS is really that horrendous...
     
  5. Uberplayer

    Uberplayer -

    Ratings:
    +454 / 0 / -0
    bj_PI / 2 is better than 1.57079480, because that's just a garbage value without any meaning.
     
  6. BlackRose

    BlackRose Forum User

    Ratings:
    +239 / 0 / -0
    Doesn't .destroy call .allocate anyways?
     
  7. Bribe

    Bribe vJass errors are legion

    Ratings:
    +67 / 0 / -0
    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.
     
  8. Ayanami

    Ayanami 칼리

    Ratings:
    +287 / 0 / -0
    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 deallocate directly. I don't think that should be a problem for this spell, right?
     
  9. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    >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.
     
    • Like Like x 1
  10. Bribe

    Bribe vJass errors are legion

    Ratings:
    +67 / 0 / -0
    Nulling onInit/player handles should be a matter of preference and not a requirement.
     
  11. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    Thank you sir, you may find it helpful to read the entire thread.
     
  12. Bribe

    Bribe vJass errors are legion

    Ratings:
    +67 / 0 / -0
    Read the thread, at no point did you clarify whether or not
    your approval depended on whether perpetual handles were
    nulled or not.
     
  13. tooltiperror

    tooltiperror Super Moderator Staff Member

    Ratings:
    +233 / 0 / -0
    >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.
     
  14. D359

    D359 Member

    Ratings:
    +1 / 0 / -0
    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?
     
  15. luorax

    luorax Invasion in Duskwood

    Ratings:
    +67 / 0 / -0
    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.
     

Share This Page