From 114c7c2878af22be94e2f8a7b88ab8eb35e61ff9 Mon Sep 17 00:00:00 2001 From: MuramasaM <107993352+MuramasaM@users.noreply.github.com> Date: Mon, 4 Jul 2022 14:42:24 -0700 Subject: [PATCH] Delete 20 --- public/game/20 | 2208 ------------------------------------------------ 1 file changed, 2208 deletions(-) delete mode 100644 public/game/20 diff --git a/public/game/20 b/public/game/20 deleted file mode 100644 index 3b1132c..0000000 --- a/public/game/20 +++ /dev/null @@ -1,2208 +0,0 @@ -%20% -local t = {} - -function waitForChild(instance, name) - while not instance:FindFirstChild(name) do - instance.ChildAdded:wait() - end -end - --- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0) --- --- vectorPos - End point of the line. --- --- Return: --- cellPos - The terrain cell intersection point if there is one, vectorPos if there isn't. --- hit - Whether there was a plane intersection. Value is true if there was, false if not. -function PlaneIntersection(vectorPos) - local hit = false - local currCamera = game.Workspace.CurrentCamera - local startPos = Vector3.new(currCamera.CoordinateFrame.p.X, currCamera.CoordinateFrame.p.Y, currCamera.CoordinateFrame.p.Z) - 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 - local 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 - - --- Purpose: --- Checks for terrain touched by the mouse hit. --- Will do a plane intersection if no terrain is touched. --- --- mouse - Mouse to check the .hit for. --- --- Return: --- cellPos - Cell position hit. Nil if none. -function GetTerrainForMouse(mouse) - -- There was no target, so all it could be is a plane intersection. - -- Check for a plane intersection. If there isn't one then nothing will get hit. - local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z)) - local planeLoc = nil - -- If nothing was hit, do the plane intersection. - if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then - cell = nil - planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z)) - if hit then - cell = planeLoc - end - end - return cell -end - --- setup helper functions -local insertBoundingBoxOverlapVector = Vector3.new(.3, .3, .3) -- we can still stamp if our character extrudes into the target stamping space by .3 or fewer units - --- rotates a model by yAngle radians about the global y-axis -local function rotatePartAndChildren(part, rotCF, offsetFromOrigin) - -- rotate this thing, if it's a part - if part:IsA("BasePart") then - part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin - end - - -- recursively do the same to all children - local partChildren = part:GetChildren() - for c = 1, #partChildren do rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin) end -end - -local function modelRotate(model, yAngle) - local rotCF = CFrame.Angles(0, yAngle, 0) - local offsetFromOrigin = model:GetModelCFrame().p - - rotatePartAndChildren(model, rotCF, offsetFromOrigin) -end - - -local function collectParts(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 index,child in pairs(object:GetChildren()) do - collectParts(child, baseParts, scripts, decals) - end -end - -local function clusterPartsInRegion(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 function findSeatsInModel(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 function setSeatEnabledStatus(model, isEnabled) - local seatList = {} - findSeatsInModel(model, seatList) - - if isEnabled then - -- remove any welds called "SeatWeld" in seats - for i = 1, #seatList do - local nextSeat = seatList[i]:FindFirstChild("SeatWeld") - while nextSeat do nextSeat:Remove() nextSeat = seatList[i]:FindFirstChild("SeatWeld") end - end - else - -- put a weld called "SeatWeld" in every seat - -- this tricks it into thinking there's already someone sitting there, and it won't make you sit XD - for i = 1, #seatList do - local fakeWeld = Instance.new("Weld") - fakeWeld.Name = "SeatWeld" - fakeWeld.Parent = seatList[i] - end - end -end - -local function autoAlignToFace(parts) - local aatf = parts:FindFirstChild("AutoAlignToFace") - if aatf then return aatf.Value else return false end -end - -local function getClosestAlignedWorldDirection(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 function positionPartsAtCFrame3(aCFrame, currentParts) - local insertCFrame = nil - 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 function calcRayHitTime(rayStart, raySlope, intersectionPlane) - if math.abs(raySlope) < .01 then return 0 end -- 0 slope --> we just say intersection time is 0, and sidestep this dimension - return (intersectionPlane - rayStart) / raySlope -end - -local function modelTargetSurface(partOrModel, rayStart, rayEnd) - if not partOrModel then - return 0 - end - - local modelCFrame = nil - local modelSize = nil - 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 - - -- find which surface the transformed mouse ray hits (using modelSize): - 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 >= 0 and yHitTime >= 0 and zHitTime >= 0 then - if xHitTime > yHitTime then - if xHitTime > zHitTime then - -- xFace is hit - hitFace = 1*xPositive - else - -- zFace is hit - hitFace = 3*zPositive - end - else - if yHitTime > zHitTime then - -- yFace is hit - hitFace = 2*yPositive - else - -- zFace is hit - hitFace = 3*zPositive - end - end - - return hitFace -end - -local function getBoundingBox2(partOrModel) - - -- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes - -- relative to the first part's coordinate frame. - 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 - - -- Adjust bounding box to reflect what the model or part author wants in terms of justification - local justifyValue = partOrModel:FindFirstChild("Justification") - if justifyValue ~= nil then - -- find the multiple of 4 that contains the model - justify = justifyValue.Value - two = Vector3.new(2, 2, 2) - actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01) - containingGridBox = Vector3.new(4 * math.ceil(actualBox.x/4), 4 * math.ceil(actualBox.y/4), 4 * math.ceil(actualBox.z/4)) - adjustment = containingGridBox - actualBox - minVec = minVec - 0.5 * adjustment * justify - maxVec = maxVec + 0.5 * adjustment * (two - justify) - end - - return minVec, maxVec -end - -local function getBoundingBoxInWorldCoordinates(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 - vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size) - 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 partOrModel:IsA("Terrain") then - -- we shouldn't have to deal with this case - --minVec = Vector3.new(-2, -2, -2) - --maxVec = Vector3.new(2, 2, 2) - else - vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize()) - 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 function getTargetPartBoundingBox(targetPart) - if targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then - return getBoundingBox2(targetPart.Parent) - else - return getBoundingBox2(targetPart) - end -end - -local function getMouseTargetCFrame(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 function isBlocker(part) -- returns whether or not we want to cancel the stamp because we're blocked by this 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 - --- helper function to determine if a character can be pushed upwards by a certain amount --- character is 5 studs tall, we'll check a 1.5 x 1.5 x 4.5 box around char, with center .5 studs below torsocenter -local function spaceAboveCharacter(charTorso, newTorsoY, stampData) - local partsAboveChar = game.Workspace:FindPartsInRegion3( - Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75), - Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .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(.75, 2.75, .75), - Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)) then - return false - end - - return true -end - - -local function findConfigAtMouseTarget(Mouse, stampData) - -- *Critical Assumption* : - -- This function assumes the target CF axes are orthogonal with the target bounding box faces - -- And, it assumes the insert CF axes are orthongonal with the insert bounding box faces - -- Therefore, insertion will not work with angled faces on wedges or other "non-block" parts, nor - -- will it work for parts in a model that are not orthogonally aligned with the model's CF. - - if not Mouse then return nil end -- This can happen sometimes, return if so - if not stampData then error("findConfigAtMouseTarget: stampData is nil") return nil end - if not stampData["CurrentParts"] then return nil 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 = nil - local success = pcall(function() targetPart = Mouse.Target end) - - if not success then-- or targetPart == nil 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 = nil - - -- Nothing was hit, so check for the default plane. - if nil == targetPart then - cellPos = GetTerrainForMouse(Mouse) - if nil == cellPos then - hitPlane = false - return admissibleConfig, targetConfig - else - targetPart = game.Workspace.Terrain - hitPlane = true - -- Take into account error that will occur. - cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z) - mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z) - end - end - - -- test mouse hit location - local 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 - -- DON'T WANT THIS IN TERMS OF THE MODEL CFRAME! (.TargetSurface is in terms of the part CFrame, so this would break, right? [HotThoth]) - -- (ideally, we would want to make the Mouse.TargetSurface a model-targetsurface instead, but for testing will be using the converse) - --targetVectorInWorld = targetCFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) - targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) -- better, but model cframe would be best - --[[if targetPart.Parent:IsA("Model") then - local hitFace = modelTargetSurface(targetPart.Parent, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) -- best, if you get it right - local WORLD_AXES = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)} - if hitFace > 0 then - targetVectorInWorld = targetCFrame:vectorToWorldSpace(WORLD_AXES[hitFace]) - elseif hitFace < 0 then - targetVectorInWorld = targetCFrame:vectorToWorldSpace(-WORLD_AXES[-hitFace]) - end - end]] - end - - local targetRefPointInTarget - 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) - - -- To Do: For cases that are not aligned to the world grid, account for the minimal rotation - -- needed to bring the Insert part(s) into alignment with the Target Part - -- Apply the rotation here - - 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 x, y, z, 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 function truncateToCircleEighth(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 > .382683432 then - -- between 22.5 and 45 degrees, so truncate to 45-degree tilt - return .707106781 * hypotenuse * bigSign, .707106781 * hypotenuse * littleSign - else - -- between 0 and 22.5 degrees, so truncate to 0-degree tilt - return hypotenuse * bigSign, 0 - end -end - - -local function saveTheWelds(object, manualWeldTable, manualWeldParentTable) - if object:IsA("ManualWeld") or object:IsA("Rotate") then - table.insert(manualWeldTable, object) - table.insert(manualWeldParentTable, object.Parent) - else - local children = object:GetChildren() - for i = 1, #children do - saveTheWelds(children[i], manualWeldTable, manualWeldParentTable) - end - end -end - -local function restoreTheWelds(manualWeldTable, manualWeldParentTable) - for i = 1, #manualWeldTable do - manualWeldTable[i].Parent = manualWeldParentTable[i] - end -end - -t.CanEditRegion = function(partOrModel, EditRegion) -- todo: use model and stamper metadata - if not EditRegion then return true, false end - - local 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 function UnlockInstances(object) - if object:IsA("BasePart") then - object.Locked = false - end - for index,child in pairs(object:GetChildren()) do - UnlockInstances(child) - end - end - - local function getClosestColorToTerrainMaterial(terrainValue) - if terrainValue == 1 then - return BrickColor.new("Bright green") - elseif terrainValue == 2 then - return BrickColor.new("Bright yellow") - elseif terrainValue == 3 then - return BrickColor.new("Bright red") - elseif terrainValue == 4 then - return BrickColor.new("Sand red") - elseif terrainValue == 5 then - return BrickColor.new("Black") - elseif terrainValue == 6 then - return BrickColor.new("Dark stone grey") - elseif terrainValue == 7 then - return BrickColor.new("Sand blue") - elseif terrainValue == 8 then - return BrickColor.new("Deep orange") - elseif terrainValue == 9 then - return BrickColor.new("Dark orange") - elseif terrainValue == 10 then - return BrickColor.new("Reddish brown") - elseif terrainValue == 11 then - return BrickColor.new("Light orange") - elseif terrainValue == 12 then - return BrickColor.new("Light stone grey") - elseif terrainValue == 13 then - return BrickColor.new("Sand green") - elseif terrainValue == 14 then - return BrickColor.new("Medium stone grey") - elseif terrainValue == 15 then - return BrickColor.new("Really red") - elseif terrainValue == 16 then - return BrickColor.new("Really blue") - elseif terrainValue == 17 then - return BrickColor.new("Bright blue") - else - return BrickColor.new("Bright green") - end - end - - local function setupFakeTerrainPart(cellMat, cellType, cellOrient) - local newTerrainPiece = nil - 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" - - -- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors - 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.morblox.us/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 - - -- This call will cause a "wait" until the data comes back - -- below we wait a max of 8 seconds before deciding to bail out on loading - local root - local loader - 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 - - --Unlock all parts that are inserted, to make sure they are editable - UnlockInstances(root) - - --Continue the insert process - root = root:GetChildren()[1] - - --Examine the contents and decide what it looks like - for pos, 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 index,child in pairs(lightingService:GetChildren()) do - if child:IsA("Sky") then - child:Remove(); - end - end - instance.Parent = lightingService - return - end - end - - -- ...and tag all inserted models for subsequent origin identification - -- if no RobloxModel tag already exists, then add it. - if root:FindFirstChild("RobloxModel") == nil then - local stringTag = Instance.new("BoolValue", root) - stringTag.Name = "RobloxModel" - - if root:FindFirstChild("RobloxStamper") == nil then - local stringTag2 = Instance.new("BoolValue", root) - stringTag2.Name = "RobloxStamper" - end - end - - if terrainShape then - if root.Name == "MegaClusterCube" then - if (terrainShape == 6) then -- insert an autowedging tag - 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 = nil - local allowedStampRegion = nil - local stampFailedFunc = nil - 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 - - -- Init all state variables - local gInitial90DegreeRotations = 0 - local stampData = nil - local mouseTarget = nil - - local errorBox = Instance.new("SelectionBox") - errorBox.Color = BrickColor.new("Bright red") - errorBox.Transparency = 0 - errorBox.Archivable = false - - -- for megacluster MEGA STAMPING - 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 = nil - - 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 function isMegaClusterPart() - 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 function DoHighScalabilityRegionSelect() - 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 = nil - local line2 = Vector3.new(0, 0, 0) - local line3 = Vector3.new(0, 0, 0) - - if HighScalabilityLine.Dimensions == 1 then - -- extract the line from these positions and limit to a 2D plane made from 2 of the world axes - -- then use dominating axis to limit line to be at 45-degree intervals - -- will use this internal representation of the line for the actual stamping - 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 - -- limit to Y/Z plane, domination unknown - 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 - -- limit to X/Y plane, with Y dominating - local 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 - -- limit to X/Z plane, domination unknown - 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 - -- limit to X/Y plane, with X dominating - local 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] - - -- take out any component of line2 along line1, so you get perpendicular to line1 component - line2 = line2 - line.unit*line.unit:Dot(line2) - - tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) - - -- then zero out whichever is the smaller component - 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] - - -- zero out all components of previous lines - line3 = line3 - line.unit * line.unit:Dot(line3) - line3 = line3 - line2.unit * line2.unit:Dot(line3) - - HighScalabilityLine.InternalLine = line3 - end - - -- resize the "line" graphic to be the correct size and orientation - tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) - - if HighScalabilityLine.Dimensions == 1 then -- faster calculation for line - 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 - - -- make player able to see this ish - - local gui = nil - 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 then -- don't show if mouse hasn't moved enough - HighScalabilityLine.Adorn.Parent = gui - elseif HighScalabilityLine.Dimensions > 1 then - HighScalabilityLine.Adorn.Parent = gui - end - end - end - - if gui == nil then -- we are in studio - gui = game:GetService("CoreGui") - if HighScalabilityLine.Dimensions == 1 and line.magnitude > 3 then -- don't show if mouse hasn't moved enough - HighScalabilityLine.Adorn.Parent = gui - elseif HighScalabilityLine.Dimensions > 1 then - HighScalabilityLine.Adorn.Parent = gui - end - end - end - - - local function DoStamperMouseMove(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 - - -- There wasn't a target (no part or terrain), so check for plane intersection. - if not Mouse.Target then - local cellPos = GetTerrainForMouse(Mouse) - if nil == cellPos then - return - end - end - - if not stampData then - return - end - - -- don't move with dragger - will move in one step on mouse down - -- draw ghost at acceptable positions - configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData) - if not configFound then - error("RbxStamper.DoStamperMouseMove No configFound, returning") - return - end - - local numRotations = 0 -- update this according to how many rotations you need to get it to target surface - if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then -- pre-rotate the flag or portrait so it's aligned correctly - 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 - --stampData.CurrentParts:Rotate(0, ry*numRotations, 0) - modelRotate(stampData.CurrentParts, ry*numRotations) - else - stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry*numRotations, 0) * stampData.CurrentParts.CFrame - end - - -- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE - local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) - - -- need to offset by distance to be dragged - local currModelCFrame = nil - 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 - - -- don't drag into terrain - 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 we are stamping a terrain part, make sure it goes on the grid! Otherwise preview block could be placed off grid, but stamped on grid - if isMegaClusterPart() then - local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p) - local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z) - local x, y, z, 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 -- successful positioning, so update 'dat cframe - 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 - - - -- auto break joints code - 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 (modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces")) or (modelInfo and myModelInfo) then -- need better targetSurface calcs - 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 - - -- are we stamping TO an unstampable surface? - for bf in string.gmatch(breakingFaces, "[^,]+") do - if hitFace == tonumber(bf) then - -- return before we hit the JointsService code below! - unstampableSurface = true - game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache - return - end - end - - -- now we have to cast the ray back in the other direction to find the surface we're stamping FROM - hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) - - -- are we stamping WITH an unstampable surface? - for bf in string.gmatch(myBreakingFaces, "[^,]+") do - if hitFace == tonumber(bf) then - unstampableSurface = true - game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache - return - end - end - - -- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame - -- targetSurface: 1 is top, 4 is bottom, - end - end - - -- to show joints during the mouse move - unstampableSurface = false - game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts) - - -- most common mouse inactive error occurs here, so check mouse active one more time in a pcall - if not pcall(function() - if Mouse and Mouse.Target and 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 Mouse.Target.Parent:FindFirstChild("RobloxModel") == nil then - game.JointsService:SetJoinAfterMoveTarget(Mouse.Target) - else - game.JointsService:SetJoinAfterMoveTarget(nil) - end - game.JointsService:ShowPermissibleJoints() - - -- here we allow for a line of high-scalability parts - if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then - DoHighScalabilityRegionSelect() - end - end - - local function setupKeyListener(key, Mouse) - if control and control["Paused"] then return end -- don't do this if we have no stamp - - key = string.lower(key) - if key == 'r' and not autoAlignToFace(stampData.CurrentParts) then -- rotate the model - gInitial90DegreeRotations = gInitial90DegreeRotations + 1 - - -- Update orientation value if this is a fake terrain part - 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 - - -- Rotate the parts or all the parts in the model - local ry = math.pi/2 - if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then - --stampData.CurrentParts:Rotate(0, ry, 0) - modelRotate(stampData.CurrentParts, ry) - else - stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame - end - - -- After rotating, update the position - configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) - if configFound then - positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) - - -- update everything else in MouseMove - DoStamperMouseMove(Mouse) - end - elseif key == 'c' then -- try to expand our high scalability dragger dimension - 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) -- init key connection (keeping code close to func) - setupKeyListener(key, Mouse) - end) - - local function resetHighScalabilityLine() - if HighScalabilityLine then - HighScalabilityLine.Start = nil - HighScalabilityLine.End = nil - HighScalabilityLine.InternalLine = nil - HighScalabilityLine.NewHint = true - end - end - - local function flashRedBox() - 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 - - delay(0,function() - for i = 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 function DoStamperMouseDown(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 function loadSurfaceTypes(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] - end - - local function saveSurfaceTypes(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 function makeSurfaceUnjoinable(part, surface) - -- TODO: FILL OUT! - end - - local function prepareModel(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 index,script in pairs(scripts) do - if not(script.Disabled) then - script.Disabled = true - stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script - end - end - for index, 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) -- give it some time to be completely transparent - - 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 index, decal in pairs(decals) do - stampData.DecalTransparencyTable[decal] = decal.Transparency - decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency - end - - -- disable all seats - setSeatEnabledStatus(clone, true) - setSeatEnabledStatus(clone, false) - - stampData.CurrentParts = clone - - -- if auto-alignable, we enforce a pre-rotation to the canonical "0-frame" - if autoAlignToFace(clone) then - stampData.CurrentParts:ResetOrientationToIdentity() - gInitial90DegreeRotations = 0 - else -- pre-rotate if necessary - local ry = gInitial90DegreeRotations * math.pi/2 - if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then - --stampData.CurrentParts:Rotate(0, ry, 0) - modelRotate(stampData.CurrentParts, ry) - else - stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame - end - end - - -- since we're cloning the old model instead of the new one, we will need to update the orientation based on the original value AND how many more - -- rotations we expect since then [either that or we need to store the just-stamped clusterMaterial.Value.Z somewhere]. This should fix the terrain rotation - -- issue (fingers crossed) [HotThoth] - - 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 - - -- After rotating, update the position - local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) - if configFound then - stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) - end - - -- to show joints during the mouse move - game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts) - - return clone, parts - end - - local function checkTerrainBlockCollisions(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 - -- pop players up above any set cells - local alreadyPushedUp = {} - -- if no blocking model below, then see if stamping on top of a character - 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 - -- if so, let's push the person upwards so they pop on top of the stamped model/part (but only if there's space above them) - 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 - -- if no space, we just skip this one - skipThisCell = true - break - end - end - ----------------------------------------------------------------------------------- - end - end - end - - if not skipThisCell then -- if we STILL aren't skipping... then we're good to go! - local canSetCell = true - - if checkHighScalabilityStamp then -- check to see if cell is in region, if not we'll skip set - if allowedStampRegion then - local cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z) - if cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X/2 then - canSetCell = false - elseif cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X/2 then - canSetCell = false - elseif cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y/2 then - canSetCell = false - elseif cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y/2 then - canSetCell = false - elseif cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z/2 then - canSetCell = false - elseif cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z/2 then - canSetCell = false - end - end - end - - return canSetCell - end - return false - end - - - local function ResolveMegaClusterStamp(checkHighScalabilityStamp) - local cellSet = false - - local cluser = game.Workspace.Terrain - - local line = HighScalabilityLine.InternalLine - local cMax = game.Workspace.Terrain.MaxExtents.Max - local cMin = game.Workspace.Terrain.MaxExtents.Min - - local clusterMaterial = 1 -- default is grass - local clusterType = 0 -- default is brick - local clusterOrientation = 0 -- default is 0 rotation - - 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 cluster = game.Workspace.Terrain - - 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+.5), math.floor(lines[i].Y+.5), math.floor(lines[i].Z+.5)) -- round to integers - - 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 < .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 - -- check if overlaps player or part - 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 - - -- auto-wedge it? - 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 -- skip all remaining loops - 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 -- skip all remaining loops - 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 - - -- and also get rid of any HighScalabilityLine stuff if it's there - HighScalabilityLine.Start = nil - HighScalabilityLine.Adorn.Parent = nil - - -- Mark for undo. - if cellSet then - stampData.CurrentParts.Parent = nil - pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StamperMulti") end) - end - - return cellSet - end - - local function DoStamperMouseUp(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 = nil - if stampInModel then - local canStamp = nil - local isHSLPart = isMegaClusterPart() - - if isHSLPart and - HighScalabilityLine and - HighScalabilityLine.Start and - HighScalabilityLine.InternalLine and - HighScalabilityLine.InternalLine.magnitude > 0 then -- we have an HSL line, test later - 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 unstampable face, then don't let us stamp there! - if unstampableSurface then - flashRedBox() - return false - end - - -- recheck if we can stamp, as we just moved part - canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion) - if not canStamp then - if stampFailedFunc then - stampFailedFunc() - end - return false - end - - -- Prevent part from being stamped on top of a player - - local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) - - -- HotThoth's note: Now that above CurrentParts positioning has been commented out, to be truly correct, we would need to use the - -- value of configFound from the previous onStamperMouseMove call which moved the CurrentParts - -- Shouldn't this be true when lastTargetCFrame has been set and false otherwise? - 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 = {} - -- if no blocking model below, then see if stamping on top of a character - 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 - -- if so, let's push the person upwards so they pop on top of the stamped model/part (but only if there's space above them) - 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 - -- if no space, we just error - flashRedBox() - return false - end - end - --------------------------------------------------------------------------- - end - end - - elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then -- if no config then only stamp if it's a real HSL! - resetHighScalabilityLine() - return false - end - - -- something will be stamped! so set the "StampedSomething" toggle to true - 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 we drew a line of mega parts, stamp them out - if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then - if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then - -- kill the ghost part - stampData.CurrentParts.Parent = nil - return true - end - end - - -- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff] - HighScalabilityLine.Start = nil - HighScalabilityLine.Adorn.Parent = nil - - local cluster = game.Workspace.Terrain - - -- if target point is in cluster, just use cluster:SetCell - if isMegaClusterPart() then - -- if targetCFrame is inside cluster, just set that cell to 1 and return - --local cellPos = cluster:WorldToCell(targetCFrame.p) - - 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 - - -- auto-wedge it - 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 - - -- kill the ghost part - stampData.CurrentParts.Parent = nil - - -- Mark for undo. It has to happen here or the selection display will come back also. - pcall(function() game:GetService("ChangeHistoryService"):SetWaypoint("StamperSingle") end) - return true - end - else - -- you tried to stamp a HSL-single part where one does not belong! - flashRedBox() - return false - end - end - - local function getPlayer() - if game:FindFirstChild("Players") then - if game.Players["LocalPlayer"] then - return game.Players.LocalPlayer - end - end - return nil - end - - - -- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints - if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then - if stampData.CurrentParts:IsA("Model") then - -- Tyler's magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don't ask X< - local manualWeldTable = {} - local manualWeldParentTable = {} - saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable) - stampData.CurrentParts:BreakJoints() - stampData.CurrentParts:MakeJoints() - restoreTheWelds(manualWeldTable, manualWeldParentTable) - end - - -- if it's a model, we also want to fill in the playerID and playerName tags, if it has those (e.g. for the friend-only door) - playerIdTag = stampData.CurrentParts:FindFirstChild("PlayerIdTag") - playerNameTag = stampData.CurrentParts:FindFirstChild("PlayerNameTag") - if playerIdTag ~= nil then - 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 - tempPlayerValue = game.Players.LocalPlayer - if tempPlayerValue ~= nil then playerNameTag.Value = tempPlayerValue.Name end - end - end - -- ...and tag all inserted models for subsequent origin identification - -- if no RobloxModel tag already exists, then add it. - if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then - local stringTag = Instance.new("BoolValue", stampData.CurrentParts) - stringTag.Name = "RobloxModel" - - if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then - local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts) - stringTag2.Name = "RobloxStamper" - end - end - - else - stampData.CurrentParts:BreakJoints() - if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then - local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts) - stringTag2.Name = "RobloxStamper" - end - end - - -- make sure all the joints are activated before restoring anchor states - if not createJoints then game.JointsService:CreateJoinAfterMoveJoints() end - - -- Restore the original properties for all parts being stamped - 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 - - -- re-enable all seats - setSeatEnabledStatus(stampData.CurrentParts, true) - - stampData.TransparencyTable = nil - stampData.ArchivableTable = nil - stampData.MaterialTable = nil - stampData.CanCollideTable = nil - stampData.AnchoredTable = nil - stampData.SurfaceTypeTable = nil - - -- ...and tag all inserted models for subsequent origin identification - -- if no RobloxModel tag already exists, then add it. - if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then - local stringTag = Instance.new("BoolValue", stampData.CurrentParts) - stringTag.Name = "RobloxModel" - end - - -- and make sure we don't delete it, now that it's not a ghost part - if ghostRemovalScript then ghostRemovalScript.Parent = nil end - - --Re-enable the scripts - for index,script in pairs(stampData.DisabledScripts) do - script.Disabled = false - end - - --Now that they are all marked enabled, reinsert them into the world so they start running - for index,script in pairs(stampData.DisabledScripts) do - local oldParent = script.Parent - script.Parent = nil - script:Clone().Parent = oldParent - end - - -- clear out more data - stampData.DisabledScripts = nil - stampData.Dragger = nil - stampData.CurrentParts = nil - - pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StampedObject") end) - return true - end - - local function pauseStamper() - for i = 1, #mouseCons do -- stop the mouse from doing anything - mouseCons[i]:disconnect() - mouseCons[i] = nil - end - mouseCons = {} - - if stampData and stampData.CurrentParts then -- remove our ghost part - stampData.CurrentParts.Parent = nil - stampData.CurrentParts:Remove() - end - - resetHighScalabilityLine() - - game.JointsService:ClearJoinAfterMoveJoints() - end - - - local function prepareUnjoinableSurfaces(modelCFrame, parts, whichSurface) - local AXIS_VECTORS = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)} -- maybe last one is negative? TODO: check this! - 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] - - -- now just need to find which surface of currPart most closely match surfaceNormal and then set that to Unjoinable - 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 function resumeStamper() - clone, parts = prepareModel(modelToStamp) - - if not clone or not parts then - return - end - - -- if we have unjoinable faces, then we want to change those surfaces to be Unjoinable - 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 -- extract all info from vector - 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 mouseTarget.Parent:FindFirstChild("RobloxModel") == nil then - game.JointsService:SetJoinAfterMoveTarget(mouseTarget) - else - game.JointsService:SetJoinAfterMoveTarget(nil) - end - game.JointsService:ShowPermissibleJoints() - - for index, object in pairs(stampData.DisabledScripts) do - if object.Name == "GhostRemovalScript" then - object.Parent = stampData.CurrentParts - end - end - - stampData.Dragger = Instance.new("Dragger") - - --Begin a movement by faking a MouseDown signal - 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() - 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 function resetStamperState(newModelToStamp) - - -- if we have a new model, swap it out - 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 - - -- first clear our state - pauseStamper() - -- now lets load in the new model - resumeStamper() - - end - - -- load the model initially - resetStamperState() - - - -- setup the control table we pass back to the user - control.Stamped = stamped -- BoolValue that fires when user stamps - control.Paused = false - - control.LoadNewModel = function(newStampModel) -- allows us to specify a new stamper model to be used with this stamper - 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 - resetStamperState(newStampModel) - end - - control.ReloadModel = function() -- will automatically set stamper to get a new model of current model and start stamping with new model - resetStamperState() - end - - control.Pause = function() -- temporarily stops stamping, use resume to start up again - if not control.Paused then - pauseStamper() - control.Paused = true - else - print("RbxStamper Warning: Tried to call Control.Pause() when already paused") - end - end - - control.Resume = function() -- resumes stamping, if currently paused - if control.Paused then - resumeStamper() - control.Paused = false - else - print("RbxStamper Warning: Tried to call Control.Resume() without Pausing First") - end - end - - control.ResetRotation = function() -- resets the model rotation so new models are at default orientation - -- gInitial90DegreeRotations = 0 - -- Note: This function will not always work quite the way we want it to; we will have to build this out further so it works with - -- High-Scalability and with the new model orientation setting methods (model:ResetOrientationToIdentity()) [HotThoth] - end - - control.Destroy = function() -- Stops current Stamp operation and destroys control construct - for i = 1, #mouseCons do - mouseCons[i]:disconnect() - mouseCons[i] = nil - end - - if keyCon then - keyCon:disconnect() - end - - game.JointsService:ClearJoinAfterMoveJoints() - - if adorn then adorn:Destroy() end - if adornPart then adornPart:Destroy() end - if errorBox then errorBox:Destroy() end - if stampData then - if stampData["Dragger"] then - stampData.Dragger:Destroy() - end - if stampData.CurrentParts then - stampData.CurrentParts:Destroy() - end - end - if control and control["Stamped"] then - control.Stamped:Destroy() - end - control = nil - end - - return control -end - -t.Help = - function(funcNameOrFunc) - --input argument can be a string or a function. Should return a description (of arguments and expected side effects) - if funcNameOrFunc == "GetStampModel" or funcNameOrFunc == t.GetStampModel 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" - end - if funcNameOrFunc == "SetupStamperDragger" or funcNameOrFunc == t.SetupStamperDragger 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