Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local isServer = game:GetService("RunService"):IsServer()
- local initPlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteFunctions").InitPlacement
- local invokePlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents").InvokePlacement
- local cancelPlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents").CancelPlacement
- local Placement = {}
- Placement.__index = Placement
- function Placement.new(canvasPart)
- -----Self------------------------------------------------------------
- local self = setmetatable({}, Placement)
- self.CanvasPart = canvasPart
- if (isServer) then
- self.CanvasObjects = Instance.new("Folder")
- self.CanvasObjects.Name = "CanvasObjects"
- self.CanvasObjects.Parent = canvasPart
- else
- self.CanvasObjects = initPlacement:InvokeServer(canvasPart)
- end
- self.Surface = Enum.NormalId.Top
- self.GridUnit = 4
- return self
- end
- ---------------------------------------------------------------------
- --Methods------------------------------------------------------------
- function Placement:CalcCanvas()
- local canvasSize = self.CanvasPart.Size
- local back = Vector3.new(0,-1,0)
- local top = Vector3.new(0,0,-1)
- local right = Vector3.new(-1,0,0)
- -- convert to world space
- local cf = self.CanvasPart.CFrame * CFrame.fromMatrix(-back*canvasSize/2, right, top, back)
- -- use object space vectors to find the width and height
- local size = Vector2.new((canvasSize * right).magnitude, (canvasSize * top).magnitude)
- return cf, size
- end
- function Placement:CalcPlacementCFrame(model, position, rotation)
- -- use other method to get info about the surface
- local cf, size = self:CalcCanvas()
- -- rotate the size so that we can properly constrain to the surface
- local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.PrimaryPart.Size
- modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))
- -- get the position relative to the surface's CFrame
- local lpos = cf:pointToObjectSpace(position);
- -- the max bounds the model can be from the surface's center
- local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
- -- constrain the position using size2
- local x = math.clamp(lpos.x, -size2.x, size2.x);
- local y = math.clamp(lpos.y, -size2.y, size2.y);
- --Grid
- local g = 5
- if (g > 0) then
- x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
- y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
- end
- -- create and return the CFrame
- return cf * CFrame.new(x, y, -modelSize.y/2) * CFrame.Angles(-math.pi/2, rotation, 0)
- end
- ---------------------------------------------------------------------
- function Placement:isColliding(model)
- local isColliding = false
- -- must have a touch interest for the :GetTouchingParts() method to work
- local touch = model.PrimaryPart.Touched:Connect(function() end)
- local touching = model.PrimaryPart:GetTouchingParts()
- -- if intersecting with something that isn't part of the model then can't place
- for i = 1, #touching do
- if (not touching[i]:IsDescendantOf(model)) then
- isColliding = true
- break
- end
- end
- -- cleanup and return
- touch:Disconnect()
- return isColliding
- end
- ---------------------------------------------------------------------
- function Placement:Place(model, cf, isColliding)
- if (not isColliding and isServer) then
- local clone = model:Clone()
- clone:SetPrimaryPartCFrame(cf)
- for i, v in pairs(clone:GetChildren()) do
- if v.Name ~= "GridPart" and v:IsA("Part") or v:IsA("MeshPart") or v:IsA("Union") then
- v.CanCollide = true
- end
- end
- clone.Parent = self.CanvasObjects
- end
- if (not isServer and not isColliding) then
- invokePlacement:FireServer("Place", model, cf, isColliding)
- cancelPlacement:Fire()
- end
- end
- ---------------------------------------------------------------------
- function Placement.fromSerialization(canvasPart, data)
- local self = Placement.new(canvasPart)
- local canvasCF = canvasPart.CFrame
- -- if data is nil or empty then this constructor is the same as .new()
- data = data or {}
- for cf, name in pairs(data) do
- -- find the furniture model with the same name
- local model = furniture:FindFirstChild(name)
- if (model) then
- -- convert the string CFrame to a real CFrame
- local components = {}
- for num in string.gmatch(cf, "[^%s,]+") do
- components[#components+1] = tonumber(num)
- end
- -- place the object
- self:Place(model, canvasCF * CFrame.new(unpack(components)), false)
- end
- end
- return self
- end
- ---------------------------------------------------------------------
- function Placement:Serialize()
- local serial = {}
- local cfi = self.CanvasPart.CFrame:inverse()
- local children = self.CanvasObjects:GetChildren()
- -- key = object space cframe string
- -- value = object name
- for i = 1, #children do
- local objectSpaceCF = cfi * children[i].PrimaryPart.CFrame
- serial[tostring(objectSpaceCF)] = children[i].Name
- end
- return serial
- end
- ---------------------------------------------------------------------
- ---------------------------------------------------------------------
- return Placement
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement