-- LUA Script - precede every function and global member with lowercase name of script + '_main' g_ENTITY_NAME_LIST = {} local turret_bases = {} -- These settings are for all turrets local detectRange = 1500 local minimumSoundRange = 4000 local gunCoolTime = 1000 local Q = require "scriptbank\\quatlib" local U = require "scriptbank\\utillib" 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") Include("utillib.lua") 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 ) 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 } CollisionOff( e ) ResetPosition( e, x, y, z ) ResetRotation( e, deg( xA ), deg( yA ), deg( zA ) ) CollisionOn( e ) 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 = U.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 = U.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 + gunCoolTime end end end end local function displayTurrets( p ) local gun = p.turret if gun ~= nil then doTurret( gun.Ent, p.x, p.y + 68, p.z, 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 + 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 ) 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 if base.state ~= 'dead' then -- 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 ) if HideGunGlow ~= nil then HideGunGlow( base.radar.Ent ) end base.radar.Ent = base.damagedRadars[ random( 1, numDRs ) ] displayRadar( base ) Show( base.radar.Ent ) end StartParticleEmitter( base.radar.Ent ) StartParticleEmitter( base.turret.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 ) CollisionOff( e ) ResetRotation( e, 0, base.gunCurrAng, 0 ) CollisionOn( e ) 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' end elseif base.state == 'scan' then base.state = 'idle' base.timer = g_Time + 3000 base.fireGun = false elseif base.state ~= 'dead' then 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 + 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