Author Topic: remote controlled turrets revisited  (Read 8809 times)

0 Members and 1 Guest are viewing this topic.

Offline takashi

  • Better than TrashMan
  • 29
Re: remote controlled turrets revisited
i would love a function that makes your ship AI controlled when you controll the turret, and vice versa. with a turret view.

maybe in 3.7.90.....

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
Re: remote controlled turrets revisited
why when you can do both at once :D
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline WMCoolmon

  • Purveyor of space crack
  • 213
Re: remote controlled turrets revisited
Nuke, what would be the idea function(s) for optimizing a script like this? I was thinking that a pointTurretAt3DPoint() function would be useful for something like this, and for other things. (Like a cutscene where you want a small ship to fly by a larger ship, while all of the larger ship's turrets track the smaller ship). But the more math I can move into the C environment, the faster the script will go.
-C

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
Re: remote controlled turrets revisited
that woyuld be awesome. currently, im using loop to go through the subsystems and check $name string in the subsystems properties for a #pt. if it finds it it will then look for other options in the name string like the rotation speed and the manual control flag. all this data is passed to the turret function which loops through the arrays and does all the math involved in manipulating the turret. big thing is im not actually firing the turret, im creating a weapon projectile with createWeapon(). im using about 6-8 calls of un/rotatesVector() per turret. using the script on a ship like the aeolas will essentially half your framerate. so doing 120 lines worth of scripot in a single function would be a hell of alot faster. volition's hackish turret code might be a mess but its a hell of alot faster than what i can do with script.
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline WMCoolmon

  • Purveyor of space crack
  • 213
Re: remote controlled turrets revisited
Do you have a script example posted?

I'd like to make it a little more usable than just a "run_Nukes_script_43" function in scripting. Something like "force turret target point" and then have a little point object you can create and move around (which I believe is already in the code) and move all the attached turrets. That way you'd just have to calculate the one 3D point based on the current mouse position. I don't know how you're doing it now.
-C

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
Re: remote controlled turrets revisited
heres the latest version, im still trying to fix the functions that i broke during my attempt to optimise it. its also full of other experemental stuff too. but it should give you an idea of what ive been up to. im not sure it will even run in this condition. should run on your new build. just remember to put #pt (#pt-m for manual mode, not sure if thats currently working though) into the name string in the subobject properties.

Code: [Select]
#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] = tonumber(string.sub(ssname, cone+1, cone+3))
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
end --we can use this later for forward facing freelancer style turrets

gr.drawString(math.deg(yawangle),100,100)
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

--[[if fore[i] and top then
local cone = fore[i] * math.pi/180 --keep forguns pointed inside the cone

gp = gp + math.pi
bh = bh + math.pi

gr.drawString(bh,100,100)

if gp < (math.pi/2) - cone then
gp = (math.pi/2) - cone
elseif gp > (math.pi/2) + cone then
gp = (math.pi/2) + cone
end

if bh > math.pi + cone then
bh = math.pi + cone
elseif bh < math.pi - cone then
bh = math.pi - cone
end

elseif fore[i] and top == false then


else]]

if top then --limit our pitch for upper and lower guns
--if gp > 0 then gp = 0 end
else
--gp = gp + math.pi
bh = bh + math.pi
--if gp > math.pi/2 then gp = math.pi/2 end
end

local bori = player[gun].Orientation --rotate base

-- local radis = bori.h - bh

-- if radis < 0 then
-- if radis < -math.pi then radis = -(radis + math.pi) end
-- else
-- if radis > math.pi then radis = -(radis - math.pi) end
-- end

-- if radis > 0.02 then
-- bori.h = bori.h - (0.8 * ba.getFrametime())
-- if bori.h < -math.pi then bori.h = bori.h + (math.pi*2) end
-- elseif radis < -0.02 then
-- bori.h = bori.h + (0.8 * ba.getFrametime())
-- if bori.h > math.pi then bori.h = bori.h - (math.pi*2) end
-- else
bori.h = bh
-- end

player[gun].Orientation = bori

local gori = player[gun].GunOrientation --rotate gun

-- radis = gori.p - gp

-- if radis < 0 then
-- if radis < -math.pi then radis = -(radis + math.pi) end
-- else
-- if radis > math.pi then radis = -(radis - math.pi) end
-- end

-- if radis > 0.02 then
-- gori.p = gori.p - (0.8 * ba.getFrametime())
-- if gori.p < -math.pi then gori.h = gori.h + (math.pi*2) end
-- elseif radis < 0.02 then
-- gori.p = gori.p + (0.8 * ba.getFrametime())
-- if gori.p > math.pi then gori.h = gori.h - (math.pi*2) end
-- else
gori.p = gp
-- end

player[gun].GunOrientation = gori

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

*edit* bugfixes
« Last Edit: April 03, 2007, 06:35:15 am by Nuke »
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
Re: remote controlled turrets revisited
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.

Code: [Select]
#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

I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline takashi

  • Better than TrashMan
  • 29
Re: remote controlled turrets revisited
head build needed?

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Mutants Worship Me
Re: remote controlled turrets revisited
it runs in the latest head. but this highly, and i mean highly experimental.
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

  

Offline takashi

  • Better than TrashMan
  • 29
Re: remote controlled turrets revisited
thanks for the warning