Spell Ultrablast

XeNiM666

I lurk for pizza
Reaction score
138
Ultrablast
by XeNiM666

I got nothing to do here so here's another spell with a stupid name. :) Can't think of any good name for this spell.

GUI/JASS/vJASS : vJASS
MUI/MPI : MUI
Leakless : Yes. Inform me if not
Lagless : Lags when spammed
Description : Channels for 2 seconds to cocentrate all of his magical power and release a wave of pure magic that deals pure magic damage depending on the caster's current mana when he started channeling.

Screenshots :
U2.jpg

U1.jpg

Code : Please critique my coding...
JASS:
//****************************************************************************************//
//****************************************************************************************//
//                                                                                        //
//                                    ULTRABLAST                                          //
//                                   by XeNiM666                                          //
//                                                                                        //
//****************************************************************************************//
//****************************************************************************************//
//                                                                                        //
//     REQUIRES:                                                                          //
//         1. A vJASS preprocessor ( Jass NewGen Pack )                                   //
//         2. Jesus4Lyf's Key Timers 2.0 ( KT2 ) ( Included )                             //
//         3. Jesus4Lyf's GTriggers ( GT ) ( Included )                                   //
//         4. A dummy with Vexorian's dummy.mdx model                                     //
//                                                                                        //
//****************************************************************************************//
//****************************************************************************************//
//                                                                                        //
//     HOW TO IMPLEMENT:                                                                  //
//         1. Create a trigger                                                            //
//         2. Convert it to custom text                                                   //
//         3. Delete the text inside                                                      //
//         4. Copy all the text here and copy it to the trigger you created               //
//         5. Copy the Universal Dummy unit in this map                                   //
//         7. Copy the Ultrablast Dummy unit in this map                                  //
//         8. Copy the Ultrablast ability in this map                                     //
//                                                                                        //
//****************************************************************************************//
//****************************************************************************************//

scope Ultrablast initializer onInit

    globals
        // THE RAW CODE OF THE SPELL
        private constant integer SPELL_ID = 'A000' 
        // THE ORDER STRING OF THE SPELL
        private constant string SPELL_ORDER_ID = "carrionswarm"
        // THE CASTING TIME OF THE SPELL
        private constant real SPELL_CASTING_TIME = 2.00
        
        // THE PERIOD OF THE TIMER USED...
        private constant real TIMER_PERIOD = 0.03125
        
        // THE RAW CODE OF THE ULTRABLAST DUMMY
        private constant integer BEAM_DUMMY_ID = 'h001'
        
        // THE RAW CODE OF THE DUMMY USED
        // IT MUST HAVE THE "dummy.mdx" AS THE MODEL
        private constant integer DUMMY_ID = 'h000'
        // THE MODEL USED AS THE BEAM
        private constant string BEAM_MODEL = "war3mapImported\\StarfallTarget2.mdx"
        // THE ATTACHMENT POINT OF THE MODEL ABOVE FOR THE DUMMY_ID
        private constant string BEAM_MODEL_ATTACH = "origin"
        // THE HEIGHT OF THE BEAM SO IT DOES'NT LOOK WIERD
        private constant real BEAM_MODEL_HEIGHT = 100.00
        
        // THE MODEL USED AS THE ORB
        private constant string ORB_MODEL = "war3mapImported\\StarfallTarget2.mdx"
        // THE NUMBER OF ORBS SPINNING WHILE CASTING
        private constant integer NUMBER_OF_ORBS = 6
        // THE STARTING DISTANCE OF THE ORBS WHEN IT FIRST STARTED CASTING
        private constant real ORB_DISTANCE_FROM_CASTER = 300.00
        // THE SPEED OF THE ORBS IN ANGLE PER SECOND
        private constant real ORB_SPEED_APS = 270.00
    
        // THE SPEED OF THE BEAM IN WC3 UNITS PER SECOND
        private constant real BEAM_SPEED = 2000.00
        // THE INTERVAL OF THE RELEASE OF EACH BEAM
        private constant real BEAMS_INTERVAL = 0.10
        
        // THE TOTAL NUMBER OF BEAMS
        private constant integer NUMBER_OF_BEAMS = 20
        // THE INITIAL AOE OF THE SPELL
        private constant real INITIAL_AOE = 50.00
    endglobals
    
    // CHECKS WHAT UNITS WILL BE DAMAGED
    // REQUIRES A LITTLE KNOWLEDGE OF JASS
    private function Conditions takes nothing returns boolean
        return GetWidgetLife( GetFilterUnit() ) > 0.408 and IsUnitType( GetFilterUnit(), UNIT_TYPE_STRUCTURE ) == false and IsUnitType( GetFilterUnit(), UNIT_TYPE_FLYING ) == false and GetUnitAbilityLevel( GetFilterUnit(), 'Aloc' ) == 0
    endfunction
    
    // HOW FAR THE BEAM WILL TRAVEL
    private function Distance takes integer lvl returns real
        return 1250.00 + ( lvl * 250.00 )
    endfunction
    
    // THE FINAL AOE OF THE SPELL
    private function Final_AOE takes integer lvl returns real
        return 300.00 + ( lvl * 50.00 )
    endfunction

     //**************************************************************************************//
    //**************************************************************************************//
   //                DO NOT TOUCH UNLESS YOU KNOW WHAT YOU'RE DOING                        //
  //**************************************************************************************//
 //**************************************************************************************//
    
    globals
        private group GROUP = CreateGroup()
    endglobals

    private struct Beam
        unit caster
        real cx
        real cy
        
        unit beam
        effect ef
        real cos
        real sin
        
        real dis
        
        // CHECKS HOW FAR THE BEAM HAS TRAVELED
        real d = 0.00
        
        real aoe
        real dmg
        
        group hit = CreateGroup()
        
        static method Move takes nothing returns boolean
            local Beam b = KT_GetData()
            
            local unit u = null
            
            local real r = BEAM_SPEED * TIMER_PERIOD
            
            local real x = GetUnitX( b.beam ) + r * b.cos
            local real y = GetUnitY( b.beam ) + r * b.sin 
            
            call SetUnitPosition( b.beam, x, y )
            
            call GroupEnumUnitsInRange( GROUP, x, y, b.aoe, Condition( function Conditions ) )
            
            loop
                set u = FirstOfGroup( GROUP )
                exitwhen u == null
                
                if IsUnitEnemy( u, GetOwningPlayer( b.caster ) ) == true and IsUnitInGroup( u, b.hit ) == false then
                    call UnitDamageTarget( b.caster, u, b.dmg, true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
                    
                    call GroupAddUnit( b.hit, u )
                endif
                    
                call GroupRemoveUnit( GROUP, u )
            endloop
            
            set b.d = b.d + r
            
            if b.d >= b.dis then
                call b.destroy()
                
                return true
            endif
            
            return false
        endmethod
        
        method onDestroy takes nothing returns nothing
            call GroupClear( .hit )
            call DestroyGroup( .hit )
            set .hit = null
        
            call DestroyEffect( .ef )
            set .ef = null
        
            call RemoveUnit( .beam )
            set .beam = null
            
            set .caster = null
        endmethod
        
        static method create takes unit caster, real sin, real cos, real aoe, real dmg returns Beam
            local Beam b = Beam.allocate()
            
            local real scale = aoe / 35.00
            // AND YES, 35.00 IS NOT A CONFIGURABLE...
            
            set b.caster = caster
            set b.cx = GetUnitX( b.caster )
            set b.cy = GetUnitY( b.caster )
            
            set b.cos = cos
            set b.sin = sin
            
            set b.aoe = aoe
            set b.dmg = dmg
            
            set b.beam = CreateUnit( GetOwningPlayer( b.caster ), DUMMY_ID, b.cx, b.cy, 270.00 )
            set b.ef = AddSpecialEffectTarget( BEAM_MODEL, b.beam, BEAM_MODEL_ATTACH )
            call SetUnitScale( b.beam, scale, scale, scale )
            call SetUnitFlyHeight( b.beam, BEAM_MODEL_HEIGHT, 0.00 )
            
            set b.dis = Distance( GetUnitAbilityLevel( b.caster, SPELL_ID ) )
            
            call SetUnitState( b.caster, UNIT_STATE_MANA, GetUnitState( b.caster, UNIT_STATE_MANA ) - b.dmg )
            
            call KT_Add( function Beam.Move, b, TIMER_PERIOD )
            
            return b
        endmethod
        
    endstruct
    
    private struct Cast
        unit caster
        real cx
        real cy
        integer order
        
        real tx
        real ty
        
        real ang
        real cos
        real sin
        
        real caoe // caoe FOR CURRENT AOE
        real faoe // faoe FOR FINAL AOE
        
        real dmg
        
        static method Release takes nothing returns boolean
            local Cast c = KT_GetData()
            local Beam b
            
            if c.caoe <= c.faoe then
                // debug call BJDebugMsg( R2S( c.caoe ) )
                set b = Beam.create( c.caster, c.sin, c.cos, c.caoe, c.dmg )
                
                set c.caoe = c.caoe + ( ( c.faoe - INITIAL_AOE ) / NUMBER_OF_BEAMS )
            else
                call c.destroy()
                
                return true 
            endif
            
            return false
        endmethod
        
        static method Wait takes nothing returns boolean
            local Cast c = KT_GetData()
            
            local unit dummy = null
            
            if GetUnitCurrentOrder( c.caster ) == c.order then
                // CREATES THE BEAM AS AN EFFECT
                set dummy = CreateUnit( GetOwningPlayer( c.caster ), BEAM_DUMMY_ID, c.cx, c.cy, bj_RADTODEG * c.ang )
                call UnitApplyTimedLife( dummy, 'BTLF', 3.00 )
                set dummy = null
                
                set c.dmg = GetUnitState( c.caster, UNIT_STATE_MANA ) / NUMBER_OF_BEAMS
                
                call KT_Add( function Cast.Release, c, BEAMS_INTERVAL )
            else
                call c.destroy()
            endif
            
            return true
        endmethod
        
        method onDestroy takes nothing returns nothing
            set .caster = null
        endmethod

    endstruct
    
    private struct Channel
        unit caster
        real cx
        real cy
        integer order
        
        unit orb
        effect eff
        
        real d
        real a
        
        static method Spin takes nothing returns boolean
            local Channel c = KT_GetData()
            
            local real a = ORB_SPEED_APS * TIMER_PERIOD
            local real d = ( ORB_DISTANCE_FROM_CASTER / SPELL_CASTING_TIME ) * TIMER_PERIOD
            
            local real x
            local real y
            
            if GetUnitCurrentOrder( c.caster ) == c.order then
                set c.a = c.a + a
                set c.d = c.d - d
                
                if c.d > 0.00 then
                    set x = c.cx + c.d * Cos( c.a * bj_DEGTORAD )
                    set y = c.cy + c.d * Sin( c.a * bj_DEGTORAD )
                    
                    call SetUnitPosition( c.orb, x, y )
                else
                    call c.destroy()
                    
                    return true
                endif
            else
                call c.destroy()
                    
                return true
            endif
            
            return false
        endmethod
        
        method onDestroy takes nothing returns nothing
            call DestroyEffect( .eff )
            set .eff = null
            
            call RemoveUnit( .orb )
            set .orb = null
            
            set .caster = null
        endmethod
        
        static method create takes unit caster, real x, real y, real ang returns Channel
            local Channel c = Channel.allocate()
            
            set c.caster = caster
            set c.cx = GetUnitX( c.caster )
            set c.cy = GetUnitY( c.caster )
            
            set c.orb = CreateUnit( GetOwningPlayer( c.caster ), DUMMY_ID, x, y, ang )
            set c.eff = AddSpecialEffectTarget( ORB_MODEL, c.orb, BEAM_MODEL_ATTACH )
            call SetUnitFlyHeight( c.orb, BEAM_MODEL_HEIGHT, 0.00 )
            set c.d = ORB_DISTANCE_FROM_CASTER
            set c.a = ang
            
            set c.order = OrderId( SPELL_ORDER_ID )
            
            call KT_Add( function Channel.Spin, c, TIMER_PERIOD )
            
            return c
        endmethod
        
    endstruct
    
    private function U_Actions takes nothing returns boolean
        local Cast c = Cast.create()
        local Channel ch
        
        local unit caster = GetTriggerUnit()
        local real cx = GetUnitX( caster )
        local real cy = GetUnitY( caster )
    
        local integer i = 0
        local real ang
        local real x
        local real y
        
        local location t = GetSpellTargetLoc()
        
        loop
            set i = i + 1
            
            set ang = ( 360.00 / NUMBER_OF_ORBS ) * i
            set x = cx + ORB_DISTANCE_FROM_CASTER * Cos( ang * bj_DEGTORAD )
            set y = cy + ORB_DISTANCE_FROM_CASTER * Sin( ang * bj_DEGTORAD )
            
            set ch = Channel.create( caster, x, y, ang )
            
            exitwhen i >= NUMBER_OF_ORBS
        endloop    
        
        set c.caster = caster
        set c.cx = GetUnitX( c.caster )
        set c.cy = GetUnitY( c.caster )
        
        set c.tx = GetLocationX( t )
        set c.ty = GetLocationY( t )
        
        set c.ang = Atan2( c.ty - c.cy, c.tx - c.cx )
        set c.cos = Cos( c.ang )
        set c.sin = Sin( c.ang )
        
        set c.order = OrderId( SPELL_ORDER_ID )
        
        set c.caoe = INITIAL_AOE
        set c.faoe = Final_AOE( GetUnitAbilityLevel( c.caster , SPELL_ID ) )

        call KT_Add( function Cast.Wait, c, SPELL_CASTING_TIME )
        
        call RemoveLocation( t )
        set t = null
        
        set caster = null

        return false
    endfunction

    //===========================================================================
    
    private function onInit takes nothing returns nothing
        call GT_AddStartsEffectAction( function U_Actions, SPELL_ID )
        
        // PRELOADING <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />
        call RemoveUnit( CreateUnit( Player( 15 ), DUMMY_ID, 0.00, 0.00, 0.00 ) )
        call RemoveUnit( CreateUnit( Player( 15 ), BEAM_DUMMY_ID, 0.00, 0.00, 0.00 ) )
        
        call Preload( BEAM_MODEL )
        call Preload( ORB_MODEL )
        call PreloadStart()
    endfunction

endscope

// ENJOY! <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile    :)" loading="lazy" data-shortname=":)" />

Changelog :
1.0 - Initial Release
1.1 - Fixed a bug where when you test it it returns to wc3's main screen. ( Thanks Weep :) )
1.2 - Made a few values configurable.

Enjoy! :)
 

Attachments

  • Ultrablast 1.2.w3x
    96.9 KB · Views: 250

Weep

Godspeed to the sound of the pounding
Reaction score
400
Imma chargin mah lazer?

The map does not seem to be compiled; war3map.j contains vJASS code and the map will exit to the WC3 main screen when I try to play it.
 

XeNiM666

I lurk for pizza
Reaction score
138
UPDATED: v.1.1
See changelog..

@Weep
- Thanks for pointing that out.... +rep
@kingkingyyk3
- Haha thanks... ( But what does GFX mean? LOL ) :)

Need more comments! :)
 
Reaction score
341
Well, you wanted code critique.

  • Why are you using [ljass]GetSpellTargetLoc()[/ljass]? GetSpellTargetX/Y were introduced in patch 1.24.
  • You should probably make the attachment point/flying height configurable/
    JASS:
        set c.eff = AddSpecialEffectTarget( ORB_MODEL, c.orb, &quot;origin&quot; )
        call SetUnitFlyHeight( c.orb, 100.00, 0.00 )
  • You make want to make the orb's pathing configurable like this;
    JASS:
    if IGNORE_PATHING then
        call SetUnitX(c.orb, x)
        call SetUnitY(c.orb, y)
    else
        call SetUnitPosition( c.orb, x, y )
    endif

    Though it would require you to do some bounds checking. Not that big of a deal though.
  • You may want to store [ljass]OrderId( SPELL_ORDER_ID )[/ljass] since your calling it multiple times.
  • Same goes for storing the casters owning player ([ljass]GetOwningPlayer( b.caster )[/ljass])
  • And also for [ljass]Atan2( c.ty - c.cy, c.tx - c.cx )[/ljass] :p

I haven't tested the spell so I have no comments for the visual aspect.
 

XeNiM666

I lurk for pizza
Reaction score
138
1. I dont have 1.24... My stupid brother doesn't want the patch to be downloaded...
2. Done :)
3. Somehow, when I use SetUnitX/Y, the dummy doesn't move...
4 - 5. Done :)
6. Why? I only use it once... :p

UPDATED v1.2
Thanks TriggerHappy! :D
 

Weep

Godspeed to the sound of the pounding
Reaction score
400
Shoop da whoop!

The FX are a bit big...maybe size it down to something a bit less screen-filling? (Especially the projectiles.)

The spell description ought to say that it will drain all of the caster's mana.

3. Somehow, when I use SetUnitX/Y, the dummy doesn't move...
For whatever reason, the position of the model for a unit with 0 movement speed will not change when using SetUnitX/Y :nuts:...projectile dummies therefore need a movement speed of at least 1. (Dummy casters should have a movement speed of 0 for instant casting.)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      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