-- src/ServerScriptService/DroneAutomationManager.server.lua local CollectionService = game:GetService("CollectionService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local AutomationConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("AutomationConfig")) local activeDrones = {} local function getPartVolume(part) return part.Size.X * part.Size.Y * part.Size.Z end local function spawnDrone(droneType, startZone, targetZone) local config = AutomationConfig.Drones[droneType] if not config then return end -- In real game, clone from ReplicatedStorage local droneModel = Instance.new("Part") droneModel.Name = "DeliveryDrone" droneModel.Size = Vector3.new(4, 1, 4) droneModel.BrickColor = BrickColor.new("Dark stone grey") droneModel.Anchored = true droneModel.CFrame = startZone.CFrame + Vector3.new(0, 5, 0) droneModel:SetAttribute("TargetZone", targetZone.Name) droneModel.Parent = workspace local droneData = { model = droneModel, config = config, state = "Idle", -- Idle, Loading, Traveling, Unloading startPos = droneModel.Position, targetPos = targetZone.Position + Vector3.new(0, 10, 0), carriedItem = nil } table.insert(activeDrones, droneData) return droneData end -- Simplified Drone AI Loop RunService.Heartbeat:Connect(function(dt) for i, drone in ipairs(activeDrones) do local model = drone.model if not model or not model.Parent then table.remove(activeDrones, i) continue end if drone.state == "Traveling" then local direction = (drone.targetPos - model.Position).Unit local distance = (drone.targetPos - model.Position).Magnitude local moveStep = math.min(drone.config.Speed * dt, distance) model.Position = model.Position + (direction * moveStep) if drone.carriedItem and drone.carriedItem.Parent then drone.carriedItem.Position = model.Position - Vector3.new(0, 3, 0) end if distance < 1 then drone.state = "Unloading" -- Drop item if drone.carriedItem then drone.carriedItem.Anchored = false drone.carriedItem = nil end -- Return Home local temp = drone.startPos drone.startPos = drone.targetPos drone.targetPos = temp drone.state = "Traveling" end end end end) -- Drone Pickup Pad Logic local function bindDronePad(pad) local debounce = {} pad.Touched:Connect(function(hit) if CollectionService:HasTag(hit, "TreeSegment") and not debounce[hit] then debounce[hit] = true local volume = getPartVolume(hit) -- Find an available idle drone assigned to this pad for _, drone in ipairs(activeDrones) do if drone.state == "Idle" and volume <= drone.config.MaxCarryWeight then drone.state = "Traveling" drone.carriedItem = hit hit.Anchored = true break end end task.delay(1, function() debounce[hit] = nil end) end end) end CollectionService:GetInstanceAddedSignal("DronePad"):Connect(bindDronePad) for _, pad in pairs(CollectionService:GetTagged("DronePad")) do bindDronePad(pad) end