Advertisement
NeonStranger

Placement System

Jul 26th, 2022 (edited)
1,240
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.20 KB | None | 0 0
  1. local isServer = game:GetService("RunService"):IsServer()
  2. local initPlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteFunctions").InitPlacement
  3. local invokePlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents").InvokePlacement
  4. local cancelPlacement = game:GetService("ReplicatedStorage"):WaitForChild("RemoteEvents").CancelPlacement
  5.  
  6.  
  7. local Placement = {}
  8. Placement.__index = Placement
  9.  
  10. function Placement.new(canvasPart)
  11.    
  12. -----Self------------------------------------------------------------
  13.    
  14.     local self = setmetatable({}, Placement)
  15.    
  16.     self.CanvasPart = canvasPart
  17.    
  18.     if (isServer) then
  19.        
  20.         self.CanvasObjects = Instance.new("Folder")
  21.         self.CanvasObjects.Name = "CanvasObjects"
  22.         self.CanvasObjects.Parent = canvasPart
  23.        
  24.     else
  25.        
  26.         self.CanvasObjects = initPlacement:InvokeServer(canvasPart)
  27.        
  28.     end
  29.    
  30.    
  31.     self.Surface = Enum.NormalId.Top
  32.     self.GridUnit = 4
  33.    
  34.     return self
  35.    
  36.    
  37. end
  38.  
  39. ---------------------------------------------------------------------
  40.  
  41.  
  42. --Methods------------------------------------------------------------
  43.  
  44. function Placement:CalcCanvas()
  45.     local canvasSize = self.CanvasPart.Size
  46.    
  47.     local back = Vector3.new(0,-1,0)
  48.     local top = Vector3.new(0,0,-1)
  49.     local right = Vector3.new(-1,0,0)
  50.    
  51.     -- convert to world space
  52.     local cf = self.CanvasPart.CFrame * CFrame.fromMatrix(-back*canvasSize/2, right, top, back)
  53.     -- use object space vectors to find the width and height
  54.     local size = Vector2.new((canvasSize * right).magnitude, (canvasSize * top).magnitude)
  55.  
  56.     return cf, size
  57. end
  58.  
  59. function Placement:CalcPlacementCFrame(model, position, rotation)
  60.     -- use other method to get info about the surface
  61.     local cf, size = self:CalcCanvas()
  62.  
  63.     -- rotate the size so that we can properly constrain to the surface
  64.     local modelSize = CFrame.fromEulerAnglesYXZ(0, rotation, 0) * model.PrimaryPart.Size
  65.     modelSize = Vector3.new(math.abs(modelSize.x), math.abs(modelSize.y), math.abs(modelSize.z))
  66.  
  67.     -- get the position relative to the surface's CFrame
  68.     local lpos = cf:pointToObjectSpace(position);
  69.     -- the max bounds the model can be from the surface's center
  70.     local size2 = (size - Vector2.new(modelSize.x, modelSize.z))/2
  71.  
  72.     -- constrain the position using size2
  73.     local x = math.clamp(lpos.x, -size2.x, size2.x);
  74.     local y = math.clamp(lpos.y, -size2.y, size2.y);
  75.     --Grid
  76.     local g = 5
  77.     if (g > 0) then
  78.         x = math.sign(x)*((math.abs(x) - math.abs(x) % g) + (size2.x % g))
  79.         y = math.sign(y)*((math.abs(y) - math.abs(y) % g) + (size2.y % g))
  80.     end
  81.     -- create and return the CFrame
  82.     return cf * CFrame.new(x, y, -modelSize.y/2) * CFrame.Angles(-math.pi/2, rotation, 0)
  83.    
  84.    
  85. end
  86.  
  87.  
  88.  
  89. ---------------------------------------------------------------------
  90. function Placement:isColliding(model)
  91.     local isColliding = false
  92.  
  93.     -- must have a touch interest for the :GetTouchingParts() method to work
  94.     local touch = model.PrimaryPart.Touched:Connect(function() end)
  95.     local touching = model.PrimaryPart:GetTouchingParts()
  96.  
  97.     -- if intersecting with something that isn't part of the model then can't place
  98.     for i = 1, #touching do
  99.         if (not touching[i]:IsDescendantOf(model)) then
  100.             isColliding = true
  101.             break
  102.         end
  103.     end
  104.  
  105.     -- cleanup and return
  106.     touch:Disconnect()
  107.     return isColliding
  108. end
  109.  
  110. ---------------------------------------------------------------------
  111. function Placement:Place(model, cf, isColliding)
  112.     if (not isColliding and isServer) then
  113.         local clone = model:Clone()
  114.         clone:SetPrimaryPartCFrame(cf)
  115.         for i, v in pairs(clone:GetChildren()) do
  116.             if v.Name ~= "GridPart" and v:IsA("Part") or v:IsA("MeshPart") or v:IsA("Union") then
  117.                 v.CanCollide = true
  118.             end
  119.         end
  120.         clone.Parent = self.CanvasObjects
  121.     end
  122.     if (not isServer and not isColliding) then
  123.         invokePlacement:FireServer("Place", model, cf, isColliding)
  124.         cancelPlacement:Fire()
  125.     end
  126. end
  127.  
  128. ---------------------------------------------------------------------
  129.  
  130. function Placement.fromSerialization(canvasPart, data)
  131.     local self = Placement.new(canvasPart)
  132.     local canvasCF = canvasPart.CFrame
  133.  
  134.     -- if data is nil or empty then this constructor is the same as .new()
  135.     data = data or {}
  136.  
  137.     for cf, name in pairs(data) do
  138.         -- find the furniture model with the same name
  139.         local model = furniture:FindFirstChild(name)
  140.         if (model) then
  141.             -- convert the string CFrame to a real CFrame
  142.             local components = {}
  143.             for num in string.gmatch(cf, "[^%s,]+") do
  144.                 components[#components+1] = tonumber(num)
  145.             end
  146.  
  147.             -- place the object
  148.             self:Place(model, canvasCF * CFrame.new(unpack(components)), false)
  149.         end
  150.     end
  151.  
  152.     return self
  153. end
  154.  
  155. ---------------------------------------------------------------------
  156.  
  157. function Placement:Serialize()
  158.     local serial = {}
  159.  
  160.     local cfi = self.CanvasPart.CFrame:inverse()
  161.     local children = self.CanvasObjects:GetChildren()
  162.  
  163.     -- key = object space cframe string
  164.     -- value = object name
  165.     for i = 1, #children do
  166.         local objectSpaceCF = cfi * children[i].PrimaryPart.CFrame
  167.         serial[tostring(objectSpaceCF)] = children[i].Name
  168.     end
  169.  
  170.     return serial
  171. end
  172.  
  173. ---------------------------------------------------------------------
  174.  
  175.  
  176. ---------------------------------------------------------------------
  177.  
  178. return Placement
  179.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement