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