Placement system is acting strange

I’ve been writing a custom placement module for anyone to easily setup a working placement system in minutes for their game. I have written two others before, but wanted to make a third one to add some new features. I have come across a problem with the new one where the model is offset for some reason. I also have a simple system that detects input to handle features such as rotation, canceling placement and moving up and down floors. The strange thing is, only one input is detected (the cancel key).

For the video I have the grid and interpolation off.

I have spent a while trying to debug these problems, but found nothing worked. I even tried coping code from the working module to see if I typed something wrong, but nothing changed. Maybe I just don’t see the problem, but I have no idea why this is happening.

I will put both the new module and the old one here. Note the new one has some comments with : P at the end of them. These are places where I think the problem is occurring.

New

-- SETTINGS

-- Bools
local interpolation = true -- Toggles interpolation (smoothing)
local moveByGrid = false -- Toggles grid system
local collisions = true -- Toggles collisions
local buildModePlacement = true -- Toggles "build mode" placement
local displayGridTexture = true -- Toggles the grid texture to be shown when placing
local smartDisplay = false -- Toggles smart display for the grid. If true, it will rescale the grid texture to match your gridsize
local enableFloors = true -- Toggles if the raise and lower keys will be enabled
local transparentModel = true -- Toggles if the model itself will be transparent

-- Color3
local collisionColor = Color3.fromRGB(255, 75, 75) -- Color of the hitbox when colliding
local hitboxColor = Color3.fromRGB(75, 255, 75) -- Color of the hitbox while not colliding

-- Integers
local maxHeight = 30 -- Max height you can place objects (in studs)
local floorStep = 10 -- The step (in studs) that the object will be raised or lowered
local rotationStep = 90 -- Rotation step
local lerpSpeed = 0 -- speed of interpolation. 0 = no interpolation, 0.9 = major interpolation

-- Numbers
local hitboxTransparency = 0.7 -- Hitbox transparency when placing
local transparencyDelta = 0.3 -- Transparency of the model itself (transparentModel must equal true)

-- Other
local gridTexture = "rbxassetid://2415319308"

-- DO NOT EDIT PAST THIS POINT UNLESS YOU KNOW WHAT YOUR DOING.

local placement = {}

placement.__index = placement

-- Essentials
local runService = game:GetService("RunService")
local userInputService = game:GetService("UserInputService")

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local mouse = player:GetMouse()	

-- math/cframe functions
local clamp = math.clamp
local floor = math.floor
local rad = math.rad
local abs = math.abs
local max = math.max

local cframe = CFrame.new
local angles = CFrame.Angles

-- Constructor variables
local grid
local itemLocation
local rotateKey
local terminateKey
local raiseKey
local lowerKey

-- Activation vatiables
local plot
local object

-- bools
--local currentlyPlacing
local canPlace
local isColliding
local stackable
local smartRot
local canActivate = true
local currentRot = false

-- values used for calculations
local speed --= 1 - clamp(abs(tonumber(lerpSpeed)), 0, 0.9)

local posX
local posY
local posZ
local rot

local lowerXBound
local upperXBound

local lowerZBound
local upperZBound

local initialY

-- collision variables
local collisionPoints
local collisionPoint
local collided

-- states
local states = {
	"movement",
	"placing",
	"colliding",
	"in-active"
}

local currentState = 4

-- other
local placedObjects
local primary
local humanoid = character:WaitForChild("Humanoid")

local function setCurrentState(state)
	currentState = clamp(state, 1, 4)
end

-- Checks for collisions on the hitbox
local function checkHitbox()
	if object then
		collisionPoint = object.PrimaryPart.Touched:Connect(function() end)
		collisionPoints = object.PrimaryPart:GetTouchingParts()
		
		for i = 1, #collisionPoints do
			if not collisionPoints[i]:IsDescendantOf(object) and not collisionPoints[i]:IsDescendantOf(character) then
				setCurrentState(3)
				
				break
			end
		end
		
		collisionPoint:Disconnect()
		
		return collided
	end
end

-- Changes the color of the hitbox depending on the current state
local function editHitboxColor()
	if currentState == 3 then
		object.PrimaryPart.Color = collisionColor
	else
		object.PrimaryPart.Color = hitboxColor
	end
end

-- switches the floor depending on the value given
local function editFloor(f)
	if enableFloors then
		if posY < maxHeight and posY > initialY then
			if f == 1  then
				posY = posY + floor(abs(floorStep))
			else
				posY = posY - floor(abs(floorStep))
			end
		end
	end
end

-- handles the grid texture
local function displayGrid()
	if displayGridTexture then
		local gridTex = Instance.new("Texture")
		
		gridTex.Name = "GridTexture"
		gridTex.Texture = gridTexture
		gridTex.Parent = plot
		gridTex.Face = Enum.NormalId.Top
		
		gridTex.StudsPerTileU = 2
		gridTex.StudsPerTileV = 2
		
		if smartDisplay then
			gridTex.StudsPerTileU = grid
			gridTex.StudsPerTileV = grid	
		end
	end
end

local function rotate()
	if smartRot then
		if currentRot then
			rot = rot + rotationStep
		else 
			rot = rot - rotationStep
		end
	else
		rot = rot + rotationStep
	end
	
	currentRot = not currentRot
end

local function calculateYPos(tp, ts, o)
	return (tp + ts / 2) + o / 2
end

-- This function may be allowing the object to leave the plot. Not sure if this function is the problem though. : P (maybe)
local function bounds()
	if currentRot then
		lowerXBound = plot.Position.X - (plot.Size.X / 2) 
		upperXBound = plot.Position.X + (plot.Size.X / 2) - primary.Size.X
		
		lowerZBound = plot.Position.Z - (plot.Size.Z / 2)	
		upperZBound = plot.Position.Z + (plot.Size.Z / 2) + primary.Size.Z
	else
		lowerXBound = plot.Position.X - (plot.Size.X / 2) 
		upperXBound = plot.Position.X + (plot.Size.X / 2) - primary.Size.Z
		
		lowerZBound = plot.Position.Z - (plot.Size.Z / 2)	
		upperZBound = plot.Position.Z + (plot.Size.Z / 2) + primary.Size.X
	end
	
	posX = math.clamp(posX, lowerXBound, upperXBound)
	posZ = math.clamp(posZ, lowerZBound, upperZBound)
end

-- Calculates the position of the object : P (maybe)
local function calculateItemLocation()
	if moveByGrid then
		posX = math.floor((mouse.Hit.X / grid) + 0.5) * grid
		posZ = math.floor((mouse.Hit.Z / grid) + 0.5) * grid
	else
		posX = mouse.Hit.X
		posZ = mouse.Hit.Z
	end
	
	bounds()
	
	if not enableFloors and stackable then
		posY = calculateYPos(mouse.Target.Position.Y, mouse.Target.Size.Y, primary.Size.Y)
	end

	--posY = clamp(posY, initialY, maxHeight)
end

-- Sets the position of the object
local function translateObj()
	if currentState ~= 4 then
		-- I dont know if it's the calc funcion of the movement itself. To me, everything is the same as my V2 module. Maybe it's just me though. : P
		calculateItemLocation()
		print(rot)
		if currentRot then
			object:SetPrimaryPartCFrame(primary.CFrame:Lerp(cframe(posX, posY, posZ) * cframe(primary.Size.X / 2, 0, primary.Size.Z / 2) * angles(0, math.rad(rot), 0), speed))
		else
			object:SetPrimaryPartCFrame(primary.CFrame:Lerp(cframe(posX, posY, posZ) * cframe(primary.Size.Z / 2, 0, primary.Size.X / 2) * angles(0, math.rad(rot), 0), speed))
		end
	end
end

-- handles user input
local function getInput(input, gpe)
	if currentState == 1 then
		print(input.KeyCode == rotateKey) -- prints true : P
		
		if input.KeyCode == raiseKey then
			editFloor(1)
		elseif input.KeyCode == lowerKey then
			editFloor(2)
		elseif input.KeyCode == terminateKey then
			placement:terminate()
		elseif input.KeyCode == rotateKey then
			print("input") -- never printed : P
			
			rotate() -- never called : P
		else
			print("input detected") -- this was used for debugging
		end
	end
end

-- Verifys that the plane which the object is going to be placed upon is the correct size
local function verifyPlane()	
	if max(plot.Size.X, plot.Size.Z) > grid and grid ~= 0 then
		if plot.Size.X / grid == floor(plot.Size.X / grid) and plot.Size.Z / grid == floor(plot.Size.Z / grid) then
			return true
		else
			return false
		end
	else
		return nil
	end
end

-- Checks if there are any problems with the users setup
local function approveActivation()
	if verifyPlane() ~= nil and not verifyPlane() then
		warn("The object that the model is moving on is not scaled correctly. Consider changing it.")
	end
	
	if grid > max(plot.Size.X, plot.Size.Z) then 
		error("Grid size is larger than the plot size. To fix this, try lowering the grid size.")
	end
end

-- Sets up placement
function placement.new(g, objs, r, t, u, l)
	local data = {}
	local metaData = setmetatable(data, placement)
	
	grid = abs(tonumber(g))
	itemLocation = objs
	rotateKey = r
	terminateKey = t
	raiseKey = r
	lowerKey = l
	
	data.gridsize = grid
	data.items = objs
	data.raise = rotateKey
	data.cancel = terminateKey
	data.raise = raiseKey
	data.lower = lowerKey
	
	return data 
end

function placement:getCurrentState()
	return states[currentState]
end

-- Requests to place down the object
function placement:requestPlacement(re, loc)
	
end

-- Terminates placement
function placement:terminate()
	stackable = nil
	canPlace = nil
	smartRot = nil
	
	object:Destroy()
	object = nil
	
	if displayGridTexture then
		for i, v in next, plot:GetChildren() do
			if v then
				if v.Name == "GridTexture" and v:IsA("Texture") then
					v:Destroy()
				end
			end
		end
	end
	
--	currentlyPlacing = false
	setCurrentState(4)
	canActivate = true
	
	return
end

-- Activates placement
function placement:activate(id, pobj, plt, stk, r)
	if object then
		object:Destroy()
		object = nil
	end
	
	plot = plt
	object = itemLocation:FindFirstChild(tostring(id))
	placedObjects = pobj
	
	approveActivation()
	
	object = itemLocation:FindFirstChild(id):Clone()
	object.PrimaryPart.Transparency = hitboxTransparency
	object.PrimaryPart.Color = hitboxColor
	
	for i, o in pairs(object:GetDescendants()) do
		if o then
			if o:IsA("Part") or o:IsA("UnionOperation") or o:IsA("MeshPart") then
				o.CanCollide = false
				
				if transparentModel then
					o.Transparency = o.Transparency + transparencyDelta
				end
			end
		end
	end
	
--	currentlyPlacing = true
	stackable = stk
	smartRot = r
	
	if not stk then
		mouse.TargetFilter = pobj
	else
		mouse.TargetFilter = object
	end
	
	initialY = calculateYPos(plt.Position.Y, plt.Size.Y, object.PrimaryPart.Size.Y)
	posY = initialY
	
	speed = 0
	rot = 0
	
	translateObj()
	displayGrid()
	
	if interpolation then
		speed = clamp(abs(tonumber(1 - lerpSpeed)), 0, 0.9)
	else
		speed = 0
	end
	
	primary = object.PrimaryPart
	object.Parent = pobj
	
	setCurrentState(1)
end

runService:BindToRenderStep("Input", Enum.RenderPriority.Input.Value, translateObj)
userInputService.InputBegan:Connect(getInput) -- : P

return placement

Old

-- Settings

-- Grid

local MoveByGrid = false -- If you wan't the model to move by grid specified units.
local GridSize = 2 -- In studs.
local DisplayGrid = true -- If you wan't to display a grid.
local SmartDisplay = true -- If you want the module to try and autoscale the texture to match your grid (may alter resolution of texture).
local GridTexture = "rbxassetid://2415319308" -- Texture used when grid is displayed.

 -- Linear Interpolation (smoothing)

local Interpolate = false -- Enable smoothing (Interpolation)
local InterpolateSpeed = 0.9 -- Speed of movement (0 = instant, 1 = no movement. Defualt = 0.7)

-- Basic Settings - Start

local RotationStep = 90 -- In degrees.
local FloorStep = 10 -- In studs.
local MaxHeight = 10 -- In studs.
local CollisionCheckCooldown = 0.3 -- In seconds

-- Bools (true or false values)

local DetectCollisions = true -- If you wan't to have a collision system.
local IgnoreItems = true -- If you want the mouse to ignore the items currently placed down.
local BuildModeEnabled = true -- If you want continual placement.
local EnableFloors = true -- If you want a floor system.
local TransparentModels = true -- if you want the models to be transparent while placing

-- Basic Settings - End

-- Advanced Settings - Start

local CollisionColor = Color3.fromRGB(255, 55, 55) -- The RGB value for when collision is detected.
local PlacingColor = Color3.fromRGB(55, 255, 55) -- The RGB value for when collision is not detected.

-- Keybinds

local KeyCodeCancel = Enum.KeyCode.X -- Key used to cancel placement.
local KeyCodeRotate = Enum.KeyCode.R -- Key used to rotate the model being placed.
local KeyCodeRaiseFloor = Enum.KeyCode.U -- Key used to raise the floor.
local KeyCodeLowerFloor = Enum.KeyCode.L -- Key used to lower the floor.

-- Advanced Settings - End

local transparentDelta = 0.6
local hitboxTransparency = 0.8
local step = 1.1

-- DO NOT EDIT PAST THIS POINT UNLESS YOU KNOW WHAT YOUR DOING!

local PlacementModuleV2 = {}

local runService = game:GetService("RunService")
local userInputService = game:GetService("UserInputService")

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local mouse = player:GetMouse()	

local posX
local posY
local posZ
local startingY
local currentRot = true
local smartRot = false

local lastY
local rot = 0
local c = 0

local modelInfo = {}
local cframe

local grid = math.abs(tonumber(GridSize))

local model
local plot
local objs
local collisionPoints
local collisionPoint

local placingMode
local canPlace
local placing
local colliding
local stacking
local canStart = true

wait(0.1) -- DO NOT REMOVE (It will error if you remove)

local humanoid = character:FindFirstChild("Humanoid")

local function EditColor()
	if PlacementModuleV2:GetCurrentState() == "Collision" and model then
		model.PrimaryPart.Color = CollisionColor
	elseif PlacementModuleV2:GetCurrentState() ~= "Collision" and model then
		model.PrimaryPart.Color = PlacingColor
	end
end

local function CheckHitbox()
	if model then
		colliding = false
		
		collisionPoint = model.PrimaryPart.Touched:Connect(function() end)
		collisionPoints = model.PrimaryPart:GetTouchingParts()
		
		for i = 1, #collisionPoints do
			if not collisionPoints[i]:IsDescendantOf(model) and not collisionPoints[i]:IsDescendantOf(character) then
				colliding = true
				
				break
			end
		end
		
		collisionPoint:Disconnect()
		
		return colliding
	end
end

local function StayInBounds()
	local lowerBoundX
	local upperBoundX
	
	local lowerBoundZ
	local upperBoundZ
	
	if currentRot then
		lowerBoundX = plot.Position.X - (plot.Size.X / 2) 
		upperBoundX = plot.Position.X + (plot.Size.X / 2) - model.PrimaryPart.Size.X
		
		lowerBoundZ = plot.Position.Z - (plot.Size.Z / 2)	
		upperBoundZ = plot.Position.Z + (plot.Size.Z / 2) - model.PrimaryPart.Size.Z
	else
		lowerBoundX = plot.Position.X - (plot.Size.X / 2) 
		upperBoundX = plot.Position.X + (plot.Size.X / 2) - model.PrimaryPart.Size.Z
		
		lowerBoundZ = plot.Position.Z - (plot.Size.Z / 2)	
		upperBoundZ = plot.Position.Z + (plot.Size.Z / 2) - model.PrimaryPart.Size.X
	end
	
	posX = math.clamp(posX, lowerBoundX, upperBoundX)
	posZ = math.clamp(posZ, lowerBoundZ, upperBoundZ)
end

local function CalcualateNewPosition()
	if MoveByGrid then
		posX = math.floor((mouse.Hit.X / grid) + 0.5) * grid
		posZ = math.floor((mouse.Hit.Z / grid) + 0.5) * grid
	else
		posX = mouse.Hit.X
		posZ = mouse.Hit.Z
	end
	
	if EnableFloors and not stacking then
		if posY > MaxHeight then
			posY = MaxHeight
		elseif posY < startingY then
			posY = startingY
		end
	end
	
	StayInBounds()
	
	if stacking then
		--posY = math.floor(mouse.Hit.Y) + step
		
		posY = (mouse.Target.Position.Y + mouse.Target.Size.Y / 2) + model.PrimaryPart.Size.Y / 2
		
		if posY > MaxHeight then
			posY = MaxHeight
		elseif posY < startingY then
			posY = startingY
		end
	end
end

local function CalcFinalCFrame()
	if currentRot then
		cframe = CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.X / 2, 0, model.PrimaryPart.Size.Z / 2)
	else
		cframe = CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.Z / 2, 0, model.PrimaryPart.Size.X / 2)
	end
end

local function Calc(tf)
	if currentRot then
		if Interpolate then
			model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:Lerp(CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.X / 2, 0, model.PrimaryPart.Size.Z / 2) * CFrame.Angles(0, math.rad(rot), 0), 1 - InterpolateSpeed))
		else
			model:SetPrimaryPartCFrame(CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.X / 2, 0, model.PrimaryPart.Size.Z / 2) * CFrame.Angles(0, math.rad(rot), 0))
		end
	else
		if Interpolate then
			model:SetPrimaryPartCFrame(model.PrimaryPart.CFrame:Lerp(CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.Z / 2, 0, model.PrimaryPart.Size.X / 2) * CFrame.Angles(0, math.rad(rot), 0), 1 - InterpolateSpeed))
		else
			model:SetPrimaryPartCFrame((CFrame.new(posX, posY, posZ) * CFrame.new(model.PrimaryPart.Size.Z / 2, 0, model.PrimaryPart.Size.X / 2)) * CFrame.Angles(0, math.rad(rot), 0))
		end
	end
end

local function ModifyCoordinates()
	if placingMode and canPlace and model then
		CalcualateNewPosition()
		Calc(true)
		
		if DetectCollisions then
			CheckHitbox()
			EditColor()
		end
	end
end
local function EditFloor(g)
	if posY <= MaxHeight and posY >= startingY then
		if g == 2 then
			posY = posY + FloorStep
		else
			posY = posY - FloorStep
		end
	end
end

local function CheckRotation()
	if model then
		if currentRot then
			currentRot = false
		else 
			currentRot = true
		end
	end
end

local function RotateModel()
	if smartRot then
		if currentRot then
			rot = rot + RotationStep
		else
			rot = rot - RotationStep
		end
	else
		rot = rot + RotationStep
	end
	
	CheckRotation()
end

local function RemovePlacement(tf)
	placingMode = false
	canPlace = false
	canStart = true
	
	if DisplayGrid then
		for i, v in pairs(plot:GetChildren()) do
			if v then
				if v:IsA("Texture") and v.Name == "GridTexture" then
					v:Destroy()
				end
			end
		end
	end
	
	stacking = false
	
	if not BuildModeEnabled and tf then
		model:Destroy()
		model = nil
	else
		model:Destroy()
		model = nil
	end
end

local function InputDetector(input, gpe)
	if placingMode and model then
		if input.KeyCode == KeyCodeCancel then
			RemovePlacement()
		elseif input.KeyCode == KeyCodeRaiseFloor then
			EditFloor(2)
		elseif input.KeyCode == KeyCodeLowerFloor then
			EditFloor(1)
--		elseif input.KeyCode == KeyCodeCancel then
--			RemovePlacement(false)
		elseif input.KeyCode == KeyCodeRotate then
			RotateModel()
		end
	end
end

local function DisplayGridOnCanvas()
	if DisplayGrid then
		local gridTex = Instance.new("Texture")
		
		gridTex.Name = "GridTexture"
		gridTex.Texture = GridTexture
		gridTex.Parent = plot
		gridTex.Face = Enum.NormalId.Top
		
		gridTex.StudsPerTileU = 2
		gridTex.StudsPerTileV = 2
		
		if SmartDisplay then
			gridTex.StudsPerTileU = grid
			gridTex.StudsPerTileV = grid	
		end
	end
end

local function GetModel(loc, id, ignoreLocation)
	model = loc:FindFirstChild(id):Clone()
	
	startingY, posY = model.PrimaryPart.CFrame.Y, model.PrimaryPart.CFrame.Y
	model:SetPrimaryPartCFrame(CFrame.new(posX, lastY, posZ))
	
	model.Parent = ignoreLocation
	
	for i, m in pairs(model:GetDescendants()) do
		if m then
			if m:IsA("Part") or m:IsA("UnionOperation") or m:IsA("MeshPart") then
				m.CanCollide = false
				
				if TransparentModels then
					m.Transparency = m.Transparency + transparentDelta
				end
			end
		end
	end
	
	model.PrimaryPart.Transparency = hitboxTransparency
	
	if IgnoreItems and not stacking then
		mouse.TargetFilter = ignoreLocation
	else
		mouse.TargetFilter = model
	end
end

local function Setup(stk, location, id, loc, sr)
	placingMode = true
	canPlace = true
	canStart = false
	stacking = stk
	smartRot = sr
	
	if BuildModeEnabled then
		canStart = true
	end
	
	GetModel(loc, id, location)
	DisplayGridOnCanvas()
end

function PlacementModuleV2:Finish(e)
	if placingMode and model and not BuildModeEnabled then
		wait(CollisionCheckCooldown)
		
		Calc(currentRot)
		CalcualateNewPosition()
		CalcFinalCFrame()
		CheckHitbox()
		
		if not colliding then
			e:FireServer(self:GetModelInfo(), model.Parent, cframe)
			
			RemovePlacement(false)
		end
	elseif BuildModeEnabled and canPlace and placingMode and not colliding and model then
		lastY = model.PrimaryPart.CFrame.Y
		
		wait(CollisionCheckCooldown)
		
		Calc(currentRot)
		CalcualateNewPosition()
		CalcFinalCFrame()
		CheckHitbox()
		
		if not colliding then
			e:FireServer(self:GetModelInfo(), model.Parent, cframe)
		end
	end
end

function PlacementModuleV2:CancelPlacementManual()
	RemovePlacement(false)
end

function PlacementModuleV2:GetModelInfo()
	if model then
		modelInfo = {
			["Name"] = model.Name;
			["CollisionState"] = colliding;
			
			
			["CFrame"] = {
				["X"] = model.PrimaryPart.CFrame.X,
				["Y"] =  model.PrimaryPart.CFrame.Y,
				["Z"] =  model.PrimaryPart.CFrame.Z,
				["Rotation"] = model.PrimaryPart.Orientation.Y
			}
		}
		
		return modelInfo
	end
end

function PlacementModuleV2:GetCurrentState()
	local state
	
	if placingMode and not colliding then
		state = "Movement"
	elseif placing and not colliding then
		state = "Placing"
	elseif colliding then
		state = "Collision"
	elseif canStart and not placingMode then
		state = "Waiting"
	else
		state = nil
	end
	
	return state
end

function PlacementModuleV2:ResumeState(msg)
	if self:GetCurrentState() == "Movement" then
		placingMode = true
		canPlace = true
		
		return msg
	elseif self:GetCurrentState() == "Placing" then
		return "Not developed yet"
	elseif self:GetCurrentState() == "Collision" then
		colliding = nil
		placingMode = true
		canPlace = true
		
		return msg
	elseif self:GetCurrentState() == "Waiting" then
		return "Not developed yet"
	end
end

function PlacementModuleV2:PauseState(msg)
	if self:GetCurrentState() == "Movement" then
		placingMode = nil
		canPlace = nil
		
		return msg
	elseif self:GetCurrentState() == "Placing" then
		return "Not developed yet"
	elseif self:GetCurrentState() == "Collision" then
		placingMode = nil
		canPlace = nil
		colliding = true
		
		return msg
	elseif self:GetCurrentState() == "Waiting" then
		return "Not developed yet"
	end
end

function PlacementModuleV2:new(id, location, plt, loc, stack, smtr)
	if canStart and not placingMode and not BuildModeEnabled then
		plot = plt
		
		Setup(stack, loc, id, location, smtr)
	elseif BuildModeEnabled then
		plot = plt
		
		if model then
			model:Destroy()
		end
		
		Setup(stack, loc, id, location, smtr)
	else
		return self:GetCurrentState()
	end
end

humanoid.Died:Connect(function()
	RemovePlacement(false)
end)

runService:BindToRenderStep("Input", Enum.RenderPriority.Input.Value, ModifyCoordinates)

userInputService.InputBegan:Connect(InputDetector)

return PlacementModuleV2

Here are how the modules are being used from local scripts:

New

local itemPlacement = require(game.ReplicatedStorage.Modules.PlacementModuleV3)

local remote = game.ReplicatedStorage.Events.PlaceObj

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()

local button = script.Parent.version3

itemPlacement.new(
	2,
	game.ReplicatedStorage.Models,
	Enum.KeyCode.R, Enum.KeyCode.X, Enum.KeyCode.U, Enum.KeyCode.L
)

button.MouseButton1Click:Connect(function()
	itemPlacement:activate("Wall", workspace.Plots.Plot1.PlacedObjects, workspace.Plots.Plot1.Plot, false, true)
end)

-- ignore this
mouse.Button1Down:Connect(function()
	
end)

Old

local placementModule = require(game.ReplicatedStorage.Modules.PlacementModuleV2EDIT)

local remote = game.ReplicatedStorage.Events.PlaceObj

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()

local button = script.Parent.version1

button.MouseButton1Click:Connect(function()
	placementModule:new("Wall", game.ReplicatedStorage.Models, workspace.Plots.Plot1.Plot, workspace.Plots.Plot1.PlacedObjects, true, true)
end)

-- ignore this
mouse.Button1Down:Connect(function()
	placementModule:Finish(remote)
end)

One more thing, the old module is not the same as the released version of it. I just added the bounds function in. Other than that, it’s the same. Just something to be aware of.

Thanks!

2 Likes

This is one lengthy script. I don’t think anyone on the devforum will debug it. Try contacting the person who made it.

EDIT: I’m an idiot and didn’t fully read the post, sorry!

Try to hire another scripter, it can help to have someone else look at it from a different vantage point.

Ok well, I can’t hire someone, so I will continue trying on my own.