library Projectiles requires ListModule, vectors, quaternions
globals
private constant real GRAVITY = 9.82 //ADJUST TO YOUR NEED
private constant real INTERVAL = 0.03 //UPDATE RATE
private constant real BOUNCE_FACTOR = 0.85 //VELOCITY LOST AT BOUNCE
private constatn real AIR_RESISTANCE = 0.97
private constant integer DUMMY_ID = 'dumm'
private location LOC = Location(0,0)
endglobals
function GetTerrainZ takes real x, real y returns real
call MoveLocation(LOC,x,y)
return GetLocationZ(LOC)
endfunction
function GetTerrainNormal takes real x, real y returns vector
local real z
local real radius = 8.
local vector v = vector.create(0,0,0)
set z = GetTerrainZ(x-radius,y)
set v.xv = z-GetTerrainZ(x+radius,y)
set z = GetTerrainZ(x,y-radius)
set v.yv = z-GetTerrainZ(x,y+radius)
set v.zv = radius*2
call v.normalize() //NORMALIZE METHOD MAKES SURE THE LENGTH OF THE VECTOR IS 1.
return v
endfunction
struct projclass
boolean isHoming = false //THIS STRUCT CAN BE FILLED WITH ALL THE DATA YOU NEED. CREATE THE CLASSES AS GLOBALS ON STARTUP.
boolean useGravity = false
boolean doesBounce = false
real damage = 10
endstruct
struct projectile
implement List
unit u
effect e
projclass class
quaternion dir //WE DON'T USE QUATERNIONS MUCH IN THIS SAMPLE, BUT IF YOU WANT HOMING MISSILES YOU WILL NEED THEM.
vector pos
vector vel
static method create takes string path, projclass c, real x, real y, real z, real pitch, real yaw, real vel returns thistype
local thistype this = thistype.allocate()
call .listAdd() //ADD THE PROJECTILE TO THE LINKED LIST
set .u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, x, y, yaw)
//....ALSO REMEMBER TO ADD LOCUST, RAVEN FORM ETC
set .e = AddSpecialEffectTarget(path, .u, "origin")
set .class = c
set .dir = quaternion.createEuler(0, pitch*bj_DEGTORAD, yaw*bj_DEGTORAD) //REMEMBER THAT QUATERNIONS TAKE RADIANS
set .pos = vector.create(x,y,z)
set .vel = vector.create(vel, 0, 0).rotateByQuaternion(.dir) //THIS ROTATES THE VELOCITY VECTOR IN THE DIRECTION OF THE MISSILE
return this
endmethod
method onDestroy takes nothing returns nothing
call RemoveUnit(.u)
call DestroyEffect(.e)
call .pos.destroy()
call .vel.destroy()
call .dir.destroy()
call .listRemove()
endmethod
method bounce takes nothing returns nothing
local vector normal = GetTerrainNormal(.pos.xv, .pos.yv)
local vector newvel = vector.reflect(.vel, normal) //THE REFLECT METHOD IS VERY EFFECTIVE AT MAKING BOUNCE EFFECTS.
//REMEMBER THAT YOU CAN REFLECT A VECTOR AGAINST ANY NORMAL, LIKE THE NORMAL OF A WALL!!
call .vel.setTo(newvel).scale(BOUNCE_FACTOR) //WE WANT TO LOSE SOME MOMENTUM FROM THE BOUNCE
call normal.destroy()
call newvel.destroy()
//ALWAYS REMEMBER TO DESTROY ANY LOCAL VECTORS YOU USE, OR ELSE THEY WILL OVERFLOW
endmethod
method explode takes nothing returns nothing
//ADD EFFECTS ETC
call .destroy()
endmethod
static method update takes nothing returns nothing
local thistype this = .first
local real tz = 0
loop
exitwhen this == 0
set tz = GetTerrainZ(.pos.xv, .pos.yv)
set .vel.zv = .vel.zv-GRAVITY //ADD GRAVITY TO MAKE IT FALL
call .vel.scale(AIR_RESISTANCE) //YOU CAN ADD A CHECK TO THIS, IF YOU WANT YOUR PROJECTILE CLASS TO IGNORE AIR RESISTANCE
call .pos.add(.vel) //ADD VELOCITY TO POSITION
if .pos.zv < tz then
set .pos.zv = tz //MAKE SURE IT DOES NOT CLIP THROUGH THE GROUND
if .class.doesBounce then //IF IT HITS THE GROUND, BOUNCE OR EXPLODE
call .bounce()
else
call .explode()
endif
endif
//DO SOME COLLISION. THIS CAN BE APPROACHED IN MANY WAYS
//UPDATE ORIENTATION
call SetUnitLookAt(.u, "bone_head", .u, v.xv*100, v.yv*100, v.zv*100)
call SetUnitPosition(.u, .pos.xv, .pos.yv)
call SetUnitFlyHeight(.u, .pos.zv-tz, 0)
set this = .next //THIS IS HOW YOU ITERATE THROUGH A LINKED LIST
endloop
endmethod
static method onInit takes nothing returns nothing
call TimerStart(CreateTimer(), INTERVAL, true, function thistype.update)
endmethod
endstruct
endlibrary