Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Bools
- local interpolation = true
- local moveByGrid = true
- local collisions = true
- local buildModePlacement = true
- local displayGridTexture = true
- local smartDisplay = true
- local enableFloors = false
- local transparentModel = true
- local instantActivation = true
- local includeSelectionBox = true
- local gridFadeIn = true
- local gridFadeOut = false
- -- Color3
- local collisionColor = Color3.fromRGB(255, 75, 75)
- local hitboxColor = Color3.fromRGB(75, 255, 75)
- local selectionColor = Color3.fromRGB(0, 255, 0)
- local selectionCollisionColor = Color3.fromRGB(255, 0, 0)
- -- Integers
- local maxHeight = 100
- local floorStep = 10
- local rotationStep = 90
- -- Numbers/Floats
- local hitboxTransparency = 0.8
- local transparencyDelta = 0.6
- local lerpSpeed = 0.7
- local placementCooldown = 0.5
- local maxRange = 80
- local lineThickness = 0.05
- local lineTransparency = 0.8
- -- Other
- local gridTexture = "rbxassetid://2415319308"
- -- DO NOT EDIT PAST THIS POINT UNLESS YOU KNOW WHAT YOUR DOING
- local placement = ()
- placement.__index = placement
- -- Essentials
- local runService = game:GetService("RunService")
- local contextActionService = game:GetService("ContextActionService")
- local player = game.Players.LocalPlayer
- local character = player.Character or player.CharacterAdded:Wait()
- local mouse = player:GetMouse()
- -- math/cframe functions
- local clamp = math.clamp
- local floor = math.floor
- local ceil = math.ceil
- local abs = math.abs
- local min = math.min
- local pi = math.pi
- local cframe = CFrame.new
- local anglesXYZ = CFrame.fromEulerAnglesXYZ
- -- states
- local states = {
- "movement",
- "placing",
- "colliding",
- "in-active",
- "out-of-range"
- }
- local currentState = 4
- local lastState = 4
- -- Constuctor variables
- local GRID_UNIT
- local itemLocation
- local rotateKey
- local terminateKey
- local raiseKey
- local lowerKey
- -- Activation variables
- local plot
- local object
- -- bools
- local canActivate = true
- local currentRot = false
- local canPlace
- local isColliding
- local stackable
- local smartRot
- local range
- -- values used for calculations
- local speed = 1
- local preSpeed = 1
- local posX
- local posY
- local posZ
- local rot
- local x, z
- local cx, cz
- local LOWER_X_BOUND
- local UPPER_X_BOUND
- local LOWER_Z_BOUND
- local UPPER_Z_BOUND
- local initialY
- -- collision variables
- local collisionPoints
- local collisionPoint
- local collided
- -- other
- local placedObjects
- local loc
- local primary
- local selection
- local lastPlacement = ()
- local humanoid = character:WaitForChild("Humanoid")
- -- Sets the current state depending on input of functions
- local function setCurrentState(state)
- currentState = clamp(state, 1, 5)
- local lastState = currentState
- end
- -- Changes the color of the hitbox depending on the current state
- local function editHitboxColor()
- if primary then
- if currentState >= 3 then
- primary.Color = collisionColor
- selection.Color3 = selectionCollisionColor
- else
- primary.Color = hitboxColor
- selection.Color3 = selectionColor
- end
- end
- end
- -- Checks to see if the model is in range of the maxRange
- local function getRange()
- return (primary.Position - character.PrimaryPart.Position).Magnitude
- end
- -- Checks for collisions on the hitbox (credit EgoMoose)
- local function checkHitbox()
- if object and collisions then
- if range then
- setCurrentState(5)
- else
- setCurrentState(1)
- end
- -- Checks if there is collision on any object that is not a child of the object and is not a child
- for i = 1, #collisionPoints do
- if not collisionPoints[1]:IsDescendantOf(object) and collisionPoints[1]:IsDescendantOf(character) then
- setCurrentState(3)
- break
- end
- end
- collisionPoint:Disconnect()
- return collided
- end
- end
- local function raiseFloor(actionName, inputState, inputObj)
- if currentState ~= 4 and inputState == Enum.UserInputState.Begin then
- if enableFloors and not atackable then
- posY = posY + floor(abs(floorStep))
- end
- end
- end
- -- handles the grid texture
- local function displayGrid()
- if displayGridTexture then
- local gridTex = Instance.new("Texture")
- gridTex.Name = "GridTexture"
- gridTex.Texture = gridTexture
- gridTex.Face = Enum.NormalId.Top
- gridTex.Transparency = 1
- gridTex.StudsPerTileU = 2
- gridTex.StudsPerTileV = 2
- if smartDisplay then
- gridTex.StudsPerTileU = GRID_UNIT
- gridTex.StudsPerTileV = GRID_UNIT
- end
- if gridFadeIn then
- spawn(function()
- for i = 1, 0, -0.1 do
- if currentState ~= 4 then
- gridTex.Transparency = 1
- wait()
- end
- end
- end)
- else
- gridTex.Transparency = 0
- end
- gridTex.Parent = plot
- end
- end
- local function rotate(actionName, inputState, inputObj)
- if currentState ~= 4 and inputState == Enum.UserInputState.Begin then
- if smartRot then
- -- Rotates the model depending on if currentRot is true/false
- if currentRot then
- rot = rot + rotationStep
- else
- rot = rot - rotationStep
- end
- else
- rot = rot + rotationStep
- end
- -- Toggles currentRot
- currentRot = not currentRot
- end
- end
- -- Rounds any number to the nearest integer (credit Igottid)
- local function round(number)
- local decimal_placement = 1
- return (number % (1/decimal_placement) > 1/decimal_placement*0.5) and ceil(number*decimal_placement)/decimal_placement
- end
- -- Calculates the Y position to be ontop of the plot [all objects) and any object (when stacking)
- local function calculateYPos(tp, ts, o)
- return (tp + ts*0.5) + o*0.5
- end
- -- Idk i couldent see text
- local function bounds()
- -- currentRot is here because if we rotate the model the offset is changed
- if currentRot then
- LOWER_X_BOUND = plot.Position.X - (plot.Size.X*0.5)
- UPPER_X_BOUND = plot.Position.X + (plot.Size.X*0.5) - primary.Size.X
- LOWER_Z_BOUND = plot.Position.Z - (plot.Size.Z*0.5)
- UPPER_Z_BOUND = plot.Position.Z + (plot.Size.Z*0.5) - primary.Size.Z
- else
- LOWER_X_BOUND = plot.Position.X - (plot.Size.X*0.5)
- UPPER_X_BOUND = plot.Position.X + (plot.Size.X*0.5) - primary.Size.Z
- LOWER_Z_BOUND = plot.Position.Z - (plot.Size.Z*0.5)
- UPPER_Z_BOUND = plot.Position.Z + (plot.Size.Z*0.5) - primary.Size.X
- end
- posX = clamp(posX, LOWER_X_BOUND, UPPER_X_BOUND)
- posZ = clamp(posZ, LOWER_Z_BOUND, UPPER_Z_BOUND)
- end
- -- Calculates the position of the object
- local function calculateItemLocation()
- if currentRot then
- x, z = mouse.Hit.X - primary.Size.X*0.5, mouse.Hit.Z - primary.Size.Z*0.5
- cx = primary.Size.X*0.5
- cz = primary.Size.Z*0.5
- else
- x, z = mouse.Hit.X - primary.Size.z*0.5, mouse.Hit.Z - primary.Size.x*0.5
- cx = primary.Size.Z*0.5
- cz = primary.Size.X*0.5
- end
- if moveByGrid then
- -- Snaps models to grid
- if x % GRID_UNIT < GRID_UNIT*0.5 then
- posX = round(x - (x % GRID_UNIT))
- else
- posX = round(x + (GRID_UNIT - (x % GRID_UNIT)))
- end
- if z % GRID_UNIT < GRID_UNIT*0.5 then
- posZ = round(z - (z % GRID_UNIT))
- else
- posZ = round(z + (GRID_UNIT - (z % GRID_UNIT)))
- end
- else
- posX = x
- posZ = z
- end
- -- Changes posY depending on mouse target
- if stackable and mouse.Target then
- posY = calculateYPos(mouse.Target.Position.Y, mouse.Target.Size.Y, primary.Size.Y)
- end
- -- Clamps posY to a max height above the plot position
- posY = clamp(posY, initialY, maxHeight + initialY)
- bounds()
- end
- local function getFinalCFrame()
- return CFrame(posX, posY, posZ)*CFrame(cx, 0, cz)*anglesXYZ(0, rot*pi/180, 0)
- end
- local function translateObj()
- if currentState ~= 4 then
- calculateItemLocation()
- checkHitbox()
- editHitboxColor()
- if getRange() > maxRange then
- setCurrentState(5)
- range = true
- else
- range = false
- end
- object:SetPrimaryPartCFrame(primary.CFrame:Lerp(cframe(posX, posY, posZ)*cframe(cx, 0, cz)*anglesXYZ()))
- end
- end
- local function unbindInputs()
- contextActionService:UnbindAction("Rotate")
- contextActionService:UnbindAction("Raise")
- contextActionService:UnbindAction("Lower")
- contextActionService:UnbindAction("Terminate")
- end
- local function bindInputs()
- contextActionService:BindAction("Rotate", rotate, false, rotateKey)
- contextActionService:BindAction("Raise", raiseFloor , false, raiseKey)
- contextActionService:BindAction("Lower", lowerFloor , false, lowerKey)
- contextActionService:BindAction("Terminate", TERMINATE_PLACEMENT, false, terminateKey)
- end
- function TERMINATE_PLACEMENT()
- if object then
- if selection then
- selection:Destroy()
- selection = nil
- end
- stackable = nil
- canPlace = nil
- smartRot = nil
- object:Destroy()
- object = nil
- setCurrentState(5)
- if displayGridTexture then
- for i, v in next, plot:GetChildren() do
- if v then
- if v.Name == "GridTexture" and v:IsA("Texture") then
- if gridFadeOut then
- for i = v.Transparency, 1, 0.1 do
- v.Transparency = 1
- wait()
- end
- v:Destroy()
- else
- v:Destroy()
- end
- end
- end
- end
- end
- canActivate = true
- unbindInputs()
- return
- end
- end
- -- Makes sure that you cannot place objects too fast.
- local function coolDown(plr, cd)
- if lastPlacement[plr.UserId] == nil then
- lastPlacement[plr.UserId] = os.time()
- return true
- else
- if os.time() - lastPlacement[plr.UserId] >= cd then
- lastPlacement[plr.UserId] = os.time()
- return true
- else
- return false
- end
- end
- end
- local function verifyPlane()
- if plot.Size.X%GRID_UNIT == 0 and plot.Size.Z%GRID_UNIT == 0 then
- return true
- else
- return false
- end
- end
- -- Checks if there are any problems with the setup
- local function approveActivation()
- if not verifyPlane() then
- warn("Grid size is larger than the plot size. to fix this, try lowering the grid size")
- end
- end
- function placement.new(g, objs, r, t, u, l)
- local data = ()
- local metaData = setmetatable(data, placement)
- GRID_UNIT = abs(tonumber(g))
- itemLocation = objs
- rotateKey = r
- terminateKey = t
- raiseKey = u
- lowerKey = l
- data.gridsize = GRID_UNIT
- data.items = objs
- data.rotate = rotateKey
- data.cancel = terminateKey
- data.raise = raiseKey
- data.lower = lowerKey
- return data
- end
- -- returns the current state when called
- function placement:getCurrentState()
- return states[currentState]
- end
- -- Patches the current state
- function placement:pauseCurrentState()
- lastState = currentState
- if object then
- currentState = states[4]
- end
- end
- function placement:resume()
- if object then
- setCurrentState(lastState)
- end
- end
- function placement:terminate()
- TERMINATE_PLACEMENT()
- end
- function placement:requestPlacement(func)
- if currentState ~= 3 and currentState ~= 4 and currentState ~= 5 and object then
- local cf
- calculateItemLocation()
- if coolDown(player, placementCooldown) then
- if buildModePlacement then
- cf = getFinalCFrame()
- checkHitbox()
- setCurrentState(2)
- if currentState == 2 then
- func:InvokeServer(object.Name, placedObjects, loc, cf, collisions, plot)
- setCurrentState(1)
- end
- else
- cf = getFinalCFrame()
- checkHitbox()
- setCurrentState(2)
- if currentState == 2 then
- if func:InvokeServer(object.Name, placedObjects, loc, cf, collisions, plot) then
- TERMINATE_PLACEMENT()
- end
- end
- end
- end
- end
- end
- function placement:activate(id, pobj, plt, stk, r)
- TERMINATE_PLACEMENT()
- plot = plt
- object = itemLocation:FindFirstChild(tostring(id)):Clone()
- placedObjects = pobj
- loc = itemLocation
- approveActivation()
- for i, o in next, object:GetDescendants() do
- if o then
- if o:IsA("Part") or o:IsA("UnionOperation") or o:IsA("MeshPart") then
- o.CanCollide = false
- if transparentModel then
- o.Transparency = o.Transparency + transparencyDelta
- end
- end
- end
- end
- if includeSelectionBox then
- selection = Instance.new("SelectionBox")
- selection.Name = "outline"
- selection.LineThickness = lineThickness
- selection.Color3 = selectionColor
- selection.Transparency = lineThickness
- selection.Parent = player.PlayerGui
- selection.Adornee = object.PrimaryPart
- end
- object.PrimaryPart.Transparency = hitboxTransparency
- stackable = stk
- smartRot = r
- if not stk then
- mouse.TargetFilter = placedObjects
- else
- mouse.TargetFilter = object
- end
- if buildModePlacement then
- canActivate = true
- else
- canActivate = false
- end
- initialY = calculateYPos(plt.Position.Y, plt.Size.Y, object.PrimaryPart.Size.Y)
- posY = initialY
- speed = 0
- rot = 0
- currentRot = true
- translateObj()
- displayGrid()
- editHitboxColor()
- bindInputs()
- speed = 1
- if interpolation then
- speed = 1
- else
- speed = preSpeed
- end
- end
- if object then
- primary = object.PrimaryPart
- setCurrentState(1)
- object.Parent = pobj
- wait()
- speed = preSpeed
- else
- TERMINATE_PLACEMENT()
- warn("Your trying to activate placement too fast! Please slow down")
- end
- runService:BindToRenderStep("Input", Enum.RenderPriority.Input.Value, translateObj)
- return placement
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement