no problem (: i made a better solution a few hours later
but with partcasting soon to be introduced it’ll soon become obsolete, too
here’s the updated code (i won’t reply to this post anymore)
--!nonstrict
local collectionService: CollectionService = game:GetService("CollectionService")
local replicatedStorage: ReplicatedStorage = game:GetService("ReplicatedStorage")
local userInputService: UserInputService = game:GetService("UserInputService")
local localPlayer: Player = game.Players.LocalPlayer
local playerGUI: PlayerGui = localPlayer:WaitForChild("PlayerGui")
local buildGUI: ScreenGui = playerGUI:WaitForChild("BuildGUI")
local mouse: Mouse = localPlayer:GetMouse()
local dragger: Dragger = Instance.new("Dragger")
local tool: Tool = script.Parent
local dragClone: Model? = nil
local rotationPointerPart: Part? = nil
local selectedItem: Model? = replicatedStorage.ItemModels.concrete:Clone()
local camera: Camera = workspace.CurrentCamera
local itemSelectedEvent: BindableEvent = buildGUI.BuildGUIScript.ItemSelectedEvent
local placeBlockFunction: RemoteFunction = replicatedStorage.Events.PlaceBlockFunction
local errorMessageEvent: BindableEvent = playerGUI.MiscGUI.ErrorMessageTextLabel.ErrorMessageScript.ErrorMessageEvent
local settingsFrame: Frame = buildGUI.ObjectSelectionFrame.SettingsFrame
local excludeList: {Instance} = {}
local equipped: boolean = false
local _time: number = os.clock()
local unsnappedCFrame: CFrame = nil
local unsnappedRotation: CFrame = CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))
local snappedRotation: CFrame = CFrame.Angles(math.rad(0), math.rad(0), math.rad(0))
local relativeRotation: boolean = true
local raycastResult: RaycastResult?
local moveSnap: number = 1
local rotateSnap: number = 1
local rotationModus: boolean = true
function destroyDragClone()
if dragClone then
dragClone:Destroy()
dragClone = nil
end
if rotationPointerPart then
rotationPointerPart:Destroy()
rotationPointerPart = nil
end
end
function createDragClone()
if not selectedItem then
return
end
if dragClone then
destroyDragClone()
end
dragClone = selectedItem:Clone()
dragClone.Parent = workspace
for i, instance: Instance in pairs(dragClone:GetDescendants()) do
if instance:IsA("BasePart") then
instance.CanCollide = false
instance.CanQuery = false
instance.CanTouch = false
if instance.Transparency <= 0.5 then
instance.Transparency = 0.5
end
end
end
local dragPrimaryPart: BasePart = dragClone.PrimaryPart
local rotationPointerClone: Part = game.ReplicatedFirst.RotationPointerPart:Clone()
rotationPointerClone.Parent = workspace
rotationPointerPart = rotationPointerClone
end
function roundNumberToBase(number: number, base: number, floor: boolean?): number
return floor and math.floor(number / base) * base or math.round(number / base) * base
end
function roundVectorToBase(vector3: Vector3, base: number): Vector3
return Vector3.new(
roundNumberToBase(vector3.X, base),
roundNumberToBase(vector3.Y, base),
roundNumberToBase(vector3.Z, base)
)
end
function getPlacementCFrame(primaryPart: BasePart, rotationSnapped: boolean?): CFrame
local rotationSnapped: boolean? = rotationSnapped
if rotationSnapped == nil then
rotationSnapped = true
end
assert(raycastResult, "raycastResult is nil!")
assert(primaryPart, "primaryPart is nil!")
local target: BasePart = raycastResult.Instance
local hitPosition: Vector3 = raycastResult.Position
local normal: Vector3 = raycastResult.Normal
local normalCFrame: CFrame = CFrame.lookAt(hitPosition, hitPosition + normal)
local surfaceNormalRelativeToTarget: Vector3 = target.CFrame:VectorToObjectSpace(raycastResult.Normal)
local _rotation: CFrame = rotationSnapped and snappedRotation or unsnappedRotation
if relativeRotation then
local relativeVector: Vector3
local absUpVector: Vector3 = Vector3.new(math.abs(target.CFrame.UpVector.X), math.abs(target.CFrame.UpVector.Y), math.abs(target.CFrame.UpVector.Z))
local absRightVector: Vector3 = Vector3.new(math.abs(target.CFrame.RightVector.X), math.abs(target.CFrame.RightVector.Y), math.abs(target.CFrame.RightVector.Z))
local absNormal: Vector3 = Vector3.new(math.abs(normal.X), math.abs(normal.Y), math.abs(normal.Z))
if absUpVector:FuzzyEq(absNormal, 0.1) then
relativeVector = target.CFrame.RightVector
if absRightVector:FuzzyEq(absNormal, 0.1) then
relativeVector = target.CFrame.LookVector
end
else
relativeVector = target.CFrame.UpVector
end
local XVector: Vector3 = normal:Cross(relativeVector)
local ZVector: Vector3 = XVector:Cross(normal)
local _relativeRotation: CFrame = CFrame.fromMatrix(
Vector3.new(),
XVector,
normal,
ZVector
)
_rotation = _relativeRotation * _rotation
end
if moveSnap ~= 0 then
local hitPositionRelativeToTarget: CFrame = target.CFrame:ToObjectSpace(CFrame.lookAt(hitPosition, hitPosition - raycastResult.Normal))
local surfaceNormalRelativeToTarget: Vector3 = target.CFrame:VectorToObjectSpace(normal)
local hitPositionSnapped: CFrame = (hitPositionRelativeToTarget - hitPositionRelativeToTarget.Position) + roundVectorToBase(hitPositionRelativeToTarget.Position + surfaceNormalRelativeToTarget * 10, moveSnap)
local snapRaycastOffsets: {Vector3} = {
Vector3.new(0,0,0),
hitPositionSnapped.RightVector * moveSnap,
-hitPositionSnapped.RightVector * moveSnap,
hitPositionSnapped.UpVector * moveSnap,
-hitPositionSnapped.UpVector * moveSnap,
}
local snapRaycastResult: RaycastResult?
for i = 1, 5 do
local raycastParams: RaycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {target}
raycastParams.FilterType = Enum.RaycastFilterType.Include
local hitPositionSnappedCFrameWorldSpace: CFrame = target.CFrame:ToWorldSpace(hitPositionSnapped+snapRaycastOffsets[i])
snapRaycastResult = workspace:Raycast(hitPositionSnappedCFrameWorldSpace.Position, normal * -1001, raycastParams)
if snapRaycastResult then
break
end
end
if snapRaycastResult then
hitPosition = snapRaycastResult.Position
end
end
local resolvedCFrame: CFrame = CFrame.new(hitPosition) * _rotation
local cornerPositionsRelativeToPrimaryPart: {Vector3} = {}
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(primaryPart.Size.X/2, primaryPart.Size.Y/2, primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(-primaryPart.Size.X/2, primaryPart.Size.Y/2, primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(primaryPart.Size.X/2, -primaryPart.Size.Y/2, primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(-primaryPart.Size.X/2, -primaryPart.Size.Y/2, primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(primaryPart.Size.X/2, primaryPart.Size.Y/2, -primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(-primaryPart.Size.X/2, primaryPart.Size.Y/2, -primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(primaryPart.Size.X/2, -primaryPart.Size.Y/2, -primaryPart.Size.Z/2))
table.insert(cornerPositionsRelativeToPrimaryPart, Vector3.new(-primaryPart.Size.X/2, -primaryPart.Size.Y/2, -primaryPart.Size.Z/2))
local cornerYPositionsRelativeToNormal: {number} = {}
for i, cornerPosition in pairs(cornerPositionsRelativeToPrimaryPart) do
local cornerPositionRelativeToNormal: Vector3 = normalCFrame:PointToObjectSpace(resolvedCFrame:PointToWorldSpace(cornerPosition))
table.insert(cornerYPositionsRelativeToNormal, cornerPositionRelativeToNormal.Z)
end
table.clear(cornerPositionsRelativeToPrimaryPart)
local deepestCornerYPosition: number = 9999999999
for i, cornerYposition in pairs(cornerYPositionsRelativeToNormal) do
if cornerYposition < deepestCornerYPosition then
deepestCornerYPosition = cornerYposition
end
end
resolvedCFrame = CFrame.new(hitPosition + -deepestCornerYPosition * normal) * _rotation
return resolvedCFrame
end
tool.Equipped:Connect(function()
equipped = true
createDragClone()
buildGUI.Enabled = true
end)
tool.Unequipped:Connect(function()
unsnappedRotation = CFrame.Angles(0, 0, 0)
destroyDragClone()
equipped = false
buildGUI.Enabled = false
end)
mouse.Button1Down:Connect(function()
_time = os.clock()
end)
mouse.Button1Up:Connect(function()
--If the player held mouse button down for more than 0.25 secs then they probably want to cancel placing it
if raycastResult and equipped and os.clock()-_time<0.25 and selectedItem and dragClone then
local pressSound: Sound = tool.PressSound:Clone()
pressSound.Parent = tool
pressSound:Play()
game:GetService("Debris"):AddItem(pressSound, pressSound.TimeLength)
placeBlockFunction:InvokeServer(selectedItem.Name, getPlacementCFrame(dragClone.PrimaryPart), settingsFrame.MergeButton:GetAttribute("toggled"), settingsFrame.AnchoredButton:GetAttribute("toggled"), settingsFrame.WeldButton:GetAttribute("toggled"))
end
end)
placeBlockFunction.OnClientInvoke = function(errorMessage: string)
errorMessageEvent:Fire(errorMessage)
end
itemSelectedEvent.Event:Connect(function(itemName: string)
selectedItem = replicatedStorage.ItemModels:FindFirstChild(itemName)
createDragClone()
end)
userInputService.InputEnded:Connect(function(input: InputObject, gameProcessed: boolean)
if gameProcessed then
return
end
if input.UserInputType == Enum.UserInputType.Keyboard then
local key: Enum.KeyCode = input.KeyCode
if key == Enum.KeyCode.Q then
rotationModus = not rotationModus
if rotationModus then
unsnappedRotation = snappedRotation
end
end
if rotationModus then
local rotateX: number = key == Enum.KeyCode.E and rotateSnap or 0
local rotateY: number = key == Enum.KeyCode.R and rotateSnap or 0
unsnappedRotation = unsnappedRotation * CFrame.Angles(math.rad(rotateX), math.rad(rotateY), 0)
snappedRotation = unsnappedRotation
end
end
end)
while task.wait() do
if equipped then
local dragPrimaryPart: BasePart = dragClone and dragClone.PrimaryPart or nil
moveSnap = tonumber(settingsFrame.SnapTextLabel.MoveSnapInput:GetAttribute("output"))
rotateSnap = tonumber(settingsFrame.SnapTextLabel.RotateSnapInput:GetAttribute("output"))
relativeRotation = settingsFrame.RelativeRotationButton:GetAttribute("toggled")
local raycastParams: RaycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {dragClone}
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
table.clear(excludeList)
table.insert(excludeList, test2)
for i, placable: Model in pairs(collectionService:GetTagged("placable")) do
for i, instance: any in pairs(placable:GetDescendants()) do
if instance ~= placable.PrimaryPart then
table.insert(excludeList, instance)
end
end
end
local unitRay: Ray = camera:ScreenPointToRay(mouse.X, mouse.Y)
local orgin: Vector3 = unitRay.Origin
raycastResult = workspace:Raycast(orgin, unitRay.Direction * 500, raycastParams)
if not raycastResult then
destroyDragClone()
end
if not dragClone and equipped and raycastResult then
createDragClone()
dragPrimaryPart = dragClone.PrimaryPart
end
if dragClone and raycastResult and mouse.Target then
dragClone:PivotTo(getPlacementCFrame(dragPrimaryPart))
end
if rotationPointerPart and dragPrimaryPart then
rotationPointerPart.CFrame = dragPrimaryPart.CFrame
rotationPointerPart.Size = dragPrimaryPart.Size
end
if not rotationModus and raycastResult then
local rotatingFactor: number = userInputService:IsKeyDown(Enum.KeyCode.LeftShift) and 0.05 or 2
rotatingFactor = userInputService:IsKeyDown(Enum.KeyCode.LeftControl) and rotatingFactor * -1 or rotatingFactor
local rotateX: number = userInputService:IsKeyDown(Enum.KeyCode.E) and 1 * rotatingFactor or 0
local rotateY: number = userInputService:IsKeyDown(Enum.KeyCode.R) and 1 * rotatingFactor or 0
unsnappedRotation = unsnappedRotation * CFrame.Angles(math.rad(rotateX), math.rad(rotateY), 0)
local rx, ry, rz = unsnappedRotation:ToOrientation();
snappedRotation = CFrame.fromOrientation(math.rad(roundNumberToBase(math.deg(rx), rotateSnap, true)), math.rad(roundNumberToBase(math.deg(ry), rotateSnap, true)), math.rad(roundNumberToBase(math.deg(rz), rotateSnap, true)))
if rotationPointerPart then
rotationPointerPart.CFrame = getPlacementCFrame(dragPrimaryPart, false)
rotationPointerPart.Size = dragPrimaryPart.Size
end
end
end
end
there’s a lot of unrelated stuff, you might need to clean that out.