Advertisement
bootyeater69420

HiddenDevs Scripter Application

Jun 25th, 2025 (edited)
42
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.50 KB | None | 0 0
  1. -- Services
  2. local TweenService = game:GetService("TweenService") -- Tweening
  3. local RunService = game:GetService("RunService") -- Provides heartbeat event for continuous updates (conveyors)
  4. local PhysicsService = game:GetService("PhysicsService")-- Manages collision groups safely
  5. local DataStoreService = game:GetService("DataStoreService") -- Persists player cash between sessions
  6. local Players = game:GetService("Players") -- Manages player connections and stats
  7. local ReplicatedStorage = game:GetService("ReplicatedStorage") -- Stores shared assets and remotes
  8.  
  9. -- Constants and configuration
  10. local DATASTORE_KEY = "CashStore" -- Identifier for our player datastore
  11. local BELT_SPEED = 10 -- Speed at which conveyor belts move parts
  12. local TWEEN_INFO = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)
  13. -- Prices used for placement validation and refunds
  14. local PRICES = {
  15. AsphaltDropper = 15,
  16. BasaltDropper = 70,
  17. BrickDropper = 350,
  18. CardboardDropper = 1750,
  19. CarpetDropper = 9000,
  20. StraightConveyor = 15,
  21. RCurvedConveyor = 100,
  22. LCurvedConveyor = 100,
  23. TShapedConveyor = 200,
  24. Sell = 70
  25. }
  26. -- Refund costs when deleting builds
  27. local COSTS = {
  28. AsphaltDropper = 15,
  29. BasaltDropper = 70,
  30. BrickDropper = 350,
  31. CardboardDropper = 1750,
  32. CarpetDropper = 9000,
  33. StraightConveyor = 15,
  34. RCurvedConveyor = 100,
  35. LCurvedConveyor = 100,
  36. TShapedConveyor = 200,
  37. Sell = 70,
  38. UpgradingConveyor = 1000000
  39. }
  40. -- How much each block pays out when sold
  41. local PAYOUTS = {
  42. [Enum.Material.Asphalt] = 1,
  43. [Enum.Material.Basalt] = 5,
  44. [Enum.Material.Brick] = 20,
  45. [Enum.Material.Cardboard] = 100,
  46. [Enum.Material.Carpet] = 500
  47. }
  48. -- Collision grouping names for separation of parts in physics engine
  49. local BLOCK_GROUP = "BlockGroup"
  50. local UPGRADE_GROUP = "UpgradeGroup"
  51.  
  52. -- Remote events and shared assets
  53. local BuildModels = ReplicatedStorage:WaitForChild("BuildModels")
  54. local PlaceRemote = ReplicatedStorage:WaitForChild("PlaceBuildRemote")
  55. local DeleteRemote = ReplicatedStorage:WaitForChild("DeleteBuildRemote")
  56.  
  57. -- Workspace references
  58. local Baseplate = workspace:WaitForChild("Baseplate")
  59.  
  60. -- In-memory tracking of active conveyor belts and grid placements per player
  61. local activeBelts = {} -- List of belt parts for movement updates
  62. local placedGridByPlayer = {} -- Tracks which grid cells are occupied by each player
  63.  
  64. -- Initialize datastore handle
  65. local dataStore = DataStoreService:GetDataStore(DATASTORE_KEY)
  66.  
  67. -- Safely register collision groups once; protect against repeated calls
  68. pcall(function() PhysicsService:RegisterCollisionGroup(BLOCK_GROUP) end)
  69. pcall(function() PhysicsService:RegisterCollisionGroup(UPGRADE_GROUP) end)
  70. -- Define non-collidable relationships: blocks don't collide with each other or upgrades
  71. PhysicsService:CollisionGroupSetCollidable(BLOCK_GROUP, BLOCK_GROUP, false)
  72. PhysicsService:CollisionGroupSetCollidable(BLOCK_GROUP, UPGRADE_GROUP, false)
  73. PhysicsService:CollisionGroupSetCollidable(UPGRADE_GROUP, UPGRADE_GROUP, true)
  74.  
  75. -- Helper: create a Tween to move a part by a vertical offset
  76. local function createTween(part, offsetY)
  77. -- Use part's current position as base and move by offsetY
  78. local targetPosition = part.Position + Vector3.new(0, offsetY, 0)
  79. return TweenService:Create(part, TWEEN_INFO, {Position = targetPosition})
  80. end
  81.  
  82. -- Helper: spawn a new block just above an original dropper block
  83. local function spawnBlock(originalBlock)
  84. if not originalBlock or not originalBlock:IsA("BasePart") then return end -- Guard invalid input
  85. local clone = originalBlock:Clone() -- Duplicate the template block
  86. clone.Transparency = 0 -- Ensure block is visible
  87. clone.Anchored = false -- Allow physics simulation
  88. -- Calculate vertical offset so new block rests above original
  89. local offsetY = (originalBlock.Size.Y + clone.Size.Y) / 2 + 1
  90. clone.CFrame = originalBlock.CFrame * CFrame.new(0, offsetY, 0)
  91. clone.CollisionGroup = BLOCK_GROUP -- Assign collision group to avoid self-collisions
  92. clone.Parent = workspace -- Insert into world for physics and rendering
  93. end
  94.  
  95. -- Starts pump animation and block spawning loop
  96. local function animatePump(pumpPart, blockTemplate)
  97. if not pumpPart or not blockTemplate then return end -- Guard invalid parameters
  98. -- Run a self-contained loop for this pump
  99. coroutine.wrap(function()
  100. while true do
  101. -- Verify pump and block still exist in world
  102. if not pumpPart:IsDescendantOf(workspace) then break end
  103. if not blockTemplate:IsDescendantOf(workspace) then break end
  104.  
  105. createTween(pumpPart, 1.25):Play() -- Move pump up
  106. wait(0.5)
  107. createTween(pumpPart, -1.25):Play() -- Move pump down
  108. wait(0.5)
  109.  
  110. spawnBlock(blockTemplate) -- generate a block
  111. end
  112. end)()
  113. end
  114.  
  115. -- Destroys any dropped blocks touching the baseplate to avoid performance issues
  116. local function onBaseplateTouched(otherPart)
  117. if otherPart:IsA("BasePart") and otherPart.Name == "Block" then
  118. otherPart:Destroy() -- Remove old blocks immediately
  119. end
  120. end
  121. Baseplate.Touched:Connect(onBaseplateTouched)
  122.  
  123. -- Registers a belt part for movement updates and sets its properties
  124. local function registerBelt(beltPart)
  125. if not beltPart or not beltPart:IsA("BasePart") then return end -- Guard invalid input
  126. beltPart.CanTouch = true -- Enable detection of parts on belt
  127. table.insert(activeBelts, beltPart)
  128. end
  129.  
  130. -- Heartbeat event handler: moves all parts touching each registered belt
  131. local function conveyorHeartbeat()
  132. -- Remove belts that no longer exist
  133. for i = #activeBelts, 1, -1 do
  134. if not activeBelts[i]:IsDescendantOf(workspace) then
  135. table.remove(activeBelts, i)
  136. end
  137. end
  138. -- For each active belt, push touching parts forward
  139. for _, belt in ipairs(activeBelts) do
  140. local direction = belt.CFrame.LookVector.Unit * BELT_SPEED
  141. local partsOnBelt = workspace:GetPartsInPart(belt)
  142. for _, part in ipairs(partsOnBelt) do
  143. if part:IsA("BasePart") and not part.Anchored and part ~= belt then
  144. part.Velocity = Vector3.new(direction.X, part.Velocity.Y, direction.Z)
  145. end
  146. end
  147. end
  148. end
  149. RunService.Heartbeat:Connect(conveyorHeartbeat)
  150.  
  151. -- Attaches sell logic so blocks pay out and get destroyed
  152. local function setupSellPart(sellPart)
  153. if not sellPart or not sellPart:IsA("BasePart") then return end -- Guard invalid input
  154. local function handleSellTouch(otherPart)
  155. if otherPart.Name ~= "Block" then return end -- Only process blocks
  156. local basePayout = PAYOUTS[otherPart.Material] or 0 -- Lookup material value
  157. if basePayout <= 0 then return end -- No payout means ignore
  158. if otherPart:GetAttribute("Upgraded") then -- Double payout if upgraded
  159. basePayout = basePayout * 2
  160. end
  161. otherPart:Destroy() -- Remove block from world
  162.  
  163. for _, player in ipairs(Players:GetPlayers()) do
  164. local leaderstats = player:FindFirstChild("leaderstats")
  165. if leaderstats then
  166. local cashValue = leaderstats:FindFirstChild("Cash")
  167. if cashValue then cashValue.Value = cashValue.Value + basePayout end
  168. end
  169. end
  170. end
  171. sellPart.Touched:Connect(handleSellTouch)
  172. end
  173.  
  174. -- Attaches upgrade logic to any 'UpgradingPart' so blocks get marked and colored
  175. local function setupUpgradePart(upgradePart)
  176. if not upgradePart or not upgradePart:IsA("BasePart") then return end -- Guard invalid input
  177. local function handleUpgradeTouch(otherPart)
  178. if otherPart.Name ~= "Block" then return end -- Only blocks
  179. if otherPart:GetAttribute("Upgraded") then return end -- Skip if already upgraded
  180. otherPart:SetAttribute("Upgraded", true) -- Mark as upgraded
  181. otherPart.Color = Color3.new(0, 1, 0) -- Visual indicator (green)
  182. end
  183. upgradePart.Touched:Connect(handleUpgradeTouch)
  184. end
  185.  
  186. -- Scans workspace for relevant parts/models at startup and sets them up
  187. local function scanWorkspaceItem(item)
  188. if item:IsA("BasePart") then
  189. if item.Name == "SellPart" then setupSellPart(item) end
  190. if item.Name == "Belt" then registerBelt(item) end
  191. if item.Name == "UpgradingPart" then setupUpgradePart(item) end
  192. return
  193. end
  194. if item:IsA("Model") then
  195. local pump = item:FindFirstChild("Pump")
  196. local blockTemplate = item:FindFirstChild("Block")
  197. if pump then animatePump(pump, blockTemplate) end
  198. end
  199. end
  200. for _, descendant in ipairs(workspace:GetDescendants()) do
  201. scanWorkspaceItem(descendant)
  202. end
  203.  
  204. -- Listen for new items added to workspace and configure them
  205. workspace.DescendantAdded:Connect(scanWorkspaceItem)
  206.  
  207. -- load cash when they join
  208. local function onPlayerAdded(player)
  209. local statsFolder = Instance.new("Folder")
  210. statsFolder.Name = "leaderstats"
  211. statsFolder.Parent = player
  212. local cashValue = Instance.new("IntValue")
  213. cashValue.Name = "Cash"
  214. -- Initialize with stored cash or zero
  215. cashValue.Value = dataStore:GetAsync(player.UserId) or 0
  216. cashValue.Parent = statsFolder
  217. end
  218. Players.PlayerAdded:Connect(onPlayerAdded)
  219.  
  220. -- Save player cash on leave
  221. local function onPlayerRemoving(player)
  222. local stats = player:FindFirstChild("leaderstats")
  223. if not stats then return end -- No stats means nothing to save
  224. local cashValue = stats:FindFirstChild("Cash")
  225. if cashValue then dataStore:SetAsync(player.UserId, cashValue.Value) end
  226. end
  227. Players.PlayerRemoving:Connect(onPlayerRemoving)
  228.  
  229. -- Handles placement requests with guard clauses
  230. local function onPlaceRequested(player, modelName, cframe, cost)
  231. local leaderstats = player:FindFirstChild("leaderstats") or {} -- Guard missing stats
  232. local cashValue = leaderstats and leaderstats:FindFirstChild("Cash")
  233. if not cashValue or cashValue.Value < cost then return end -- Insufficient funds guard
  234. local template = BuildModels:FindFirstChild(modelName)
  235. if not template then return end -- Invalid model guard
  236.  
  237. cashValue.Value = cashValue.Value - cost -- Deduct cost
  238. local modelClone = template:Clone()
  239. modelClone:PivotTo(cframe) -- Position clone
  240.  
  241. -- Configure each part of the clone
  242. for _, part in ipairs(modelClone:GetDescendants()) do
  243. if not part:IsA("BasePart") then continue end
  244. part.Transparency = 0
  245. part.CanCollide = true
  246. if part.Name == "UpgradingPart" then
  247. part.Transparency = 0.5
  248. part.CollisionGroup = UPGRADE_GROUP
  249. elseif part.Name == "Block" then
  250. part.Transparency = 1 -- Hidden until spawned
  251. end
  252. end
  253. modelClone.Parent = workspace
  254. end
  255. PlaceRemote.OnServerEvent:Connect(onPlaceRequested)
  256.  
  257. -- Handles deletion with refund and optional grid freeing
  258. local function onDeleteRequested(player, model)
  259. if not model or not model:IsA("Model") then return end -- Guard invalid input
  260. local leaderstats = player:FindFirstChild("leaderstats") or {}
  261. local cashValue = leaderstats and leaderstats:FindFirstChild("Cash")
  262. if not cashValue then return end -- Guard missing stats
  263.  
  264. local refundAmount = COSTS[model.Name] or 0
  265. cashValue.Value = cashValue.Value + refundAmount -- Refund to player
  266. model:Destroy() -- Remove model
  267. end
  268. DeleteRemote.OnServerEvent:Connect(onDeleteRequested)
  269.  
  270. -- Handles deletion with grid freeing refunds and clears grid cells
  271. local function onDeleteWithGrid(player, model, gridPos)
  272. if not model:IsDescendantOf(workspace) then return end -- Guard: model must be in world
  273. local leaderstats = player:FindFirstChild("leaderstats") or {}
  274. local cashValue = leaderstats and leaderstats:FindFirstChild("Cash")
  275. if not cashValue then return end -- Guard: ensure stats exist
  276.  
  277. local refund = PRICES[model.Name] or 0 -- Use placement price for refund
  278. cashValue.Value = cashValue.Value + refund -- Refund to player
  279. -- Free up grid position if tracked
  280. if placedGridByPlayer[player] then
  281. placedGridByPlayer[player][gridPos] = nil
  282. end
  283. model:Destroy()
  284. end
  285. DeleteRemote.OnServerEvent:Connect(onDeleteWithGrid)
  286.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement