ok i killed that damn bug flat and beefed up my getturretangs() function. now it returns the angular values in a slightly more sane manor. ready to be plugged directly into the base's/gun's heading/pitch without further modification. i dont really want a direct plugging though, i want to apply limiters and also animate the motion at a user definable rate. the limiters on the pitch are fairly easy to do, as is the rate, but im tired so il deal with it later.
#Global Hooks
$GameInit:
[
--[[=============================]]--
--[[======= function defs =======]]--
--[[=============================]]--
-------------------------------
----------screen vars----------
-------------------------------
setscreenvars = function() --scalers and constants for multi res support
local w = gr.getScreenWidth()
local h = gr.getScreenHeight()
local cx = w/2
local cy = h/2
local wf = 0
local hf = 0
local awh = 0
local hires = false
if w >= 1024 then
wf = w / 1024
hf = h / 768
awh = (wf + hf) / 2
hires = true
else
wf = w / 640
hf = h / 480
awh = ((wf + hf) / 2) * 0.625
hires = false
end
return w,h,cx,cy,wf,hf,awh,hires
end
----------------------------
----------controls----------
----------------------------
mousejoy = function(deadzone) --mousejoy function, returns a value from -1 to 1 depending on the mouse's position
local X = io.getMouseX()
local Y = io.getMouseY()
if hires then
X = (X / 511.5) - 1
Y = (Y / 383.5) - 1
else
X = (X / 319.5) - 1
Y = (Y / 239.5) - 1
end
if deadzone == 1 then --now supports full on and full off mode
return 0,0,X,Y
elseif deadzone == 0 then
return X,Y,X,Y
else
local Mx = X --theese are for the menu, -1 to 1 without deadzone applied
local My = Y
local tweak = 1 / (1 - deadzone)
if X < deadzone and X > -deadzone then
X=0
else
if X > 0 then
X = (X - deadzone) * tweak
elseif X < 0 then
X = (X + deadzone) * tweak
end
end
if Y < deadzone and Y > -deadzone then
Y=0
else
if Y > 0 then
Y = (Y - deadzone) * tweak
elseif Y < 0 then
Y = (Y + deadzone) * tweak
end
end
return X, Y, Mx, My
end
end
mjtonormal = function(X,Y) --converts screen coords to normal
N = ba.createVector(X * 0.741 ,-Y * 0.741 * (h/w) ,1)
N = normalize(N) --make its length 1, otherwise its not a normal
return N
end
-------------------------
----------Maths----------
-------------------------
round = function(number) --rounds to nearest whole number, for some stupid reason, lua doesnt have one
local W,F = math.modf(number)
if F < 0 then
if F <= -0.5 then W = W - 1 end
else
if F >= 0.5 then W = W + 1 end
end
return W
end
normalize = function(norm) --takes a vector and scales it to a length of 1
local len = math.sqrt( (norm.x^2) + (norm.y^2) + (norm.z^2) )
norm = norm / len
return norm
end
multmat = function(mat1,mat2) --multiply a couple 3x3 matricies
local mat3 = {}
mat3[1] = (mat1[1] * mat2[1]) + (mat1[2] * mat2[4]) + (mat1[3] * mat2[7])
mat3[2] = (mat1[1] * mat2[2]) + (mat1[2] * mat2[5]) + (mat1[3] * mat2[8])
mat3[3] = (mat1[1] * mat2[3]) + (mat1[2] * mat2[6]) + (mat1[3] * mat2[9])
mat3[4] = (mat1[4] * mat2[1]) + (mat1[5] * mat2[4]) + (mat1[6] * mat2[7])
mat3[5] = (mat1[4] * mat2[2]) + (mat1[5] * mat2[5]) + (mat1[6] * mat2[8])
mat3[6] = (mat1[4] * mat2[3]) + (mat1[5] * mat2[6]) + (mat1[6] * mat2[9])
mat3[7] = (mat1[7] * mat2[1]) + (mat1[8] * mat2[4]) + (mat1[9] * mat2[7])
mat3[8] = (mat1[7] * mat2[2]) + (mat1[8] * mat2[5]) + (mat1[9] * mat2[8])
mat3[9] = (mat1[7] * mat2[3]) + (mat1[8] * mat2[6]) + (mat1[9] * mat2[9])
return mat3
end
appmat = function(mat,vec) --apply a matrix
local x = (mat[1] * vec[1]) + (mat[2] * vec[2]) + (mat[3] * vec[3])
local y = (mat[4] * vec[1]) + (mat[5] * vec[2]) + (mat[6] * vec[3])
local z = (mat[7] * vec[1]) + (mat[8] * vec[2]) + (mat[9] * vec[3])
vec[1],vec[2],vec[3] = x,y,z
return vec
end
prirotpoint = function(vec,angs) --rotates a point around principle axes
local matx = {1,0,0,0,1,0,0,0,1}
local maty = {1,0,0,0,1,0,0,0,1} --create 3x3 matrix arrays for xyz and fill it in with an identity matrix
local matz = {1,0,0,0,1,0,0,0,1}
matx[5] = math.cos(angs[1])
matx[6] = math.sin(angs[1])
matx[8] = -math.sin(angs[1])
matx[9] = math.cos(angs[1])
maty[1] = math.cos(angs[2])
maty[3] = -math.sin(angs[2]) --fill in the nessicary parts of the matrix for each axis
maty[7] = math.sin(angs[2])
maty[9] = math.cos(angs[2])
matz[1] = math.cos(angs[3])
matz[2] = math.sin(angs[3])
matz[4] = -math.sin(angs[3])
matz[5] = math.cos(angs[3])
local mat = multmat(matx,maty) --multiply our matrixies
mat = multmat(mat,matz) --to get total matrix
vec = appmat(mat,vec) --apply the matrix
return vec
end
rotpoint = function(vec,ang,axis) --rotate around arbitrary axis, axis is a normal defining axis of rotation, ang is amount
local mat = {}
local c = math.cos(ang)
local s = math.sin(ang)
local t = 1 - c
mat[1] = t * axis[1]^2 + c
mat[2] = t * axis[1] * axis[2] + s * axis[3]
mat[3] = t * axis[1] * axis[3] - s * axis[2]
mat[4] = t * axis[1] * axis[2] - s * axis[3]
mat[5] = t * axis[2]^2 + c
mat[6] = t * axis[2] * axis[3] + s * axis[1]
mat[7] = t * axis[1] * axis[3] + s * axis[2]
mat[8] = t * axis[2] * axis[3] - s * axis[1]
mat[9] = t * axis[3]^2 + c
vec = appmat(mat,vec) --apply the matrix
return vec
end
---------------------------------------
----------procedural graphics----------
---------------------------------------
blindfirereticle = function(x,y)
x, y = x*wf, y*hf
gr.setColor(192,192,64,200)
gr.drawGradientLine(x + (8*awh), y, x + (2*awh), y )
gr.drawGradientLine(x - (8*awh), y, x - (2*awh), y )
gr.drawGradientLine(x, y + (8*awh), x, y + (2*awh) )
gr.drawGradientLine(x, y - (8*awh), x, y - (2*awh) )
end
mousereticle = function(x,y)
x, y = x*wf, y*hf
gr.setColor(32,32,50,50)
gr.drawCircle(12*awh,x,y)
gr.setColor(64,64,96,200)
for i=0, 359, 1 do
rot = i * (math.pi/180)
gr.drawPixel(x + (math.sin(rot)*12*awh), y + (math.cos(rot)*12*awh) )
end
gr.setColor(96,96,144,255)
gr.drawPixel(x,y)
end
prograderet = function(x,y,vel)
x, y = x*wf, y*hf
gr.setColor(72,192,72,222)
gr.drawGradientLine(x + (12*awh), y + (12*awh), x + (6*awh), y + (6*awh) )
gr.drawGradientLine(x - (12*awh), y - (12*awh), x - (6*awh), y - (6*awh) )
gr.drawGradientLine(x - (12*awh), y + (12*awh), x - (6*awh), y + (6*awh) )
gr.drawGradientLine(x + (12*awh), y - (12*awh), x + (6*awh), y - (6*awh) )
vel = tostring(vel)
gr.setColor(128,192,128,222)
gr.drawString(vel, x - (gr.getStringWidth(vel) / 2), y - (2*awh) )
end
----------------------------------
----------test functions----------
----------------------------------
printmatrix = function(ori,v) --function to help understand the mystyries of the orientation matrix
gr.setColor(200,200,200,200)
gr.drawString(ori[1],150,100+v)
gr.drawString(ori[2],300,100+v)
gr.drawString(ori[3],450,100+v)
gr.drawString(ori.p, 600,100+v)
gr.drawString(ori.p*180/math.pi,800,100+v)
gr.drawString(ori[4],150,115+v)
gr.drawString(ori[5],300,115+v)
gr.drawString(ori[6],450,115+v)
gr.drawString(ori.h, 600,115+v)
gr.drawString(ori.h*180/math.pi,800,115+v)
gr.drawString(ori[7],150,130+v)
gr.drawString(ori[8],300,130+v)
gr.drawString(ori[9],450,130+v)
gr.drawString(ori.b, 600,130+v)
gr.drawString(ori.b*180/math.pi,800,130+v)
end
printvector = function(vec, hpos, vpos)
gr.drawString(vec.x,hpos - 200 , vpos)
gr.drawString(vec.y,hpos, vpos)
gr.drawString(vec.z,hpos + 200 , vpos)
end
------------------------------------
--------general fire control--------
------------------------------------
plotlead = function(weaponvel, plr, tgt, retvec) --lead function, complements of wanderer, slightly modified
local targetpos = tgt.Position
local targetvel = tgt.Physics.Velocity
local lenghttargetvel = targetvel:getMagnitude()
local playerpos = plr.Position
local plrtotrg = playerpos - targetpos
local lenghtplrtotrg = plrtotrg:getMagnitude()
local trgangle = plrtotrg:getDotProduct(targetvel)
local a = (( weaponvel * weaponvel ) - ( lenghttargetvel * lenghttargetvel ))
local b = ( 2 * trgangle )
local c = -( lenghtplrtotrg * lenghtplrtotrg )
local discrim = ((b * b) - 4 * a * c)
local leadx = 0
local leady = 0
if discrim >= 0 and a ~= 0 then
local multipl1 = (( -b + math.sqrt(discrim)) / ( 2 * a))
local multipl2 = (( -b - math.sqrt(discrim)) / ( 2 * a))
local targetmult = nil
if multipl1 >=0 and multipl1 <= multipl2 and multipl2 >= 0 then
targetmult = multipl1
elseif multipl1 >=0 and multipl2 < 0 then
targetmult = multipl1
elseif multipl2 >=0 then
targetmult = multipl2
else
targetmult = nil
end
if targetmult ~= nil then
local leadvel = targetvel/(1/targetmult)
local leadpos = targetpos + leadvel
if retvec then --sometimes i want a vector rather than a screen coord
return leadpos
elseif leadpos:getScreenCoords() ~= false then
leadx, leady = leadpos:getScreenCoords() --rather than just simply draw the coords, il scale and return them so my code can **** with them
leadx = leadx * wf --drawleadreticle(leadx,leady) pft, my way is better :D
leady = leady * hf --theese factors make things so much easyer
return leadx, leady, leadpos --yep, thats what i need there
else
return -1, -1, leadpos --return -1-1 if this all fails, so it doesnt tweak my math
end
end
end
end
nukelead = function(tgt, srcv, wvel) --pretty much the same math as wandereds, but reduced in size
local epos = tgt.Position
local evel = tgt.Physics.Velocity
local vlen = evel:getMagnitude()
local edis = srcv - epos
local dlen = edis:getMagnitude()
local trig = edis:getDotProduct(evel)
local a = (wvel^2) - (vlen^2)
local b = trig * 2
local c = -(dlen^2)
local d = (b^2) - 4 * a * c
if d >= 0 and a ~= 0 then
local m1 = ( -b + math.sqrt(d) ) / ( 2 * a)
local m2 = ( -b - math.sqrt(d) ) / ( 2 * a)
if (m1 >= 0 and m1 <= m2 and m2 >= 0) or (m1 >= 0 and m2 < 0) then
return epos + ( evel / (1 / m1) )
elseif m2 >=0 then
return epos + ( evel / (1 / m2) )
else
return epos
end
else
return epos
end
end
getwepvel = function(player)
local banks = player.PrimaryBanks
local vel = 0
if banks.Linked then
local div = 0
local num = #banks
for i=1, num do
vel = vel + banks[i].WeaponClass.Speed
div = div + 1
end
vel = vel / div
else
vel = banks[1].WeaponClass.Speed
end
return vel
end
----------------------------
--------turret funcs--------
----------------------------
getturretsubs = function(player) --returns a list of turet subobject numbers and other options
local curnum = 1
local gunnums = {} --subsystem indices
local arms = {} --p/sbank weaponclasses
local foregun = {} --this option tells turrets to gimbal forward, and within what cone, for ships like apis with turreted blindfire
local mantarg = {} --uses manual targeting
local trackrate ={} --turret tracking rate
for i=1, #player do
local ssname = player[i].Name
if string.find(ssname, "#pt") then --use the built in lua funcs or die!
gunnums[curnum] = i
if player[i].PrimaryBanks:isValid() then
arms[curnum] = player[i].PrimaryBanks[1].WeaponClass --only supporting the first s or p bank
elseif player[i].SecondaryBanks:isValid() then
arms[curnum] = player[i].SecondaryBanks[1].WeaponClass --maybe more later
else
arms[curnum] = tb.WeaponClasses["@Subach HL-7"] --incase nothihng works, we still have good ol hl7
end
local a,cone = string.find(ssname, "-f")
if a ~= nil then
foregun[curnum] = math.rad(tonumber(string.sub(ssname, cone+1, cone+3))) --get the gimbal cone in degrees and convert it to radians
end
if string.find(ssname, "-m") then mantarg[curnum] = true else mantarg[curnum] = false end
local b,rate = string.find(ssname, "-r")
if b ~= nil then
trackrate[curnum] = tonumber(string.sub(ssname, rate+1, rate+3))
else
trackrate[curnum] = 10
end
curnum = curnum + 1
end
end
return gunnums, arms, foregun, mantarg ,trackrate
end
getturretgunpos = function(player,tsub) --find the turret firing point
local pos = player.Position + player.Orientation:unrotateVector(tsub.Position) + tsub.GunPosition
local top = nil
if tsub.Position.y + tsub.GunPosition.y >= tsub.Position.y then
top = true
else
top = false
end
return pos,top
end
getturretangs = function(pori,ppos,tpos,top) --find the angles the turret must rotate to hit a location
local tpos = tpos - ppos --subtract player from tpos to get the player at center
local tpos = pori:rotateVector(tpos) --bring coorsd into player space (tm), now its a 2d problem
local hyp = math.sqrt( (tpos.z^2) + (tpos.x^2) ) --get hypotenuce, tpos.z is opposite and tpos.x is adjacent
local yawangle = math.acos(tpos.z/hyp) --use acos to determine rotation
if tpos.x < 0 then yawangle = -yawangle end --turn it around if its backwards, now lets figure gun pitch
local opp = math.sqrt( (tpos.x^2) + (tpos.z^2) ) --figure out our adjacet in order to get our hypotenuce
hyp = math.sqrt( (tpos.y^2) + (opp^2) ) --this time tpos.y is our adjacent
local pitchangle = math.asin(opp/hyp)
if (top and tpos.y < 0) or (top == false and tpos.y >= 0) then
pitchangle = -pitchangle --give a negative pitch value if the guns are not in the turret arc
pitchangle = pitchangle + math.pi --rotate 180
end --we can use this later for forward facing freelancer style turrets
if top == false then yawangle = yawangle + math.pi end --turn around underside turrets
--gr.drawString(math.deg(yawangle),100,100) --testing prints
--gr.drawString(math.deg(pitchangle),100,120)
return yawangle, pitchangle
end
getturretnorm = function(player,tsub,top) --gets turret firing normal
local gnorm = nil
if top then
gnorm = ba.createVector(0,1,0)
else
gnorm = ba.createVector(0,-1,0)
end
gnorm = tsub.GunOrientation:unrotateVector(gnorm) --unrotate the up vector to the gun
gnorm = tsub.Orientation:unrotateVector(gnorm) --then unrotate the gun normal to the base
gnorm = player.Orientation:unrotateVector(gnorm) --then to the ship
return gnorm --were done
end
fireturret = function(plr,wep,pos,dir) --fires the guns, simple for now
if io.isMouseButtonDown(MOUSE_LEFT_BUTTON) then
mn.createWeapon(wep,dir:getOrientation(),pos,plr,-1)
end
end
rcturret = function(player,target) --ship player, str turret name, bool underside gun, weaponclass
local guns,tweps,fore,manual,track = getturretsubs(player) --get our subobject numbers for our weapons, and options too
if guns[1] ~= nil then --bail if no turrets
for i=1, #guns do
local gun = guns[i]
local gpnt, top = getturretgunpos(player, player[gun]) --find firing point and wether its an upper turret or lower turret
local tpos = nil
local wep = tweps[i]
local dis = wep.Speed
if manual[i] then
tpos = mjtonormal(U,V)
tpos = player.Orientation:unrotateVector(tpos)
if target:isValid() then
dis = target.Position:getDistance(gpnt)
tpos = player.Position + ( tpos * dis ) --converge on target
else
tpos = gpnt + ( tpos * dis ) --no target, go straight
end
else
if target:isValid() then
dis = target.Position:getDistance(gpnt)
tpos = nukelead(target, gpnt, wep.Speed) --find target lead solution if needed
else
tpos = gpnt + player.Orientation:unrotateVector(ba.createVector(0,0,1)) --or just fire sgraight if no target
end
end
local bh, gp = getturretangs(player.Orientation ,gpnt ,tpos, top) --calculate gun pitch and heading in order to hit target
gr.drawString("Turret "..tostring(i).." Heading: "..string.sub(tostring(math.deg(bh)),1,5).." , Pitch: "..string.sub(tostring(math.deg(gp)),1,5), 30, 30 + (i*20) ) --test print
local bori = player[gun].Orientation --get base rotation
local radis = bori.h - bh
gr.drawString(math.deg(bori.h))
-- if bh > bori.h + 0.01 then
-- bori.h = bori.h - ( track[i] * 0.1 * ba.getFrametime() )
-- elseif bh < bori.h - 0.01 then
-- bori.h = bori.h + ( track[i] * 0.1 * ba.getFrametime() )
-- else
bori.h = bh
-- end
gr.drawString(math.deg(bori.h))
-- local dir = nil
-- if radis >= 0 then dir = 0.01 else dir = -0.01 end
-- if math.abs(radis) > math.pi then dir = -dir end
-- if math.abs(radis) > 0.02 then
-- bori.h = bori.h - ( track[i] * ba.getFrametime() * dir ) --animate rotation
-- if top then --make sure were not too rotated
-- if bori.h > math.pi then
-- bori.h = bori.h - (math.pi*2)
-- elseif bori.h < -math.pi then
-- bori.h = bori.h + (math.pi*2)
-- end
-- else --tops are on a -180 to 180 scale, bots are 0 to 360
-- if bori.h > math.pi * 2 then
-- bori.h = bori.h - (math.pi*2)
-- elseif bori.h < 0 then
-- bori.h = bori.h + (math.pi*2)
-- end
-- end
-- else
-- bori.h = bh --unless your so close that animation becomes stupid
-- end
player[gun].Orientation = bori --rotate base
local gori = player[gun].GunOrientation --get gun rotation
gori.p = gp
player[gun].GunOrientation = gori --rotate gun
local aim = getturretnorm(player, player[gun], top) * dis --aim normal
fireturret(player,wep,gpnt,aim)
aim = gpnt + aim
if aim:getScreenCoords() then blindfirereticle(aim:getScreenCoords()) end
end
end
end
-------------------------------
--------blindfire funcs--------
-------------------------------
getblindfireguns = function() --returns a list of all guns with blindfire. theese are the dummy weapons that tell when and where to launch a real one
local num = 1
local weaps = {}
local cones = {}
local autos = {}
for i=1, #mn.Weapons do
local wepname = mn.Weapons[i].Class.Name
if string.find(wepname, "#bf") then
weaps[num] = mn.Weapons[i]
local cone, cvar = string.find(wepname, "-c") --cone is just a place holder as i only need the second number, but i can use the var later
if cvar ~= nil then
cone = tonumber( string.sub(wepname, cvar+1, cvar+3) ) --get the two numbers after the -c tag and convert them to number
else
cone = 10 --if the tag isnt in the weapon name, use a default of 15
end
cones[num] = cone
if string.find(wepname, "-a") then autos[num] = false else autos[num] = true end
num = num + 1
end
end
return weaps, cones, autos --list of weapon objects
end
blindfire = function(plr, tgt) --like the one for turrets sept for standrard guns
local guns, cones, autos = getblindfireguns()
if guns[1] ~= nil then
local tvec = nil
for i=1, #guns do
local gpos = guns[i].Position
local cone = math.cos( cones[i] * math.pi / 180 )
local man = autos[i]
if man then
tvec = mjtonormal(U,V)
if tvec.z < cone then tvec = ba.createVector(0,0,1) end
tvec = plr.Orientation:unrotateVector(tvec)
else
if tgt:isValid() then
tvec = nukelead(tgt,gpos,guns[i].Class.Speed)
tvec = tvec - gpos
local tvec2 = normalize( plr.Orientation:rotateVector(tvec) )
if tvec2.z < cone then tvec = plr.Orientation:unrotateVector( ba.createVector(0,0,1) ) end
else
tvec = plr.Orientation:unrotateVector(ba.createVector(0,0,1)) --if our target doesnt exist, feed it some bull
end
end
local wpn = guns[i].Class.Name --get our weapons name
local wnend = string.find(wpn, "#")
wpn = string.sub(wpn, 1, wnend-1) --remove everything after and including #
local newgun = tb.WeaponClasses[wpn] --now get the regular weaponclass
mn.createWeapon(newgun,tvec:getOrientation(),gpos,plr,-1)
guns[i].LifeLeft = 0.0
end
end
end
--[[===============================================]]--
--[[======= function calls and initial vars =======]]--
--[[===============================================]]--
w,h,cx,cy,wf,hf,awh,hires = setscreenvars()
]
$Simulation:
[
player = mn.Ships["Alpha 1"]
if player:isValid() then target = player.Target end
if io.isMouseButtonDown(MOUSE_RIGHT_BUTTON) then
X,Y,U,V = mousejoy(1)
else
X,Y,U,V = mousejoy(0)
end
]
$HUD:
[
if player:isValid() then
rcturret(player,target)
blindfire(player,target)
end
--[[ newtonian
if player:isValid() then
if lvel == nil then lvel = ba.createVector(0,0,0) end
if ppos == nil then ppos = player.Position end
lvel = lvel + ( ((player.Physics.VelocityDesired*1000) / player.Model.Mass) * ba.getFrametime() ) --reads max velocity as max output in kilonewtons
wvel = ppos + lvel
lmag = lvel:getMagnitude()
lmag = lmag / ba.getFrametime()
gr.drawString( round(lmag), 100 ,100)
Vx,Vy = (wvel + (lvel*lmag) ):getScreenCoords()
player.Position = wvel
player.Physics.Velocity = wvel
ppos = wvel
end
if Vx then prograderet( Vx, Vy, round(lmag) ) end
]]
mousereticle(io.getMouseX(),io.getMouseY())
]
#End