-- 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, showGlow) 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)) if showGlow then ShowGunGlow(e, xP, y, zP) end 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, p.state == 'alert') 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, p.state == 'alert') end end local function displayRadar(p) if p.radar ~= nil then doTurret(p.radar.Ent, p.x, p.y + 488, p.z, 0, -20, p.radAng, p.radar.quat, p.radar.rotQ) -- 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 CalcAngleDiff(newAng, currAng) local Ang = newAng - currAng if Ang < 0 then if Ang < -180 then return Ang + 360 end else if Ang > 180 then return Ang - 360 end end return Ang end local function RotateGuns(b) local gun1, gun2 = b.turrets[1], b.turrets[2] if gun1 ~= nil and gun2 ~= nil then local rotNeeded = CalcAngleDiff(b.gunReqAng, b.gunCurrAng) 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 local function CloserThan(x1, z1, x2, z2, dist) local dX, dZ = x1 - x2, z1 - z2 return (dX*dX + dZ*dZ) < dist*dist 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 = g_Time + 10000, 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)) if g_Time > base.timer then base.gunReqAng = random(0,359) base.gunReqPitch = 0 base.timer = g_Time + 10000 end 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' AddGunGlowEnt(base.turrets[1].Ent) AddGunGlowEnt(base.turrets[2].Ent) end elseif base.state == 'scan' then base.state = 'idle' base.timer = g_Time + 3000 HideGunGlow(base.turrets[1].Ent) HideGunGlow(base.turrets[2].Ent) else displayTurrets(base) displayRadar(base) -- detect player local PX, PY, PZ = g_PlayerPosX, g_PlayerPosY, g_PlayerPosZ if not CloserThan(PX, PZ, base.x, base.z, 3000) then if base.state == 'alert' then base.state = 'scan' end return end 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 abs(base.gunCurrAng - base.gunReqAng) < 2 and abs(base.gunCurrPitch - base.gunReqPitch) < 2 then if random(1,10) == 5 then Shoot(base, PX, PY, PZ) end end end end end