I have made a grid placement system using the placement module by Tunicus and glitchifyed (Tunicus-Placement-v3/3.2.lua at main · glitchifyed/Tunicus-Placement-v3 · GitHub), but I notice my script can only function if the baseplate is at an angle increment of 90 degrees.
I have been trying to make it work with baseplates angled in different ways, but I could not find a way of doing it without the object being placed not alligned with the hologram and grid.
I would appreciate anything to help me achieve this
Below is my serverscript:
local building = game.Workspace.Land.one
local land = building.plot
local function angleIsValid(angle)
if typeof(angle) == "number" then
if angle == -90 then
angle = 270
elseif angle == -180 then
angle = 180
end
return angle / 90 == math.floor(angle / 90)
end
end
local function getAngle(rotation, coord)
if string.lower(coord) == "x" then
coord = 1
elseif string.lower(coord) == "y" then
coord = 2
elseif string.lower(coord) == "z" then
coord = 3
end
if typeof(coord) ~= "number" or coord < 1 or coord > 3 then
warn("Incorrect coordinate:", coord)
return
end
local angle = typeof(rotation) == "CFrame" and math.round(math.deg(({rotation:ToEulerAnglesYXZ()})[coord])) or typeof(rotation) == "Vector3" and rotation[coord]
if typeof(angle) == "number" then
if angle == -90 then
angle = 270
elseif angle == -180 then
angle = 180
end
return angle
end
warn(angle, "is not a number, type", typeof(angle))
end
local function getExtents(rotation, size)
if typeof(rotation) == "Instance" then
if rotation:IsA("Model") and rotation.PrimaryPart then
rotation = rotation.PrimaryPart
end
if rotation:IsA("BasePart") then
size = rotation.Size
rotation = rotation.CFrame
end
end
local yRotation = getAngle(rotation, "y")
local extents = size
if math.abs(yRotation) == 90 or yRotation == 270 then
extents = Vector3.new(size.Z, size.Y, size.X)
end
return extents
end
local function round(n, to)
if typeof(n) == "Vector3" then
if typeof(to) == "number" then
to = Vector3.new(to, to, to)
end
return Vector3.new(to.X ~= 0 and round(n.X, to.X) or n.X, to.Y ~= 0 and round(n.Y, to.Y) or n.Y, to.Z ~= 0 and round(n.Z, to.Z) or n.Z)
end
return n % to ~= 0 and (n % to) > to/2 and (n + to - (n % to)) or (n - (n % to))
end
local function floatEqual(a, b)
return (math.abs(a - b) < .01)
end
local function floatGreater(a, b)
return (a - b) > .01
end
local function floatLesser(a, b)
return (b - a) > .01
end
local function floatLesserOrEqual(a, b)
return (b - a) > .01 or floatEqual(a, b)
end
local function floatGreaterOrEqual(a, b)
return (a - b) > .01 or floatEqual(a, b)
end
local function getTotalPosition(obj)
return obj.position + Vector3.new(0, obj.height + obj.platformY, 0) * grid
end
local function isColliding(obj, colliders)
local position0
if typeof(obj) == "table" then
if typeof(obj.item) == "Instance" and typeof(obj.position) == "CFrame" and typeof(obj.height) == "number" and typeof(obj.platformY) == "number" then
position0 = getTotalPosition(obj)
obj = obj.item
end
end
if typeof(obj) == "Instance" and obj:IsA("Model") and obj.PrimaryPart then
obj = obj.PrimaryPart
end
if typeof(colliders) == "Instance" then
colliders = colliders:GetChildren()
end
if typeof(obj) == "Instance" and obj:IsA("BasePart") and obj.Parent and obj.Parent:IsA("Model") and obj == obj.Parent.PrimaryPart and typeof(colliders) == "table" then
if not position0 then
position0 = obj.CFrame
end
local extents0 = getExtents(position0, obj.Size)
local x0, y0, z0, extentsX0, extentsY0, extentsZ0 = position0.X, position0.Y, position0.Z, extents0.X, extents0.Y, extents0.Z
for _, collider in pairs(colliders) do
local position1
if typeof(collider) == "table" then
if typeof(collider.item) == "Instance" and typeof(collider.position) == "CFrame" and typeof(collider.height) == "number" and typeof(collider.platformY) == "number" then
position1 = getTotalPosition(collider)
collider = collider.item
end
end
if typeof(collider) == "Instance" and collider:IsA("Model") and collider.PrimaryPart and collider.PrimaryPart ~= obj then
if not position1 then
position1 = collider:GetPivot()
end
local extents1 = getExtents(position1, collider.PrimaryPart.Size)
local x1, y1, z1, extentsX1, extentsY1, extentsZ1 = position1.X, position1.Y, position1.Z, extents1.X, extents1.Y, extents1.Z
if floatLesser(x0 - extentsX0/2, x1 + extentsX1/2) and floatGreater(x0 + extentsX0/2, x1 - extentsX1/2) and floatLesser(z0 - extentsZ0/2, z1 + extentsZ1/2) and floatGreater(z0 + extentsZ0/2, z1 - extentsZ1/2) and floatLesser(y0 - extentsY0/2, y1 + extentsY1/2) and floatGreater(y0 + extentsY0/2, y1 - extentsY1/2) then
return true
end
end
end
return
end
warn(obj, "or", colliders, "may be invalid. types:", typeof(obj), "|", typeof(colliders))
end
function game.ReplicatedStorage.place.OnServerInvoke(player, names, locations, heights)
if typeof(names) == "table" and typeof(locations) == "table" and typeof(heights) == "table" and typeof(heights.heights) == "table" and typeof(heights.platforms) == "table" then
local items = {}
--making sure the items actually exist and stuff
for i, name in pairs(names) do
if typeof(i) == "number" and typeof(name) == "string" then
local item, position, height, platformY = game.ReplicatedStorage.items:FindFirstChild(name), locations[i], heights.heights[i], heights.platforms[i]
if typeof(item) == "Instance" and item:IsA("Model") and item.PrimaryPart and --[[insert inventory check here and]] typeof(position) == "CFrame" and typeof(height) == "number" and typeof(platformY) == "number" and angleIsValid(getAngle(position, "y")) then
local extents = getExtents(position, item.PrimaryPart.Size)
position = CFrame.new((round(position.Position - land.Position, Vector3.new(grid, 0, grid))) * Vector3.new(1, 0, 1) + land.Position + Vector3.new(0, land.Size.Y + item.PrimaryPart.Size.Y, 0) / 2 + Vector3.new(extents.X / 2 % grid, 0, extents.Z / 2 % grid)) * (position - position.Position)
height = math.floor(math.clamp(height, item:GetAttribute("minHeight") or item:GetAttribute("height") or 0, item:GetAttribute("maxHeight") or item:GetAttribute("height") or 0))
platformY = math.clamp(platformY, 0, math.huge / grid)
items[i] = {item = item, position = position, height = height, platformY = platformY}
end
end
end
--platforms
local platforms = {}
for _, platform in pairs(gym.items:GetChildren()) do
if platform:IsA("Model") and platform.PrimaryPart and platform:FindFirstChild("platform") and platform.platform:IsA("BasePart") then
table.insert(platforms, platform.platform)
end
end
for _, platform in pairs(items) do
if platform.item:FindFirstChild("platform") and platform.item.platform:IsA("BasePart") then
local fake_platform_part_data = {CFrame = platform.position + Vector3.new(0, platform.height + platform.platformY, 0) * grid, Size = platform.item.PrimaryPart.Size}
fake_platform_part_data.Position = fake_platform_part_data.CFrame.Position
table.insert(platforms, fake_platform_part_data)
end
end
--items that actually get placed
local atLeastOneItemGotPlaced
--collisions and final placement
for _, item in pairs(items) do
if not isColliding(item, items) and not isColliding(item, gym.items) then
local position0 = item.position
local extents0 = getExtents(position0, item.item.PrimaryPart.Size)
local x0, z0, extentsX0, extentsZ0 = position0.X, position0.Z, extents0.X, extents0.Z
--check if item is inside base
local position2 = land.CFrame
local extents2 = getExtents(position2, land.Size)
local x2, z2, extentsX2, extentsZ2 = position2.X, position2.Z, extents2.X, extents2.Z
if not (floatLesser(x0 - extentsX0/2, x2 + extentsX2/2) and floatGreater(x0 + extentsX0/2, x2 - extentsX2/2) and floatLesser(z0 - extentsZ0/2, z2 + extentsZ2/2) and floatGreater(z0 + extentsZ0/2, z2 - extentsZ2/2) and floatLesserOrEqual(x0 + extentsX0/2, x2 + extentsX2/2) and floatGreaterOrEqual(x0 - extentsX0/2, x2 - extentsX2/2) and floatLesserOrEqual(z0 + extentsZ0/2, z2 + extentsZ2/2) and floatGreaterOrEqual(z0 - extentsZ0/2, z2 - extentsZ2/2)) then
continue
end
if item.platformY ~= 0 then
local points = {}
for x = x0 - extentsX0 / 2, x0 + extentsX0 / 2 + 0.001, grid / 2 do
for z = z0 - extentsZ0 / 2, z0 + extentsZ0 / 2 + 0.001, grid / 2 do
points[Vector3.new(x, 0, z)] = false
end
end
local function allPointsInsideAPlatform()
for point, inside in pairs(points) do
if not inside then
return
end
end
return true
end
local pointsInsidePlatform
for _, platform in pairs(platforms) do
if (platform.Position.Y + game.ReplicatedStorage.platformSize.Value / 2 - land.Position.Y - land.Size.Y / 2) / grid == item.platformY then
local position1 = platform.CFrame
local extents1 = getExtents(position1, platform.Size)
local x1, z1, extentsX1, extentsZ1 = position1.X, position1.Z, extents1.X, extents1.Z
for point, inside in pairs(points) do
if not inside then
if floatLesser(point.X - grid / 2, x1 + extentsX1 / 2) and floatGreater(point.X + grid / 2, x1 - extentsX1 / 2) and floatLesser(point.Z - grid / 2, z1 + extentsZ1 / 2) and floatGreater(point.Z + grid / 2, z1 - extentsZ1 / 2) then
points[point] = true
end
end
pointsInsidePlatform = allPointsInsideAPlatform()
if pointsInsidePlatform then
break
end
end
if pointsInsidePlatform then
break
end
end
end
if not pointsInsidePlatform then
continue
end
end
--clone the item to get ready to place
local clone = item.item:Clone()
clone:PivotTo(getTotalPosition(item))
clone:SetAttribute("height", item.height)
clone.PrimaryPart.Material = Enum.Material.SmoothPlastic
clone.PrimaryPart.CanCollide = false
clone.PrimaryPart.Transparency = 1
for _, obj in pairs(clone:GetDescendants()) do
if obj:IsA("BasePart") then
obj.Anchored = true
end
end
--hydraulic legs (if the item has any)
local legs = clone:FindFirstChild("legs")
if legs and legs:IsA("Model") then
local cframe = clone:GetPivot() - Vector3.new(0, clone.PrimaryPart.Size.Y / 2, 0)
local height = item.height
for _, part in pairs(legs:GetChildren()) do
if part:IsA("BasePart") then
part.CanCollide = false
if part.Name == "leg" then
local partSize = Vector3.new(0, height * grid, 0)
part.Size = Vector3.new(part.Size.X, 0, part.Size.Z) + partSize
part.Position = Vector3.new(part.Position.X, cframe.Position.Y, part.Position.Z) - partSize / 2
else
local partSize = Vector3.new(0, height * grid - part.Size.Y / 2, 0)
part.Position = Vector3.new(part.Position.X, cframe.Position.Y, part.Position.Z) - partSize
end
end
end
end
--finally add the item to the base
clone.Parent = gym.items
if not atLeastOneItemGotPlaced then
atLeastOneItemGotPlaced = true
end
end
end
--tell the client if the placement succeeded or not
return atLeastOneItemGotPlaced
end
end