function Parabola takes real MaxDist, boolean Positive, real x returns real
if Positive then
// y = x*x - MaxDist
return x*x + MaxDist
else
return -1*(x*x) - MaxDist
endif
endfunction
function Differentiate takes real MaxDist, boolean Positive, real Max returns real
local real x
// function = x*x +- MaxDist
if Positive then
// function = x*x + MaxDist
// Differentiated = 2*x // just learned that at school! =D
// now we have to find the 'x' value that fits our 'Max' value
// This max value is the 'coefficient' for the formula of the ramp
// for further explanation (in good english, which is not my native language)
// google/wikipedia for differentiation of functions and what it does
set x = Max/2
else
// function = -1* (x*x) - MaxDist
// Differentiated = -1 * 2*x = -2*x
// read comment above the 'else'
set x = Max/-2
endif
return x
endfunction
// IF YOU CHANGE THE PARABOLA FUNcTION, YOU HAVE TO REDO THE ABOVE FUNCTION! ( DIFFERENTIATE IT AGAIN )
function DifferentiateYCalc takes real MaxDist, boolean Positive, real MaxDiff returns real
local real a
local real b
local real y
if Positive then
// function = x*x + MaxDist
// Dfunc = 2x
// DFunc = 2* MaxDiff
set a = 2*MaxDiff
// y = a*x + b
set b = Parabola(MaxDist, Positive, x) - a*x
// fill in to calculate 'a' and 'b'
// now fill in OUR x,,
set y = a * x + b
else
set a = -2 *MaxDiff
set b = Parabola(MaxDist, Positive, x) - a*x
set y = a*x + b
endif
return y
endfunction
struct
unit boomerang
real MaxDifferentiatedX
real x
real y
real MaxBoomerangDistance
real cos
real sin
boolean PositiveBool
boolean NegativeParaFirst
endstruct
function Loop takes nothing returns nothing
// loop through structs
if dat.PositiveBool then
set dat.x = dat.x + 1
// or - 1, according to the side you want the boomerang to go to
// decrease or increase this number (the '1' ) to make the boomerang go faster/slower
set dat.y = Parabola(dat.MaxBoomerangDistance/2, dat.PositiveBool, dat.x)
// recalculate coordinates to Warcraft coordinates, use the given 'dat.cos' and 'dat.sin' for it
// if dat.yx >= DifferentiateYCalc(dat.MaxBoomerangDistance/2, dat.PositiveBool, dat.MaxDifferentiatedX) then
// change to other parabola
set dat.PositiveBool = false
endif
else
set dat.x = dat.x - 1
// this one has to be the exact opposite of the one under the 'then'
set dat.y = Parabola(dat.MaxBoomerangDistance/2, dat.PositiveBool, dat.x)
// recalculated coordinates
if dat.y <= DifferentiateYCalc(dat.MaxBoomerangDistance/2, dat.PositiveBool, dat.MaxDifferentiatedX) then
if dat.NegativeParaFirst = true then
set dat.PositiveBool = true
set dat.NegativeParaFirst = false
else
// END THE SPELL HERE
endif
endif
endif
endfunction
function Create takes nothing returns nothing
// initialize struct,, I call it 'dat' now,,
set dat.MaxBoomerangDistance = 500 // Total distance the boomerang will go
set dat.PositiveBool = false // we start with the 'valley' parabola
set dat.NegativeParaFirst = true
set dat.cos = Cos(//angle here
set dat.sin = Sin(//angle here
set dat.x = 0
set dat.y = 0
set dat.MaxDifferentiatedX = Differentiate(dat.MaxBoomerangDistance/2, dat.PositiveBool, -500) // the -500 means that the.. (next line)
// .. coefficient of the formula that calculates the RAMP of the parabola, is -500, which means it is a really steep line.. (next line)
// .. almost vertical, which means we can go to the next parabola safely
set x = Parabola(dat.MaxBoomerangDistance/2, dat.PositiveBool, Differentiate(dat.MaxBoomerangDistance/2, dat.PositiveBool, 0)
// This x is the 'x' in the co-ordinate system of the 'top' of the first parabola, the point where the caster is,,
// if we fill this line in in the Parabola Function, we get our Startin 'y' in the co-ordinate system
// these values have to be recalculated to become Warcraft Coordinates
set dat.y = Parabola(dat.MaxBoomerangDistance/2, dat.PositiveBool, dat.x)
// returns, as stated above, our 'y' coordinate
// Timer Start stuff
endfunction
library Boomerang uses GroupUtils // maybe you need an initializer and requirements; add them here
// Credits:
// - Rising_Dusk for his GroupUtils library
// - Vexorian for JassHelper
// - PipeDream for Grimoire
// - PitzerMike and MindWorX for JassNewGenPack
// - SFilip for TESH
globals
private constant real TICK = 1./32
private constant integer AID = 'A000'
private constant integer DUMMY_ID = 'h000'
private constant real BOOMERANG_SPEED = 600.
private real array DAMAGE //
private constant real BOOMERANG_COLLSIZE = 96.
private constant real BOOMERANG_SIZE = 1.25
private constant real BOOMERANG_HEIGHT = 64.
private constant string HIT_FX = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
private constant string HIT_FX_ATTPT = "chest"
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC
endglobals
private function SetUpDAMAGE takes nothing returns nothing
set DAMAGE[1]=100.
set DAMAGE[2]=150.
set DAMAGE[3]=200.
endfunction
private function Damage takes integer level returns real
return DAMAGE[level]
endfunction
private keyword Data
globals
private location l
private Data tmps
endglobals
private struct Data
unit c // caster
unit dum // boomerang dummy
integer level
real d // planned distance
real a // current angle
real p // periodic angle change
real x // x-coord of launch point
real y // y-coord
real tx // targeted x-coord
real ty // targeted y-coord
real f // base-angle
group g // group holding already damaged enemies
static boolexpr DamageFilter
private integer i
private static thistype array Structs
private static timer T=CreateTimer()
private static integer Count=0
private static method DamageFilterFunc takes nothing returns boolean
local unit u=GetFilterUnit()
if IsUnitType(u, UNIT_TYPE_DEAD)==false and IsUnitType(u, UNIT_TYPE_STRUCTURE)==false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)==false and (not IsUnitInGroup(u, tmps.g)) and IsUnitEnemy(u, GetOwningPlayer(tmps.c)) then
if UnitDamageTarget(tmps.c, u, Damage(tmps.level), false, false, ATTACK_TYPE, DAMAGE_TYPE, null) then
call DestroyEffect(AddSpecialEffectTarget(HIT_FX, u, HIT_FX_ATTPT))
call GroupAddUnit(tmps.g, u)
endif
endif
set u=null
return false
endmethod
method onDestroy takes nothing returns nothing
set .c=null
call KillUnit(.dum)
call ShowUnit(.dum, false)
set .dum=null
call ReleaseGroup(.g)
// clean your struct here
set thistype.Count=thistype.Count-1
set thistype.Structs[.i]=thistype.Structs[thistype.Count]
set thistype.Structs[.i].i=.i
if thistype.Count==0 then
call PauseTimer(thistype.T)
endif
endmethod
private static method Callback takes nothing returns nothing
local integer i=thistype.Count-1
local thistype s
local real r
local real x
local real y
loop
exitwhen i<0
set s=thistype.Structs[i]
// make the boomerang home, even if the caster moves
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.d=SquareRoot( ((s.tx-s.x)*(s.tx-s.x))+((s.ty-s.y)*(s.ty-s.y)) )
set s.p=TICK*((bj_PI*BOOMERANG_SPEED)/(4*s.d))
set s.f=Atan2(s.ty-s.y, s.tx-s.x)-(bj_PI/4)
// functions for moving the boomerang:
// r(a)=distance*Sin(2*a) // a is the angle and goes from 90 to 0 // distance from center point
// x(a)=Cos(a)*r(a) // x and y coordinates in relation to the location it was cast.
// y(a)=Sin(a)*r(a) // note that i inlined some things to allow casting the boomerang in all directions from any point on the map
set r=(s.d*Sin(2*s.a))
set x=s.x+(Cos(s.a+s.f)*r)
set y=s.y+(Sin(s.a+s.f)*r)
call SetUnitX(s.dum, x)
call SetUnitY(s.dum, y)
set tmps=s
call GroupEnumUnitsInRange(ENUM_GROUP, x, y, BOOMERANG_COLLSIZE, Data.DamageFilter)
set s.a=s.a-s.p
if s.a<=0 then
call s.destroy()
endif
// do your things here, dont forget to call s.destroy() somewhen
set i=i-1
endloop
endmethod
static method create takes nothing returns Data
local thistype s=thistype.allocate()
set l=GetSpellTargetLoc()
set s.tx=GetLocationX(l)
set s.ty=GetLocationY(l)
call RemoveLocation(l)
set l=null
set s.c=GetTriggerUnit()
set s.x=GetUnitX(s.c)
set s.y=GetUnitY(s.c)
set s.dum=CreateUnit(GetOwningPlayer(s.c), DUMMY_ID, s.x, s.y, 0)
call SetUnitX(s.dum, s.x)
call SetUnitY(s.dum, s.y)
call UnitAddAbility(s.dum, 'Amrf')
call UnitRemoveAbility(s.dum, 'Amrf')
call SetUnitScale(s.dum, BOOMERANG_SIZE, BOOMERANG_SIZE, BOOMERANG_SIZE)
call SetUnitFlyHeight(s.dum, BOOMERANG_HEIGHT, 0)
set s.level=GetUnitAbilityLevel(s.c, AID)
set s.d=SquareRoot( ((s.tx-s.x)*(s.tx-s.x))+((s.ty-s.y)*(s.ty-s.y)) )
set s.a=(bj_PI/2)
set s.p=TICK*((bj_PI*BOOMERANG_SPEED)/(4*s.d))
set s.f=Atan2(s.ty-s.y, s.tx-s.x)-(bj_PI/4)
set s.g=NewGroup()
// initialize the struct here
set thistype.Structs[thistype.Count]=s
set s.i=thistype.Count
if thistype.Count==0 then
call TimerStart(thistype.T, TICK, true, function thistype.Callback)
endif
set thistype.Count=thistype.Count+1
return s
endmethod
private static method onInit takes nothing returns nothing
local trigger t=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, function Data.create)
set Data.DamageFilter=Condition(function Data.DamageFilterFunc)
call SetUpDAMAGE()
endmethod
endstruct
endlibrary