-- src/StarterPlayer/StarterCharacterScripts/BuildController.client.lua local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local BuildingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("BuildingConfig")) local PlaceBlueprintEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("PlaceBlueprintEvent") local player = Players.LocalPlayer local mouse = player:GetMouse() local character = player.Character or player.CharacterAdded:Wait() local buildModeActive = false local currentStructureIndex = 1 local currentStructure = BuildingConfig.StructureOrder[1] local currentRotation = 0 local previewModel = nil local function snapToGrid(position, gridSize) return Vector3.new( math.floor((position.X + gridSize/2) / gridSize) * gridSize, position.Y, math.floor((position.Z + gridSize/2) / gridSize) * gridSize ) end local function getStructureSize() local config = BuildingConfig.Structures[currentStructure] if config and config.Size then return config.Size end return Vector3.new(10, 10, 1) end local function cleanupPreview() if previewModel then previewModel:Destroy() previewModel = nil end end local function createPreview() cleanupPreview() local size = getStructureSize() previewModel = Instance.new("Part") previewModel.Size = size previewModel.Transparency = 0.5 previewModel.BrickColor = BrickColor.new("Neon blue") previewModel.CanCollide = false previewModel.Anchored = true previewModel.Material = Enum.Material.ForceField previewModel.Parent = workspace.Terrain -- Add label local billboard = Instance.new("BillboardGui") billboard.Size = UDim2.new(0, 200, 0, 50) billboard.StudsOffset = Vector3.new(0, size.Y/2 + 2, 0) billboard.AlwaysOnTop = true billboard.Parent = previewModel local label = Instance.new("TextLabel") label.Name = "StructureLabel" label.Size = UDim2.new(1, 0, 1, 0) label.BackgroundTransparency = 0.3 label.BackgroundColor3 = Color3.new(0, 0, 0) label.TextColor3 = Color3.new(1, 1, 1) label.TextScaled = true label.Font = Enum.Font.GothamBold local config = BuildingConfig.Structures[currentStructure] label.Text = (config and config.Name or currentStructure) .. " [Q] Cycle [R] Rotate" label.Parent = billboard end local function toggleBuildMode() buildModeActive = not buildModeActive if not buildModeActive then cleanupPreview() else createPreview() end end local function cycleStructure() currentStructureIndex = currentStructureIndex + 1 if currentStructureIndex > #BuildingConfig.StructureOrder then currentStructureIndex = 1 end currentStructure = BuildingConfig.StructureOrder[currentStructureIndex] if buildModeActive then createPreview() end end local function updatePreview() if not buildModeActive or not previewModel then return end local char = player.Character if char and char:FindFirstChild("HumanoidRootPart") then local distance = (char.HumanoidRootPart.Position - mouse.Hit.Position).Magnitude if distance <= BuildingConfig.MaxPlacementDistance then local snappedPos = snapToGrid(mouse.Hit.Position, BuildingConfig.GridSnap) snappedPos = Vector3.new(snappedPos.X, mouse.Hit.Position.Y + (previewModel.Size.Y / 2), snappedPos.Z) previewModel.CFrame = CFrame.new(snappedPos) * CFrame.Angles(0, math.rad(currentRotation), 0) previewModel.BrickColor = BrickColor.new("Neon blue") else previewModel.Position = mouse.Hit.Position previewModel.BrickColor = BrickColor.new("Really red") end end end local function onInputBegan(input, gameProcessed) if gameProcessed then return end if input.KeyCode == Enum.KeyCode.B then toggleBuildMode() end if input.KeyCode == Enum.KeyCode.Q and buildModeActive then cycleStructure() end if input.KeyCode == Enum.KeyCode.R and buildModeActive then currentRotation = (currentRotation + 90) % 360 end if input.UserInputType == Enum.UserInputType.MouseButton1 and buildModeActive then if previewModel and previewModel.BrickColor.Name == "Neon blue" then PlaceBlueprintEvent:FireServer(currentStructure, previewModel.CFrame) end end end player.CharacterAdded:Connect(function(char) character = char end) UserInputService.InputBegan:Connect(onInputBegan) RunService.RenderStepped:Connect(function() if buildModeActive then updatePreview() end end)