Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local player = game.Players.LocalPlayer
- local mouse = player:GetMouse()
- local replicatedStorage = game:GetService("ReplicatedStorage")
- local buildModels = replicatedStorage:WaitForChild("BuildModels")
- local UIS = game:GetService("UserInputService")
- local TS = game:GetService("TweenService")
- local buildsGui = script.Parent:WaitForChild("Builds")
- local remotePlace = replicatedStorage:WaitForChild("PlaceBuildRemote")
- local currentButton = nil
- local selectedModelName = nil
- local previewModel = nil
- local rotationAngle = 0
- local placedPositions = {} -- [Vector3 as string] = true
- local trashButton = script.Parent:WaitForChild("TrashButtonBackground"):WaitForChild("TrashButton")
- local deleteRemote = replicatedStorage:WaitForChild("DeleteBuildRemote")
- local selectionTemplate = replicatedStorage:WaitForChild("SelectionBox")
- local deleteMode = false
- local hoverHighlight = {}
- local btnDebounce = false
- local TweenService = game:GetService("TweenService")
- local buildButton = script.Parent:WaitForChild("BuildButtonBackground"):WaitForChild("BuildButton")
- local buildsFrame = script.Parent:WaitForChild("Builds")
- local isOpen = false
- -- Initial hidden state
- buildsFrame.Position = UDim2.new(0.5, 0, 0.984, 0)
- buildsFrame.Size = UDim2.new(0, 0, 0, 0)
- -- Define animation targets
- local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
- local openPosition = UDim2.new(0.5, 0, 0.873, 0)
- local closedPosition = UDim2.new(0.5, 0, 0.984, 0)
- local openSize = UDim2.new(0.423, 0, 0.086, 0)
- local closedSize = UDim2.new(0, 0, 0, 0)
- local prices = {
- AsphaltDropper = 15,
- BasaltDropper = 70,
- BrickDropper = 350,
- CardboardDropper = 1750,
- CarpetDropper = 9000,
- StraightConveyor = 15,
- RCurvedConveyor = 100,
- LCurvedConveyor = 100,
- TShapedConveyor = 200,
- Sell = 70,
- UpgradingConveyor = 1000000
- }
- -- Function to clear all UIStrokes
- local function clearStrokes()
- for _, button in pairs(buildsGui:GetChildren()) do
- if button:IsA("ImageButton") or button:IsA("TextButton") then
- local stroke = button:FindFirstChild("UIStroke")
- if stroke then stroke:Destroy() end
- end
- end
- end
- -- Select a build button
- -- Snap to 4x4 grid
- local function snapToGrid(pos)
- local gridSize = 4
- return Vector3.new(
- math.floor(pos.X / gridSize + 0.5) * gridSize,
- 0.5,
- math.floor(pos.Z / gridSize + 0.5) * gridSize
- )
- end
- -- Tween model to position
- local function tweenModelTo(model, targetCFrame)
- if not model.PrimaryPart then return end
- -- Use CFrameValue to tween model's PivotTo
- local cframeVal = Instance.new("CFrameValue")
- cframeVal.Value = model:GetPivot()
- cframeVal:GetPropertyChangedSignal("Value"):Connect(function()
- if model and model.Parent then
- model:PivotTo(cframeVal.Value)
- -- right after model:PivotTo(...)
- model:SetAttribute("Owner", player.UserId)
- end
- end)
- local tween = TS:Create(cframeVal, TweenInfo.new(0.1), {Value = targetCFrame})
- tween:Play()
- -- Cleanup after tween
- tween.Completed:Connect(function()
- cframeVal:Destroy()
- end)
- end
- -- Update preview position
- local lastCFrame = nil
- local function updatePreview()
- if not previewModel or not previewModel.PrimaryPart then return end
- local mouseHit = mouse.Hit
- if not mouseHit then return end
- local targetPos = snapToGrid(mouseHit.Position)
- local targetCFrame = CFrame.new(targetPos) * CFrame.Angles(0, math.rad(rotationAngle), 0)
- if not lastCFrame or (lastCFrame.Position - targetCFrame.Position).Magnitude > 0.01 or lastCFrame.Rotation ~= targetCFrame.Rotation then
- tweenModelTo(previewModel, targetCFrame)
- lastCFrame = targetCFrame
- end
- end
- -- Left click to place
- mouse.Button1Down:Connect(function()
- if not previewModel or not selectedModelName then return end
- local finalCFrame = previewModel:GetPrimaryPartCFrame()
- local pos = snapToGrid(finalCFrame.Position)
- local key = tostring(pos)
- if placedPositions[key] then
- warn("Cannot place here - already occupied")
- return
- end
- local cost = prices[selectedModelName]
- if not cost then return end
- -- Fire placement to server
- remotePlace:FireServer(selectedModelName, finalCFrame, cost)
- -- Mark spot as taken
- placedPositions[key] = true
- -- Cleanup
- previewModel:Destroy()
- previewModel = nil
- clearStrokes()
- selectedModelName = nil
- end)
- -- Rotate with R
- UIS.InputBegan:Connect(function(input, gameProcessed)
- if gameProcessed then return end
- if input.KeyCode == Enum.KeyCode.R and previewModel then
- rotationAngle = (rotationAngle + 90) % 360
- end
- end)
- -- Update preview model
- game:GetService("RunService").RenderStepped:Connect(function()
- if previewModel then
- updatePreview()
- end
- end)
- local function selectBuild(button)
- -- Always turn off delete mode and clear hover highlights
- deleteMode = false
- for part, box in pairs(hoverHighlight) do
- box:Destroy()
- end
- hoverHighlight = {}
- -- Optional: reset trash button visuals (if using UIStroke or any indicator)
- local trashStroke = trashButton:FindFirstChild("UIStroke")
- if trashStroke then
- trashStroke:Destroy()
- end
- -- Clear UI strokes on all buttons
- clearStrokes()
- -- Highlight selected build button
- local stroke = Instance.new("UIStroke")
- stroke.Thickness = 3
- stroke.Color = Color3.fromRGB(0, 255, 0)
- stroke.Name = "UIStroke"
- stroke.Parent = button
- selectedModelName = button.Name
- currentButton = button
- rotationAngle = 0
- if previewModel then
- previewModel:Destroy()
- end
- local modelTemplate = buildModels:FindFirstChild(selectedModelName)
- if modelTemplate then
- previewModel = modelTemplate:Clone()
- for _, part in pairs(previewModel:GetDescendants()) do
- if part:IsA("BasePart") then
- part.Transparency = 0.5
- part.CanCollide = false
- end
- end
- previewModel.Parent = workspace
- end
- end
- -- Connect GUI buttons
- for _, button in pairs(buildsGui:GetChildren()) do
- if button:IsA("ImageButton") or button:IsA("TextButton") then
- button.MouseButton1Click:Connect(function()
- selectBuild(button)
- end)
- end
- end
- ---------------------------------------------------
- -- Toggle open/close
- local function toggleBuildsMenu()
- isOpen = not isOpen
- -- Menu animation
- local positionGoal = isOpen and openPosition or closedPosition
- local sizeGoal = isOpen and openSize or closedSize
- local positionTween = TweenService:Create(buildsFrame, tweenInfo, {Position = positionGoal})
- local sizeTween = TweenService:Create(buildsFrame, tweenInfo, {Size = sizeGoal})
- positionTween:Play()
- sizeTween:Play()
- -- Button rotation animation
- local rotateTween1 = TweenService:Create(buildButton, TweenInfo.new(0.25), {Rotation = 80})
- local rotateTween2 = TweenService:Create(buildButton, TweenInfo.new(0.25), {Rotation = 0})
- rotateTween1:Play()
- rotateTween1.Completed:Connect(function()
- rotateTween2:Play()
- end)
- end
- buildButton.MouseButton1Click:Connect(toggleBuildsMenu)
- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- trashButton.MouseButton1Click:Connect(function()
- if btnDebounce then return end
- btnDebounce = true
- -- calculate 10% larger size
- local orig = trashButton.Size
- local upSize = UDim2.new(orig.X.Scale * 1.1, orig.X.Offset, orig.Y.Scale * 1.1, orig.Y.Offset)
- local downSize = orig
- -- tween button scale up then back
- local up = TweenService:Create(trashButton, TweenInfo.new(0.1), {Size = upSize})
- local down = TweenService:Create(trashButton, TweenInfo.new(0.1), {Size = downSize})
- up:Play()
- up.Completed:Connect(function()
- down:Play()
- end)
- -- flip deleteMode & close build menu if needed
- deleteMode = not deleteMode
- if deleteMode and isOpen then
- toggleBuildsMenu()
- end
- wait(0.2)
- btnDebounce = false
- end)
- game:GetService("RunService").RenderStepped:Connect(function()
- if not deleteMode then return end
- local target = mouse.Target and mouse.Target:FindFirstAncestorOfClass("Model")
- -- clear old highlights
- for part, box in pairs(hoverHighlight) do
- box:Destroy()
- hoverHighlight[part] = nil
- end
- if target then
- for _, part in ipairs(target:GetDescendants()) do
- if part:IsA("BasePart") then
- local box = selectionTemplate:Clone()
- box.Adornee = part
- box.Parent = part
- hoverHighlight[part] = box
- end
- end
- end
- end)
- mouse.Button1Down:Connect(function()
- if not deleteMode then return end
- local targetModel = mouse.Target and mouse.Target:FindFirstAncestorOfClass("Model")
- if not targetModel then return end
- -- compute grid key BEFORE destroying (so we can get its PrimaryPart)
- local cframe = targetModel:GetPrimaryPartCFrame()
- local pos = snapToGrid(cframe.Position)
- local key = tostring(pos)
- -- immediately destroy locally (stops client scripts/functionality)
- targetModel:Destroy()
- -- free up the slot locally
- placedPositions[key] = nil
- -- notify server: it will refund & destroy on the server side
- deleteRemote:FireServer(targetModel, key)
- -- clear any lingering highlights
- for part, box in pairs(hoverHighlight) do
- box:Destroy()
- hoverHighlight[part] = nil
- end
- end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement