- Joined
- Jul 10, 2007
- Messages
- 6,306
edit
Do not use this ; O. I am planning on a major revision for v2 as I think that this is designed wrong. It'll get rid of things like homing.
Demonstration
Do not use this ; O. I am planning on a major revision for v2 as I think that this is designed wrong. It'll get rid of things like homing.
JASS:
library Projectile2D /* v1.0.3.3
*************************************************************************************
*
* Simple 2D projectiles that are relatively flexible and very efficient.
*
*************************************************************************************
*
* */uses/*
*
* */ Position /* hiveworkshop.com/forums/jass-resources-412/snippet-position-184578/
* */ PositionCollision /*
* */ CTL /* hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/
* */ Particle /* hiveworkshop.com/forums/submissions-414/snippet-needs-work-particle-206279/
* */ UnitInRangeEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-unitinrangeevent-205036/
*
*************************************************************************************
*
* struct Projectile2D extends array
*
* Fields
* -------------
*
* readonly static Projectile2D eventProjectile
*
* readonly Particle particle
*
* Position target
* real angle
* real turnRate
* real speed
* real acceleration
* real accelerationAngle
*
* Methods
* -------------
*
* static method start takes Particle particle, Position target, real turnRate, real speed, real angle, real lifespan, code onCollide, boolexpr onDestroy, boolexpr onResolve returns nothing
*
* - onCollide: Runs when projectile collides with any non locust unit
* Does not do onCollide detection if this is null
* Does not destroy the projectile
*
* - onDestroy: Runs when projectile is destroyed
* Must Return True
*
* - onResolve: Runs when projectile runs into the target
* Does not do target collision detection if this is null
* Does not destroy the projectile, you must destroy it yourself if you want it destroyed
* Must Return True
*
* method destroy takes nothing returns nothing
*
*************************************************************************************
*
* module Projectile2D
*
* Fields
* -------------
*
* readonly static integer count
* static method operator [] takes integer index returns thistype
*
* Methods
* -------------
*
* static method start takes Particle particle, Position target, real turnRate, real speed, real angle, real lifespan returns nothing
* method destroy takes nothing returns nothing
*
* Interface
* -------------
*
* private method onCollided takes unit whichUnit returns nothing (optional)
* private method onResolved takes nothing returns nothing (optional)
* private method onDestroyed takes nothing returns nothing (optional)
* private method onMove takes nothing returns nothing (optional)
*
*************************************************************************************/
private struct Projectile2D_p extends array
method operator particle takes nothing returns Particle
return this
endmethod
static thistype eventProjectile = 0
private static force evalForce = CreateForce()
static Table table
timer lifetimer
Position target
real angle
real turnRate
real speed
real dx
real dy
real ox
real oy
real ax
real ay
real px
real py
integer rangeEvent
real tcollision
real pcollision
real cturnRate
real targetAngle
real acceleration
real accelerationAngle
boolexpr onDestroy
boolexpr onResolve
static integer count = 0
static integer array values
static integer array indexes
method destroy takes nothing returns nothing
local integer index
local integer last
if (null != onDestroy) then
set index = eventProjectile
set eventProjectile = this
call ForceEnumPlayersCounted(evalForce, onDestroy, 1)
set eventProjectile = index
endif
call target.unlock()
if (0 != rangeEvent) then
call UnregisterUnitInRangeEvent(rangeEvent)
set rangeEvent = 0
endif
call PauseTimer(lifetimer)
set count = count - 1
if (0 == count) then
call stop()
else
set index = indexes[particle]
set last = values[count]
set values[index] = last
set indexes[last] = index
endif
call Particle(this).destroy()
endmethod
implement CT32
local thistype this
local real x
local real y
local real tx
local real ty
local real xd
local real yd
local real direction
local real angle
local real distance
local boolean hit
local integer current = count
local real collision
loop
exitwhen 0 == current
set current = current - 1
set this = values[current]
/*
* Retrieve coordinates
*/
set x = particle.x
set y = particle.y
set tx = target.x
set ty = target.y
/*
* If can resolve, calculate for possible collision with target
*/
if (null != onResolve) then
set xd = tx - x
set yd = ty - y
set distance = xd*xd + yd*yd
set collision = pcollision + tcollision
set hit = distance < collision*collision
if (hit) then
set eventProjectile = this
call ForceEnumPlayersCounted(evalForce, onResolve, 1)
set eventProjectile = 0
endif
else
set hit = false
endif
/*
* If didn't resolve, translate and possibly home
*/
if (not hit) then
/*
* If homing and the target moved, home in
*/
if (0 != acceleration) then
set dx = dx + ax
set dy = dy + ay
set speed = SquareRoot(dx*dx + dy*dy)
set this.angle = Atan2(dy, dx)
endif
if (0 != turnRate) then
set angle = this.angle
if (tx != ox or ty != oy or px != x or py != y) then
set targetAngle = Atan2(ty - y, tx - x)
set ox = tx
set oy = ty
elseif (0 != acceleration) then
set targetAngle = Atan2(ty - y, tx - x)
endif
if (angle != targetAngle) then
set direction = targetAngle
if (Cos(angle - direction) < cturnRate) then
if (0 < Sin(direction - angle)) then
set angle = angle + turnRate
else
set angle = angle - turnRate
endif
set particle.xyAngle = angle
set this.angle = angle
set dx = speed*Cos(angle)
set dy = speed*Sin(angle)
else
set particle.xyAngle = direction
set this.angle = direction
set dx = speed*Cos(direction)
set dy = speed*Sin(direction)
endif
endif
set x = x + dx
set y = y + dy
set px = x
set py = y
else
set x = x + dx
set y = y + dy
endif
if (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
call destroy()
else
set particle.x = x
set particle.y = y
endif
endif
endloop
implement CT32End
static method expire takes nothing returns nothing
call thistype(table[GetHandleId(GetExpiredTimer())]).destroy()
endmethod
private static method onInit takes nothing returns nothing
set table = Table.create()
endmethod
endstruct
struct Projectile2D extends array
static method operator eventProjectile takes nothing returns Projectile2D
return Projectile2D_p.eventProjectile
endmethod
method operator particle takes nothing returns Particle
return this
endmethod
method operator target takes nothing returns Position
return Projectile2D_p(this).target
endmethod
method operator target= takes Position target returns nothing
call Projectile2D_p(this).target.unlock()
set Projectile2D_p(this).target = target
call target.lock()
endmethod
method operator angle takes nothing returns real
return Projectile2D_p(this).angle
endmethod
method operator angle= takes real val returns nothing
set Projectile2D_p(this).angle = val
set Projectile2D_p(this).dx = Projectile2D_p(this).speed*Cos(val)
set Projectile2D_p(this).dy = Projectile2D_p(this).speed*Sin(val)
endmethod
method operator turnRate takes nothing returns real
return Projectile2D_p(this).turnRate*32
endmethod
method operator turnRate= takes real turnRate returns nothing
set Projectile2D_p(this).turnRate = turnRate*.031250000
set Projectile2D_p(this).cturnRate = Cos(Projectile2D_p(this).turnRate)
endmethod
method operator speed takes nothing returns real
return Projectile2D_p(this).speed*32
endmethod
method operator speed= takes real value returns nothing
set Projectile2D_p(this).speed = value*.031250000
set Projectile2D_p(this).dx = Projectile2D_p(this).speed*Cos(Projectile2D_p(this).angle)
set Projectile2D_p(this).dy = Projectile2D_p(this).speed*Sin(Projectile2D_p(this).angle)
endmethod
method operator acceleration takes nothing returns real
return Projectile2D_p(this).acceleration*32
endmethod
method operator acceleration= takes real value returns nothing
set Projectile2D_p(this).acceleration = value*.031250000
set Projectile2D_p(this).ax = Projectile2D_p(this).acceleration*Cos(Projectile2D_p(this).accelerationAngle)
set Projectile2D_p(this).ay = Projectile2D_p(this).acceleration*Sin(Projectile2D_p(this).accelerationAngle)
endmethod
method operator accelerationAngle takes nothing returns real
return Projectile2D_p(this).accelerationAngle
endmethod
method operator accelerationAngle= takes real value returns nothing
set Projectile2D_p(this).accelerationAngle = value
set Projectile2D_p(this).ax = Projectile2D_p(this).acceleration*Cos(Projectile2D_p(this).accelerationAngle)
set Projectile2D_p(this).ay = Projectile2D_p(this).acceleration*Sin(Projectile2D_p(this).accelerationAngle)
endmethod
static method start takes Particle particle, Position target, real turnRate, real speed, real angle, real lifespan, code onCollide, boolexpr onDestroy, boolexpr onResolve returns nothing
if (0 == Projectile2D_p.count) then
call Projectile2D_p.start()
endif
set Projectile2D_p.values[Projectile2D_p.count] = particle
set Projectile2D_p.indexes[particle] = Projectile2D_p.count
set Projectile2D_p.count = Projectile2D_p.count + 1
call target.lock()
set Projectile2D_p(particle).target = target
set Projectile2D_p(particle).turnRate = turnRate*.031250000
set Projectile2D_p(particle).cturnRate = Cos(Projectile2D_p(particle).turnRate)
set Projectile2D_p(particle).speed = speed*.031250000
set Projectile2D_p(particle).angle = angle
set Projectile2D_p(particle).dx = Projectile2D_p(particle).speed*Cos(angle)
set Projectile2D_p(particle).dy = Projectile2D_p(particle).speed*Sin(angle)
set Projectile2D_p(particle).ox = target.x
set Projectile2D_p(particle).oy = target.y
set Projectile2D_p(particle).px = particle.x
set Projectile2D_p(particle).py = particle.y
set Projectile2D_p(particle).onResolve = onResolve
set Projectile2D_p(particle).pcollision = particle.collision
set Projectile2D_p(particle).tcollision = target.collision
set Projectile2D_p(particle).targetAngle = angle
set Projectile2D_p(particle).ax = 0
set Projectile2D_p(particle).ay = 0
set Projectile2D_p(particle).acceleration = 0
set Projectile2D_p(particle).accelerationAngle = 0
set particle.xyAngle = angle
if (null != onCollide) then
set Projectile2D_p(particle).rangeEvent = RegisterUnitInRangeEvent(onCollide, particle.unit, particle.collision + 16)
endif
set Projectile2D_p(particle).onDestroy = onDestroy
if (0 < lifespan) then
if (null == Projectile2D_p(particle).lifetimer) then
set Projectile2D_p(particle).lifetimer = CreateTimer()
set Projectile2D_p.table[GetHandleId(Projectile2D_p(particle).lifetimer)] = particle
endif
call TimerStart(Projectile2D_p(particle).lifetimer, lifespan, false, function Projectile2D_p.expire)
endif
endmethod
method destroy takes nothing returns nothing
call Projectile2D_p(this).destroy()
endmethod
endstruct
module Projectile2D
static if thistype.onMove.exists then
private static TimerGroup32 timerGroup
endif
private static integer array values
private static integer array indexes
readonly static integer count = 0
private static code onCollided_c = null
private static boolexpr onResolved_c = null
private static boolexpr onDestroyed_c
static method operator [] takes integer index returns thistype
return values[index]
endmethod
static if thistype.onCollided.exists then
private static method onProjectileCollide_p takes nothing returns nothing
call thistype(GetEventSourceUnitId()).onCollided(GetTriggerUnit())
endmethod
endif
static if thistype.onResolved.exists then
private static method onProjectileResolve_p takes nothing returns boolean
call thistype(Projectile2D.eventProjectile).onResolved()
return true
endmethod
endif
private static method onProjectileDestroyed_p takes nothing returns boolean
local thistype projectile = Projectile2D.eventProjectile
local integer index
local integer last
set count = count - 1
set index = indexes[projectile]
set last = values[count]
set values[index] = last
set indexes[last] = index
static if thistype.onMove.exists then
if (0 == count) then
call timerGroup.stop()
endif
endif
static if thistype.onDestroyed.exists then
call projectile.onDestroyed()
endif
return true
endmethod
static if thistype.onMove.exists then
private static method periodic_p takes nothing returns nothing
local integer current = count
loop
exitwhen 0 == current
set current = current - 1
call thistype(values[current]).onMove()
endloop
endmethod
endif
static method start takes Particle particle, Position target, real turnRate, real speed, real angle, real lifespan returns nothing
call Projectile2D.start(particle, target, turnRate, speed, angle, lifespan, onCollided_c, onDestroyed_c, onResolved_c)
static if thistype.onMove.exists then
if (0 == count) then
call timerGroup.start()
endif
endif
set values[count] = particle
set indexes[particle] = count
set count = count + 1
endmethod
method destroy takes nothing returns nothing
call Projectile2D(this).destroy()
endmethod
private static method onInit takes nothing returns nothing
static if thistype.onMove.exists then
set timerGroup = TimerGroup32.create(function thistype.periodic_p)
endif
set onDestroyed_c = Condition(function thistype.onProjectileDestroyed_p)
static if thistype.onCollided.exists then
set onCollided_c = function thistype.onProjectileCollide_p
endif
static if thistype.onResolved.exists then
set onResolved_c = Condition(function thistype.onProjectileResolve_p)
endif
endmethod
endmodule
endlibrary
Demonstration
JASS:
struct Tester extends array
private static unit u
private static unit u2
private static Projectile2D projectile
private static method onResolve takes nothing returns boolean
if (projectile.target.unit == u) then
set projectile.target = Position[u2]
else
set projectile.target = Position[u]
endif
return true
endmethod
private static method onCollide takes nothing returns nothing
call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,10,"Collided with "+GetUnitName(GetTriggerUnit()))
endmethod
private static method onInit takes nothing returns nothing
set u = CreateUnit(Player(0), 'hfoo', GetStartLocationX(0) - 512, GetStartLocationY(0), 270)
set u2 = CreateUnit(Player(0), 'hfoo', GetStartLocationX(0) + 512, GetStartLocationY(0), 270)
set projectile = Particle.create(Player(0), GetUnitX(u), GetUnitY(u), 0, "units\\nightelf\\Wisp\\Wisp.mdl", 32)
call Projectile2D.start(projectile, Position[u2], bj_PI, 128, 0, 0, function thistype.onCollide, null, Condition(function thistype.onResolve))
endmethod
endstruct
Last edited: