Need Help With My Code Please

I am creating a placement system. In my system, I have something called “carry capacity.” Now, I want to make my code repeat placing products until the carry capacity limit is reached. It’s kind of working but not fully. Hopefully, someone could help me.

here is video of my problem:
https://streamable.com/g1zhe0

my code (Local script \ StarterPlayerScripts):

local gridSize = script:FindFirstChild("GridSize").Value -- Change this to adjust grid size

game.ReplicatedStorage.Events.TogglePlacement.OnClientEvent:Connect(function(structureName, carryCapacity)
	local ReplicatedStorage = game:GetService("ReplicatedStorage")
	local camera = workspace.CurrentCamera
	local UIS = game:GetService("UserInputService")
	local RunService = game:GetService("RunService")
	local structures = ReplicatedStorage:FindFirstChild("Structures")
	local player = game.Players.LocalPlayer
	local char = player.Character or player.CharacterAdded:Wait()
	local HumanoidRootPart = char:FindFirstChild("HumanoidRootPart")

	local maxPlacingDistance = script:FindFirstChild("maxPlacingDistance").Value
	local rKeyIsPressed = false
	local placingStructure = false
	local clientStructure = nil	
	local yOrientation = 0
	local okToPlace = false
	local placedCount = 0 -- Track how many structures have been placed

	local function placeStructure()
		placingStructure = true
		clientStructure = structures:FindFirstChild(structureName):Clone()

		local PlacementGui = player.PlayerGui:FindFirstChild("PlacementContorlls")
		local MainFrame = PlacementGui:FindFirstChild("Main")

		if clientStructure then
			if MainFrame.Visible == false then
				MainFrame.Visible = true
			end

			clientStructure.CanCollide = false
			clientStructure.Parent = game.Workspace

			local selectionBox = Instance.new("SelectionBox")
			selectionBox.Adornee = clientStructure
			selectionBox.Parent = clientStructure
			selectionBox.LineThickness = -1
			selectionBox.SurfaceColor3 = script:FindFirstChild("PlaceableColor").Value
			selectionBox.SurfaceTransparency = 0.75

			RunService.RenderStepped:Connect(function()
				if not placingStructure then return end
				local mousePosition = UIS:GetMouseLocation()
				local mouseRay = camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
				local raycastParams = RaycastParams.new()
				raycastParams.FilterDescendantsInstances = {clientStructure, char}
				local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)

				if raycastResult then
					local targetPosition = raycastResult.Position
					local hitPart = raycastResult.Instance
					local canPlaceFolder = workspace:FindFirstChild("CanPlaceProducts")
					okToPlace = false

					local anotherCanPlaceFolder = game.Workspace.RestockedProducts
					if (canPlaceFolder and hitPart:IsDescendantOf(canPlaceFolder)) or 
						(anotherCanPlaceFolder and hitPart:IsDescendantOf(anotherCanPlaceFolder)) then
						okToPlace = (HumanoidRootPart.Position - targetPosition).Magnitude < maxPlacingDistance
					end

					clientStructure.CanCollide = true

					-- Snap to grid
					targetPosition = Vector3.new(
						math.floor(targetPosition.X / gridSize + 0.5) * gridSize,
						targetPosition.Y + (clientStructure.Size.Y / 2),
						math.floor(targetPosition.Z / gridSize + 0.5) * gridSize
					)

					-- Update SelectionBox color based on placement status
					selectionBox.SurfaceColor3 = okToPlace and script:FindFirstChild("PlaceableColor").Value or script:FindFirstChild("NonPlaceableColor").Value
					clientStructure.CFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(yOrientation), 0)
				end
			end)

			UIS.InputBegan:Connect(function(input)
				if input.KeyCode == Enum.KeyCode.R then
					rKeyIsPressed = true
					while rKeyIsPressed do
						wait(0.1)
						if placingStructure then 
							yOrientation = yOrientation + script:FindFirstChild("RotateDegree").Value 
						end
					end
				elseif input.UserInputType == Enum.UserInputType.MouseButton1 and placingStructure and okToPlace then
					clientStructure.CanCollide = true
					game.ReplicatedStorage.Events.PlaceStructureEvent:FireServer(clientStructure.Name, clientStructure.CFrame)
					placedCount = placedCount + 1 -- Increment placed count
					if placedCount <= carryCapacity then
						clientStructure:Destroy() -- Destroy the client structure
						placeStructure() -- Allow placement of another structure
					else
						if MainFrame.Visible == true then
							MainFrame.Visible = false
						end
						placingStructure = false
						clientStructure:Destroy()
						print("Carry capacity reached!")
					end
				elseif input.KeyCode == Enum.KeyCode.Q and placingStructure then
					placingStructure = false
					if MainFrame.Visible == true then
						MainFrame.Visible = false
					end
					clientStructure:Destroy()
				end
			end)

			UIS.InputEnded:Connect(function(input)
				if input.KeyCode == Enum.KeyCode.R then
					rKeyIsPressed = false
				end
			end)
		else
			warn("Structure not found: " .. structureName)
		end
	end

	placeStructure() -- Start the placement process
end)

as you can see I have 3 max carry capacity and when I am trying to place it gives me reach to the max(you can see print) but here’s the problem. when I come second time to take products and place then it giving me the option to get into negative amount, I want to make it able to calculate how much left inside the storage and how much can I can carry up and then it will allow me that amount.

second problem is when I first time trying to place I have 5 left in stock and I can place 3 and when I place the first one its changing the amount to 4 and after I place second one instead giving me 3 its giving me 2 why??

video:
https://streamable.com/n2vwpr

server side code:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local PlaceStructureEvent = ReplicatedStorage.Events:WaitForChild("PlaceStructureEvent")
local structuresFolder = ReplicatedStorage:WaitForChild("Structures") -- Folder with available structures
local workspaceFolder = game.Workspace:WaitForChild("RestockedProducts") -- Folder in Workspace for placed structures
local ServerStorage = game:GetService("ServerStorage")
local CurrentStock = ServerStorage:FindFirstChild("ProductsCurrentStock")

-- Function to place a structure in the world
local function placeStructure(structureName, cframe, player)
	-- Check if the structure exists in the "Structures" folder
	local structureTemplate = structuresFolder:FindFirstChild(structureName)
	if structureTemplate then
		-- Clone the structure, set its CFrame, and parent it to the Workspace
		local newStructure = structureTemplate:Clone()
		newStructure.CFrame = cframe
		newStructure.Parent = workspaceFolder
		
		if CurrentStock and CurrentStock:FindFirstChild(structureName) then
			CurrentStock:FindFirstChild(structureName).Value -= 1
		end

	else
		warn("Structure not found:", structureName)
	end
end

-- Remote event connection
PlaceStructureEvent.OnServerEvent:Connect(function(player, structureName, cframe)
	placeStructure(structureName, cframe, player)
end)

thanks for helping me my last hope you guys.

The placedCount is resetting back to 0 each time the TogglePlacement event is fired. This is happening because placedCount is declared inside the scope of the function that’s connected to the event, rather than at the top of the script

As a matter of fact, most of the values that are inside of the scope mentioned above should ideally be declared once at the top of the script, such as the variables storing services

omg yes i didn’t think about it, but now the second problem can you tell me why its happening?

1 Like

This should fix the value going into the negatives (replace the placeStructure function in the server script with this):

local function placeStructure(structureName, cframe, player)
	local structureInStock = CurrentStock and CurrentStock:FindFirstChild(structureName)
	if structureInStock then
		if structureInStock.Value > 0 then -- Check if the value is positive
			structureInStock.Value -= 1
		else
			print(`{structureName}'s value is 0`)
			return -- Exit from the function, so that a structure isn't created
		end
	end
	-- Check if the structure exists in the "Structures" folder
	local structureTemplate = structuresFolder:FindFirstChild(structureName)
	if structureTemplate then
		-- Clone the structure, set its CFrame, and parent it to the Workspace
		local newStructure = structureTemplate:Clone()
		newStructure.CFrame = cframe
		newStructure.Parent = workspaceFolder
	else
		warn("Structure not found:", structureName)
	end
end

I haven’t yet found what’s causing the value to jump from 4 to 2, though


@artum669

I found the cause, it’s happening because the InputBegan and InputEnded connections are being created inside of the TogglePlacement connection, which is causing a memory leak and also firing the PlaceStructureEvent more frequently than intended

i did what you said, now its not going negative, but my main problem is the update the structureinstock value

image
this is my structure, I have folder inside that I have the name of the product in a number value

I found the cause, it’s happening because the InputBegan and InputEnded connections are being created inside of the TogglePlacement connection, which is causing a memory leak and also firing the PlaceStructureEvent more frequently than intended

and how you suggest me to fix it, because I was trying to fix that 2 days and my brain not working

Do remember that it’s risky creating connections inside of another connection unless you’re very careful about disconnecting it properly

I’ll fix the code for you to help you understand what I mean, and I’ll edit this comment when I’m done :slight_smile::+1:


@artum669

Sorry that it’s taking me so long, but I’ve decided to tackle this problem using a different approach, which should be more efficient and secure than if I were to try to fix the original script. Don’t worry though, I’m still working on it :grin:

appreciate it a lot and don’t know how I can thank you for that.

if you have any other suggestions to make this code more optimized please make it happen and explain me I want to learn. :pray:

1 Like

I’m finally done :grin:

These scripts are more of an example of what you could do rather than intended as a direct replacement of your existings scripts, though

This will be the LocalScript:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
local Workspace = game:GetService("Workspace")

local MOUSE_DISTANCE = 1000 -- In studs
local ROTATION_ANGLE = 60 -- In degrees per second

local placementValidator = require(ReplicatedStorage:WaitForChild("PlacementValidator"))

local camera = Workspace.CurrentCamera or Workspace:WaitForChild("Camera")

local player = Players.LocalPlayer:: Player

local remoteEvent = ReplicatedStorage:WaitForChild("RemoteEvent")

local structureValue = ReplicatedStorage:WaitForChild("StructureValue")


local character: Model
local isRotating = false
local mousePosition = Vector3.zero
local raycastParams: RaycastParams
local structure: BasePart


local function get3dMousePosition(): Vector3
	local mouseLocation = UserInputService:GetMouseLocation()
	local ray = camera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
	local raycastResult = Workspace:Raycast(ray.Origin, ray.Direction * MOUSE_DISTANCE, raycastParams)

	return if raycastResult then raycastResult.Position else ray.Origin + (ray.Direction * MOUSE_DISTANCE)
end

local function updateRaycastParams()
	local RaycastParams = RaycastParams.new()
	RaycastParams.FilterDescendantsInstances = {character, structure}
	RaycastParams.FilterType = Enum.RaycastFilterType.Exclude

	raycastParams = RaycastParams
end

local function onCharacterAdded(newCharacter: Model)
	character = newCharacter

	updateRaycastParams()
end

local function onStructureValueChanged(value: Instance)
	if value:IsA("BasePart") then
		structure = value

		updateRaycastParams()
	else
		warn("Value placed inside StructureValue does not inherit from BasePart")
	end
end

onCharacterAdded(player.Character or player.CharacterAdded:Wait())

player.CharacterAdded:Connect(onCharacterAdded)

onStructureValueChanged(structureValue.Value or structureValue.Changed:Wait())

structureValue.Changed:Connect(onStructureValueChanged)

RunService.RenderStepped:Connect(function(deltaTime: number)
	local position = get3dMousePosition()

	structure.Position = placementValidator.SnapToGrid(position, structure.Size.Y)
	mousePosition = position

	if isRotating then
		structure.Orientation += Vector3.new(0, ROTATION_ANGLE * deltaTime, 0)
	end
end)

UserInputService.InputBegan:Connect(function(inputObject: InputObject, gameProcessedEvent: boolean)
	if gameProcessedEvent then return end

	if inputObject.KeyCode == Enum.KeyCode.R then
		isRotating = true
	elseif inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
		remoteEvent:FireServer(structure.Name, mousePosition, structure.Orientation)
		structure.Orientation = Vector3.zero
	end
end)

UserInputService.InputEnded:Connect(function(inputObject: InputObject, gameProcessedEvent: boolean)
	if gameProcessedEvent then return end

	if inputObject.KeyCode == Enum.KeyCode.R then
		isRotating = false
	end
end)

This is the server script:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

local placementValidator = require(ReplicatedStorage.PlacementValidator)

local remoteEvent = ReplicatedStorage.RemoteEvent

local testPart = Instance.new("Part")
testPart.Parent = Workspace

local structureValue = ReplicatedStorage.StructureValue
structureValue.Value = testPart


local function valuesAreValid(structureName: any, mousePosition: any, structureOrientation: any): boolean
	return (
		typeof(structureName) == "string"
		and typeof(mousePosition) == "Vector3"
		and typeof(structureOrientation) == "Vector3")
end

remoteEvent.OnServerEvent:Connect(function(player: Player, structureName: string, mousePosition: Vector3, structureOrientation: Vector3)
	if player.Character and valuesAreValid(structureName, mousePosition, structureOrientation) then
		local playerPosition = player.Character:GetPivot().Position
		if (mousePosition - playerPosition).Magnitude > 1000 then return end

		local part = Instance.fromExisting(structureValue.Value)
		part.Position = placementValidator.SnapToGrid(mousePosition, part.Size.Y)
		part.Orientation = structureOrientation * Vector3.yAxis -- This is so that the X and Z axis are ignored
		part.Anchored = true
		part.Parent = Workspace
	end
end)

And this is the PlacementValidator module:

local GRID_SIZE = 4


local module = {}

function module.SnapToGrid(position: Vector3, ySize: number): Vector3
	return Vector3.new(
		math.round(position.X / GRID_SIZE) * GRID_SIZE,
		position.Y + (ySize / 2),
		math.round(position.Z / GRID_SIZE) * GRID_SIZE)
end

return module

Modules are quite useful for when you’d want to make a function that will be used on both the client and the server, and I’m using the StructureValue ObjectValue as a way to synchronize the currently selected structure (In my case, I used the testPart as a stand-in for your structures) between the client and the server

When the player selects a new structure, the server should check if it’s valid using a method similar to how I’m checking the value’s types, and if so, it will change the StructureValue’s value to the new structure (Unfortunately for the sake of time, I didn’t implement this myself)


@artum669

Made an edit to the LocalScript that should improve the accuracy of the grid snapping

can you explain me please more about it?

like where to place each object

number value in replicated storage called “StructureValue”?
and where its looking for the parts that can be placed?

@JohhnyLegoKing

I was trying to understand the code but ummm its weird because I did this other way

@JohhnyLegoKing maybe share me the file you was working on I will see on it and understand better

1 Like

Here’s the place file:

GridPlacementExample.rbxl (57.7 KB)

I went ahead and implemented a (basic but functional) way to change between different structures, what the client needs to do is fire a RemoteEvent named ChangeStructure with the name of the desired structure, and the server will only change the structure if the name matches with a real structure found inside of a Folder named Structures that’s inside of Workspace


@artum669

I’ll retry fixing the original script as well, but I still recommend checking out the new scripts I’ve made


@artum669

This is the (hopefully) fixed version of the original LocalScript:

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local camera = workspace.CurrentCamera
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local structures = ReplicatedStorage:FindFirstChild("Structures")
local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local HumanoidRootPart = char:FindFirstChild("HumanoidRootPart")

local gridSize = script:FindFirstChild("GridSize").Value -- Change this to adjust grid size

local maxPlacingDistance = script:FindFirstChild("maxPlacingDistance").Value
local rKeyIsPressed = false
local placingStructure = false
local clientStructure = nil	
local yOrientation = 0
local okToPlace = false
local placedCount = 0 -- Track how many structures have been placed

local structureName
local carryCapacity

local function placeStructure()
	placingStructure = true
	clientStructure = structures:FindFirstChild(structureName):Clone()

	local PlacementGui = player.PlayerGui:FindFirstChild("PlacementContorlls")
	local MainFrame = PlacementGui:FindFirstChild("Main")

	if clientStructure then
		if MainFrame.Visible == false then
			MainFrame.Visible = true
		end

		clientStructure.CanCollide = false
		clientStructure.Parent = game.Workspace

		local selectionBox = Instance.new("SelectionBox")
		selectionBox.Adornee = clientStructure
		selectionBox.Parent = clientStructure
		selectionBox.LineThickness = -1
		selectionBox.SurfaceColor3 = script:FindFirstChild("PlaceableColor").Value
		selectionBox.SurfaceTransparency = 0.75

		while placingStructure do
			local mousePosition = UIS:GetMouseLocation()
			local mouseRay = camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = {clientStructure, char}
			local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)

			if raycastResult then
				local targetPosition = raycastResult.Position
				local hitPart = raycastResult.Instance
				local canPlaceFolder = workspace:FindFirstChild("CanPlaceProducts")
				okToPlace = false

				local anotherCanPlaceFolder = game.Workspace.RestockedProducts
				if (canPlaceFolder and hitPart:IsDescendantOf(canPlaceFolder)) or 
					(anotherCanPlaceFolder and hitPart:IsDescendantOf(anotherCanPlaceFolder)) then
					okToPlace = (HumanoidRootPart.Position - targetPosition).Magnitude < maxPlacingDistance
				end

				clientStructure.CanCollide = true

				-- Snap to grid
				targetPosition = Vector3.new(
					math.floor(targetPosition.X / gridSize + 0.5) * gridSize,
					targetPosition.Y + (clientStructure.Size.Y / 2),
					math.floor(targetPosition.Z / gridSize + 0.5) * gridSize
				)

				-- Update SelectionBox color based on placement status
				selectionBox.SurfaceColor3 = okToPlace and script:FindFirstChild("PlaceableColor").Value or script:FindFirstChild("NonPlaceableColor").Value
				clientStructure.CFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(yOrientation), 0)
			end

			RunService.RenderStepped:Wait()
		end
	else
		warn("Structure not found: " .. structureName)
	end
end

game.ReplicatedStorage.Events.TogglePlacement.OnClientEvent:Connect(function(StructureName, CarryCapacity)
	structureName = StructureName
	carryCapacity = CarryCapacity

	placeStructure() -- Start the placement process
end)

UIS.InputBegan:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.R then
		rKeyIsPressed = true
		while rKeyIsPressed do
			wait(0.1)
			if placingStructure then 
				yOrientation = yOrientation + script:FindFirstChild("RotateDegree").Value 
			end
		end
	elseif input.UserInputType == Enum.UserInputType.MouseButton1 and placingStructure and okToPlace then
		clientStructure.CanCollide = true
		game.ReplicatedStorage.Events.PlaceStructureEvent:FireServer(clientStructure.Name, clientStructure.CFrame)
		placedCount = placedCount + 1 -- Increment placed count
		if placedCount <= carryCapacity then
			clientStructure:Destroy() -- Destroy the client structure
			placeStructure() -- Allow placement of another structure
		else
			if MainFrame.Visible == true then
				MainFrame.Visible = false
			end
			placingStructure = false
			clientStructure:Destroy()
			print("Carry capacity reached!")
		end
	elseif input.KeyCode == Enum.KeyCode.Q and placingStructure then
		placingStructure = false
		if MainFrame.Visible == true then
			MainFrame.Visible = false
		end
		clientStructure:Destroy()
	end
end)

UIS.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.R then
		rKeyIsPressed = false
	end
end)