As part of the first game I’m working on, I have a pair of vehicles (one land vehicle and one air vehicle) that both have what are supposed to be turrets that are independently controlled by another player in a different seat than the driver’s seat. The code I’ve written so far works great if one person is driving the vehicle or manning the turret, but the moment two players try to do both at the same time in the same vehicle, the script fails to take into account the actions of whichever player sat down first. The vehicles are assembled entirely using constraints, like HingeConstraints, CylindricalConstraints, and SpringConstraints, and don’t use welds except for attaching the seats to the body.
I am aware this may be a network ownership issue and I have seen this solution, but my vehicle’s turrets move in four directions (up/down, left/right) and use the mouse cursor to aim. I don’t know what introducing that level of complexity to that solution would look like or what kind of new headaches it would result in. If another, simpler solution exists then I would much rather prefer to use that one.
I’ve attached the two relevant scripts below. I’m not interested in other possible problems that may exist with the code at the moment, only the issue mentioned above.
Client Script - Located in StarterCharacterScripts:
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")
local run = game:GetService("RunService")
local tweenInfo = TweenInfo.new(0.4)
local events = RS:WaitForChild("Events")
local boost = events:WaitForChild("Boost")
local BD = events:WaitForChild("BoostDepleted")
local drift = events:WaitForChild("Drift")
local fire = events:WaitForChild("FireTurret")
local character = script.Parent
local pilotForce = script:WaitForChild("VectorForce")
local gravity = Vector3.new(0, workspace.Gravity, 0)
local sitConnection = nil
local flightConnection = nil
local promptConnection = nil
local inputStart = nil
local inputEnd = nil
local speedChange = nil
local seat = nil
local forceValue = nil
local boosting = false
local drifting = false
local firing = false
local vectorForce = nil
local alignOrientation = nil
local force = 1
local drag = 0.5
local yAxis = 0
function getWheels(array)
local result = {}
for _, child in ipairs(array) do
if child.ClassName == "Part" and child.Shape == Enum.PartType.Cylinder then
table.insert(result, child)
end
end
return result
end
function getPassengerSeats(array)
local result = {}
for _, child in ipairs(array) do
if child.ClassName == "Seat" then
table.insert(result, child)
end
end
return result
end
function getAllSeats(array)
local result = {}
for _, child in ipairs(array) do
if (child.ClassName == "VehicleSeat" or child.ClassName == "Seat") and child:FindFirstChildWhichIsA("ProximityPrompt") then
table.insert(result, child)
end
end
return result
end
function steer(degrees, car)
local turnL = 0
local turnR = 0
local fl = car:FindFirstChild("AttachmentFL", true)
local fr = car:FindFirstChild("AttachmentFR", true)
local b = car:FindFirstChild("AttachmentML", true) or car:FindFirstChild("AttachmentBL", true)
if not fl or not fr or not b then return turnL, turnR end
local radians = math.rad(90 - math.abs(degrees))
local h = (fl.Position - b.Position).Magnitude
local z = (fl.Position - fr.Position).Magnitude
local x = math.tan(radians) * h
local y = (x + z)
local outerTurnAngle = 90 - math.deg(math.atan2(y, h))
if (degrees > 0) then
turnL = degrees
turnR = outerTurnAngle
else
outerTurnAngle = -outerTurnAngle
turnL = outerTurnAngle
turnR = degrees
end
return turnL, turnR
end
BD.OnClientEvent:Connect(function()
TS:Create(workspace.Camera, tweenInfo, {FieldOfView = 70}):Play()
end)
character.Humanoid.Seated:Connect(function(active, seatPart)
local allSeats = getAllSeats(workspace:GetDescendants())
if sitConnection ~= nil then
if promptConnection ~= nil then
promptConnection:Disconnect()
promptConnection = nil
end
if flightConnection ~= nil then
vectorForce.Enabled = false
pilotForce.Enabled = false
alignOrientation.Enabled = false
flightConnection:Disconnect()
flightConnection = nil
vectorForce = nil
alignOrientation = nil
end
if inputStart ~= nil then
inputStart:Disconnect()
inputStart = nil
end
if inputEnd ~= nil then
inputEnd:Disconnect()
inputEnd = nil
end
if speedChange ~= nil then
speedChange:Disconnect()
speedChange = nil
end
sitConnection:Disconnect()
sitConnection = nil
forceValue = nil
firing = false
end
if seatPart == nil then
for _, chair in pairs(allSeats) do
if chair.Parent.PrimaryPart.CFrame.UpVector.Y < 0 then continue end
chair.ProximityPrompt.Enabled = true
end
if seat == nil then return end
local attachment = nil
local height = -math.huge
for i, child in ipairs(seat:GetChildren()) do
if child.ClassName ~= "Attachment" then continue end
if child.WorldPosition.Y <= height then continue end
height = child.WorldPosition.Y
attachment = child
end
character.PrimaryPart.CFrame = attachment.WorldCFrame
seat = nil
else
promptConnection = run.RenderStepped:Connect(function()
for _, chair in pairs(allSeats) do
chair.ProximityPrompt.Enabled = false
end
end)
seat = seatPart
if seatPart.ClassName == "VehicleSeat" then
if seatPart.Parent.Name == "ATV" or seatPart.Parent.Name == "Wheeler" then
local maxAngularVelocity = seatPart.MaxSpeed / (seatPart.Parent.WheelBR.Size.Y / 2)
inputStart = UIS.InputBegan:Connect(function(input, GPE)
if GPE then return end
if input.KeyCode == Enum.KeyCode.LeftShift then
boosting = true
boost:FireServer(boosting, seatPart.Parent)
TS:Create(workspace.Camera, tweenInfo, {FieldOfView = 100}):Play()
elseif input.KeyCode == Enum.KeyCode.Q then
drifting = true
drift:FireServer(drifting)
end
end)
inputEnd = UIS.InputEnded:Connect(function(input, GPE)
if GPE then return end
if input.KeyCode == Enum.KeyCode.LeftShift then
boosting = false
boost:FireServer(boosting, seatPart.Parent)
TS:Create(workspace.Camera, tweenInfo, {FieldOfView = 70}):Play()
elseif input.KeyCode == Enum.KeyCode.Q then
drifting = false
drift:FireServer(drifting)
end
end)
speedChange = seatPart:GetPropertyChangedSignal("MaxSpeed"):Connect(function()
maxAngularVelocity = seatPart.MaxSpeed / (seatPart.Parent.WheelBR.Size.Y / 2)
seatPart.ThrottleFloat = 0
end)
local attachmentFL = seatPart.Parent.PrimaryPart:FindFirstChild("AttachmentFL")
local attachmentFR = seatPart.Parent.PrimaryPart:FindFirstChild("AttachmentFR")
local wheels = getWheels(seatPart.Parent:GetChildren())
sitConnection = seatPart.Changed:Connect(function(property)
if property == "SteerFloat" then
local angleL, angleR = steer(-seatPart.SteerFloat * seatPart.TurnSpeed, seatPart.Parent)
local orientationL, orientationR = Vector3.new(0, angleL, 90), Vector3.new(0, angleR, 90)
TS:Create(attachmentFL, tweenInfo, {Orientation = orientationL}):Play()
TS:Create(attachmentFR, tweenInfo, {Orientation = orientationR}):Play()
elseif property == "ThrottleFloat" then
local torque = math.abs(seatPart.ThrottleFloat * seatPart.Torque)
if torque == 0 then torque = 2000 end
local angularVelocity = math.sign(seatPart.ThrottleFloat) * maxAngularVelocity
for _, wheel in ipairs(wheels) do
wheel.CylindricalConstraint.MotorMaxTorque = torque
wheel.CylindricalConstraint.AngularVelocity = angularVelocity
end
end
end)
elseif seatPart.Parent.Name == "Flyer" or seatPart.Parent.Name == "Carrier" then
local vehicle = seatPart.Parent
local primaryPart = vehicle.PrimaryPart
vectorForce = primaryPart.VectorForce
pilotForce.Attachment0 = primaryPart:FindFirstChild("CenterOfMass")
alignOrientation = primaryPart.AlignOrientation
local maxSpeed = seatPart.MaxSpeed
local seats = getPassengerSeats(vehicle:GetChildren())
local forceValue = force * maxSpeed
speedChange = seatPart:GetPropertyChangedSignal("MaxSpeed"):Connect(function()
maxSpeed = seatPart.MaxSpeed
forceValue = force * maxSpeed
seatPart.ThrottleFloat = 0
end)
sitConnection = UIS.InputBegan:Connect(function(input, GPE)
if GPE then return end
if input.KeyCode == Enum.KeyCode.F then
if flightConnection == nil then
vectorForce.Enabled = true
pilotForce.Enabled = true
alignOrientation.CFrame = primaryPart.CFrame
alignOrientation.Enabled = true
inputStart = UIS.InputBegan:Connect(function(input, GPE)
if GPE then return end
if input.KeyCode == Enum.KeyCode.E then
yAxis = 1
elseif input.KeyCode == Enum.KeyCode.Q then
yAxis = -1
elseif input.KeyCode == Enum.KeyCode.LeftShift then
boosting = true
boost:FireServer(boosting, seatPart.Parent)
TS:Create(workspace.Camera, tweenInfo, {FieldOfView = 100}):Play()
end
end)
inputEnd = UIS.InputEnded:Connect(function(input, GPE)
if GPE then return end
if input.KeyCode == Enum.KeyCode.E or input.KeyCode == Enum.KeyCode.Q then
yAxis = 0
elseif input.KeyCode == Enum.KeyCode.LeftShift then
boosting = false
boost:FireServer(boosting, seatPart.Parent)
TS:Create(workspace.Camera, tweenInfo, {FieldOfView = 70}):Play()
end
end)
flightConnection = run.Heartbeat:Connect(function(deltaTime)
local mousePosition = UIS:GetMouseLocation()
local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
local passengers = {}
for _, passenger in pairs(seats) do
if passenger.Occupant then
table.insert(passengers, passenger.Occupant.Parent)
end
end
raycastParams.FilterDescendantsInstances = {vehicle, character, passengers}
local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
if raycastResult then
alignOrientation.CFrame = CFrame.lookAt(primaryPart.Position, raycastResult.Position)
else
alignOrientation.CFrame = CFrame.new(Vector3.new(0, 0, 0), mouseRay.Direction)
end
vectorForce.Force = (gravity - gravity / 25) * primaryPart.AssemblyMass
local moveVector = Vector3.new(seatPart.SteerFloat, yAxis, -seatPart.ThrottleFloat)
if moveVector.Magnitude > 0 then moveVector = moveVector.Unit end
pilotForce.Force = moveVector * forceValue * primaryPart.AssemblyMass
if primaryPart.AssemblyLinearVelocity.Magnitude > 0 then
local dragVector = -primaryPart.AssemblyLinearVelocity.Unit * primaryPart.AssemblyLinearVelocity.Magnitude ^ 1.2
vectorForce.Force += dragVector * drag * primaryPart.AssemblyMass
end
end)
else
vectorForce.Enabled = false
pilotForce.Enabled = false
alignOrientation.Enabled = false
flightConnection:Disconnect()
flightConnection = nil
end
end
end)
end
elseif seatPart.Name == "TurretSeat" then
local vehicle = seatPart.Parent
alignOrientation = vehicle.TurretBarrel.AlignOrientation
alignOrientation.Enabled = true
local seats = getPassengerSeats(vehicle:GetChildren())
inputStart = UIS.InputBegan:Connect(function(input, GPE)
if GPE then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
firing = true
while firing do
task.wait()
fire:FireServer(vehicle)
end
end
end)
inputEnd = UIS.InputEnded:Connect(function(input, GPE)
if GPE then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
firing = false
end
end)
sitConnection = run.RenderStepped:Connect(function()
local mousePosition = UIS:GetMouseLocation()
local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
local passengers = {}
for _, passenger in pairs(seats) do
if passenger.Occupant then
table.insert(passengers, passenger.Occupant.Parent)
end
end
raycastParams.FilterDescendantsInstances = {vehicle, character, passengers}
local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
if raycastResult then
alignOrientation.CFrame = CFrame.lookAt(alignOrientation.Parent.Position, raycastResult.Position)
else
alignOrientation.CFrame = CFrame.new(Vector3.new(0, 0, 0), mouseRay.Direction)
end
end)
end
end
end)
Server Script - Located in ServerScriptService:
local CS = game:GetService("CollectionService")
local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local players = game:GetService("Players")
local debris = game:GetService("Debris")
local run = game:GetService("RunService")
local events = RS:WaitForChild("Events")
local boost = events:WaitForChild("Boost")
local BD = events:WaitForChild("BoostDepleted")
local drift = events:WaitForChild("Drift")
local fire = events:WaitForChild("FireTurret")
local vehicles = SS:WaitForChild("Vehicles")
local config = vehicles:WaitForChild("Config")
local projectiles = SS:WaitForChild("Projectiles")
local turretBullet = projectiles:WaitForChild("Turret")
local landVehicles = CS:GetTagged("Land Vehicle")
local airVehicles = CS:GetTagged("Air Vehicle")
local ATVs = CS:GetTagged("ATV")
local wheelers = CS:GetTagged("Wheeler")
local flyers = CS:GetTagged("Flyer")
local carriers = CS:GetTagged("Carrier")
local seats = CS:GetTagged("Vehicle Seat")
local healths = {}
local boostConnections = {}
local cooldowns = {}
local lastFired = {}
function getBodyParts(array)
local result = {}
for _, child in ipairs(array) do
if child.ClassName == "Part" or child.ClassName == "MeshPart" or child.ClassName == "UnionOperation" then
table.insert(result, child)
end
end
return result
end
function getDriftWheels(array)
local result = {}
for _, child in ipairs(array) do
if child.ClassName == "Part" then
if child.Shape == Enum.PartType.Cylinder and not (child.Name == "WheelFL" or child.Name == "WheelFR") then
table.insert(result, child)
end
end
end
return result
end
function getSeats(array)
local result = {}
for _, child in ipairs(array) do
if child.ClassName == "VehicleSeat" or child.ClassName == "Seat" then
table.insert(result, child)
end
end
return result
end
function getOccupants(array)
local result = {}
for _, child in ipairs(array) do
table.insert(result, child.Occupant)
end
return result
end
function onLandVehicleAdded(vehicle)
if vehicle.ClassName == "Model" then
local body = vehicle.PrimaryPart
local vehicleConfig = config:FindFirstChild(vehicle.Name)
local maxHealth = vehicleConfig.Health
local maxSpeed = vehicleConfig.Speed
healths[vehicle] = maxHealth.Value
local parts = getBodyParts(vehicle:GetChildren())
local driftWheels = getDriftWheels(vehicle:GetChildren())
local seats = getSeats(vehicle:GetChildren())
local occupants = getOccupants(seats)
local MAX_HEALTH = maxHealth.Value
local MAX_SPEED = maxSpeed.Value
local FRICTION = vehicle.WheelBR.CustomPhysicalProperties.Friction
cooldowns[vehicle] = vehicleConfig.Duration.Value
local healthDebounce = false
local damageDebounce = false
local connection = nil
local timeframe = nil
for _, part in pairs(parts) do
part.Touched:Connect(function(otherPart)
if otherPart.Name == "Projectile" and healthDebounce == false then
healthDebounce = true
healths[vehicle] -= 10
if healths[vehicle] <= 0 then
for _, attachment in pairs(vehicle:GetDescendants()) do
if attachment.ClassName == "Attachment" or attachment.ClassName == "SpringConstraint" or attachment.ClassName == "CylindricalConstraint" or attachment.ClassName == "WeldConstraint" then
attachment:Destroy()
end
end
local boom = Instance.new("Explosion", body)
boom.BlastRadius = body.Size.Z
boom.ExplosionType = Enum.ExplosionType.NoCraters
boom.DestroyJointRadiusPercent = 0
boom.Position = body.Position
boom.Hit:Connect(function(obj, dist)
local humanoid = obj.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health -= 150 / (dist / 2)
end
end)
debris:AddItem(vehicle, 5)
healths[vehicle] = nil
lastFired[vehicle] = nil
cooldowns[vehicle] = nil
elseif healths[vehicle] <= MAX_HEALTH / 2 then
body.ParticleAttachment.Smoke.Enabled = true
if healths[vehicle] <= MAX_HEALTH / 4 then
body.ParticleAttachment.Fire.Enabled = true
end
end
task.wait()
healthDebounce = false
elseif damageDebounce == false and otherPart.Name ~= "Projectile" then
damageDebounce = true
local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
if humanoid.Sit == false and part.Velocity.Magnitude > 20 and not table.find(occupants, humanoid) then
humanoid.Health -= part.Velocity.Magnitude
end
end
task.wait()
damageDebounce = false
end
end)
end
for _, seat in pairs(seats) do
seat:GetPropertyChangedSignal("Occupant"):Connect(function()
if seat.Occupant == nil then
task.wait(1)
end
occupants = getOccupants(seats)
if connection ~= nil then connection:Disconnect() connection = nil end
if seat.Occupant ~= nil then
connection = run.Heartbeat:Connect(function()
if body.CFrame.UpVector.Y < 0 then
for _, seat in pairs(seats) do
seat.ProximityPrompt.Enabled = false
end
if timeframe and math.abs(timeframe - tick()) > 1 then
for _, occupant in pairs(occupants) do
occupant.Jump = true
end
timeframe = nil
body.ProximityPrompt.Enabled = true
elseif not timeframe then
timeframe = tick()
end
else
timeframe = nil
for _, seat in pairs(seats) do
if seat.Occupant == nil then
seat.ProximityPrompt.Enabled = true
end
end
end
end)
end
end)
end
body.ProximityPrompt.Triggered:Connect(function()
local flip = body.AlignOrientation
flip.Enabled = true
task.wait(0.5)
flip.Enabled = false
task.wait(0.25)
if body.CFrame.UpVector.Y > 0 then
for _, seat in pairs(seats) do
seat.ProximityPrompt.Enabled = true
end
body.ProximityPrompt.Enabled = false
end
end)
drift.OnServerEvent:Connect(function(player, drifting)
if table.find(occupants, player.Character.Humanoid) then
if drifting == true then
for _, wheel in pairs(driftWheels) do
wheel.CustomPhysicalProperties = PhysicalProperties.new(5, 0, 0.2, 1, 1)
end
else
for _, wheel in pairs(driftWheels) do
wheel.CustomPhysicalProperties = PhysicalProperties.new(5, FRICTION, 0.2, 1, 1)
end
end
end
end)
end
end
function onAirVehicleAdded(vehicle)
if vehicle.ClassName == "Model" then
local body = vehicle.PrimaryPart
local vehicleConfig = config:FindFirstChild(vehicle.Name)
local maxHealth = vehicleConfig.Health
local maxSpeed = vehicleConfig.Speed
healths[vehicle] = maxHealth.Value
local parts = getBodyParts(vehicle:GetChildren())
local seats = getSeats(vehicle:GetChildren())
local occupants = getOccupants(seats)
local MAX_HEALTH = maxHealth.Value
local MAX_SPEED = maxSpeed.Value
cooldowns[vehicle] = vehicleConfig.Duration.Value
local healthDebounce = false
local damageDebounce = false
for _, part in pairs(parts) do
part.Touched:Connect(function(otherPart)
if otherPart.Name == "Projectile" and healthDebounce == false then
healthDebounce = true
healths[vehicle] -= 10
if healths[vehicle] <= 0 then
for _, attachment in pairs(vehicle:GetDescendants()) do
if attachment.ClassName == "Attachment" or attachment.ClassName == "SpringConstraint" or attachment.ClassName == "CylindricalConstraint" or attachment.ClassName == "WeldConstraint" then
attachment:Destroy()
end
end
local boom = Instance.new("Explosion", body)
boom.BlastRadius = body.Size.Z
boom.ExplosionType = Enum.ExplosionType.NoCraters
boom.DestroyJointRadiusPercent = 0
boom.Position = body.Position
boom.Hit:Connect(function(obj, dist)
local humanoid = obj.Parent:FindFirstChild("Humanoid")
if humanoid then
humanoid.Health -= 150 / (dist / 2)
end
end)
debris:AddItem(vehicle, 5)
healths[vehicle] = nil
lastFired[vehicle] = nil
cooldowns[vehicle] = nil
elseif healths[vehicle] <= MAX_HEALTH / 2 then
body.ParticleAttachment.Smoke.Enabled = true
if healths[vehicle] <= MAX_HEALTH / 4 then
body.ParticleAttachment.Fire.Enabled = true
end
end
task.wait()
healthDebounce = false
elseif damageDebounce == false and otherPart.Name ~= "Projectile" then
damageDebounce = true
local humanoid = otherPart.Parent:FindFirstChildWhichIsA("Humanoid")
if humanoid then
if humanoid.Sit == false and part.Velocity.Magnitude > 20 and not table.find(occupants, humanoid) then
humanoid.Health -= part.Velocity.Magnitude
end
end
task.wait()
damageDebounce = false
end
end)
end
for _, seat in pairs(seats) do
seat:GetPropertyChangedSignal("Occupant"):Connect(function()
if seat.Occupant == nil then
task.wait(1)
end
occupants = getOccupants(seats)
end)
end
end
end
function onSeatAdded(seat)
if seat.ClassName == "Seat" or seat.ClassName == "VehicleSeat" then
local body = seat.Parent.PrimaryPart
seat:GetPropertyChangedSignal("Occupant"):Connect(function()
if seat.Occupant == nil and body.CFrame.UpVector.Y > 0 then
seat.ProximityPrompt.Enabled = true
else
seat.ProximityPrompt.Enabled = false
end
end)
seat.ProximityPrompt.Triggered:Connect(function(player)
seat:Sit(player.Character.Humanoid)
end)
end
end
players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
character.Humanoid.Seated:Connect(function(active, seatPart)
if seatPart == nil then return end
if seatPart.ClassName ~= "VehicleSeat" or seatPart.Name ~= "TurretSeat" then return end
seatPart:SetNetworkOwner(player)
print("Owner:", player.Name)
end)
end)
end)
boost.OnServerEvent:Connect(function(player, boosting, vehicle)
if table.find(getOccupants(getSeats(vehicle:GetChildren())), player.Character.Humanoid) then
if boostConnections[vehicle] ~= nil then
boostConnections[vehicle]:Disconnect()
boostConnections[vehicle] = nil
end
local vehicleConfig = config:FindFirstChild(vehicle.Name)
if not vehicleConfig then return end
if boosting == true then
boostConnections[vehicle] = run.Heartbeat:Connect(function(dt)
if cooldowns[vehicle] <= 0 then
vehicle.Drive.MaxSpeed = vehicleConfig.Speed.Value
cooldowns[vehicle] = 0
BD:FireClient(player)
boostConnections[vehicle]:Disconnect()
boostConnections[vehicle] = nil
else
vehicle.Drive.MaxSpeed = vehicleConfig.Speed.Value + vehicleConfig.Boost.Value
cooldowns[vehicle] -= dt
end
end)
else
vehicle.Drive.MaxSpeed = vehicleConfig.Speed.Value
boostConnections[vehicle] = run.Heartbeat:Connect(function(dt)
if cooldowns[vehicle] >= vehicleConfig.Duration.Value then
cooldowns[vehicle] = vehicleConfig.Duration.Value
boostConnections[vehicle]:Disconnect()
boostConnections[vehicle] = nil
else
cooldowns[vehicle] += dt
end
end)
end
end
end)
fire.OnServerEvent:Connect(function(player, vehicle)
lastFired[vehicle] = lastFired[vehicle] or 0
local vehicleConfig = config:FindFirstChild(vehicle.Name)
if not vehicleConfig then return end
if tick() - (1 / vehicleConfig.FireRate.Value) >= lastFired[vehicle] then
lastFired[vehicle] = tick()
local bullet = turretBullet:Clone()
bullet.CFrame = vehicle.TurretBarrel.FiringPort.WorldCFrame
bullet.Parent = vehicle
local speed = vehicleConfig.FireSpeed.Value
local params = RaycastParams.new()
local iterations = math.round(vehicleConfig.Range.Value / speed)
params.FilterDescendantsInstances = {vehicle, getOccupants(getSeats(vehicle:GetChildren()))}
local bulletConnection
bulletConnection = run.Heartbeat:Connect(function(deltaTime)
local position,nextPosition = bullet.Position, bullet.CFrame.LookVector * deltaTime * speed
local result = workspace:Raycast(position, nextPosition, params)
if result then
bulletConnection:Disconnect()
bullet:Destroy()
local human = result.Instance.Parent:FindFirstChildWhichIsA("Humanoid")
if human and human.Health > 0 and not players:GetPlayerFromCharacter(human.Parent) then
human:TakeDamage(math.random(vehicleConfig.Damage.Value / 2, vehicleConfig.Damage.Value * 3/2))
end
else
bullet.Position = position + nextPosition
end
iterations -= 1
if iterations < 0 then
bulletConnection:Disconnect()
bullet:Destroy()
end
end)
end
end)
CS:GetInstanceAddedSignal("Land Vehicle"):Connect(onLandVehicleAdded)
CS:GetInstanceAddedSignal("Air Vehicle"):Connect(onAirVehicleAdded)
CS:GetInstanceAddedSignal("Vehicle Seat"):Connect(onSeatAdded)
for _, vehicle in ipairs(landVehicles) do onLandVehicleAdded(vehicle) end
for _, vehicle in ipairs(airVehicles) do onAirVehicleAdded(vehicle) end
for _, seat in ipairs(seats) do onSeatAdded(seat) end