Dirac
22710180
- Reaction score
- 147
JASS:
library ProjectileStruct /* v1.0.0
*/uses/*
*/ LinkedListModule /*
*/ optional AutoFly /*
**********************************************************************/
globals
//***********************************************************************
// This sets the distance threshold for unit recognition in the path of
// the projectile
private constant real PROJECTILE_HIT_COLLISION = 80.0
//***********************************************************************
// This dummy must use Vexorian's dummy model and it's movement type
// should be "Hover" if you want to correctly move over water, otherwise
// use "None"
private constant integer DUMMY_ID = 'e000'
endglobals
/**********************************************************************
*
* struct Projectile
* - Has a LinkedList incorporated of all active projectiles.
*
* static method create takes real ox, real oy, real oz, real tx, real ty, real tz returns thistype
* - Projectiles have the following values to be set by you.
* - real speed
* - real height / arc
* - real collides
* - real turn
* - unit target
* - unit source
*
* real currentX
* real currentY
* real currentZ
*
* real targetX
* real targetY
* real targetZ
*
* real distanceFromTarget
*
* method deflect takes real x, real y returns nothing
* - Deflects the projectile from the target point, changing
* - it's course, doesn't work when the projectile has a
* - target.
*
* method bounce takes real newHeight, real howFar returns nothing
* - Bounces the projectile from it's current Z position
*
***********************************************************************
*
* module ProjectileStruct
*
* static method launch takes Projectile toLaunch returns nothing
*
* static method onImpact takes Projectile this, unit justHit returns boolean
* - Must be defined in structs that have this module, it
* - will run everytime the projectile hits a target, if it
* - returns true, the projectile ends it's course.
*
**********************************************************************/
globals
private location GetZ = Location(0,0)
endglobals
struct Projectile extends array
implement LinkedList
static method operator first takes nothing returns thistype
return base.next
endmethod
real currentX
real currentY
real currentZ
real targetX
real targetY
real targetZ
real distanceFromTarget
real oX
real oY
real oZ
real oD
real angle
real fZ
effect fx
string fP
unit dummy
unit target
unit source
boolean collides
real speed
real height
real turn
real curve
method operator model= takes string path returns nothing
call DestroyEffect(fx)
set fP=path
set fx=AddSpecialEffectTarget(path,dummy,"origin")
endmethod
method operator model takes nothing returns string
return fP
endmethod
method operator arc= takes real value returns nothing
set height=Tan(value)*oD
endmethod
static method createEx takes unit whichUnit, real ox, real oy, real oz, real tx, real ty, real tz returns thistype
local thistype this=allocate()
set source=null
set target=null
set speed=0
set height=0
set turn=0
set collides=false
set fP=""
set angle=Atan2(ty-oy,tx-ox)
call MoveLocation(GetZ,ox,oy)
set oX=ox
set oY=oy
set oZ=oz+GetLocationZ(GetZ)
set oD=SquareRoot((tx-ox)*(tx-ox)+(ty-oy)*(ty-oy))
set currentX=oX
set currentY=oY
set currentZ=oZ
set distanceFromTarget=oD
call MoveLocation(GetZ,tx,ty)
set targetX=tx
set targetY=ty
set targetZ=tz+GetLocationZ(GetZ)
set fZ=(targetZ-oZ)/oD
set dummy=whichUnit
static if not(LIBRARY_AutoFly) then
call UnitAddAbility(dummy,'Amrf')
call UnitRemoveAbility(dummy,'Amrf')
endif
call SetUnitFlyHeight(dummy,oz,0)
set fx=AddSpecialEffectTarget(model,dummy,"origin")
return this
endmethod
static method create takes real ox, real oy, real oz, real tx, real ty, real tz returns thistype
return createEx(CreateUnit(Player(15),DUMMY_ID,ox,oy,Atan2(ty-oy,tx-ox)*bj_RADTODEG),ox,oy,oz,tx,ty,tz)
endmethod
method deflect takes real x, real y returns nothing
set angle=2*Atan2(y-currentY,x-currentY)+bj_PI-angle
set oX=currentX
set oY=currentY
set oZ=currentZ
set targetX=currentX+distanceFromTarget*Cos(angle)
set targetY=currentY+distanceFromTarget*Sin(angle)
call SetUnitFacing(dummy,angle*bj_RADTODEG)
endmethod
method bounce takes real newHeight, real howFar returns nothing
set oX=currentX
set oY=currentY
set oZ=currentZ
set targetX=currentX+howFar*Cos(angle)
set targetY=currentY+howFar*Sin(angle)
set oD=SquareRoot((targetX-oX)*(targetX-oX)+(targetY-oY)*(targetY-oY))
set distanceFromTarget=oD
set fZ=(targetZ-oZ)/oD
set height=newHeight
endmethod
endstruct
globals
private integer COUNT = 0
private integer ACTIVE = 0
private timer TIMER = CreateTimer()
private trigger FIRE = CreateTrigger()
private boolexpr array METHOD
private integer array INSTANCES
private triggercondition array COND
private Projectile array NODE
endglobals
private function Execute takes nothing returns nothing
call TriggerEvaluate(FIRE)
endfunction
private function StartPeriodic takes integer index, Projectile this returns nothing
if INSTANCES[index]==0 then
call Projectile.base.insertNode(this)
set NODE[index]=this
set COND[index]=TriggerAddCondition(FIRE,METHOD[index])
else
call NODE[index].insertNode(this)
endif
if ACTIVE==0 then
call TimerStart(TIMER,0.03125,true,function Execute)
endif
set ACTIVE=ACTIVE+1
set INSTANCES[index]=INSTANCES[index]+1
endfunction
private function StopPeriodic takes integer index, Projectile this returns nothing
set INSTANCES[index]=INSTANCES[index]-1
if NODE[index]==this then
set NODE[index]=NODE[index].next
endif
call this.removeNode()
set ACTIVE=ACTIVE-1
if INSTANCES[index]==0 then
call TriggerRemoveCondition(FIRE,COND[index])
endif
if ACTIVE==0 then
call PauseTimer(TIMER)
endif
endfunction
module ProjectileStruct
private static integer INDEX
private static method terminate takes Projectile this, unit u returns nothing
if thistype.onImpact(this,u) then
call DestroyEffect(this.fx)
call RemoveUnit(this.dummy)
call StopPeriodic(INDEX,this)
endif
endmethod
private static method onPeriod takes nothing returns boolean
local unit u
local real a
local real x
local real y
local Projectile this=NODE[INDEX]
local integer i=INSTANCES[INDEX]
loop
exitwhen i==0
if this.target!=null and GetUnitTypeId(this.target)!=0 and GetWidgetLife(this.target)>=0.405 then
call MoveLocation(GetZ,this.targetX,this.targetY)
set this.targetX=GetUnitX(this.target)
set this.targetY=GetUnitY(this.target)
set this.targetZ=GetUnitFlyHeight(this.target)+GetLocationZ(GetZ)
set a=Atan2(this.targetY-this.currentY,this.targetX-this.currentX)
if this.turn!=0 then
if Sin(a-this.angle)>=0 then
set this.angle=this.angle+this.turn
else
set this.angle=this.angle-this.turn
endif
else
set this.angle=a
endif
set this.fZ=(this.targetZ-this.oZ)/this.oD
call SetUnitFacing(this.dummy,this.angle*bj_RADTODEG)
if this.distanceFromTarget<=PROJECTILE_HIT_COLLISION then
call terminate(this,this.target)
endif
else
if this.distanceFromTarget<=PROJECTILE_HIT_COLLISION then
call terminate(this,null)
endif
endif
set this.currentX=this.currentX+this.speed*Cos(this.angle)
set this.currentY=this.currentY+this.speed*Sin(this.angle)
if this.curve!=0 then
set a=4*this.curve*this.distanceFromTarget*(this.oD-this.distanceFromTarget)/(this.oD*this.oD)
set x=this.currentX+a*Cos(this.angle+bj_PI/2)
set y=this.currentY+a*Sin(this.angle+bj_PI/2)
call SetUnitFacing(this.dummy,(this.angle+Atan((8*this.curve*this.distanceFromTarget-4*this.oD*this.curve)/(this.oD*this.oD)))*bj_RADTODEG)
else
set x=this.currentX
set y=this.currentY
endif
set this.oD=SquareRoot((this.targetX-this.oX)*(this.targetX-this.oX)+(this.targetY-this.oY)*(this.targetY-this.oY))
set this.distanceFromTarget=SquareRoot((this.currentX-this.targetX)*(this.currentX-this.targetX)+(this.currentY-this.targetY)*(this.currentY-this.targetY))
if this.height!=0 or this.fZ!=0 then
set this.currentZ=4*this.height*this.distanceFromTarget*(this.oD-this.distanceFromTarget)/(this.oD*this.oD)+this.oZ+this.fZ*(this.oD-this.distanceFromTarget)
call MoveLocation(GetZ,x,y)
call SetUnitFlyHeight(this.dummy,this.currentZ-GetLocationZ(GetZ),0)
call SetUnitAnimationByIndex(this.dummy,R2I((Atan((8*this.height*this.distanceFromTarget-4*this.oD*this.height)/(this.oD*this.oD))+Atan(this.fZ))*bj_RADTODEG)+90)
endif
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
if this.collides then
call GroupEnumUnitsInRange(bj_lastCreatedGroup,x,y,PROJECTILE_HIT_COLLISION,null)
loop
set u=FirstOfGroup(bj_lastCreatedGroup)
exitwhen u==null
call terminate(this,u)
call GroupRemoveUnit(bj_lastCreatedGroup,u)
endloop
endif
set this=this.next
set i=i-1
endloop
set u=null
return false
endmethod
static method launch takes Projectile this returns nothing
call StartPeriodic(INDEX,this)
endmethod
private static method onInit takes nothing returns nothing
set INDEX=COUNT+1
set COUNT=INDEX
set METHOD[INDEX]=Filter(function thistype.onPeriod)
endmethod
endmodule
endlibrary
JASS:
struct TESTER extends array
static unit u
static method onImpact takes Projectile this, unit u returns boolean
if u==null then
//Makes it bounce until its height is below 20
if this.height>20 then
call this.bounce(this.height*0.6,this.oD*0.6)
set this.speed=this.speed*0.6
else
return true
endif
elseif u!=this.source then
//If it encounters a unit it bounces off it, it works better if you add the unit
//to a group and prevent it from bouncing from the same unit twice
call this.deflect(GetUnitX(u),GetUnitY(u))
endif
return false
endmethod
implement ProjectileStruct
static method onPeriod takes nothing returns nothing
local real a=GetRandomReal(0,bj_PI*2)
local Projectile new = Projectile.create(GetUnitX(u),GetUnitY(u),65,GetUnitX(u)+1000*Cos(a),GetUnitY(u)+1000*Sin(a),0)
set new.speed=20
set new.model="Abilities\\Weapons\\SentinelMissile\\SentinelMissile.mdl"
set new.height=200
set new.source=u
set new.collides=true
//set new.curve=500
call thistype.launch(new)
endmethod
static method onInit takes nothing returns nothing
set u=CreateUnit(Player(0),'hfoo',0,0,270)
call TimerStart(CreateTimer(),0.5,true,function thistype.onPeriod)
endmethod
endstruct