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

Sparkling Bolt v0.02

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
First of all: Credits are in the map.

Description: Launches a big energy ball. The ball charges towards the target unit damages the target on contact. On its way the ball loses some energy in form of liddle sparks which drops down. On impact they deal some damage to all enemies around.

Please give credits if you use this spell.

Its now possible to target ground. Also added the possibility to create more then one sparkel per interval.


JASS:
scope BurningOrbit initializer init

//*************************************************************************************************************//
//                                                  Sparkling Bolt                                             //
//                                                        by                                                   //
//                                                      cedi                                                   //
//                                                                                                             //
//                                         needs: TimerUtils by Vexorian                                       //
//                                                Bound Sentinel by Vexorian                                   //
//                                                Dummy Model by                                               //
//                                                Vector lib by                                                //
//                                                Heights by cedi                                              //
//*************************************************************************************************************//

//For use, copy the trigger to your map, copy the dummy create a spell and adjust the values below.

private keyword Bolt
private keyword Spark

globals
    //ID of the spell
    private constant integer        SPELL_ID            = 'A000'
    //ID of your dummy
    private constant integer        DUMMY_ID            = 'h000'
    //Interval of the moves
    private constant real           TIMER_INTERVAL      = 0.035
    //The normal gravity ( change to adjust the drop speed )
    private constant real           GRAVITY             = 9.81
    //BOLT
    //Color of the bolt in rgb
    private constant integer        BOLT_RED            = 255
    private constant integer        BOLT_GREEN          = 255
    private constant integer        BOLT_BLUE           = 255
    private constant integer        BOLT_ALPHA          = 255
    //Speed of the bolt in wc3 units
    private constant real           BOLT_SPEED          = 500.00
    //Shoot angle like the one in the oe
    private constant real           BOLT_HEIGHT         = 0.65
    //Size of the bolt
    private constant real           BOLT_SIZE           = 4.00
    //Collisions range of the bolt
    private constant real           BOLT_COL_RANGE      = 50.00 //Collisionsrange
    //Model of the bolt
    private constant string         BOLT_MODEL          = "Abilities\\Weapons\\VengeanceMissile\\VengeanceMissile.mdl"
    //Effect on hit
    private constant string         BOLT_HIT_SFX        = ""
    //SPARK
    //Color of the spark in rgb
    private constant integer        SPARK_RED           = 255
    private constant integer        SPARK_GREEN         = 255
    private constant integer        SPARK_BLUE          = 255
    private constant integer        SPARK_ALPHA         = 255
    //Start speed of the sparks in wc3 units
    private constant real           SPARK_SPEED         = 250.00
    //Size of the sparks
    private constant real           SPARK_SIZE          = 0.5
    //Each ... seconds create a spark
    private constant real           SPARK_CD            = 0.10
    //Speed lose of the speed gain through the movement of the bolt
    private constant real           SPARK_SPEED_LOSE    = 0.04
    //Sparks model
    private constant string         SPARK_MODEL         = "Abilities\\Weapons\\SerpentWardMissile\\SerpentWardMissile.mdl"
    //Effect when the sparks hit the ground.
    private constant string         SPARK_HIT_SFX       = ""
    //private constant string         SPARK_SPELL_ORDER   = ""
    
    //SYSTEM
    private          vector         G
    private          vector         TEMPVEC
    private          Bolt           TEMPBOLT
    private          Spark          TEMPSPARK
    private          group          GROUP               = CreateGroup()
endglobals

private function BOLT_DAMAGE takes integer level returns real
    return 100.00 + 50.00 * level
endfunction

private function SPARK_DAMAGE takes integer level returns real
    return 20.00 + 10.00 * level
endfunction

private function AOE takes integer level returns real
    return 70.00 + 5.00 * level
endfunction

//*************************************************************************************************************//
//                                                     !SYSTEM!                                                //
//*************************************************************************************************************//

private function AngleBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function DistanceBetweenCoordinates takes real x1, real x2, real y1, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function AngleBetweenUnits takes unit u, unit u2 returns real
    return bj_RADTODEG * Atan2(GetUnitY( u2 ) - GetUnitY( u ), GetUnitX( u2 ) - GetUnitX( u ))
endfunction

private function IsAliveAndUnitAndNotMagicImmune takes nothing returns boolean
    return GetWidgetLife( GetFilterUnit() ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false
endfunction

private function ParabolaZ2 takes real y0, real y1, real h, real d, real x returns real
  local real A = (2*(y0+y1)-4*h)/(d*d)
  local real B = (y1-y0-A*d*d)/d
  return A*x*x + B*x + y0
endfunction

private function BoltControl takes nothing returns nothing
    set TEMPBOLT = GetTimerData( GetExpiredTimer() )
    call TEMPBOLT.control()
endfunction

private function SparkControl takes nothing returns nothing
    set TEMPSPARK = GetTimerData( GetExpiredTimer() )
    call TEMPSPARK.control()
endfunction

private struct Spark
    unit spark
    unit caster
    vector startspeed
    vector boltspeed
    vector posi
    timer t
    effect model
    integer level
    
    method onDestroy takes nothing returns nothing
        local unit u
        call GroupEnumUnitsInRange( GROUP, .posi.x, .posi.y, AOE( .level ), function IsAliveAndUnitAndNotMagicImmune )
        loop
            set u = FirstOfGroup( GROUP )
            exitwhen u == null
            if IsUnitEnemy( u, GetOwningPlayer( .spark ) ) then
                call UnitDamageTarget( .caster, u, SPARK_DAMAGE( .level ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
            endif
            call GroupRemoveUnit( GROUP, u )
            set u = null
        endloop
        call DestroyEffect( AddSpecialEffect( SPARK_HIT_SFX, .posi.x, .posi.y ) )
        call .startspeed.destroy()
        call .boltspeed.destroy()
        call .posi.destroy()
        call ReleaseTimer( .t )
        set .t = null
        call DestroyEffect( .model )
        set .model = null
        call KillUnit( .spark )
        set .spark = null
    endmethod
    
    private method Zangle takes real z returns nothing
        local integer i=R2I(z*bj_RADTODEG+90.5)
        if(i>=180) then
            set i=179
        elseif(i<0) then
            set i=0
        endif
            
        call SetUnitAnimationByIndex( .spark, i  )
    endmethod
    
    method control takes nothing returns nothing
        local real z
        local real x
        local real y
        if GetWidgetLife( .spark ) <= 0.405 then
            call .destroy()
            return
        endif
        //Gravity
        call .startspeed.subtract( G )
        //boltspeed
        set .boltspeed.x = .boltspeed.x * ( 1.00 - SPARK_SPEED_LOSE )
        set .boltspeed.y = .boltspeed.y * ( 1.00 - SPARK_SPEED_LOSE )
        set .boltspeed.z = .boltspeed.z * ( 1.00 - SPARK_SPEED_LOSE )
        //Calc
        set TEMPVEC.x = .posi.x
        set TEMPVEC.y = .posi.y
        set TEMPVEC.z = .posi.z
        call TEMPVEC.add( .startspeed )
        call TEMPVEC.add( .boltspeed )
        //dif
        set x = TEMPVEC.x - .posi.x
        set y = TEMPVEC.y - .posi.y
        set z = TEMPVEC.z - .posi.z
        //Zangle
        call .Zangle( Atan(( z  / (SquareRoot(x*x + y*y))) ) ) 
        //Unit facing
        call SetUnitFacing( .spark, AngleBetweenCoordinates( .posi.x, TEMPVEC.x, .posi.y, TEMPVEC.y ) )
        //new posi
        set .posi.x = TEMPVEC.x
        set .posi.y = TEMPVEC.y
        set .posi.z = TEMPVEC.z
        //move there
        call SetUnitX( .spark, .posi.x )
        call SetUnitY( .spark, .posi.y )
        call SetUnitZ( .spark, .posi.z )
        //touch ground?
        if GetUnitZ( .spark ) <= 1.00 or GetUnitFlyHeight( .spark ) <= 1.00 or .posi.z <= 1.00 then
            call .destroy() //damage in destroy
        endif
    endmethod
    
    private method calcRandomSpeed takes nothing returns nothing
        local real a = GetRandomReal( 0.00, 360.00 ) * bj_DEGTORAD
        local real b = GetRandomReal( 0.00, 180.00 ) * bj_DEGTORAD
        local real r = SPARK_SPEED
        local vector temp = vector.create( r*Cos(a)*Cos(b), r*Cos(a)*Sin(b), r*Sin(a) )
        set .startspeed = vector.create( temp.x * TIMER_INTERVAL, temp.y * TIMER_INTERVAL, temp.z * TIMER_INTERVAL )
    endmethod
    
    static method create takes Bolt b returns thistype
        local thistype this = thistype.allocate()
        set .caster = b.caster
        call .calcRandomSpeed() //stes startspeed
        set .boltspeed = vector.create( b.move.x, b.move.y, b.zdif )
        set .posi = vector.create( b.posi.x, b.posi.y, b.posi.z )
        set .level = b.level
        set .spark = CreateUnit( GetOwningPlayer( b.bolt ), DUMMY_ID, .posi.x, .posi.y, 0.00 )
        set .model = AddSpecialEffectTarget( SPARK_MODEL, .spark, "origin" )
        set .t = NewTimer()
        call SetUnitVertexColor( .spark, SPARK_RED, SPARK_GREEN, SPARK_BLUE, SPARK_ALPHA )
        call SetUnitScale( .spark, SPARK_SIZE, SPARK_SIZE, SPARK_SIZE )
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function SparkControl )
        return this
    endmethod
    
endstruct

private struct Bolt
    unit bolt       = null
    unit caster     = null
    unit target     = null
    integer level   = 1
    real cd         = 0.00
    real zdif       = 0.00
    vector posi
    vector move
    vector start
    timer t         = null
    effect model    = null
    
    method onDestroy takes nothing returns nothing
        call DestroyEffect( AddSpecialEffect( BOLT_HIT_SFX, .posi.x, .posi.y ) )
        call UnitDamageTarget( .caster, .target, BOLT_DAMAGE( .level ), true, false, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, null )
        call .posi.destroy()
        call .move.destroy()
        call .start.destroy()
        call ReleaseTimer( .t )
        set .t = null
        call DestroyEffect( .model )
        set .model = null
        call KillUnit( .bolt )
        set .bolt = null
    endmethod
    
    private method Zangle takes real z returns nothing
        local integer i=R2I(z*bj_RADTODEG+90.5)
        if(i>=180) then
            set i=179
        elseif(i<0) then
            set i=0
        endif
            
        call SetUnitAnimationByIndex(.bolt, i  )
    endmethod
    
    method control takes nothing returns nothing
        local real angle
        local real xdif
        local real ydif
        local real dist
        if GetWidgetLife( .bolt ) <= 0.405 then
            call .destroy()
            return
        endif
        if IsUnitInRange( .bolt, .target, BOLT_COL_RANGE ) then
            call .destroy()
            return
        endif
        //Bolt move
        set angle = AngleBetweenUnits( .bolt, .target ) * bj_DEGTORAD
        set xdif = .posi.x
        set ydif = .posi.y
        set .move.x = Cos( angle ) * BOLT_SPEED * TIMER_INTERVAL
        set .move.y = Sin( angle ) * BOLT_SPEED * TIMER_INTERVAL
        call .posi.add( .move )
        set xdif = .posi.x - xdif
        set ydif = .posi.y - ydif
        call SetUnitX( .bolt, .posi.x )
        call SetUnitY( .bolt, .posi.y )
        call SetUnitFacing( .bolt, angle * bj_RADTODEG )
        set dist = DistanceBetweenCoordinates( .start.x, GetUnitX( .target), .start.y, GetUnitY( .target ) )
        set .posi.z = ParabolaZ2( .start.z, GetUnitZ( .target ), dist * BOLT_HEIGHT, dist, DistanceBetweenCoordinates( .start.x, .posi.x, .start.y, .posi.y ) ) 
        set .zdif = GetUnitZ( .bolt )
        call SetUnitZ( .bolt, .posi.z )
        set .zdif = GetUnitZ( .bolt ) - .zdif
        call .Zangle( Atan(( .zdif  / (SquareRoot(xdif*xdif + ydif*ydif))) ) ) 
        //spark
        set .cd = .cd - TIMER_INTERVAL
        if .cd <= 0.00 then
            set .cd = SPARK_CD
            call Spark.create( this )
        endif
    endmethod
    
    static method create takes unit caster, unit target returns thistype
        local thistype this = thistype.allocate()
        local real angle
        set .caster = caster
        set .target = target
        set .level = GetUnitAbilityLevel( caster, SPELL_ID )
        set .cd = SPARK_CD
        set .posi = vector.create( GetUnitX( caster ), GetUnitY( caster ), GetUnitZ( caster ))
        set angle = AngleBetweenUnits( caster, target )
        set .bolt = CreateUnit( GetOwningPlayer( caster ), DUMMY_ID, .posi.x, .posi.y, angle )
        set .model = AddSpecialEffectTarget( BOLT_MODEL, .bolt, "origin" )
        call SetUnitVertexColor( .bolt, BOLT_RED, BOLT_GREEN, BOLT_BLUE, BOLT_ALPHA )
        call SetUnitScale( .bolt, BOLT_SIZE, BOLT_SIZE, BOLT_SIZE )
        set .move = vector.create( ( BOLT_SPEED * TIMER_INTERVAL ) * Cos( angle * bj_DEGTORAD ), ( BOLT_SPEED * TIMER_INTERVAL ) * Sin( angle * bj_DEGTORAD ), 0.00 )
        set .start = vector.create( .posi.x, .posi.y, .posi.z )
        
        set .t = NewTimer()
        call SetTimerData( .t, this )
        call TimerStart( .t, TIMER_INTERVAL, true, function BoltControl )
        return this
    endmethod
endstruct

private function IsSpell takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call Bolt.create( GetTriggerUnit(), GetSpellTargetUnit() )
    endif
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function IsSpell ) )
    
    set G = vector.create( 0.00, 0.00, GRAVITY * TIMER_INTERVAL )
    set TEMPVEC = vector.create( 0.00, 0.00, 0.00 )
    
    set t = null
endfunction

endscope

Keywords:
Energy, lightning, bolt, spark, missile, red, bullet, orb, ball
Contents

Sparkling Bolt v0.02 (Map)

Reviews
12th Dec 2015 IcemanBo: Too long time as NeedsFix. Rejected. 19:07, 17th Jun 2010 The_Reborn_Devil: The coding looks good, but I wouldn't advice using TimerUtils for this. Your two functions "DistanceBetweenCoordinates" and "ParabolaZ2" could...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: Too long time as NeedsFix. Rejected.

19:07, 17th Jun 2010
The_Reborn_Devil:

The coding looks good, but I wouldn't advice using TimerUtils for this. Your two functions "DistanceBetweenCoordinates" and "ParabolaZ2" could be one-liners. You have also forgotten to give credits to the creator of the ParabolaZ2 function.


Status: Rejected until updated
Rating: N/A

PM me or another mod once you've updated this to get it reviewed again. Have a nice day!
 
Level 11
Joined
Apr 29, 2007
Messages
826
Nope, when the model vanish also the unit vanish:

call KillUnit( .bolt )
set .bolt = null
call KillUnit( .spark )
set .spark = null

In the ondestroy methods of the structs.

You don't need to null struct members ;)


Nice usage of the vector library.

Some of the functions could be constant.

Also no need for hidden tags, the jass tags are awesome now.

Constants functions are not different from normal ones, except they can't use non-constant function calls.
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
cedi don't mind Mainywc3sear, as far as i know he has joined the THW recently and most of his observations aren't viewed the same by anyone than himself.

I personally think the eyecandy is good and that Mainywc3sear should try to be more objective and not so subjective when it comes to his comments :p
 
Level 8
Joined
May 31, 2009
Messages
439
Quite interesting. But I think what would make it better is if it would be able to target a point instead of a target...or maybe even both? It was a little frustrating because it would keep asking me to target a unit.

Asides from that, I give it a 4/5. It's a very cool spell with lots of cool SFX, but in all honesty I odn't see in what situation I could use a spell like this. And if you manage to to get it to target a point and unit, I am ready to boost it :D.
 
Level 7
Joined
Feb 9, 2009
Messages
203
cedi don't mind Mainywc3sear, as far as i know he has joined the THW recently and most of his observations aren't viewed the same by anyone than himself.

I personally think the eyecandy is good and that Mainywc3sear should try to be more objective and not so subjective when it comes to his comments :p

eyecandy so bad because they are only effects the game itself. not much different
 
Level 10
Joined
May 19, 2008
Messages
176
LOL. You're the type person which wants everywhere imports. Dude you should know that the people doesn't like spells which needs imports. But hey, I made it very easy to change anything, so go on import your things and be happy.

cedi
 
Level 25
Joined
Jun 5, 2008
Messages
2,572
@Mainywc3sear

I strongly suggest you to stick to your icons, since you know nothing of coding and possibly learn to articulate your words into a sentence before posting such useless comments.

Imports aren't allowed in spell submissions, the rules state so.

@cedi

Sorry for posting offtopic stuff in your spell thread.

EDIT:

Mainywc3sear seems to be banned, i wonder why...
 
Top