-- LUA Script - precede every function and global member with lowercase name of script + '_main' -- Default script - does nothing. local turret_bases = {} local Q = require "scriptbank\\quatlib" math.randomseed(os.time()) local deg = math.deg local rad = math.rad local sin = math.sin local cos = math.cos local atan = math.atan2 local tan = math.tan local pi = math.pi local abs = math.abs local random = math.random local sqrt = math.sqrt local modf = math.modf local atan = math.atan2 -- make sure random number generator is seeded random(); random(); random() function turret_base_init(e) Include("quatlib.lua") end local function Rotate3D (x, y, z, xrot, yrot, zrot) function RotatePoint2D (x, y, Ang) -- Ang in radians local Sa, Ca = sin(Ang), cos(Ang) return x*Ca - y*Sa, x*Sa + y*Ca end local NX, NY, NZ = x, y, z -- X NZ, NY = RotatePoint2D (NZ, NY, -xrot) -- Y NX, NZ = RotatePoint2D (NX, NZ, -yrot) -- Z NY, NX = RotatePoint2D (NY, NX, -zrot) return NX, NY, NZ end local function InitTurret(e, index) if index == 1 then return {Ent = e, Obj = g_Entity[e].obj, quat = Q.FromEuler(rad( 90), 0, rad( 90)), rotQ = Q.FromEuler(0, 0, 0), pitQ = Q.FromEuler(0, 0, 0)} else return {Ent = e, Obj = g_Entity[e].obj, quat = Q.FromEuler(rad(-90), 0, rad(-90)), rotQ = Q.FromEuler(0, 0, 0), pitQ = Q.FromEuler(0, 0, 0)} end end function BaseAddTurret(e) for k,v in pairs(turret_bases) do for i = 1,2 do if v.turrets ~= nil and v.turrets[i] == nil then turret_bases[k].turrets[i] = InitTurret(e, i) return turret_bases[k] end end end return nil end function BaseAddRadar(e) for k,v in pairs(turret_bases) do if v.radar == nil then turret_bases[k].radar = {Ent = e, quat = Q.FromEuler(rad( -90), 0, 0), rotQ = Q.FromEuler(0, 0, rad(v.radAng))} return turret_bases[k] end end return nil end local function WrapAng(Ang) local RAng = Ang while RAng >= 360 do RAng = RAng - 360 end while RAng < 0 do RAng = RAng + 360 end return RAng end local turretPos = {} local function doTurret(e, x, y, z, xo, zo, Ang, bq, rq, pq) local quat = Q.Mul(bq, rq) if pq ~= nil then quat = Q.Mul(quat, pq) end local xA, yA, zA = Q.ToEuler(quat) local XO, YO, ZO = Rotate3D (xo, 0, zo, 0, rad(Ang), 0) local xP, zP = x - XO, z - ZO turretPos[e] = {x = xP, y = y, z = zP, xa = xA, ya = yA, za = zA} ResetPosition(e, xP, y, zP) ResetRotation(e, deg(xA), deg(yA), deg(zA)) end local function Shoot(p, PX, PY, PZ) if FireGunShot ~= nil then local gun = p.turrets[random(1,2)] local tp = turretPos[gun.Ent] -- work out position of gun barrels local XO, YO, ZO = Rotate3D (-8.4, 0, 118, tp.xa, tp.ya, tp.za) -- can we see player? (note this doesn't yet account for terrain!) if IntersectAll(PX, PY, PZ, tp.x + XO, tp.y + YO, tp.z + ZO, gun.obj) ~= 0 then return end -- set speed and vector of shot local xi, yi, zi = Rotate3D (0, 0, 40, tp.xa, tp.ya, tp.za) FireGunShot(tp.x + XO, tp.y + YO, tp.z + ZO, xi, yi, zi) XO, YO, ZO = Rotate3D (8.4, 0, 118, tp.xa, tp.ya, tp.za) FireGunShot(tp.x + XO, tp.y + YO, tp.z + ZO, xi, yi, zi) end end local function displayTurrets(p) local gun = p.turrets[1] if gun ~= nil then doTurret(gun.Ent, p.x, p.y + 416, p.z, 0, 35, p.gunCurrAng, gun.quat, gun.rotQ, gun.pitQ) end gun = p.turrets[2] if gun ~= nil then doTurret(gun.Ent, p.x, p.y + 416, p.z, 0, -35, p.gunCurrAng, gun.quat, gun.rotQ, gun.pitQ) end end local function displayRadar(p) if p.radar ~= nil then doTurret(p.radar.Ent, p.x, p.y + 494, p.z, 0, -15, p.radAng, p.radar.quat, p.radar.rotQ) end end local function RotateGuns(b) local gun1, gun2 = b.turrets[1], b.turrets[2] if gun1 ~= nil and gun2 ~= nil then local rotNeeded = b.gunReqAng - b.gunCurrAng local move = false if abs(rotNeeded) < 0.5 then b.gunCurrAng = b.gunReqAng elseif rotNeeded < 0 then b.gunCurrAng = b.gunCurrAng - 0.5 else b.gunCurrAng = b.gunCurrAng + 0.5 end gun1.rotQ = Q.FromEuler(rad( b.gunCurrAng), 0, 0) gun2.rotQ = Q.FromEuler(rad(-b.gunCurrAng), 0, 0) end end local function PitchGuns(b) local gun1, gun2 = b.turrets[1], b.turrets[2] if gun1 ~= nil and gun2 ~= nil then local pitchNeeded = b.gunReqPitch - b.gunCurrPitch if abs(pitchNeeded) < 0.1 then b.gunCurrPitch = b.gunReqPitch elseif pitchNeeded < 0 then b.gunCurrPitch = b.gunCurrPitch - 0.1 else b.gunCurrPitch = b.gunCurrPitch + 0.1 end gun1.pitQ = Q.FromEuler(0, rad( b.gunCurrPitch), 0) gun2.pitQ = Q.FromEuler(0, rad(-b.gunCurrPitch), 0) end end local function DistanceTo(x1,z1,x2,z2) local dX, dZ = x1 - x2, z1 - z2 return sqrt(dX*dX + dZ*dZ) end function turret_base_main(e) local Ent = g_Entity[e] if Ent == nil then return end local base = turret_bases[e] if base == nil then local Ang = WrapAng(Ent.angley) if Ang == 90 or Ang == 270 then Ang = Ang + 0.0001 end local XO, YO, ZO = Rotate3D (-4.1, 0, 3.75, 0, rad(Ang), 0) turret_bases[e] = {state = 'init', x = Ent.x + XO, y = Ent.y, z = Ent.z + ZO, turrets = {}, radar = nil, gunCurrAng = 0, gunReqAng = 0, gunCurrPitch = 0, gunReqPitch = 0, timer = math.huge, radAng = random(1,360), radCW = (random(1,2) == 1)} return end if base.radar ~= nil and base.state == 'idle' then if base.radCW then base.radAng = WrapAng(base.radAng - 1) else base.radAng = WrapAng(base.radAng + 1) end base.radar.rotQ = Q.FromEuler(0, 0, rad(base.radAng)) end if base.gunCurrAng ~= base.gunReqAng then RotateGuns(base) end if base.gunCurrPitch ~= base.gunReqPitch then PitchGuns(base) end if base.state == 'init' then if #base.turrets == 2 and base.radar ~= nil then base.state = 'idle' end elseif base.state == 'scan' then base.state = 'idle' else displayTurrets(base) displayRadar(base) -- detect player local PX, PY, PZ = g_PlayerPosX, g_PlayerPosY, g_PlayerPosZ local angToPlayer = modf(WrapAng(deg(atan(PX - base.x, PZ - base.z)))) if base.radAng == angToPlayer then -- Check if player visible if IntersectAll(PX, PY, PZ, base.x, base.y + 300, base.z, Ent.obj) == 0 then base.state = 'alert' end elseif base.state == 'alert' then base.state = 'scan' if base.radAng > angToPlayer then base.radCW = true else base.radCW = false end end if base.state == 'alert' then base.gunReqAng = WrapAng(angToPlayer - 90) local hgt = (base.y + 416) - PY local GP = deg(atan(hgt, DistanceTo(base.x, base.z,PX,PZ))) local fireGun = false if GP > -40 and GP < 40 then base.gunReqPitch = GP fireGun = true end if fireGun and base.gunCurrAng == base.gunReqAng and base.gunCurrPitch == base.gunReqPitch then if random(1,10) == 5 then Shoot(base, PX, PY, PZ) end end -- PromptLocal(e, base.state .. ", " .. modf(GP)) end end end