- Joined
- Mar 27, 2012
- Messages
- 3,232
I'm coding a spell with a tracking component, but I don't know how to do it properly.
The intuitive solution would be to make it apply speed toward the target, but this causes a problem that I call "orbiting" - Some projectiles will circle around their targets forever.
This is the current code and map with my tries to fix the issue commented out, so you could see how it happens.
Note that I do not want the tracking to mean instant changing of direction, but rather a gradual tracking.
The spell and most of its mechanics is inspired by Jan from Little Fighter 2.
Requirements
The intuitive solution would be to make it apply speed toward the target, but this causes a problem that I call "orbiting" - Some projectiles will circle around their targets forever.
This is the current code and map with my tries to fix the issue commented out, so you could see how it happens.
Note that I do not want the tracking to mean instant changing of direction, but rather a gradual tracking.
The spell and most of its mechanics is inspired by Jan from Little Fighter 2.
JASS:
native UnitAlive takes unit u returns boolean
//! import "ExtraFunctions.j"
//! import "Effect.j"
library HealSpirit initializer in requires ExtraFunctions,Effect
globals
private trigger CastTrig = CreateTrigger()
private trigger LoopTrig = CreateTrigger()
private unit array Unit
private unit array Target
private real array Vx
private real array Vy
private real array Heal
private real array Speed
private real array Torque
private real array Angle
private integer Units = 0
private constant integer ABIL = 'A000'
private constant integer DUMMY = 'h001'
private constant string SFX = "Abilities\\Spells\\Human\\Heal\\HealTarget.mdl"
private constant real RANGE = 1000
private constant real TIMEOUT = 0.0312500
private player p
endglobals
private function filter takes nothing returns boolean
if IsUnitEnemy(GetFilterUnit(),p) then
return false
endif
if not(UnitAlive(GetFilterUnit())) then
return false
endif
return true
endfunction
private function Loop takes nothing returns nothing
local integer i = 1
local real x1
local real y1
local real x2
local real y2
local real distance
local real currentangle
local real r
local real r2
loop
exitwhen i > Units
set x1 = GetUnitX(Unit[i])
set y1 = GetUnitY(Unit[i])
set x2 = GetUnitX(Target[i])
set y2 = GetUnitY(Target[i])
set distance = RealDistance(x1,y1,x2,y2)
if distance < 100 then
call SetUnitX(Unit[i],x2)
call SetUnitY(Unit[i],y2)
call ApplyTimedEffectTarget(SFX,Target[i],"chest",1)
call KillUnit(Unit[i])
set Unit[i] = Unit[Units]
set Target[i] = Target[Units]
set Vx[i] = Vx[Units]
set Vy[i] = Vy[Units]
set Speed[i] = Speed[Units]
set Torque[i] = Torque[Units]
set Angle[i] = Angle[Units]
set Units = Units - 1
else
set x1 = x1+Vx[i]
set y1 = y1+Vy[i]
call SetUnitX(Unit[i],x1)
call SetUnitY(Unit[i],y1)
set Angle[i] = AngleBetweenPointsEx(x1,y1,x2,y2)
call SetUnitFacing(Unit[i],Angle[i])
//set currentangle = Asin(Vy[i]/Vx[i])*bj_RADTODEG
//set r = currentangle - Angle[i]
//call Debug(R2S(r))
set Vx[i] = Vx[i] + Torque[i] * Cos((Angle[i]) * bj_DEGTORAD)
set Vy[i] = Vy[i] + Torque[i] * Sin((Angle[i]) * bj_DEGTORAD)/*
set Vx[i] = Vx[i] + Torque[i] * Cos((Angle[i]+r) * bj_DEGTORAD)
set Vy[i] = Vy[i] + Torque[i] * Sin((Angle[i]+r) * bj_DEGTORAD)*/
set Speed[i] = SquareRoot(Vx[i]*Vx[i]+Vy[i]*Vy[i])
set i = i + 1
endif
endloop
if Units == 0 then
call DisableTrigger(LoopTrig)
endif
endfunction
private function Create takes real x,real y,unit target,real speed,real healamount, boolean randomstart returns nothing
local real r
if randomstart then
set r = GetRandomReal(0,360)
else
set r = AngleBetweenPointsEx(x,y,GetUnitX(target),GetUnitY(target))
endif
set Units = Units + 1
set Unit[Units] = CreateUnit(GetOwningPlayer(target),DUMMY,x,y,r)
set Target[Units] = target
set Vx[Units] = speed * Cos(r * bj_DEGTORAD)
set Vy[Units] = speed * Sin(r * bj_DEGTORAD)
set Heal[Units] = healamount
set Speed[Units] = speed
set Torque[Units] = speed*TIMEOUT
set Angle[Units] = r
call EnableTrigger(LoopTrig)
endfunction
private function Cast takes nothing returns nothing
local unit caster
local unit u
local group g
local real x
local real y
local real speed
if GetSpellAbilityId() == ABIL then
set caster = GetTriggerUnit()
set x = GetUnitX(caster)
set y = GetUnitY(caster)
set g = CreateGroup()
set p = GetOwningPlayer(caster)
call GroupEnumUnitsInRange(g,x,y,RANGE,Filter(function filter))
loop
set u = FirstOfGroup(g)
exitwhen u == null
call Create(x,y,u,10,200,true)
call Create(x,y,u,15,100,true)
call Create(x,y,u,20,50,true)
call GroupRemoveUnit(g,u)
endloop
call DestroyGroup(g)
set g = null
endif
endfunction
private function in takes nothing returns nothing
local integer i = 0
loop
exitwhen i > 15
call TriggerRegisterPlayerUnitEvent(CastTrig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set i = i + 1
endloop
call TriggerAddAction(CastTrig,function Cast)
call TriggerRegisterTimerEvent(LoopTrig,TIMEOUT,true)
call TriggerAddAction(LoopTrig,function Loop)
endfunction
endlibrary