Blaze v2
Explanations:
- What it does - upon casting the ability, conjures X flaming orbs, which circle the caster, and upon reaching their ascent, careen forth at random targets in the area, exploding upon impact, dealing damage to the unlucky foe.
- Why I made it - I made it for a tower contest, and actually managed to place, which was kind of neat.
- Why you should try it - I think it is a pretty neat ability. Makes use of vJASS to move lots of projectiles around, and you may actually find it useful, if not to use in a map, possibly to learn from.
- Concerns - there is a lot of stuff flying about all at once, so it could get laggy with 10 or less firing at the same time on low end computers. I believe I have updated the trigger to be completely self sufficient, however if I missed something, let me know. Also, do not mind all the other triggers in the map, this was the test map I used for the ability, and I think it is an easy way to demo the spell as it is, simply look in the Spells folder for the abilities trigger.
JASS:
scope Blaze
//******************************************************************************************
//*
//* Blaze v2
//*
//*
//* A neat ability which throws homing fireballs all around at random enemies and
//* deals damage upon collision with them.
//*
//* Requires:
//* - A blaze dummy and blaze abilty in your map, similar to the ones found here
//* - This code copied to a trigger in your map
//*
//******************************************************************************************
globals
//Config Options:
//General:
private constant integer abil_id = 039;A001039; //blaze ability rawcode
private constant real int = .025 //timer interval for effect movement
private constant integer dummy_id = 039;n001039; //blaze dummy rawcode
private constant integer max_missiles = 6 //max number of missiles + 1, ever created for an instance of the spell
//Twirl Portion:
private constant real start_dist = 150. //start distance of missiles before they twirl, away from the caster
private constant real end_height = 280. //end height of missiles when they reach the top of the twirl
private constant real twirl_time = 1. //length in seconds of the twirl portion of the ability
private constant real twirl_speed = 5. //rate of change per timer interval for the angle change of the missiles during the twirl
//Homing Portion:
private constant real speed = 15. //missile speed per timer interval while homing in on a target
private constant boolean new_target = true //whether missiles can get a new target if their original dies while in route
private constant real height_diff = 35. //difference between targets current fly height and the height you want the missile to lower/raise to
private constant attacktype a_type = ATTACK_TYPE_MAGIC //attack type for damage done
private constant damagetype d_type = DAMAGE_TYPE_FIRE //damage type for damage done
endglobals
private function damage takes integer lvl returns real
return lvl*25. //damage done per missile
endfunction
private function range takes integer lvl returns real
return (lvl*50.)+750. //range for missiles to check for targets
endfunction
private function count takes integer lvl returns integer
return 4+lvl //number of missiles created
endfunction
//******************************************************************************************
//DON"T TOUCH PAST THIS POINT, UNLESS YOU PAIN FOR DEATH!
globals
//needed globals:
private unit U = null
private group G = CreateGroup()
private timer Tball = CreateTimer()
private integer Total1 = 0
private integer Total2 = 0
private Blaze_data1 array structArray1
private Blaze_data2 array structArray2
public trigger Trigger = CreateTrigger()
endglobals
//angle between units
private function ABU takes unit a, unit b returns real
return bj_RADTODEG * Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
endfunction
//distance between units
private function DBU takes unit a, unit b returns real
return SquareRoot(Pow(GetUnitX(b) - GetUnitX(a), 2) + Pow(GetUnitY(b)-GetUnitY(a),2))
endfunction
//filter for missiles, this can be edited as desired for further configuration
private function Filt takes nothing returns boolean
return GetWidgetLife(GetFilterUnit())>.405 and IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(U))==true
endfunction
//needed struct 2
struct Blaze_data2
unit ball = null
unit targ = null
real height = end_height
integer lvl = 0
//init struct
method Start takes unit ball, integer lvl returns nothing
//update missile
set .ball = ball
//grab first target
set .targ = GroupPickRandomUnit(G)
set .lvl = lvl
endmethod
//move missile towards target
method Effects takes nothing returns nothing
local real ang = ABU(.ball,.targ)
call SetUnitX(.ball,GetUnitX(.ball) + speed * Cos(ang * bj_DEGTORAD))
call SetUnitY(.ball,GetUnitY(.ball) + speed * Sin(ang * bj_DEGTORAD))
call SetUnitFacing(.ball,ang)
set .height = .height - 10. //this makes it look pretty fluent IMO
if .height<GetUnitFlyHeight(.targ)+height_diff then
set .height = GetUnitFlyHeight(.targ)+height_diff
endif
call SetUnitFlyHeight(.ball,.height,0.0)
endmethod
//update globals
method Update takes integer i returns nothing
set structArray2<i> = structArray2[Total2]
set Total2 = Total2 - 1
endmethod
//end struct
method onDestroy takes nothing returns nothing
//damage target, destroy missile
if .targ!=null then
call UnitDamageTarget(.ball,.targ,damage(.lvl),false,false,a_type,d_type,null)
endif
call KillUnit(.ball)
endmethod
endstruct
//needed struct 1
struct Blaze_data1
integer runs = 0
unit tower = null
unit array ball[max_missiles]
real array ang[max_missiles]
real xtower
real ytower
integer total = 0
real increment = 0.
real change = 0.
real Hchange = 0.
integer total_runs = 0
//initiate all needed struct data
method Start takes unit tower returns nothing
local integer i = 1
local real x = GetUnitX(tower)
local real x2
local real y = GetUnitY(tower)
local real y2
local player p = GetOwningPlayer(tower)
set .total = count(GetUnitAbilityLevel(tower,abil_id))
set .increment = 360/.total
set .total_runs = R2I(twirl_time/int)
set .change = start_dist/.total_runs
set .Hchange = end_height/.total_runs
//loop from 1-total, create balls, store angles
loop
exitwhen i>.total
set .ang<i> = i*.increment
set x2 = x + start_dist * Cos(.ang<i> * bj_DEGTORAD)
set y2 = y + start_dist * Sin(.ang<i> * bj_DEGTORAD)
set .ball<i> = CreateUnit(p,dummy_id,x2,y2,0.)
set i = i + 1
endloop
//store tower and target
set .tower = tower
set .xtower = x
set .ytower = y
endmethod
//effects for each run, update angle, move ball, update fly height
method Effects takes nothing returns nothing
local integer i = 1
local real height = .runs * .Hchange
local real distance = start_dist-(.runs*.change) //get closer as we twirl up, eventually meeting in the middle
loop
exitwhen i>.total
set .ang<i> = .ang<i> + twirl_speed
call SetUnitX(.ball<i>, .xtower + distance * Cos(.ang<i> * bj_DEGTORAD))
call SetUnitY(.ball<i>, .ytower + distance * Sin(.ang<i> * bj_DEGTORAD))
call SetUnitFlyHeight(.ball<i>,height,0.0)
set i = i + 1
endloop
endmethod
//update globals
method Update takes integer i returns nothing
set structArray1<i> = structArray1[Total1]
set Total1 = Total1 - 1
endmethod
//when our struct is destroyed, loop through balls and possibly destroy
method onDestroy takes nothing returns nothing
local integer i = 1
local Blaze_data2 dat
local integer lvl
//tower is dead, end
if GetWidgetLife(.tower)<.405 then
loop
exitwhen i>.total
call KillUnit(.ball<i>)
set i = i + 1
endloop
else
//tower is not dead, add missiles to new struct, and begin the detonation phase
set lvl = GetUnitAbilityLevel(.tower,abil_id)
call GroupClear(G)
set U = .tower
call GroupEnumUnitsInRange(G,.xtower,.ytower,range(lvl),Condition(function Filt))
loop
exitwhen i>.total
set dat = Blaze_data2.create()
call dat.Start(.ball<i>,lvl)
set Total2 = Total2 + 1
set structArray2[Total2] = dat
set i = i + 1
endloop
endif
endmethod
endstruct
//filter for attackers
private function Conditions takes nothing returns boolean
return GetSpellAbilityId()==abil_id
endfunction
//movement for missiles
private function Movement takes nothing returns nothing
local integer i = 1
local Blaze_data1 dat1
local Blaze_data2 dat2
loop
//loop through all current structs 1
exitwhen i > Total1
set dat1 = structArray1<i>
//update our runs total
set dat1.runs = dat1.runs + 1
//effects are over, remove struct and clean up
if dat1.runs>dat1.total_runs or GetWidgetLife(dat1.tower)<.405 then
call dat1.destroy()
call dat1.Update(i)
set i = i - 1
else
//runs are not over, continue with effects
call dat1.Effects()
endif
set i = i + 1
endloop
set i = 1
loop
//loop through all current structs 2
exitwhen i > Total2
set dat2 = structArray2<i>
//target is nothing, or is dead
if dat2.targ==null or GetWidgetLife(dat2.targ)<.405 then
//if the missile can get another target, try and do so
if new_target then
call GroupClear(G)
set U = dat2.ball
call GroupEnumUnitsInRange(G,GetUnitX(dat2.ball),GetUnitY(dat2.ball),range(dat2.lvl),Condition(function Filt))
set dat2.targ = GroupPickRandomUnit(G)
//no new target available
if dat2.targ==null then
call dat2.destroy()
call dat2.Update(i)
endif
//so it runs this missile again, though it is now what was the last missile
set i = i + 1
else
call dat2.destroy()
call dat2.Update(i)
set i = i - 1
endif
//unit is close enough to damage, remove struct and clean up
elseif DBU(dat2.ball,dat2.targ)<speed then //distance between missile and target is less then speed, therefore another movement would most likely carry us past the target
call dat2.destroy()
call dat2.Update(i)
set i = i - 1
else
//runs are not over, continue with effects
call dat2.Effects()
endif
set i = i + 1
endloop
//no more active structs, pause timer
if Total1==0 and Total2==0 then
call PauseTimer(Tball)
endif
endfunction
//init for missiles
private function Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local Blaze_data1 dat = Blaze_data1.create()
//init struct data, add it to array, start timer if not already done so
if Total1==0 and Total2==0 then
call TimerStart(Tball,int,true,function Movement)
endif
call dat.Start(u)
set Total1 = Total1 + 1
set structArray1[Total1] = dat
set u = null
endfunction
//===========================================================================
public function InitTrig takes nothing returns nothing
call TriggerRegisterAnyUnitEventBJ( Trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( Trigger, Condition( function Conditions ) )
call TriggerAddAction( Trigger, function Actions )
endfunction
endscope</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
Updates:
- v2 -fixed some spelling/grammar errors in my documentation and explanations
some functions renamed
added a public global trigger named Blaze_Trigger which is now used at the abilities global trigger
ported to an active instant cast ability
updated the Blaze_data1 arrays to a size so it works properly
changed setunitposition to setunitx/y
reduced groupenum call from max missiles to 1 when grabbing targets for the missiles
removed getunitx/y calls for each missile and during every twirl interval, now done only once
added many configuration parameters
removed all unneeded systems from map - v1 - Initial release
Thoughts? Suggestions? Concerns? Mal contempt?