roblonium-web/Game/FinobeTools/PBSTools/Stamper2.rbxm

3665 lines
156 KiB
Plaintext

<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
<External>null</External>
<External>nil</External>
<Item class="Tool" referent="RBX0">
<Properties>
<bool name="CanBeDropped">true</bool>
<bool name="Enabled">true</bool>
<CoordinateFrame name="Grip">
<X>0</X>
<Y>-0.100000001</Y>
<Z>0.100000001</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>-4.37113883e-008</R11>
<R12>-1</R12>
<R20>0</R20>
<R21>1</R21>
<R22>-4.37113883e-008</R22>
</CoordinateFrame>
<string name="Name">Stamper 2.0</string>
<Content name="TextureId"><url>http://www.roblox.com/asset/?id=73241762</url></Content>
<string name="ToolTip"></string>
</Properties>
<Item class="Part" referent="RBX1">
<Properties>
<bool name="Anchored">false</bool>
<float name="BackParamA">-0.5</float>
<float name="BackParamB">0.5</float>
<token name="BackSurface">0</token>
<token name="BackSurfaceInput">0</token>
<float name="BottomParamA">-0.5</float>
<float name="BottomParamB">0.5</float>
<token name="BottomSurface">4</token>
<token name="BottomSurfaceInput">0</token>
<int name="BrickColor">194</int>
<CoordinateFrame name="CFrame">
<X>0</X>
<Y>0.600000024</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<bool name="CanCollide">true</bool>
<float name="Elasticity">0.5</float>
<float name="Friction">0.300000012</float>
<float name="FrontParamA">-0.5</float>
<float name="FrontParamB">0.5</float>
<token name="FrontSurface">0</token>
<token name="FrontSurfaceInput">0</token>
<float name="LeftParamA">-0.5</float>
<float name="LeftParamB">0.5</float>
<token name="LeftSurface">0</token>
<token name="LeftSurfaceInput">0</token>
<bool name="Locked">false</bool>
<token name="Material">256</token>
<string name="Name">Handle</string>
<float name="Reflectance">0</float>
<float name="RightParamA">-0.5</float>
<float name="RightParamB">0.5</float>
<token name="RightSurface">0</token>
<token name="RightSurfaceInput">0</token>
<Vector3 name="RotVelocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<float name="TopParamA">-0.5</float>
<float name="TopParamB">0.5</float>
<token name="TopSurface">3</token>
<token name="TopSurfaceInput">0</token>
<float name="Transparency">0</float>
<Vector3 name="Velocity">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<token name="formFactorRaw">1</token>
<token name="shape">1</token>
<Vector3 name="size">
<X>2</X>
<Y>2.4000001</Y>
<Z>2</Z>
</Vector3>
</Properties>
<Item class="SpecialMesh" referent="RBX2">
<Properties>
<token name="LODX">2</token>
<token name="LODY">2</token>
<Content name="MeshId"><url>http://www.roblox.com/asset/?id=42163552</url></Content>
<token name="MeshType">5</token>
<string name="Name">Mesh</string>
<Vector3 name="Offset">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
</Vector3>
<Vector3 name="Scale">
<X>0.899999976</X>
<Y>0.899999976</Y>
<Z>0.899999976</Z>
</Vector3>
<Content name="TextureId"><url>http://www.roblox.com/asset/?id=42163513</url></Content>
<Vector3 name="VertexColor">
<X>1</X>
<Y>1</Y>
<Z>1</Z>
</Vector3>
</Properties>
</Item>
</Item>
<Item class="LocalScript" referent="RBX3">
<Properties>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<string name="Name">nstamperScript</string>
<ProtectedString name="Source">-----------------------
--[[RBXStamper code]]--
-----------------------
--consiter exporting to own file later
local RBXStamper = {}
do
&#9;-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
&#9;--
&#9;-- vectorPos - End point of the line.
&#9;--
&#9;-- Return:
&#9;-- cellPos - The terrain cell intersection point if there is one, vectorPos if there isn&apos;t.
&#9;-- hit - Whether there was a plane intersection. Value is true if there was, false if not.
&#9;local function PlaneIntersection(vectorPos)
&#9;&#9;local hit = false
&#9;&#9;local currCamera = game.Workspace.CurrentCamera
&#9;&#9;local startPos = Vector3.new(currCamera.CoordinateFrame.p.X, currCamera.CoordinateFrame.p.Y, currCamera.CoordinateFrame.p.Z)
&#9;&#9;local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
&#9;&#9;local normal = Vector3.new(0, 1, 0)
&#9;&#9;local p3 = Vector3.new(0, 0, 0)
&#9;&#9;local startEndDot = normal:Dot(endPos - startPos)
&#9;&#9;local cellPos = vectorPos
&#9;&#9;if startEndDot ~= 0 then
&#9;&#9;&#9;local t = normal:Dot(p3 - startPos) / startEndDot
&#9;&#9;&#9;if(t &gt;=0 and t &lt;=1) then
&#9;&#9;&#9;&#9;local intersection = ((endPos - startPos) * t) + startPos
&#9;&#9;&#9;&#9;cellPos = game.Workspace.Terrain:WorldToCell(intersection)
&#9;&#9;&#9;&#9;hit = true
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return cellPos, hit
&#9;end
&#9;-- Purpose:
&#9;-- Checks for terrain touched by the mouse hit.
&#9;-- Will do a plane intersection if no terrain is touched.
&#9;--
&#9;-- mouse - Mouse to check the .hit for.
&#9;--
&#9;-- Return:
&#9;-- cellPos - Cell position hit. Nil if none.
&#9;local function GetTerrainForMouse(mouse)
&#9;&#9;-- There was no target, so all it could be is a plane intersection.
&#9;&#9;-- Check for a plane intersection. If there isn&apos;t one then nothing will get hit.
&#9;&#9;local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
&#9;&#9;local planeLoc = nil
&#9;&#9;-- If nothing was hit, do the plane intersection.
&#9;&#9;if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then
&#9;&#9;&#9;cell = nil
&#9;&#9;&#9;planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
&#9;&#9;&#9;if hit then
&#9;&#9;&#9;&#9;cell = planeLoc
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return cell
&#9;end
&#9;-- setup helper functions
&#9;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
&#9;-- rotates a model by yAngle radians about the global y-axis
&#9;local function rotatePartAndChildren(part, rotCF, offsetFromOrigin)
&#9;&#9;-- rotate this thing, if it&apos;s a part
&#9;&#9;if part:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin
&#9;&#9;end
&#9;&#9;-- recursively do the same to all children
&#9;&#9;local partChildren = part:GetChildren()
&#9;&#9;for c = 1, #partChildren do rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin) end
&#9;end
&#9;local function modelRotate(model, yAngle)
&#9;&#9;local rotCF = CFrame.Angles(0, yAngle, 0)
&#9;&#9;local offsetFromOrigin = model:GetModelCFrame().p
&#9;&#9;rotatePartAndChildren(model, rotCF, offsetFromOrigin)
&#9;end
&#9;local function collectParts(object, baseParts, scripts, decals)
&#9;&#9;if object:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;baseParts[#baseParts+1] = object
&#9;&#9;elseif object:IsA(&quot;Script&quot;) then
&#9;&#9;&#9;scripts[#scripts+1] = object
&#9;&#9;elseif object:IsA(&quot;Decal&quot;) then
&#9;&#9;&#9;decals[#decals+1] = object
&#9;&#9;end
&#9;&#9;for index,child in pairs(object:GetChildren()) do
&#9;&#9;&#9;collectParts(child, baseParts, scripts, decals)
&#9;&#9;end
&#9;end
&#9;local function clusterPartsInRegion(startVector, endVector)
&#9;&#9;local cluster = game.Workspace:FindFirstChild(&quot;Terrain&quot;)
&#9;&#9;local startCell = cluster:WorldToCell(startVector)
&#9;&#9;local endCell = cluster:WorldToCell(endVector)
&#9;&#9;local startX = startCell.X
&#9;&#9;local startY = startCell.Y
&#9;&#9;local startZ = startCell.Z
&#9;&#9;local endX = endCell.X
&#9;&#9;local endY = endCell.Y
&#9;&#9;local endZ = endCell.Z
&#9;&#9;if startX &lt; cluster.MaxExtents.Min.X then startX = cluster.MaxExtents.Min.X end
&#9;&#9;if startY &lt; cluster.MaxExtents.Min.Y then startY = cluster.MaxExtents.Min.Y end
&#9;&#9;if startZ &lt; cluster.MaxExtents.Min.Z then startZ = cluster.MaxExtents.Min.Z end
&#9;&#9;if endX &gt; cluster.MaxExtents.Max.X then endX = cluster.MaxExtents.Max.X end
&#9;&#9;if endY &gt; cluster.MaxExtents.Max.Y then endY = cluster.MaxExtents.Max.Y end
&#9;&#9;if endZ &gt; cluster.MaxExtents.Max.Z then endZ = cluster.MaxExtents.Max.Z end
&#9;&#9;for x = startX, endX do
&#9;&#9;&#9;for y = startY, endY do
&#9;&#9;&#9;&#9;for z = startZ, endZ do
&#9;&#9;&#9;&#9;&#9;if (cluster:GetCell(x, y, z).Value) &gt; 0 then return true end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return false
&#9;end
&#9;local function findSeatsInModel(parent, seatTable)
&#9;&#9;if not parent then return end
&#9;&#9;if parent.className == &quot;Seat&quot; or parent.className == &quot;VehicleSeat&quot; then
&#9;&#9;&#9;table.insert(seatTable, parent)
&#9;&#9;end
&#9;&#9;local myChildren = parent:GetChildren()
&#9;&#9;for j = 1, #myChildren do
&#9;&#9;&#9;findSeatsInModel(myChildren[j], seatTable)
&#9;&#9;end
&#9;end
&#9;local function setSeatEnabledStatus(model, isEnabled)
&#9;&#9;local seatList = {}
&#9;&#9;findSeatsInModel(model, seatList)
&#9;&#9;if isEnabled then
&#9;&#9;&#9;-- remove any welds called &quot;SeatWeld&quot; in seats
&#9;&#9;&#9;for i = 1, #seatList do
&#9;&#9;&#9;&#9;local nextSeat = seatList[i]:FindFirstChild(&quot;SeatWeld&quot;)
&#9;&#9;&#9;&#9;while nextSeat do nextSeat:Remove() nextSeat = seatList[i]:FindFirstChild(&quot;SeatWeld&quot;) end
&#9;&#9;&#9;end
&#9;&#9;else
&#9;&#9;&#9;-- put a weld called &quot;SeatWeld&quot; in every seat
&#9;&#9;&#9;-- this tricks it into thinking there&apos;s already someone sitting there, and it won&apos;t make you sit XD
&#9;&#9;&#9;for i = 1, #seatList do
&#9;&#9;&#9;&#9;local fakeWeld = Instance.new(&quot;Weld&quot;)
&#9;&#9;&#9;&#9;fakeWeld.Name = &quot;SeatWeld&quot;
&#9;&#9;&#9;&#9;fakeWeld.Parent = seatList[i]
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;local function autoAlignToFace(parts)
&#9;&#9;local aatf = parts:FindFirstChild(&quot;AutoAlignToFace&quot;)
&#9;&#9;if aatf then return aatf.Value else return false end
&#9;end
&#9;local function getClosestAlignedWorldDirection(aVector3InWorld)
&#9;&#9;local xDir = Vector3.new(1,0,0)
&#9;&#9;local yDir = Vector3.new(0,1,0)
&#9;&#9;local zDir = Vector3.new(0,0,1)
&#9;&#9;local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z
&#9;&#9;local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z
&#9;&#9;local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z
&#9;&#9;if math.abs(xDot) &gt; math.abs(yDot) and math.abs(xDot) &gt; math.abs(zDot) then
&#9;&#9;&#9;if xDot &gt; 0 then
&#9;&#9;&#9;&#9;return 0
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;return 3
&#9;&#9;&#9;end
&#9;&#9;elseif math.abs(yDot) &gt; math.abs(xDot) and math.abs(yDot) &gt; math.abs(zDot) then
&#9;&#9;&#9;if yDot &gt; 0 then
&#9;&#9;&#9;&#9;return 1
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;return 4
&#9;&#9;&#9;end
&#9;&#9;else
&#9;&#9;&#9;if zDot &gt; 0 then
&#9;&#9;&#9;&#9;return 2
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;return 5
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;local function positionPartsAtCFrame3(aCFrame, currentParts)
&#9;&#9;local insertCFrame = nil
&#9;&#9;if not currentParts then return currentParts end
&#9;&#9;if currentParts and (currentParts:IsA(&quot;Model&quot;) or currentParts:IsA(&quot;Tool&quot;)) then
&#9;&#9;&#9;insertCFrame = currentParts:GetModelCFrame()
&#9;&#9;&#9;currentParts:TranslateBy(aCFrame.p - insertCFrame.p)
&#9;&#9;else
&#9;&#9;&#9;currentParts.CFrame = aCFrame
&#9;&#9;end
&#9;&#9;return currentParts
&#9;end
&#9;local function calcRayHitTime(rayStart, raySlope, intersectionPlane)
&#9;&#9;if math.abs(raySlope) &lt; .01 then return 0 end -- 0 slope --&gt; we just say intersection time is 0, and sidestep this dimension
&#9;&#9;return (intersectionPlane - rayStart) / raySlope
&#9;end
&#9;local function modelTargetSurface(partOrModel, rayStart, rayEnd)
&#9;&#9;if not partOrModel then
&#9;&#9;&#9;return 0
&#9;&#9;end
&#9;&#9;local modelCFrame = nil
&#9;&#9;local modelSize = nil
&#9;&#9;if partOrModel:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;modelCFrame = partOrModel:GetModelCFrame()
&#9;&#9;&#9;modelSize = partOrModel:GetModelSize()
&#9;&#9;else
&#9;&#9;&#9;modelCFrame = partOrModel.CFrame
&#9;&#9;&#9;modelSize = partOrModel.Size
&#9;&#9;end
&#9;&#9;local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart)
&#9;&#9;local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd)
&#9;&#9;local mouseSlope = mouseRayEnd - mouseRayStart
&#9;&#9;local xPositive = 1
&#9;&#9;local yPositive = 1
&#9;&#9;local zPositive = 1
&#9;&#9;if mouseSlope.X &gt; 0 then xPositive = -1 end
&#9;&#9;if mouseSlope.Y &gt; 0 then yPositive = -1 end
&#9;&#9;if mouseSlope.Z &gt; 0 then zPositive = -1 end
&#9;&#9;-- find which surface the transformed mouse ray hits (using modelSize):
&#9;&#9;local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X/2 * xPositive)
&#9;&#9;local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y/2 * yPositive)
&#9;&#9;local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z/2 * zPositive)
&#9;&#9;local hitFace = 0
&#9;&#9;--if xHitTime &gt;= 0 and yHitTime &gt;= 0 and zHitTime &gt;= 0 then
&#9;&#9;if xHitTime &gt; yHitTime then
&#9;&#9;&#9;if xHitTime &gt; zHitTime then
&#9;&#9;&#9;&#9;-- xFace is hit
&#9;&#9;&#9;&#9;hitFace = 1*xPositive
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;-- zFace is hit
&#9;&#9;&#9;&#9;hitFace = 3*zPositive
&#9;&#9;&#9;end
&#9;&#9;else
&#9;&#9;&#9;if yHitTime &gt; zHitTime then
&#9;&#9;&#9;&#9;-- yFace is hit
&#9;&#9;&#9;&#9;hitFace = 2*yPositive
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;-- zFace is hit
&#9;&#9;&#9;&#9;hitFace = 3*zPositive
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return hitFace
&#9;end
&#9;local function getBoundingBox2(partOrModel)
&#9;&#9;-- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes
&#9;&#9;-- relative to the first part&apos;s coordinate frame.
&#9;&#9;local minVec = Vector3.new(math.huge, math.huge, math.huge)
&#9;&#9;local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
&#9;&#9;if partOrModel:IsA(&quot;Terrain&quot;) then
&#9;&#9;&#9;minVec = Vector3.new(-2, -2, -2)
&#9;&#9;&#9;maxVec = Vector3.new(2, 2, 2)
&#9;&#9;elseif partOrModel:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;minVec = -0.5 * partOrModel.Size
&#9;&#9;&#9;maxVec = -minVec
&#9;&#9;else
&#9;&#9;&#9;maxVec = partOrModel:GetModelSize()*0.5
&#9;&#9;&#9;minVec = -maxVec
&#9;&#9;end
&#9;&#9;-- Adjust bounding box to reflect what the model or part author wants in terms of justification
&#9;&#9;local justifyValue = partOrModel:FindFirstChild(&quot;Justification&quot;)
&#9;&#9;if justifyValue ~= nil then
&#9;&#9;&#9;-- find the multiple of 4 that contains the model
&#9;&#9;&#9;justify = justifyValue.Value
&#9;&#9;&#9;two = Vector3.new(2, 2, 2)
&#9;&#9;&#9;actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01)
&#9;&#9;&#9;containingGridBox = Vector3.new(4 * math.ceil(actualBox.x/4), 4 * math.ceil(actualBox.y/4), 4 * math.ceil(actualBox.z/4))
&#9;&#9;&#9;adjustment = containingGridBox - actualBox
&#9;&#9;&#9;minVec = minVec - 0.5 * adjustment * justify
&#9;&#9;&#9;maxVec = maxVec + 0.5 * adjustment * (two - justify)
&#9;&#9;end
&#9;&#9;return minVec, maxVec
&#9;end
&#9;local function getBoundingBoxInWorldCoordinates(partOrModel)
&#9;&#9;local minVec = Vector3.new(math.huge, math.huge, math.huge)
&#9;&#9;local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
&#9;&#9;if partOrModel:IsA(&quot;BasePart&quot;) and not partOrModel:IsA(&quot;Terrain&quot;) then
&#9;&#9;&#9;vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size)
&#9;&#9;&#9;vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size)
&#9;&#9;&#9;minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
&#9;&#9;&#9;maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
&#9;&#9;elseif partOrModel:IsA(&quot;Terrain&quot;) then
&#9;&#9;&#9;-- we shouldn&apos;t have to deal with this case
&#9;&#9;&#9;--minVec = Vector3.new(-2, -2, -2)
&#9;&#9;&#9;--maxVec = Vector3.new(2, 2, 2)
&#9;&#9;else
&#9;&#9;&#9;vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize())
&#9;&#9;&#9;vec2 = partOrModel:GetModelCFrame():pointToWorldSpace(0.5 * partOrModel:GetModelSize())
&#9;&#9;&#9;minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
&#9;&#9;&#9;maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
&#9;&#9;end
&#9;&#9;return minVec, maxVec
&#9;end
&#9;local function getTargetPartBoundingBox(targetPart)
&#9;&#9;if targetPart.Parent:FindFirstChild(&quot;RobloxModel&quot;) ~= nil then
&#9;&#9;&#9;return getBoundingBox2(targetPart.Parent)
&#9;&#9;else
&#9;&#9;&#9;return getBoundingBox2(targetPart)
&#9;&#9;end
&#9;end
&#9;local function getMouseTargetCFrame(targetPart)
&#9;&#9;if targetPart.Parent:FindFirstChild(&quot;RobloxModel&quot;) ~= nil then
&#9;&#9;&#9;if targetPart.Parent:IsA(&quot;Tool&quot;) then return targetPart.Parent.Handle.CFrame
&#9;&#9;&#9;else return targetPart.Parent:GetModelCFrame() end
&#9;&#9;else
&#9;&#9;&#9;return targetPart.CFrame
&#9;&#9;end
&#9;end
&#9;local function isBlocker(part) -- returns whether or not we want to cancel the stamp because we&apos;re blocked by this part
&#9;&#9;if not part then return false end
&#9;&#9;if not part.Parent then return false end
&#9;&#9;if part:FindFirstChild(&quot;Humanoid&quot;) then return false end
&#9;&#9;if part:FindFirstChild(&quot;RobloxStamper&quot;) or part:FindFirstChild(&quot;RobloxModel&quot;) then return true end
&#9;&#9;if part:IsA(&quot;Part&quot;) and not part.CanCollide then return false end
&#9;&#9;if part == game.Lighting then return false end
&#9;&#9;return isBlocker(part.Parent)
&#9;end
&#9;-- helper function to determine if a character can be pushed upwards by a certain amount
&#9;-- character is 5 studs tall, we&apos;ll check a 1.5 x 1.5 x 4.5 box around char, with center .5 studs below torsocenter
&#9;local function spaceAboveCharacter(charTorso, newTorsoY, stampData)
&#9;&#9;local partsAboveChar = game.Workspace:FindPartsInRegion3(
&#9;&#9;&#9;Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
&#9;&#9;&#9;Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)),
&#9;&#9;&#9;charTorso.Parent,
&#9;&#9;&#9;100)
&#9;&#9;&#9;
&#9;&#9;for j = 1, #partsAboveChar do
&#9;&#9;&#9;if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(stampData.CurrentParts) then return false end
&#9;&#9;end
&#9;&#9;if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
&#9;&#9;&#9;Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)) then
&#9;&#9;&#9;&#9;return false
&#9;&#9;end
&#9;&#9;return true
&#9;end
&#9;local function findConfigAtMouseTarget(Mouse, stampData)
&#9;&#9;-- *Critical Assumption* :
&#9;&#9;--&#9;&#9;&#9;&#9;This function assumes the target CF axes are orthogonal with the target bounding box faces
&#9;&#9;--&#9;&#9;&#9;&#9;And, it assumes the insert CF axes are orthongonal with the insert bounding box faces
&#9;&#9;--&#9;&#9;&#9;&#9;Therefore, insertion will not work with angled faces on wedges or other &quot;non-block&quot; parts, nor
&#9;&#9;--&#9;&#9;&#9;&#9;will it work for parts in a model that are not orthogonally aligned with the model&apos;s CF.
&#9;&#9;if not Mouse then return nil end -- This can happen sometimes, return if so
&#9;&#9;if not stampData then error(&quot;findConfigAtMouseTarget: stampData is nil&quot;) return nil end
&#9;&#9;if not stampData[&quot;CurrentParts&quot;] then return nil end
&#9;&#9;local grid = 4.0
&#9;&#9;local admissibleConfig = false
&#9;&#9;local targetConfig = CFrame.new(0,0,0)
&#9;&#9;local minBB, maxBB = getBoundingBox2(stampData.CurrentParts)
&#9;&#9;local diagBB = maxBB - minBB
&#9;&#9;local insertCFrame
&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) or stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;insertCFrame = stampData.CurrentParts:GetModelCFrame()
&#9;&#9;else
&#9;&#9;&#9;insertCFrame = stampData.CurrentParts.CFrame
&#9;&#9;end
&#9;&#9;if Mouse then
&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;Mouse.TargetFilter = stampData.CurrentParts.Handle
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;Mouse.TargetFilter = stampData.CurrentParts
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;local hitPlane = false
&#9;&#9;local targetPart = nil
&#9;&#9;local success = pcall(function() targetPart = Mouse.Target end)
&#9;&#9;if not success then-- or targetPart == nil then
&#9;&#9;&#9;return admissibleConfig, targetConfig
&#9;&#9;end
&#9;&#9;
&#9;&#9;local mouseHitInWorld = Vector3.new(0, 0, 0)
&#9;&#9;if Mouse then
&#9;&#9;&#9;mouseHitInWorld = Vector3.new(Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z)
&#9;&#9;end
&#9;&#9;
&#9;&#9;local cellPos = nil
&#9;&#9;
&#9;&#9;-- Nothing was hit, so check for the default plane.
&#9;&#9;if nil == targetPart then
&#9;&#9;&#9;cellPos = GetTerrainForMouse(Mouse)
&#9;&#9;&#9;if nil == cellPos then
&#9;&#9;&#9;&#9;hitPlane = false
&#9;&#9;&#9;&#9;return admissibleConfig, targetConfig
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;targetPart = game.Workspace.Terrain
&#9;&#9;&#9;&#9;hitPlane = true
&#9;&#9;&#9;&#9;-- Take into account error that will occur.
&#9;&#9;&#9;&#9;cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z)
&#9;&#9;&#9;&#9;mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;-- test mouse hit location
&#9;&#9;local minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart)
&#9;&#9;local diagBBTarget = maxBBTarget - minBBTarget
&#9;&#9;local targetCFrame = getMouseTargetCFrame(targetPart)
&#9;&#9;if targetPart:IsA(&quot;Terrain&quot;) then
&#9;&#9;&#9;if not cluster then cluster = game.Workspace:FindFirstChild(&quot;Terrain&quot;) end
&#9;&#9;&#9;local cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld)
&#9;&#9;&#9;if hitPlane then
&#9;&#9;&#9;&#9;cellID = cellPos
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;targetCFrame = CFrame.new(game.Workspace.Terrain:CellCenterToWorld(cellID.x, cellID.y, cellID.z))
&#9;&#9;end
&#9;&#9;
&#9;&#9;local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld)
&#9;&#9;local targetVectorInWorld = Vector3.new(0,0,0)
&#9;&#9;if Mouse then
&#9;&#9;&#9;-- DON&apos;T WANT THIS IN TERMS OF THE MODEL CFRAME! (.TargetSurface is in terms of the part CFrame, so this would break, right? [HotThoth])
&#9;&#9;&#9;-- (ideally, we would want to make the Mouse.TargetSurface a model-targetsurface instead, but for testing will be using the converse)
&#9;&#9;&#9;--targetVectorInWorld = targetCFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface))
&#9;&#9;&#9;targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) -- better, but model cframe would be best
&#9;&#9;&#9;--[[if targetPart.Parent:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;local hitFace = modelTargetSurface(targetPart.Parent, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) -- best, if you get it right
&#9;&#9;&#9;&#9;local WORLD_AXES = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
&#9;&#9;&#9;&#9;if hitFace &gt; 0 then
&#9;&#9;&#9;&#9;&#9;targetVectorInWorld = targetCFrame:vectorToWorldSpace(WORLD_AXES[hitFace])
&#9;&#9;&#9;&#9;elseif hitFace &lt; 0 then
&#9;&#9;&#9;&#9;&#9;targetVectorInWorld = targetCFrame:vectorToWorldSpace(-WORLD_AXES[-hitFace])
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end]]
&#9;&#9;end
&#9;&#9;local targetRefPointInTarget
&#9;&#9;local clampToSurface
&#9;&#9;if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
&#9;&#9;&#9;clampToSurface = Vector3.new(0,1,1)
&#9;&#9;elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
&#9;&#9;&#9;clampToSurface = Vector3.new(0,1,1)
&#9;&#9;elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
&#9;&#9;&#9;clampToSurface = Vector3.new(1,0,1)&#9;&#9;
&#9;&#9;elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
&#9;&#9;&#9;clampToSurface = Vector3.new(1,0,1)
&#9;&#9;elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
&#9;&#9;&#9;clampToSurface = Vector3.new(1,1,0)
&#9;&#9;else
&#9;&#9;&#9;targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
&#9;&#9;&#9;insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
&#9;&#9;&#9;clampToSurface = Vector3.new(1,1,0)
&#9;&#9;end
&#9;&#9;targetRefPointInTarget = targetRefPointInTarget * (0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget)
&#9;&#9;insertRefPointInInsert = insertRefPointInInsert * (0.5 * diagBB) + 0.5 * (maxBB + minBB)
&#9;&#9;
&#9;&#9;-- To Do: For cases that are not aligned to the world grid, account for the minimal rotation
&#9;&#9;-- needed to bring the Insert part(s) into alignment with the Target Part
&#9;&#9;-- Apply the rotation here
&#9;&#9;local delta = mouseHitInTarget - targetRefPointInTarget
&#9;&#9;local deltaClamped = Vector3.new(grid * math.modf(delta.x/grid), grid * math.modf(delta.y/grid), grid * math.modf(delta.z/grid))
&#9;&#9;deltaClamped = deltaClamped * clampToSurface
&#9;&#9;local targetTouchInTarget = deltaClamped + targetRefPointInTarget
&#9;&#9;local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget)
&#9;&#9;local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert)
&#9;&#9;local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld
&#9;&#9;local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components()
&#9;&#9;targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
&#9;&#9;admissibleConfig = true
&#9;&#9;return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld)
&#9;end
&#9;local function truncateToCircleEighth(bigValue, littleValue)
&#9;&#9;local big = math.abs(bigValue)
&#9;&#9;local little = math.abs(littleValue)
&#9;&#9;local hypotenuse = math.sqrt(big*big + little*little)
&#9;&#9;local frac = little / hypotenuse
&#9;&#9;local bigSign = 1
&#9;&#9;local littleSign = 1
&#9;&#9;if bigValue &lt; 0 then bigSign = -1 end
&#9;&#9;if littleValue &lt; 0 then littleSign = -1 end
&#9;&#9;if frac &gt; .382683432 then
&#9;&#9;&#9;-- between 22.5 and 45 degrees, so truncate to 45-degree tilt
&#9;&#9;&#9;return .707106781 * hypotenuse * bigSign, .707106781 * hypotenuse * littleSign
&#9;&#9;else
&#9;&#9;&#9;-- between 0 and 22.5 degrees, so truncate to 0-degree tilt
&#9;&#9;&#9;return hypotenuse * bigSign, 0
&#9;&#9;end
&#9;end
&#9;local function saveTheWelds(object, manualWeldTable, manualWeldParentTable)
&#9;&#9;if object:IsA(&quot;ManualWeld&quot;) or object:IsA(&quot;Rotate&quot;) then
&#9;&#9;&#9;table.insert(manualWeldTable, object)
&#9;&#9;&#9;table.insert(manualWeldParentTable, object.Parent)
&#9;&#9;else
&#9;&#9;&#9;local children = object:GetChildren()
&#9;&#9;&#9;for i = 1, #children do
&#9;&#9;&#9;&#9;saveTheWelds(children[i], manualWeldTable, manualWeldParentTable)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;local function restoreTheWelds(manualWeldTable, manualWeldParentTable)
&#9;&#9;for i = 1, #manualWeldTable do
&#9;&#9;&#9;manualWeldTable[i].Parent = manualWeldParentTable[i]
&#9;&#9;end
&#9;end
&#9;RBXStamper.CanEditRegion = function(partOrModel, EditRegion) -- todo: use model and stamper metadata
&#9;&#9;if not EditRegion then return true, false end
&#9;&#9;local minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel)
&#9;&#9;
&#9;&#9;if minBB.X &lt; EditRegion.CFrame.p.X - EditRegion.Size.X/2 or
&#9;&#9;&#9;minBB.Y &lt; EditRegion.CFrame.p.Y - EditRegion.Size.Y/2 or
&#9;&#9;&#9;minBB.Z &lt; EditRegion.CFrame.p.Z - EditRegion.Size.Z/2 then
&#9;&#9;&#9;&#9;return false, false
&#9;&#9;end
&#9;&#9;
&#9;&#9;if maxBB.X &gt; EditRegion.CFrame.p.X + EditRegion.Size.X/2 or
&#9;&#9;&#9;maxBB.Y &gt; EditRegion.CFrame.p.Y + EditRegion.Size.Y/2 or
&#9;&#9;&#9;maxBB.Z &gt; EditRegion.CFrame.p.Z + EditRegion.Size.Z/2 then
&#9;&#9;&#9;&#9;return false, false
&#9;&#9;end
&#9;&#9;
&#9;&#9;return true, false
&#9;end
&#9;RBXStamper.GetStampModel = function(assetId, terrainShape, useAssetVersionId)
&#9;&#9;if assetId == 0 then
&#9;&#9;&#9;return nil, &quot;No Asset&quot;
&#9;&#9;end
&#9;&#9;if assetId &lt; 0 then
&#9;&#9;&#9;return nil, &quot;Negative Asset&quot;
&#9;&#9;end
&#9;&#9;local function UnlockInstances(object)
&#9;&#9;&#9;if object:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;&#9;object.Locked = false
&#9;&#9;&#9;end
&#9;&#9;&#9;for index,child in pairs(object:GetChildren()) do
&#9;&#9;&#9;&#9;UnlockInstances(child)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;local TerrainColorTable =
&#9;&#9;{
&#9;&#9;&#9;BrickColor.new(&quot;Bright green&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Bright yellow&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Bright red&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Sand red&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Black&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Dark stone grey&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Sand blue&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Deep orange&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Dark orange&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Reddish brown&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Light orange&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Light stone grey&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Sand green&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Medium stone grey&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Really red&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Really blue&quot;),
&#9;&#9;&#9;BrickColor.new(&quot;Bright blue&quot;),
&#9;&#9;}
&#9;&#9;local function getClosestColorToTerrainMaterial(terrainValue)
&#9;&#9;&#9;if TerrainColorTable[terrainValue] then
&#9;&#9;&#9;&#9;return TerrainColorTable[terrainValue]
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;return BrickColor.new(&quot;Bright green&quot;)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function setupFakeTerrainPart(cellMat, cellType, cellOrient)
&#9;&#9;&#9;local newTerrainPiece = nil
&#9;&#9;&#9;if (cellType == 1 or cellType == 4) then newTerrainPiece = Instance.new(&quot;WedgePart&quot;) newTerrainPiece.formFactor = &quot;Custom&quot;
&#9;&#9;&#9;elseif (cellType == 2) then newTerrainPiece = Instance.new(&quot;CornerWedgePart&quot;)
&#9;&#9;&#9;else newTerrainPiece = Instance.new(&quot;Part&quot;) newTerrainPiece.formFactor = &quot;Custom&quot; end
&#9;&#9;&#9;newTerrainPiece.Name = &quot;MegaClusterCube&quot;
&#9;&#9;&#9;newTerrainPiece.Size = Vector3.new(4, 4, 4)
&#9;&#9;&#9;newTerrainPiece.BottomSurface = &quot;Smooth&quot;
&#9;&#9;&#9;newTerrainPiece.TopSurface = &quot;Smooth&quot;
&#9;&#9;&#9;-- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors
&#9;&#9;&#9;newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat)
&#9;&#9;&#9;local sideways = 0
&#9;&#9;&#9;local flipped = math.pi
&#9;&#9;&#9;if cellType == 4 then sideways = -math.pi/2 end
&#9;&#9;&#9;if cellType == 2 or cellType == 3 then flipped = 0 end
&#9;&#9;&#9;newTerrainPiece.CFrame = CFrame.Angles(0, math.pi/2*cellOrient + flipped, sideways)
&#9;&#9;&#9;if cellType == 3 then
&#9;&#9;&#9;&#9;local inverseCornerWedgeMesh = Instance.new(&quot;SpecialMesh&quot;)
&#9;&#9;&#9;&#9;inverseCornerWedgeMesh.MeshType = &quot;FileMesh&quot;
&#9;&#9;&#9;&#9;inverseCornerWedgeMesh.MeshId = &quot;http://www.roblox.com/asset?id=66832495&quot;
&#9;&#9;&#9;&#9;inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2)
&#9;&#9;&#9;&#9;inverseCornerWedgeMesh.Parent = newTerrainPiece
&#9;&#9;&#9;end
&#9;&#9;&#9;local materialTag = Instance.new(&quot;Vector3Value&quot;)
&#9;&#9;&#9;materialTag.Value = Vector3.new(cellMat, cellType, cellOrient)
&#9;&#9;&#9;materialTag.Name = &quot;ClusterMaterial&quot;
&#9;&#9;&#9;materialTag.Parent = newTerrainPiece
&#9;&#9;&#9;return newTerrainPiece
&#9;&#9;end
&#9;&#9;
&#9;&#9;-- This call will cause a &quot;wait&quot; until the data comes back
&#9;&#9;-- below we wait a max of 8 seconds before deciding to bail out on loading
&#9;&#9;local root
&#9;&#9;local loader
&#9;&#9;loading = true
&#9;&#9;if useAssetVersionId then
&#9;&#9;&#9;loader = coroutine.create(function()&#9;
&#9;&#9;&#9;&#9;root = game:GetService(&quot;InsertService&quot;):LoadAssetVersion(assetId)
&#9;&#9;&#9;&#9;loading = false
&#9;&#9;&#9;end)
&#9;&#9;&#9;coroutine.resume(loader)
&#9;&#9;else
&#9;&#9;&#9;loader = coroutine.create(function()&#9;
&#9;&#9;&#9;&#9;root = game:GetService(&quot;InsertService&quot;):LoadAsset(assetId)
&#9;&#9;&#9;&#9;loading = false
&#9;&#9;&#9;end)
&#9;&#9;&#9;coroutine.resume(loader)
&#9;&#9;end
&#9;&#9;local lastGameTime = 0
&#9;&#9;local totalTime = 0
&#9;&#9;local maxWait = 8
&#9;&#9;while loading and totalTime &lt; maxWait do
&#9;&#9;&#9;lastGameTime = tick()
&#9;&#9;&#9;wait(1)
&#9;&#9;&#9;totalTime = totalTime + tick() - lastGameTime
&#9;&#9;end
&#9;&#9;loading = false
&#9;&#9;if totalTime &gt;= maxWait then
&#9;&#9;&#9;return nil, &quot;Load Time Fail&quot;
&#9;&#9;end
&#9;&#9;if root == nil then
&#9;&#9;&#9;return nil, &quot;Load Asset Fail&quot;
&#9;&#9;end
&#9;&#9;if not root:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;return nil, &quot;Load Type Fail&quot;
&#9;&#9;end
&#9;&#9;local instances = root:GetChildren()
&#9;&#9;if #instances == 0 then
&#9;&#9;&#9;return nil, &quot;Empty Model Fail&quot;
&#9;&#9;end
&#9;&#9;--Unlock all parts that are inserted, to make sure they are editable
&#9;&#9;UnlockInstances(root)
&#9;&#9;--Continue the insert process
&#9;&#9;root = root:GetChildren()[1]
&#9;&#9;--Examine the contents and decide what it looks like
&#9;&#9;for pos, instance in pairs(instances) do
&#9;&#9;&#9;if instance:IsA(&quot;Team&quot;) then
&#9;&#9;&#9;&#9;instance.Parent = game:GetService(&quot;Teams&quot;)
&#9;&#9;&#9;elseif instance:IsA(&quot;Sky&quot;) then
&#9;&#9;&#9;&#9;local lightingService = game:GetService(&quot;Lighting&quot;)
&#9;&#9;&#9;&#9;for index,child in pairs(lightingService:GetChildren()) do
&#9;&#9;&#9;&#9;&#9;if child:IsA(&quot;Sky&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;child:Remove();
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;instance.Parent = lightingService
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;-- ...and tag all inserted models for subsequent origin identification
&#9;&#9;-- if no RobloxModel tag already exists, then add it.
&#9;&#9;if root:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;local stringTag = Instance.new(&quot;BoolValue&quot;, root)
&#9;&#9;&#9;stringTag.Name = &quot;RobloxModel&quot;
&#9;&#9;&#9;if root:FindFirstChild(&quot;RobloxStamper&quot;) == nil then
&#9;&#9;&#9;&#9;local stringTag2 = Instance.new(&quot;BoolValue&quot;, root)
&#9;&#9;&#9;&#9;stringTag2.Name = &quot;RobloxStamper&quot;
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;if terrainShape then
&#9;&#9;&#9;if root.Name == &quot;MegaClusterCube&quot; then
&#9;&#9;&#9;&#9;if (terrainShape == 6) then -- insert an autowedging tag
&#9;&#9;&#9;&#9;&#9;local autowedgeTag = Instance.new(&quot;BoolValue&quot;)
&#9;&#9;&#9;&#9;&#9;autowedgeTag.Name = &quot;AutoWedge&quot;
&#9;&#9;&#9;&#9;&#9;autowedgeTag.Parent = root
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;local clusterTag = root:FindFirstChild(&quot;ClusterMaterial&quot;)
&#9;&#9;&#9;&#9;&#9;if clusterTag then
&#9;&#9;&#9;&#9;&#9;&#9;if clusterTag:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z)
&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0)
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;root = setupFakeTerrainPart(1, terrainShape, 0)
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return root
&#9;end
&#9;RBXStamper.SetupStamperDragger = function(modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc)
&#9;&#9;wait(0)
&#9;&#9;if not modelToStamp then
&#9;&#9;&#9;error(&quot;SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model&quot;)
&#9;&#9;&#9;return nil
&#9;&#9;end
&#9;&#9;if not modelToStamp:IsA(&quot;Model&quot;) and not modelToStamp:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;error(&quot;SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!&quot;)
&#9;&#9;&#9;return nil
&#9;&#9;end
&#9;&#9;if not Mouse then
&#9;&#9;&#9;error(&quot;SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object&quot;)
&#9;&#9;&#9;return nil
&#9;&#9;end
&#9;&#9;if not Mouse:IsA(&quot;Mouse&quot;) then
&#9;&#9;&#9;error(&quot;SetupStamperDragger: Mouse (second arg) is not of type Mouse!&quot;)
&#9;&#9;&#9;return nil
&#9;&#9;end
&#9;&#9;
&#9;&#9;local stampInModel = nil
&#9;&#9;local allowedStampRegion = nil
&#9;&#9;local stampFailedFunc = nil
&#9;&#9;if StampInModel then
&#9;&#9;&#9;if not StampInModel:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;error(&quot;SetupStamperDragger: StampInModel (optional third arg) is not of type &apos;Model&apos;&quot;)
&#9;&#9;&#9;&#9;return nil
&#9;&#9;&#9;end
&#9;&#9;&#9;if not AllowedStampRegion then
&#9;&#9;&#9;&#9;error(&quot;SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined&quot;)
&#9;&#9;&#9;&#9;return nil
&#9;&#9;&#9;end
&#9;&#9;&#9;stampFailedFunc = StampFailedFunc
&#9;&#9;&#9;stampInModel = StampInModel
&#9;&#9;&#9;allowedStampRegion = AllowedStampRegion
&#9;&#9;end
&#9;&#9;
&#9;&#9;-- Init all state variables
&#9;&#9;local gInitial90DegreeRotations = 0
&#9;&#9;local stampData = nil
&#9;&#9;local mouseTarget = nil
&#9;&#9;
&#9;&#9;local errorBox = Instance.new(&quot;SelectionBox&quot;)
&#9;&#9;errorBox.Color = BrickColor.new(&quot;Bright red&quot;)
&#9;&#9;errorBox.Transparency = 0
&#9;&#9;errorBox.Archivable = false
&#9;&#9;
&#9;&#9;-- for megacluster MEGA STAMPING
&#9;&#9;local adornPart = Instance.new(&quot;Part&quot;)
&#9;&#9;adornPart.Parent = nil
&#9;&#9;adornPart.formFactor = &quot;Custom&quot;
&#9;&#9;adornPart.Size = Vector3.new(4, 4, 4)
&#9;&#9;adornPart.CFrame = CFrame.new()
&#9;&#9;adornPart.Archivable = false
&#9;&#9;local adorn = Instance.new(&quot;SelectionBox&quot;)
&#9;&#9;adorn.Color = BrickColor.new(&quot;Toothpaste&quot;)
&#9;&#9;adorn.Adornee = adornPart
&#9;&#9;adorn.Visible = true
&#9;&#9;adorn.Transparency = 0
&#9;&#9;adorn.Name = &quot;HighScalabilityStamperLine&quot;
&#9;&#9;adorn.Archivable = false
&#9;&#9;local HighScalabilityLine = {}
&#9;&#9;HighScalabilityLine.Start = nil
&#9;&#9;HighScalabilityLine.End = nil
&#9;&#9;HighScalabilityLine.Adorn = adorn
&#9;&#9;HighScalabilityLine.AdornPart = adornPart
&#9;&#9;HighScalabilityLine.InternalLine = nil
&#9;&#9;HighScalabilityLine.NewHint = true
&#9;&#9;HighScalabilityLine.MorePoints = {nil, nil}
&#9;&#9;HighScalabilityLine.MoreLines = {nil, nil}
&#9;&#9;HighScalabilityLine.Dimensions = 1
&#9;&#9;
&#9;&#9;local control = {}
&#9;&#9;local movingLock = false
&#9;&#9;local stampUpLock = false
&#9;&#9;local unstampableSurface = false
&#9;&#9;local mouseCons = {}
&#9;&#9;local keyCon = nil
&#9;&#9;
&#9;&#9;local stamped = Instance.new(&quot;BoolValue&quot;)
&#9;&#9;stamped.Archivable = false
&#9;&#9;stamped.Value = false
&#9;&#9;
&#9;&#9;local lastTarget = {}
&#9;&#9;lastTarget.TerrainOrientation = 0
&#9;&#9;lastTarget.CFrame = 0
&#9;&#9;
&#9;&#9;local cellInfo = {}
&#9;&#9;cellInfo.Material = 1
&#9;&#9;cellInfo.clusterType = 0
&#9;&#9;cellInfo.clusterOrientation = 0
&#9;&#9;
&#9;&#9;local function isMegaClusterPart()
&#9;&#9;&#9;if not stampData then return false end
&#9;&#9;&#9;if not stampData.CurrentParts then return false end
&#9;&#9;&#9;
&#9;&#9;&#9;return ( stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;,true) or (stampData.CurrentParts.Name == &quot;MegaClusterCube&quot;) )
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function DoHighScalabilityRegionSelect()
&#9;&#9;&#9;local megaCube = stampData.CurrentParts:FindFirstChild(&quot;MegaClusterCube&quot;)
&#9;&#9;&#9;if not megaCube then
&#9;&#9;&#9;&#9;if not stampData.CurrentParts.Name == &quot;MegaClusterCube&quot; then
&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;megaCube = stampData.CurrentParts
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;HighScalabilityLine.End = megaCube.CFrame.p
&#9;&#9;&#9;local line = nil
&#9;&#9;&#9;local line2 = Vector3.new(0, 0, 0)
&#9;&#9;&#9;local line3 = Vector3.new(0, 0, 0)
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;if HighScalabilityLine.Dimensions == 1 then
&#9;&#9;&#9;&#9;-- extract the line from these positions and limit to a 2D plane made from 2 of the world axes
&#9;&#9;&#9;&#9;-- then use dominating axis to limit line to be at 45-degree intervals
&#9;&#9;&#9;&#9;-- will use this internal representation of the line for the actual stamping
&#9;&#9;&#9;&#9;line = (HighScalabilityLine.End - HighScalabilityLine.Start)
&#9;&#9;&#9;&#9;if math.abs(line.X) &lt; math.abs(line.Y) then
&#9;&#9;&#9;&#9;&#9;if math.abs(line.X) &lt; math.abs(line.Z) then
&#9;&#9;&#9;&#9;&#9;&#9;-- limit to Y/Z plane, domination unknown
&#9;&#9;&#9;&#9;&#9;&#9;local newY, newZ
&#9;&#9;&#9;&#9;&#9;&#9;if (math.abs(line.Y) &gt; math.abs(line.Z)) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;newY, newZ = truncateToCircleEighth(line.Y, line.Z)
&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;newZ, newY = truncateToCircleEighth(line.Z, line.Y)
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;line = Vector3.new(0, newY, newZ)
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;-- limit to X/Y plane, with Y dominating
&#9;&#9;&#9;&#9;&#9;&#9;local newY, newX = truncateToCircleEighth(line.Y, line.X)
&#9;&#9;&#9;&#9;&#9;&#9;line = Vector3.new(newX, newY, 0)
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;if math.abs(line.Y) &lt; math.abs(line.Z) then
&#9;&#9;&#9;&#9;&#9;&#9;-- limit to X/Z plane, domination unknown
&#9;&#9;&#9;&#9;&#9;&#9;local newX, newZ
&#9;&#9;&#9;&#9;&#9;&#9;if math.abs(line.X) &gt; math.abs(line.Z) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;newX, newZ = truncateToCircleEighth(line.X, line.Z)
&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;newZ, newX = truncateToCircleEighth(line.Z, line.X)
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;line = Vector3.new(newX, 0, newZ)
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;-- limit to X/Y plane, with X dominating
&#9;&#9;&#9;&#9;&#9;&#9;local newX, newY = truncateToCircleEighth(line.X, line.Y)
&#9;&#9;&#9;&#9;&#9;&#9;line = Vector3.new(newX, newY, 0)
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine = line
&#9;&#9;&#9;elseif HighScalabilityLine.Dimensions == 2 then
&#9;&#9;&#9;&#9;line = HighScalabilityLine.MoreLines[1]
&#9;&#9;&#9;&#9;line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1]
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;-- take out any component of line2 along line1, so you get perpendicular to line1 component
&#9;&#9;&#9;&#9;line2 = line2 - line.unit*line.unit:Dot(line2)
&#9;&#9;&#9;&#9;tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
&#9;&#9;&#9;&#9;-- then zero out whichever is the smaller component
&#9;&#9;&#9;&#9;local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0))
&#9;&#9;&#9;&#9;local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0))
&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;local xComp = xAxis:Dot(line2)
&#9;&#9;&#9;&#9;local yComp = yAxis:Dot(line2)
&#9;&#9;&#9;&#9;if math.abs(yComp) &gt; math.abs(xComp) then
&#9;&#9;&#9;&#9;&#9;line2 = line2 - xAxis * xComp
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;line2 = line2 - yAxis * yComp
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine = line2
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;elseif HighScalabilityLine.Dimensions == 3 then
&#9;&#9;&#9;&#9;line = HighScalabilityLine.MoreLines[1]
&#9;&#9;&#9;&#9;line2 = HighScalabilityLine.MoreLines[2]
&#9;&#9;&#9;&#9;line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2]
&#9;&#9;&#9;&#9;-- zero out all components of previous lines
&#9;&#9;&#9;&#9;line3 = line3 - line.unit * line.unit:Dot(line3)
&#9;&#9;&#9;&#9;line3 = line3 - line2.unit * line2.unit:Dot(line3)
&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine = line3
&#9;&#9;&#9;end
&#9;&#9;&#9;-- resize the &quot;line&quot; graphic to be the correct size and orientation
&#9;&#9;&#9;tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;if HighScalabilityLine.Dimensions == 1 then -- faster calculation for line
&#9;&#9;&#9;&#9;HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4)
&#9;&#9;&#9;&#9;HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size/2)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3)
&#9;&#9;&#9;&#9;HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z))
&#9;&#9;&#9;&#9;HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize/2)
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- make player able to see this ish
&#9;&#9;&#9;local gui = nil
&#9;&#9;&#9;if game.Players[&quot;LocalPlayer&quot;] then
&#9;&#9;&#9;&#9;gui = game.Players.LocalPlayer:FindFirstChild(&quot;PlayerGui&quot;)
&#9;&#9;&#9;&#9;if gui and gui:IsA(&quot;PlayerGui&quot;) then
&#9;&#9;&#9;&#9;&#9;if HighScalabilityLine.Dimensions == 1 and line.magnitude &gt; 3 then -- don&apos;t show if mouse hasn&apos;t moved enough
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = gui
&#9;&#9;&#9;&#9;&#9;elseif HighScalabilityLine.Dimensions &gt; 1 then
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = gui
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;if gui == nil then -- we are in studio
&#9;&#9;&#9;&#9;gui = game:GetService(&quot;CoreGui&quot;)
&#9;&#9;&#9;&#9;if HighScalabilityLine.Dimensions == 1 and line.magnitude &gt; 3 then -- don&apos;t show if mouse hasn&apos;t moved enough
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = gui
&#9;&#9;&#9;&#9;elseif HighScalabilityLine.Dimensions &gt; 1 then
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = gui
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;
&#9;&#9;local function DoStamperMouseMove(Mouse)
&#9;&#9;&#9;if not Mouse then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseMove: Mouse is nil&quot;)
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;if not Mouse:IsA(&quot;Mouse&quot;) then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseMove: Mouse is of type&quot;, Mouse.className,&quot;should be of type Mouse&quot;)
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- There wasn&apos;t a target (no part or terrain), so check for plane intersection.
&#9;&#9;&#9;if not Mouse.Target then
&#9;&#9;&#9;&#9;local cellPos = GetTerrainForMouse(Mouse)
&#9;&#9;&#9;&#9;if nil == cellPos then
&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;if not stampData then
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;-- don&apos;t move with dragger - will move in one step on mouse down
&#9;&#9;&#9;-- draw ghost at acceptable positions
&#9;&#9;&#9;configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData)
&#9;&#9;&#9;if not configFound then
&#9;&#9;&#9;&#9;error(&quot;RbxStamper.DoStamperMouseMove No configFound, returning&quot;)
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end&#9;
&#9;&#9;&#9;local numRotations = 0 -- update this according to how many rotations you need to get it to target surface
&#9;&#9;&#9;if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then -- pre-rotate the flag or portrait so it&apos;s aligned correctly
&#9;&#9;&#9;&#9;if&#9;&#9;targetSurface == 3 then numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
&#9;&#9;&#9;&#9;elseif&#9;targetSurface == 0 then numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
&#9;&#9;&#9;&#9;elseif&#9;targetSurface == 5 then numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
&#9;&#9;&#9;&#9;elseif&#9;targetSurface == 2 then numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end&#9;
&#9;&#9;&#9;local ry = math.pi/2
&#9;&#9;&#9;gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations
&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) or stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;--stampData.CurrentParts:Rotate(0, ry*numRotations, 0)
&#9;&#9;&#9;&#9;modelRotate(stampData.CurrentParts, ry*numRotations)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry*numRotations, 0) * stampData.CurrentParts.CFrame
&#9;&#9;&#9;end
&#9;&#9;&#9;-- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE
&#9;&#9;&#9;local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
&#9;&#9;&#9;-- need to offset by distance to be dragged
&#9;&#9;&#9;local currModelCFrame = nil
&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;currModelCFrame = stampData.CurrentParts:GetModelCFrame()
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;currModelCFrame = stampData.CurrentParts.CFrame
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;minBB = minBB + targetCFrame.p - currModelCFrame.p
&#9;&#9;&#9;maxBB = maxBB + targetCFrame.p - currModelCFrame.p
&#9;&#9;&#9;-- don&apos;t drag into terrain
&#9;&#9;&#9;if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
&#9;&#9;&#9;&#9;if lastTarget.CFrame then
&#9;&#9;&#9;&#9;&#9;if (stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)) then
&#9;&#9;&#9;&#9;&#9;&#9;local theClusterMaterial = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;&#9;&#9;if theClusterMaterial:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;local stampClusterMaterial = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;if stampClusterMaterial then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stampClusterMaterial = clusterMat
&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;-- 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
&#9;&#9;&#9;if isMegaClusterPart() then
&#9;&#9;&#9;&#9;local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p)
&#9;&#9;&#9;&#9;local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z)
&#9;&#9;&#9;&#9;local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame:components()
&#9;&#9;&#9;&#9;targetCFrame = CFrame.new(newCFramePosition.X,newCFramePosition.Y,newCFramePosition.Z,R00, R01, R02, R10, R11, R12, R20, R21, R22)
&#9;&#9;&#9;end
&#9;&#9;&#9;positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
&#9;&#9;&#9;lastTarget.CFrame = targetCFrame -- successful positioning, so update &apos;dat cframe
&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true) then
&#9;&#9;&#9;&#9;local clusterMat = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;if clusterMat:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;lastTarget.TerrainOrientation = clusterMat.Value.Z
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- auto break joints code
&#9;&#9;&#9;if Mouse and Mouse.Target and Mouse.Target.Parent then
&#9;&#9;&#9;&#9;local modelInfo = Mouse.Target:FindFirstChild(&quot;RobloxModel&quot;)
&#9;&#9;&#9;&#9;if not modelInfo then modelInfo = Mouse.Target.Parent:FindFirstChild(&quot;RobloxModel&quot;) end
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;local myModelInfo = stampData.CurrentParts:FindFirstChild(&quot;UnstampableFaces&quot;)
&#9;&#9;&#9;&#9;--if (modelInfo and modelInfo.Parent:FindFirstChild(&quot;UnstampableFaces&quot;)) or (modelInfo and myModelInfo) then -- need better targetSurface calcs
&#9;&#9;&#9;&#9;if (true) then
&#9;&#9;&#9;&#9;&#9;local breakingFaces = &quot;&quot;
&#9;&#9;&#9;&#9;&#9;local myBreakingFaces = &quot;&quot;
&#9;&#9;&#9;&#9;&#9;if modelInfo and modelInfo.Parent:FindFirstChild(&quot;UnstampableFaces&quot;) then breakingFaces = modelInfo.Parent.UnstampableFaces.Value end
&#9;&#9;&#9;&#9;&#9;if myModelInfo then myBreakingFaces = myModelInfo.Value end
&#9;&#9;&#9;&#9;&#9;local hitFace = 0
&#9;&#9;&#9;&#9;&#9;if modelInfo then hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, Mouse.Hit.p) end
&#9;&#9;&#9;&#9;&#9;-- are we stamping TO an unstampable surface?
&#9;&#9;&#9;&#9;&#9;for bf in string.gmatch(breakingFaces, &quot;[^,]+&quot;) do
&#9;&#9;&#9;&#9;&#9;&#9;if hitFace == tonumber(bf) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- return before we hit the JointsService code below!
&#9;&#9;&#9;&#9;&#9;&#9;&#9;unstampableSurface = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
&#9;&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;-- now we have to cast the ray back in the other direction to find the surface we&apos;re stamping FROM
&#9;&#9;&#9;&#9;&#9;hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p)
&#9;&#9;&#9;&#9;&#9;-- are we stamping WITH an unstampable surface?
&#9;&#9;&#9;&#9;&#9;for bf in string.gmatch(myBreakingFaces, &quot;[^,]+&quot;) do
&#9;&#9;&#9;&#9;&#9;&#9;if hitFace == tonumber(bf) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;unstampableSurface = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
&#9;&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;-- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame
&#9;&#9;&#9;&#9;&#9;-- targetSurface: 1 is top, 4 is bottom,
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- to show joints during the mouse move
&#9;&#9;&#9;unstampableSurface = false
&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
&#9;&#9;&#9;-- most common mouse inactive error occurs here, so check mouse active one more time in a pcall
&#9;&#9;&#9;if not pcall(function()
&#9;&#9;&#9;&#9;&#9;if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end)
&#9;&#9;&#9;then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check&quot;)
&#9;&#9;&#9;&#9;game.JointsService:ClearJoinAfterMoveJoints()
&#9;&#9;&#9;&#9;Mouse = nil
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveTarget(Mouse.Target)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveTarget(nil)
&#9;&#9;&#9;end
&#9;&#9;&#9;game.JointsService:ShowPermissibleJoints()
&#9;&#9;&#9;-- here we allow for a line of high-scalability parts
&#9;&#9;&#9;if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then
&#9;&#9;&#9;&#9;&#9;DoHighScalabilityRegionSelect()
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function setupKeyListener(key, Mouse)
&#9;&#9;&#9;if control and control[&quot;Paused&quot;] then return end -- don&apos;t do this if we have no stamp
&#9;&#9;&#9;
&#9;&#9;&#9;key = string.lower(key)
&#9;&#9;&#9;if key == &apos;r&apos; and not autoAlignToFace(stampData.CurrentParts) then -- rotate the model
&#9;&#9;&#9;&#9;gInitial90DegreeRotations = gInitial90DegreeRotations + 1
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;-- Update orientation value if this is a fake terrain part
&#9;&#9;&#9;&#9;local clusterValues = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;if clusterValues and clusterValues:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z + 1) % 4)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;-- Rotate the parts or all the parts in the model
&#9;&#9;&#9;&#9;local ry = math.pi/2
&#9;&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) or stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;&#9;--stampData.CurrentParts:Rotate(0, ry, 0)
&#9;&#9;&#9;&#9;&#9;modelRotate(stampData.CurrentParts, ry)
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;-- After rotating, update the position
&#9;&#9;&#9;&#9;configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
&#9;&#9;&#9;&#9;if configFound then
&#9;&#9;&#9;&#9;&#9;positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;-- update everything else in MouseMove
&#9;&#9;&#9;&#9;&#9;DoStamperMouseMove(Mouse)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;elseif key == &apos;c&apos; then -- try to expand our high scalability dragger dimension
&#9;&#9;&#9;&#9;if HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude &gt; 0 and HighScalabilityLine.Dimensions &lt; 3 then
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.NewHint = true
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;keyCon = Mouse.KeyDown:connect(function(key) -- init key connection (keeping code close to func)
&#9;&#9;&#9;setupKeyListener(key, Mouse)
&#9;&#9;end)
&#9;&#9;
&#9;&#9;local function resetHighScalabilityLine()
&#9;&#9;&#9;if HighScalabilityLine then
&#9;&#9;&#9;&#9;HighScalabilityLine.Start = nil
&#9;&#9;&#9;&#9;HighScalabilityLine.End = nil
&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine = nil
&#9;&#9;&#9;&#9;HighScalabilityLine.NewHint = true
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;local function flashRedBox()
&#9;&#9;&#9;local gui = game.CoreGui
&#9;&#9;&#9;if game:FindFirstChild(&quot;Players&quot;) then
&#9;&#9;&#9;&#9;if game.Players[&quot;LocalPlayer&quot;] then
&#9;&#9;&#9;&#9;&#9;if game.Players.LocalPlayer:FindFirstChild(&quot;PlayerGui&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;gui = game.Players.LocalPlayer.PlayerGui
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;if not stampData[&quot;ErrorBox&quot;] then return end
&#9;&#9;&#9;
&#9;&#9;&#9;stampData.ErrorBox.Parent = gui
&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;stampData.ErrorBox.Adornee = stampData.CurrentParts
&#9;&#9;&#9;end
&#9;&#9;&#9;delay(0,function()
&#9;&#9;&#9;&#9;for i = 1, 3 do
&#9;&#9;&#9;&#9;&#9;if stampData[&quot;ErrorBox&quot;] then stampData.ErrorBox.Visible = true end
&#9;&#9;&#9;&#9;&#9;wait(0.13)
&#9;&#9;&#9;&#9;&#9;if stampData[&quot;ErrorBox&quot;] then stampData.ErrorBox.Visible = false end
&#9;&#9;&#9;&#9;&#9;wait(0.13)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;if stampData[&quot;ErrorBox&quot;] then
&#9;&#9;&#9;&#9;&#9;stampData.ErrorBox.Adornee = nil
&#9;&#9;&#9;&#9;&#9;stampData.ErrorBox.Parent = Tool
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end)
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function DoStamperMouseDown(Mouse)
&#9;&#9;&#9;if not Mouse then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseDown: Mouse is nil&quot;)
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;if not Mouse:IsA(&quot;Mouse&quot;) then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseDown: Mouse is of type&quot;, Mouse.className,&quot;should be of type Mouse&quot;)
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;if not stampData then
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;if isMegaClusterPart() then
&#9;&#9;&#9;&#9;if Mouse and HighScalabilityLine then
&#9;&#9;&#9;&#9;&#9;local megaCube = stampData.CurrentParts:FindFirstChild(&quot;MegaClusterCube&quot;, true)
&#9;&#9;&#9;&#9;&#9;local terrain = game.Workspace.Terrain
&#9;&#9;&#9;&#9;&#9;if megaCube then
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Dimensions = 1
&#9;&#9;&#9;&#9;&#9;&#9;local tempCell = terrain:WorldToCell(megaCube.CFrame.p)
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Dimensions = 1
&#9;&#9;&#9;&#9;&#9;&#9;local tempCell = terrain:WorldToCell(stampData.CurrentParts.CFrame.p)
&#9;&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
&#9;&#9;&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function loadSurfaceTypes(part, surfaces)
&#9;&#9;&#9;part.TopSurface = surfaces[1]
&#9;&#9;&#9;part.BottomSurface = surfaces[2]
&#9;&#9;&#9;part.LeftSurface = surfaces[3]
&#9;&#9;&#9;part.RightSurface = surfaces[4]
&#9;&#9;&#9;part.FrontSurface = surfaces[5]
&#9;&#9;&#9;part.BackSurface = surfaces[6]
&#9;&#9;end
&#9;&#9;local function saveSurfaceTypes(part, myTable)
&#9;&#9;&#9;local tempTable = {}
&#9;&#9;&#9;tempTable[1] = part.TopSurface
&#9;&#9;&#9;tempTable[2] = part.BottomSurface
&#9;&#9;&#9;tempTable[3] = part.LeftSurface
&#9;&#9;&#9;tempTable[4] = part.RightSurface
&#9;&#9;&#9;tempTable[5] = part.FrontSurface
&#9;&#9;&#9;tempTable[6] = part.BackSurface
&#9;&#9;&#9;
&#9;&#9;&#9;myTable[part] = tempTable
&#9;&#9;end
&#9;&#9;local function makeSurfaceUnjoinable(part, surface)
&#9;&#9;&#9;-- TODO: FILL OUT!
&#9;&#9;end
&#9;&#9;local function prepareModel(model)
&#9;&#9;&#9;if not model then return nil end
&#9;&#9;&#9;
&#9;&#9;&#9;local gDesiredTrans = 0.7
&#9;&#9;&#9;local gStaticTrans = 1
&#9;&#9;&#9;local clone = model:Clone()
&#9;&#9;&#9;local scripts = {}
&#9;&#9;&#9;local parts = {}
&#9;&#9;&#9;local decals = {}
&#9;&#9;&#9;
&#9;&#9;&#9;stampData = {}
&#9;&#9;&#9;stampData.DisabledScripts = {}
&#9;&#9;&#9;stampData.TransparencyTable = {}
&#9;&#9;&#9;stampData.MaterialTable = {}
&#9;&#9;&#9;stampData.CanCollideTable = {}
&#9;&#9;&#9;stampData.AnchoredTable = {}
&#9;&#9;&#9;stampData.ArchivableTable = {}
&#9;&#9;&#9;stampData.DecalTransparencyTable = {}
&#9;&#9;&#9;stampData.SurfaceTypeTable = {}
&#9;&#9;&#9;
&#9;&#9;&#9;collectParts(clone, parts, scripts, decals)
&#9;&#9;&#9;if #parts &lt;= 0 then return nil, &quot;no parts found in modelToStamp&quot; end
&#9;&#9;&#9;for index,script in pairs(scripts) do
&#9;&#9;&#9;&#9;if not(script.Disabled) then
&#9;&#9;&#9;&#9;&#9;script.Disabled = true
&#9;&#9;&#9;&#9;&#9;stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;for index, part in pairs(parts) do
&#9;&#9;&#9;&#9;stampData.TransparencyTable[part] = part.Transparency
&#9;&#9;&#9;&#9;part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency
&#9;&#9;&#9;&#9;stampData.MaterialTable[part] = part.Material
&#9;&#9;&#9;&#9;part.Material = Enum.Material.Plastic
&#9;&#9;&#9;&#9;stampData.CanCollideTable[part] = part.CanCollide
&#9;&#9;&#9;&#9;part.CanCollide = false
&#9;&#9;&#9;&#9;stampData.AnchoredTable[part] = part.Anchored
&#9;&#9;&#9;&#9;part.Anchored = true
&#9;&#9;&#9;&#9;stampData.ArchivableTable[part] = part.Archivable
&#9;&#9;&#9;&#9;part.Archivable = false
&#9;&#9;&#9;&#9;saveSurfaceTypes(part, stampData.SurfaceTypeTable)
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;local fadeInDelayTime = 0.5
&#9;&#9;&#9;&#9;local transFadeInTime = 0.5
&#9;&#9;&#9;&#9;delay(0,function()
&#9;&#9;&#9;&#9;&#9;wait(fadeInDelayTime) -- give it some time to be completely transparent
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;local begTime = tick()
&#9;&#9;&#9;&#9;&#9;local currTime = begTime
&#9;&#9;&#9;&#9;&#9;while (currTime - begTime) &lt; transFadeInTime and part and part:IsA(&quot;BasePart&quot;) and part.Transparency &gt; gDesiredTrans do
&#9;&#9;&#9;&#9;&#9;&#9;local newTrans = 1 - (((currTime - begTime)/transFadeInTime) * (gStaticTrans - gDesiredTrans))
&#9;&#9;&#9;&#9;&#9;&#9;if stampData[&quot;TransparencyTable&quot;] and stampData.TransparencyTable[part] then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part]
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;wait(0.03)
&#9;&#9;&#9;&#9;&#9;&#9;currTime = tick()
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;if part and part:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;if stampData[&quot;TransparencyTable&quot;] and stampData.TransparencyTable[part] then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part]
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end)
&#9;&#9;&#9;end
&#9;&#9;&#9;for index, decal in pairs(decals) do
&#9;&#9;&#9;&#9;stampData.DecalTransparencyTable[decal] = decal.Transparency
&#9;&#9;&#9;&#9;decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- disable all seats
&#9;&#9;&#9;setSeatEnabledStatus(clone, true)
&#9;&#9;&#9;setSeatEnabledStatus(clone, false)
&#9;&#9;&#9;stampData.CurrentParts = clone
&#9;&#9;&#9;-- if auto-alignable, we enforce a pre-rotation to the canonical &quot;0-frame&quot;
&#9;&#9;&#9;if autoAlignToFace(clone) then
&#9;&#9;&#9;&#9;stampData.CurrentParts:ResetOrientationToIdentity()
&#9;&#9;&#9;&#9;gInitial90DegreeRotations = 0
&#9;&#9;&#9;else -- pre-rotate if necessary
&#9;&#9;&#9;&#9;local ry = gInitial90DegreeRotations * math.pi/2
&#9;&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) or stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;&#9;--stampData.CurrentParts:Rotate(0, ry, 0)
&#9;&#9;&#9;&#9;&#9;modelRotate(stampData.CurrentParts, ry)
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- since we&apos;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
&#9;&#9;&#9;-- 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
&#9;&#9;&#9;-- issue (fingers crossed) [HotThoth]
&#9;&#9;&#9;local clusterMaterial = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;if clusterMaterial and clusterMaterial:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;clusterMaterial.Value = Vector3.new(clusterMaterial.Value.X, clusterMaterial.Value.Y, (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4)
&#9;&#9;&#9;end
&#9;&#9;&#9;-- After rotating, update the position
&#9;&#9;&#9;local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
&#9;&#9;&#9;if configFound then
&#9;&#9;&#9;&#9;stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- to show joints during the mouse move
&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
&#9;&#9;&#9;
&#9;&#9;&#9;return clone, parts
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
&#9;&#9;&#9;local cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld
&#9;&#9;&#9;local cellCenter = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
&#9;&#9;&#9;local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), stampData.CurrentParts,&#9;100)
&#9;&#9;&#9;local skipThisCell = false
&#9;&#9;&#9;
&#9;&#9;&#9;for b = 1, #cellBlockingParts do
&#9;&#9;&#9;&#9;if isBlocker(cellBlockingParts[b]) then skipThisCell = true break end
&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;if not skipThisCell then
&#9;&#9;&#9;&#9;-- pop players up above any set cells
&#9;&#9;&#9;&#9;local alreadyPushedUp = {}
&#9;&#9;&#9;&#9;-- if no blocking model below, then see if stamping on top of a character
&#9;&#9;&#9;&#9;for b = 1, #cellBlockingParts do
&#9;&#9;&#9;&#9;&#9;if&#9;cellBlockingParts[b].Parent and
&#9;&#9;&#9;&#9;&#9;&#9;not alreadyPushedUp[cellBlockingParts[b].Parent] and
&#9;&#9;&#9;&#9;&#9;&#9;cellBlockingParts[b].Parent:FindFirstChild(&quot;Humanoid&quot;) and
&#9;&#9;&#9;&#9;&#9;&#9;cellBlockingParts[b].Parent:FindFirstChild(&quot;Humanoid&quot;):IsA(&quot;Humanoid&quot;) then
&#9;&#9;&#9;&#9;&#9;-----------------------------------------------------------------------------------
&#9;&#9;&#9;&#9;&#9;&#9;&#9;local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild(&quot;Torso&quot;)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;alreadyPushedUp[cellBlockingParts[b].Parent] = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;if blockingPersonTorso then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- if so, let&apos;s push the person upwards so they pop on top of the stamped model/part (but only if there&apos;s space above them)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local newY = cellCenter.Y + 5
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- if no space, we just skip this one
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;skipThisCell = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;break
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;-----------------------------------------------------------------------------------
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;if not skipThisCell then -- if we STILL aren&apos;t skipping... then we&apos;re good to go!
&#9;&#9;&#9;&#9;local canSetCell = true
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;if checkHighScalabilityStamp then -- check to see if cell is in region, if not we&apos;ll skip set
&#9;&#9;&#9;&#9;&#9;if allowedStampRegion then
&#9;&#9;&#9;&#9;&#9;&#9;local cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
&#9;&#9;&#9;&#9;&#9;&#9;if cellPos.X + 2 &gt; allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;elseif cellPos.X - 2 &lt; allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;elseif cellPos.Y + 2 &gt; allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;elseif cellPos.Y - 2 &lt; allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;elseif cellPos.Z + 2 &gt; allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;elseif cellPos.Z - 2 &lt; allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z/2 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;canSetCell = false
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;return canSetCell
&#9;&#9;&#9;end
&#9;&#9;&#9;return false
&#9;&#9;end
&#9;&#9;local function ResolveMegaClusterStamp(checkHighScalabilityStamp)
&#9;&#9;&#9;local cellSet = false
&#9;&#9;&#9;local cluser = game.Workspace.Terrain
&#9;&#9;&#9;
&#9;&#9;&#9;local line = HighScalabilityLine.InternalLine
&#9;&#9;&#9;local cMax = game.Workspace.Terrain.MaxExtents.Max
&#9;&#9;&#9;local cMin = game.Workspace.Terrain.MaxExtents.Min
&#9;&#9;&#9;local clusterMaterial = 1 -- default is grass
&#9;&#9;&#9;local clusterType = 0 -- default is brick
&#9;&#9;&#9;local clusterOrientation = 0 -- default is 0 rotation
&#9;&#9;&#9;local autoWedgeClusterParts = false
&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;AutoWedge&quot;) then autoWedgeClusterParts = true end
&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true) then
&#9;&#9;&#9;&#9;clusterMaterial = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;if clusterMaterial:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;clusterType = clusterMaterial.Value.Y
&#9;&#9;&#9;&#9;&#9;clusterOrientation = clusterMaterial.Value.Z
&#9;&#9;&#9;&#9;&#9;clusterMaterial = clusterMaterial.Value.X
&#9;&#9;&#9;&#9;elseif clusterMaterial:IsA(&quot;IntValue&quot;) then
&#9;&#9;&#9;&#9;&#9;clusterMaterial = clusterMaterial.Value
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions &gt; 1) or (line and line.magnitude &gt; 0)) then
&#9;&#9;&#9;&#9;local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start)
&#9;&#9;&#9;&#9;local xInc = {0,0,0}
&#9;&#9;&#9;&#9;local yInc = {0,0,0}
&#9;&#9;&#9;&#9;local zInc = {0,0,0}
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;local cluster = game.Workspace.Terrain
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;local incrementVect = {nil, nil, nil}
&#9;&#9;&#9;&#9;local stepVect = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 0), Vector3.new(0, 0, 0)}
&#9;&#9;&#9;&#9;local worldAxes = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
&#9;&#9;&#9;&#9;local lines = {}
&#9;&#9;&#9;&#9;if HighScalabilityLine.Dimensions &gt; 1 then table.insert(lines, HighScalabilityLine.MoreLines[1]) end
&#9;&#9;&#9;&#9;if line and line.magnitude &gt; 0 then table.insert(lines, line) end
&#9;&#9;&#9;&#9;if HighScalabilityLine.Dimensions &gt; 2 then table.insert(lines, HighScalabilityLine.MoreLines[2]) end
&#9;&#9;&#9;&#9;for i = 1, #lines do
&#9;&#9;&#9;&#9;&#9;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
&#9;&#9;&#9;&#9;&#9;if lines[i].X &gt; 0 then xInc[i] = 1 elseif lines[i].X &lt; 0 then xInc[i] = -1 end
&#9;&#9;&#9;&#9;&#9;if lines[i].Y &gt; 0 then yInc[i] = 1 elseif lines[i].Y &lt; 0 then yInc[i] = -1 end
&#9;&#9;&#9;&#9;&#9;if lines[i].Z &gt; 0 then zInc[i] = 1 elseif lines[i].Z &lt; 0 then zInc[i] = -1 end
&#9;&#9;&#9;&#9;&#9;incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i])
&#9;&#9;&#9;&#9;&#9;if incrementVect[i].magnitude &lt; .9 then incrementVect[i] = nil end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;if not lines[2] then lines[2] = Vector3.new(0, 0, 0) end
&#9;&#9;&#9;&#9;if not lines[3] then lines[3] = Vector3.new(0, 0, 0) end
&#9;&#9;&#9;&#9;local waterForceTag = stampData.CurrentParts:FindFirstChild(&quot;WaterForceTag&quot;, true)
&#9;&#9;&#9;&#9;local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild(&quot;WaterForceDirectionTag&quot;, true)
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;while (stepVect[3].magnitude*4 &lt;= lines[3].magnitude) do
&#9;&#9;&#9;&#9;&#9;local outerStepVectIndex = 1
&#9;&#9;&#9;&#9;&#9;while outerStepVectIndex &lt; 4 do
&#9;&#9;&#9;&#9;&#9;&#9;stepVect[2] = Vector3.new(0, 0, 0)
&#9;&#9;&#9;&#9;&#9;&#9;while (stepVect[2].magnitude*4 &lt;= lines[2].magnitude) do
&#9;&#9;&#9;&#9;&#9;&#9;&#9;local innerStepVectIndex = 1
&#9;&#9;&#9;&#9;&#9;&#9;&#9;while innerStepVectIndex &lt; 4 do
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[1] = Vector3.new(0, 0, 0)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;while (stepVect[1].magnitude*4 &lt;= lines[1].magnitude) do
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3]
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if cellPos.X &gt;= cMin.X and cellPos.Y &gt;= cMin.Y and cellPos.Z &gt;= cMin.Z and cellPos.X &lt; cMax.X and cellPos.Y &lt; cMax.Y and cellPos.Z &lt; cMax.Z then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- check if overlaps player or part
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local okToStampTerrainBlock = checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if okToStampTerrainBlock then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if waterForceTag then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;cellSet = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- auto-wedge it?
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if (autoWedgeClusterParts) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[1] = stepVect[1] + incrementVect[1]
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if incrementVect[2] then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;while innerStepVectIndex &lt; 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;innerStepVectIndex = innerStepVectIndex + 1
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if innerStepVectIndex &lt; 4 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex] * worldAxes[innerStepVectIndex]:Dot(incrementVect[2])
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;innerStepVectIndex = innerStepVectIndex + 1
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[2] = Vector3.new(1, 0, 0)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;innerStepVectIndex = 4 -- skip all remaining loops
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if (stepVect[2].magnitude*4 &gt; lines[2].magnitude) then innerStepVectIndex = 4 end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;if incrementVect[3] then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;while outerStepVectIndex &lt; 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;outerStepVectIndex = outerStepVectIndex + 1
&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;if outerStepVectIndex &lt; 4 then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex] * worldAxes[outerStepVectIndex]:Dot(incrementVect[3])
&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;outerStepVectIndex = outerStepVectIndex + 1
&#9;&#9;&#9;&#9;&#9;&#9;else -- skip all remaining loops
&#9;&#9;&#9;&#9;&#9;&#9;&#9;stepVect[3] = Vector3.new(1, 0, 0) outerStepVectIndex = 4
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;if (stepVect[3].magnitude*4 &gt; lines[3].magnitude) then outerStepVectIndex = 4 end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- and also get rid of any HighScalabilityLine stuff if it&apos;s there
&#9;&#9;&#9;HighScalabilityLine.Start = nil
&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = nil
&#9;&#9;&#9;
&#9;&#9;&#9;-- Mark for undo.
&#9;&#9;&#9;if cellSet then
&#9;&#9;&#9;&#9;stampData.CurrentParts.Parent = nil
&#9;&#9;&#9;&#9;pcall(function() game:GetService(&quot;ChangeHistoryService&quot;): SetWaypoint(&quot;StamperMulti&quot;) end)
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;return cellSet
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function DoStamperMouseUp(Mouse)
&#9;&#9;&#9;if not Mouse then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseUp: Mouse is nil&quot;)
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;if not Mouse:IsA(&quot;Mouse&quot;) then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseUp: Mouse is of type&quot;, Mouse.className,&quot;should be of type Mouse&quot;)
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;if not stampData.Dragger then
&#9;&#9;&#9;&#9;error(&quot;Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil&quot;)
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;if not HighScalabilityLine then
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;local checkHighScalabilityStamp = nil
&#9;&#9;&#9;if stampInModel then
&#9;&#9;&#9;&#9;local canStamp = nil
&#9;&#9;&#9;&#9;local isHSLPart = isMegaClusterPart()
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;if isHSLPart and
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine and
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.Start and
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine and
&#9;&#9;&#9;&#9;&#9;HighScalabilityLine.InternalLine.magnitude &gt; 0 then -- we have an HSL line, test later
&#9;&#9;&#9;&#9;&#9;&#9;canStamp = true
&#9;&#9;&#9;&#9;&#9;&#9;checkHighScalabilityStamp = true
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;canStamp, checkHighScalabilityStamp = RBXStamper.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;if not canStamp then
&#9;&#9;&#9;&#9;&#9;if stampFailedFunc then
&#9;&#9;&#9;&#9;&#9;&#9;stampFailedFunc()
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- if unstampable face, then don&apos;t let us stamp there!
&#9;&#9;&#9;if unstampableSurface then
&#9;&#9;&#9;&#9;flashRedBox()
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- recheck if we can stamp, as we just moved part
&#9;&#9;&#9;canStamp, checkHighScalabilityStamp = RBXStamper.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
&#9;&#9;&#9;if not canStamp then
&#9;&#9;&#9;&#9;if stampFailedFunc then
&#9;&#9;&#9;&#9;&#9;stampFailedFunc()
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;-- Prevent part from being stamped on top of a player
&#9;&#9;&#9;local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
&#9;&#9;&#9;-- HotThoth&apos;s note: Now that above CurrentParts positioning has been commented out, to be truly correct, we would need to use the
&#9;&#9;&#9;-- value of configFound from the previous onStamperMouseMove call which moved the CurrentParts
&#9;&#9;&#9;-- Shouldn&apos;t this be true when lastTargetCFrame has been set and false otherwise?
&#9;&#9;&#9;configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
&#9;&#9;&#9;if configFound and not HighScalabilityLine.Adorn.Parent then
&#9;&#9;&#9;&#9;if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
&#9;&#9;&#9;&#9;&#9;flashRedBox()
&#9;&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB + insertBoundingBoxOverlapVector,
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;maxBB - insertBoundingBoxOverlapVector),
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts,
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;100)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;for b = 1, #blockingParts do
&#9;&#9;&#9;&#9;&#9;if isBlocker(blockingParts[b]) then
&#9;&#9;&#9;&#9;&#9;&#9;flashRedBox()
&#9;&#9;&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;local alreadyPushedUp = {}
&#9;&#9;&#9;&#9;&#9;-- if no blocking model below, then see if stamping on top of a character
&#9;&#9;&#9;&#9;&#9;for b = 1, #blockingParts do
&#9;&#9;&#9;&#9;&#9;&#9;if&#9;blockingParts[b].Parent and
&#9;&#9;&#9;&#9;&#9;&#9;&#9;not alreadyPushedUp[blockingParts[b].Parent] and
&#9;&#9;&#9;&#9;&#9;&#9;&#9;blockingParts[b].Parent:FindFirstChild(&quot;Humanoid&quot;) and
&#9;&#9;&#9;&#9;&#9;&#9;&#9;blockingParts[b].Parent:FindFirstChild(&quot;Humanoid&quot;):IsA(&quot;Humanoid&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;---------------------------------------------------------------------------
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild(&quot;Torso&quot;)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;alreadyPushedUp[blockingParts[b].Parent] = true
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if blockingPersonTorso then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- if so, let&apos;s push the person upwards so they pop on top of the stamped model/part (but only if there&apos;s space above them)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;local newY = maxBB.Y + 3
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;-- if no space, we just error
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;flashRedBox()
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;---------------------------------------------------------------------------
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then -- if no config then only stamp if it&apos;s a real HSL!
&#9;&#9;&#9;&#9;resetHighScalabilityLine()
&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;end
&#9;&#9;&#9;-- something will be stamped! so set the &quot;StampedSomething&quot; toggle to true
&#9;&#9;&#9;if game:FindFirstChild(&quot;Players&quot;) then
&#9;&#9;&#9;&#9;if game.Players[&quot;LocalPlayer&quot;] then
&#9;&#9;&#9;&#9;&#9;if game.Players.LocalPlayer[&quot;Character&quot;] then
&#9;&#9;&#9;&#9;&#9;&#9;local localChar = game.Players.LocalPlayer.Character
&#9;&#9;&#9;&#9;&#9;&#9;local stampTracker = localChar:FindFirstChild(&quot;StampTracker&quot;)
&#9;&#9;&#9;&#9;&#9;&#9;if stampTracker and not stampTracker.Value then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;stampTracker.Value = true
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- if we drew a line of mega parts, stamp them out
&#9;&#9;&#9;if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then
&#9;&#9;&#9;&#9;if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then
&#9;&#9;&#9;&#9;&#9;-- kill the ghost part
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts.Parent = nil
&#9;&#9;&#9;&#9;&#9;return true
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff]
&#9;&#9;&#9;HighScalabilityLine.Start = nil
&#9;&#9;&#9;HighScalabilityLine.Adorn.Parent = nil
&#9;&#9;&#9;
&#9;&#9;&#9;local cluster = game.Workspace.Terrain
&#9;&#9;&#9;-- if target point is in cluster, just use cluster:SetCell
&#9;&#9;&#9;if isMegaClusterPart() then
&#9;&#9;&#9;&#9;-- if targetCFrame is inside cluster, just set that cell to 1 and return
&#9;&#9;&#9;&#9;--local cellPos = cluster:WorldToCell(targetCFrame.p)
&#9;&#9;&#9;&#9;local cellPos
&#9;&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) then cellPos = cluster:WorldToCell(stampData.CurrentParts:GetModelCFrame().p)
&#9;&#9;&#9;&#9;else cellPos = cluster:WorldToCell(stampData.CurrentParts.CFrame.p) end
&#9;&#9;&#9;&#9;local cMax = game.Workspace.Terrain.MaxExtents.Max
&#9;&#9;&#9;&#9;local cMin = game.Workspace.Terrain.MaxExtents.Min
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;if checkTerrainBlockCollisions(cellPos, false) then
&#9;&#9;&#9;&#9;&#9;local clusterValues = stampData.CurrentParts:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;&#9;local waterForceTag = stampData.CurrentParts:FindFirstChild(&quot;WaterForceTag&quot;, true)
&#9;&#9;&#9;&#9;&#9;local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild(&quot;WaterForceDirectionTag&quot;, true)
&#9;&#9;&#9;&#9;&#9;if cellPos.X &gt;= cMin.X and cellPos.Y &gt;= cMin.Y and cellPos.Z &gt;= cMin.Z and cellPos.X &lt; cMax.X and cellPos.Y &lt; cMax.Y and cellPos.Z &lt; cMax.Z then
&#9;&#9;&#9;&#9;&#9;&#9;if waterForceTag then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
&#9;&#9;&#9;&#9;&#9;&#9;elseif not clusterValues then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, cellInfo.Material, cellInfo.clusterType, gInitial90DegreeRotations % 4)
&#9;&#9;&#9;&#9;&#9;&#9;elseif clusterValues:IsA(&quot;Vector3Value&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value.X, clusterValues.Value.Y, clusterValues.Value.Z)
&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0)
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;local autoWedgeClusterParts = false
&#9;&#9;&#9;&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;AutoWedge&quot;) then autoWedgeClusterParts = true end
&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;-- auto-wedge it
&#9;&#9;&#9;&#9;&#9;&#9;if (autoWedgeClusterParts) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;game.Workspace.Terrain:AutowedgeCells(
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Region3int16.new(
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;)
&#9;&#9;&#9;&#9;&#9;&#9;&#9;)
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;-- kill the ghost part
&#9;&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts.Parent = nil
&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;-- Mark for undo. It has to happen here or the selection display will come back also.
&#9;&#9;&#9;&#9;&#9;&#9;pcall(function() game:GetService(&quot;ChangeHistoryService&quot;):SetWaypoint(&quot;StamperSingle&quot;) end)&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;&#9;&#9;return true
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;-- you tried to stamp a HSL-single part where one does not belong!
&#9;&#9;&#9;&#9;&#9;flashRedBox()
&#9;&#9;&#9;&#9;&#9;return false
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;-- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints
&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) or stampData.CurrentParts:IsA(&quot;Tool&quot;) then
&#9;&#9;&#9;&#9;if stampData.CurrentParts:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;&#9;-- Tyler&apos;s magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don&apos;t ask X&lt;
&#9;&#9;&#9;&#9;&#9;local manualWeldTable = {}
&#9;&#9;&#9;&#9;&#9;local manualWeldParentTable = {}
&#9;&#9;&#9;&#9;&#9;saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable)
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts:BreakJoints()
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts:MakeJoints()
&#9;&#9;&#9;&#9;&#9;restoreTheWelds(manualWeldTable, manualWeldParentTable)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;&#9;-- if it&apos;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)
&#9;&#9;&#9;&#9;if game:FindFirstChild(&quot;Players&quot;) and game.Players.LocalPlayer then
&#9;&#9;&#9;&#9;&#9;playerIdTag = stampData.CurrentParts:FindFirstChild(&quot;PlayerIdTag&quot;)
&#9;&#9;&#9;&#9;&#9;playerNameTag = stampData.CurrentParts:FindFirstChild(&quot;PlayerNameTag&quot;)
&#9;&#9;&#9;&#9;&#9;if playerIdTag ~= nil then
&#9;&#9;&#9;&#9;&#9;&#9;playerIdTag.Value = game.Players.LocalPlayer.userId
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;if playerNameTag ~= nil then
&#9;&#9;&#9;&#9;&#9;&#9;playerNameTag.Value = game.Players.LocalPlayer.Name
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;-- ...and tag all inserted models for subsequent origin identification
&#9;&#9;&#9;&#9;-- if no RobloxModel tag already exists, then add it.
&#9;&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;&#9;&#9;local stringTag = Instance.new(&quot;BoolValue&quot;, stampData.CurrentParts)
&#9;&#9;&#9;&#9;&#9;stringTag.Name = &quot;RobloxModel&quot;
&#9;&#9;&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;RobloxStamper&quot;) == nil then
&#9;&#9;&#9;&#9;&#9;&#9;local stringTag2 = Instance.new(&quot;BoolValue&quot;, stampData.CurrentParts)
&#9;&#9;&#9;&#9;&#9;&#9;stringTag2.Name = &quot;RobloxStamper&quot;
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;stampData.CurrentParts:BreakJoints()
&#9;&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;RobloxStamper&quot;) == nil then
&#9;&#9;&#9;&#9;&#9;local stringTag2 = Instance.new(&quot;BoolValue&quot;, stampData.CurrentParts)
&#9;&#9;&#9;&#9;&#9;stringTag2.Name = &quot;RobloxStamper&quot;
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;
&#9;&#9;&#9;-- make sure all the joints are activated before restoring anchor states
&#9;&#9;&#9;if not createJoints then game.JointsService:CreateJoinAfterMoveJoints() end
&#9;&#9;&#9;-- Restore the original properties for all parts being stamped
&#9;&#9;&#9;for part, transparency in pairs(stampData.TransparencyTable) do
&#9;&#9;&#9;&#9;part.Transparency = transparency
&#9;&#9;&#9;end
&#9;&#9;&#9;for part, archivable in pairs(stampData.ArchivableTable) do
&#9;&#9;&#9;&#9;part.Archivable = archivable
&#9;&#9;&#9;end
&#9;&#9;&#9;for part, material in pairs(stampData.MaterialTable) do
&#9;&#9;&#9;&#9;part.Material = material
&#9;&#9;&#9;end
&#9;&#9;&#9;for part, collide in pairs(stampData.CanCollideTable) do
&#9;&#9;&#9;&#9;part.CanCollide = collide
&#9;&#9;&#9;end
&#9;&#9;&#9;for part, anchored in pairs(stampData.AnchoredTable) do
&#9;&#9;&#9;&#9;part.Anchored = anchored
&#9;&#9;&#9;end
&#9;&#9;&#9;for decal, transparency in pairs(stampData.DecalTransparencyTable) do
&#9;&#9;&#9;&#9;decal.Transparency = transparency
&#9;&#9;&#9;end
&#9;&#9;&#9;for part, surfaces in pairs(stampData.SurfaceTypeTable) do
&#9;&#9;&#9;&#9;loadSurfaceTypes(part, surfaces)
&#9;&#9;&#9;end
&#9;&#9;&#9;if isMegaClusterPart() then
&#9;&#9;&#9;&#9;stampData.CurrentParts.Transparency = 0
&#9;&#9;&#9;end
&#9;&#9;&#9;-- re-enable all seats
&#9;&#9;&#9;setSeatEnabledStatus(stampData.CurrentParts, true)
&#9;&#9;&#9;stampData.TransparencyTable = nil
&#9;&#9;&#9;stampData.ArchivableTable = nil
&#9;&#9;&#9;stampData.MaterialTable = nil
&#9;&#9;&#9;stampData.CanCollideTable = nil
&#9;&#9;&#9;stampData.AnchoredTable = nil
&#9;&#9;&#9;stampData.SurfaceTypeTable = nil
&#9;&#9;&#9;-- ...and tag all inserted models for subsequent origin identification
&#9;&#9;&#9;-- if no RobloxModel tag already exists, then add it.
&#9;&#9;&#9;if stampData.CurrentParts:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;&#9;local stringTag = Instance.new(&quot;BoolValue&quot;, stampData.CurrentParts)
&#9;&#9;&#9;&#9;stringTag.Name = &quot;RobloxModel&quot;
&#9;&#9;&#9;end
&#9;&#9;&#9;-- and make sure we don&apos;t delete it, now that it&apos;s not a ghost part
&#9;&#9;&#9;if ghostRemovalScript then ghostRemovalScript.Parent = nil end
&#9;&#9;&#9;--Re-enable the scripts
&#9;&#9;&#9;for index,script in pairs(stampData.DisabledScripts) do
&#9;&#9;&#9;&#9;script.Disabled = false
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;--Now that they are all marked enabled, reinsert them into the world so they start running
&#9;&#9;&#9;for index,script in pairs(stampData.DisabledScripts) do
&#9;&#9;&#9;&#9;local oldParent = script.Parent
&#9;&#9;&#9;&#9;script.Parent = nil
&#9;&#9;&#9;&#9;script:Clone().Parent = oldParent
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;-- clear out more data
&#9;&#9;&#9;stampData.DisabledScripts = nil
&#9;&#9;&#9;stampData.Dragger = nil
&#9;&#9;&#9;stampData.CurrentParts = nil
&#9;&#9;
&#9;&#9;&#9;pcall(function() game:GetService(&quot;ChangeHistoryService&quot;): SetWaypoint(&quot;StampedObject&quot;) end)
&#9;&#9;&#9;return true
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function pauseStamper()
&#9;&#9;&#9;for i = 1, #mouseCons do -- stop the mouse from doing anything
&#9;&#9;&#9;&#9;mouseCons[i]:disconnect()
&#9;&#9;&#9;&#9;mouseCons[i] = nil
&#9;&#9;&#9;end
&#9;&#9;&#9;mouseCons = {}
&#9;&#9;&#9;
&#9;&#9;&#9;if stampData and stampData.CurrentParts then -- remove our ghost part
&#9;&#9;&#9;&#9;stampData.CurrentParts.Parent = nil
&#9;&#9;&#9;&#9;stampData.CurrentParts:Remove()
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;resetHighScalabilityLine()
&#9;&#9;&#9;
&#9;&#9;&#9;game.JointsService:ClearJoinAfterMoveJoints()
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function prepareUnjoinableSurfaces(modelCFrame, parts, whichSurface)
&#9;&#9;&#9;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!
&#9;&#9;&#9;local isPositive = 1
&#9;&#9;&#9;if whichSurface &lt; 0 then isPositive = isPositive * -1 whichSurface = whichSurface*-1 end
&#9;&#9;&#9;local surfaceNormal = isPositive * modelCFrame:vectorToWorldSpace(AXIS_VECTORS[whichSurface])
&#9;&#9;&#9;for i = 1, #parts do
&#9;&#9;&#9;&#9;local currPart = parts[i]
&#9;&#9;&#9;&#9;-- now just need to find which surface of currPart most closely match surfaceNormal and then set that to Unjoinable
&#9;&#9;&#9;&#9;local surfaceNormalInLocalCoords = currPart.CFrame:vectorToObjectSpace(surfaceNormal)
&#9;&#9;&#9;&#9;if math.abs(surfaceNormalInLocalCoords.X) &gt; math.abs(surfaceNormalInLocalCoords.Y) then
&#9;&#9;&#9;&#9;&#9;if math.abs(surfaceNormalInLocalCoords.X) &gt; math.abs(surfaceNormalInLocalCoords.Z) then
&#9;&#9;&#9;&#9;&#9;&#9;if surfaceNormalInLocalCoords.X &gt; 0 then currPart.RightSurface = &quot;Unjoinable&quot; else currPart.LeftSurface = &quot;Unjoinable&quot; end
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;if surfaceNormalInLocalCoords.Z &gt; 0 then currPart.BackSurface = &quot;Unjoinable&quot; else currPart.FrontSurface = &quot;Unjoinable&quot; end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;if math.abs(surfaceNormalInLocalCoords.Y) &gt; math.abs(surfaceNormalInLocalCoords.Z) then
&#9;&#9;&#9;&#9;&#9;&#9;if surfaceNormalInLocalCoords.Y &gt; 0 then currPart.TopSurface = &quot;Unjoinable&quot; else currPart.BottomSurface = &quot;Unjoinable&quot; end
&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;if surfaceNormalInLocalCoords.Z &gt; 0 then currPart.BackSurface = &quot;Unjoinable&quot; else currPart.FrontSurface = &quot;Unjoinable&quot; end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;local function resumeStamper()
&#9;&#9;&#9;
&#9;&#9;&#9;clone, parts = prepareModel(modelToStamp)
&#9;&#9;&#9;if not clone or not parts then
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;&#9;-- if we have unjoinable faces, then we want to change those surfaces to be Unjoinable
&#9;&#9;&#9;local unjoinableTag = clone:FindFirstChild(&quot;UnjoinableFaces&quot;, true)
&#9;&#9;&#9;if unjoinableTag then
&#9;&#9;&#9;&#9;for unjoinableSurface in string.gmatch(unjoinableTag.Value, &quot;[^,]*&quot;) do
&#9;&#9;&#9;&#9;&#9;if tonumber(unjoinableSurface) then
&#9;&#9;&#9;&#9;&#9;&#9;if clone:IsA(&quot;Model&quot;) then
&#9;&#9;&#9;&#9;&#9;&#9;&#9;prepareUnjoinableSurfaces(clone:GetModelCFrame(), parts, tonumber(unjoinableSurface))
&#9;&#9;&#9;&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;&#9;&#9;&#9;prepareUnjoinableSurfaces(clone.CFrame, parts, tonumber(unjoinableSurface))
&#9;&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;stampData.ErrorBox = errorBox
&#9;&#9;&#9;if stampInModel then
&#9;&#9;&#9;&#9;clone.Parent = stampInModel
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;clone.Parent = game.Workspace
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;if clone:FindFirstChild(&quot;ClusterMaterial&quot;, true) then -- extract all info from vector
&#9;&#9;&#9;&#9;clusterMaterial = clone:FindFirstChild(&quot;ClusterMaterial&quot;, true)
&#9;&#9;&#9;&#9;if (clusterMaterial:IsA(&quot;Vector3Value&quot;)) then
&#9;&#9;&#9;&#9;&#9;cellInfo.Material = clusterMaterial.Value.X
&#9;&#9;&#9;&#9;&#9;cellInfo.clusterType = clusterMaterial.Value.Y
&#9;&#9;&#9;&#9;&#9;cellInfo.clusterOrientation = clusterMaterial.Value.Z
&#9;&#9;&#9;&#9;elseif clusterMaterial:IsA(&quot;IntValue&quot;) then
&#9;&#9;&#9;&#9;&#9;cellInfo.Material = clusterMaterial.Value
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;pcall(function() mouseTarget = Mouse.Target end)
&#9;&#9;&#9;if mouseTarget and mouseTarget.Parent:FindFirstChild(&quot;RobloxModel&quot;) == nil then
&#9;&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveTarget(mouseTarget)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;game.JointsService:SetJoinAfterMoveTarget(nil)
&#9;&#9;&#9;end
&#9;&#9;&#9;game.JointsService:ShowPermissibleJoints()
&#9;&#9;&#9;
&#9;&#9;&#9;for index, object in pairs(stampData.DisabledScripts) do
&#9;&#9;&#9;&#9;if object.Name == &quot;GhostRemovalScript&quot; then
&#9;&#9;&#9;&#9;&#9;object.Parent = stampData.CurrentParts
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;stampData.Dragger = Instance.new(&quot;Dragger&quot;)
&#9;&#9;&#9;
&#9;&#9;&#9;--Begin a movement by faking a MouseDown signal
&#9;&#9;&#9;stampData.Dragger:MouseDown(parts[1], Vector3.new(0,0,0), parts)
&#9;&#9;&#9;stampData.Dragger:MouseUp()
&#9;&#9;&#9;
&#9;&#9;&#9;DoStamperMouseMove(Mouse)
&#9;&#9;&#9;
&#9;&#9;&#9;table.insert(mouseCons,Mouse.Move:connect(function()
&#9;&#9;&#9;&#9;if movingLock or stampUpLock then return end
&#9;&#9;&#9;&#9;movingLock = true
&#9;&#9;&#9;&#9;&#9;DoStamperMouseMove(Mouse)
&#9;&#9;&#9;&#9;movingLock = false
&#9;&#9;&#9;end))
&#9;&#9;&#9;
&#9;&#9;&#9;table.insert(mouseCons,Mouse.Button1Down:connect(function()
&#9;&#9;&#9;&#9;DoStamperMouseDown(Mouse)
&#9;&#9;&#9;end))
&#9;&#9;&#9;
&#9;&#9;&#9;table.insert(mouseCons,Mouse.Button1Up:connect(function()
&#9;&#9;&#9;&#9;stampUpLock = true
&#9;&#9;&#9;&#9;&#9;while movingLock do wait() end
&#9;&#9;&#9;&#9;&#9;--added zach stuff
&#9;&#9;&#9;&#9;&#9;wait(0)
&#9;&#9;&#9;&#9;&#9;DoStamperMouseMove(Mouse)
&#9;&#9;&#9;&#9;&#9;--end zach stuff
&#9;&#9;&#9;&#9;&#9;stamped.Value = DoStamperMouseUp(Mouse)
&#9;&#9;&#9;&#9;&#9;resetHighScalabilityLine()
&#9;&#9;&#9;&#9;stampUpLock = false
&#9;&#9;&#9;end))
&#9;&#9;&#9;
&#9;&#9;&#9;stamped.Value = false
&#9;&#9;end
&#9;&#9;
&#9;&#9;local function resetStamperState(newModelToStamp)
&#9;&#9;
&#9;&#9;&#9;-- if we have a new model, swap it out
&#9;&#9;&#9;if newModelToStamp then
&#9;&#9;&#9;&#9;if not newModelToStamp:IsA(&quot;Model&quot;) and not newModelToStamp:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;&#9;&#9;error(&quot;resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!&quot;)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;modelToStamp = newModelToStamp
&#9;&#9;&#9;end
&#9;&#9;
&#9;&#9;&#9;-- first clear our state
&#9;&#9;&#9;pauseStamper()
&#9;&#9;&#9;-- now lets load in the new model
&#9;&#9;&#9;resumeStamper()
&#9;&#9;&#9;
&#9;&#9;end
&#9;&#9;
&#9;&#9;-- load the model initially
&#9;&#9;resetStamperState()
&#9;&#9;
&#9;&#9;
&#9;&#9;-- setup the control table we pass back to the user
&#9;&#9;control.Stamped = stamped -- BoolValue that fires when user stamps
&#9;&#9;control.Paused = false
&#9;&#9;
&#9;&#9;control.LoadNewModel = function(newStampModel) -- allows us to specify a new stamper model to be used with this stamper
&#9;&#9;&#9;if newStampModel and not newStampModel:IsA(&quot;Model&quot;) and not newStampModel:IsA(&quot;BasePart&quot;) then
&#9;&#9;&#9;&#9;error(&quot;Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!&quot;)
&#9;&#9;&#9;&#9;return nil
&#9;&#9;&#9;end
&#9;&#9;&#9;resetStamperState(newStampModel)
&#9;&#9;end
&#9;&#9;
&#9;&#9;control.ReloadModel = function() -- will automatically set stamper to get a new model of current model and start stamping with new model
&#9;&#9;&#9;resetStamperState()
&#9;&#9;end
&#9;&#9;
&#9;&#9;control.Pause = function() -- temporarily stops stamping, use resume to start up again
&#9;&#9;&#9;if not control.Paused then
&#9;&#9;&#9;&#9;pauseStamper()
&#9;&#9;&#9;&#9;control.Paused = true
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;print(&quot;RbxStamper Warning: Tried to call Control.Pause() when already paused&quot;)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;control.Resume = function() -- resumes stamping, if currently paused
&#9;&#9;&#9;if control.Paused then
&#9;&#9;&#9;&#9;resumeStamper()
&#9;&#9;&#9;&#9;control.Paused = false
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;print(&quot;RbxStamper Warning: Tried to call Control.Resume() without Pausing First&quot;)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;
&#9;&#9;control.ResetRotation = function() -- resets the model rotation so new models are at default orientation
&#9;&#9;&#9;-- gInitial90DegreeRotations = 0
&#9;&#9;&#9;-- 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
&#9;&#9;&#9;-- High-Scalability and with the new model orientation setting methods (model:ResetOrientationToIdentity()) [HotThoth]
&#9;&#9;end
&#9;&#9;
&#9;&#9;control.Destroy = function() -- Stops current Stamp operation and destroys control construct
&#9;&#9;&#9;for i = 1, #mouseCons do
&#9;&#9;&#9;&#9;mouseCons[i]:disconnect()
&#9;&#9;&#9;&#9;mouseCons[i] = nil
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;if keyCon then
&#9;&#9;&#9;&#9;keyCon:disconnect()
&#9;&#9;&#9;end
&#9;&#9;&#9;
&#9;&#9;&#9;game.JointsService:ClearJoinAfterMoveJoints()
&#9;&#9;&#9;
&#9;&#9;&#9;if adorn then adorn:Destroy() end
&#9;&#9;&#9;if adornPart then adornPart:Destroy() end
&#9;&#9;&#9;if errorBox then errorBox:Destroy() end
&#9;&#9;&#9;if stampData then
&#9;&#9;&#9;&#9;if stampData[&quot;Dragger&quot;] then
&#9;&#9;&#9;&#9;&#9;stampData.Dragger:Destroy()
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;if stampData.CurrentParts then
&#9;&#9;&#9;&#9;&#9;stampData.CurrentParts:Destroy()
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;&#9;if control and control[&quot;Stamped&quot;] then
&#9;&#9;&#9;&#9;control.Stamped:Destroy()
&#9;&#9;&#9;end
&#9;&#9;&#9;control = nil
&#9;&#9;end
&#9;&#9;return control
&#9;end
&#9;RBXStamper.Help =
&#9;&#9;function(funcNameOrFunc)
&#9;&#9;&#9;--input argument can be a string or a function. Should return a description (of arguments and expected side effects)
&#9;&#9;&#9;if funcNameOrFunc == &quot;GetStampModel&quot; or funcNameOrFunc == t.GetStampModel then
&#9;&#9;&#9;&#9;return &quot;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&quot;
&#9;&#9;&#9;end
&#9;&#9;&#9;if funcNameOrFunc == &quot;SetupStamperDragger&quot; or funcNameOrFunc == t.SetupStamperDragger then
&#9;&#9;&#9;&#9;return &quot;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 &apos;stamp&apos; 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.&quot;
&#9;&#9;&#9;end
&#9;&#9;end
end
------------------------
--[[UTIL Functions]]--
------------------------
--Global functions used by all classes are wrapped in UTIL
--deal with it.
local UTIL = {}
function UTIL.Class(tableIn,template)
&#9;local mt = {
&#9;__metatable = template;
&#9;__index = template;
&#9;}
&#9;return setmetatable(tableIn, mt)
end
function UTIL.MakeClass(...)
&#9;local arg = {...}
&#9;assert(#arg&gt;0, &apos;ERROR: class needs 1 argument or more&apos;)
&#9;local members = arg[1]
&#9;for i=2,#arg,1 do
&#9;&#9;if type(arg[i])==&apos;table&apos; then
&#9;&#9;&#9;for key,val in pairs(arg[i]) do
&#9;&#9;&#9;&#9;if not members[key] then
&#9;&#9;&#9;&#9;&#9;members[key] = val
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;local function New(init)
&#9;&#9;return UTIL.Class(init or {},members)
&#9;end
&#9;local function Copy(obj, ...)
&#9;&#9;local newobj = obj:New(unpack(arg))
&#9;&#9;for n,v in pairs(obj) do newobj[n] = v end
&#9;&#9;return newobj
&#9;end
&#9;members.New = members.New or New
&#9;members.Copy = members.Copy or Copy
&#9;return mt
end
function UTIL.Instantiate(guiType)
&#9;return function(data)
&#9;&#9;local obj = Instance.new(guiType)
&#9;&#9;for k, v in pairs(data) do
&#9;&#9;&#9;if type(k) == &apos;number&apos; then
&#9;&#9;&#9;&#9;v.Parent = obj
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;obj[k] = v
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return obj
&#9;end
end
function UTIL.RetroRegister(func,...)
&#9;func()
&#9;for _,i in ipairs({...}) do
&#9;&#9;i:connect(func)
&#9;end
end
------------------------
--[[UTIL Variables]]--
------------------------
--Global variables used by all classes are wrapped in UTIL
--deal with it.
UTIL.InsertService = game:GetService(&apos;InsertService&apos;)
--[[WARNING giving all classes access to playermouse may be a bad idea]]--
--[[currently needed by scrollFrame]]--
UTIL.LocalPlayer = game:GetService(&apos;Players&apos;).LocalPlayer
UTIL.PlayerMouse = UTIL.LocalPlayer:GetMouse()
UTIL.AssetURL = game:GetService(&quot;ContentProvider&quot;).BaseUrl:lower()..&apos;asset/?id=&apos;
UTIL.ProdAssetURL = &apos;http://www.roblox.com/asset/?id=&apos;
---------------
--[[Classes]]--
---------------
--[[Drop Down Class]]--
local DropDown =
{
&#9;MainFrame = UTIL.Instantiate&apos;Frame&apos;
&#9;{
&#9;&#9;Name =&apos;DropDown&apos;,
&#9;&#9;Size = UDim2.new(1,0,1,0),
&#9;&#9;BackgroundColor3 = Color3.new(.1,.1,.1),
&#9;&#9;BackgroundTransparency = 0,
&#9;&#9;ClipsDescendants = true,
&#9;},
&#9;Elements = {},
&#9;Callbacks = {},
&#9;IsOpen = false,
}
do
&#9;UTIL.MakeClass(DropDown)
&#9;function DropDown.New()
&#9;&#9;local init = {}
&#9;&#9;init = UTIL.Class(init,DropDown)
&#9;&#9;init.MainFrame = DropDown.MainFrame:Clone()
&#9;&#9;init.Elements = {}
&#9;&#9;init.Callbacks = {}
&#9;&#9;return init
&#9;end
&#9;function DropDown:Invalidate()
&#9;&#9;for index,i in ipairs(self.Elements) do
&#9;&#9;&#9;i.Parent= self.MainFrame
&#9;&#9;&#9;i.Position = UDim2.new(0,0,0,i.Size.Y.Offset*(index-1))
&#9;&#9;end
&#9;&#9;if self.IsOpen then
&#9;&#9;&#9;self.MainFrame.Size = UDim2.new(1,0,#self.Elements,0)
&#9;&#9;else
&#9;&#9;&#9;self.MainFrame.Size = UDim2.new(1,0,1,0)
&#9;&#9;end
&#9;end
&#9;function DropDown:Open()
&#9;&#9;self.IsOpen = true
&#9;&#9;self:Invalidate()
&#9;end
&#9;function DropDown:Close(elementSelected)
&#9;&#9;for index,i in ipairs(self.Elements) do
&#9;&#9;&#9;if i==elementSelected and index ~=1 then
&#9;&#9;&#9;&#9;table.remove(self.Elements,index)
&#9;&#9;&#9;&#9;table.insert(self.Elements,1,elementSelected)
&#9;&#9;&#9;&#9;self.IsOpen=false
&#9;&#9;&#9;&#9;for _,i in ipairs(self.Callbacks) do
&#9;&#9;&#9;&#9;&#9;i(elementSelected)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;self:Invalidate()
&#9;&#9;&#9;&#9;return
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;self.IsOpen=false
&#9;&#9;self:Invalidate()
&#9;end
&#9;function DropDown:AddElement(element)
&#9;&#9;table.insert(self.Elements,element)
&#9;&#9;element.MouseButton1Down:connect(function()
&#9;&#9;&#9;if self.IsOpen then
&#9;&#9;&#9;&#9;self:Close(element)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;self:Open()
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;self:Invalidate()
&#9;end
&#9;function DropDown:OnButtonSelected(func)
&#9;&#9;table.insert(self.Callbacks,func)
&#9;end
&#9;function DropDown:GetCurrentSelection()
&#9;&#9;return self.Elements[1]
&#9;end
end
--[[Scroll Frame Class]]--
local ScrollFrame =
{
&#9;Columns = 1,
&#9;MainFrame = UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = &quot;ScrollFrame&quot;,
&#9;&#9;&#9;Size = UDim2.new(1,0,1,0),
&#9;&#9;&#9;BackgroundTransparency =1,
&#9;&#9;&#9;ClipsDescendants = true,
&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;ContentFrame&apos;,
&#9;&#9;&#9;&#9;Active = true,
&#9;&#9;&#9;&#9;BackgroundTransparency =1,
&#9;&#9;&#9;&#9;Size = UDim2.new(1,-18,100,0),
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &quot;ControlFrame&quot;,
&#9;&#9;&#9;&#9;Size = UDim2.new(0,18,1,0),
&#9;&#9;&#9;&#9;Position =UDim2.new(1,-18,0,0),
&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name = &apos;DragTab&apos;,
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(1,0,1,-36),
&#9;&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name = &quot;UpButton&quot;,
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,18,0,18),
&#9;&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageLabel&apos;
&#9;&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;&#9;Image = UTIL.ProdAssetURL..112365584,
&#9;&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,16,0,16),
&#9;&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(.5,-8,.5,-8),
&#9;&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name = &quot;DownButton&quot;,
&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(0,0,1,-18),
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,18,0,18),
&#9;&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageLabel&apos;
&#9;&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;&#9;Image = UTIL.ProdAssetURL..112365220,
&#9;&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,16,0,16),
&#9;&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(.5,-8,.5,-8),
&#9;&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;}
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;},
&#9;&#9;},
&#9;ContentFrame = nil,
&#9;ControlFrame = nil,
&#9;UpButton = nil,
&#9;DownButton = nil,
&#9;Entries = nil,
&#9;ScrollAmount = 0,
&#9;ButtonScrollAmount = 40,
&#9;WheelScrollAmount = 20,
&#9;BottomElementPosition = 0,
}
do
&#9;UTIL.MakeClass(ScrollFrame)
&#9;--[[class local functions]]--
&#9;local function UDim2Add(a,b)
&#9;&#9;return UDim2.new(a.X.Scale+b.X.Scale,a.X.Offset+b.X.Offset,a.Y.Scale+b.Y.Scale,a.Y.Offset+b.Y.Offset)
&#9;end
&#9;--[[class functions]]--
&#9;function ScrollFrame.New()
&#9;&#9;local init ={}
&#9;&#9;init = UTIL.Class(init,ScrollFrame)
&#9;&#9;init.MainFrame= ScrollFrame.MainFrame:Clone()
&#9;&#9;init.ContentFrame= init.MainFrame:WaitForChild(&apos;ContentFrame&apos;)
&#9;&#9;init.ControlFrame= init.MainFrame:WaitForChild(&apos;ControlFrame&apos;)
&#9;&#9;init.UpButton= init.ControlFrame:WaitForChild(&apos;UpButton&apos;)
&#9;&#9;init.DownButton= init.ControlFrame:WaitForChild(&apos;DownButton&apos;)
&#9;&#9;init.UpButton.MouseButton1Down:connect(function() init:Scroll(init.ButtonScrollAmount) end)
&#9;&#9;init.DownButton.MouseButton1Down:connect(function() init:Scroll(-init.ButtonScrollAmount) end)
&#9;&#9;init.Entries = {}
&#9;&#9;--untill I can get scroll wheel, this wont work :(
&#9;&#9;--[[
&#9;&#9;init.ContentFrame.MouseEnter:connect(function()
&#9;&#9;&#9;print(&apos;connected to mouse wheel&apos;)
&#9;&#9;&#9;local scrollConnectUp= UTIL.PlayerMouse.WheelForward:connect(function()
&#9;&#9;&#9;&#9;print(&apos;scrolled up&apos;)
&#9;&#9;&#9;&#9;init:Scroll(init.WheelScrollAmount)
&#9;&#9;&#9;end)
&#9;&#9;&#9;local scrollConnectDown= UTIL.PlayerMouse.WheelForward:connect(function()
&#9;&#9;&#9;&#9;init:Scroll(-init.WheelScrollAmount)
&#9;&#9;&#9;end)
&#9;&#9;&#9;init.ContentFrame.MouseLeave:connect(function()
&#9;&#9;&#9;&#9;print(&apos;disconnected mouse wheel&apos;)
&#9;&#9;&#9;&#9;scrollConnectUp:disconnect()
&#9;&#9;&#9;&#9;scrollConnectDown:disconnect()
&#9;&#9;&#9;end)
&#9;&#9;end)
&#9;&#9;--]]
&#9;&#9;init.ControlFrame.DragTab.MouseButton1Down:connect(function(x,y)init:StartTabDrag(x,y) end)
&#9;&#9;init.MainFrame.Changed:connect(function(prop)
&#9;&#9;&#9;if prop==&apos;Size&apos; or prop==&apos;AbsoluteSize&apos; or prop==&apos;Parent&apos; then
&#9;&#9;&#9;&#9;ScrollFrame.Invalidate(init)
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;return init
&#9;end
&#9;function ScrollFrame:AddEntry(nEntry,nIndex)
&#9;&#9;if nIndex then
&#9;&#9;&#9;table.insert(self.Entries,nIndex,nEntry)&#9;
&#9;&#9;else
&#9;&#9;&#9;table.insert(self.Entries,nEntry)
&#9;&#9;end
&#9;&#9;nEntry.Parent = self.ContentFrame
&#9;&#9;self:Invalidate()
&#9;end
&#9;function ScrollFrame:RemoveEntry(entry)
&#9;
&#9;end
&#9;function ScrollFrame:ClearEntries()
&#9;
&#9;end
&#9;function ScrollFrame:SetScroll(position)
&#9;
&#9;end
&#9;function ScrollFrame:Scroll(amount)
&#9;&#9;self.ScrollAmount= self.ScrollAmount+amount
&#9;&#9;self:InvalidateScrolling()
&#9;end
&#9;function ScrollFrame:StartTabDrag(startx,starty)
&#9;&#9;local dragTab = self.ControlFrame.DragTab
&#9;&#9;--local StartTabPos = dragTab.Position.Y.Scale
&#9;&#9;local startScrollAmount = self.ScrollAmount
&#9;&#9;local leaveConnect
&#9;&#9;local upConnect
&#9;&#9;local moveConnect
&#9;&#9;local function updateDrag(x,y)
&#9;&#9;&#9;local dragTabScale = (self.ControlFrame.AbsoluteSize.Y-36-self.ControlFrame.DragTab.AbsoluteSize.Y)/(self.BottomElementPosition)
&#9;&#9;&#9;self.ScrollAmount = startScrollAmount-((y-starty)/dragTabScale)
&#9;&#9;&#9;self:InvalidateScrolling()
&#9;&#9;end
&#9;&#9;local function endDrag(x,y)
&#9;&#9;&#9;updateDrag(x,y)
&#9;&#9;&#9;if leaveConnect then leaveConnect:disconnect() end
&#9;&#9;&#9;if upConnect then upConnect:disconnect() end
&#9;&#9;&#9;if moveConnect then moveConnect:disconnect() end
&#9;&#9;end
&#9;&#9;leaveConnect = dragTab.MouseLeave:connect(endDrag)
&#9;&#9;upConnect = dragTab.MouseButton1Up:connect(endDrag)
&#9;&#9;moveConnect = dragTab.MouseMoved:connect(updateDrag)
&#9;end
&#9;
&#9;function ScrollFrame:InvalidateScrolling()
&#9;&#9;--print(&apos;scrollamount:&apos;..self.ScrollAmount..&apos; BottomPos:&apos;..self.BottomElementPosition)
&#9;&#9;if self.BottomElementPosition&gt;0 then
&#9;&#9;&#9;self.ControlFrame.Visible = true
&#9;&#9;else
&#9;&#9;&#9;self.ControlFrame.Visible = false
&#9;&#9;end
&#9;&#9;local dragTabScale = self.ControlFrame.AbsoluteSize.Y/(self.BottomElementPosition+self.ControlFrame.AbsoluteSize.Y)
&#9;&#9;self.ControlFrame.DragTab.Size = UDim2.new(1,0,dragTabScale,-36)
&#9;&#9;self.ScrollAmount = math.min(0,math.max(self.ScrollAmount,-self.BottomElementPosition))
&#9;&#9;self.ControlFrame.DragTab.Position = UDim2.new(0,0,((self.ScrollAmount/(-self.BottomElementPosition)))*(1-dragTabScale),18)
&#9;&#9;self.ContentFrame.Position = UDim2.new(0,0,0,self.ScrollAmount)
&#9;end
&#9;function ScrollFrame:Invalidate()
&#9;&#9;local nextPos = UDim2.new(0,0,0,0)
&#9;&#9;local CurrentColumn = 1
&#9;&#9;local bottomElement = nil
&#9;&#9;for _, i in ipairs(self.Entries) do
&#9;&#9;&#9;i.Position = nextPos
&#9;&#9;&#9;CurrentColumn=CurrentColumn+1
&#9;&#9;&#9;if self.Columns&lt;=0 then
&#9;&#9;&#9;&#9;--print(i.Parent.AbsoluteSize.x)
&#9;&#9;&#9;&#9;if i.Position.X.Offset+i.Size.X.Offset&gt;i.Parent.AbsoluteSize.x then
&#9;&#9;&#9;&#9;&#9;nextPos = UDim2.new(0,0,nextPos.Y.Scale+i.Size.Y.Scale,nextPos.Y.Offset+i.Size.Y.Offset)
&#9;&#9;&#9;&#9;&#9;i.Position = nextPos
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;&#9;nextPos = UDim2Add(nextPos,UDim2.new(i.Size.X.Scale,i.Size.X.Offset,0,0))
&#9;&#9;&#9;elseif CurrentColumn&gt;self.Columns then
&#9;&#9;&#9;&#9;nextPos = UDim2.new(0,0,nextPos.Y.Scale+i.Size.Y.Scale,nextPos.Y.Offset+i.Size.Y.Offset)
&#9;&#9;&#9;&#9;CurrentColumn = 1
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;nextPos = UDim2Add(nextPos,UDim2.new(i.Size.X.Scale,i.Size.X.Offset,0,0))
&#9;&#9;&#9;end
&#9;&#9;&#9;bottomElement= i
&#9;&#9;end
&#9;&#9;if bottomElement then
&#9;&#9;&#9;self.BottomElementPosition= bottomElement.AbsolutePosition.Y+bottomElement.AbsoluteSize.Y- self.ContentFrame.Parent.AbsoluteSize.Y-self.ContentFrame.Parent.AbsolutePosition.Y
&#9;&#9;&#9;self:InvalidateScrolling()
&#9;&#9;end
&#9;end
&#9;function ScrollFrame:SetColumns(ncol)
&#9;&#9;self.Columns=ncol
&#9;&#9;self:Invalidate()
&#9;end
end
--[[Stamp Object Class]]--
local StampObject =
{
&#9;IconURL = &apos;&apos;,
&#9;PreviewURL = &apos;&apos;,
&#9;AssetId = &apos;&apos;,
&#9;Name = &apos;&apos;,
&#9;Asset = nil,
}
do
&#9;UTIL.MakeClass(StampObject)
&#9;--[[class static variables/functions]]--
&#9;local BaseUrl = game:GetService(&quot;ContentProvider&quot;).BaseUrl:lower()
&#9;local ThumbnailBase = BaseUrl .. &quot;Game/Tools/ThumbnailAsset.ashx?fmt=png&amp;wd=75&amp;ht=75&amp;aid=&quot;
&#9;local PreviewBase = BaseUrl .. &quot;Game/Tools/ThumbnailAsset.ashx?fmt=png&amp;wd=420&amp;ht=420&amp;aid=&quot;
&#9;--[[Class functions]]--
&#9;function StampObject.New(name,assetId)
&#9;&#9;local init = {}
&#9;&#9;init.Name = name
&#9;&#9;init.IconURL = ThumbnailBase..assetId
&#9;&#9;init.PreviewURL = PreviewBase..assetId
&#9;&#9;init.AssetId = assetId
&#9;&#9;init.Asset = nil
&#9;&#9;return UTIL.Class(init,StampObject)
&#9;end
end
--[[Model Object Class]]--
--[[
&#9;old cludge, canidate for deletion
&#9;
local ModelObject =
{
&#9;
}
do
&#9;UTIL.MakeClass(ModelObject,StampObject)
&#9;function ModelObject.New(assetId)
&#9;&#9;local init = StampObject.New(assetId)
&#9;&#9;return Class(init,ModelObject)
&#9;end
end
]]--
--[[StamperSet Class]]--
local StamperSet =
{
&#9;Name = &apos;&apos;,
&#9;Objects = {},
}
do
&#9;UTIL.MakeClass(StamperSet)
&#9;function StamperSet.New(setName,setId)
&#9;&#9;local init ={}
&#9;&#9;init = UTIL.Class(init,StamperSet)
&#9;&#9;init.Name = setName
&#9;&#9;init:InitSet(setId)
&#9;&#9;return init
&#9;end
&#9;function StamperSet:InitSet(setId)
&#9;--&#9;print(setId)
&#9;&#9;self.Objects = {}
&#9;&#9;if type(setId)==&apos;number&apos; then
&#9;&#9;&#9;for _,i in pairs(UTIL.InsertService:GetCollection(setId)) do
&#9;&#9;&#9;&#9;table.insert(self.Objects,StampObject.New(i.Name,i.AssetId))
&#9;&#9;&#9;end
&#9;&#9;elseif type(setId)==&apos;table&apos; then
&#9;&#9;&#9;for _,i in pairs(setId) do
&#9;&#9;&#9;&#9;table.insert(self.Objects,StampObject.New(&quot;&quot;,i))
&#9;&#9;&#9;end
&#9;&#9;elseif setId:IsA(&apos;Instance&apos;) then
&#9;&#9;&#9;for _,i in pairs(setId:GetChildren()) do
&#9;&#9;&#9;&#9;if i:IsA(&apos;IntValue&apos;) then
&#9;&#9;&#9;&#9;&#9;table.insert(self.Objects,StampObject.New(i.Name,i.Value))
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
end
--[[SetManager Class]]--
local SetManager =
{
&#9;Sets = {},
}
do
&#9;
&#9;UTIL.MakeClass(SetManager)
&#9;function SetManager.New()
&#9;&#9;local init = {}
&#9;&#9;init.Sets={}
&#9;&#9;return UTIL.Class(init,SetManager)
&#9;end
&#9;function SetManager:AddModelSet(nmodel)
&#9;&#9;self.Sets[nmodel.Name]=StamperSet.New(nmodel.Name,nmodel)
&#9;&#9;return self.Sets[nmodel.Name]
&#9;end
&#9;function SetManager:AddSet(setName,setId)
&#9;&#9;--print(setName,setId)
&#9;&#9;self.Sets[setName]=StamperSet.New(setName,setId)
&#9;&#9;return self.Sets[setName]
&#9;end
&#9;function SetManager:AddUserSets(userId)
&#9;&#9;local userSets = UTIL.InsertService:GetUserSets(userId)
&#9;&#9;local nsets = {}
&#9;&#9;for _, i in pairs(userSets) do
&#9;&#9;&#9;if tonumber(i.AssetSetId)&gt;0 then
&#9;&#9;&#9;&#9;table.insert(nsets,self:AddSet(i.Name,tonumber(i.AssetSetId)))
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;return nsets
&#9;end
end
--[[Stamper Gui Class]]--
local StamperGui =
{
&#9;ScreenGui = UTIL.Instantiate&apos;ScreenGui&apos;
&#9;{
&#9;&#9;Name = &apos;StamperScreen&apos;,
&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = &apos;MainFrame&apos;,
&#9;&#9;&#9;Position = UDim2.new(-1,29, 0.100000001, 24),
&#9;&#9;&#9;Size = UDim2.new(0.600000024, -58, 0.639999986, 0),
&#9;&#9;&#9;Style = &apos;RobloxRound&apos;,&#9;
&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;Catagory&apos;,
&#9;&#9;&#9;&#9;BackgroundTransparency =1,
&#9;&#9;&#9;&#9;Size = UDim2.new(.22,0,1,0),
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate &apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;DividerLine&apos;,
&#9;&#9;&#9;&#9;BackgroundTransparency =.7,
&#9;&#9;&#9;&#9;Size = UDim2.new(0, 3, 0.95, 0),
&#9;&#9;&#9;&#9;Position = UDim2.new(.22,0,.025,0),
&#9;&#9;&#9;&#9;BorderSizePixel = 0,
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate &apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;DividerLine&apos;,
&#9;&#9;&#9;&#9;BackgroundTransparency =.7,
&#9;&#9;&#9;&#9;Size = UDim2.new(0, 3, 0.95, 0),
&#9;&#9;&#9;&#9;Position = UDim2.new(.71,0,.025,0),
&#9;&#9;&#9;&#9;BorderSizePixel = 0,
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;StampObjs&apos;,
&#9;&#9;&#9;&#9;BackgroundTransparency =1,
&#9;&#9;&#9;&#9;Position = UDim2.new(.22,0,0,0),
&#9;&#9;&#9;&#9;Size = UDim2.new(.5,0,1,0),
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;PreviewFrame&apos;,
&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;Position = UDim2.new(.72,0,0,0),
&#9;&#9;&#9;&#9;Size = UDim2.new(.28,0,1,0),
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageLabel&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name=&apos;PreviewImage&apos;,
&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;Image =&quot;&quot;,
&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(0,0,0,0),
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(1,0,1,0),
&#9;&#9;&#9;&#9;&#9;SizeConstraint =&apos;RelativeXX&apos;,
&#9;&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;TextLabel&apos;
&#9;&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;&#9;Name = &apos;PreviewName&apos;,
&#9;&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;&#9;Text = &quot;&quot;,
&#9;&#9;&#9;&#9;&#9;&#9;Font = &apos;ArialBold&apos;,
&#9;&#9;&#9;&#9;&#9;&#9;FontSize = &apos;Size24&apos;,
&#9;&#9;&#9;&#9;&#9;&#9;TextWrapped = true,
&#9;&#9;&#9;&#9;&#9;&#9;TextColor3 = Color3.new(1,1,1),
&#9;&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(0,0,1,0),
&#9;&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(1,0,0,50),
&#9;&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;&#9;Name = &apos;TerrainSettings&apos;,
&#9;&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(0,0,1,50),
&#9;&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(1,0,0,150),
&#9;&#9;&#9;&#9;&#9;&#9;Visible = false,
&#9;&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;OpenFrame&apos;,
&#9;&#9;&#9;&#9;Style = &apos;RobloxRound&apos;,
&#9;&#9;&#9;&#9;Size = UDim2.new(0,64,0,43),
&#9;&#9;&#9;&#9;Position = UDim2.new(1,8,0,0),
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name = &apos;OpenButton&apos;,
&#9;&#9;&#9;&#9;&#9;Image = UTIL.ProdAssetURL..112989057,
&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,32,0,32),
&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(.5,-20,.5,-16)
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;&#9;UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;&#9;Name = &apos;OpenButtonArrow&apos;,
&#9;&#9;&#9;&#9;&#9;Image = UTIL.ProdAssetURL..112989231,
&#9;&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;&#9;Size = UDim2.new(0,32,0,32),
&#9;&#9;&#9;&#9;&#9;Position = UDim2.new(.5,10,.5,-16)
&#9;&#9;&#9;&#9;},
&#9;&#9;&#9;},
&#9;&#9;}
&#9;},
&#9;CallBacks = {},
&#9;CatagoryFrame = ScrollFrame.New(),
&#9;--ObjectFrame = ScrollFrame.New(),
&#9;SetMan = SetManager:New(),
&#9;PreviewImage = nil,
&#9;PreviewText = nil,
&#9;OpenButton = nil,
&#9;OpenButtonArrow = nil,
&#9;CurrentSet=nil,
&#9;State = &apos;Inactive&apos;,--can be Active, Inactive or Waiting
&#9;InactivePosition = UDim2.new(-0.6, 58, 0.3, 24),
&#9;ActivePosition = UDim2.new(0.2, 29, 0.1, 24),
&#9;TerrainShapeData =
&#9;{
&#9;&#9;{Name = &apos;Block&apos;,Id = 0,},
&#9;&#9;{Name = &apos;Vertical Ramp&apos;,Id = 1,},
&#9;&#9;{Name = &apos;Corner&apos;,Id = 2,},
&#9;&#9;{Name = &apos;Inverse Corner&apos;,Id = 3,},
&#9;&#9;{Name = &apos;Horisontal Ramp&apos;,Id = 4,},
&#9;&#9;{Name = &apos;Auto-Wedge&apos;,Id = 6,},
&#9;},
&#9;CurrentTerrainShape = -1,
}
do
&#9;UTIL.MakeClass(StamperGui)
&#9;
&#9;--[[class functions]]--
&#9;function StamperGui.New(nplayergui)
&#9;&#9;local init = StamperGui
&#9;&#9;init.PreviewImage = init.ScreenGui.MainFrame.PreviewFrame.PreviewImage
&#9;&#9;init.PreviewText = init.PreviewImage.PreviewName
&#9;&#9;init.OpenButton = init.ScreenGui.MainFrame.OpenFrame.OpenButton
&#9;&#9;init.OpenButtonArrow = init.ScreenGui.MainFrame.OpenFrame.OpenButtonArrow
&#9;&#9;init.OpenButton.MouseButton1Down:connect(function()
&#9;&#9;&#9;init:ToggleGui(nplayergui)
&#9;&#9;end)
&#9;&#9;init:InitTerrainSettings()
&#9;&#9;return init
&#9;end
&#9;function StamperGui:SetTerrainShape(name,id)
&#9;&#9;for _, i in pairs(self.PreviewImage.TerrainSettings:GetChildren()) do
&#9;&#9;&#9;if i.Name == name then
&#9;&#9;&#9;&#9;i.Style = &apos;RobloxButtonDefault&apos;
&#9;&#9;&#9;&#9;i.TextColor3= Color3.new(1,1,1)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;i.Style = &apos;RobloxButton&apos;
&#9;&#9;&#9;&#9;i.TextColor3 = Color3.new(.85,.85,.85)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;self.CurrentTerrainShape = id
&#9;end
&#9;function StamperGui:InitTerrainSettings()
&#9;&#9;for index,i in ipairs(self.TerrainShapeData) do
&#9;&#9;&#9;local tbut =UTIL.Instantiate&apos;TextButton&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = i.Name,
&#9;&#9;&#9;&#9;Text = i.Name,
&#9;&#9;&#9;&#9;Size = UDim2.new(1,0,.16,0),
&#9;&#9;&#9;&#9;Position = UDim2.new(0,0,.16*(index-1),0),
&#9;&#9;&#9;&#9;TextColor3 = Color3.new(1,1,1),
&#9;&#9;&#9;&#9;Font = &apos;ArialBold&apos;,
&#9;&#9;&#9;&#9;FontSize = &apos;Size14&apos;,
&#9;&#9;&#9;&#9;Parent = self.PreviewImage.TerrainSettings,
&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;}
&#9;&#9;&#9;tbut.MouseButton1Down:connect(function()
&#9;&#9;&#9;&#9;self:SetTerrainShape(i.Name,i.Id)
&#9;&#9;&#9;end)
&#9;&#9;end
&#9;end
&#9;function StamperGui:CreateObjectButton(stampObj)
&#9;&#9;game:GetService(&quot;ContentProvider&quot;):Preload(stampObj.IconURL)
&#9;&#9;local tbut =UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = stampObj.Name,
&#9;&#9;&#9;Size = UDim2.new(0,64,0,64),
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;UTIL.Instantiate&apos;TextButton&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;Button&apos;,
&#9;&#9;&#9;&#9;Text = &apos;&apos;,
&#9;&#9;&#9;&#9;Size =UDim2.new(0,60,0,60),
&#9;&#9;&#9;&#9;Position = UDim2.new(0,2,0,2),
&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;ImageLabel&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;Image&apos;,
&#9;&#9;&#9;&#9;Image = stampObj.IconURL,
&#9;&#9;&#9;&#9;Active = false,
&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;Size = UDim2.new(0,48,0,48),
&#9;&#9;&#9;&#9;Position = UDim2.new(0,8,0,8),
&#9;&#9;&#9;},
&#9;&#9;}
&#9;&#9;tbut.Button.MouseButton1Down:connect(function()
&#9;&#9;&#9;for _, j in pairs(self.CallBacks) do
&#9;&#9;&#9;&#9;j(stampObj,self.CurrentTerrainShape)
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;tbut.Button.MouseEnter:connect(function()
&#9;&#9;&#9;self.PreviewImage.Image= stampObj.IconURL
&#9;&#9;&#9;game:GetService(&quot;ContentProvider&quot;):Preload(stampObj.PreviewURL)
&#9;&#9;&#9;self.PreviewImage.Image= stampObj.PreviewURL
&#9;&#9;&#9;self.PreviewText.Text = stampObj.Name
&#9;&#9;end)
&#9;&#9;
&#9;&#9;return tbut
&#9;end
&#9;function StamperGui:CreateCatagoryButton(setObj)
&#9;&#9;local outputGui = self.ScreenGui.MainFrame.StampObjs
&#9;&#9;local tbut =UTIL.Instantiate&apos;TextButton&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = setObj.Name,
&#9;&#9;&#9;Text = setObj.Name,
&#9;&#9;&#9;Font = &apos;Arial&apos;,
&#9;&#9;&#9;FontSize = &apos;Size18&apos;,
&#9;&#9;&#9;TextXAlignment = &apos;Left&apos;,
&#9;&#9;&#9;BackgroundTransparency=1,
&#9;&#9;&#9;AutoButtonColor = false,
&#9;&#9;&#9;TextColor3 = Color3.new(1,1,1),
&#9;&#9;&#9;Size = UDim2.new(1,0,0,20),
&#9;&#9;&#9;ClipsDescendants = true,
&#9;&#9;}
&#9;&#9;local ObjectFrame = ScrollFrame.New()
&#9;&#9;ObjectFrame:SetColumns(-1)
&#9;&#9;for _,i in ipairs(setObj.Objects) do
&#9;&#9;&#9;local obut = self:CreateObjectButton(i)
&#9;&#9;&#9;ObjectFrame:AddEntry(obut,1)
&#9;&#9;end
&#9;&#9;tbut.MouseEnter:connect(function()
&#9;&#9;&#9;if self.CurrentSet~=tbut then
&#9;&#9;&#9;&#9;tbut.BackgroundTransparency = 0
&#9;&#9;&#9;&#9;tbut.BackgroundColor3 = Color3.new(1,1,1)
&#9;&#9;&#9;&#9;tbut.TextColor3 = Color3.new(0,0,0)
&#9;&#9;&#9;end
&#9;&#9;&#9;self.PreviewImage.Image= &quot;&quot;
&#9;&#9;&#9;self.PreviewText.Text = &quot;&quot;
&#9;&#9;end)
&#9;&#9;tbut.MouseLeave:connect(function()
&#9;&#9;&#9;if self.CurrentSet~=tbut then
&#9;&#9;&#9;&#9;tbut.BackgroundTransparency = 1
&#9;&#9;&#9;&#9;tbut.TextColor3 = Color3.new(1,1,1)
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;local function OnMouseDown()
&#9;&#9;&#9;if self.CurrentSet then
&#9;&#9;&#9;&#9;self.CurrentSet.BackgroundTransparency = 1
&#9;&#9;&#9;&#9;self.CurrentSet.TextColor3 = Color3.new(1,1,1)
&#9;&#9;&#9;end
&#9;&#9;&#9;self.CurrentSet=tbut
&#9;&#9;&#9;tbut.BackgroundTransparency = 0
&#9;&#9;&#9;tbut.BackgroundColor3 = Color3.new(0,204/255,0)
&#9;&#9;&#9;tbut.TextColor3 = Color3.new(0,0,0)
&#9;&#9;&#9;for _,i in pairs(outputGui:GetChildren()) do
&#9;&#9;&#9;&#9;i.Parent = nil
&#9;&#9;&#9;end
&#9;&#9;&#9;--[[HACKITY HACK]]--
&#9;&#9;&#9;if setObj.Name == &apos;High Scalability&apos; then
&#9;&#9;&#9;&#9;self.PreviewImage.TerrainSettings.Visible = true
&#9;&#9;&#9;&#9;StamperGui:SetTerrainShape(&apos;Block&apos;,0)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;self.PreviewImage.TerrainSettings.Visible = false
&#9;&#9;&#9;&#9;StamperGui:SetTerrainShape(&apos;nill&apos;,-1)
&#9;&#9;&#9;end
&#9;&#9;&#9;--[[dont come back]]--
&#9;&#9;&#9;ObjectFrame.MainFrame.Parent = outputGui
&#9;&#9;end
&#9;&#9;tbut.MouseButton1Down:connect(OnMouseDown)
&#9;&#9;if #self.CatagoryFrame.Entries ==0 then
&#9;&#9;&#9;OnMouseDown()
&#9;&#9;end
&#9;&#9;return tbut
&#9;end
&#9;function StamperGui:ToggleGui(nplayerGui)
&#9;&#9;if self.State==&apos;Active&apos; then
&#9;&#9;&#9;self:RemoveGui()
&#9;&#9;elseif self.State==&apos;Inactive&apos; then
&#9;&#9;&#9;self:AddGui(nplayerGui)
&#9;&#9;end
&#9;end
&#9;function StamperGui:AddGui(nplayerGui)
&#9;&#9;self.State=&apos;Waiting&apos;
&#9;&#9;assert(nplayerGui, &apos;ERROR, got bad playerGui&apos;)
&#9;&#9;--setting up catagory frame
&#9;&#9;self.CatagoryFrame.MainFrame.Parent = self.ScreenGui.MainFrame.Catagory
&#9;&#9;self.OpenButtonArrow.Image = UTIL.ProdAssetURL..112990112
&#9;&#9;self.ScreenGui.Parent = nplayerGui
&#9;&#9;self.ScreenGui.MainFrame:TweenPosition(self.ActivePosition, &quot;Out&quot;, &quot;Linear&quot;, .25, true)
&#9;&#9;Delay(.25,function() self.State=&apos;Active&apos; end)
&#9;&#9;
&#9;end
&#9;function StamperGui:RemoveGui()
&#9;&#9;self.State=&apos;Waiting&apos;
&#9;&#9;self.OpenButtonArrow.Image = UTIL.ProdAssetURL..112989231
&#9;&#9;self.ScreenGui.MainFrame:TweenPosition(self.InactivePosition, &quot;Out&quot;, &quot;Linear&quot;, .25, true)
&#9;&#9;Delay(.25,function()
&#9;&#9;&#9;--self.ScreenGui.Parent = nil
&#9;&#9;&#9;self.State=&apos;Inactive&apos;
&#9;&#9;end)
&#9;&#9;
&#9;end
&#9;function StamperGui:AddSet(nset)
&#9;&#9;print(&apos;Adding set&apos;)
&#9;&#9;if type(nset)==&apos;number&apos; then
&#9;&#9;&#9;--[[WARNING, bottleneck here, fetching collection twice]]--
&#9;&#9;&#9;-- create new path in setgui that accepts a collection to fix
&#9;&#9;&#9;local tcollection = UTIL.InsertService:GetCollection(i)
&#9;&#9;&#9;self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(self.SetMan:AddSet(tcollection.Name,nset)))
&#9;&#9;else
&#9;&#9;&#9;self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(self.SetMan:AddModelSet(nset)))
&#9;&#9;end
&#9;end
&#9;function StamperGui:AddUserSets(nuser)
&#9;&#9;local nsets = self.SetMan:AddUserSets(nuser)
&#9;&#9;for _,i in pairs(nsets) do
&#9;&#9;&#9;self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(i))
&#9;&#9;end
&#9;end
&#9;function StamperGui:RemoveSet(nset)
&#9;
&#9;end
&#9;function StamperGui:OnObjectSelected(callback)
&#9;&#9;table.insert(self.CallBacks,callback)
&#9;end
&#9;
&#9;
end
--[[Water Gui Class]]--
local WaterGui =
{
&#9;WaterMenu = UTIL.Instantiate&apos;Frame&apos;
&#9;{
&#9;&#9;Name=&apos;WaterFrame&apos;,
&#9;&#9;Visible = false,
&#9;&#9;Size = UDim2.new(0,120,0,110),
&#9;&#9;Position = UDim2.new(0,55,0,0),
&#9;&#9;BackgroundColor3 = Color3.new(0,0,0),
&#9;&#9;BackgroundTransparency = .4,
&#9;&#9;UTIL.Instantiate&apos;TextLabel&apos;
&#9;&#9;{
&#9;&#9;&#9;Text = &apos;Force&apos;,
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;Font = &apos;ArialBold&apos;,
&#9;&#9;&#9;FontSize = &apos;Size18&apos;,
&#9;&#9;&#9;TextColor3=Color3.new(1,1,1),
&#9;&#9;&#9;Position = UDim2.new(0,10,0,20),
&#9;&#9;&#9;TextYAlignment = &apos;Bottom&apos;,
&#9;&#9;&#9;TextXAlignment = &apos;Left&apos;,
&#9;&#9;},
&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = &apos;ForceFrame&apos;,
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;Size = UDim2.new(1,0,0,25),
&#9;&#9;&#9;Position = UDim2.new(0,0,0,25),
&#9;&#9;},
&#9;&#9;UTIL.Instantiate&apos;TextLabel&apos;
&#9;&#9;{
&#9;&#9;&#9;Text = &apos;Direction&apos;,
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;Font = &apos;ArialBold&apos;,
&#9;&#9;&#9;FontSize = &apos;Size18&apos;,
&#9;&#9;&#9;TextColor3=Color3.new(1,1,1),
&#9;&#9;&#9;Position = UDim2.new(0,10,0,73),
&#9;&#9;&#9;TextYAlignment = &apos;Bottom&apos;,
&#9;&#9;&#9;TextXAlignment = &apos;Left&apos;,
&#9;&#9;},
&#9;&#9;UTIL.Instantiate&apos;Frame&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = &apos;DirectionFrame&apos;,
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;Size = UDim2.new(1,0,0,25),
&#9;&#9;&#9;Position = UDim2.new(0,0,0,75),
&#9;&#9;},
&#9;},
&#9;WaterForceDropDown = DropDown.New(),
&#9;WaterDirectionDropDown = DropDown.New(),
&#9;ForceTypes = {&apos;None&apos;,&apos;Small&apos;,&apos;Medium&apos;,&apos;Max&apos;,&apos;Strong&apos;},
&#9;DirectionTypes = {[&apos; X&apos;]=&apos;X&apos;,[&apos;-X&apos;]=&apos;NegX&apos;,[&apos; Y&apos;]=&apos;Y&apos;,[&apos;-Y&apos;]=&apos;NegY&apos;,[&apos; Z&apos;]=&apos;Z&apos;,[&apos;-Z&apos;]=&apos;NegZ&apos;},
}
do
&#9;UTIL.MakeClass(WaterGui)
&#9;function WaterGui.New(screenGui)
&#9;&#9;local templatebut = UTIL.Instantiate&apos;TextButton&apos;
&#9;&#9;{
&#9;&#9;&#9;Size = UDim2.new(1,0,0,25),
&#9;&#9;&#9;BackgroundTransparency =1,
&#9;&#9;&#9;Font = &apos;ArialBold&apos;,
&#9;&#9;&#9;FontSize = &apos;Size24&apos;,
&#9;&#9;&#9;TextColor3=Color3.new(1,1,1),
&#9;&#9;}
&#9;&#9;for _, i in ipairs(WaterGui.ForceTypes) do
&#9;&#9;&#9;local tbut = templatebut:Clone()
&#9;&#9;&#9;tbut.ZIndex = 2
&#9;&#9;&#9;tbut.Text = i
&#9;&#9;&#9;WaterGui.WaterForceDropDown:AddElement(tbut)
&#9;&#9;end
&#9;&#9;for i, _ in pairs(WaterGui.DirectionTypes) do
&#9;&#9;&#9;local tbut = templatebut:Clone()
&#9;&#9;&#9;tbut.ZIndex = 1
&#9;&#9;&#9;tbut.Text = i
&#9;&#9;&#9;WaterGui.WaterDirectionDropDown:AddElement(tbut)
&#9;&#9;end
&#9;&#9;WaterGui.WaterMenu.Parent = screenGui
&#9;&#9;WaterGui.WaterForceDropDown.MainFrame.Parent = WaterGui.WaterMenu.ForceFrame
&#9;&#9;WaterGui.WaterForceDropDown.MainFrame.ZIndex = 2
&#9;&#9;WaterGui.WaterDirectionDropDown.MainFrame.Parent = WaterGui.WaterMenu.DirectionFrame
&#9;&#9;return WaterGui
&#9;end
&#9;function WaterGui:GetWaterData()
&#9;&#9;return self.WaterForceDropDown:GetCurrentSelection().Text,
&#9;&#9;&#9;self.DirectionTypes[self.WaterDirectionDropDown:GetCurrentSelection().Text]
&#9;end
end
--[[Recent Parts Gui Class]]--
local RecentPartsGui =
{
&#9;Gui = UTIL.Instantiate&apos;Frame&apos;
&#9;{
&#9;&#9;Name = &apos;RecentPartsFrame&apos;,
&#9;&#9;Size = UDim2.new(0,50,0,0),
&#9;&#9;Style = &apos;RobloxRound&apos;,
&#9;&#9;Position = UDim2.new(0,-5,0.4, 70),
&#9;},
&#9;Objects = {},
&#9;Callbacks = {},
&#9;NumSaved = 5,
}
do
&#9;UTIL.MakeClass(RecentPartsGui)
&#9;function RecentPartsGui:MakeRecentIcon(stampObj)
&#9;&#9;local tbut =UTIL.Instantiate&apos;ImageButton&apos;
&#9;&#9;{
&#9;&#9;&#9;Name = stampObj.Name,
&#9;&#9;&#9;Size = UDim2.new(0,50,0,50),
&#9;&#9;&#9;Position = UDim2.new(0,-5,0,-10),
&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;UTIL.Instantiate&apos;TextButton&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;Button&apos;,
&#9;&#9;&#9;&#9;Text = &apos;&apos;,
&#9;&#9;&#9;&#9;Size =UDim2.new(.90909,0,.90909,0),
&#9;&#9;&#9;&#9;Position = UDim2.new(.03636363,0,.03636363,0),
&#9;&#9;&#9;&#9;Style = &apos;RobloxButton&apos;,
&#9;&#9;&#9;&#9;
&#9;&#9;&#9;},
&#9;&#9;&#9;UTIL.Instantiate&apos;ImageLabel&apos;
&#9;&#9;&#9;{
&#9;&#9;&#9;&#9;Name = &apos;Image&apos;,
&#9;&#9;&#9;&#9;Image = stampObj.IconURL,
&#9;&#9;&#9;&#9;Active = false,
&#9;&#9;&#9;&#9;BackgroundTransparency = 1,
&#9;&#9;&#9;&#9;Size = UDim2.new(.63636363,0,.63636363,0),
&#9;&#9;&#9;&#9;Position = UDim2.new(.1454545,0,.1454545,0),
&#9;&#9;&#9;},
&#9;&#9;}
&#9;&#9;
&#9;&#9;tbut.Button.MouseEnter:connect(function()
&#9;&#9;&#9;tbut.Size = UDim2.new(0,55,0,55)
&#9;&#9;&#9;tbut.Position = UDim2.new(0,0,tbut.Position.Y.Scale,tbut.Position.Y.Offset)
&#9;&#9;end)
&#9;&#9;tbut.Button.MouseLeave:connect(function()
&#9;&#9;&#9;tbut.Size = UDim2.new(0,50,0,50)
&#9;&#9;&#9;tbut.Position = UDim2.new(0,-5,tbut.Position.Y.Scale,tbut.Position.Y.Offset)
&#9;&#9;end)
&#9;&#9;tbut.Button.MouseButton1Down:connect(function()
&#9;&#9;&#9;for _, j in pairs(self.Callbacks) do
&#9;&#9;&#9;&#9;j(stampObj)
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;return tbut
&#9;end
&#9;function RecentPartsGui:Open()
&#9;&#9;--self.Gui:TweenPosition
&#9;end
&#9;function RecentPartsGui:Close()
&#9;
&#9;end
&#9;function RecentPartsGui:Invalidate()
&#9;&#9;while #self.Objects&gt;self.NumSaved do
&#9;&#9;&#9;self.Objects[1][2].Parent = nil
&#9;&#9;&#9;table.remove(self.Objects,1)
&#9;&#9;end
&#9;&#9;self.Gui.Size = UDim2.new(0,50,0,50*#self.Objects)
&#9;&#9;for index= 1, #self.Objects, 1 do
&#9;&#9;&#9;local i = self.Objects[#self.Objects-(index-1)]
&#9;&#9;&#9;i[2].Parent = self.Gui
&#9;&#9;&#9;i[2].Position = UDim2.new(0,i[2].Position.X.Offset,0,(i[2].Size.Y.Offset*(index-1))-10)
&#9;&#9;end
&#9;end
&#9;function RecentPartsGui:AddStampObject(stampObj)
&#9;&#9;for index,i in pairs(self.Objects) do
&#9;&#9;&#9;if i[1]==stampObj then
&#9;&#9;&#9;&#9;table.remove(self.Objects,index)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;table.insert(self.Objects,{stampObj,self:MakeRecentIcon(stampObj)})
&#9;&#9;self:Invalidate()
&#9;end
&#9;function RecentPartsGui:OnPartSelected(func)
&#9;&#9;table.insert(self.Callbacks,func)
&#9;end
end
--[[Stamper Main Class]]--
local StamperMain =
{
&#9;Gui = StamperGui.New(UTIL.LocalPlayer.PlayerGui),
&#9;WaterGui = nil,
&#9;RecentPartsGui = RecentPartsGui.New(),
&#9;CurrentObject = nil,
&#9;StampArea = nil,
&#9;Mouse = nil,
&#9;RbxStamper = RBXStamper,
&#9;StampMadeConnect = nil,
&#9;ActiveStampController = nil,
&#9;StampModel =nil,
&#9;RestrictedRegion = nil,
&#9;ObjectCache= {},
&#9;ObjectCacheSize = 1,
&#9;MouseIcons =
&#9;{
&#9;&#9;Normal = 112497861,
&#9;&#9;Stamp = 112497874,
&#9;&#9;Error = 112497904,
&#9;&#9;Hourglass1 =112979217,--112978896,
&#9;&#9;Hourglass2 = 112979274,--112978935,
&#9;},
&#9;WaterAssetId = 82717697,
&#9;LastWaterObject = nil,
}
do
&#9;UTIL.MakeClass(StamperMain)
&#9;function StamperMain:New()
&#9;&#9;local init = {}
&#9;&#9;init = UTIL.Class(init,StamperMain)
&#9;&#9;init.RecentPartsGui.Gui.Parent = init.Gui.ScreenGui
&#9;&#9;init.RecentPartsGui:OnPartSelected(function(stampObj) init:SetStampObject(stampObj,0) end)
&#9;&#9;init.WaterGui= WaterGui.New(init.Gui.ScreenGui)
&#9;&#9;init.WaterGui.WaterForceDropDown:OnButtonSelected(function()
&#9;&#9;&#9;print(&apos;got force changed&apos;) init:SetStampObject(init.LastWaterObject,0) end)
&#9;&#9;init.WaterGui.WaterDirectionDropDown:OnButtonSelected(function()
&#9;&#9;&#9;init:SetStampObject(init.LastWaterObject,0) end)
&#9;&#9;init.Gui:OnObjectSelected(function (i,shape) init:SetStampObject(i,shape) end)
&#9;&#9;init.ObjectCacheSize = 20
&#9;&#9;return init
&#9;end
&#9;function StamperMain:SetMouse(nmouse)
&#9;&#9;self.Mouse = nmouse
&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
&#9;end
&#9;do
&#9;&#9;local FlashId = 0
&#9;&#9;function StamperMain:FlashMouse(nicon,length)
&#9;&#9;&#9;FlashId=FlashId+1
&#9;&#9;&#9;local myflash = FlashId
&#9;&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..nicon
&#9;&#9;&#9;Delay(length,function()
&#9;&#9;&#9;&#9;if FlashId==myflash then
&#9;&#9;&#9;&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end)
&#9;&#9;end
&#9;end
&#9;function StamperMain:SetRestrictedRegion(region)
&#9;&#9;self.RestrictedRegion = region
&#9;end
&#9;function StamperMain:SetStampModel(nmodel)
&#9;&#9;self.StampModel = nmodel
&#9;end
&#9;function StamperMain:OnMouseDown(x,y)
&#9;
&#9;end
&#9;function StamperMain:SetWaterProperties(asset,force,direction)
&#9;&#9;print(&apos;setting props:&apos;..force..&apos; &apos;..direction)
&#9;&#9;if not asset:FindFirstChild(&apos;WaterForceTag&apos;) then
&#9;&#9;&#9;local ntag = Instance.new(&apos;StringValue&apos;)
&#9;&#9;&#9;ntag.Name = &apos;WaterForceTag&apos;
&#9;&#9;&#9;ntag.Value = force
&#9;&#9;&#9;ntag.Parent = asset
&#9;&#9;else
&#9;&#9;&#9;asset.WaterForceTag.Value = force
&#9;&#9;end
&#9;&#9;if not asset:FindFirstChild(&apos;WaterForceDirectionTag&apos;) then
&#9;&#9;&#9;local ntag = Instance.new(&apos;StringValue&apos;)
&#9;&#9;&#9;ntag.Name = &apos;WaterForceDirectionTag&apos;
&#9;&#9;&#9;ntag.Value= direction
&#9;&#9;&#9;ntag.Parent = asset
&#9;&#9;else
&#9;&#9;&#9;asset.WaterForceDirectionTag.Value = direction
&#9;&#9;end
&#9;end
&#9;
&#9;function StamperMain:LoadStampObject(stampObj,terrainShape)
&#9;&#9;
&#9;&#9;--[[WARNING, not using cacheing for terrain yet]]--
&#9;&#9;if tonumber(stampObj.AssetId)==self.WaterAssetId then
&#9;&#9;&#9;print(&apos;Loaded Water obj:&apos;.. stampObj.AssetId)
&#9;&#9;&#9;self.LastWaterObject = stampObj
&#9;&#9;&#9;if not stampObj.Asset then
&#9;&#9;&#9;&#9;stampObj.Asset = self.RbxStamper.GetStampModel(tonumber(stampObj.AssetId), terrainShape)
&#9;&#9;&#9;end
&#9;&#9;&#9;self:SetWaterProperties(stampObj.Asset,self.WaterGui:GetWaterData())
&#9;&#9;elseif terrainShape and terrainShape&gt;=0 then
&#9;&#9;&#9;print(&apos;Loaded terrain obj:&apos;.. stampObj.AssetId)
&#9;&#9;&#9;stampObj.Asset = self.RbxStamper.GetStampModel(tonumber(stampObj.AssetId), terrainShape)
&#9;&#9;elseif not stampObj.Asset then
&#9;&#9;&#9;print(&apos;Loaded model obj:&apos;.. stampObj.AssetId)
&#9;&#9;&#9;stampObj.Asset = UTIL.InsertService:LoadAsset( stampObj.AssetId ):GetChildren()[1]
&#9;&#9;&#9;table.insert(self.ObjectCache, stampObj)
&#9;&#9;&#9;if #self.ObjectCache &gt; self.ObjectCacheSize then
&#9;&#9;&#9;&#9;self.ObjectCache[1].Asset:Remove()
&#9;&#9;&#9;&#9;--[[WARNING, Order n operation, replace with stack]]--
&#9;&#9;&#9;&#9;table.remove(self.ObjectCache,1)
&#9;&#9;&#9;&#9;print(&apos;removed object!&apos;)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;
&#9;function StamperMain:SetStampObject(stampObj,terrainShape)
&#9;&#9;if tonumber(stampObj.AssetId)==self.WaterAssetId then
&#9;&#9;&#9;self.WaterGui.WaterMenu.Visible=true
&#9;&#9;else
&#9;&#9;&#9;self.WaterGui.WaterMenu.Visible=false
&#9;&#9;end
&#9;&#9;
&#9;&#9;self.Gui:RemoveGui()
&#9;&#9;local isLoading = true
&#9;&#9;Spawn(function()
&#9;&#9;&#9;while isLoading do
&#9;&#9;&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Hourglass1
&#9;&#9;&#9;&#9;wait(.4)
&#9;&#9;&#9;&#9;if isLoading then
&#9;&#9;&#9;&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Hourglass2
&#9;&#9;&#9;&#9;&#9;wait(.4)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;self:LoadStampObject(stampObj,terrainShape)
&#9;&#9;isLoading = false
&#9;&#9;self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
&#9;&#9;self.RecentPartsGui:AddStampObject(stampObj)
&#9;&#9;
&#9;&#9;if self.ActiveStampController then self.ActiveStampController.Destroy() end
&#9;&#9;if self.StampMadeConnect then self.StampMadeConnect:disconnect() end
&#9;&#9;
&#9;&#9;
&#9;&#9;local function stampFailed()
&#9;&#9;&#9;self:FlashMouse(self.MouseIcons.Error,.1)
&#9;&#9;end
&#9;&#9;self.ActiveStampController = self.RbxStamper.SetupStamperDragger(stampObj.Asset,self.Mouse,
&#9;&#9;&#9;self.StampModel,self.RestrictedRegion,stampFailed)
&#9;&#9;self.StampMadeConnect = self.ActiveStampController.Stamped.Changed:connect(function()
&#9;&#9;&#9;if self.ActiveStampController.Stamped.Value then
&#9;&#9;&#9;&#9;self:FlashMouse(self.MouseIcons.Stamp,.2)
&#9;&#9;&#9;&#9;self.ActiveStampController.ReloadModel()
&#9;&#9;&#9;end
&#9;&#9;end)
&#9;&#9;
&#9;end
&#9;function StamperMain:Destroy()
&#9;&#9;print(&apos;unconnected!&apos;)
&#9;&#9;if self.ActiveStampController then self.ActiveStampController.Destroy() end
&#9;&#9;if self.StampMadeConnect then self.StampMadeConnect:disconnect() end
&#9;&#9;self.ActiveStampController=nil
&#9;&#9;self.StampMadeConnect = nil
&#9;&#9;self.Gui.ScreenGui.Parent = nil
&#9;end
end
------------------
--[[Tool Logic]]--
------------------
do
&#9;--[[Tool Related Varibles]]--
&#9;local MyStamper = StamperMain.New()
&#9;local Tool = script.Parent
&#9;local Sets = script:WaitForChild(&apos;Sets&apos;)
&#9;local Users = script:WaitForChild(&apos;Users&apos;)
&#9;local MyStampModel = script:WaitForChild(&apos;MyStampModel&apos;)
&#9;local RestrictedArea = script:WaitForChild(&apos;RestrictedArea&apos;)
&#9;local MaxObjects = script:FindFirstChild(&apos;MaxObjects&apos;)
&#9;--[[Tool Related functions]]--
&#9;local function InitToolSets()
&#9;&#9;print(&apos;finished adding sets&apos;)
&#9;&#9;for _, i in pairs(Users:GetChildren()) do
&#9;&#9;&#9;MyStamper.Gui:AddUserSets(i.Value)
&#9;&#9;end
&#9;&#9;print(&apos;in init tool sets&apos;)
&#9;&#9;for _, i in pairs(Sets:GetChildren()) do
&#9;&#9;&#9;print(&apos;In sets&apos;)
&#9;&#9;&#9;if i:IsA(&apos;IntValue&apos;) then
&#9;&#9;&#9;&#9;MyStamper.Gui:AddSet(i.Value)
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;print(&apos;Adding &apos; .. i.Name)
&#9;&#9;&#9;&#9;MyStamper.Gui:AddSet(i)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;end
&#9;local function InitMyStampModel()
&#9;&#9;UTIL.RetroRegister(function() MyStamper:SetStampModel(MyStampModel.Value) end, MyStampModel.Changed)
&#9;end
&#9;local function InitRestrictedRegion()
&#9;&#9;local function pushUpdate()
&#9;&#9;&#9;if RestrictedArea:FindFirstChild(&apos;Low&apos;) and RestrictedArea:FindFirstChild(&apos;High&apos;) then
&#9;&#9;&#9;&#9;MyStamper:SetRestrictedRegion(Region3.new(RestrictedArea.Low.Value,RestrictedArea.High.Value))
&#9;&#9;&#9;else
&#9;&#9;&#9;&#9;MyStamper:SetRestrictedRegion(nil)
&#9;&#9;&#9;end
&#9;&#9;end
&#9;&#9;UTIL.RetroRegister(pushUpdate,RestrictedArea.ChildAdded,RestrictedArea.ChildRemoved)
&#9;end
&#9;--[[Running Logic]]--
&#9;InitMyStampModel()
&#9;InitRestrictedRegion()
&#9;Spawn(InitToolSets)
&#9;Tool.Equipped:connect(
&#9;&#9;function(nmouse)
&#9;&#9;&#9;local MyPlayer = Game.Players:GetPlayerFromCharacter(Tool.Parent)
&#9;&#9;&#9;if not MyPlayer then return end
&#9;&#9;&#9;MyStamper:SetMouse(nmouse)
&#9;&#9;&#9;--[[SLIGHT HACK]]--
&#9;&#9;&#9;nmouse.KeyDown:connect(function(key)
&#9;&#9;&#9;&#9;if key ==&apos;q&apos; then
&#9;&#9;&#9;&#9;&#9;MyStamper.Gui:ToggleGui(UTIL.LocalPlayer.PlayerGui)
&#9;&#9;&#9;&#9;end
&#9;&#9;&#9;end)
&#9;&#9;&#9;--[[END SLIGHT HACK]]--
&#9;&#9;&#9;MyStamper.Gui:AddGui(MyPlayer.PlayerGui)
&#9;&#9;&#9;if #MyStamper.RecentPartsGui.Objects&gt;0 then
&#9;&#9;&#9;&#9;MyStamper:SetStampObject(MyStamper.RecentPartsGui.Objects[#MyStamper.RecentPartsGui.Objects][1])
&#9;&#9;&#9;end
&#9;&#9;end
&#9;)
&#9;Tool.Unequipped:connect(
&#9;&#9;function()
&#9;&#9;&#9;print(&apos;in unequip&apos;)
&#9;&#9;&#9;if not MyStamper then return end
&#9;&#9;&#9;print(&apos;past check&apos;)
&#9;&#9;&#9;MyStamper:Destroy()
&#9;&#9;&#9;print(&apos;past destory&apos;)
&#9;&#9;end
&#9;)
end
</ProtectedString>
</Properties>
<Item class="Model" referent="RBX4">
<Properties>
<CoordinateFrame name="ModelInPrimary">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<string name="Name">Sets</string>
<Ref name="PrimaryPart">null</Ref>
</Properties>
</Item>
<Item class="ObjectValue" referent="RBX5">
<Properties>
<string name="Name">MyStampModel</string>
<Ref name="Value">null</Ref>
</Properties>
</Item>
<Item class="Model" referent="RBX6">
<Properties>
<CoordinateFrame name="ModelInPrimary">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<string name="Name">RestrictedArea</string>
<Ref name="PrimaryPart">null</Ref>
</Properties>
</Item>
<Item class="Model" referent="RBX7">
<Properties>
<CoordinateFrame name="ModelInPrimary">
<X>0</X>
<Y>0</Y>
<Z>0</Z>
<R00>1</R00>
<R01>0</R01>
<R02>0</R02>
<R10>0</R10>
<R11>1</R11>
<R12>0</R12>
<R20>0</R20>
<R21>0</R21>
<R22>1</R22>
</CoordinateFrame>
<string name="Name">Users</string>
<Ref name="PrimaryPart">null</Ref>
</Properties>
<Item class="IntValue" referent="RBX8">
<Properties>
<string name="Name">Default1</string>
<int name="Value">11744447</int>
</Properties>
</Item>
<Item class="IntValue" referent="RBX9">
<Properties>
<string name="Name">Default2</string>
<int name="Value">18881789</int>
</Properties>
</Item>
<Item class="IntValue" referent="RBX10">
<Properties>
<string name="Name">Default3</string>
<int name="Value">18881808</int>
</Properties>
</Item>
<Item class="IntValue" referent="RBX11">
<Properties>
<string name="Name">nonRestrict1</string>
<int name="Value">18881829</int>
</Properties>
</Item>
<Item class="IntValue" referent="RBX12">
<Properties>
<string name="Name">nonRestrict2</string>
<int name="Value">18881853</int>
</Properties>
</Item>
<Item class="IntValue" referent="RBX13">
<Properties>
<string name="Name">nonRestrict3</string>
<int name="Value">18881866</int>
</Properties>
</Item>
</Item>
<Item class="LocalScript" referent="RBX14">
<Properties>
<bool name="Disabled">false</bool>
<Content name="LinkedSource"><null></null></Content>
<string name="Name">README</string>
<ProtectedString name="Source">--[[
&#9;New Stamper
&#9;UpdateLog:
&#9;&#9;4/16/2013-initial release
&#9;&#9;
&#9;How to add user sets:
&#9;&#9;add user IDs as intValue&apos;s in the Users model to add all sets related to that user
&#9;How to add induvidual sets:
&#9;&#9;add set IDs as intValue&apos;s in the Sets model
&#9;How to make custom sets:
&#9;&#9;add models under the Sets model, containing intValue&apos;s with asset IDs
&#9;How to limit stamping area:
&#9;&#9;inside the RestrictedArea model, add 2 Vector3Value&apos;s named &quot;High&quot; and &quot;Low&quot;, these will form the Region3 this tool can stamp in
&#9;How to Stamp inside a model:
&#9;&#9;Set the value of MyStampModel to the object you want to place stamped models inside
&#9;
&#9;Contact fusroblox for any and all issues relating to Stamper&#9;
--]]</ProtectedString>
</Properties>
</Item>
</Item>
</Item>
</roblox>