Advertisement
bootyeater69420

Localscript Tycoon Building System

Jul 12th, 2025
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.11 KB | Writing | 0 0
  1. local player = game.Players.LocalPlayer
  2. local mouse = player:GetMouse()
  3. local replicatedStorage = game:GetService("ReplicatedStorage")
  4. local buildModels = replicatedStorage:WaitForChild("BuildModels")
  5. local UIS = game:GetService("UserInputService")
  6. local TS = game:GetService("TweenService")
  7.  
  8. local buildsGui = script.Parent:WaitForChild("Builds")
  9. local remotePlace = replicatedStorage:WaitForChild("PlaceBuildRemote")
  10.  
  11. local currentButton = nil
  12. local selectedModelName = nil
  13. local previewModel = nil
  14. local rotationAngle = 0
  15. local placedPositions = {} -- [Vector3 as string] = true
  16. local trashButton = script.Parent:WaitForChild("TrashButtonBackground"):WaitForChild("TrashButton")
  17. local deleteRemote = replicatedStorage:WaitForChild("DeleteBuildRemote")
  18. local selectionTemplate = replicatedStorage:WaitForChild("SelectionBox")
  19. local deleteMode = false
  20. local hoverHighlight = {}
  21. local btnDebounce = false
  22.  
  23. local TweenService = game:GetService("TweenService")
  24.  
  25. local buildButton = script.Parent:WaitForChild("BuildButtonBackground"):WaitForChild("BuildButton")
  26. local buildsFrame = script.Parent:WaitForChild("Builds")
  27.  
  28. local isOpen = false
  29.  
  30. -- Initial hidden state
  31. buildsFrame.Position = UDim2.new(0.5, 0, 0.984, 0)
  32. buildsFrame.Size = UDim2.new(0, 0, 0, 0)
  33.  
  34. -- Define animation targets
  35. local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
  36. local openPosition = UDim2.new(0.5, 0, 0.873, 0)
  37. local closedPosition = UDim2.new(0.5, 0, 0.984, 0)
  38. local openSize = UDim2.new(0.423, 0, 0.086, 0)
  39. local closedSize = UDim2.new(0, 0, 0, 0)
  40.  
  41.  
  42. local prices = {
  43.     AsphaltDropper = 15,
  44.     BasaltDropper = 70,
  45.     BrickDropper = 350,
  46.     CardboardDropper = 1750,
  47.     CarpetDropper = 9000,
  48.     StraightConveyor = 15,
  49.     RCurvedConveyor = 100,
  50.     LCurvedConveyor = 100,
  51.     TShapedConveyor = 200,
  52.     Sell = 70,
  53.     UpgradingConveyor = 1000000
  54. }
  55.  
  56. -- Function to clear all UIStrokes
  57. local function clearStrokes()
  58.     for _, button in pairs(buildsGui:GetChildren()) do
  59.         if button:IsA("ImageButton") or button:IsA("TextButton") then
  60.             local stroke = button:FindFirstChild("UIStroke")
  61.             if stroke then stroke:Destroy() end
  62.         end
  63.     end
  64. end
  65.  
  66. -- Select a build button
  67.  
  68.  
  69. -- Snap to 4x4 grid
  70. local function snapToGrid(pos)
  71.     local gridSize = 4
  72.     return Vector3.new(
  73.         math.floor(pos.X / gridSize + 0.5) * gridSize,
  74.         0.5,
  75.         math.floor(pos.Z / gridSize + 0.5) * gridSize
  76.     )
  77. end
  78.  
  79. -- Tween model to position
  80. local function tweenModelTo(model, targetCFrame)
  81.     if not model.PrimaryPart then return end
  82.  
  83.     -- Use CFrameValue to tween model's PivotTo
  84.     local cframeVal = Instance.new("CFrameValue")
  85.     cframeVal.Value = model:GetPivot()
  86.  
  87.     cframeVal:GetPropertyChangedSignal("Value"):Connect(function()
  88.         if model and model.Parent then
  89.             model:PivotTo(cframeVal.Value)
  90.             -- right after model:PivotTo(...)
  91.             model:SetAttribute("Owner", player.UserId)
  92.         end
  93.     end)
  94.  
  95.     local tween = TS:Create(cframeVal, TweenInfo.new(0.1), {Value = targetCFrame})
  96.     tween:Play()
  97.  
  98.     -- Cleanup after tween
  99.     tween.Completed:Connect(function()
  100.         cframeVal:Destroy()
  101.     end)
  102. end
  103.  
  104.  
  105. -- Update preview position
  106. local lastCFrame = nil
  107. local function updatePreview()
  108.     if not previewModel or not previewModel.PrimaryPart then return end
  109.  
  110.     local mouseHit = mouse.Hit
  111.     if not mouseHit then return end
  112.  
  113.     local targetPos = snapToGrid(mouseHit.Position)
  114.     local targetCFrame = CFrame.new(targetPos) * CFrame.Angles(0, math.rad(rotationAngle), 0)
  115.  
  116.     if not lastCFrame or (lastCFrame.Position - targetCFrame.Position).Magnitude > 0.01 or lastCFrame.Rotation ~= targetCFrame.Rotation then
  117.         tweenModelTo(previewModel, targetCFrame)
  118.         lastCFrame = targetCFrame
  119.     end
  120. end
  121.  
  122.  
  123. -- Left click to place
  124. mouse.Button1Down:Connect(function()
  125.     if not previewModel or not selectedModelName then return end
  126.  
  127.     local finalCFrame = previewModel:GetPrimaryPartCFrame()
  128.     local pos = snapToGrid(finalCFrame.Position)
  129.     local key = tostring(pos)
  130.  
  131.     if placedPositions[key] then
  132.         warn("Cannot place here - already occupied")
  133.         return
  134.     end
  135.  
  136.     local cost = prices[selectedModelName]
  137.     if not cost then return end
  138.  
  139.     -- Fire placement to server
  140.     remotePlace:FireServer(selectedModelName, finalCFrame, cost)
  141.  
  142.     -- Mark spot as taken
  143.     placedPositions[key] = true
  144.  
  145.     -- Cleanup
  146.     previewModel:Destroy()
  147.     previewModel = nil
  148.     clearStrokes()
  149.     selectedModelName = nil
  150. end)
  151.  
  152.  
  153. -- Rotate with R
  154. UIS.InputBegan:Connect(function(input, gameProcessed)
  155.     if gameProcessed then return end
  156.     if input.KeyCode == Enum.KeyCode.R and previewModel then
  157.         rotationAngle = (rotationAngle + 90) % 360
  158.     end
  159. end)
  160.  
  161. -- Update preview model
  162. game:GetService("RunService").RenderStepped:Connect(function()
  163.     if previewModel then
  164.         updatePreview()
  165.     end
  166. end)
  167.  
  168. local function selectBuild(button)
  169.     -- Always turn off delete mode and clear hover highlights
  170.     deleteMode = false
  171.     for part, box in pairs(hoverHighlight) do
  172.         box:Destroy()
  173.     end
  174.     hoverHighlight = {}
  175.  
  176.     -- Optional: reset trash button visuals (if using UIStroke or any indicator)
  177.     local trashStroke = trashButton:FindFirstChild("UIStroke")
  178.     if trashStroke then
  179.         trashStroke:Destroy()
  180.     end
  181.  
  182.     -- Clear UI strokes on all buttons
  183.     clearStrokes()
  184.  
  185.     -- Highlight selected build button
  186.     local stroke = Instance.new("UIStroke")
  187.     stroke.Thickness = 3
  188.     stroke.Color = Color3.fromRGB(0, 255, 0)
  189.     stroke.Name = "UIStroke"
  190.     stroke.Parent = button
  191.  
  192.     selectedModelName = button.Name
  193.     currentButton = button
  194.     rotationAngle = 0
  195.  
  196.     if previewModel then
  197.         previewModel:Destroy()
  198.     end
  199.  
  200.     local modelTemplate = buildModels:FindFirstChild(selectedModelName)
  201.     if modelTemplate then
  202.         previewModel = modelTemplate:Clone()
  203.         for _, part in pairs(previewModel:GetDescendants()) do
  204.             if part:IsA("BasePart") then
  205.                 part.Transparency = 0.5
  206.                 part.CanCollide = false
  207.             end
  208.         end
  209.         previewModel.Parent = workspace
  210.     end
  211. end
  212.  
  213.  
  214. -- Connect GUI buttons
  215. for _, button in pairs(buildsGui:GetChildren()) do
  216.     if button:IsA("ImageButton") or button:IsA("TextButton") then
  217.         button.MouseButton1Click:Connect(function()
  218.             selectBuild(button)
  219.         end)
  220.     end
  221. end
  222.  
  223.  
  224. ---------------------------------------------------
  225.  
  226. -- Toggle open/close
  227. local function toggleBuildsMenu()
  228.     isOpen = not isOpen
  229.  
  230.     -- Menu animation
  231.     local positionGoal = isOpen and openPosition or closedPosition
  232.     local sizeGoal = isOpen and openSize or closedSize
  233.  
  234.     local positionTween = TweenService:Create(buildsFrame, tweenInfo, {Position = positionGoal})
  235.     local sizeTween = TweenService:Create(buildsFrame, tweenInfo, {Size = sizeGoal})
  236.  
  237.     positionTween:Play()
  238.     sizeTween:Play()
  239.  
  240.     -- Button rotation animation
  241.     local rotateTween1 = TweenService:Create(buildButton, TweenInfo.new(0.25), {Rotation = 80})
  242.     local rotateTween2 = TweenService:Create(buildButton, TweenInfo.new(0.25), {Rotation = 0})
  243.  
  244.     rotateTween1:Play()
  245.     rotateTween1.Completed:Connect(function()
  246.         rotateTween2:Play()
  247.     end)
  248. end
  249.  
  250. buildButton.MouseButton1Click:Connect(toggleBuildsMenu)
  251.  
  252. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  253.  
  254. trashButton.MouseButton1Click:Connect(function()
  255.     if btnDebounce then return end
  256.     btnDebounce = true
  257.  
  258.     -- calculate 10% larger size
  259.     local orig = trashButton.Size
  260.     local upSize = UDim2.new(orig.X.Scale * 1.1, orig.X.Offset, orig.Y.Scale * 1.1, orig.Y.Offset)
  261.     local downSize = orig
  262.  
  263.     -- tween button scale up then back
  264.     local up = TweenService:Create(trashButton, TweenInfo.new(0.1), {Size = upSize})
  265.     local down = TweenService:Create(trashButton, TweenInfo.new(0.1), {Size = downSize})
  266.  
  267.     up:Play()
  268.     up.Completed:Connect(function()
  269.         down:Play()
  270.     end)
  271.  
  272.     -- flip deleteMode & close build menu if needed
  273.     deleteMode = not deleteMode
  274.     if deleteMode and isOpen then
  275.         toggleBuildsMenu()
  276.     end
  277.  
  278.     wait(0.2)
  279.     btnDebounce = false
  280. end)
  281.  
  282. game:GetService("RunService").RenderStepped:Connect(function()
  283.     if not deleteMode then return end
  284.  
  285.     local target = mouse.Target and mouse.Target:FindFirstAncestorOfClass("Model")
  286.     -- clear old highlights
  287.     for part, box in pairs(hoverHighlight) do
  288.         box:Destroy()
  289.         hoverHighlight[part] = nil
  290.     end
  291.  
  292.     if target then
  293.         for _, part in ipairs(target:GetDescendants()) do
  294.             if part:IsA("BasePart") then
  295.                 local box = selectionTemplate:Clone()
  296.                 box.Adornee = part
  297.                 box.Parent = part
  298.                 hoverHighlight[part] = box
  299.             end
  300.         end
  301.     end
  302. end)
  303.  
  304. mouse.Button1Down:Connect(function()
  305.     if not deleteMode then return end
  306.  
  307.     local targetModel = mouse.Target and mouse.Target:FindFirstAncestorOfClass("Model")
  308.     if not targetModel then return end
  309.  
  310.     -- compute grid key BEFORE destroying (so we can get its PrimaryPart)
  311.     local cframe = targetModel:GetPrimaryPartCFrame()
  312.     local pos = snapToGrid(cframe.Position)
  313.     local key = tostring(pos)
  314.  
  315.     -- immediately destroy locally (stops client scripts/functionality)
  316.     targetModel:Destroy()
  317.  
  318.     -- free up the slot locally
  319.     placedPositions[key] = nil
  320.  
  321.     -- notify server: it will refund & destroy on the server side
  322.     deleteRemote:FireServer(targetModel, key)
  323.  
  324.     -- clear any lingering highlights
  325.     for part, box in pairs(hoverHighlight) do
  326.         box:Destroy()
  327.         hoverHighlight[part] = nil
  328.     end
  329. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement