Spell Water Cyclone

BlackRose

Forum User
Reaction score
239
Water Cyclone

Becomes the core of a vicious cyclone, waves of water swirl in, sweeping in enemy units. Upon reaching the eye, the water violently explodes, releasing enemy units swept in a violent nova outwards.

Screenshots:
Thanks to Tom_Kazansky
watercyclone1.jpg
watercyclone2.jpg

The libraries this spell requires are:
TimerUtils, xefx, xepreload, AutoFly

JASS:
//+--------------------------------------------
//|     Water Cyclone by BlackRose             
//+--------------------------------------------
//| Requires:
//+----------
//| TimerUtils by Vexorian
//| AutoFly    by Azlier
//| xefx       by Vexorian
//| xepreload  by Vexorian
//|
//| Description
//+------------
//| Becomes the core of a vicious cyclone, waves of water swirl in, 
//| sweeping in enemy units. Upon reaching the eye, the water violently
//| explodes, releasing enemy units swept in a violent nova outwards.
//|
//| Other:
//+--------
//| Uses a modified model of the NagaDeath effect. For performance issues. 
//+---------------------------------------------------------------------------+

scope WaterCyclone initializer onInit // requires TimerUtils, AutoFly, xefx, xepreload

	globals
		private constant integer SPELL_ID = 'A000'
        
        private constant boolean DAMAGE_OVERTIME = false
        private constant boolean SETXY           = false
        // Use Position or XY. Position stops orders.
        
		private constant real TIMER_INTERVAL = 0.03125  // Each x do stuff.
        
        private constant integer FIREWORKS_NUMBER = 35  // How many firework effects are made when done spinning in.
        private constant string  FIREWORKS_EFFECT = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdx"  // Main model of the effect.
        private constant string  FIREWORKS_FLASH  = ""  // Constant flash on effect.
        private constant real    FIREWORKS_SCALE  = 1.00
        private constant string  ON_LANDING_EFFECT = "war3mapImported\\NagaDeathSpare.mdx"//""
        private constant boolean CREATE_ON_DUMMY   = false
        
        private constant real AOE = 900
        private constant real ENUM_AOE = 200
        private constant real ENUM_HEIGHT = 90
        private constant integer SPIRAL_NUMBER = 3
        private constant real SPININ_DURATION = 1.5
        private constant real ANGLE_INCREMENT = 5
        private constant real HEIGHT = 45.
        private constant string INWARDS_EFFECT = ""//"Abilities\\Weapons\\WardenMissile\\WardenMissile.mdx"
        private constant string FLASH_EFFECT = "war3mapImported\\NagaDeathSpare.mdx"//"Abilities\\Spells\\Human\\Feedback\\ArcaneTowerAttack.mdx"
        private constant real   INWARDS_SCALE = 1.00
        
        private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL
        private constant damagetype DMG_TYPE = DAMAGE_TYPE_MAGIC
        private constant weapontype WPN_TYPE = null

	endglobals

    // First half is dealt over time during the spin.
    // Second half is dealt upon the landing.
    // If DAMAGE_OVERTIME is true.
    private function DAMAGE takes integer Level returns real
        return Level * 125.
    endfunction
        
    globals
        private constant group ENUM_GROUP = CreateGroup()
        private constant group INGROUP = CreateGroup()
        private unit TEMP_UNIT
        private unit TEMP_CASTER
        private player TEMP_PLAYER
        private boolexpr UNIT_FILTER
        
        private real TX
        private real TY
        private real TEMP_REAL
    endglobals

// AceHart.
private function GetParabolaZ takes real x,real d,real h returns real
    return 4 * h * x * (d - x) / (d * d)
endfunction

    private struct FireworksData
        boolean NotDummy = false
        real Distance
        real Height
        real Duration
        unit Dummy
        unit Source
        
        real DamageDealt = 0
        real DamageMax
        
        real DPS
        real x
        real y
        real z
        real Speed
        real Cosine
        real Sine
        real d = 0
        
        timer t
        
        xefx myfx
        method onDestroy takes nothing returns nothing
            if not DAMAGE_OVERTIME then
                call UnitDamageTarget( .Source, .Dummy, .DPS, true, true, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
            elseif .NotDummy then
                set .DPS = .DamageMax - .DamageDealt
                call UnitDamageTarget( .Source, .Dummy, .DPS, true, true, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
            endif
            if not .NotDummy then
                if CREATE_ON_DUMMY then
                    call DestroyEffect( AddSpecialEffect( ON_LANDING_EFFECT, .x, .y ) )
                endif
                call .myfx.destroy()
            else
                call DestroyEffect( AddSpecialEffect( ON_LANDING_EFFECT, .x, .y ) )
                call SetUnitPathing( .Dummy, true )
                call GroupRemoveUnit( INGROUP, .Dummy )
                set .Dummy = null
                set .Source = null
            endif
            call ReleaseTimer( .t )
        endmethod
        
        static method move takes nothing returns nothing
            local thistype d = GetTimerData( GetExpiredTimer() )
            set d.d = d.d + d.Speed
            set d.x = d.x + d.Speed * d.Cosine 
            set d.y = d.y + d.Speed * d.Sine 
            set d.z = GetParabolaZ( d.d, d.Distance, d.Height )

            if d.NotDummy then
                if SETXY then
                    call SetUnitX( d.Dummy, d.x )
                    call SetUnitY( d.Dummy, d.y )
                else
                    call SetUnitPosition( d.Dummy, d.x, d.y )
                endif
                call SetUnitFlyHeight( d.Dummy, d.z, 0 )
            else
                set d.myfx.x = d.x
                set d.myfx.y = d.y
                set d.myfx.z = d.z
                if FIREWORKS_FLASH != "" then
                    call d.myfx.flash( FIREWORKS_FLASH )
                endif
            endif

            if DAMAGE_OVERTIME and d.NotDummy then
                call UnitDamageTarget( d.Source, d.Dummy, d.DPS, true, false, ATK_TYPE, /*
                */ DMG_TYPE, WPN_TYPE )
                set d.DamageDealt = d.DamageDealt + d.DPS
            endif

            if d.d >= d.Distance then
                call d.destroy()
            endif
        endmethod
    
        static method create takes real x, real y, unit Target, unit Source, real DPS returns thistype
            local thistype d = thistype.allocate()
            local real angle = GetRandomReal( 0, 359 ) * bj_DEGTORAD
    
            set d.Distance = GetRandomReal( 200, 600 )
            set d.Duration = d.Distance / 280
            set d.Height = d.Distance * 1.1
            
            set d.x = x
            set d.y = y
            set d.z = 0
            set d.Speed = d.Distance / ( d.Duration / TIMER_INTERVAL )
            set d.Cosine = Cos( angle )
            set d.Sine = Sin( angle )
            
            if Target == null then
                //set d.Dummy = CreateUnit( Player(14), DUMMY_ID, x, y, angle * bj_RADTODEG)
                set d.myfx = xefx.create( x, y, angle )
                set d.myfx.fxpath = FIREWORKS_EFFECT
            else
                set d.NotDummy = true
                set d.Dummy = Target
                call SetUnitPathing( d.Dummy, false )
                set d.Source = Source
                set d.DPS = DPS
                if DAMAGE_OVERTIME then
                    set d.DamageMax = d.DPS * (SPININ_DURATION / TIMER_INTERVAL )
                endif
            endif
            
            set d.t = NewTimer()
            call SetTimerData( d.t, d )
            call TimerStart( d.t, TIMER_INTERVAL, true, function thistype.move )
            return 0
        endmethod
    endstruct

	private struct WCData
		unit Caster
		player Owner
		xefx array myfx[SPIRAL_NUMBER]
        real array Cosine[SPIRAL_NUMBER]
        real array Sine[SPIRAL_NUMBER]
        group array Group[SPIRAL_NUMBER]
        group DamagedGroup
        real Radius = AOE
        real RadianAdder
        real RadiusDecrement
        real cx
        real cy
        real DPS
        
        timer t
        debug real Seconds = 0.00
        method onDestroy takes nothing returns nothing
            local integer i = 0
            local unit u
            local FireworksData f
            call ReleaseTimer( .t )
            if INWARDS_EFFECT != "" then
                loop
                    exitwhen i == SPIRAL_NUMBER
                    call .myfx<i>.destroy()
                    set i = i + 1
                endloop
            endif
            set i = 0
            loop
                exitwhen i == FIREWORKS_NUMBER
                set f = FireworksData.create( .cx, .cy, null, null, 0 )
                set i = i + 1
            endloop
            loop
                set u = FirstOfGroup( .DamagedGroup )
                exitwhen u == null
                set f = FireworksData.create( .cx, .cy, u, .Caster, .DPS )
                if not DAMAGE_OVERTIME then
                    call UnitDamageTarget( .Caster, u, .DPS, true, true, /*
                    */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
                endif
                call GroupRemoveUnit( .DamagedGroup, u )
            endloop
            set .Caster = null
            set .Owner = null
        endmethod
        
		static method create takes nothing returns thistype
			local thistype d = thistype.allocate()
            local real x
            local real y
            local integer i = 0
            local real angle = 360 / SPIRAL_NUMBER
                        
            set d.t = NewTimer()

			set d.Caster = GetTriggerUnit()
			set d.Owner = GetOwningPlayer( d.Caster )
            set d.cx = GetUnitX(d.Caster)
            set d.cy = GetUnitY(d.Caster)
            
            set d.RadiusDecrement = ( AOE / ( SPININ_DURATION/TIMER_INTERVAL) )
            set d.RadianAdder     = ANGLE_INCREMENT * bj_DEGTORAD
            
            set d.DPS = DAMAGE( GetUnitAbilityLevel( d.Caster, SPELL_ID ) ) / 2
            if DAMAGE_OVERTIME then
                set d.DPS = d.DPS / ( SPININ_DURATION / TIMER_INTERVAL )
            endif
            
            loop
                exitwhen i == SPIRAL_NUMBER
                set d.Cosine<i> = ( bj_DEGTORAD * (angle*i) )
                set d.Sine<i> = ( bj_DEGTORAD * (angle*i) )
                set x = d.cx + AOE * Cos(d.Cosine<i>)
                set y = d.cy + AOE * Sin(d.Sine<i>)
                //set d.Dummy<i> = CreateUnit( d.Owner, DUMMY_ID, x, y, bj_UNIT_FACING )
                if INWARDS_EFFECT != &quot;&quot; then
                    set d.myfx<i> = xefx.create( x, y, HEIGHT )
                    set d.myfx<i>.fxpath = INWARDS_EFFECT
                endif

                if d.Group<i> == null then
                    set d.Group<i> = CreateGroup()
                else
                    call GroupClear( d.Group<i> )
                endif
                set i = i + 1
            endloop
            
            if d.DamagedGroup == null then
                set d.DamagedGroup = CreateGroup()
            else
                call GroupClear( d.DamagedGroup )
            endif
            
			return d
		endmethod
        
        static method UnitFilter takes nothing returns boolean
            set TEMP_UNIT = GetFilterUnit()
            return IsUnitEnemy( TEMP_UNIT, TEMP_PLAYER ) and /*
            */ GetWidgetLife( TEMP_UNIT ) &gt; 0.405 and /*
            */ IsUnitType( TEMP_UNIT, UNIT_TYPE_STRUCTURE ) == false and/*
            */ GetUnitFlyHeight( TEMP_UNIT ) &lt; ENUM_HEIGHT and /*
            */ IsUnitInGroup( TEMP_UNIT, INGROUP ) == false
        endmethod
        
        static method MoveXY takes nothing returns nothing
            local unit u = GetEnumUnit()
            call SetUnitX( u, TX )
            call SetUnitY( u, TY )
            if DAMAGE_OVERTIME then
                call UnitDamageTarget( TEMP_UNIT, u, TEMP_REAL, true, true, /*
                */ ATK_TYPE, DMG_TYPE, WPN_TYPE )
            endif
        endmethod
        
        static method update takes nothing returns nothing
            local thistype d = GetTimerData(GetExpiredTimer())
            local real x
            local real y
            local integer i = 0
            local unit u
            debug set d.Seconds = d.Seconds + TIMER_INTERVAL
            loop
                exitwhen i == SPIRAL_NUMBER
                set d.Cosine<i> = d.Cosine<i> + d.RadianAdder
                set d.Sine<i> = d.Sine<i> + d.RadianAdder

                set x = d.cx + d.Radius * Cos(d.Cosine<i>)
                set y = d.cy + d.Radius * Sin(d.Sine<i>)
                if INWARDS_EFFECT != &quot;&quot; then
                    set d.myfx<i>.x = x
                    set d.myfx<i>.y = y
                endif
                if FLASH_EFFECT != &quot;&quot; then
                    call DestroyEffect( AddSpecialEffect( FLASH_EFFECT, x, y ) )
                endif
                set TEMP_PLAYER = d.Owner
                call GroupEnumUnitsInRange( ENUM_GROUP, x, y, ENUM_AOE, UNIT_FILTER )
                loop
                    set u = FirstOfGroup( ENUM_GROUP )
                    exitwhen u == null
                    call GroupAddUnit( d.DamagedGroup, u )
                    call GroupAddUnit( d.Group<i>, u )
                    call GroupAddUnit( INGROUP, u )
                    call GroupRemoveUnit( ENUM_GROUP, u )
                endloop
                set TX = x
                set TY = y
                set TEMP_UNIT = d.Caster
                set TEMP_REAL = d.DPS
                call ForGroup( d.Group<i>, function thistype.MoveXY )
                set i = i + 1
            endloop

            set d.Radius = d.Radius - d.RadiusDecrement

            if d.Radius &lt;= 0 then
                call d.destroy()
            endif
        endmethod
	endstruct

	private function CheckSpell takes nothing returns boolean
        local WCData d
		if GetSpellAbilityId() == SPELL_ID then
            set d = WCData.create()
            call SetTimerData( d.t, d )
			call TimerStart( d.t, TIMER_INTERVAL, true, function WCData.update )
		endif
		return false
	endfunction

	private function onInit takes nothing returns nothing
		local trigger t = CreateTrigger()
        
        call XE_PreloadAbility( SPELL_ID )

		call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
		call TriggerAddCondition( t, Condition( function CheckSpell ) )

        set UNIT_FILTER = Condition( function WCData.UnitFilter ) 
		set t = null
	endfunction


endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>


Is my indenting really that messed up? [del]I don't recall having random blank lines everywhere.[/del] Alright, I do have random lines. But the spacing is copying / pasting wrongly!!!
 

BlackRose

Forum User
Reaction score
239
[del]I tried. It failed. It made my .gif into a .jpg and then the screenshot failed even more. =.="

Getting a new one.[/del]

And, my god. I don't like [ljass]BJDebugMsg[/ljass]'s stacking up to the point I get confused as to which instance it's showing messages for. [del]I'll debug the function and endfunction then o.o[/del] Removed it entirely.
 

BlackRose

Forum User
Reaction score
239
Try working around with the constant globals, like setting ANGLE_INCREMENT to 25 or something :)
 

BRUTAL

I'm working
Reaction score
118
I don't know if this counts as a glitch, but i spam-casted it like, 4 times quickly.
This happened.
idkr.jpg
 

BlackRose

Forum User
Reaction score
239
How many firework missiles are there? Chances are maybe exceeded TimerUtils quantity. Debug mode will confirm this! Try again :)
 

Joker(Div)

Always Here..
Reaction score
86
Oh, I somehow missed it.

Anyway, spell looks awesome. The screenshot doesnt really give it any justice. I thought it was going to be one of those boring spiral abilites, but it has a cool finish. Good job. :)
 
General chit-chat
Help Users

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top