Copy salient and essential scripts and models from Plane Building Roblox
This commit is contained in:
@@ -0,0 +1,627 @@
|
||||
local run = game:GetService("RunService")
|
||||
local inputService = game:GetService("UserInputService")
|
||||
local collectionService = _G.Tags
|
||||
local mouseModule = require(_G.Modules.MouseModule)
|
||||
local methodModule = require(_G.Modules.MethodModule)
|
||||
local partStateModule = require(_G.Modules.PartStateModule)
|
||||
local selectionBox = script.SelectionBox
|
||||
local adjacentBox = script.AdjacentBox
|
||||
local statGui
|
||||
if game.Players.LocalPlayer then
|
||||
statGui = game.Players.LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("StatGui")
|
||||
end
|
||||
local gui
|
||||
local current = nil
|
||||
|
||||
local mode = "place"
|
||||
local realMode = "place"
|
||||
local rotSnap = 15
|
||||
local rotSnapKey = 45.
|
||||
local moveSnap = 0.5
|
||||
local modelScaleSnap = 0.1
|
||||
local touchModeConnect = true
|
||||
local touchingBoxes = {}
|
||||
|
||||
local controlModifier = false -- embed
|
||||
local shiftModifier = false -- lower snap
|
||||
|
||||
local ModeButtons = {
|
||||
clone = nil,
|
||||
resize = nil
|
||||
}
|
||||
|
||||
local pointer = nil
|
||||
|
||||
local function temp()
|
||||
local a = Instance.new("Part")
|
||||
a.Parent = game.Workspace
|
||||
a.Size = Vector3.new(1,1,1)
|
||||
a.Anchored = true
|
||||
a.CanCollide = false
|
||||
a.Transparency = 0.5
|
||||
a.Material = Enum.Material.Neon
|
||||
return a
|
||||
end
|
||||
|
||||
local a = temp()
|
||||
local b = temp()
|
||||
|
||||
local modeEnd = function()
|
||||
if current.axes then current.axes:Destroy() end
|
||||
end
|
||||
local focusAxes = function()
|
||||
current.axes:ScaleTo(current.size.Magnitude*1.3)
|
||||
methodModule.setItemCFrame(current.axes,current.cframe)
|
||||
for _,part in pairs(current.axes:GetChildren()) do
|
||||
if not current.axis then
|
||||
part.Transparency = 0.5
|
||||
else
|
||||
part.Transparency = 1
|
||||
end
|
||||
end
|
||||
if current.axis then current.axis.Transparency = 0 end
|
||||
end
|
||||
local createAxes = function(name)
|
||||
current.axes = _G.Storage.Assets[name]:Clone()
|
||||
current.axes.Parent = game.Workspace
|
||||
current.axes.PrimaryPart.Shape = Enum.PartType.Ball
|
||||
focusAxes()
|
||||
end
|
||||
local getBounds = function()
|
||||
local cframe,size
|
||||
if current.model:IsA("Model") then
|
||||
cframe,size = current.model:GetBoundingBox()
|
||||
else
|
||||
cframe = current.model.CFrame
|
||||
size = current.model.size
|
||||
end
|
||||
current.cframe = cframe
|
||||
current.size = size
|
||||
end
|
||||
local styleModel = function()
|
||||
local function style(part)
|
||||
part.Anchored = true
|
||||
part.CanCollide = false
|
||||
end
|
||||
if current.model:IsA("BasePart") then style(current.model) end
|
||||
for _,part in pairs(current.model:GetChildren()) do
|
||||
if part:IsA("BasePart") then
|
||||
style(part)
|
||||
end
|
||||
end
|
||||
end
|
||||
local distanceAlong = function(o : Vector3,n : Vector3,p : Vector3) -- origin, normal, point
|
||||
local k = (n.X*(p.X-o.X)+n.Y*(p.Y-o.Y)+n.Z*(p.Z-o.Z))--/(n.x*n.x+n.y*n.y+n.z*n.z) -- is 1
|
||||
return (n*k+o-p).Magnitude
|
||||
end
|
||||
local processPointer = function(inputObject)
|
||||
local position = inputObject.Position
|
||||
local result,ray = mouseModule.getMouseHit() -- get whatever we're hovering
|
||||
current.location = position
|
||||
current.ray = ray
|
||||
current.result = result
|
||||
end
|
||||
|
||||
local function axisClick(inputObject) -- For whenever we click in a mode supporting axis
|
||||
if current.axes then -- if there are axes (there should be)
|
||||
processPointer(inputObject)
|
||||
if current.result then
|
||||
if current.result.Instance.Parent == current.axes then -- if it's an axis
|
||||
current.axis = current.result.Instance -- set it
|
||||
pointer = inputObject -- set the inputObject for current selection
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function round(v : Vector3,snap) -- just round a vector based on the snapping
|
||||
snap = snap or moveSnap
|
||||
return Vector3.new(math.round(v.X/snap)*snap,math.round(v.Y/snap)*snap,math.round(v.Z/snap)*snap)
|
||||
end
|
||||
|
||||
local function mask(a : Vector3,b : Vector3) -- multiply each axis together
|
||||
return Vector3.new(a.X*b.X,a.Y*b.Y,a.Z*b.Z)
|
||||
end
|
||||
|
||||
local function clamp(a : Vector3, min : Vector3, max : Vector3)
|
||||
return Vector3.new(math.clamp(a.X,min.X,max.X),math.clamp(a.Y,min.Y,max.Y),math.clamp(a.Z,min.Z,max.Z))
|
||||
end
|
||||
|
||||
local function rayProjectToRay(ray,axis,root)
|
||||
local n2 = ray.Direction
|
||||
local o2 = ray.Origin
|
||||
local n1 = (axis.Position - axis.Parent.PrimaryPart.Position).Unit
|
||||
local o1 = root or current.axis.Position
|
||||
-- using more vector magic to find the closest point on the axis line to the ray of the pointer
|
||||
local s = n2:Dot(n1)
|
||||
return o1+n1*((n1:Dot(o2-o1)+s*(n2:Dot(o1-o2)))/(1-s*s))
|
||||
end
|
||||
|
||||
local function axisDragClick(inputObject)
|
||||
axisClick(inputObject)
|
||||
if current.axis then
|
||||
local p = rayProjectToRay(current.ray,current.axis)
|
||||
current.modelOrigin = current.touchingCFrame
|
||||
current.dragOrigin = current.axes.PrimaryPart.CFrame:PointToObjectSpace(p)
|
||||
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
|
||||
end
|
||||
end
|
||||
|
||||
local function rayProjectToPlane(ray,axis)
|
||||
-- using some vector magic to find the point of intersection between the ray of the pointer and the plane of rotation (arc handle plane)
|
||||
return ray.Origin+(axis.CFrame.RightVector:Dot(axis.Position-ray.Origin))/(axis.CFrame.RightVector:Dot(ray.Direction))*ray.Direction
|
||||
end
|
||||
|
||||
local function axisRotateClick(inputObject)
|
||||
axisClick(inputObject)
|
||||
if current.axis then
|
||||
local p = rayProjectToPlane(current.ray,current.axis)
|
||||
current.dragOrigin = current.axis.CFrame:PointToObjectSpace(p)
|
||||
current.modelOrigin = current.touchingCFrame
|
||||
current.axisOrigin = current.touching.CFrame:ToObjectSpace(current.axis.CFrame)
|
||||
current.initialRotation = current.rotation
|
||||
end
|
||||
end
|
||||
|
||||
local modeStart = {}
|
||||
modeStart.clone = function()
|
||||
modeEnd()
|
||||
end
|
||||
modeStart.rotate = function()
|
||||
modeEnd()
|
||||
createAxes("RotateAxes")
|
||||
end
|
||||
modeStart.resize = function()
|
||||
modeEnd()
|
||||
createAxes("ResizeAxes")
|
||||
for _,item in pairs(current.axes:GetChildren()) do
|
||||
local axis = item.Name:match("Part(.)")
|
||||
if axis then
|
||||
if (current.scaleTerms or Vector3.one)[axis] == 0 then axis:Destroy() end -- if the scale terms say that this axis should not be scaled
|
||||
if current.model:IsA("Model") then item.Color = Color3.fromRGB(13, 105, 172) end
|
||||
end
|
||||
end
|
||||
end
|
||||
modeStart.move = function()
|
||||
modeEnd()
|
||||
createAxes("MoveAxes")
|
||||
end
|
||||
modeStart.place = function()
|
||||
modeEnd()
|
||||
end
|
||||
|
||||
local function setMode(newMode)
|
||||
for _,other_button in pairs(_G.PlaceModeFrame:GetChildren()) do
|
||||
if other_button:IsA("TextButton") then
|
||||
other_button.BackgroundColor3 = Color3.new(1,1,1)
|
||||
if (current.disabled or {})[other_button.Name] then other_button.Visible = false else other_button.Visible = true end
|
||||
end
|
||||
end
|
||||
realMode = newMode
|
||||
mode = newMode
|
||||
gui.Confirm.Text = "Place"
|
||||
if not current.touching and newMode ~= "place" then
|
||||
mode = "place"
|
||||
gui.Confirm.Text = "Confirm"
|
||||
end
|
||||
_G.PlaceModeFrame[realMode].BackgroundColor3 = Color3.new(1, 0.956863, 0.345098)
|
||||
_G.PlaceModeFrame[mode].BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
|
||||
modeStart[mode]()
|
||||
end
|
||||
|
||||
local function confirmPlace(inputObject,forced)
|
||||
if not current then return end
|
||||
if realMode ~= mode then setMode(realMode) return end
|
||||
if (inputObject and inputObject.UserInputType == Enum.UserInputType.MouseButton1) or not inputObject then
|
||||
current:confirm(forced)
|
||||
if not current then return end
|
||||
methodModule.setItemCFrame(current.model,CFrame.new())
|
||||
current.touching = nil
|
||||
current.touchingCFrame = CFrame.new()
|
||||
setMode(realMode)
|
||||
end
|
||||
end
|
||||
|
||||
local modeSnap = {
|
||||
|
||||
}
|
||||
|
||||
local modeClick = {}
|
||||
modeClick.place = function(inputObject)
|
||||
confirmPlace(inputObject,false)
|
||||
end
|
||||
modeClick.clone = function(inputObject)
|
||||
setMode("place")
|
||||
end
|
||||
modeClick.move = function(inputObject)
|
||||
axisDragClick(inputObject)
|
||||
current.moveOffset = current.newMoveOffset or Vector3.zero
|
||||
end
|
||||
modeClick.rotate = axisRotateClick
|
||||
modeClick.resize = function(inputObject)
|
||||
axisDragClick(inputObject)
|
||||
current.startSize = current.size
|
||||
if current.model:IsA("Model") then current.startScale = current.model:GetScale() end
|
||||
end
|
||||
local modeLoop = {}
|
||||
modeLoop.default = function()
|
||||
if current.touching and current.touching:IsA("BasePart") then
|
||||
methodModule.setItemCFrame(current.model,current.touching.CFrame:ToWorldSpace(current.touchingCFrame or CFrame.identity))
|
||||
end
|
||||
statGui.Enabled = not not statGui.Adornee
|
||||
getBounds()
|
||||
end
|
||||
modeLoop.clone = function()
|
||||
current:clone(current.touching)
|
||||
modeLoop.default()
|
||||
end
|
||||
modeLoop.rotate = function()
|
||||
modeLoop.default()
|
||||
focusAxes()
|
||||
if pointer then
|
||||
processPointer(pointer)
|
||||
local p = rayProjectToPlane(current.ray,current.axis)
|
||||
local plane = current.touching.CFrame:ToWorldSpace(current.axisOrigin)
|
||||
local toDrag = plane.Rotation:PointToWorldSpace(current.dragOrigin)
|
||||
local toPointer = p-plane.Position
|
||||
local angle = math.round(toDrag:Angle(toPointer,plane.RightVector)/math.rad(rotSnap))*math.rad(rotSnap)
|
||||
local modelCFrame = current.touching.CFrame:ToWorldSpace(current.modelOrigin) -- cframe of axes/object in the world
|
||||
local newCFrame = plane.Rotation:ToWorldSpace(CFrame.Angles(angle,0,0)*plane.Rotation:ToObjectSpace(modelCFrame.Rotation)) + modelCFrame.Position -- put it back into object space
|
||||
current.rotation = current.initialRotation * newCFrame:ToObjectSpace(modelCFrame):Inverse()
|
||||
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(newCFrame) -- need to add rotation
|
||||
statGui.TextLabel.Text = tonumber(math.round(math.deg(angle)*1000)/1000)
|
||||
statGui.Adornee = current.model
|
||||
else
|
||||
statGui.Adornee = nil
|
||||
end
|
||||
end
|
||||
local function psumag(v)
|
||||
return v.X + v.Y + v.Z
|
||||
end
|
||||
modeLoop.resize = function()
|
||||
modeLoop.default()
|
||||
focusAxes()
|
||||
if pointer then
|
||||
processPointer(pointer)
|
||||
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
|
||||
local start = origin:PointToWorldSpace(current.dragOrigin) -- the start of the scale drag
|
||||
local diff = (rayProjectToRay(current.ray,current.axis)-start) -- difference between start and end
|
||||
local scaleChange = origin.Rotation:PointToObjectSpace(diff)
|
||||
if current.model:IsA("Model") then
|
||||
local before = mask(current.startSize,current.dragOrigin.Unit) -- the size in this axis before scaling
|
||||
local after = psumag(mask(scaleChange,current.dragOrigin.Unit)) -- the size in this axis after scaling
|
||||
local scale = math.round(((((after*math.sign(psumag(before)))/psumag(before))+1)/modelScaleSnap)*current.startScale)*modelScaleSnap
|
||||
scale = math.clamp(scale,0.5,current.model:GetAttribute("MaxSize") or 3)
|
||||
current.model:ScaleTo(scale)
|
||||
current.model:SetAttribute("Size",Vector3.new(scale,0,0))
|
||||
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(((scale/current.startScale)-1)*before*0.5)
|
||||
statGui.TextLabel.Text = tonumber(math.round(scale*1000)/1000)
|
||||
else
|
||||
local change = round(mask(scaleChange,current.scaleTerms or Vector3.one))
|
||||
current.model.Size = clamp(current.startSize + mask(change,current.dragOrigin.Unit),Vector3.one*0.1,current.model:GetAttribute("MaxSize") or Vector3.one*20)
|
||||
current.model:SetAttribute("Size",current.model.Size)
|
||||
current.touchingCFrame = current.modelOrigin + current.modelOrigin.Rotation:PointToWorldSpace(change/2)
|
||||
statGui.TextLabel.Text = tonumber(math.round(mask(current.model.Size,current.dragOrigin.Unit).Magnitude*1000)/1000)
|
||||
end
|
||||
statGui.Adornee = current.model
|
||||
else
|
||||
statGui.Adornee = nil
|
||||
end
|
||||
modeLoop.default() -- the resize system has this weird lag, i cannot be asked fixing it unfortunately
|
||||
end
|
||||
modeLoop.move = function()
|
||||
modeLoop.default()
|
||||
focusAxes()
|
||||
if pointer then
|
||||
processPointer(pointer)
|
||||
local origin = current.touching.CFrame:ToWorldSpace(current.modelOrigin)
|
||||
local start = origin:PointToWorldSpace(current.dragOrigin)
|
||||
local diff = (rayProjectToRay(current.ray,current.axis,start)-start) -- difference between start and end
|
||||
diff = current.touching.CFrame.Rotation:PointToObjectSpace(diff)
|
||||
local offset = psumag(mask(current.moveOffset,diff.Unit))
|
||||
local len = math.abs(psumag(mask(current.size,origin:ToObjectSpace(current.axis.CFrame).RightVector)))
|
||||
diff = diff.Unit * math.round(math.clamp(diff.Magnitude,-len*0.5-offset,len*0.5-offset)/moveSnap) * moveSnap
|
||||
current.touchingCFrame = current.modelOrigin + diff
|
||||
current.newMoveOffset = current.moveOffset + diff
|
||||
statGui.TextLabel.Text = tonumber(math.round(diff.Magnitude*1000)/1000)
|
||||
statGui.Adornee = current.model
|
||||
else
|
||||
statGui.Adornee = nil
|
||||
end
|
||||
end
|
||||
modeLoop.place = function()
|
||||
local result,ray,update = mouseModule.getMouseHit()
|
||||
if result and result.Instance and update then
|
||||
current.touching = result.Instance
|
||||
if current.touching.Locked then adjacentBox.Adornee = nil end
|
||||
if not current.touching then return end
|
||||
if not current.touching.Position then print(current.touching) return end
|
||||
local rotation = (current.touching.CFrame - current.touching.Position)
|
||||
rotation = rotation * current.rotation
|
||||
local s = current.size*0.5 -- So half the size of the part, rotated correctly
|
||||
local max = 0
|
||||
local points = {Vector3.new(s.X,s.Y,s.Z),Vector3.new(s.X,-s.Y,s.Z),Vector3.new(-s.X,s.Y,s.Z),Vector3.new(-s.X,-s.Y,s.Z),Vector3.new(s.X,s.Y,-s.Z),Vector3.new(s.X,-s.Y,-s.Z),Vector3.new(-s.X,s.Y,-s.Z),Vector3.new(-s.X,-s.Y,-s.Z)}
|
||||
for _,point in pairs(points) do
|
||||
max = math.max(max,result.Normal:Dot(rotation:PointToWorldSpace(point)))
|
||||
end
|
||||
local push = max
|
||||
local snap = moveSnap
|
||||
if shiftModifier then snap = moveSnap * 0.5 end
|
||||
local depth = (Ray.new(result.Position,-result.Normal):ClosestPoint(current.touching.Position)-result.Position).Magnitude
|
||||
local flat = current.touching.CFrame:PointToWorldSpace(round(current.touching.CFrame:PointToObjectSpace(result.Position - result.Normal * depth),snap))
|
||||
if controlModifier or current.model:HasTag("Node") then push = 0 end
|
||||
local cframe = rotation + flat + result.Normal * (depth + push) -- Add the rotation, Snapped movement perpendicular to normal and the Offset/Pushback
|
||||
if current.model:HasTag("Node") then
|
||||
local part
|
||||
if current.touching:GetAttribute("Nodes") then
|
||||
part = current.touching
|
||||
elseif current.touching.Parent:GetAttribute("Nodes") then
|
||||
part = current.touching.Parent
|
||||
end
|
||||
if part then
|
||||
local ideal
|
||||
local current_distance = 1
|
||||
for _,node in ipairs(partStateModule.get_points(part)) do
|
||||
local distance = (node - result.Position).Magnitude
|
||||
if distance < current_distance then
|
||||
current_distance = distance
|
||||
if part:IsA("Model") then
|
||||
ideal = part.PrimaryPart
|
||||
else
|
||||
ideal = part
|
||||
end
|
||||
if current.model:IsA("Model") then
|
||||
cframe = rotation + node
|
||||
else
|
||||
cframe = current.model.CFrame.Rotation + node
|
||||
end
|
||||
current.touching = ideal
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if current.touching:HasTag("Node") then
|
||||
if current.model:IsA("Model") then
|
||||
cframe = rotation + current.touching.Position
|
||||
else
|
||||
cframe = current.model.CFrame.Rotation + current.touching.Position
|
||||
end
|
||||
end
|
||||
methodModule.setItemCFrame(current.model,cframe)
|
||||
current.touchingCFrame = current.touching.CFrame:ToObjectSpace(cframe)
|
||||
current.newMoveOffset = Vector3.zero
|
||||
adjacentBox.Adornee = current.touching
|
||||
end
|
||||
getBounds()
|
||||
end
|
||||
|
||||
local function render()
|
||||
if not current then return end
|
||||
controlModifier = inputService:IsKeyDown(Enum.KeyCode.LeftControl) or inputService:IsKeyDown(Enum.KeyCode.LeftMeta)
|
||||
shiftModifier = inputService:IsKeyDown(Enum.KeyCode.LeftShift)
|
||||
modeLoop[mode]()
|
||||
end
|
||||
|
||||
--[[
|
||||
config:
|
||||
- Can Resize
|
||||
- Confirm Callback
|
||||
]]
|
||||
local placetouchbuttoncolor
|
||||
local function toggleTouching()
|
||||
touchModeConnect = not touchModeConnect
|
||||
if touchModeConnect then
|
||||
_G.PlaceTouchButton.BackgroundColor3 = placetouchbuttoncolor
|
||||
else
|
||||
_G.PlaceTouchButton.BackgroundColor3 = Color3.new(1,1,1)
|
||||
end
|
||||
end
|
||||
function begin(model,config)
|
||||
if current then current:stop() end
|
||||
gui.Visible = true
|
||||
current = config or {}
|
||||
current.stop = function(config)
|
||||
if not config then warn("No config passed to PlacementModule; current:stop(). Using current.") config = current end
|
||||
if not config then return end
|
||||
if config.axes then config.axes:Destroy() end
|
||||
if config.stopped then config:stopped() end
|
||||
current = nil
|
||||
selectionBox.Adornee = nil
|
||||
adjacentBox.Adornee = nil
|
||||
for _,box in pairs(touchingBoxes) do
|
||||
box:Destroy()
|
||||
end
|
||||
touchingBoxes = {}
|
||||
gui.Visible = false
|
||||
end
|
||||
model.Parent = game.Workspace.Hidden
|
||||
if model:IsA("Model") then
|
||||
model.PrimaryPart.Anchored = true
|
||||
else
|
||||
model.Anchored = true
|
||||
end
|
||||
ModeButtons.resize.Visible = config.resize
|
||||
if not config.resize and mode == "resize" then mode = "place" end
|
||||
if not config.rotate and mode == "rotate" then mode = "place" end
|
||||
_G.PlaceTouchButton.Visible = config.touch
|
||||
selectionBox.Adornee = model
|
||||
current.model = model
|
||||
if not current.model then error("urmum") end
|
||||
current.connections = current.connections or {}
|
||||
current.rotation = current.model:GetAttribute("Rotation") or CFrame.identity
|
||||
setMode(realMode)
|
||||
styleModel()
|
||||
getBounds()
|
||||
modeStart[mode]()
|
||||
return config
|
||||
end
|
||||
function init(_gui)
|
||||
gui = _gui
|
||||
_G:WaitFor("PlaceModeFrame") _G:WaitFor("PlaceSnapFrame") _G:WaitFor("PlaceCancelButton")
|
||||
statGui.Enabled = false
|
||||
placetouchbuttoncolor = _G.PlaceTouchButton.BackgroundColor3
|
||||
gui.Visible = false
|
||||
run:BindToRenderStep("placementmodule",Enum.RenderPriority.Last.Value,render)
|
||||
local touchOverlapParams = OverlapParams.new()
|
||||
touchOverlapParams.MaxParts = 3
|
||||
touchOverlapParams.FilterType = Enum.RaycastFilterType.Include
|
||||
touchOverlapParams.FilterDescendantsInstances = {game.Workspace.Objects}
|
||||
touchOverlapParams.RespectCanCollide = true
|
||||
touchOverlapParams.Tolerance = 0.05
|
||||
run.Heartbeat:Connect(function() -- Touching parts loop
|
||||
if current and current.model and current.touch then -- in placement
|
||||
local loop_current = current
|
||||
local connections = {}
|
||||
if touchModeConnect then
|
||||
local items
|
||||
if current.model:IsA("Model") then -- Check all potential subparts
|
||||
items = current.model:GetChildren()
|
||||
elseif current.model:IsA("BasePart") then
|
||||
items = {current.model}
|
||||
end
|
||||
local count = 0
|
||||
for _,item in pairs(items) do
|
||||
if item:IsA("BasePart") then
|
||||
for _,part in pairs(workspace:GetPartsInPart(item,touchOverlapParams)) do -- Get touching parts
|
||||
if current.connections[part] then connections[part] = part continue end
|
||||
local object = _G.Methods.getOwnedOrNil(_G.Player,part) -- Check if this is owned
|
||||
if object then
|
||||
connections[object] = true -- Add to the current touching ones
|
||||
current.connections[object] = { -- Add to the final list
|
||||
part = object
|
||||
}
|
||||
if current.model ~= item then current.connections[object].name = item.Name end
|
||||
|
||||
--if part == current.touching then continue end
|
||||
local box = object:FindFirstChild("TouchingBox")
|
||||
if not box then box = script.TouchingBox:Clone() box.Parent = object end
|
||||
touchingBoxes[object] = box
|
||||
end
|
||||
end
|
||||
end
|
||||
if count > 3 then wait() count = 0 else count = count + 1 end -- Dangerous! Things can change after this (wait)
|
||||
if current ~= loop_current then return end -- Current is invalid now so stop!
|
||||
end
|
||||
elseif current.touching then
|
||||
local connection = {
|
||||
part = current.touching
|
||||
}
|
||||
current.connections[current.touching] = connection
|
||||
connections[current.touching] = connection
|
||||
end
|
||||
for index,connection in pairs(current.connections) do -- Remove all invalid connections too
|
||||
if not connections[index] then
|
||||
if touchingBoxes[index] then
|
||||
touchingBoxes[index]:Destroy()
|
||||
touchingBoxes[index] = nil
|
||||
end
|
||||
current.connections[index] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
_G.PlaceTouchButton.Activated:Connect(toggleTouching)
|
||||
inputService.InputBegan:Connect(function(inputObject,irrelevant)
|
||||
if not current then return end
|
||||
if irrelevant then return end
|
||||
local sign = 1
|
||||
if shiftModifier then sign = -1 end
|
||||
if inputObject.UserInputType == Enum.UserInputType.Keyboard then
|
||||
if inputObject.KeyCode == Enum.KeyCode.R then
|
||||
if controlModifier then
|
||||
_G.PlaceSnapInputFrame.RotSnap:CaptureFocus()
|
||||
else
|
||||
current.rotation = current.rotation * CFrame.Angles(math.rad(rotSnapKey*sign),0,0)
|
||||
end
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.T then
|
||||
if controlModifier then
|
||||
_G.PlaceSnapInputFrame.MoveSnap:CaptureFocus()
|
||||
else
|
||||
current.rotation = current.rotation * CFrame.Angles(0,math.rad(rotSnapKey*sign),0)
|
||||
end
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.Y then
|
||||
if controlModifier then
|
||||
_G.PlacePolyInputFrame.PolyThicknessInputBox:CaptureFocus()
|
||||
else
|
||||
current.rotation = current.rotation * CFrame.Angles(0,0,math.rad(rotSnapKey*sign))
|
||||
end
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.G and not controlModifier then
|
||||
current.rotation = CFrame.identity
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.E then
|
||||
setMode("place")
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.Q and current.rotate then
|
||||
setMode("rotate")
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.Z and current.resize then
|
||||
setMode("resize")
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.X then
|
||||
setMode("move")
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.U and current.touch then
|
||||
toggleTouching()
|
||||
elseif inputObject.KeyCode == Enum.KeyCode.Backspace then
|
||||
if current and current.cancel then current:cancel() end
|
||||
end
|
||||
elseif inputObject.UserInputType == Enum.UserInputType.MouseButton1 then
|
||||
if not shiftModifier then modeClick[mode](inputObject) else confirmPlace(inputObject,true) end
|
||||
end
|
||||
end)
|
||||
inputService.TouchStarted:Connect(function(object)
|
||||
modeClick[mode](object)
|
||||
end)
|
||||
inputService.InputEnded:Connect(function(inputObject,irrelevant)
|
||||
if current and inputObject == pointer then
|
||||
pointer = nil
|
||||
current.axis = nil
|
||||
end
|
||||
end)
|
||||
gui.Confirm.Activated:Connect(function()
|
||||
confirmPlace(nil,true)
|
||||
end)
|
||||
_G.PlaceCancelButton.Activated:Connect(function()
|
||||
if current and current.cancel then current:cancel() end
|
||||
end)
|
||||
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
|
||||
if button:IsA("TextButton") then
|
||||
ModeButtons[button.Name] = button
|
||||
if mode == button.Name then
|
||||
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
|
||||
end
|
||||
button.Activated:Connect(function()
|
||||
setMode(button.Name)
|
||||
modeStart[mode]()
|
||||
end)
|
||||
end
|
||||
end
|
||||
local moveSnapInput = _G.PlaceSnapInputFrame.MoveSnap
|
||||
local function evaluateSnapInput(text)
|
||||
return text:match("([%d%.]+).-")
|
||||
end
|
||||
_G.PlaceSnapInputFrame.MoveSnap.FocusLost:Connect(function()
|
||||
local num = evaluateSnapInput(moveSnapInput.Text)
|
||||
if num then moveSnap = tonumber(num) end
|
||||
moveSnapInput.Text = tostring(moveSnap).." stud"
|
||||
end)
|
||||
local rotSnapInput = _G.PlaceSnapInputFrame.RotSnap
|
||||
_G.PlaceSnapInputFrame.RotSnap.FocusLost:Connect(function()
|
||||
local num = evaluateSnapInput(rotSnapInput.Text)
|
||||
if num then rotSnap = tonumber(num) end
|
||||
rotSnapInput.Text = tostring(rotSnap).." deg"
|
||||
end)
|
||||
moveSnapInput.Text = tostring(moveSnap).." stud"
|
||||
rotSnapInput.Text = tostring(rotSnap).." deg"
|
||||
|
||||
local snap_buttons = {}
|
||||
for _,button in pairs(_G.PlaceModeFrame:GetChildren()) do
|
||||
if button:IsA("TextButton") then
|
||||
table.insert(snap_buttons,button)
|
||||
if mode == button.Name then
|
||||
button.BackgroundColor3 = Color3.new(0.6, 1, 0.333333)
|
||||
end
|
||||
button.Activated:Connect(function()
|
||||
setMode(button.Name)
|
||||
modeStart[mode]()
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return getfenv()
|
||||
Reference in New Issue
Block a user