-- LUA Script - precede every function and global member with lowercase name of script + '_main' -- Default script - does nothing. g_ENTITY_NAME_LIST = {} local turret_bases = {} -- These settings are for all turrets local detectRange = 2000 local minimumSoundRange = 3500 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_dual_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) return {Ent = e, Obj = g_Entity[e].obj, quat = Q.FromEuler(0, 0, 0), rotQ = Q.FromEuler(0, 0, 0), pitQ = Q.FromEuler(0, 0, 0)} end function BaseDualAddTurret(e) for k,v in pairs(turret_bases) do if v.turret == nil then turret_bases[k].turret = InitTurret(e, i) return turret_bases[k] end end end local function AlreadyInList(e, list) for _,v in pairs(list) do if g_ENTITY_NAME_LIST[v] == g_ENTITY_NAME_LIST[e] then return true end end return false end function BaseDualAddDamagedTurret(e) for k,v in pairs(turret_bases) do if v.turret ~= nil then if not AlreadyInList(e, v.damagedTurrets) then local numDTs = #v.damagedTurrets turret_bases[k].damagedTurrets[numDTs + 1] = e return turret_bases[k] end end end end function BaseDualAddRadar(e) for k,v in pairs(turret_bases) do if v.radar == nil then turret_bases[k].radar = {Ent = e, quat = Q.FromEuler(0, 0, 0), rotQ = Q.FromEuler(0, 0, 0)} turret_bases[k].radObj = g_Entity[e].obj return turret_bases[k] end end end function BaseDualAddDamagedRadar(e) for k,v in pairs(turret_bases) do if v.radar ~= nil then if not AlreadyInList(e, v.damagedRadars) then local numDRs = #v.damagedRadars turret_bases[k].damagedRadars[numDRs + 1] = e return turret_bases[k] end end end 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, 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) turretPos[e] = {x = x, y = y, z = z, xa = xA, ya = yA, za = zA} ResetPosition(e, x, y, z) ResetRotation(e, deg(xA), deg(yA), deg(zA)) if showGlow and ShowGunGlow ~= nil then ShowGunGlow(e, x, y, z) end end local barrelPos = {[1] = {xo = 33.87, yo = 3.9, zo = 84.83, fireTime = 0}, [2] = {xo = 30.28, yo = 3.9, zo = 84.83, fireTime = 0}, [3] = {xo = -30.44, yo = 3.9, zo = 84.83, fireTime = 0}, [4] = {xo = -35.84, yo = 3.9, zo = 84.83, fireTime = 0}, [5] = {xo = 36.0 , yo = -5.7, zo = 64.20, fireTime = 0}, [6] = {xo = 30.44, yo = -5.7, zo = 64.20, fireTime = 0}, [7] = {xo = -30.5 , yo = -5.7, zo = 64.20, fireTime = 0}, [8] = {xo = -33.84, yo = -5.7, zo = 64.20, fireTime = 0} } local function Shoot(e, p, PX, PY, PZ, timeNow) if FireGunShot ~= nil then local gun = p.turret local tp = turretPos[gun.Ent] -- choose a barrel local b = barrelPos[random(1,8)] if timeNow > b.fireTime then -- work out position of gun barrel local XO, YO, ZO = Rotate3D (b.xo, b.yo, b.zo, tp.xa, tp.ya, tp.za) -- can we see player? if IntersectAll(PX + XO, PY, PZ + ZO, tp.x + XO, tp.y + YO, tp.z + ZO, gun.obj) ~= 0 then return end if RayTerrain (PX + XO, PY, PZ + ZO, tp.x + XO, tp.y + YO, tp.z + ZO) ~= 1 then -- set speed and vector of shot local xi, yi, zi = Rotate3D (0, 0, random(35, 45), tp.xa, tp.ya, tp.za) FireGunShot(tp.x + XO, tp.y + YO, tp.z + ZO, xi, yi, zi, p.soundVolume) b.fireTime = timeNow + 500 end end end end local function displayTurrets(p) local gun = p.turret if gun ~= nil then -- doTurret(gun.Ent, p.x, p.y + 37, p.z, p.gunCurrAng, gun.quat, gun.rotQ, gun.pitQ, p.fireGun) doTurret(gun.Ent, p.x, p.y + 68, p.z, p.gunCurrAng, gun.quat, gun.rotQ, gun.pitQ, p.fireGun) end end local function displayRadar(p) if p.radar ~= nil then -- doTurret(p.radar.Ent, p.x, p.y + 45, p.z, p.radAng, p.radar.quat, p.radar.rotQ) doTurret(p.radar.Ent, p.x, p.y + 85, p.z, 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(e, b) local gun = b.turret if gun ~= nil then local rotNeeded = CalcAngleDiff(b.gunReqAng, b.gunCurrAng) if abs(rotNeeded) < 1 then b.gunCurrAng = b.gunReqAng elseif rotNeeded < 0 then b.gunCurrAng = b.gunCurrAng - 1 else b.gunCurrAng = b.gunCurrAng + 1 end if b.gunCurrAng == 90 or b.gunCurrAng == 270 then b.gunCurrAng = b.gunCurrAng + 0.001 end gun.rotQ = Q.FromEuler(0, rad( b.gunCurrAng), 0, 0) if abs(rotNeeded) > 10 then PlaySoundIfSilent(e, 0) SetSoundVolume(b.soundVolume) end end end local function PitchGuns(b) local gun = b.turret if gun ~= 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 gun.pitQ = Q.FromEuler(rad(-b.gunCurrPitch), 0, 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_dual_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 turret_bases[e] = {state = 'init', x = Ent.x, y = Ent.y, z = Ent.z, turret = nil, damagedTurrets = {}, radar = nil, damagedRadars = {}, gunCurrAng = Ang, gunReqAng = Ang, gunCurrPitch = 0, gunReqPitch = 0, fireGun = false, timer = g_Time + 10000, soundVolume = 70, radAng = random(1,360), radCW = (random(1,2) == 1)} return end if base.turret == nil or base.radar == nil then PromptLocal(e, 'Waiting for entities') return end -- work out how healthy we are local combHealth = Ent.health + g_Entity[base.turret.Ent].health + g_Entity[base.radar.Ent].health if combHealth == 0 then -- we are dead so swap entities for damaged ones base.state = 'dead' local numDTs = #base.damagedTurrets if numDTs > 0 then Hide(base.turret.Ent) base.turret.Ent = base.damagedTurrets[random(1,numDTs)] displayTurrets(base) Show(base.turret.Ent) end local numDRs = #base.damagedRadars if numDRs > 0 then Hide(base.radar.Ent) base.radar.Ent = base.damagedRadars[random(1,numDRs)] displayRadar(base) Show(base.radar.Ent) end end local PX, PY, PZ = g_PlayerPosX, g_PlayerPosY, g_PlayerPosZ -- set sound volume local dx,dz = PX - Ent.x, PZ - Ent.z local dist = sqrt(dx*dx + dz*dz) if dist > minimumSoundRange then base.soundVolume = 70 else base.soundVolume = 70 + (1 - dist / minimumSoundRange) * 29 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, rad(base.radAng), 0) 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(e, base) ResetRotation(e, 0, base.gunCurrAng, 0) end if base.gunCurrPitch ~= base.gunReqPitch then PitchGuns(base) end if base.state == 'init' then if base.turret ~= nil and base.radar ~= nil then base.state = 'idle' if AddGunGlowEnt ~= nil then AddGunGlowEnt(base.turret.Ent) end end elseif base.state == 'scan' then base.state = 'idle' base.timer = g_Time + 3000 if HideGunGlow ~= nil then HideGunGlow(base.turret.Ent) end base.fireGun = false else displayTurrets(base) displayRadar(base) -- detect player if not CloserThan(PX, PZ, base.x, base.z, detectRange) then if base.state == 'alert' then base.state = 'scan' end return end local angToPlayer = WrapAng(deg(atan(PX - base.x, PZ - base.z))) if abs(base.radAng - angToPlayer) < 1 then -- Check if player visible if IntersectAll(PX, PY + 35, PZ, base.x, base.y + 110, base.z, base.radObj) == 0 and RayTerrain (PX, PY + 35, PZ, base.x, base.y + 110, base.z) ~= 1 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 = angToPlayer -- local hgt = (base.y + 37) - PY local hgt = (base.y + 68) - PY local GP = deg(atan(hgt, DistanceTo(base.x, base.z,PX,PZ))) base.fireGun = false if GP > -40 and GP < 40 then base.gunReqPitch = GP base.fireGun = true end if base.fireGun and abs(base.gunCurrAng - base.gunReqAng) < 2 and abs(base.gunCurrPitch - base.gunReqPitch) < 2 then Shoot(e, base, PX, PY, PZ, g_Time) end end end end