627 lines
22 KiB
Lua
627 lines
22 KiB
Lua
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() |