• 🏆 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!

Arrow Storm 1.5

Hi everybody,
Here is a new spell !

The spell works like this : The mage creates a huge arrow that goes in line like a missile.
But when it encounters an enemy it will create again 8 little arrows in circle that will damage enemies. Little arrows don't create again littler arrows it would be mad ^^

Requirements :
- JNGP
- Bound Sentinel by Vexorian that can be found here or in the test map


The code :
JASS:
scope ArrowStorm 
    //requires Bound Sountinel By Vexorian : [url]http://www.wc3c.net/showthread.php?t=102576[/url]
    
    //Feel free to comment this native if you already have it in your map
    native UnitAlive takes unit id returns boolean
    
    
    //CONFIGURATION
    globals
        //The periodic check timer, if you don't know what to do let is as it is.
        private constant real FPS = 0.0312500
        
        //The id of the spell
        private constant integer SPELL_ID = 'C000'
        
        //The id of the primary missile
        private constant integer ID_PRIMARY = 'l000'
        
        //The id of the secondary missile
        private constant integer ID_SECONDARY = 'l001'
        
        //The speed of the primary missile
        private constant real SPEED_PRIMARY = 950.
        
        //The speed of the secondary missile
        private constant real SPEED_SECONDARY = 650.
        
        //The amount of units the missile should start from the caster
        private constant real START_OFFSET = 150.
        
        //The radius around the primary missile the units should get damaged
        private constant real RADIUS_PRIMARY = 150.
        
        //The radius around the secondary missile the units should get damaged
        private constant real RADIUS_SECONDARY = 100.
        
        //The attacktype of the damage
        private constant attacktype A_TYPE =ATTACK_TYPE_MAGIC
        
        //The damagetype of the damage
        private constant damagetype D_TYPE = DAMAGE_TYPE_MAGIC
        
        //The weapontype of the damage
        private constant weapontype W_TYPE = null
        
        //The path of the effect each time the primary missile create secondary missile
        private constant string PATH = "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdl"
        
        //In radian
        //The angle between each secondary missile upon creating
        private constant real SECONDARY_OFFSET = bj_PI/4
        
        //This will tell you how many secondary missiles it will create
        //If you wan to change it usually you have to change the precedent constant.
        //It works with a 2*pi reference.
        //Dividing 2*pi by the SECONDARY_OFFSET
        private constant integer NUMBER_SECONDARY = R2I(bj_PI*2/SECONDARY_OFFSET)
    endglobals
    
    
    //How many damage should the primary missile deal
    private constant function DamagePrimary takes integer level returns real
        return 55.*level
    endfunction
    
    
    //How many damage should the secondary missile deal
    private constant function DamageSecondary takes integer level returns real
        return 15.*level
    endfunction
    
    
    //The distance the primary missile should travel before the end
    private constant function DistancePrimary takes integer level returns real
        return 750.+50.*level
    endfunction
    
    
    //The distance the secondary missile should travel before the end
    private constant function DistanceSecondary takes integer level returns real
        return 300.+25.*level
    endfunction
    
    
    //The filter function 
    private function Unit_Filter takes player source, unit targ returns boolean
        return (UnitAlive(targ)) and (not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)) and IsUnitEnemy(targ, source)
    endfunction
    //END CONFIGURATION
    //MAD CODING AFTER D: !
    
    
    //Textmacro for allocating because I'm lazy ^^
    //! textmacro AS_Alloc takes prefix
        if thistype(0).prev == 0 then
            set count = count + 1
            set this$prefix$ = count
        else
            set this$prefix$ = 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$prefix$
        endif
        set this$prefix$.next = thistype(0).next
        set thistype(0).next = this$prefix$
        set this$prefix$.prev = thistype(0)
    //! endtextmacro
    
    private struct ArrowStorm extends array
        unit caster
        unit missile
        real dmg
        boolean head
        integer lvl
        real X
        real Y
        real steps
        player owner
        group already
        thistype prev
        thistype next
        static integer count
        static timer period
        static group g
        
        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 DestroyGroup(this.already)
            set this.caster = null
            set this.missile = null
            set this.already = null
            set this.owner = null
        endmethod
        
        static method periodic takes nothing returns nothing
            local thistype this = thistype(0).next
            local thistype this2
            local unit u 
            local integer i
            local real x
            local real y
            local real angle
            loop
                exitwhen this == 0
                set x = GetUnitX(this.missile)+this.X
                set y = GetUnitY(this.missile)+this.Y
                call SetUnitX(this.missile, x)
                call SetUnitY(this.missile, y)
                if this.head then
                    call GroupEnumUnitsInRange(g, x, y, RADIUS_PRIMARY, null)
                else
                    call GroupEnumUnitsInRange(g, x, y, RADIUS_SECONDARY, null)
                endif
                loop
                    set u = FirstOfGroup(g)
                    exitwhen u == null
                    call GroupRemoveUnit(g,u)
                    if Unit_Filter(this.owner,u) and not IsUnitInGroup(u,this.already) then
                        call UnitDamageTarget(this.caster, u, this.dmg, true, false, A_TYPE, D_TYPE, W_TYPE)
                        call GroupAddUnit(this.already, u)
                        if this.head then
                            set i = 0
                            set x = GetUnitX(this.missile)
                            set y = GetUnitY(this.missile)
                            call DestroyEffect(AddSpecialEffect(PATH, x, y))
                            loop
                                exitwhen i>NUMBER_SECONDARY
                                //! runtextmacro AS_Alloc("2")
                                set this2.caster = this.caster
                                set this2.owner = this.owner
                                set this2.dmg = DamageSecondary(this.lvl)
                                set this2.head = false
                                set angle = SECONDARY_OFFSET*i
                                set this2.missile = CreateUnit(this2.owner, ID_SECONDARY, x + START_OFFSET*Cos(angle), y + START_OFFSET*Sin(angle), angle*bj_RADTODEG)
                                call UnitApplyTimedLife(this2.missile, 'BTLF', DistanceSecondary(this.lvl)/SPEED_SECONDARY)
                                set this2.X = SPEED_SECONDARY*FPS*Cos(angle)
                                set this2.Y = SPEED_SECONDARY*FPS*Sin(angle)
                                set this2.steps = R2I( DistanceSecondary(this.lvl)/SPEED_SECONDARY/FPS )
                                set this2.already = CreateGroup()
                                set i = i + 1
                            endloop
                        endif
                    endif
                endloop
                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 real x
            local real y
            local real tx
            local real ty
            local real angle
            if GetSpellAbilityId() == SPELL_ID then
                //! runtextmacro AS_Alloc("")
                set this.caster = GetTriggerUnit()
                set this.lvl = GetUnitAbilityLevel(this.caster, SPELL_ID)
                set this.dmg = DamagePrimary(this.lvl)
                set this.head = true
                set x = GetUnitX(this.caster)
                set y = GetUnitY(this.caster)
                set tx = GetSpellTargetX()
                set ty = GetSpellTargetY()
                set angle = Atan2(ty-y, tx-x)
                set this.owner = GetOwningPlayer(this.caster)
                set this.missile = CreateUnit(this.owner, ID_PRIMARY, x+START_OFFSET*Cos(angle), y+START_OFFSET*Sin(angle), angle*bj_RADTODEG)
                call UnitApplyTimedLife(this.missile, 'BTLF', DistancePrimary(this.lvl)/SPEED_PRIMARY)
                set this.X = SPEED_PRIMARY*FPS*Cos(angle)
                set this.Y = SPEED_PRIMARY*FPS*Sin(angle)
                set this.steps = R2I(DistancePrimary(this.lvl)/SPEED_PRIMARY/FPS)
                set this.already = CreateGroup()
            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))
            set count = 0
            set period = CreateTimer()
            set g = CreateGroup()
            call Preload(PATH)
            set t = null
        endmethod
    endstruct
endscope

Credits :
- CakeMaster for the Sun Spear model
- Vexorian for vJASS and Bound Sentinel



v1.0.0 :
- Initial Release

v1.1.0 :
- Added a filter function

v1.2.0 :
- Fix a huge bug with MUI
- Trigger optimization

v1.2.1 :
- Change some coordinates in creation
- Fix a thing in globals
- Change the name of the textmacro for not confusing with Alloc module ^^
- Using Timed life

v1.2.2 :
- Change the look ^^ Thanks to Jad

v1.3 :
- Trigger optimizations.
- Now each period can fire more than one explosion :)

v1.4 :
- Objects changements.
- Added the weapontype in constants.

v1.5 :
- Model updated !


Feel free to leave a comment !
Malhorne

Keywords:
vJASS, Malhorne, arrow, storm, Wrathion, sunspear, sun spear
Contents

Just another Warcraft III map (Map)

Reviews
Arrow Storm 1.3 | Reviewed by Maker | 18.01.14 Concept[/COLOR]] The ability is a basic projectile spell with a twist, it explodes into smaller fragments upon contacting units However I don't think the name is very fitting Triggers[/COLOR]]...
Level 12
Joined
May 11, 2014
Messages
1,257
Lightning fang is an attack who unleash the power of lightning destoying all in a line . What you think ?
 
Top