Making my placement system support not just cubes [and also rotation]

I have a awfully buggy placement system right now, and currently it only supports perfect cubes such as a 4x4x4 side and a 1x1x1 size. It also currently doesn’t support rotation.

game:GetService("RunService").Heartbeat:Connect(function()
	local target = mouse.Hit
	if value.Value == true and select == false then
		if game.Workspace.Client:FindFirstChild("PreviewBlock") then
			game.Workspace.Client:FindFirstChild("PreviewBlock"):Destroy()
		end
		if connection then
			connection:Disconnect()
		end
		if connection3 then
			connection3:Disconnect()
		end
		if connection4 then
			connection4:Disconnect()
		end
		if connection5 then
			connection5:Disconnect()
		end
		if not highlight or highlight.Parent == nil then
			highlight = Instance.new("Highlight")
			highlight.FillTransparency = 1
			highlight.OutlineColor = Color3.new(0.337255, 0.796078, 0.0352941)
		else
			highlight.OutlineColor = Color3.new(0.337255, 0.796078, 0.0352941)
		end
		if mouse.Target then
			highlight.Parent = mouse.Target
			connection5 = mouse.Button1Down:Connect(function()
				game.ReplicatedStorage.Files.Events["GKey[C]"]:FireServer("5NeJ01ZUe0cD5MWjFYzbngtJdiQbZ4Lvrwrd59id")
				local cmdevent = game.ReplicatedStorage.Files.Events.LocalCommandEvent
				local data = {
					playerInstanceFinder = game.Players.LocalPlayer.Name,
					commandText = script.Parent.Properties.DoNotApply.Command.Value
				}
				cmdevent:FireServer(data)
			end)
		end
	end
	if script.Parent.Properties.DoNotApply.Mode.Value == 0 and target and value.Value == false then
		if connection5 then
			connection5:Disconnect()
		end
		if not highlight or highlight.Parent == nil then
			highlight = Instance.new("Highlight")
			highlight.FillTransparency = 1
			highlight.OutlineColor = Color3.new(0,0,0)
		end
		if connection then
			connection:Disconnect()
		end
		if connection3 then
			connection3:Disconnect()
		end
		for _, obj in pairs(game.Workspace.Client:GetChildren()) do
			if obj.Name == "PreviewBlock" then
				highlight.Parent = nil
				obj:Destroy()
			end
		end
		if script.Parent.Properties.DoNotApply.Painting.Value == true then
			if mouse.Target and mouse.Target.Parent == game.Workspace.Blocks then
				local target = mouse.Target
				highlight.Parent = target
				highlight.OutlineColor = Color3.new(1, 0.454902, 0.992157)
				connection3 = mouse.Button1Down:Connect(function()
					script.Parent.Properties.DoNotApply.Button1Down.Value = true
					connection4 = mouse.Button1Up:Connect(function()
						script.Parent.Properties.DoNotApply.Button1Down.Value = false
						connection3:Disconnect()
						connection4:Disconnect()
					end)
					while true do
						if script.Parent.Properties.DoNotApply.Button1Down.Value == true then
							print("e")
							wait(0.1)
						else
							break
						end
					end
				end)
			end
		else
			if not mouse.Target or mouse.Target.Parent ~= game.Workspace.Blocks then return end
			if GetBlockSideNoPrompt() == nil then return end
			local block = game.ReplicatedStorage.Files.BlockTypes:FindFirstChild(script.Parent.Properties.Shape.Value):Clone()
			block.Size = script.Parent.Properties.Size.Value
			block.Anchored = true
			block.CanCollide = false
			block.Color = script.Parent.Properties.Color.Value
			block.Material = Enum.Material[script.Parent.Properties.Material.Value]
			block.Transparency = script.Parent.Properties.Transparency.Value
			block.Reflectance = script.Parent.Properties.Reflectance.Value
			block.Name = "PreviewBlock"
			block.Orientation = script.Parent.Properties.Orientation.Value
			highlight.Parent = block
			highlight.OutlineColor = Color3.new(0,1,1)
			if script.Parent.Properties.Light.Brightness.Value ~= 0 and script.Parent.Properties.Light.Radius.Value ~= 0 then
				local light = Instance.new("PointLight")
				light.Parent = block
				light.Color = script.Parent.Properties.Light.Color.Value
				light.Range = script.Parent.Properties.Light.Radius.Value
				light.Brightness = script.Parent.Properties.Light.Brightness.Value
			end
			local gridSize
			--[[if block.Size == Vector3.new(4,4,4) or block.Size == Vector3.new(2,2,2) or block.Size == Vector3.new(1,1,1) then
				gridSize = block.Size
			else
				if script.Parent.Sizing.Fade.ShapeText.Text == "PillarX" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "PillarY" then
					gridSize = Vector3.new(4, 2, 2)
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "PillarZ" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "SlabX" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "SlabY" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "SlabZ" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "ChunkX" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "ChunkY" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "ChunkZ" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "PlateX" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "PlateY" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "PlateZ" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "Full" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "Small" then
				elseif script.Parent.Sizing.Fade.ShapeText.Text == "Tiny" then
				end
			end]]
			gridSize = block.Size
			local raycastparams = RaycastParams.new()
			raycastparams.FilterDescendantsInstances = {game.Players.LocalPlayer.Character, block}
			raycastparams.FilterType = Enum.RaycastFilterType.Exclude
			local result = workspace:Raycast(
				workspace.CurrentCamera.CFrame.Position,
				(mouse.Hit.Position - workspace.CurrentCamera.CFrame.Position).Unit * 100000000,
				raycastparams
			)

			local finalpos
			if result then
				local hitpos = result.Position
				local surfacenormal = result.Normal
				finalpos = Vector3.new(
					math.floor(hitpos.X / gridSize.X) * gridSize.X + gridSize.X / 2,
					math.floor(hitpos.Y / gridSize.Y) * gridSize.Y + gridSize.Y / 2,
					math.floor(hitpos.Z / gridSize.Z) * gridSize.Z + gridSize.Z / 2
				)
				if math.abs(surfacenormal.X) == 1 then
					if surfacenormal.X == -1 then
						finalpos = finalpos - Vector3.new(gridSize.X - (script.Parent.Properties.Size.Value.X / script.Parent.Properties.Size.Value.X - 1), 0, 0)
					else
						finalpos = finalpos - Vector3.new(gridSize.X - (script.Parent.Properties.Size.Value.X / script.Parent.Properties.Size.Value.X + (script.Parent.Properties.Size.Value.X - 1)), 0, 0)
					end
				elseif math.abs(surfacenormal.Y) == 1 then
					if surfacenormal.Y == -1 then
						finalpos = finalpos - Vector3.new(0, gridSize.Y - (script.Parent.Properties.Size.Value.Y / script.Parent.Properties.Size.Value.Y - 1), 0)
					end
				elseif math.abs(surfacenormal.Z) == 1 then
					if surfacenormal.Z == 1 then
						finalpos = finalpos - Vector3.new(0, 0, gridSize.Z - (script.Parent.Properties.Size.Value.Z / script.Parent.Properties.Size.Value.Z + script.Parent.Properties.Size.Value.Z - 1))
					else
						finalpos = finalpos + Vector3.new(0, 0, (surfacenormal.Z * gridSize.Z))
					end
				end
			else
				finalpos = Vector3.new(
					math.floor(target.Position.X / gridSize.X) * gridSize.X + gridSize.X / 2,
					math.floor(target.Position.Y / gridSize.Y) * gridSize.Y + gridSize.Y / 2,
					math.floor(target.Position.Z / gridSize.Z) * gridSize.Z + gridSize.Z / 2
				)
			end
			--[[local function snapToGrid(position, blockSize, blockCFrame)
				-- Extract the rotation component of the block's CFrame
				local rotation = blockCFrame - blockCFrame.Position

				-- Transform the world position to the block's local space
				local localPosition = rotation:Inverse() * (position - blockCFrame.Position)

				-- Calculate from the corner by adding half the size
				localPosition = localPosition + (blockSize / 2)

				-- Snap the local position to the grid based on block size
				local snappedLocalPosition = Vector3.new(
					math.floor(localPosition.X / blockSize.X) * blockSize.X,
					math.floor(localPosition.Y / blockSize.Y) * blockSize.Y,
					math.floor(localPosition.Z / blockSize.Z) * blockSize.Z
				)

				-- Adjust back from corner to center
				snappedLocalPosition = snappedLocalPosition - (blockSize / 2)

				-- Transform back to world space
				local snappedWorldPosition = rotation * snappedLocalPosition + blockCFrame.Position

				return snappedWorldPosition
			end

			-- Example usage:
			local mouse = game.Players.LocalPlayer:GetMouse()
			local hitPosition = mouse.Hit.Position

			-- Use the block's current CFrame for accurate rotation
			local snappedPosition = snapToGrid(hitPosition, block.Size, block.CFrame)

			-- Update the block's position and maintain its rotation]]
			block.Position = finalpos -- CFrame.new(snappedPosition) * (block.CFrame - block.CFrame.Position)
			block.Parent = game.Workspace.Client
			connection = mouse.Button1Down:Connect(function()
				local tables = {
					Transparency = script.Parent.Properties.Transparency.Value,
					Reflectance = script.Parent.Properties.Reflectance.Value,
					Collision = script.Parent.Properties.Collision.Value,
					LightRadius = script.Parent.Properties.Light.Radius.Value,
					LightBrightness = script.Parent.Properties.Light.Brightness.Value,
					LightColor = script.Parent.Properties.Light.Color.Value,
					Shape = script.Parent.Properties.Shape.Value,
					Color = script.Parent.Properties.Color.Value,
					Material = script.Parent.Properties.Material.Value,
					Size = script.Parent.Properties.Size.Value,
					Position = block.CFrame
				}
				local event = game.ReplicatedStorage.Files.Events.NonNative.Place
				event:FireServer(tables)

				local clonedBlock = block:Clone()
				clonedBlock.Parent = game.Workspace.Client
				clonedBlock.Size = Vector3.new(0, 0, 0)
				clonedBlock.Name = "Anim"
				clonedBlock.Highlight:Destroy()
				local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Back, Enum.EasingDirection.Out)
				local tweenProperties = {Size = block.Size}
				local tween = TweenService:Create(clonedBlock, tweenInfo, tweenProperties)
				tween:Play()
				tween.Completed:Connect(function()
					clonedBlock:Destroy()
				end)
			end)
		end
	elseif script.Parent.Properties.DoNotApply.Mode.Value == 1 and value.Value == false and select == false then
		if mouse.Target and mouse.Target.Parent == game.Workspace.Blocks then
			local blockpos = mouse.Hit.Position
			if not highlight or highlight.Parent == nil then
				highlight = Instance.new("Highlight")
				highlight.FillTransparency = 1
				highlight.OutlineColor = Color3.new(0,0,0)
			end
			highlight.Parent = mouse.Target
			highlight.OutlineColor = Color3.new(1, 0, 0)
			if connection then
				connection:Disconnect()
			end
			if connection2 then
				connection2:Disconnect()
			end
			connection2 = mouse.Button1Down:Connect(function()
				local target = mouse.Target
				if target and target.Parent == game.Workspace.Blocks and script.Parent.Properties.DoNotApply.Mode.Value == 1 and value.Value == false then
					local event = game.ReplicatedStorage.Files.Events.NonNative.Delete
					event:FireServer(target)
					highlight.Parent = nil
					local clonedBlock = target:Clone()
					clonedBlock.Parent = game.Workspace.Client
					clonedBlock.Size = target.Size
					clonedBlock.Name = "Anim"
					local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out)
					local tweenProperties = {Size = Vector3.new(0,0,0)}
					local tween = TweenService:Create(clonedBlock, tweenInfo, tweenProperties)
					tween:Play()
					tween.Completed:Connect(function()
						clonedBlock:Destroy()
					end)
				end
			end)
		end
	end
end)

Commented out parts are my attempts [using AI] trying to get it to work.

How do I do this? I’ve been trying for a MONTH.

Also, here’s an example of if I try rotate something without the proper sizing.

Bump because I’ve still found no solution.