1946 lines
68 KiB
Lua
1946 lines
68 KiB
Lua
print("[Mercury]: Loaded corescript 73157242")
|
|
local t = { }
|
|
local PlaneIntersection
|
|
PlaneIntersection = function(vectorPos)
|
|
local hit = false
|
|
local currCamera = game.Workspace.CurrentCamera
|
|
local startPos
|
|
do
|
|
local _with_0 = currCamera.CoordinateFrame.p
|
|
startPos = Vector3.new(_with_0.X, _with_0.Y, _with_0.Z)
|
|
end
|
|
local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
|
|
local normal = Vector3.new(0, 1, 0)
|
|
local p3 = Vector3.new(0, 0, 0)
|
|
local startEndDot = normal:Dot(endPos - startPos)
|
|
local cellPos = vectorPos
|
|
if startEndDot ~= 0 then
|
|
t = normal:Dot(p3 - startPos) / startEndDot
|
|
if t >= 0 and t <= 1 then
|
|
local intersection = ((endPos - startPos) * t) + startPos
|
|
cellPos = game.Workspace.Terrain:WorldToCell(intersection)
|
|
hit = true
|
|
end
|
|
end
|
|
return cellPos, hit
|
|
end
|
|
local GetTerrainForMouse
|
|
GetTerrainForMouse = function(mouse)
|
|
local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
|
|
local planeLoc
|
|
if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then
|
|
cell = nil
|
|
local hit
|
|
planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
|
|
if hit then
|
|
cell = planeLoc
|
|
end
|
|
end
|
|
return cell
|
|
end
|
|
local insertBoundingBoxOverlapVector = Vector3.new(0.3, 0.3, 0.3)
|
|
local rotatePartAndChildren
|
|
rotatePartAndChildren = function(part, rotCF, offsetFromOrigin)
|
|
if part:IsA("BasePart") then
|
|
part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin
|
|
end
|
|
local partChildren = part:GetChildren()
|
|
for c = 1, #partChildren do
|
|
rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin)
|
|
end
|
|
end
|
|
local modelRotate
|
|
modelRotate = function(model, yAngle)
|
|
local rotCF = CFrame.Angles(0, yAngle, 0)
|
|
local offsetFromOrigin = model:GetModelCFrame().p
|
|
return rotatePartAndChildren(model, rotCF, offsetFromOrigin)
|
|
end
|
|
local collectParts
|
|
collectParts = function(object, baseParts, scripts, decals)
|
|
if object:IsA("BasePart") then
|
|
baseParts[#baseParts + 1] = object
|
|
elseif object:IsA("Script") then
|
|
scripts[#scripts + 1] = object
|
|
elseif object:IsA("Decal") then
|
|
decals[#decals + 1] = object
|
|
end
|
|
for _, child in pairs(object:GetChildren()) do
|
|
collectParts(child, baseParts, scripts, decals)
|
|
end
|
|
end
|
|
local clusterPartsInRegion
|
|
clusterPartsInRegion = function(startVector, endVector)
|
|
local cluster = game.Workspace:FindFirstChild("Terrain")
|
|
local startCell = cluster:WorldToCell(startVector)
|
|
local endCell = cluster:WorldToCell(endVector)
|
|
local startX = startCell.X
|
|
local startY = startCell.Y
|
|
local startZ = startCell.Z
|
|
local endX = endCell.X
|
|
local endY = endCell.Y
|
|
local endZ = endCell.Z
|
|
if startX < cluster.MaxExtents.Min.X then
|
|
startX = cluster.MaxExtents.Min.X
|
|
end
|
|
if startY < cluster.MaxExtents.Min.Y then
|
|
startY = cluster.MaxExtents.Min.Y
|
|
end
|
|
if startZ < cluster.MaxExtents.Min.Z then
|
|
startZ = cluster.MaxExtents.Min.Z
|
|
end
|
|
if endX > cluster.MaxExtents.Max.X then
|
|
endX = cluster.MaxExtents.Max.X
|
|
end
|
|
if endY > cluster.MaxExtents.Max.Y then
|
|
endY = cluster.MaxExtents.Max.Y
|
|
end
|
|
if endZ > cluster.MaxExtents.Max.Z then
|
|
endZ = cluster.MaxExtents.Max.Z
|
|
end
|
|
for x = startX, endX do
|
|
for y = startY, endY do
|
|
for z = startZ, endZ do
|
|
if cluster:GetCell(x, y, z).Value > 0 then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
local findSeatsInModel
|
|
findSeatsInModel = function(parent, seatTable)
|
|
if not parent then
|
|
return
|
|
end
|
|
if parent.className == "Seat" or parent.className == "VehicleSeat" then
|
|
table.insert(seatTable, parent)
|
|
end
|
|
local myChildren = parent:GetChildren()
|
|
for j = 1, #myChildren do
|
|
findSeatsInModel(myChildren[j], seatTable)
|
|
end
|
|
end
|
|
local setSeatEnabledStatus
|
|
setSeatEnabledStatus = function(model, isEnabled)
|
|
local seatList = { }
|
|
findSeatsInModel(model, seatList)
|
|
if isEnabled then
|
|
for i = 1, #seatList do
|
|
local nextSeat = seatList[i]:FindFirstChild("SeatWeld")
|
|
while nextSeat do
|
|
nextSeat:Remove()
|
|
nextSeat = seatList[i]:FindFirstChild("SeatWeld")
|
|
end
|
|
end
|
|
else
|
|
for i = 1, #seatList do
|
|
local fakeWeld = Instance.new("Weld")
|
|
fakeWeld.Name = "SeatWeld"
|
|
fakeWeld.Parent = seatList[i]
|
|
end
|
|
end
|
|
end
|
|
local autoAlignToFace
|
|
autoAlignToFace = function(parts)
|
|
local aatf = parts:FindFirstChild("AutoAlignToFace")
|
|
if aatf then
|
|
return aatf.Value
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
local getClosestAlignedWorldDirection
|
|
getClosestAlignedWorldDirection = function(aVector3InWorld)
|
|
local xDir = Vector3.new(1, 0, 0)
|
|
local yDir = Vector3.new(0, 1, 0)
|
|
local zDir = Vector3.new(0, 0, 1)
|
|
local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z
|
|
local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z
|
|
local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z
|
|
if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs(zDot) then
|
|
if xDot > 0 then
|
|
return 0
|
|
else
|
|
return 3
|
|
end
|
|
elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs(zDot) then
|
|
if yDot > 0 then
|
|
return 1
|
|
else
|
|
return 4
|
|
end
|
|
else
|
|
if zDot > 0 then
|
|
return 2
|
|
else
|
|
return 5
|
|
end
|
|
end
|
|
end
|
|
local positionPartsAtCFrame3
|
|
positionPartsAtCFrame3 = function(aCFrame, currentParts)
|
|
local insertCFrame
|
|
if not currentParts then
|
|
return currentParts
|
|
end
|
|
if currentParts and (currentParts:IsA("Model") or currentParts:IsA("Tool")) then
|
|
insertCFrame = currentParts:GetModelCFrame()
|
|
currentParts:TranslateBy(aCFrame.p - insertCFrame.p)
|
|
else
|
|
currentParts.CFrame = aCFrame
|
|
end
|
|
return currentParts
|
|
end
|
|
local calcRayHitTime
|
|
calcRayHitTime = function(rayStart, raySlope, intersectionPlane)
|
|
if math.abs(raySlope) < 0.01 then
|
|
return 0
|
|
end
|
|
return (intersectionPlane - rayStart) / raySlope
|
|
end
|
|
local modelTargetSurface
|
|
modelTargetSurface = function(partOrModel, rayStart, rayEnd)
|
|
if not partOrModel then
|
|
return 0
|
|
end
|
|
local modelCFrame, modelSize
|
|
if partOrModel:IsA("Model") then
|
|
modelCFrame = partOrModel:GetModelCFrame()
|
|
modelSize = partOrModel:GetModelSize()
|
|
else
|
|
modelCFrame = partOrModel.CFrame
|
|
modelSize = partOrModel.Size
|
|
end
|
|
local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart)
|
|
local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd)
|
|
local mouseSlope = mouseRayEnd - mouseRayStart
|
|
local xPositive = 1
|
|
local yPositive = 1
|
|
local zPositive = 1
|
|
if mouseSlope.X > 0 then
|
|
xPositive = -1
|
|
end
|
|
if mouseSlope.Y > 0 then
|
|
yPositive = -1
|
|
end
|
|
if mouseSlope.Z > 0 then
|
|
zPositive = -1
|
|
end
|
|
local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X / 2 * xPositive)
|
|
local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y / 2 * yPositive)
|
|
local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z / 2 * zPositive)
|
|
local hitFace = 0
|
|
if xHitTime > yHitTime then
|
|
if xHitTime > zHitTime then
|
|
hitFace = 1 * xPositive
|
|
else
|
|
hitFace = 3 * zPositive
|
|
end
|
|
else
|
|
if yHitTime > zHitTime then
|
|
hitFace = 2 * yPositive
|
|
else
|
|
hitFace = 3 * zPositive
|
|
end
|
|
end
|
|
return hitFace
|
|
end
|
|
local getBoundingBox2
|
|
getBoundingBox2 = function(partOrModel)
|
|
local minVec = Vector3.new(math.huge, math.huge, math.huge)
|
|
local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
|
|
if partOrModel:IsA("Terrain") then
|
|
minVec = Vector3.new(-2, -2, -2)
|
|
maxVec = Vector3.new(2, 2, 2)
|
|
elseif partOrModel:IsA("BasePart") then
|
|
minVec = -0.5 * partOrModel.Size
|
|
maxVec = -minVec
|
|
else
|
|
maxVec = partOrModel:GetModelSize() * 0.5
|
|
minVec = -maxVec
|
|
end
|
|
local justifyValue = partOrModel:FindFirstChild("Justification")
|
|
if (justifyValue ~= nil) then
|
|
local justify = justifyValue.Value
|
|
local two = Vector3.new(2, 2, 2)
|
|
local actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01)
|
|
local containingGridBox = Vector3.new(4 * math.ceil(actualBox.x / 4), 4 * math.ceil(actualBox.y / 4), 4 * math.ceil(actualBox.z / 4))
|
|
local adjustment = containingGridBox - actualBox
|
|
minVec = minVec - (0.5 * adjustment * justify)
|
|
maxVec = maxVec + (0.5 * adjustment * (two - justify))
|
|
end
|
|
return minVec, maxVec
|
|
end
|
|
local getBoundingBoxInWorldCoordinates
|
|
getBoundingBoxInWorldCoordinates = function(partOrModel)
|
|
local minVec = Vector3.new(math.huge, math.huge, math.huge)
|
|
local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
|
|
if partOrModel:IsA("BasePart") and not partOrModel:IsA("Terrain") then
|
|
local vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size)
|
|
local vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size)
|
|
minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
|
|
maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
|
|
elseif not partOrModel:IsA("Terrain") then
|
|
local vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize())
|
|
local vec2 = partOrModel:GetModelCFrame():pointToWorldSpace(0.5 * partOrModel:GetModelSize())
|
|
minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
|
|
maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
|
|
end
|
|
return minVec, maxVec
|
|
end
|
|
local getTargetPartBoundingBox
|
|
getTargetPartBoundingBox = function(targetPart)
|
|
return getBoundingBox2((function()
|
|
if (targetPart.Parent:FindFirstChild("RobloxModel") ~= nil) then
|
|
return targetPart.Parent
|
|
else
|
|
return targetPart
|
|
end
|
|
end)())
|
|
end
|
|
local getMouseTargetCFrame
|
|
getMouseTargetCFrame = function(targetPart)
|
|
if (targetPart.Parent:FindFirstChild("RobloxModel") ~= nil) then
|
|
if targetPart.Parent:IsA("Tool") then
|
|
return targetPart.Parent.Handle.CFrame
|
|
else
|
|
return targetPart.Parent:GetModelCFrame()
|
|
end
|
|
else
|
|
return targetPart.CFrame
|
|
end
|
|
end
|
|
local isBlocker
|
|
isBlocker = function(part)
|
|
if not part then
|
|
return false
|
|
end
|
|
if not part.Parent then
|
|
return false
|
|
end
|
|
if part:FindFirstChild("Humanoid") then
|
|
return false
|
|
end
|
|
if part:FindFirstChild("RobloxStamper" or part:FindFirstChild("RobloxModel")) then
|
|
return true
|
|
end
|
|
if part:IsA("Part") and not part.CanCollide then
|
|
return false
|
|
end
|
|
if part == game.Lighting then
|
|
return false
|
|
end
|
|
return isBlocker(part.Parent)
|
|
end
|
|
local spaceAboveCharacter
|
|
spaceAboveCharacter = function(charTorso, newTorsoY, stampData)
|
|
local partsAboveChar = game.Workspace:FindPartsInRegion3(Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75)), charTorso.Parent, 100)
|
|
for j = 1, #partsAboveChar do
|
|
if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(stampData.CurrentParts) then
|
|
return false
|
|
end
|
|
end
|
|
if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75)) then
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
local findConfigAtMouseTarget
|
|
findConfigAtMouseTarget = function(Mouse, stampData)
|
|
if not Mouse then
|
|
return
|
|
end
|
|
if not stampData then
|
|
return error("findConfigAtMouseTarget: stampData is nil")
|
|
end
|
|
if not stampData["CurrentParts"] then
|
|
return
|
|
end
|
|
local grid = 4.0
|
|
local admissibleConfig = false
|
|
local targetConfig = CFrame.new(0, 0, 0)
|
|
local minBB, maxBB = getBoundingBox2(stampData.CurrentParts)
|
|
local diagBB = maxBB - minBB
|
|
local insertCFrame
|
|
if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
|
|
insertCFrame = stampData.CurrentParts:GetModelCFrame()
|
|
else
|
|
insertCFrame = stampData.CurrentParts.CFrame
|
|
end
|
|
if Mouse then
|
|
if stampData.CurrentParts:IsA("Tool") then
|
|
Mouse.TargetFilter = stampData.CurrentParts.Handle
|
|
else
|
|
Mouse.TargetFilter = stampData.CurrentParts
|
|
end
|
|
end
|
|
local hitPlane = false
|
|
local targetPart
|
|
local success = pcall(function()
|
|
targetPart = Mouse.Target
|
|
end)
|
|
if not success then
|
|
return admissibleConfig, targetConfig
|
|
end
|
|
local mouseHitInWorld = Vector3.new(0, 0, 0)
|
|
if Mouse then
|
|
mouseHitInWorld = Vector3.new(Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z)
|
|
end
|
|
local cellPos
|
|
if nil == targetPart then
|
|
cellPos = GetTerrainForMouse(Mouse)
|
|
if nil == cellPos then
|
|
hitPlane = false
|
|
return admissibleConfig, targetConfig
|
|
else
|
|
targetPart = game.Workspace.Terrain
|
|
hitPlane = true
|
|
cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z)
|
|
mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z)
|
|
end
|
|
end
|
|
local minBBTarget, maxBBTarget
|
|
minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart)
|
|
local diagBBTarget = maxBBTarget - minBBTarget
|
|
local targetCFrame = getMouseTargetCFrame(targetPart)
|
|
if targetPart:IsA("Terrain") then
|
|
if not cluster then
|
|
cluster = game.Workspace:FindFirstChild("Terrain")
|
|
end
|
|
local cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld)
|
|
if hitPlane then
|
|
cellID = cellPos
|
|
end
|
|
targetCFrame = CFrame.new(game.Workspace.Terrain:CellCenterToWorld(cellID.x, cellID.y, cellID.z))
|
|
end
|
|
local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld)
|
|
local targetVectorInWorld = Vector3.new(0, 0, 0)
|
|
if Mouse then
|
|
targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface))
|
|
end
|
|
local targetRefPointInTarget, insertRefPointInInsert
|
|
local clampToSurface
|
|
if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
|
|
clampToSurface = Vector3.new(0, 1, 1)
|
|
elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
|
|
clampToSurface = Vector3.new(0, 1, 1)
|
|
elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
|
|
clampToSurface = Vector3.new(1, 0, 1)
|
|
elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
|
|
clampToSurface = Vector3.new(1, 0, 1)
|
|
elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
|
|
clampToSurface = Vector3.new(1, 1, 0)
|
|
else
|
|
targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
|
|
insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
|
|
clampToSurface = Vector3.new(1, 1, 0)
|
|
end
|
|
targetRefPointInTarget = targetRefPointInTarget * ((0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget))
|
|
insertRefPointInInsert = insertRefPointInInsert * ((0.5 * diagBB) + 0.5 * (maxBB + minBB))
|
|
local delta = mouseHitInTarget - targetRefPointInTarget
|
|
local deltaClamped = Vector3.new(grid * math.modf(delta.x / grid), grid * math.modf(delta.y / grid), grid * math.modf(delta.z / grid))
|
|
deltaClamped = deltaClamped * clampToSurface
|
|
local targetTouchInTarget = deltaClamped + targetRefPointInTarget
|
|
local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget)
|
|
local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert)
|
|
local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld
|
|
local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22
|
|
_, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components()
|
|
targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
|
|
admissibleConfig = true
|
|
return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld)
|
|
end
|
|
local truncateToCircleEighth
|
|
truncateToCircleEighth = function(bigValue, littleValue)
|
|
local big = math.abs(bigValue)
|
|
local little = math.abs(littleValue)
|
|
local hypotenuse = math.sqrt(big * big + little * little)
|
|
local frac = little / hypotenuse
|
|
local bigSign = 1
|
|
local littleSign = 1
|
|
if bigValue < 0 then
|
|
bigSign = -1
|
|
end
|
|
if littleValue < 0 then
|
|
littleSign = -1
|
|
end
|
|
if frac > 0.382683432 then
|
|
return 0.707106781 * hypotenuse * bigSign, 0.707106781 * hypotenuse * littleSign
|
|
else
|
|
return hypotenuse * bigSign, 0
|
|
end
|
|
end
|
|
local saveTheWelds
|
|
saveTheWelds = function(object, manualWeldTable, manualWeldParentTable)
|
|
if object:IsA("ManualWeld") or object:IsA("Rotate") then
|
|
table.insert(manualWeldTable, object)
|
|
return table.insert(manualWeldParentTable, object.Parent)
|
|
else
|
|
local children = object:GetChildren()
|
|
for i = 1, #children do
|
|
saveTheWelds(children[i], manualWeldTable, manualWeldParentTable)
|
|
end
|
|
end
|
|
end
|
|
local restoreTheWelds
|
|
restoreTheWelds = function(manualWeldTable, manualWeldParentTable)
|
|
for i = 1, #manualWeldTable do
|
|
manualWeldTable[i].Parent = manualWeldParentTable[i]
|
|
end
|
|
end
|
|
t.CanEditRegion = function(partOrModel, EditRegion)
|
|
if not EditRegion then
|
|
return true, false
|
|
end
|
|
local minBB, maxBB
|
|
minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel)
|
|
if minBB.X < EditRegion.CFrame.p.X - EditRegion.Size.X / 2 or minBB.Y < EditRegion.CFrame.p.Y - EditRegion.Size.Y / 2 or minBB.Z < EditRegion.CFrame.p.Z - EditRegion.Size.Z / 2 then
|
|
return false, false
|
|
end
|
|
if maxBB.X > EditRegion.CFrame.p.X + EditRegion.Size.X / 2 or maxBB.Y > EditRegion.CFrame.p.Y + EditRegion.Size.Y / 2 or maxBB.Z > EditRegion.CFrame.p.Z + EditRegion.Size.Z / 2 then
|
|
return false, false
|
|
end
|
|
return true, false
|
|
end
|
|
t.GetStampModel = function(assetId, terrainShape, useAssetVersionId)
|
|
if assetId == 0 then
|
|
return nil, "No Asset"
|
|
end
|
|
if assetId < 0 then
|
|
return nil, "Negative Asset"
|
|
end
|
|
local UnlockInstances
|
|
UnlockInstances = function(object)
|
|
if object:IsA("BasePart") then
|
|
object.Locked = false
|
|
end
|
|
for _, child in pairs(object:GetChildren()) do
|
|
UnlockInstances(child)
|
|
end
|
|
end
|
|
local getClosestColorToTerrainMaterial
|
|
getClosestColorToTerrainMaterial = function(terrainValue)
|
|
return BrickColor.new((function()
|
|
if 1 == terrainValue then
|
|
return "Bright green"
|
|
elseif 2 == terrainValue then
|
|
return "Bright yellow"
|
|
elseif 3 == terrainValue then
|
|
return "Bright red"
|
|
elseif 4 == terrainValue then
|
|
return "Sand red"
|
|
elseif 5 == terrainValue then
|
|
return "Black"
|
|
elseif 6 == terrainValue then
|
|
return "Dark stone grey"
|
|
elseif 7 == terrainValue then
|
|
return "Sand blue"
|
|
elseif 8 == terrainValue then
|
|
return "Deep orange"
|
|
elseif 9 == terrainValue then
|
|
return "Dark orange"
|
|
elseif 10 == terrainValue then
|
|
return "Reddish brown"
|
|
elseif 11 == terrainValue then
|
|
return "Light orange"
|
|
elseif 12 == terrainValue then
|
|
return "Light stone grey"
|
|
elseif 13 == terrainValue then
|
|
return "Sand green"
|
|
elseif 14 == terrainValue then
|
|
return "Medium stone grey"
|
|
elseif 15 == terrainValue then
|
|
return "Really red"
|
|
elseif 16 == terrainValue then
|
|
return "Really blue"
|
|
elseif 17 == terrainValue then
|
|
return "Bright blue"
|
|
else
|
|
return "Bright green"
|
|
end
|
|
end)())
|
|
end
|
|
local setupFakeTerrainPart
|
|
setupFakeTerrainPart = function(cellMat, cellType, cellOrient)
|
|
local newTerrainPiece
|
|
if cellType == 1 or cellType == 4 then
|
|
newTerrainPiece = Instance.new("WedgePart")
|
|
newTerrainPiece.formFactor = "Custom"
|
|
elseif cellType == 2 then
|
|
newTerrainPiece = Instance.new("CornerWedgePart")
|
|
else
|
|
newTerrainPiece = Instance.new("Part")
|
|
newTerrainPiece.formFactor = "Custom"
|
|
end
|
|
newTerrainPiece.Name = "MegaClusterCube"
|
|
newTerrainPiece.Size = Vector3.new(4, 4, 4)
|
|
newTerrainPiece.BottomSurface = "Smooth"
|
|
newTerrainPiece.TopSurface = "Smooth"
|
|
newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat)
|
|
local sideways = 0
|
|
local flipped = math.pi
|
|
if cellType == 4 then
|
|
sideways = -math.pi / 2
|
|
end
|
|
if cellType == 2 or cellType == 3 then
|
|
flipped = 0
|
|
end
|
|
newTerrainPiece.CFrame = CFrame.Angles(0, math.pi / 2 * cellOrient + flipped, sideways)
|
|
if cellType == 3 then
|
|
local inverseCornerWedgeMesh = Instance.new("SpecialMesh")
|
|
inverseCornerWedgeMesh.MeshType = "FileMesh"
|
|
inverseCornerWedgeMesh.MeshId = "http://www.roblox.com/asset?id=66832495"
|
|
inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2)
|
|
inverseCornerWedgeMesh.Parent = newTerrainPiece
|
|
end
|
|
local materialTag = Instance.new("Vector3Value")
|
|
materialTag.Value = Vector3.new(cellMat, cellType, cellOrient)
|
|
materialTag.Name = "ClusterMaterial"
|
|
materialTag.Parent = newTerrainPiece
|
|
return newTerrainPiece
|
|
end
|
|
local root
|
|
local loader
|
|
local loading = true
|
|
if useAssetVersionId then
|
|
loader = coroutine.create(function()
|
|
root = game:GetService("InsertService"):LoadAssetVersion(assetId)
|
|
loading = false
|
|
end)
|
|
coroutine.resume(loader)
|
|
else
|
|
loader = coroutine.create(function()
|
|
root = game:GetService("InsertService"):LoadAsset(assetId)
|
|
loading = false
|
|
end)
|
|
coroutine.resume(loader)
|
|
end
|
|
local lastGameTime = 0
|
|
local totalTime = 0
|
|
local maxWait = 8
|
|
while loading and totalTime < maxWait do
|
|
lastGameTime = tick()
|
|
wait(1)
|
|
totalTime = totalTime + (tick() - lastGameTime)
|
|
end
|
|
loading = false
|
|
if totalTime >= maxWait then
|
|
return nil, "Load Time Fail"
|
|
end
|
|
if root == nil then
|
|
return nil, "Load Asset Fail"
|
|
end
|
|
if not root:IsA("Model") then
|
|
return nil, "Load Type Fail"
|
|
end
|
|
local instances = root:GetChildren()
|
|
if #instances == 0 then
|
|
return nil, "Empty Model Fail"
|
|
end
|
|
UnlockInstances(root)
|
|
root = root:GetChildren()[1]
|
|
for _, instance in pairs(instances) do
|
|
if instance:IsA("Team") then
|
|
instance.Parent = game:GetService("Teams")
|
|
elseif instance:IsA("Sky") then
|
|
local lightingService = game:GetService("Lighting")
|
|
for _, child in pairs(lightingService:GetChildren()) do
|
|
if child:IsA("Sky") then
|
|
child:Remove()
|
|
end
|
|
end
|
|
instance.Parent = lightingService
|
|
return
|
|
end
|
|
end
|
|
if not (root:FindFirstChild("RobloxModel") ~= nil) then
|
|
local stringTag = Instance.new("BoolValue")
|
|
stringTag.Name = "RobloxModel"
|
|
stringTag.Parent = root
|
|
if not (root:FindFirstChild("RobloxStamper") ~= nil) then
|
|
local stringTag2 = Instance.new("BoolValue")
|
|
stringTag2.Name = "RobloxStamper"
|
|
stringTag2.Parent = root
|
|
end
|
|
end
|
|
if terrainShape then
|
|
if root.Name == "MegaClusterCube" then
|
|
if terrainShape == 6 then
|
|
local autowedgeTag = Instance.new("BoolValue")
|
|
autowedgeTag.Name = "AutoWedge"
|
|
autowedgeTag.Parent = root
|
|
else
|
|
local clusterTag = root:FindFirstChild("ClusterMaterial")
|
|
if clusterTag then
|
|
if clusterTag:IsA("Vector3Value") then
|
|
root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z)
|
|
else
|
|
root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0)
|
|
end
|
|
else
|
|
root = setupFakeTerrainPart(1, terrainShape, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return root
|
|
end
|
|
t.SetupStamperDragger = function(modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc)
|
|
if not modelToStamp then
|
|
error("SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model")
|
|
return nil
|
|
end
|
|
if not modelToStamp:IsA("Model") and not modelToStamp:IsA("BasePart") then
|
|
error("SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!")
|
|
return nil
|
|
end
|
|
if not Mouse then
|
|
error("SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object")
|
|
return nil
|
|
end
|
|
if not Mouse:IsA("Mouse") then
|
|
error("SetupStamperDragger: Mouse (second arg) is not of type Mouse!")
|
|
return nil
|
|
end
|
|
local stampInModel
|
|
local allowedStampRegion
|
|
local stampFailedFunc
|
|
if StampInModel then
|
|
if not StampInModel:IsA("Model") then
|
|
error("SetupStamperDragger: StampInModel (optional third arg) is not of type 'Model'")
|
|
return nil
|
|
end
|
|
if not AllowedStampRegion then
|
|
error("SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined")
|
|
return nil
|
|
end
|
|
stampFailedFunc = StampFailedFunc
|
|
stampInModel = StampInModel
|
|
allowedStampRegion = AllowedStampRegion
|
|
end
|
|
local gInitial90DegreeRotations = 0
|
|
local stampData
|
|
local mouseTarget
|
|
local errorBox = Instance.new("SelectionBox")
|
|
errorBox.Color = BrickColor.new("Bright red")
|
|
errorBox.Transparency = 0
|
|
errorBox.Archivable = false
|
|
local adornPart = Instance.new("Part")
|
|
adornPart.Parent = nil
|
|
adornPart.formFactor = "Custom"
|
|
adornPart.Size = Vector3.new(4, 4, 4)
|
|
adornPart.CFrame = CFrame.new()
|
|
adornPart.Archivable = false
|
|
local adorn = Instance.new("SelectionBox")
|
|
adorn.Color = BrickColor.new("Toothpaste")
|
|
adorn.Adornee = adornPart
|
|
adorn.Visible = true
|
|
adorn.Transparency = 0
|
|
adorn.Name = "HighScalabilityStamperLine"
|
|
adorn.Archivable = false
|
|
local HighScalabilityLine = { }
|
|
HighScalabilityLine.Start = nil
|
|
HighScalabilityLine.End = nil
|
|
HighScalabilityLine.Adorn = adorn
|
|
HighScalabilityLine.AdornPart = adornPart
|
|
HighScalabilityLine.InternalLine = nil
|
|
HighScalabilityLine.NewHint = true
|
|
HighScalabilityLine.MorePoints = {
|
|
nil,
|
|
nil
|
|
}
|
|
HighScalabilityLine.MoreLines = {
|
|
nil,
|
|
nil
|
|
}
|
|
HighScalabilityLine.Dimensions = 1
|
|
local control = { }
|
|
local movingLock = false
|
|
local stampUpLock = false
|
|
local unstampableSurface = false
|
|
local mouseCons = { }
|
|
local keyCon
|
|
local stamped = Instance.new("BoolValue")
|
|
stamped.Archivable = false
|
|
stamped.Value = false
|
|
local lastTarget = { }
|
|
lastTarget.TerrainOrientation = 0
|
|
lastTarget.CFrame = 0
|
|
local cellInfo = { }
|
|
cellInfo.Material = 1
|
|
cellInfo.clusterType = 0
|
|
cellInfo.clusterOrientation = 0
|
|
local isMegaClusterPart
|
|
isMegaClusterPart = function()
|
|
if not stampData then
|
|
return false
|
|
end
|
|
if not stampData.CurrentParts then
|
|
return false
|
|
end
|
|
return stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) or (stampData.CurrentParts.Name == "MegaClusterCube")
|
|
end
|
|
local DoHighScalabilityRegionSelect
|
|
DoHighScalabilityRegionSelect = function()
|
|
local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube")
|
|
if not megaCube then
|
|
if not stampData.CurrentParts.Name == "MegaClusterCube" then
|
|
return
|
|
else
|
|
megaCube = stampData.CurrentParts
|
|
end
|
|
end
|
|
HighScalabilityLine.End = megaCube.CFrame.p
|
|
local line
|
|
local line2 = Vector3.new(0, 0, 0)
|
|
local line3 = Vector3.new(0, 0, 0)
|
|
if HighScalabilityLine.Dimensions == 1 then
|
|
line = (HighScalabilityLine.End - HighScalabilityLine.Start)
|
|
if math.abs(line.X) < math.abs(line.Y) then
|
|
if math.abs(line.X) < math.abs(line.Z) then
|
|
local newY, newZ
|
|
if math.abs(line.Y) > math.abs(line.Z) then
|
|
newY, newZ = truncateToCircleEighth(line.Y, line.Z)
|
|
else
|
|
newZ, newY = truncateToCircleEighth(line.Z, line.Y)
|
|
end
|
|
line = Vector3.new(0, newY, newZ)
|
|
else
|
|
local newY, newX
|
|
newY, newX = truncateToCircleEighth(line.Y, line.X)
|
|
line = Vector3.new(newX, newY, 0)
|
|
end
|
|
else
|
|
if math.abs(line.Y) < math.abs(line.Z) then
|
|
local newX, newZ
|
|
if math.abs(line.X) > math.abs(line.Z) then
|
|
newX, newZ = truncateToCircleEighth(line.X, line.Z)
|
|
else
|
|
newZ, newX = truncateToCircleEighth(line.Z, line.X)
|
|
end
|
|
line = Vector3.new(newX, 0, newZ)
|
|
else
|
|
local newX, newY
|
|
newX, newY = truncateToCircleEighth(line.X, line.Y)
|
|
line = Vector3.new(newX, newY, 0)
|
|
end
|
|
end
|
|
HighScalabilityLine.InternalLine = line
|
|
elseif HighScalabilityLine.Dimensions == 2 then
|
|
line = HighScalabilityLine.MoreLines[1]
|
|
line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1]
|
|
line2 = line2 - (line.unit * line.unit:Dot(line2))
|
|
local tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
|
|
local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0))
|
|
local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0))
|
|
local xComp = xAxis:Dot(line2)
|
|
local yComp = yAxis:Dot(line2)
|
|
if math.abs(yComp) > math.abs(xComp) then
|
|
line2 = line2 - (xAxis * xComp)
|
|
else
|
|
line2 = line2 - (yAxis * yComp)
|
|
end
|
|
HighScalabilityLine.InternalLine = line2
|
|
elseif HighScalabilityLine.Dimensions == 3 then
|
|
line = HighScalabilityLine.MoreLines[1]
|
|
line2 = HighScalabilityLine.MoreLines[2]
|
|
line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2]
|
|
line3 = line3 - (line.unit * line.unit:Dot(line3))
|
|
line3 = line3 - (line2.unit * line2.unit:Dot(line3))
|
|
HighScalabilityLine.InternalLine = line3
|
|
end
|
|
local tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
|
|
if HighScalabilityLine.Dimensions == 1 then
|
|
HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4)
|
|
HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size / 2)
|
|
else
|
|
local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3)
|
|
HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z))
|
|
HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize / 2)
|
|
end
|
|
local gui
|
|
if game.Players["LocalPlayer"] then
|
|
gui = game.Players.LocalPlayer:FindFirstChild("PlayerGui")
|
|
if gui and gui:IsA("PlayerGui") then
|
|
if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or HighScalabilityLine.Dimensions > 1 then
|
|
HighScalabilityLine.Adorn.Parent = gui
|
|
end
|
|
end
|
|
end
|
|
if not (gui ~= nil) then
|
|
gui = game:GetService("CoreGui")
|
|
if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or HighScalabilityLine.Dimensions > 1 then
|
|
HighScalabilityLine.Adorn.Parent = gui
|
|
end
|
|
end
|
|
end
|
|
local DoStamperMouseMove
|
|
DoStamperMouseMove = function(Mouse)
|
|
if not Mouse then
|
|
error("Error: RbxStamper.DoStamperMouseMove: Mouse is nil")
|
|
return
|
|
end
|
|
if not Mouse:IsA("Mouse") then
|
|
error("Error: RbxStamper.DoStamperMouseMove: Mouse is of type", Mouse.className, "should be of type Mouse")
|
|
return
|
|
end
|
|
if not Mouse.Target then
|
|
local cellPos = GetTerrainForMouse(Mouse)
|
|
if nil == cellPos then
|
|
return
|
|
end
|
|
end
|
|
if not stampData then
|
|
return
|
|
end
|
|
local configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData)
|
|
if not configFound then
|
|
error("RbxStamper.DoStamperMouseMove No configFound, returning")
|
|
return
|
|
end
|
|
local numRotations = 0
|
|
if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then
|
|
if targetSurface == 3 then
|
|
numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
|
|
elseif targetSurface == 0 then
|
|
numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
|
|
elseif targetSurface == 5 then
|
|
numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
|
|
elseif targetSurface == 2 then
|
|
numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
|
|
end
|
|
end
|
|
local ry = math.pi / 2
|
|
gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations
|
|
if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
|
|
modelRotate(stampData.CurrentParts, ry * numRotations)
|
|
else
|
|
stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry * numRotations, 0)({
|
|
stampData.CurrentParts.CFrame
|
|
})
|
|
end
|
|
local minBB, maxBB
|
|
minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
|
|
local currModelCFrame
|
|
if stampData.CurrentParts:IsA("Model") then
|
|
currModelCFrame = stampData.CurrentParts:GetModelCFrame()
|
|
else
|
|
currModelCFrame = stampData.CurrentParts.CFrame
|
|
end
|
|
minBB = minBB + (targetCFrame.p - currModelCFrame.p)
|
|
maxBB = maxBB + (targetCFrame.p - currModelCFrame.p)
|
|
if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
|
|
if lastTarget.CFrame then
|
|
if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
|
|
local theClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if theClusterMaterial:IsA("Vector3Value") then
|
|
local stampClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if stampClusterMaterial then
|
|
stampClusterMaterial = clusterMat
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return
|
|
end
|
|
if isMegaClusterPart() then
|
|
local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p)
|
|
local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z)
|
|
local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22
|
|
_, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame:components()
|
|
targetCFrame = CFrame.new(newCFramePosition.X, newCFramePosition.Y, newCFramePosition.Z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
|
|
end
|
|
positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
|
|
lastTarget.CFrame = targetCFrame
|
|
if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
|
|
local clusterMat = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if clusterMat:IsA("Vector3Value") then
|
|
lastTarget.TerrainOrientation = clusterMat.Value.Z
|
|
end
|
|
end
|
|
if Mouse and Mouse.Target and Mouse.Target.Parent then
|
|
local modelInfo = Mouse.Target:FindFirstChild("RobloxModel")
|
|
if not modelInfo then
|
|
modelInfo = Mouse.Target.Parent:FindFirstChild("RobloxModel")
|
|
end
|
|
local myModelInfo = stampData.CurrentParts:FindFirstChild("UnstampableFaces")
|
|
if true then
|
|
local breakingFaces = ""
|
|
local myBreakingFaces = ""
|
|
if modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces") then
|
|
breakingFaces = modelInfo.Parent.UnstampableFaces.Value
|
|
end
|
|
if myModelInfo then
|
|
myBreakingFaces = myModelInfo.Value
|
|
end
|
|
local hitFace = 0
|
|
if modelInfo then
|
|
hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, Mouse.Hit.p)
|
|
end
|
|
for bf in string.gmatch(breakingFaces, "[^,]+") do
|
|
if hitFace == tonumber(bf) then
|
|
unstampableSurface = true
|
|
game.JointsService:ClearJoinAfterMoveJoints()
|
|
return
|
|
end
|
|
end
|
|
hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p)
|
|
for bf in string.gmatch(myBreakingFaces, "[^,]+") do
|
|
if hitFace == tonumber(bf) then
|
|
unstampableSurface = true
|
|
game.JointsService:ClearJoinAfterMoveJoints()
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
unstampableSurface = false
|
|
game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
|
|
if (not pcall(function()
|
|
if Mouse and Mouse.Target and not (Mouse.Target.Parent:FindFirstChild("RobloxModel") ~= nil) then
|
|
return
|
|
else
|
|
return
|
|
end
|
|
end)) then
|
|
error("Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check")
|
|
game.JointsService:ClearJoinAfterMoveJoints()
|
|
Mouse = nil
|
|
return
|
|
end
|
|
if Mouse and Mouse.Target and not (Mouse.Target.Parent:FindFirstChild("RobloxModel") ~= nil) then
|
|
game.JointsService:SetJoinAfterMoveTarget(Mouse.Target)
|
|
else
|
|
game.JointsService:SetJoinAfterMoveTarget(nil)
|
|
end
|
|
game.JointsService:ShowPermissibleJoints()
|
|
if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then
|
|
return DoHighScalabilityRegionSelect()
|
|
end
|
|
end
|
|
local setupKeyListener
|
|
setupKeyListener = function(key, Mouse)
|
|
if control and control["Paused"] then
|
|
return
|
|
end
|
|
key = string.lower(key)
|
|
if key == "r" and not autoAlignToFace(stampData.CurrentParts) then
|
|
gInitial90DegreeRotations = gInitial90DegreeRotations + 1
|
|
local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if clusterValues and clusterValues:IsA("Vector3Value") then
|
|
clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z + 1) % 4)
|
|
end
|
|
local ry = math.pi / 2
|
|
if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
|
|
modelRotate(stampData.CurrentParts, ry)
|
|
else
|
|
stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
|
|
end
|
|
local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
|
|
if configFound then
|
|
positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
|
|
return DoStamperMouseMove(Mouse)
|
|
end
|
|
elseif key == "c" then
|
|
if HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 and HighScalabilityLine.Dimensions < 3 then
|
|
HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End
|
|
HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine
|
|
HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1
|
|
HighScalabilityLine.NewHint = true
|
|
end
|
|
end
|
|
end
|
|
keyCon = Mouse.KeyDown:connect(function(key)
|
|
return setupKeyListener(key, Mouse)
|
|
end)
|
|
local resetHighScalabilityLine
|
|
resetHighScalabilityLine = function()
|
|
if HighScalabilityLine then
|
|
HighScalabilityLine.Start = nil
|
|
HighScalabilityLine.End = nil
|
|
HighScalabilityLine.InternalLine = nil
|
|
HighScalabilityLine.NewHint = true
|
|
end
|
|
end
|
|
local flashRedBox
|
|
flashRedBox = function()
|
|
local gui = game.CoreGui
|
|
if game:FindFirstChild("Players") then
|
|
if game.Players["LocalPlayer"] then
|
|
if game.Players.LocalPlayer:FindFirstChild("PlayerGui") then
|
|
gui = game.Players.LocalPlayer.PlayerGui
|
|
end
|
|
end
|
|
end
|
|
if not stampData["ErrorBox"] then
|
|
return
|
|
end
|
|
stampData.ErrorBox.Parent = gui
|
|
if stampData.CurrentParts:IsA("Tool") then
|
|
stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle
|
|
else
|
|
stampData.ErrorBox.Adornee = stampData.CurrentParts
|
|
end
|
|
return delay(0, function()
|
|
for _ = 1, 3 do
|
|
if stampData["ErrorBox"] then
|
|
stampData.ErrorBox.Visible = true
|
|
end
|
|
wait(0.13)
|
|
if stampData["ErrorBox"] then
|
|
stampData.ErrorBox.Visible = false
|
|
end
|
|
wait(0.13)
|
|
end
|
|
if stampData["ErrorBox"] then
|
|
stampData.ErrorBox.Adornee = nil
|
|
stampData.ErrorBox.Parent = Tool
|
|
end
|
|
end)
|
|
end
|
|
local DoStamperMouseDown
|
|
DoStamperMouseDown = function(Mouse)
|
|
if not Mouse then
|
|
error("Error: RbxStamper.DoStamperMouseDown: Mouse is nil")
|
|
return
|
|
end
|
|
if not Mouse:IsA("Mouse") then
|
|
error("Error: RbxStamper.DoStamperMouseDown: Mouse is of type", Mouse.className, "should be of type Mouse")
|
|
return
|
|
end
|
|
if not stampData then
|
|
return
|
|
end
|
|
if isMegaClusterPart() then
|
|
if Mouse and HighScalabilityLine then
|
|
local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube", true)
|
|
local terrain = game.Workspace.Terrain
|
|
if megaCube then
|
|
HighScalabilityLine.Dimensions = 1
|
|
local tempCell = terrain:WorldToCell(megaCube.CFrame.p)
|
|
HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
|
|
return
|
|
else
|
|
HighScalabilityLine.Dimensions = 1
|
|
local tempCell = terrain:WorldToCell(stampData.CurrentParts.CFrame.p)
|
|
HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local loadSurfaceTypes
|
|
loadSurfaceTypes = function(part, surfaces)
|
|
part.TopSurface = surfaces[1]
|
|
part.BottomSurface = surfaces[2]
|
|
part.LeftSurface = surfaces[3]
|
|
part.RightSurface = surfaces[4]
|
|
part.FrontSurface = surfaces[5]
|
|
part.BackSurface = surfaces[6]
|
|
return part
|
|
end
|
|
local saveSurfaceTypes
|
|
saveSurfaceTypes = function(part, myTable)
|
|
local tempTable = { }
|
|
tempTable[1] = part.TopSurface
|
|
tempTable[2] = part.BottomSurface
|
|
tempTable[3] = part.LeftSurface
|
|
tempTable[4] = part.RightSurface
|
|
tempTable[5] = part.FrontSurface
|
|
tempTable[6] = part.BackSurface
|
|
myTable[part] = tempTable
|
|
end
|
|
local prepareModel
|
|
prepareModel = function(model)
|
|
if not model then
|
|
return nil
|
|
end
|
|
local gDesiredTrans = 0.7
|
|
local gStaticTrans = 1
|
|
local clone = model:Clone()
|
|
local scripts = { }
|
|
local parts = { }
|
|
local decals = { }
|
|
stampData = { }
|
|
stampData.DisabledScripts = { }
|
|
stampData.TransparencyTable = { }
|
|
stampData.MaterialTable = { }
|
|
stampData.CanCollideTable = { }
|
|
stampData.AnchoredTable = { }
|
|
stampData.ArchivableTable = { }
|
|
stampData.DecalTransparencyTable = { }
|
|
stampData.SurfaceTypeTable = { }
|
|
collectParts(clone, parts, scripts, decals)
|
|
if #parts <= 0 then
|
|
return nil, "no parts found in modelToStamp"
|
|
end
|
|
for _, script in pairs(scripts) do
|
|
if not script.Disabled then
|
|
script.Disabled = true
|
|
stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script
|
|
end
|
|
end
|
|
for _, part in pairs(parts) do
|
|
stampData.TransparencyTable[part] = part.Transparency
|
|
part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency
|
|
stampData.MaterialTable[part] = part.Material
|
|
part.Material = Enum.Material.Plastic
|
|
stampData.CanCollideTable[part] = part.CanCollide
|
|
part.CanCollide = false
|
|
stampData.AnchoredTable[part] = part.Anchored
|
|
part.Anchored = true
|
|
stampData.ArchivableTable[part] = part.Archivable
|
|
part.Archivable = false
|
|
saveSurfaceTypes(part, stampData.SurfaceTypeTable)
|
|
local fadeInDelayTime = 0.5
|
|
local transFadeInTime = 0.5
|
|
delay(0, function()
|
|
wait(fadeInDelayTime)
|
|
local begTime = tick()
|
|
local currTime = begTime
|
|
while (currTime - begTime) < transFadeInTime and part and part:IsA("BasePart") and part.Transparency > gDesiredTrans do
|
|
local newTrans = 1 - (((currTime - begTime) / transFadeInTime) * (gStaticTrans - gDesiredTrans))
|
|
if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
|
|
part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part]
|
|
end
|
|
wait(0.03)
|
|
currTime = tick()
|
|
end
|
|
if part and part:IsA("BasePart") then
|
|
if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
|
|
part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part]
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
for _, decal in pairs(decals) do
|
|
stampData.DecalTransparencyTable[decal] = decal.Transparency
|
|
decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency
|
|
end
|
|
setSeatEnabledStatus(clone, true)
|
|
setSeatEnabledStatus(clone, false)
|
|
stampData.CurrentParts = clone
|
|
if autoAlignToFace(clone) then
|
|
stampData.CurrentParts:ResetOrientationToIdentity()
|
|
gInitial90DegreeRotations = 0
|
|
else
|
|
local ry = gInitial90DegreeRotations * math.pi / 2
|
|
if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
|
|
modelRotate(stampData.CurrentParts, ry)
|
|
else
|
|
stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
|
|
end
|
|
end
|
|
local clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if clusterMaterial and clusterMaterial:IsA("Vector3Value") then
|
|
clusterMaterial.Value = Vector3.new(clusterMaterial.Value.X, clusterMaterial.Value.Y, (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4)
|
|
end
|
|
local configFound, targetCFrame
|
|
configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
|
|
if configFound then
|
|
stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
|
|
end
|
|
game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
|
|
return clone, parts
|
|
end
|
|
local checkTerrainBlockCollisions
|
|
checkTerrainBlockCollisions = function(cellPos, checkHighScalabilityStamp)
|
|
local cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld
|
|
local cellCenter = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
|
|
local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100)
|
|
local skipThisCell = false
|
|
for b = 1, #cellBlockingParts do
|
|
if isBlocker(cellBlockingParts[b]) then
|
|
skipThisCell = true
|
|
break
|
|
end
|
|
end
|
|
if not skipThisCell then
|
|
local alreadyPushedUp = { }
|
|
for b = 1, #cellBlockingParts do
|
|
if cellBlockingParts[b].Parent and not alreadyPushedUp[cellBlockingParts[b].Parent] and cellBlockingParts[b].Parent:FindFirstChild("Humanoid" and cellBlockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid")) then
|
|
local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild("Torso")
|
|
alreadyPushedUp[cellBlockingParts[b].Parent] = true
|
|
if blockingPersonTorso then
|
|
local newY = cellCenter.Y + 5
|
|
if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
|
|
blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
|
|
else
|
|
skipThisCell = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if not skipThisCell then
|
|
local canSetCell = true
|
|
if checkHighScalabilityStamp then
|
|
if allowedStampRegion then
|
|
cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
|
|
if (cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X / 2) or (cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X / 2) or (cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y / 2) or (cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y / 2) or (cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z / 2) or (cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z / 2) then
|
|
canSetCell = false
|
|
end
|
|
end
|
|
end
|
|
return canSetCell
|
|
end
|
|
return false
|
|
end
|
|
local ResolveMegaClusterStamp
|
|
ResolveMegaClusterStamp = function(checkHighScalabilityStamp)
|
|
local cellSet = false
|
|
local cluster = game.Workspace.Terrain
|
|
local line = HighScalabilityLine.InternalLine
|
|
local cMax = game.Workspace.Terrain.MaxExtents.Max
|
|
local cMin = game.Workspace.Terrain.MaxExtents.Min
|
|
local clusterMaterial = 1
|
|
local clusterType = 0
|
|
local clusterOrientation = 0
|
|
local autoWedgeClusterParts = false
|
|
if stampData.CurrentParts:FindFirstChild("AutoWedge") then
|
|
autoWedgeClusterParts = true
|
|
end
|
|
if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
|
|
clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
if clusterMaterial:IsA("Vector3Value") then
|
|
clusterType = clusterMaterial.Value.Y
|
|
clusterOrientation = clusterMaterial.Value.Z
|
|
clusterMaterial = clusterMaterial.Value.X
|
|
elseif clusterMaterial:IsA("IntValue") then
|
|
clusterMaterial = clusterMaterial.Value
|
|
end
|
|
end
|
|
if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) then
|
|
local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start)
|
|
local xInc = {
|
|
0,
|
|
0,
|
|
0
|
|
}
|
|
local yInc = {
|
|
0,
|
|
0,
|
|
0
|
|
}
|
|
local zInc = {
|
|
0,
|
|
0,
|
|
0
|
|
}
|
|
local incrementVect = {
|
|
nil,
|
|
nil,
|
|
nil
|
|
}
|
|
local stepVect = {
|
|
Vector3.new(0, 0, 0),
|
|
Vector3.new(0, 0, 0),
|
|
Vector3.new(0, 0, 0)
|
|
}
|
|
local worldAxes = {
|
|
Vector3.new(1, 0, 0),
|
|
Vector3.new(0, 1, 0),
|
|
Vector3.new(0, 0, 1)
|
|
}
|
|
local lines = { }
|
|
if HighScalabilityLine.Dimensions > 1 then
|
|
table.insert(lines, HighScalabilityLine.MoreLines[1])
|
|
end
|
|
if line and line.magnitude > 0 then
|
|
table.insert(lines, line)
|
|
end
|
|
if HighScalabilityLine.Dimensions > 2 then
|
|
table.insert(lines, HighScalabilityLine.MoreLines[2])
|
|
end
|
|
for i = 1, #lines do
|
|
lines[i] = Vector3.new(math.floor(lines[i].X + 0.5), math.floor(lines[i].Y + 0.5), math.floor(lines[i].Z + 0.5))
|
|
if lines[i].X > 0 then
|
|
xInc[i] = 1
|
|
elseif lines[i].X < 0 then
|
|
xInc[i] = -1
|
|
end
|
|
if lines[i].Y > 0 then
|
|
yInc[i] = 1
|
|
elseif lines[i].Y < 0 then
|
|
yInc[i] = -1
|
|
end
|
|
if lines[i].Z > 0 then
|
|
zInc[i] = 1
|
|
elseif lines[i].Z < 0 then
|
|
zInc[i] = -1
|
|
end
|
|
incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i])
|
|
if incrementVect[i].magnitude < 0.9 then
|
|
incrementVect[i] = nil
|
|
end
|
|
end
|
|
if not lines[2] then
|
|
lines[2] = Vector3.new(0, 0, 0)
|
|
end
|
|
if not lines[3] then
|
|
lines[3] = Vector3.new(0, 0, 0)
|
|
end
|
|
local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
|
|
local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
|
|
while stepVect[3].magnitude * 4 <= lines[3].magnitude do
|
|
local outerStepVectIndex = 1
|
|
while outerStepVectIndex < 4 do
|
|
stepVect[2] = Vector3.new(0, 0, 0)
|
|
while stepVect[2].magnitude * 4 <= lines[2].magnitude do
|
|
local innerStepVectIndex = 1
|
|
while innerStepVectIndex < 4 do
|
|
stepVect[1] = Vector3.new(0, 0, 0)
|
|
while stepVect[1].magnitude * 4 <= lines[1].magnitude do
|
|
local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3]
|
|
local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z)
|
|
if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then
|
|
local okToStampTerrainBlock = checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
|
|
if okToStampTerrainBlock then
|
|
if waterForceTag then
|
|
cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
|
|
else
|
|
cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
|
|
end
|
|
cellSet = true
|
|
if autoWedgeClusterParts then
|
|
game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
|
|
end
|
|
end
|
|
end
|
|
stepVect[1] = stepVect[1] + incrementVect[1]
|
|
end
|
|
if incrementVect[2] then
|
|
while innerStepVectIndex < 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do
|
|
innerStepVectIndex = innerStepVectIndex + 1
|
|
end
|
|
if innerStepVectIndex < 4 then
|
|
stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex] * worldAxes[innerStepVectIndex]:Dot(incrementVect[2])
|
|
end
|
|
innerStepVectIndex = innerStepVectIndex + 1
|
|
else
|
|
stepVect[2] = Vector3.new(1, 0, 0)
|
|
innerStepVectIndex = 4
|
|
end
|
|
if stepVect[2].magnitude * 4 > lines[2].magnitude then
|
|
innerStepVectIndex = 4
|
|
end
|
|
end
|
|
end
|
|
if incrementVect[3] then
|
|
while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do
|
|
outerStepVectIndex = outerStepVectIndex + 1
|
|
end
|
|
if outerStepVectIndex < 4 then
|
|
stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex] * worldAxes[outerStepVectIndex]:Dot(incrementVect[3])
|
|
end
|
|
outerStepVectIndex = outerStepVectIndex + 1
|
|
else
|
|
stepVect[3] = Vector3.new(1, 0, 0)
|
|
outerStepVectIndex = 4
|
|
end
|
|
if stepVect[3].magnitude * 4 > lines[3].magnitude then
|
|
outerStepVectIndex = 4
|
|
end
|
|
end
|
|
end
|
|
end
|
|
HighScalabilityLine.Start = nil
|
|
HighScalabilityLine.Adorn.Parent = nil
|
|
if cellSet then
|
|
stampData.CurrentParts.Parent = nil
|
|
pcall(function()
|
|
return game:GetService("ChangeHistoryService"):SetWaypoint("StamperMulti")
|
|
end)
|
|
end
|
|
return cellSet
|
|
end
|
|
local DoStamperMouseUp
|
|
DoStamperMouseUp = function(Mouse)
|
|
if not Mouse then
|
|
error("Error: RbxStamper.DoStamperMouseUp: Mouse is nil")
|
|
return false
|
|
end
|
|
if not Mouse:IsA("Mouse") then
|
|
error("Error: RbxStamper.DoStamperMouseUp: Mouse is of type", Mouse.className, "should be of type Mouse")
|
|
return false
|
|
end
|
|
if not stampData.Dragger then
|
|
error("Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil")
|
|
return false
|
|
end
|
|
if not HighScalabilityLine then
|
|
return false
|
|
end
|
|
local checkHighScalabilityStamp
|
|
if stampInModel then
|
|
local canStamp
|
|
local isHSLPart = isMegaClusterPart()
|
|
if isHSLPart and HighScalabilityLine and HighScalabilityLine.Start and HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 then
|
|
canStamp = true
|
|
checkHighScalabilityStamp = true
|
|
else
|
|
canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
|
|
end
|
|
if not canStamp then
|
|
if stampFailedFunc then
|
|
stampFailedFunc()
|
|
end
|
|
return false
|
|
end
|
|
end
|
|
if unstampableSurface then
|
|
flashRedBox()
|
|
return false
|
|
end
|
|
local canStamp
|
|
canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
|
|
if not canStamp then
|
|
if stampFailedFunc then
|
|
stampFailedFunc()
|
|
end
|
|
return false
|
|
end
|
|
local minBB, maxBB
|
|
minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
|
|
local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
|
|
if configFound and not HighScalabilityLine.Adorn.Parent then
|
|
if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
|
|
flashRedBox()
|
|
return false
|
|
end
|
|
local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100)
|
|
for b = 1, #blockingParts do
|
|
if isBlocker(blockingParts[b]) then
|
|
flashRedBox()
|
|
return false
|
|
end
|
|
end
|
|
local alreadyPushedUp = { }
|
|
for b = 1, #blockingParts do
|
|
if blockingParts[b].Parent and not alreadyPushedUp[blockingParts[b].Parent] and blockingParts[b].Parent:FindFirstChild("Humanoid") and blockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
|
|
local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild("Torso")
|
|
alreadyPushedUp[blockingParts[b].Parent] = true
|
|
if blockingPersonTorso then
|
|
local newY = maxBB.Y + 3
|
|
if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
|
|
blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
|
|
else
|
|
flashRedBox()
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then
|
|
resetHighScalabilityLine()
|
|
return false
|
|
end
|
|
if game:FindFirstChild("Players") then
|
|
if game.Players["LocalPlayer"] then
|
|
if game.Players.LocalPlayer["Character"] then
|
|
local localChar = game.Players.LocalPlayer.Character
|
|
local stampTracker = localChar:FindFirstChild("StampTracker")
|
|
if stampTracker and not stampTracker.Value then
|
|
stampTracker.Value = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then
|
|
if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then
|
|
stampData.CurrentParts.Parent = nil
|
|
return true
|
|
end
|
|
end
|
|
HighScalabilityLine.Start = nil
|
|
HighScalabilityLine.Adorn.Parent = nil
|
|
local cluster = game.Workspace.Terrain
|
|
if isMegaClusterPart() then
|
|
local cellPos
|
|
if stampData.CurrentParts:IsA("Model") then
|
|
cellPos = cluster:WorldToCell(stampData.CurrentParts:GetModelCFrame().p)
|
|
else
|
|
cellPos = cluster:WorldToCell(stampData.CurrentParts.CFrame.p)
|
|
end
|
|
local cMax = game.Workspace.Terrain.MaxExtents.Max
|
|
local cMin = game.Workspace.Terrain.MaxExtents.Min
|
|
if checkTerrainBlockCollisions(cellPos, false) then
|
|
local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
|
|
local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
|
|
local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
|
|
if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then
|
|
if waterForceTag then
|
|
cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
|
|
elseif not clusterValues then
|
|
cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, cellInfo.Material, cellInfo.clusterType, gInitial90DegreeRotations % 4)
|
|
elseif clusterValues:IsA("Vector3Value") then
|
|
cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value.X, clusterValues.Value.Y, clusterValues.Value.Z)
|
|
else
|
|
cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0)
|
|
end
|
|
local autoWedgeClusterParts = false
|
|
if stampData.CurrentParts:FindFirstChild("AutoWedge") then
|
|
autoWedgeClusterParts = true
|
|
end
|
|
if autoWedgeClusterParts then
|
|
game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
|
|
end
|
|
stampData.CurrentParts.Parent = nil
|
|
pcall(function()
|
|
return game:GetService("ChangeHistoryService"):SetWaypoint("StamperSingle")
|
|
end)
|
|
return true
|
|
end
|
|
else
|
|
flashRedBox()
|
|
return false
|
|
end
|
|
end
|
|
local getPlayer
|
|
getPlayer = function()
|
|
if game:FindFirstChild("Players") then
|
|
if game.Players["LocalPlayer"] then
|
|
return game.Players.LocalPlayer
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
|
|
if stampData.CurrentParts:IsA("Model") then
|
|
local manualWeldTable = { }
|
|
local manualWeldParentTable = { }
|
|
saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable)
|
|
stampData.CurrentParts:BreakJoints()
|
|
stampData.CurrentParts:MakeJoints()
|
|
restoreTheWelds(manualWeldTable, manualWeldParentTable)
|
|
end
|
|
local playerIdTag = stampData.CurrentParts:FindFirstChild("PlayerIdTag")
|
|
local playerNameTag = stampData.CurrentParts:FindFirstChild("PlayerNameTag")
|
|
if (playerIdTag ~= nil) then
|
|
local tempPlayerValue = getPlayer()
|
|
if (tempPlayerValue ~= nil) then
|
|
playerIdTag.Value = tempPlayerValue.userId
|
|
end
|
|
end
|
|
if (playerNameTag ~= nil) then
|
|
if game:FindFirstChild("Players" and game.Players["LocalPlayer"]) then
|
|
local tempPlayerValue = game.Players.LocalPlayer
|
|
if (tempPlayerValue ~= nil) then
|
|
playerNameTag.Value = tempPlayerValue.Name
|
|
end
|
|
end
|
|
end
|
|
if not (stampData.CurrentParts:FindFirstChild("RobloxModel") ~= nil) then
|
|
local stringTag = Instance.new("BoolValue")
|
|
stringTag.Name = "RobloxModel"
|
|
stringTag.Parent = stampData.CurrentParts
|
|
if not (stampData.CurrentParts:FindFirstChild("RobloxStamper") ~= nil) then
|
|
local stringTag2 = Instance.new("BoolValue")
|
|
stringTag2.Name = "RobloxStamper"
|
|
stringTag2.Parent = stampData.CurrentParts
|
|
end
|
|
end
|
|
else
|
|
stampData.CurrentParts:BreakJoints()
|
|
if not (stampData.CurrentParts:FindFirstChild("RobloxStamper") ~= nil) then
|
|
local stringTag2 = Instance.new("BoolValue")
|
|
stringTag2.Name = "RobloxStamper"
|
|
stringTag2.Parent = stampData.CurrentParts
|
|
end
|
|
end
|
|
if not createJoints then
|
|
game.JointsService:CreateJoinAfterMoveJoints()
|
|
end
|
|
for part, transparency in pairs(stampData.TransparencyTable) do
|
|
part.Transparency = transparency
|
|
end
|
|
for part, archivable in pairs(stampData.ArchivableTable) do
|
|
part.Archivable = archivable
|
|
end
|
|
for part, material in pairs(stampData.MaterialTable) do
|
|
part.Material = material
|
|
end
|
|
for part, collide in pairs(stampData.CanCollideTable) do
|
|
part.CanCollide = collide
|
|
end
|
|
for part, anchored in pairs(stampData.AnchoredTable) do
|
|
part.Anchored = anchored
|
|
end
|
|
for decal, transparency in pairs(stampData.DecalTransparencyTable) do
|
|
decal.Transparency = transparency
|
|
end
|
|
for part, surfaces in pairs(stampData.SurfaceTypeTable) do
|
|
loadSurfaceTypes(part, surfaces)
|
|
end
|
|
if isMegaClusterPart() then
|
|
stampData.CurrentParts.Transparency = 0
|
|
end
|
|
setSeatEnabledStatus(stampData.CurrentParts, true)
|
|
stampData.TransparencyTable = nil
|
|
stampData.ArchivableTable = nil
|
|
stampData.MaterialTable = nil
|
|
stampData.CanCollideTable = nil
|
|
stampData.AnchoredTable = nil
|
|
stampData.SurfaceTypeTable = nil
|
|
if not (stampData.CurrentParts:FindFirstChild("RobloxModel") ~= nil) then
|
|
local stringTag = Instance.new("BoolValue")
|
|
stringTag.Name = "RobloxModel"
|
|
stringTag.Parent = stampData.CurrentParts
|
|
end
|
|
if ghostRemovalScript then
|
|
ghostRemovalScript.Parent = nil
|
|
end
|
|
for _, script in pairs(stampData.DisabledScripts) do
|
|
script.Disabled = false
|
|
end
|
|
for _, script in pairs(stampData.DisabledScripts) do
|
|
local oldParent = script.Parent
|
|
script.Parent = nil
|
|
script:Clone().Parent = oldParent
|
|
end
|
|
stampData.DisabledScripts = nil
|
|
stampData.Dragger = nil
|
|
stampData.CurrentParts = nil
|
|
pcall(function()
|
|
return game:GetService("ChangeHistoryService"):SetWaypoint("StampedObject")
|
|
end)
|
|
return true
|
|
end
|
|
local pauseStamper
|
|
pauseStamper = function()
|
|
for i = 1, #mouseCons do
|
|
mouseCons[i]:disconnect()
|
|
mouseCons[i] = nil
|
|
end
|
|
mouseCons = { }
|
|
if stampData and stampData.CurrentParts then
|
|
stampData.CurrentParts.Parent = nil
|
|
stampData.CurrentParts:Remove()
|
|
end
|
|
resetHighScalabilityLine()
|
|
return game.JointsService:ClearJoinAfterMoveJoints()
|
|
end
|
|
local prepareUnjoinableSurfaces
|
|
prepareUnjoinableSurfaces = function(modelCFrame, parts, whichSurface)
|
|
local AXIS_VECTORS = {
|
|
Vector3.new(1, 0, 0),
|
|
Vector3.new(0, 1, 0),
|
|
Vector3.new(0, 0, 1)
|
|
}
|
|
local isPositive = 1
|
|
if whichSurface < 0 then
|
|
isPositive = isPositive * (-1)
|
|
whichSurface = whichSurface * (-1)
|
|
end
|
|
local surfaceNormal = isPositive * modelCFrame:vectorToWorldSpace(AXIS_VECTORS[whichSurface])
|
|
for i = 1, #parts do
|
|
local currPart = parts[i]
|
|
local surfaceNormalInLocalCoords = currPart.CFrame:vectorToObjectSpace(surfaceNormal)
|
|
if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Y) then
|
|
if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Z) then
|
|
if surfaceNormalInLocalCoords.X > 0 then
|
|
currPart.RightSurface = "Unjoinable"
|
|
else
|
|
currPart.LeftSurface = "Unjoinable"
|
|
end
|
|
else
|
|
if surfaceNormalInLocalCoords.Z > 0 then
|
|
currPart.BackSurface = "Unjoinable"
|
|
else
|
|
currPart.FrontSurface = "Unjoinable"
|
|
end
|
|
end
|
|
else
|
|
if math.abs(surfaceNormalInLocalCoords.Y) > math.abs(surfaceNormalInLocalCoords.Z) then
|
|
if surfaceNormalInLocalCoords.Y > 0 then
|
|
currPart.TopSurface = "Unjoinable"
|
|
else
|
|
currPart.BottomSurface = "Unjoinable"
|
|
end
|
|
else
|
|
if surfaceNormalInLocalCoords.Z > 0 then
|
|
currPart.BackSurface = "Unjoinable"
|
|
else
|
|
currPart.FrontSurface = "Unjoinable"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local resumeStamper
|
|
resumeStamper = function()
|
|
local clone, parts = prepareModel(modelToStamp)
|
|
if not clone or not parts then
|
|
return
|
|
end
|
|
local unjoinableTag = clone:FindFirstChild("UnjoinableFaces", true)
|
|
if unjoinableTag then
|
|
for unjoinableSurface in string.gmatch(unjoinableTag.Value, "[^,]*") do
|
|
if tonumber(unjoinableSurface) then
|
|
if clone:IsA("Model") then
|
|
prepareUnjoinableSurfaces(clone:GetModelCFrame(), parts, tonumber(unjoinableSurface))
|
|
else
|
|
prepareUnjoinableSurfaces(clone.CFrame, parts, tonumber(unjoinableSurface))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
stampData.ErrorBox = errorBox
|
|
if stampInModel then
|
|
clone.Parent = stampInModel
|
|
else
|
|
clone.Parent = game.Workspace
|
|
end
|
|
if clone:FindFirstChild("ClusterMaterial", true) then
|
|
local clusterMaterial = clone:FindFirstChild("ClusterMaterial", true)
|
|
if clusterMaterial:IsA("Vector3Value") then
|
|
cellInfo.Material = clusterMaterial.Value.X
|
|
cellInfo.clusterType = clusterMaterial.Value.Y
|
|
cellInfo.clusterOrientation = clusterMaterial.Value.Z
|
|
elseif clusterMaterial:IsA("IntValue") then
|
|
cellInfo.Material = clusterMaterial.Value
|
|
end
|
|
end
|
|
pcall(function()
|
|
mouseTarget = Mouse.Target
|
|
end)
|
|
if mouseTarget and not (mouseTarget.Parent:FindFirstChild("RobloxModel") ~= nil) then
|
|
game.JointsService:SetJoinAfterMoveTarget(mouseTarget)
|
|
else
|
|
game.JointsService:SetJoinAfterMoveTarget(nil)
|
|
end
|
|
game.JointsService:ShowPermissibleJoints()
|
|
for _, object in pairs(stampData.DisabledScripts) do
|
|
if object.Name == "GhostRemovalScript" then
|
|
object.Parent = stampData.CurrentParts
|
|
end
|
|
end
|
|
stampData.Dragger = Instance.new("Dragger")
|
|
stampData.Dragger:MouseDown(parts[1], Vector3.new(0, 0, 0, parts))
|
|
stampData.Dragger:MouseUp()
|
|
DoStamperMouseMove(Mouse)
|
|
table.insert(mouseCons, Mouse.Move:connect(function()
|
|
if movingLock or stampUpLock then
|
|
return
|
|
end
|
|
movingLock = true
|
|
DoStamperMouseMove(Mouse)
|
|
movingLock = false
|
|
end))
|
|
table.insert(mouseCons, Mouse.Button1Down:connect(function()
|
|
return DoStamperMouseDown(Mouse)
|
|
end))
|
|
table.insert(mouseCons, Mouse.Button1Up:connect(function()
|
|
stampUpLock = true
|
|
while movingLock do
|
|
wait()
|
|
end
|
|
stamped.Value = DoStamperMouseUp(Mouse)
|
|
resetHighScalabilityLine()
|
|
stampUpLock = false
|
|
end))
|
|
stamped.Value = false
|
|
end
|
|
local resetStamperState
|
|
resetStamperState = function(newModelToStamp)
|
|
if newModelToStamp then
|
|
if not newModelToStamp:IsA("Model") and not newModelToStamp:IsA("BasePart") then
|
|
error("resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!")
|
|
end
|
|
modelToStamp = newModelToStamp
|
|
end
|
|
pauseStamper()
|
|
return resumeStamper()
|
|
end
|
|
resetStamperState()
|
|
control.Stamped = stamped
|
|
control.Paused = false
|
|
control.LoadNewModel = function(newStampModel)
|
|
if newStampModel and not newStampModel:IsA("Model") and not newStampModel:IsA("BasePart") then
|
|
error("Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!")
|
|
return nil
|
|
end
|
|
return resetStamperState(newStampModel)
|
|
end
|
|
control.ReloadModel = function()
|
|
return resetStamperState()
|
|
end
|
|
control.Pause = function()
|
|
if not control.Paused then
|
|
pauseStamper()
|
|
control.Paused = true
|
|
else
|
|
return print("RbxStamper Warning: Tried to call Control.Pause! when already paused")
|
|
end
|
|
end
|
|
control.Resume = function()
|
|
if control.Paused then
|
|
resumeStamper()
|
|
control.Paused = false
|
|
else
|
|
return print("RbxStamper Warning: Tried to call Control.Resume! without Pausing First")
|
|
end
|
|
end
|
|
control.ResetRotation = function() end
|
|
control.Destroy = function()
|
|
for i = 1, #mouseCons do
|
|
mouseCons[i]:disconnect()
|
|
mouseCons[i] = nil
|
|
end
|
|
if keyCon ~= nil then
|
|
keyCon:disconnect()
|
|
end
|
|
game.JointsService:ClearJoinAfterMoveJoints()
|
|
if adorn ~= nil then
|
|
adorn:Destroy()
|
|
end
|
|
if adornPart ~= nil then
|
|
adornPart:Destroy()
|
|
end
|
|
if errorBox ~= nil then
|
|
errorBox:Destroy()
|
|
end
|
|
if stampData ~= nil then
|
|
do
|
|
local _obj_0 = stampData.Dragger
|
|
if _obj_0 ~= nil then
|
|
_obj_0:Destroy()
|
|
end
|
|
end
|
|
end
|
|
if stampData ~= nil then
|
|
do
|
|
local _obj_0 = stampData.CurrentParts
|
|
if _obj_0 ~= nil then
|
|
_obj_0:Destroy()
|
|
end
|
|
end
|
|
end
|
|
if control and control["Stamped"] then
|
|
control.Stamped:Destroy()
|
|
end
|
|
control = nil
|
|
end
|
|
return control
|
|
end
|
|
t.Help = function(funcNameOrFunc)
|
|
if "GetStampModel" == funcNameOrFunc or t.GetStampModel == funcNameOrFunc then
|
|
return "Function GetStampModel. Arguments: assetId, useAssetVersionId. assetId is the asset to load in, define useAssetVersionId as true if assetId is a version id instead of a relative assetId. Side effect: returns a model of the assetId, or a string with error message if something fails"
|
|
elseif "SetupStamperDragger" == funcNameOrFunc or t.SetupStamperDragger == funcNameOrFunc then
|
|
return "Function SetupStamperDragger. Side Effect: Creates 4x4 stamping mechanism for building out parts quickly. Arguments: ModelToStamp, Mouse, LegalStampCheckFunction. ModelToStamp should be a Model or Part, preferrably loaded from RbxStamper.GetStampModel and should have extents that are multiples of 4. Mouse should be a mouse object (obtained from things such as Tool.OnEquipped), used to drag parts around 'stamp' them out. LegalStampCheckFunction is optional, used as a callback with a table argument (table is full of instances about to be stamped). Function should return either true or false, false stopping the stamp action."
|
|
end
|
|
end
|
|
return t
|