• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Static Charge v1.3

Static Charge

This is a spellpack that contains 4 abilities all having the same concept,movement.

Requirements :
- JNGP
- TerrainPathability by Rising Dusk : http://www.wc3c.net/showthread.php?t=103862


(Q) When used pulls all nearby enemy units close to the hero dealing 30-75 damage. The unit takes more damage the farther it is from the caster.

(W) When used pushes all nearby units away from the hero dealing 30-75 damage. The unit takes more damage the closer it is from the caster.

(E) When used pulls all enemies in a designated location dealing 30-75 damage. The unit takes more damage the farther it is from the location.

(R) When used pushes away all units from the designated location dealing 30-75 damage. The unit takes more damage the closer it is from the location.


Static Field - Attract [Q] :
JASS:
scope StaticFieldA
    native UnitAlive takes unit id returns boolean
    
    globals
        private constant real FPS = 0.0312500
        private constant string MODEL_PATH = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
        private constant integer SPELL_ID = 'A000'
        private constant string LIGHTNING_ID = "FORK"
        private constant real LIGHTNING_RED = 0.15
        private constant real LIGHTNING_GREEN = 0.45
        private constant real LIGHTNING_BLUE = 1.
        private constant real LIGHTNING_ALPHA = 1.
        private constant real DURATION = 0.75
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC
        private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
    endglobals
    
    private constant function MaxDamage takes integer level returns real
        return 75.*level
    endfunction
    
    private constant function MinDamage takes integer level returns real
        return 30.*level
    endfunction
    
    private constant function Aoe takes integer level returns real
        return 600 + 75.*level
    endfunction
    
    private function UnitFilter takes player source, unit targ returns boolean
        return IsUnitEnemy(targ, source) and UnitAlive(targ) and not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
    //! textmacro_once STATIC_FIELD_ALLOC
        if thistype(0).prev == 0 then
            set count = count + 1
            set this = count
        else
            set this = thistype(0).prev
            set thistype(0).prev = thistype(0).prev.prev
        endif
        if thistype(0).next == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        else
            set thistype(0).next.prev = this
        endif
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
    //! endtextmacro
    
    private struct Caster extends array
        real x
        real y
        real temp
        thistype prev
        thistype next
        static integer count
        static timer period
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.temp = this.temp - FPS
                if this.temp <= 0 then
                    call DestroyEffect(AddSpecialEffect(MODEL_PATH, this.x, this.y))
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method add takes real x, real y returns nothing
            local thistype this
            //! runtextmacro STATIC_FIELD_ALLOC()
            set this.x = x
            set this.y = y
            set this.temp = DURATION
        endmethod
        
        static method onInit takes nothing returns nothing
            set count = 0
            set period = CreateTimer()
        endmethod
    endstruct
    
    private struct SF extends array
        unit targ
        unit cast
        lightning light
        real dmg
        real cx
        real cy
        real cz
        real tx
        real ty
        real x
        real y
        real z
        integer steps
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
        static location loc
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
            call UnitDamageTarget(this.cast, this.targ, this.dmg, true, false, A_TYPE, D_TYPE, null)
            call DestroyLightning(this.light)
            call SetUnitPropWindow(this.targ, GetUnitDefaultPropWindow(this.targ)*bj_DEGTORAD)
            set this.light = null
            set this.cast = null
            set this.targ = null
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.tx = this.tx+this.x
                set this.ty = this.ty+this.y
                call MoveLocation(loc, this.tx, this.ty)
                set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(this.targ)
                if IsTerrainWalkable(this.tx, this.ty) then
                    call MoveLightningEx(this.light, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                    call SetUnitX(this.targ, this.tx)
                    call SetUnitY(this.targ, this.ty)
                endif
                set this.steps = this.steps - 1
                if this.steps == 0 then
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method cond takes nothing returns boolean
            local thistype this
            local integer lvl
            local real angle
            local unit u
            local unit v
            local real x
            local real y
            local player p
            local real dist
            if GetSpellAbilityId() == SPELL_ID then
                set u = GetTriggerUnit()
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set p = GetOwningPlayer(u)
                set lvl = GetUnitAbilityLevel(u, SPELL_ID)
                call GroupEnumUnitsInRange(g, x, y, Aoe(lvl), null)
                loop
                    set v = FirstOfGroup(g)
                    exitwhen v == null
                    call GroupRemoveUnit(g,v)
                    if UnitFilter(p,v) then
                        //! runtextmacro STATIC_FIELD_ALLOC()
                        set this.cast = u
                        set this.targ = v
                        set this.cx = x
                        set this.cy = y
                        set this.tx = GetUnitX(v)
                        set this.ty = GetUnitY(v)
                        set angle = Atan2(this.ty-y, this.tx-x)
                        set dist = SquareRoot((this.tx-x)*(this.tx-x) + (this.ty-y)*(this.ty-y))
                        set this.dmg = MinDamage(lvl) + (dist/Aoe(lvl))*(MaxDamage(lvl)-MinDamage(lvl))
                        set this.x = -(dist/DURATION)*FPS*Cos(angle)
                        set this.y = -(dist/DURATION)*FPS*Sin(angle)
                        call MoveLocation(loc, x, y)
                        set this.cz = GetLocationZ(loc) + 20
                        call MoveLocation(loc, this.tx, this.ty)
                        set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(v)
                        set this.light = AddLightningEx(LIGHTNING_ID, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        set this.steps = R2I(DURATION/FPS)
                        call SetUnitPropWindow(this.targ, 0)
                    endif
                endloop
                call Caster.add(x,y)
                set u = null
                set p = null
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.cond))
            call Preload(MODEL_PATH)
            set count = 0
            set period = CreateTimer()
            set g = CreateGroup()
            set loc = Location(0,0)
            set t = null
        endmethod
    endstruct
endscope

Static Field - Expulse [W] :
JASS:
scope StaticFieldE
    //native UnitAlive takes unit id returns boolean
    
    globals
        private constant real FPS = 0.0312500
        private constant string MODEL_PATH = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
        private constant integer SPELL_ID = 'A001'
        private constant string LIGHTNING_ID = "FORK"
        private constant real LIGHTNING_RED = 1
        private constant real LIGHTNING_GREEN = 0.45
        private constant real LIGHTNING_BLUE = 0.15
        private constant real LIGHTNING_ALPHA = 1.
        private constant real DURATION = 0.75
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC
        private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
        private constant real SPEED = 600.
    endglobals
    
    private constant function MaxDamage takes integer level returns real
        return 75.*level
    endfunction
    
    private constant function MinDamage takes integer level returns real
        return 30.*level
    endfunction
    
    private constant function Aoe takes integer level returns real
        return 600 + 75.*level
    endfunction
    
    private function UnitFilter takes player source, unit targ returns boolean
        return IsUnitEnemy(targ, source) and UnitAlive(targ) and not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
    //! textmacro_once STATIC_FIELD_ALLOC
        if thistype(0).prev == 0 then
            set count = count + 1
            set this = count
        else
            set this = thistype(0).prev
            set thistype(0).prev = thistype(0).prev.prev
        endif
        if thistype(0).next == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        else
            set thistype(0).next.prev = this
        endif
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
    //! endtextmacro
    
    private struct SF extends array
        unit targ
        unit cast
        lightning light
        real dmg
        real cx
        real cy
        real cz
        real tx
        real ty
        real x
        real y
        real z
        integer steps
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
        static location loc
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
            call UnitDamageTarget(this.cast, this.targ, this.dmg, true, false, A_TYPE, D_TYPE, null)
            call DestroyLightning(this.light)
            call SetUnitPropWindow(this.targ, GetUnitDefaultPropWindow(this.targ)*bj_DEGTORAD)
            set this.light = null
            set this.cast = null
            set this.targ = null
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.tx = this.tx+this.x
                set this.ty = this.ty+this.y
                call MoveLocation(loc, this.tx, this.ty)
                set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(this.targ)
                if IsTerrainWalkable(this.tx, this.ty) then
                    call MoveLightningEx(this.light, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                    call SetUnitX(this.targ, this.tx)
                    call SetUnitY(this.targ, this.ty)
                endif
                set this.steps = this.steps - 1
                if this.steps == 0 then
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method cond takes nothing returns boolean
            local thistype this
            local integer lvl
            local real angle
            local unit u
            local unit v
            local real x
            local real y
            local player p
            local real dist
            if GetSpellAbilityId() == SPELL_ID then
                set u = GetTriggerUnit()
                set x = GetUnitX(u)
                set y = GetUnitY(u)
                set p = GetOwningPlayer(u)
                set lvl = GetUnitAbilityLevel(u, SPELL_ID)
                call GroupEnumUnitsInRange(g, x, y, Aoe(lvl), null)
                call DestroyEffect(AddSpecialEffect(MODEL_PATH, x, y))
                loop
                    set v = FirstOfGroup(g)
                    exitwhen v == null
                    call GroupRemoveUnit(g,v)
                    if UnitFilter(p,v) then
                        //! runtextmacro STATIC_FIELD_ALLOC()
                        set this.cast = u
                        set this.targ = v
                        set this.cx = x
                        set this.cy = y
                        set this.tx = GetUnitX(v)
                        set this.ty = GetUnitY(v)
                        set angle = Atan2(this.ty-y, this.tx-x)
                        set dist = SquareRoot((this.tx-x)*(this.tx-x) + (this.ty-y)*(this.ty-y))
                        set this.dmg = MinDamage(lvl) + (1-(dist/Aoe(lvl)))*(MaxDamage(lvl)-MinDamage(lvl))
                        set this.x = SPEED*FPS*Cos(angle)
                        set this.y = SPEED*FPS*Sin(angle)
                        call MoveLocation(loc, x, y)
                        set this.cz = GetLocationZ(loc)
                        call MoveLocation(loc, this.tx, this.ty)
                        set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(v)
                        set this.light = AddLightningEx(LIGHTNING_ID, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        set this.steps = R2I(DURATION/FPS)
                        call SetUnitPropWindow(this.targ, 0)
                    endif
                endloop
                set u = null
                set p = null
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.cond))
            call Preload(MODEL_PATH)
            set count = 0
            set period = CreateTimer()
            set g = CreateGroup()
            set loc = Location(0,0)
            set t = null
        endmethod
    endstruct
endscope

Distant Field - Attract [E] :
JASS:
scope DistantFieldA
    //native UnitAlive takes unit id returns boolean
    
    globals
        private constant real FPS = 0.0312500
        private constant string MODEL_PATH = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
        private constant integer SPELL_ID = 'A002'
        private constant string LIGHTNING_ID = "FORK"
        private constant real LIGHTNING_RED = 0.15
        private constant real LIGHTNING_GREEN = 0.45
        private constant real LIGHTNING_BLUE = 1.
        private constant real LIGHTNING_ALPHA = 1.
        private constant real DURATION = 0.75
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC
        private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
    endglobals
    
    private constant function MaxDamage takes integer level returns real
        return 75.*level
    endfunction
    
    private constant function MinDamage takes integer level returns real
        return 30.*level
    endfunction
    
    private constant function Aoe takes integer level returns real
        return 600 + 75.*level
    endfunction
    
    private function UnitFilter takes player source, unit targ returns boolean
        return IsUnitEnemy(targ, source) and UnitAlive(targ) and not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
    //! textmacro_once STATIC_FIELD_ALLOC
        if thistype(0).prev == 0 then
            set count = count + 1
            set this = count
        else
            set this = thistype(0).prev
            set thistype(0).prev = thistype(0).prev.prev
        endif
        if thistype(0).next == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        else
            set thistype(0).next.prev = this
        endif
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
    //! endtextmacro
    
    private struct Caster extends array
        real x
        real y
        real temp
        thistype prev
        thistype next
        static integer count
        static timer period
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.temp = this.temp - FPS
                if this.temp <= 0 then
                    call DestroyEffect(AddSpecialEffect(MODEL_PATH, this.x, this.y))
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method add takes real x, real y returns nothing
            local thistype this
            //! runtextmacro STATIC_FIELD_ALLOC()
            set this.x = x
            set this.y = y
            set this.temp = DURATION
        endmethod
        
        static method onInit takes nothing returns nothing
            set count = 0
            set period = CreateTimer()
        endmethod
    endstruct
    
    private struct SF extends array
        unit targ
        unit cast
        lightning light
        real dmg
        real cx
        real cy
        real cz
        real tx
        real ty
        real x
        real y
        real z
        integer steps
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
        static location loc
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
            call UnitDamageTarget(this.cast, this.targ, this.dmg, true, false, A_TYPE, D_TYPE, null)
            call DestroyLightning(this.light)
            call SetUnitPropWindow(this.targ, GetUnitDefaultPropWindow(this.targ)*bj_DEGTORAD)
            set this.light = null
            set this.cast = null
            set this.targ = null
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.tx = this.tx+this.x
                set this.ty = this.ty+this.y
                call MoveLocation(loc, this.tx, this.ty)
                set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(this.targ)
                if IsTerrainWalkable(this.tx, this.ty) then
                    call MoveLightningEx(this.light, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                    call SetUnitX(this.targ, this.tx)
                    call SetUnitY(this.targ, this.ty)
                endif
                set this.steps = this.steps - 1
                if this.steps == 0 then
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method cond takes nothing returns boolean
            local thistype this
            local integer lvl
            local real angle
            local unit u
            local unit v
            local real x
            local real y
            local player p
            local real dist
            if GetSpellAbilityId() == SPELL_ID then
                set u = GetTriggerUnit()
                set x = GetSpellTargetX()
                set y = GetSpellTargetY()
                set p = GetOwningPlayer(u)
                set lvl = GetUnitAbilityLevel(u, SPELL_ID)
                call GroupEnumUnitsInRange(g, x, y, Aoe(lvl), null)
                loop
                    set v = FirstOfGroup(g)
                    exitwhen v == null
                    call GroupRemoveUnit(g,v)
                    if UnitFilter(p,v) then
                        //! runtextmacro STATIC_FIELD_ALLOC()
                        set this.cast = u
                        set this.targ = v
                        set this.cx = x
                        set this.cy = y
                        set this.tx = GetUnitX(v)
                        set this.ty = GetUnitY(v)
                        set angle = Atan2(this.ty-y, this.tx-x)
                        set dist = SquareRoot((this.tx-x)*(this.tx-x) + (this.ty-y)*(this.ty-y))
                        set this.dmg = MinDamage(lvl) + (dist/Aoe(lvl))*(MaxDamage(lvl)-MinDamage(lvl))
                        set this.x = -(dist/DURATION)*FPS*Cos(angle)
                        set this.y = -(dist/DURATION)*FPS*Sin(angle)
                        call MoveLocation(loc, x, y)
                        set this.cz = GetLocationZ(loc) + 20
                        call MoveLocation(loc, this.tx, this.ty)
                        set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(v)
                        set this.light = AddLightningEx(LIGHTNING_ID, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        set this.steps = R2I(DURATION/FPS)
                        call SetUnitPropWindow(this.targ, 0)
                    endif
                endloop
                call Caster.add(x,y)
                set u = null
                set p = null
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.cond))
            call Preload(MODEL_PATH)
            set count = 0
            set period = CreateTimer()
            set g = CreateGroup()
            set loc = Location(0,0)
            set t = null
        endmethod
    endstruct
endscope

Distant Field - Expulse [R] :
JASS:
scope DistantFieldE
    //native UnitAlive takes unit id returns boolean
    
    globals
        private constant real FPS = 0.0312500
        private constant string MODEL_PATH = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl"
        private constant integer SPELL_ID = 'A003'
        private constant string LIGHTNING_ID = "FORK"
        private constant real LIGHTNING_RED = 1
        private constant real LIGHTNING_GREEN = 0.45
        private constant real LIGHTNING_BLUE = 0.15
        private constant real LIGHTNING_ALPHA = 1.
        private constant real DURATION = 0.75
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC
        private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
        private constant real SPEED = 600.
    endglobals
    
    private constant function MaxDamage takes integer level returns real
        return 75.*level
    endfunction
    
    private constant function MinDamage takes integer level returns real
        return 30.*level
    endfunction
    
    private constant function Aoe takes integer level returns real
        return 600 + 75.*level
    endfunction
    
    private function UnitFilter takes player source, unit targ returns boolean
        return IsUnitEnemy(targ, source) and UnitAlive(targ) and not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
    //! textmacro_once STATIC_FIELD_ALLOC
        if thistype(0).prev == 0 then
            set count = count + 1
            set this = count
        else
            set this = thistype(0).prev
            set thistype(0).prev = thistype(0).prev.prev
        endif
        if thistype(0).next == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        else
            set thistype(0).next.prev = this
        endif
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
    //! endtextmacro
    
    private struct SF extends array
        unit targ
        unit cast
        lightning light
        real dmg
        real cx
        real cy
        real cz
        real tx
        real ty
        real x
        real y
        real z
        integer steps
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
        static location loc
        
        method destroy takes nothing returns nothing
            if this.next != 0 then
                set this.next.prev = this.prev
            endif
            set this.prev.next = this.next
            set this.prev = thistype(0).prev
            set thistype(0).prev = this
            if thistype(0).next == 0 then
                call PauseTimer(period)
            endif
            call UnitDamageTarget(this.cast, this.targ, this.dmg, true, false, A_TYPE, D_TYPE, null)
            call DestroyLightning(this.light)
            call SetUnitPropWindow(this.targ, GetUnitDefaultPropWindow(this.targ)*bj_DEGTORAD)
            set this.light = null
            set this.cast = null
            set this.targ = null
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            loop
                exitwhen this == 0
                set this.tx = this.tx+this.x
                set this.ty = this.ty+this.y
                call MoveLocation(loc, this.tx, this.ty)
                set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(this.targ)
                if IsTerrainWalkable(this.tx, this.ty) then
                    call MoveLightningEx(this.light, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                    call SetUnitX(this.targ, this.tx)
                    call SetUnitY(this.targ, this.ty)
                endif
                set this.steps = this.steps - 1
                if this.steps == 0 then
                    call this.destroy()
                endif
                set this = this.next
            endloop
        endmethod
        
        static method cond takes nothing returns boolean
            local thistype this
            local integer lvl
            local real angle
            local unit u
            local unit v
            local real x
            local real y
            local player p
            local real dist
            if GetSpellAbilityId() == SPELL_ID then
                set u = GetTriggerUnit()
                set x = GetSpellTargetX()
                set y = GetSpellTargetY()
                set p = GetOwningPlayer(u)
                set lvl = GetUnitAbilityLevel(u, SPELL_ID)
                call GroupEnumUnitsInRange(g, x, y, Aoe(lvl), null)
                call DestroyEffect(AddSpecialEffect(MODEL_PATH, x, y))
                loop
                    set v = FirstOfGroup(g)
                    exitwhen v == null
                    call GroupRemoveUnit(g,v)
                    if UnitFilter(p,v) then
                        //! runtextmacro STATIC_FIELD_ALLOC()
                        set this.cast = u
                        set this.targ = v
                        set this.cx = x
                        set this.cy = y
                        set this.tx = GetUnitX(v)
                        set this.ty = GetUnitY(v)
                        set angle = Atan2(this.ty-y, this.tx-x)
                        set dist = SquareRoot((this.tx-x)*(this.tx-x) + (this.ty-y)*(this.ty-y))
                        set this.dmg = MinDamage(lvl) + (1-(dist/Aoe(lvl)))*(MaxDamage(lvl)-MinDamage(lvl))
                        set this.x = SPEED*FPS*Cos(angle)
                        set this.y = SPEED*FPS*Sin(angle)
                        call MoveLocation(loc, x, y)
                        set this.cz = GetLocationZ(loc) + 20
                        call MoveLocation(loc, this.tx, this.ty)
                        set this.z = GetLocationZ(loc) + 20 + GetUnitFlyHeight(v)
                        set this.light = AddLightningEx(LIGHTNING_ID, true, this.cx, this.cy, this.cz, this.tx, this.ty, this.z)
                        call SetLightningColor(this.light, LIGHTNING_RED, LIGHTNING_GREEN, LIGHTNING_BLUE, LIGHTNING_ALPHA)
                        set this.steps = R2I(DURATION/FPS)
                        call SetUnitPropWindow(this.targ, 0)
                    endif
                endloop
                set u = null
                set p = null
            endif
            return false
        endmethod
        
        static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
            call TriggerAddCondition(t, Condition(function thistype.cond))
            call Preload(MODEL_PATH)
            set count = 0
            set period = CreateTimer()
            set g = CreateGroup()
            set loc = Location(0,0)
            set t = null
        endmethod
    endstruct
endscope

To import one of those spells :
- Copy and paste the code of the spell you want.
- Copy the ability and paste.
- Change the rawcode if necessary in the globals block of the spell.
- Customize the spell with everything above the textmacro
- Enjoy !


Credits :
- Vexorian for vJASS
- Rising Dusk for the TerrainPathability library

Another Credits :
- My mother for giving birth to me
- My neighbour that hasn't called the police yet even though if I blast Conception@max volume on 3 A.M.
- Roy Khan for singing in Conception and Kamelot
- Riot Games for creating League of Legends
- Black Sabbath for creating Metal
- My fencing sword that hasn't broken
- My battery that has died and deserves a proper burial.
- The Hive Workshop because...it exists?
- My imaginary friend that told me Warcraft 3 is awesome
- My fridge for conserving my food
- All the animals that are butchered just to be turned to food so I can survive.
- The Blood Mage that let me teach him some cool spells
- My bed that lets me sleep on it
- All my clothes that keep me warm
- Last but not least, all my organs that keep working :D
- Paliouras for finding all those useless credits :p


Constructive criticism is welcome !
WARNING : No real footmen were hurt during the process...oh well maybe some!

v1.0 : Initial release :)

v1.1 : Added TerrainPathability :p

v1.2 : Added textmacro_once :)

v1.3 : Fix bug with flying height of units


Keywords:
static, static field, lightning, Malhorne, malhorne, vJASS, JESP, spellpack, attract, expulse, static charge, charge, field
Contents

Just another Warcraft III map (Map)

Reviews
22:25, 9th Feb 2014 BPower: Required changes made and overall good coding. Approved.

Moderator

M

Moderator

22:25, 9th Feb 2014
BPower:
Required changes made and overall good coding.
Approved.
 
Yo Malhorne, nice credit list you got there! ^^

You're using MoveUnitX/Y and this causes some unwilled situations for me.

I changed test map a bit and I was able to push units over cliffs, into buildings, into water, and yes also into deep water.

Please make some kind of pathing check before you move units.

For spell itself... I really like the concept, man! I especially enjoyed the spell where you cast on a point and pushes all enemies away. This can be nice support to help nearby units which have low life, for example. :csmile:
 
I haven't had a chance to test it yet but here's some comments

  • The screenshot needs to be fixed.
  • You don't need to comment the UnitAlive declaration, JassHelper handles it correctly (vexorian's at least).
  • The Aloc textmacros could possibly be a modules (thus being private). The name should at least be a bit more unique. Although I think I would prefer the standard allocation.
  • Preloading should be optional.
  • You should be using GroupUtils's ENUM_GROUP instead of creating 4 groups for each variation.
 
Malhorne said:
Vexorian's bug on UnitAlive.

What bug? The map optimizer works perfectly fine with UnitAlive and so does JassHelper.

Malhorne said:
Inline is 55% faster so textmacros>modules

Modules are basically text macros, why don't you compile and see.

Malhorne said:
What about preloading :eek: ?

It should be optional with static if's.

Malhorne said:
I hate using other resources and just for this ...

Static if's.
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
If you declare multiple times UnitAlive it throws an error.

I inline my allocation in the function whereas Alloc module is a function so calling it is slower....

I don't get why ?
It is always better for the sake of the spell that I preload :/
But if you think it is necessary why not.

I won't use other people resource as usual.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
All spells share the same textmacro, hence you will do the following:
JASS:
    //! textmacro_once STATIC_FIELD_ALLOC
        if thistype(0).prev == 0 then
            set count = count + 1
            set this = count
        else
            set this = thistype(0).prev
            set thistype(0).prev = thistype(0).prev.prev
        endif
        if thistype(0).next == 0 then
            call TimerStart(period, FPS, true, function thistype.periodic)
        else
            set thistype(0).next.prev = this
        endif
        set this.next = thistype(0).next
        set thistype(0).next = this
        set this.prev = thistype(0)
    //! endtextmacro
Similar to library_once the textmacro_once allows you to declare the same textmacro name twice, it would just ignore the second declaration and avoid to add its contents instead of showing a syntax error.

- Preloading could be optional, because it consumes extra time while loading the map.
- The tooltips are horrible.
- The screenshot is horrible.
- Overall good coding.
- SpellEffectEvent could be optional.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
You re-named the textmacro and now it won't mess with others, also Textmacro_once is a solid solution.
GetUnitDefaultPropWindow(this.targ)*bj_DEGTORAD) --> I tested it and it seems to work.

Required

In here something is very wrong. You check Pathability for tx/ty but move to x/y.
In any case I would recommend to keep the cos/sin member. Now either use local x/y or the tx/ty struct members. You have this block in all 4 spells.
JASS:
                set this.tx = this.tx+this.x
                set this.ty = this.ty+this.y
                set x = GetUnitX(this.targ)+this.x
                set y = GetUnitY(this.targ)+this.y
                if IsTerrainWalkable(this.tx, this.ty) then
                    call MoveLightningEx(this.light, true, this.cx, this.cy, this.z, this.tx, this.ty, this.z)
                    call SetUnitX(this.targ, x)
                    call SetUnitY(this.targ, y)
                endif
The lightning should be created and move relative to the units height. Currently it's working well for ground units, but not for type flying (check with a gryphon rider)
or units which change their flyheight during the spell.

Optional
  • The lightning is not associated to the caster, but to a fix point --> It doesn't move with the caster.
    The optimal concept is of course a personal preference, so your way is totally fine aswell.
  • The tooltips are lacking in all spells, you could use the blizzard standart tooltip as template. :thumbs_down:
  • Could use optional SpellEffectEvent (I know you won't do this one)
 
Level 18
Joined
Sep 14, 2012
Messages
3,413
I'll change the pathability (added them while in class was a bad idea) now.
For the unit I'll see now too.

For the lightning I added follow through time because I wanted this spell to be channeled ^^

Updated !
v1.0 : Initial release :)

v1.1 : Added TerrainPathability :p

v1.2 : Added textmacro_once :)

v1.3 : Fix bug with flying height of units
 
Last edited:

Deleted member 219079

D

Deleted member 219079

Your struct handling is beautiful ! no homo !
Could use optional SpellEffectEvent (I know you won't do this one)
Lol :p

I have suggestion ; make the attract spell to be targeted at point with a maximum range of 800 or something like that. Then according to the dist from caster to cast point, you calc the aoe and damage. The bigger the aoe, smaller the damage. Like this:
JASS:
...
private function Pythagoras takes real x, real y returns real
    return SquareRoot(x*x + y*y)
endfunction

private struct vec2
    real x
    real y
endstruct

private function vec takes real whatX, real whatY returns vec2
    local vec2 v = vec2.create()
    set v.x = whatX
    set v.y = whatY
    return v
endfunction

private function V2V takes vec2 v1, vec2 v2 returns real
    return Pythagoras(v1.x-v2.x, v1.y-v2.y)
endfunction

private constant function minDmg takes integer lvl returns real
    return 50. + 50.*lvl
endfunction

private constant function maxDmg takes integer lvl returns real
    return 150. + 50.*lvl
endfunction

struct
    private static real aoeDmgRatio = 0.3 // how much the aoe reduces dmg
    ...
    private static method onCast
        ...
        local vec2 cPos = vec(GetUnitX(caster),GetUnitY(caster)) // castvec
        local vec2 tPos = vec(GetSpellTargetX(), GetSpellTargetY()) // targetvec
        local real dist = V2V(cPos,tPos) // dist (=aoe)
        set damage = RMaxBJ( minDmg(level) , maxDmg(level) - dist*aoeDmgRatio ) // damage relative to the aoe
        ...
    endmethod
    ...
endstruct
...

Sorry if this was a bad suggestion I'm still just your scrubby jass student lolol

And awesome spell pack +rep
 
Level 12
Joined
May 11, 2014
Messages
1,257
It works good and you are a good spell maker but how about to change the name in Gravity Collaps or Presure Damage ? Just an idea .
 
Level 8
Joined
Jul 14, 2010
Messages
235
The ones that attract, attract buildings as well as spell immune units. At least for me and I've only changed the range and damage fields.
Screws the actual position of buildings as the model stay at the old spot, but the whole building is moved.
 
Top