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