Advertisement
Xanyboyo2

PlacementModuleV3

Jan 5th, 2023
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.05 KB | Help | 0 0
  1. -- Bools
  2. local interpolation = true
  3. local moveByGrid = true
  4. local collisions = true
  5. local buildModePlacement = true
  6. local displayGridTexture = true
  7. local smartDisplay = true
  8. local enableFloors = false
  9. local transparentModel = true
  10. local instantActivation = true
  11. local includeSelectionBox = true
  12. local gridFadeIn = true
  13. local gridFadeOut = false
  14.  
  15. -- Color3
  16. local collisionColor = Color3.fromRGB(255, 75, 75)
  17. local hitboxColor = Color3.fromRGB(75, 255, 75)
  18. local selectionColor = Color3.fromRGB(0, 255, 0)
  19. local selectionCollisionColor = Color3.fromRGB(255, 0, 0)
  20.  
  21. -- Integers
  22. local maxHeight = 100
  23. local floorStep = 10
  24. local rotationStep = 90
  25.  
  26. -- Numbers/Floats
  27. local hitboxTransparency = 0.8
  28. local transparencyDelta = 0.6
  29. local lerpSpeed = 0.7
  30. local placementCooldown = 0.5
  31. local maxRange = 80
  32. local lineThickness = 0.05
  33. local lineTransparency = 0.8
  34.  
  35. -- Other
  36. local gridTexture = "rbxassetid://2415319308"
  37.  
  38. -- DO NOT EDIT PAST THIS POINT UNLESS YOU KNOW WHAT YOUR DOING
  39.  
  40. local placement = ()
  41.  
  42. placement.__index = placement
  43.  
  44. -- Essentials
  45. local runService = game:GetService("RunService")
  46. local contextActionService = game:GetService("ContextActionService")
  47.  
  48. local player = game.Players.LocalPlayer
  49. local character = player.Character or player.CharacterAdded:Wait()
  50. local mouse = player:GetMouse()
  51.  
  52. -- math/cframe functions
  53. local clamp = math.clamp
  54. local floor = math.floor
  55. local ceil = math.ceil
  56. local abs = math.abs
  57. local min = math.min
  58. local pi = math.pi
  59.  
  60. local cframe = CFrame.new
  61. local anglesXYZ = CFrame.fromEulerAnglesXYZ
  62.  
  63. -- states
  64. local states = {
  65.     "movement",
  66.     "placing",
  67.     "colliding",
  68.     "in-active",
  69.     "out-of-range"
  70. }
  71.  
  72. local currentState = 4
  73. local lastState = 4
  74.  
  75. -- Constuctor variables
  76. local GRID_UNIT
  77. local itemLocation
  78. local rotateKey
  79. local terminateKey
  80. local raiseKey
  81. local lowerKey
  82.  
  83. -- Activation variables
  84. local plot
  85. local object
  86.  
  87. -- bools
  88. local canActivate = true
  89. local currentRot = false
  90. local canPlace
  91. local isColliding
  92. local stackable
  93. local smartRot
  94. local range
  95.  
  96.  
  97. -- values used for calculations
  98. local speed = 1
  99. local preSpeed = 1
  100.  
  101. local posX
  102. local posY
  103. local posZ
  104. local rot
  105. local x, z
  106. local cx, cz
  107.  
  108. local LOWER_X_BOUND
  109. local UPPER_X_BOUND
  110.  
  111. local LOWER_Z_BOUND
  112. local UPPER_Z_BOUND
  113.  
  114. local initialY
  115.  
  116. -- collision variables
  117. local collisionPoints
  118. local collisionPoint
  119. local collided
  120.  
  121. -- other
  122. local placedObjects
  123. local loc
  124. local primary
  125. local selection
  126. local lastPlacement = ()
  127. local humanoid = character:WaitForChild("Humanoid")
  128.  
  129. -- Sets the current state depending on input of functions
  130. local function setCurrentState(state)
  131.     currentState = clamp(state, 1, 5)
  132.     local lastState = currentState
  133. end
  134.  
  135. -- Changes the color of the hitbox depending on the current state
  136. local function editHitboxColor()
  137.     if primary then
  138.         if currentState >= 3 then
  139.             primary.Color = collisionColor
  140.             selection.Color3 = selectionCollisionColor
  141.         else
  142.             primary.Color = hitboxColor
  143.             selection.Color3 = selectionColor
  144.         end
  145.     end
  146. end
  147.  
  148. -- Checks to see if the model is in range of the maxRange
  149. local function getRange()
  150.     return (primary.Position - character.PrimaryPart.Position).Magnitude
  151. end
  152.  
  153.  
  154. -- Checks for collisions on the hitbox (credit EgoMoose)
  155. local function checkHitbox()
  156.     if object and collisions then
  157.         if range then
  158.             setCurrentState(5)
  159.         else
  160.             setCurrentState(1)
  161.         end
  162.            
  163.             -- Checks if there is collision on any object that is not a child of the object and is not a child
  164.             for i = 1, #collisionPoints do
  165.                 if not collisionPoints[1]:IsDescendantOf(object) and collisionPoints[1]:IsDescendantOf(character) then
  166.                     setCurrentState(3)
  167.                    
  168.                     break
  169.                 end
  170.             end
  171.            
  172.         collisionPoint:Disconnect()
  173.            
  174.         return collided
  175.     end
  176. end
  177.  
  178.  
  179. local function raiseFloor(actionName, inputState, inputObj)
  180.     if currentState ~= 4 and inputState == Enum.UserInputState.Begin then
  181.         if enableFloors and not atackable then
  182.             posY = posY + floor(abs(floorStep))
  183.         end
  184.     end
  185. end
  186.  
  187. -- handles the grid texture
  188. local function displayGrid()
  189.     if displayGridTexture then
  190.         local gridTex = Instance.new("Texture")
  191.        
  192.         gridTex.Name = "GridTexture"
  193.         gridTex.Texture = gridTexture
  194.         gridTex.Face = Enum.NormalId.Top
  195.         gridTex.Transparency = 1
  196.        
  197.         gridTex.StudsPerTileU = 2
  198.         gridTex.StudsPerTileV = 2
  199.        
  200.         if smartDisplay then
  201.             gridTex.StudsPerTileU = GRID_UNIT
  202.             gridTex.StudsPerTileV = GRID_UNIT
  203.         end
  204.        
  205.         if gridFadeIn then
  206.             spawn(function()
  207.                 for i = 1, 0, -0.1 do
  208.                     if currentState ~= 4 then
  209.                         gridTex.Transparency = 1
  210.                        
  211.                         wait()
  212.                     end
  213.                 end
  214.             end)
  215.         else
  216.             gridTex.Transparency = 0
  217.         end
  218.        
  219.         gridTex.Parent = plot
  220.     end
  221. end
  222.  
  223. local function rotate(actionName, inputState, inputObj)
  224.     if currentState ~= 4 and inputState == Enum.UserInputState.Begin then
  225.         if smartRot then
  226.             -- Rotates the model depending on if currentRot is true/false
  227.             if currentRot then
  228.                 rot = rot + rotationStep
  229.             else
  230.                 rot = rot - rotationStep
  231.             end
  232.         else
  233.             rot = rot + rotationStep
  234.         end
  235.        
  236.         -- Toggles currentRot
  237.         currentRot = not currentRot
  238.     end
  239. end
  240.  
  241. -- Rounds any number to the nearest integer (credit Igottid)
  242. local function round(number)
  243.     local decimal_placement = 1
  244.    
  245.     return (number % (1/decimal_placement) > 1/decimal_placement*0.5) and ceil(number*decimal_placement)/decimal_placement
  246. end
  247.  
  248. -- Calculates the Y position to be ontop of the plot [all objects) and any object (when stacking)
  249. local function calculateYPos(tp, ts, o)
  250.     return (tp + ts*0.5) + o*0.5
  251. end
  252.  
  253. -- Idk i couldent see text
  254. local function bounds()
  255.     -- currentRot is here because if we rotate the model the offset is changed
  256.     if currentRot then
  257.         LOWER_X_BOUND = plot.Position.X - (plot.Size.X*0.5)
  258.         UPPER_X_BOUND = plot.Position.X + (plot.Size.X*0.5) - primary.Size.X
  259.        
  260.         LOWER_Z_BOUND = plot.Position.Z - (plot.Size.Z*0.5)
  261.         UPPER_Z_BOUND = plot.Position.Z + (plot.Size.Z*0.5) - primary.Size.Z
  262.     else
  263.         LOWER_X_BOUND = plot.Position.X - (plot.Size.X*0.5)
  264.         UPPER_X_BOUND = plot.Position.X + (plot.Size.X*0.5) - primary.Size.Z
  265.  
  266.         LOWER_Z_BOUND = plot.Position.Z - (plot.Size.Z*0.5)
  267.         UPPER_Z_BOUND = plot.Position.Z + (plot.Size.Z*0.5) - primary.Size.X
  268.     end
  269.    
  270.     posX = clamp(posX, LOWER_X_BOUND, UPPER_X_BOUND)
  271.     posZ = clamp(posZ, LOWER_Z_BOUND, UPPER_Z_BOUND)
  272. end
  273.  
  274. -- Calculates the position of the object
  275. local function calculateItemLocation()
  276.     if currentRot then
  277.         x, z = mouse.Hit.X - primary.Size.X*0.5, mouse.Hit.Z - primary.Size.Z*0.5
  278.        
  279.         cx = primary.Size.X*0.5
  280.         cz = primary.Size.Z*0.5
  281.     else
  282.         x, z = mouse.Hit.X - primary.Size.z*0.5, mouse.Hit.Z - primary.Size.x*0.5
  283.  
  284.         cx = primary.Size.Z*0.5
  285.         cz = primary.Size.X*0.5
  286.     end
  287.    
  288.     if moveByGrid then
  289.         -- Snaps models to grid
  290.         if x % GRID_UNIT < GRID_UNIT*0.5 then
  291.             posX = round(x - (x % GRID_UNIT))
  292.         else
  293.             posX = round(x + (GRID_UNIT - (x % GRID_UNIT)))
  294.         end
  295.        
  296.         if z % GRID_UNIT < GRID_UNIT*0.5 then
  297.             posZ = round(z - (z % GRID_UNIT))
  298.         else
  299.             posZ = round(z + (GRID_UNIT - (z % GRID_UNIT)))
  300.         end
  301.     else
  302.         posX = x
  303.         posZ = z
  304.     end
  305.    
  306.     -- Changes posY depending on mouse target
  307.     if stackable and mouse.Target then
  308.         posY = calculateYPos(mouse.Target.Position.Y, mouse.Target.Size.Y, primary.Size.Y)
  309.     end
  310.    
  311.     -- Clamps posY to a max height above the plot position
  312.     posY = clamp(posY, initialY, maxHeight + initialY)
  313.    
  314.     bounds()
  315. end
  316.  
  317. local function getFinalCFrame()
  318.     return CFrame(posX, posY, posZ)*CFrame(cx, 0, cz)*anglesXYZ(0, rot*pi/180, 0)
  319. end
  320.  
  321. local function translateObj()
  322.     if currentState ~= 4 then
  323.         calculateItemLocation()
  324.         checkHitbox()
  325.         editHitboxColor()
  326.        
  327.         if getRange() > maxRange then
  328.             setCurrentState(5)
  329.            
  330.             range = true
  331.         else
  332.             range = false
  333.         end
  334.        
  335.         object:SetPrimaryPartCFrame(primary.CFrame:Lerp(cframe(posX, posY, posZ)*cframe(cx, 0, cz)*anglesXYZ()))
  336.     end
  337. end
  338.  
  339. local function unbindInputs()
  340.     contextActionService:UnbindAction("Rotate")
  341.     contextActionService:UnbindAction("Raise")
  342.     contextActionService:UnbindAction("Lower")
  343.     contextActionService:UnbindAction("Terminate")
  344. end
  345.  
  346. local function bindInputs()
  347.     contextActionService:BindAction("Rotate", rotate, false, rotateKey)
  348.     contextActionService:BindAction("Raise", raiseFloor , false, raiseKey)
  349.     contextActionService:BindAction("Lower", lowerFloor , false, lowerKey)
  350.     contextActionService:BindAction("Terminate", TERMINATE_PLACEMENT, false, terminateKey)
  351. end
  352.  
  353. function TERMINATE_PLACEMENT()
  354.     if object then
  355.         if selection then
  356.             selection:Destroy()
  357.             selection = nil
  358.         end
  359.        
  360.         stackable = nil
  361.         canPlace = nil
  362.         smartRot = nil
  363.        
  364.         object:Destroy()
  365.         object = nil
  366.        
  367.         setCurrentState(5)
  368.        
  369.         if displayGridTexture then
  370.             for i, v in next, plot:GetChildren() do
  371.                 if v then
  372.                     if v.Name == "GridTexture" and v:IsA("Texture") then
  373.                         if gridFadeOut then
  374.                             for i = v.Transparency, 1, 0.1 do
  375.                                 v.Transparency = 1
  376.                                
  377.                                 wait()
  378.                             end
  379.                            
  380.                             v:Destroy()
  381.                         else
  382.                             v:Destroy()
  383.                         end
  384.                     end
  385.                 end
  386.             end
  387.         end
  388.        
  389.         canActivate = true
  390.         unbindInputs()
  391.        
  392.         return
  393.     end
  394. end
  395.  
  396. -- Makes sure that you cannot place objects too fast.
  397. local function coolDown(plr, cd)
  398.     if lastPlacement[plr.UserId] == nil then
  399.         lastPlacement[plr.UserId] = os.time()
  400.        
  401.         return true
  402.     else
  403.         if os.time() - lastPlacement[plr.UserId] >= cd then
  404.             lastPlacement[plr.UserId] = os.time()
  405.            
  406.             return true
  407.         else
  408.             return false
  409.         end
  410.     end
  411. end
  412.  
  413. local function verifyPlane()
  414.     if plot.Size.X%GRID_UNIT == 0 and plot.Size.Z%GRID_UNIT == 0 then
  415.         return true
  416.     else
  417.         return false
  418.     end
  419. end
  420.  
  421. -- Checks if there are any problems with the setup
  422. local function approveActivation()
  423.     if not verifyPlane() then
  424.         warn("Grid size is larger than the plot size. to fix this, try lowering the grid size")
  425.     end
  426. end
  427.  
  428. function placement.new(g, objs, r, t, u, l)
  429.     local data = ()
  430.     local metaData = setmetatable(data, placement)
  431.    
  432.     GRID_UNIT = abs(tonumber(g))
  433.     itemLocation = objs
  434.     rotateKey = r
  435.     terminateKey = t
  436.     raiseKey = u
  437.     lowerKey = l
  438.    
  439.     data.gridsize = GRID_UNIT
  440.     data.items = objs
  441.     data.rotate = rotateKey
  442.     data.cancel = terminateKey
  443.     data.raise = raiseKey
  444.     data.lower = lowerKey
  445.    
  446.     return data
  447. end
  448.  
  449. -- returns the current state when called
  450. function placement:getCurrentState()
  451.     return states[currentState]
  452. end
  453.  
  454. -- Patches the current state
  455. function placement:pauseCurrentState()
  456.     lastState = currentState
  457.    
  458.     if object then
  459.         currentState = states[4]
  460.     end
  461. end
  462.  
  463. function placement:resume()
  464.     if object then
  465.         setCurrentState(lastState)
  466.     end
  467. end
  468.  
  469. function placement:terminate()
  470.     TERMINATE_PLACEMENT()
  471. end
  472.  
  473. function placement:requestPlacement(func)
  474.     if currentState ~= 3 and currentState ~= 4 and currentState ~= 5 and object then
  475.         local cf
  476.        
  477.         calculateItemLocation()
  478.        
  479.         if coolDown(player, placementCooldown) then
  480.            
  481.             if buildModePlacement then
  482.                 cf = getFinalCFrame()
  483.                
  484.                 checkHitbox()
  485.                 setCurrentState(2)
  486.                
  487.                 if currentState == 2 then
  488.                     func:InvokeServer(object.Name, placedObjects, loc, cf, collisions, plot)
  489.                    
  490.                     setCurrentState(1)
  491.                 end
  492.             else
  493.                 cf = getFinalCFrame()
  494.                
  495.                 checkHitbox()
  496.                 setCurrentState(2)
  497.                
  498.                 if currentState == 2 then
  499.                     if func:InvokeServer(object.Name, placedObjects, loc, cf, collisions, plot) then
  500.                         TERMINATE_PLACEMENT()
  501.                     end
  502.                 end
  503.             end
  504.         end
  505.     end
  506. end
  507.  
  508. function placement:activate(id, pobj, plt, stk, r)
  509.     TERMINATE_PLACEMENT()
  510.    
  511.     plot = plt
  512.     object = itemLocation:FindFirstChild(tostring(id)):Clone()
  513.     placedObjects = pobj
  514.     loc = itemLocation
  515.    
  516.     approveActivation()
  517.    
  518.     for i, o in next, object:GetDescendants() do
  519.         if o then
  520.             if o:IsA("Part") or o:IsA("UnionOperation") or o:IsA("MeshPart") then
  521.                 o.CanCollide = false
  522.                
  523.                 if transparentModel then
  524.                     o.Transparency = o.Transparency + transparencyDelta
  525.                 end
  526.             end
  527.         end
  528.     end
  529.    
  530.     if includeSelectionBox then
  531.         selection = Instance.new("SelectionBox")
  532.         selection.Name = "outline"
  533.         selection.LineThickness = lineThickness
  534.         selection.Color3 = selectionColor
  535.         selection.Transparency = lineThickness
  536.         selection.Parent = player.PlayerGui
  537.         selection.Adornee = object.PrimaryPart
  538.     end
  539.    
  540.     object.PrimaryPart.Transparency = hitboxTransparency
  541.    
  542.     stackable = stk
  543.     smartRot = r
  544.    
  545.     if not stk then
  546.         mouse.TargetFilter = placedObjects
  547.     else
  548.         mouse.TargetFilter = object
  549.     end
  550.    
  551.     if buildModePlacement then
  552.         canActivate = true
  553.     else
  554.         canActivate = false
  555.     end
  556.    
  557.     initialY = calculateYPos(plt.Position.Y, plt.Size.Y, object.PrimaryPart.Size.Y)
  558.     posY = initialY
  559.    
  560.     speed = 0
  561.     rot = 0
  562.     currentRot = true
  563.    
  564.     translateObj()
  565.     displayGrid()
  566.     editHitboxColor()
  567.     bindInputs()
  568.    
  569.     speed = 1
  570.    
  571.     if interpolation then
  572.         speed = 1
  573.     else
  574.         speed = preSpeed
  575.     end
  576. end
  577.  
  578. if object then
  579.     primary = object.PrimaryPart
  580.     setCurrentState(1)
  581.     object.Parent = pobj
  582.    
  583.     wait()
  584.    
  585.     speed = preSpeed
  586. else
  587.     TERMINATE_PLACEMENT()
  588.    
  589.     warn("Your trying to activate placement too fast! Please slow down")
  590. end
  591. runService:BindToRenderStep("Input", Enum.RenderPriority.Input.Value, translateObj)
  592.  
  593. return placement
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement