· 5 years ago · Mar 21, 2020, 11:58 PM
1
2function sandbox(var,func)
3 local env = getfenv(func)
4 local newenv = setmetatable({},{
5 __index = function(self,k)
6 if k=="script" then
7 return var
8 else
9 return env[k]
10 end
11 end,
12 })
13 setfenv(func,newenv)
14 return func
15end
16cors = {}
17mas = Instance.new("Model",game:GetService("Lighting"))
18Tool0 = Instance.new("Tool")
19Part1 = Instance.new("Part")
20SpecialMesh2 = Instance.new("SpecialMesh")
21LocalScript3 = Instance.new("LocalScript")
22Model4 = Instance.new("Model")
23ObjectValue5 = Instance.new("ObjectValue")
24Model6 = Instance.new("Model")
25Model7 = Instance.new("Model")
26IntValue8 = Instance.new("IntValue")
27IntValue9 = Instance.new("IntValue")
28IntValue10 = Instance.new("IntValue")
29IntValue11 = Instance.new("IntValue")
30IntValue12 = Instance.new("IntValue")
31IntValue13 = Instance.new("IntValue")
32LocalScript14 = Instance.new("LocalScript")
33Tool0.Name = "Stamper 2.0"
34Tool0.Parent = mas
35Tool0.TextureId = "http://www.roblox.com/asset/?id=73241762"
36Tool0.Grip = CFrame.new(0, -0.100000001, 0.100000001, 1, 0, 0, 0, -4.37113883e-08, -1, 0, 1, -4.37113883e-08)
37Tool0.GripForward = Vector3.new(-0, 1, 4.37113883e-08)
38Tool0.GripPos = Vector3.new(0, -0.100000001, 0.100000001)
39Tool0.GripUp = Vector3.new(0, -4.37113883e-08, 1)
40Part1.Name = "Handle"
41Part1.Parent = Tool0
42Part1.CFrame = CFrame.new(-99.9999847, 1.19999075, 48, 1, -9.94300535e-06, -1.1920929e-07, -9.94131278e-06, -1.00000048, -1.70530257e-13, 5.96046448e-08, 4.48835635e-10, -1.00000036)
43Part1.Orientation = Vector3.new(0, -180, -180)
44Part1.Position = Vector3.new(-99.9999847, 1.19999075, 48)
45Part1.Rotation = Vector3.new(180, 0, 0)
46Part1.Size = Vector3.new(2, 2.4000001, 2)
47SpecialMesh2.Parent = Part1
48SpecialMesh2.MeshId = "http://www.roblox.com/asset/?id=42163552"
49SpecialMesh2.Scale = Vector3.new(0.899999976, 0.899999976, 0.899999976)
50SpecialMesh2.TextureId = "http://www.roblox.com/asset/?id=42163513"
51SpecialMesh2.MeshType = Enum.MeshType.FileMesh
52LocalScript3.Name = "nstamperScript"
53LocalScript3.Parent = Tool0
54table.insert(cors,sandbox(LocalScript3,function()
55-----------------------
56--[[RBXStamper code]]--
57-----------------------
58--consiter exporting to own file later
59local RBXStamper = {}
60
61do
62 -- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
63 --
64 -- vectorPos - End point of the line.
65 --
66 -- Return:
67 -- cellPos - The terrain cell intersection point if there is one, vectorPos if there isn't.
68 -- hit - Whether there was a plane intersection. Value is true if there was, false if not.
69 local function PlaneIntersection(vectorPos)
70 local hit = false
71 local currCamera = game.Workspace.CurrentCamera
72 local startPos = Vector3.new(currCamera.CoordinateFrame.p.X, currCamera.CoordinateFrame.p.Y, currCamera.CoordinateFrame.p.Z)
73 local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
74 local normal = Vector3.new(0, 1, 0)
75 local p3 = Vector3.new(0, 0, 0)
76 local startEndDot = normal:Dot(endPos - startPos)
77 local cellPos = vectorPos
78 if startEndDot ~= 0 then
79 local t = normal:Dot(p3 - startPos) / startEndDot
80 if(t >=0 and t <=1) then
81 local intersection = ((endPos - startPos) * t) + startPos
82 cellPos = game.Workspace.Terrain:WorldToCell(intersection)
83 hit = true
84 end
85 end
86
87 return cellPos, hit
88 end
89
90
91 -- Purpose:
92 -- Checks for terrain touched by the mouse hit.
93 -- Will do a plane intersection if no terrain is touched.
94 --
95 -- mouse - Mouse to check the .hit for.
96 --
97 -- Return:
98 -- cellPos - Cell position hit. Nil if none.
99 local function GetTerrainForMouse(mouse)
100 -- There was no target, so all it could be is a plane intersection.
101 -- Check for a plane intersection. If there isn't one then nothing will get hit.
102 local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
103 local planeLoc = nil
104 -- If nothing was hit, do the plane intersection.
105 if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then
106 cell = nil
107 planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z))
108 if hit then
109 cell = planeLoc
110 end
111 end
112 return cell
113 end
114
115 -- setup helper functions
116 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
117
118 -- rotates a model by yAngle radians about the global y-axis
119 local function rotatePartAndChildren(part, rotCF, offsetFromOrigin)
120 -- rotate this thing, if it's a part
121 if part:IsA("BasePart") then
122 part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin
123 end
124
125 -- recursively do the same to all children
126 local partChildren = part:GetChildren()
127 for c = 1, #partChildren do rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin) end
128 end
129
130 local function modelRotate(model, yAngle)
131 local rotCF = CFrame.Angles(0, yAngle, 0)
132 local offsetFromOrigin = model:GetModelCFrame().p
133
134 rotatePartAndChildren(model, rotCF, offsetFromOrigin)
135 end
136
137
138 local function collectParts(object, baseParts, scripts, decals)
139 if object:IsA("BasePart") then
140 baseParts[#baseParts+1] = object
141 elseif object:IsA("Script") then
142 scripts[#scripts+1] = object
143 elseif object:IsA("Decal") then
144 decals[#decals+1] = object
145 end
146
147 for index,child in pairs(object:GetChildren()) do
148 collectParts(child, baseParts, scripts, decals)
149 end
150 end
151
152 local function clusterPartsInRegion(startVector, endVector)
153 local cluster = game.Workspace:FindFirstChild("Terrain")
154
155 local startCell = cluster:WorldToCell(startVector)
156 local endCell = cluster:WorldToCell(endVector)
157
158 local startX = startCell.X
159 local startY = startCell.Y
160 local startZ = startCell.Z
161
162 local endX = endCell.X
163 local endY = endCell.Y
164 local endZ = endCell.Z
165
166 if startX < cluster.MaxExtents.Min.X then startX = cluster.MaxExtents.Min.X end
167 if startY < cluster.MaxExtents.Min.Y then startY = cluster.MaxExtents.Min.Y end
168 if startZ < cluster.MaxExtents.Min.Z then startZ = cluster.MaxExtents.Min.Z end
169
170 if endX > cluster.MaxExtents.Max.X then endX = cluster.MaxExtents.Max.X end
171 if endY > cluster.MaxExtents.Max.Y then endY = cluster.MaxExtents.Max.Y end
172 if endZ > cluster.MaxExtents.Max.Z then endZ = cluster.MaxExtents.Max.Z end
173
174 for x = startX, endX do
175 for y = startY, endY do
176 for z = startZ, endZ do
177 if (cluster:GetCell(x, y, z).Value) > 0 then return true end
178 end
179 end
180 end
181
182 return false
183 end
184
185 local function findSeatsInModel(parent, seatTable)
186 if not parent then return end
187
188 if parent.className == "Seat" or parent.className == "VehicleSeat" then
189 table.insert(seatTable, parent)
190 end
191 local myChildren = parent:GetChildren()
192 for j = 1, #myChildren do
193 findSeatsInModel(myChildren[j], seatTable)
194 end
195 end
196
197 local function setSeatEnabledStatus(model, isEnabled)
198 local seatList = {}
199 findSeatsInModel(model, seatList)
200
201 if isEnabled then
202 -- remove any welds called "SeatWeld" in seats
203 for i = 1, #seatList do
204 local nextSeat = seatList[i]:FindFirstChild("SeatWeld")
205 while nextSeat do nextSeat:Remove() nextSeat = seatList[i]:FindFirstChild("SeatWeld") end
206 end
207 else
208 -- put a weld called "SeatWeld" in every seat
209 -- this tricks it into thinking there's already someone sitting there, and it won't make you sit XD
210 for i = 1, #seatList do
211 local fakeWeld = Instance.new("Weld")
212 fakeWeld.Name = "SeatWeld"
213 fakeWeld.Parent = seatList[i]
214 end
215 end
216 end
217
218 local function autoAlignToFace(parts)
219 local aatf = parts:FindFirstChild("AutoAlignToFace")
220 if aatf then return aatf.Value else return false end
221 end
222
223 local function getClosestAlignedWorldDirection(aVector3InWorld)
224 local xDir = Vector3.new(1,0,0)
225 local yDir = Vector3.new(0,1,0)
226 local zDir = Vector3.new(0,0,1)
227 local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z
228 local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z
229 local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z
230
231 if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs(zDot) then
232 if xDot > 0 then
233 return 0
234 else
235 return 3
236 end
237 elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs(zDot) then
238 if yDot > 0 then
239 return 1
240 else
241 return 4
242 end
243 else
244 if zDot > 0 then
245 return 2
246 else
247 return 5
248 end
249 end
250 end
251
252 local function positionPartsAtCFrame3(aCFrame, currentParts)
253 local insertCFrame = nil
254 if not currentParts then return currentParts end
255 if currentParts and (currentParts:IsA("Model") or currentParts:IsA("Tool")) then
256 insertCFrame = currentParts:GetModelCFrame()
257 currentParts:TranslateBy(aCFrame.p - insertCFrame.p)
258 else
259 currentParts.CFrame = aCFrame
260 end
261 return currentParts
262 end
263
264 local function calcRayHitTime(rayStart, raySlope, intersectionPlane)
265 if math.abs(raySlope) < .01 then return 0 end -- 0 slope --> we just say intersection time is 0, and sidestep this dimension
266 return (intersectionPlane - rayStart) / raySlope
267 end
268
269 local function modelTargetSurface(partOrModel, rayStart, rayEnd)
270 if not partOrModel then
271 return 0
272 end
273
274 local modelCFrame = nil
275 local modelSize = nil
276 if partOrModel:IsA("Model") then
277 modelCFrame = partOrModel:GetModelCFrame()
278 modelSize = partOrModel:GetModelSize()
279 else
280 modelCFrame = partOrModel.CFrame
281 modelSize = partOrModel.Size
282 end
283
284 local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart)
285 local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd)
286 local mouseSlope = mouseRayEnd - mouseRayStart
287
288 local xPositive = 1
289 local yPositive = 1
290 local zPositive = 1
291 if mouseSlope.X > 0 then xPositive = -1 end
292 if mouseSlope.Y > 0 then yPositive = -1 end
293 if mouseSlope.Z > 0 then zPositive = -1 end
294
295 -- find which surface the transformed mouse ray hits (using modelSize):
296 local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X/2 * xPositive)
297 local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y/2 * yPositive)
298 local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z/2 * zPositive)
299
300 local hitFace = 0
301
302 --if xHitTime >= 0 and yHitTime >= 0 and zHitTime >= 0 then
303 if xHitTime > yHitTime then
304 if xHitTime > zHitTime then
305 -- xFace is hit
306 hitFace = 1*xPositive
307 else
308 -- zFace is hit
309 hitFace = 3*zPositive
310 end
311 else
312 if yHitTime > zHitTime then
313 -- yFace is hit
314 hitFace = 2*yPositive
315 else
316 -- zFace is hit
317 hitFace = 3*zPositive
318 end
319 end
320
321 return hitFace
322 end
323
324 local function getBoundingBox2(partOrModel)
325
326 -- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes
327 -- relative to the first part's coordinate frame.
328 local minVec = Vector3.new(math.huge, math.huge, math.huge)
329 local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
330
331 if partOrModel:IsA("Terrain") then
332 minVec = Vector3.new(-2, -2, -2)
333 maxVec = Vector3.new(2, 2, 2)
334 elseif partOrModel:IsA("BasePart") then
335 minVec = -0.5 * partOrModel.Size
336 maxVec = -minVec
337 else
338 maxVec = partOrModel:GetModelSize()*0.5
339 minVec = -maxVec
340 end
341
342 -- Adjust bounding box to reflect what the model or part author wants in terms of justification
343 local justifyValue = partOrModel:FindFirstChild("Justification")
344 if justifyValue ~= nil then
345 -- find the multiple of 4 that contains the model
346 justify = justifyValue.Value
347 two = Vector3.new(2, 2, 2)
348 actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01)
349 containingGridBox = Vector3.new(4 * math.ceil(actualBox.x/4), 4 * math.ceil(actualBox.y/4), 4 * math.ceil(actualBox.z/4))
350 adjustment = containingGridBox - actualBox
351 minVec = minVec - 0.5 * adjustment * justify
352 maxVec = maxVec + 0.5 * adjustment * (two - justify)
353 end
354
355 return minVec, maxVec
356 end
357
358 local function getBoundingBoxInWorldCoordinates(partOrModel)
359 local minVec = Vector3.new(math.huge, math.huge, math.huge)
360 local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
361
362 if partOrModel:IsA("BasePart") and not partOrModel:IsA("Terrain") then
363 vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size)
364 vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size)
365 minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
366 maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
367 elseif partOrModel:IsA("Terrain") then
368 -- we shouldn't have to deal with this case
369 --minVec = Vector3.new(-2, -2, -2)
370 --maxVec = Vector3.new(2, 2, 2)
371 else
372 vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize())
373 vec2 = partOrModel:GetModelCFrame():pointToWorldSpace(0.5 * partOrModel:GetModelSize())
374 minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
375 maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
376 end
377
378 return minVec, maxVec
379 end
380
381 local function getTargetPartBoundingBox(targetPart)
382 if targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
383 return getBoundingBox2(targetPart.Parent)
384 else
385 return getBoundingBox2(targetPart)
386 end
387 end
388
389 local function getMouseTargetCFrame(targetPart)
390 if targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
391 if targetPart.Parent:IsA("Tool") then return targetPart.Parent.Handle.CFrame
392 else return targetPart.Parent:GetModelCFrame() end
393 else
394 return targetPart.CFrame
395 end
396 end
397
398 local function isBlocker(part) -- returns whether or not we want to cancel the stamp because we're blocked by this part
399 if not part then return false end
400 if not part.Parent then return false end
401 if part:FindFirstChild("Humanoid") then return false end
402 if part:FindFirstChild("RobloxStamper") or part:FindFirstChild("RobloxModel") then return true end
403 if part:IsA("Part") and not part.CanCollide then return false end
404 if part == game.Lighting then return false end
405 return isBlocker(part.Parent)
406 end
407
408 -- helper function to determine if a character can be pushed upwards by a certain amount
409 -- 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
410 local function spaceAboveCharacter(charTorso, newTorsoY, stampData)
411 local partsAboveChar = game.Workspace:FindPartsInRegion3(
412 Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
413 Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)),
414 charTorso.Parent,
415 100)
416
417 for j = 1, #partsAboveChar do
418 if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(stampData.CurrentParts) then return false end
419 end
420
421 if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75),
422 Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)) then
423 return false
424 end
425
426 return true
427 end
428
429
430 local function findConfigAtMouseTarget(Mouse, stampData)
431 -- *Critical Assumption* :
432 -- This function assumes the target CF axes are orthogonal with the target bounding box faces
433 -- And, it assumes the insert CF axes are orthongonal with the insert bounding box faces
434 -- Therefore, insertion will not work with angled faces on wedges or other "non-block" parts, nor
435 -- will it work for parts in a model that are not orthogonally aligned with the model's CF.
436
437 if not Mouse then return nil end -- This can happen sometimes, return if so
438 if not stampData then error("findConfigAtMouseTarget: stampData is nil") return nil end
439 if not stampData["CurrentParts"] then return nil end
440
441 local grid = 4.0
442 local admissibleConfig = false
443 local targetConfig = CFrame.new(0,0,0)
444
445 local minBB, maxBB = getBoundingBox2(stampData.CurrentParts)
446 local diagBB = maxBB - minBB
447
448 local insertCFrame
449 if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
450 insertCFrame = stampData.CurrentParts:GetModelCFrame()
451 else
452 insertCFrame = stampData.CurrentParts.CFrame
453 end
454
455 if Mouse then
456 if stampData.CurrentParts:IsA("Tool") then
457 Mouse.TargetFilter = stampData.CurrentParts.Handle
458 else
459 Mouse.TargetFilter = stampData.CurrentParts
460 end
461 end
462
463 local hitPlane = false
464 local targetPart = nil
465 local success = pcall(function() targetPart = Mouse.Target end)
466
467 if not success then-- or targetPart == nil then
468 return admissibleConfig, targetConfig
469 end
470
471 local mouseHitInWorld = Vector3.new(0, 0, 0)
472 if Mouse then
473 mouseHitInWorld = Vector3.new(Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z)
474 end
475
476 local cellPos = nil
477
478 -- Nothing was hit, so check for the default plane.
479 if nil == targetPart then
480 cellPos = GetTerrainForMouse(Mouse)
481 if nil == cellPos then
482 hitPlane = false
483 return admissibleConfig, targetConfig
484 else
485 targetPart = game.Workspace.Terrain
486 hitPlane = true
487 -- Take into account error that will occur.
488 cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z)
489 mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z)
490 end
491 end
492
493 -- test mouse hit location
494 local minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart)
495 local diagBBTarget = maxBBTarget - minBBTarget
496 local targetCFrame = getMouseTargetCFrame(targetPart)
497
498 if targetPart:IsA("Terrain") then
499 if not cluster then cluster = game.Workspace:FindFirstChild("Terrain") end
500 local cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld)
501 if hitPlane then
502 cellID = cellPos
503 end
504
505 targetCFrame = CFrame.new(game.Workspace.Terrain:CellCenterToWorld(cellID.x, cellID.y, cellID.z))
506 end
507
508 local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld)
509 local targetVectorInWorld = Vector3.new(0,0,0)
510 if Mouse then
511 -- DON'T WANT THIS IN TERMS OF THE MODEL CFRAME! (.TargetSurface is in terms of the part CFrame, so this would break, right? [HotThoth])
512 -- (ideally, we would want to make the Mouse.TargetSurface a model-targetsurface instead, but for testing will be using the converse)
513 --targetVectorInWorld = targetCFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface))
514 targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) -- better, but model cframe would be best
515 --[[if targetPart.Parent:IsA("Model") then
516 local hitFace = modelTargetSurface(targetPart.Parent, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) -- best, if you get it right
517 local WORLD_AXES = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
518 if hitFace > 0 then
519 targetVectorInWorld = targetCFrame:vectorToWorldSpace(WORLD_AXES[hitFace])
520 elseif hitFace < 0 then
521 targetVectorInWorld = targetCFrame:vectorToWorldSpace(-WORLD_AXES[-hitFace])
522 end
523 end]]
524 end
525
526 local targetRefPointInTarget
527 local clampToSurface
528
529 if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then
530 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
531 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
532 clampToSurface = Vector3.new(0,1,1)
533 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then
534 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
535 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
536 clampToSurface = Vector3.new(0,1,1)
537 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then
538 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
539 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
540 clampToSurface = Vector3.new(1,0,1)
541 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then
542 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
543 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
544 clampToSurface = Vector3.new(1,0,1)
545 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then
546 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
547 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
548 clampToSurface = Vector3.new(1,1,0)
549 else
550 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
551 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
552 clampToSurface = Vector3.new(1,1,0)
553 end
554
555 targetRefPointInTarget = targetRefPointInTarget * (0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget)
556 insertRefPointInInsert = insertRefPointInInsert * (0.5 * diagBB) + 0.5 * (maxBB + minBB)
557
558 -- To Do: For cases that are not aligned to the world grid, account for the minimal rotation
559 -- needed to bring the Insert part(s) into alignment with the Target Part
560 -- Apply the rotation here
561
562 local delta = mouseHitInTarget - targetRefPointInTarget
563 local deltaClamped = Vector3.new(grid * math.modf(delta.x/grid), grid * math.modf(delta.y/grid), grid * math.modf(delta.z/grid))
564 deltaClamped = deltaClamped * clampToSurface
565 local targetTouchInTarget = deltaClamped + targetRefPointInTarget
566
567 local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget)
568 local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert)
569 local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld
570
571 local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components()
572 targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
573 admissibleConfig = true
574
575 return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld)
576 end
577
578 local function truncateToCircleEighth(bigValue, littleValue)
579 local big = math.abs(bigValue)
580 local little = math.abs(littleValue)
581 local hypotenuse = math.sqrt(big*big + little*little)
582 local frac = little / hypotenuse
583
584 local bigSign = 1
585 local littleSign = 1
586 if bigValue < 0 then bigSign = -1 end
587 if littleValue < 0 then littleSign = -1 end
588
589 if frac > .382683432 then
590 -- between 22.5 and 45 degrees, so truncate to 45-degree tilt
591 return .707106781 * hypotenuse * bigSign, .707106781 * hypotenuse * littleSign
592 else
593 -- between 0 and 22.5 degrees, so truncate to 0-degree tilt
594 return hypotenuse * bigSign, 0
595 end
596 end
597
598
599 local function saveTheWelds(object, manualWeldTable, manualWeldParentTable)
600 if object:IsA("ManualWeld") or object:IsA("Rotate") then
601 table.insert(manualWeldTable, object)
602 table.insert(manualWeldParentTable, object.Parent)
603 else
604 local children = object:GetChildren()
605 for i = 1, #children do
606 saveTheWelds(children[i], manualWeldTable, manualWeldParentTable)
607 end
608 end
609 end
610
611 local function restoreTheWelds(manualWeldTable, manualWeldParentTable)
612 for i = 1, #manualWeldTable do
613 manualWeldTable[i].Parent = manualWeldParentTable[i]
614 end
615 end
616
617 RBXStamper.CanEditRegion = function(partOrModel, EditRegion) -- todo: use model and stamper metadata
618 if not EditRegion then return true, false end
619
620 local minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel)
621
622 if minBB.X < EditRegion.CFrame.p.X - EditRegion.Size.X/2 or
623 minBB.Y < EditRegion.CFrame.p.Y - EditRegion.Size.Y/2 or
624 minBB.Z < EditRegion.CFrame.p.Z - EditRegion.Size.Z/2 then
625 return false, false
626 end
627
628 if maxBB.X > EditRegion.CFrame.p.X + EditRegion.Size.X/2 or
629 maxBB.Y > EditRegion.CFrame.p.Y + EditRegion.Size.Y/2 or
630 maxBB.Z > EditRegion.CFrame.p.Z + EditRegion.Size.Z/2 then
631 return false, false
632 end
633
634 return true, false
635 end
636
637 RBXStamper.GetStampModel = function(assetId, terrainShape, useAssetVersionId)
638 if assetId == 0 then
639 return nil, "No Asset"
640 end
641 if assetId < 0 then
642 return nil, "Negative Asset"
643 end
644
645 local function UnlockInstances(object)
646 if object:IsA("BasePart") then
647 object.Locked = false
648 end
649 for index,child in pairs(object:GetChildren()) do
650 UnlockInstances(child)
651 end
652 end
653
654 local TerrainColorTable =
655 {
656 BrickColor.new("Bright green"),
657 BrickColor.new("Bright yellow"),
658 BrickColor.new("Bright red"),
659 BrickColor.new("Sand red"),
660 BrickColor.new("Black"),
661 BrickColor.new("Dark stone grey"),
662 BrickColor.new("Sand blue"),
663 BrickColor.new("Deep orange"),
664 BrickColor.new("Dark orange"),
665 BrickColor.new("Reddish brown"),
666 BrickColor.new("Light orange"),
667 BrickColor.new("Light stone grey"),
668 BrickColor.new("Sand green"),
669 BrickColor.new("Medium stone grey"),
670 BrickColor.new("Really red"),
671 BrickColor.new("Really blue"),
672 BrickColor.new("Bright blue"),
673 }
674 local function getClosestColorToTerrainMaterial(terrainValue)
675 if TerrainColorTable[terrainValue] then
676 return TerrainColorTable[terrainValue]
677 else
678 return BrickColor.new("Bright green")
679 end
680 end
681
682 local function setupFakeTerrainPart(cellMat, cellType, cellOrient)
683 local newTerrainPiece = nil
684 if (cellType == 1 or cellType == 4) then newTerrainPiece = Instance.new("WedgePart") newTerrainPiece.formFactor = "Custom"
685 elseif (cellType == 2) then newTerrainPiece = Instance.new("CornerWedgePart")
686 else newTerrainPiece = Instance.new("Part") newTerrainPiece.formFactor = "Custom" end
687 newTerrainPiece.Name = "MegaClusterCube"
688 newTerrainPiece.Size = Vector3.new(4, 4, 4)
689 newTerrainPiece.BottomSurface = "Smooth"
690 newTerrainPiece.TopSurface = "Smooth"
691
692 -- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors
693 newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat)
694
695 local sideways = 0
696 local flipped = math.pi
697 if cellType == 4 then sideways = -math.pi/2 end
698 if cellType == 2 or cellType == 3 then flipped = 0 end
699 newTerrainPiece.CFrame = CFrame.Angles(0, math.pi/2*cellOrient + flipped, sideways)
700
701 if cellType == 3 then
702 local inverseCornerWedgeMesh = Instance.new("SpecialMesh")
703 inverseCornerWedgeMesh.MeshType = "FileMesh"
704 inverseCornerWedgeMesh.MeshId = "http://www.roblox.com/asset?id=66832495"
705 inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2)
706 inverseCornerWedgeMesh.Parent = newTerrainPiece
707 end
708
709 local materialTag = Instance.new("Vector3Value")
710 materialTag.Value = Vector3.new(cellMat, cellType, cellOrient)
711 materialTag.Name = "ClusterMaterial"
712 materialTag.Parent = newTerrainPiece
713
714 return newTerrainPiece
715 end
716
717 -- This call will cause a "wait" until the data comes back
718 -- below we wait a max of 8 seconds before deciding to bail out on loading
719 local root
720 local loader
721 loading = true
722 if useAssetVersionId then
723 loader = coroutine.create(function()
724 root = game:GetService("InsertService"):LoadAssetVersion(assetId)
725 loading = false
726 end)
727 coroutine.resume(loader)
728 else
729 loader = coroutine.create(function()
730 root = game:GetService("InsertService"):LoadAsset(assetId)
731 loading = false
732 end)
733 coroutine.resume(loader)
734 end
735
736 local lastGameTime = 0
737 local totalTime = 0
738 local maxWait = 8
739 while loading and totalTime < maxWait do
740 lastGameTime = tick()
741 wait(1)
742 totalTime = totalTime + tick() - lastGameTime
743 end
744 loading = false
745
746 if totalTime >= maxWait then
747 return nil, "Load Time Fail"
748 end
749
750
751 if root == nil then
752 return nil, "Load Asset Fail"
753 end
754
755 if not root:IsA("Model") then
756 return nil, "Load Type Fail"
757 end
758
759 local instances = root:GetChildren()
760 if #instances == 0 then
761 return nil, "Empty Model Fail"
762 end
763
764 --Unlock all parts that are inserted, to make sure they are editable
765 UnlockInstances(root)
766
767 --Continue the insert process
768 root = root:GetChildren()[1]
769
770 --Examine the contents and decide what it looks like
771 for pos, instance in pairs(instances) do
772 if instance:IsA("Team") then
773 instance.Parent = game:GetService("Teams")
774 elseif instance:IsA("Sky") then
775 local lightingService = game:GetService("Lighting")
776 for index,child in pairs(lightingService:GetChildren()) do
777 if child:IsA("Sky") then
778 child:Remove();
779 end
780 end
781 instance.Parent = lightingService
782 return
783 end
784 end
785
786 -- ...and tag all inserted models for subsequent origin identification
787 -- if no RobloxModel tag already exists, then add it.
788 if root:FindFirstChild("RobloxModel") == nil then
789 local stringTag = Instance.new("BoolValue", root)
790 stringTag.Name = "RobloxModel"
791
792 if root:FindFirstChild("RobloxStamper") == nil then
793 local stringTag2 = Instance.new("BoolValue", root)
794 stringTag2.Name = "RobloxStamper"
795 end
796 end
797
798 if terrainShape then
799 if root.Name == "MegaClusterCube" then
800 if (terrainShape == 6) then -- insert an autowedging tag
801 local autowedgeTag = Instance.new("BoolValue")
802 autowedgeTag.Name = "AutoWedge"
803 autowedgeTag.Parent = root
804 else
805 local clusterTag = root:FindFirstChild("ClusterMaterial")
806 if clusterTag then
807 if clusterTag:IsA("Vector3Value") then
808 root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z)
809 else
810 root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0)
811 end
812 else
813 root = setupFakeTerrainPart(1, terrainShape, 0)
814 end
815 end
816 end
817 end
818
819 return root
820 end
821
822
823
824 RBXStamper.SetupStamperDragger = function(modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc)
825 wait(0)
826 if not modelToStamp then
827 error("SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model")
828 return nil
829 end
830 if not modelToStamp:IsA("Model") and not modelToStamp:IsA("BasePart") then
831 error("SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!")
832 return nil
833 end
834 if not Mouse then
835 error("SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object")
836 return nil
837 end
838 if not Mouse:IsA("Mouse") then
839 error("SetupStamperDragger: Mouse (second arg) is not of type Mouse!")
840 return nil
841 end
842
843 local stampInModel = nil
844 local allowedStampRegion = nil
845 local stampFailedFunc = nil
846 if StampInModel then
847 if not StampInModel:IsA("Model") then
848 error("SetupStamperDragger: StampInModel (optional third arg) is not of type 'Model'")
849 return nil
850 end
851 if not AllowedStampRegion then
852 error("SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined")
853 return nil
854 end
855 stampFailedFunc = StampFailedFunc
856 stampInModel = StampInModel
857 allowedStampRegion = AllowedStampRegion
858 end
859
860 -- Init all state variables
861 local gInitial90DegreeRotations = 0
862 local stampData = nil
863 local mouseTarget = nil
864
865 local errorBox = Instance.new("SelectionBox")
866 errorBox.Color = BrickColor.new("Bright red")
867 errorBox.Transparency = 0
868 errorBox.Archivable = false
869
870 -- for megacluster MEGA STAMPING
871 local adornPart = Instance.new("Part")
872 adornPart.Parent = nil
873 adornPart.formFactor = "Custom"
874 adornPart.Size = Vector3.new(4, 4, 4)
875 adornPart.CFrame = CFrame.new()
876 adornPart.Archivable = false
877
878 local adorn = Instance.new("SelectionBox")
879 adorn.Color = BrickColor.new("Toothpaste")
880 adorn.Adornee = adornPart
881 adorn.Visible = true
882 adorn.Transparency = 0
883 adorn.Name = "HighScalabilityStamperLine"
884 adorn.Archivable = false
885
886 local HighScalabilityLine = {}
887 HighScalabilityLine.Start = nil
888 HighScalabilityLine.End = nil
889 HighScalabilityLine.Adorn = adorn
890 HighScalabilityLine.AdornPart = adornPart
891 HighScalabilityLine.InternalLine = nil
892 HighScalabilityLine.NewHint = true
893
894 HighScalabilityLine.MorePoints = {nil, nil}
895 HighScalabilityLine.MoreLines = {nil, nil}
896 HighScalabilityLine.Dimensions = 1
897
898 local control = {}
899 local movingLock = false
900 local stampUpLock = false
901 local unstampableSurface = false
902 local mouseCons = {}
903 local keyCon = nil
904
905 local stamped = Instance.new("BoolValue")
906 stamped.Archivable = false
907 stamped.Value = false
908
909 local lastTarget = {}
910 lastTarget.TerrainOrientation = 0
911 lastTarget.CFrame = 0
912
913 local cellInfo = {}
914 cellInfo.Material = 1
915 cellInfo.clusterType = 0
916 cellInfo.clusterOrientation = 0
917
918 local function isMegaClusterPart()
919 if not stampData then return false end
920 if not stampData.CurrentParts then return false end
921
922 return ( stampData.CurrentParts:FindFirstChild("ClusterMaterial",true) or (stampData.CurrentParts.Name == "MegaClusterCube") )
923 end
924
925 local function DoHighScalabilityRegionSelect()
926 local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube")
927 if not megaCube then
928 if not stampData.CurrentParts.Name == "MegaClusterCube" then
929 return
930 else
931 megaCube = stampData.CurrentParts
932 end
933 end
934
935 HighScalabilityLine.End = megaCube.CFrame.p
936 local line = nil
937 local line2 = Vector3.new(0, 0, 0)
938 local line3 = Vector3.new(0, 0, 0)
939
940 if HighScalabilityLine.Dimensions == 1 then
941 -- extract the line from these positions and limit to a 2D plane made from 2 of the world axes
942 -- then use dominating axis to limit line to be at 45-degree intervals
943 -- will use this internal representation of the line for the actual stamping
944 line = (HighScalabilityLine.End - HighScalabilityLine.Start)
945
946 if math.abs(line.X) < math.abs(line.Y) then
947 if math.abs(line.X) < math.abs(line.Z) then
948 -- limit to Y/Z plane, domination unknown
949 local newY, newZ
950 if (math.abs(line.Y) > math.abs(line.Z)) then
951 newY, newZ = truncateToCircleEighth(line.Y, line.Z)
952 else
953 newZ, newY = truncateToCircleEighth(line.Z, line.Y)
954 end
955 line = Vector3.new(0, newY, newZ)
956 else
957 -- limit to X/Y plane, with Y dominating
958 local newY, newX = truncateToCircleEighth(line.Y, line.X)
959 line = Vector3.new(newX, newY, 0)
960 end
961 else
962 if math.abs(line.Y) < math.abs(line.Z) then
963 -- limit to X/Z plane, domination unknown
964 local newX, newZ
965 if math.abs(line.X) > math.abs(line.Z) then
966 newX, newZ = truncateToCircleEighth(line.X, line.Z)
967 else
968 newZ, newX = truncateToCircleEighth(line.Z, line.X)
969 end
970 line = Vector3.new(newX, 0, newZ)
971 else
972 -- limit to X/Y plane, with X dominating
973 local newX, newY = truncateToCircleEighth(line.X, line.Y)
974 line = Vector3.new(newX, newY, 0)
975 end
976 end
977 HighScalabilityLine.InternalLine = line
978
979 elseif HighScalabilityLine.Dimensions == 2 then
980 line = HighScalabilityLine.MoreLines[1]
981 line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1]
982
983 -- take out any component of line2 along line1, so you get perpendicular to line1 component
984 line2 = line2 - line.unit*line.unit:Dot(line2)
985
986 tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
987
988 -- then zero out whichever is the smaller component
989 local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0))
990 local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0))
991
992 local xComp = xAxis:Dot(line2)
993 local yComp = yAxis:Dot(line2)
994
995 if math.abs(yComp) > math.abs(xComp) then
996 line2 = line2 - xAxis * xComp
997 else
998 line2 = line2 - yAxis * yComp
999 end
1000
1001 HighScalabilityLine.InternalLine = line2
1002
1003 elseif HighScalabilityLine.Dimensions == 3 then
1004 line = HighScalabilityLine.MoreLines[1]
1005 line2 = HighScalabilityLine.MoreLines[2]
1006 line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2]
1007
1008 -- zero out all components of previous lines
1009 line3 = line3 - line.unit * line.unit:Dot(line3)
1010 line3 = line3 - line2.unit * line2.unit:Dot(line3)
1011
1012 HighScalabilityLine.InternalLine = line3
1013 end
1014
1015 -- resize the "line" graphic to be the correct size and orientation
1016 tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
1017
1018 if HighScalabilityLine.Dimensions == 1 then -- faster calculation for line
1019 HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4)
1020 HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size/2)
1021 else
1022 local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3)
1023 HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z))
1024 HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize/2)
1025 end
1026
1027 -- make player able to see this ish
1028
1029 local gui = nil
1030 if game.Players["LocalPlayer"] then
1031 gui = game.Players.LocalPlayer:FindFirstChild("PlayerGui")
1032 if gui and gui:IsA("PlayerGui") then
1033 if HighScalabilityLine.Dimensions == 1 and line.magnitude > 3 then -- don't show if mouse hasn't moved enough
1034 HighScalabilityLine.Adorn.Parent = gui
1035 elseif HighScalabilityLine.Dimensions > 1 then
1036 HighScalabilityLine.Adorn.Parent = gui
1037 end
1038 end
1039 end
1040
1041 if gui == nil then -- we are in studio
1042 gui = game:GetService("CoreGui")
1043 if HighScalabilityLine.Dimensions == 1 and line.magnitude > 3 then -- don't show if mouse hasn't moved enough
1044 HighScalabilityLine.Adorn.Parent = gui
1045 elseif HighScalabilityLine.Dimensions > 1 then
1046 HighScalabilityLine.Adorn.Parent = gui
1047 end
1048 end
1049 end
1050
1051
1052 local function DoStamperMouseMove(Mouse)
1053 if not Mouse then
1054 error("Error: RbxStamper.DoStamperMouseMove: Mouse is nil")
1055 return
1056 end
1057 if not Mouse:IsA("Mouse") then
1058 error("Error: RbxStamper.DoStamperMouseMove: Mouse is of type", Mouse.className,"should be of type Mouse")
1059 return
1060 end
1061
1062 -- There wasn't a target (no part or terrain), so check for plane intersection.
1063 if not Mouse.Target then
1064 local cellPos = GetTerrainForMouse(Mouse)
1065 if nil == cellPos then
1066 return
1067 end
1068 end
1069
1070 if not stampData then
1071 return
1072 end
1073
1074 -- don't move with dragger - will move in one step on mouse down
1075 -- draw ghost at acceptable positions
1076 configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData)
1077 if not configFound then
1078 error("RbxStamper.DoStamperMouseMove No configFound, returning")
1079 return
1080 end
1081
1082 local numRotations = 0 -- update this according to how many rotations you need to get it to target surface
1083 if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then -- pre-rotate the flag or portrait so it's aligned correctly
1084 if targetSurface == 3 then numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
1085 elseif targetSurface == 0 then numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
1086 elseif targetSurface == 5 then numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
1087 elseif targetSurface == 2 then numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts)
1088 end
1089 end
1090
1091 local ry = math.pi/2
1092 gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations
1093 if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
1094 --stampData.CurrentParts:Rotate(0, ry*numRotations, 0)
1095 modelRotate(stampData.CurrentParts, ry*numRotations)
1096 else
1097 stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry*numRotations, 0) * stampData.CurrentParts.CFrame
1098 end
1099
1100 -- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE
1101 local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
1102
1103 -- need to offset by distance to be dragged
1104 local currModelCFrame = nil
1105 if stampData.CurrentParts:IsA("Model") then
1106 currModelCFrame = stampData.CurrentParts:GetModelCFrame()
1107 else
1108 currModelCFrame = stampData.CurrentParts.CFrame
1109 end
1110
1111 minBB = minBB + targetCFrame.p - currModelCFrame.p
1112 maxBB = maxBB + targetCFrame.p - currModelCFrame.p
1113
1114 -- don't drag into terrain
1115 if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
1116 if lastTarget.CFrame then
1117 if (stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)) then
1118 local theClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1119 if theClusterMaterial:IsA("Vector3Value") then
1120 local stampClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1121 if stampClusterMaterial then
1122 stampClusterMaterial = clusterMat
1123 end
1124 end
1125 end
1126 end
1127 return
1128 end
1129
1130 -- 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
1131 if isMegaClusterPart() then
1132 local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p)
1133 local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z)
1134 local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame:components()
1135 targetCFrame = CFrame.new(newCFramePosition.X,newCFramePosition.Y,newCFramePosition.Z,R00, R01, R02, R10, R11, R12, R20, R21, R22)
1136 end
1137
1138 positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
1139 lastTarget.CFrame = targetCFrame -- successful positioning, so update 'dat cframe
1140 if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
1141 local clusterMat = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1142 if clusterMat:IsA("Vector3Value") then
1143 lastTarget.TerrainOrientation = clusterMat.Value.Z
1144 end
1145 end
1146
1147
1148 -- auto break joints code
1149 if Mouse and Mouse.Target and Mouse.Target.Parent then
1150 local modelInfo = Mouse.Target:FindFirstChild("RobloxModel")
1151 if not modelInfo then modelInfo = Mouse.Target.Parent:FindFirstChild("RobloxModel") end
1152
1153 local myModelInfo = stampData.CurrentParts:FindFirstChild("UnstampableFaces")
1154
1155 --if (modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces")) or (modelInfo and myModelInfo) then -- need better targetSurface calcs
1156 if (true) then
1157 local breakingFaces = ""
1158 local myBreakingFaces = ""
1159 if modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces") then breakingFaces = modelInfo.Parent.UnstampableFaces.Value end
1160 if myModelInfo then myBreakingFaces = myModelInfo.Value end
1161 local hitFace = 0
1162
1163 if modelInfo then hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, Mouse.Hit.p) end
1164
1165 -- are we stamping TO an unstampable surface?
1166 for bf in string.gmatch(breakingFaces, "[^,]+") do
1167 if hitFace == tonumber(bf) then
1168 -- return before we hit the JointsService code below!
1169 unstampableSurface = true
1170 game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
1171 return
1172 end
1173 end
1174
1175 -- now we have to cast the ray back in the other direction to find the surface we're stamping FROM
1176 hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p)
1177
1178 -- are we stamping WITH an unstampable surface?
1179 for bf in string.gmatch(myBreakingFaces, "[^,]+") do
1180 if hitFace == tonumber(bf) then
1181 unstampableSurface = true
1182 game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
1183 return
1184 end
1185 end
1186
1187 -- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame
1188 -- targetSurface: 1 is top, 4 is bottom,
1189 end
1190 end
1191
1192 -- to show joints during the mouse move
1193 unstampableSurface = false
1194 game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
1195
1196 -- most common mouse inactive error occurs here, so check mouse active one more time in a pcall
1197 if not pcall(function()
1198 if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild("RobloxModel") == nil then
1199 return
1200 else
1201 return
1202 end
1203 end)
1204 then
1205 error("Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check")
1206 game.JointsService:ClearJoinAfterMoveJoints()
1207 Mouse = nil
1208 return
1209 end
1210
1211 if Mouse and Mouse.Target and Mouse.Target.Parent:FindFirstChild("RobloxModel") == nil then
1212 game.JointsService:SetJoinAfterMoveTarget(Mouse.Target)
1213 else
1214 game.JointsService:SetJoinAfterMoveTarget(nil)
1215 end
1216 game.JointsService:ShowPermissibleJoints()
1217
1218 -- here we allow for a line of high-scalability parts
1219 if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then
1220 DoHighScalabilityRegionSelect()
1221 end
1222 end
1223
1224 local function setupKeyListener(key, Mouse)
1225 if control and control["Paused"] then return end -- don't do this if we have no stamp
1226
1227 key = string.lower(key)
1228 if key == 'r' and not autoAlignToFace(stampData.CurrentParts) then -- rotate the model
1229 gInitial90DegreeRotations = gInitial90DegreeRotations + 1
1230
1231 -- Update orientation value if this is a fake terrain part
1232 local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1233 if clusterValues and clusterValues:IsA("Vector3Value") then
1234 clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z + 1) % 4)
1235 end
1236
1237 -- Rotate the parts or all the parts in the model
1238 local ry = math.pi/2
1239 if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
1240 --stampData.CurrentParts:Rotate(0, ry, 0)
1241 modelRotate(stampData.CurrentParts, ry)
1242 else
1243 stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
1244 end
1245
1246 -- After rotating, update the position
1247 configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
1248 if configFound then
1249 positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
1250
1251 -- update everything else in MouseMove
1252 DoStamperMouseMove(Mouse)
1253 end
1254 elseif key == 'c' then -- try to expand our high scalability dragger dimension
1255 if HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 and HighScalabilityLine.Dimensions < 3 then
1256 HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End
1257 HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine
1258 HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1
1259 HighScalabilityLine.NewHint = true
1260 end
1261 end
1262 end
1263
1264 keyCon = Mouse.KeyDown:connect(function(key) -- init key connection (keeping code close to func)
1265 setupKeyListener(key, Mouse)
1266 end)
1267
1268 local function resetHighScalabilityLine()
1269 if HighScalabilityLine then
1270 HighScalabilityLine.Start = nil
1271 HighScalabilityLine.End = nil
1272 HighScalabilityLine.InternalLine = nil
1273 HighScalabilityLine.NewHint = true
1274 end
1275 end
1276
1277 local function flashRedBox()
1278 local gui = game.CoreGui
1279 if game:FindFirstChild("Players") then
1280 if game.Players["LocalPlayer"] then
1281 if game.Players.LocalPlayer:FindFirstChild("PlayerGui") then
1282 gui = game.Players.LocalPlayer.PlayerGui
1283 end
1284 end
1285 end
1286 if not stampData["ErrorBox"] then return end
1287
1288 stampData.ErrorBox.Parent = gui
1289 if stampData.CurrentParts:IsA("Tool") then
1290 stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle
1291 else
1292 stampData.ErrorBox.Adornee = stampData.CurrentParts
1293 end
1294
1295 delay(0,function()
1296 for i = 1, 3 do
1297 if stampData["ErrorBox"] then stampData.ErrorBox.Visible = true end
1298 wait(0.13)
1299 if stampData["ErrorBox"] then stampData.ErrorBox.Visible = false end
1300 wait(0.13)
1301 end
1302 if stampData["ErrorBox"] then
1303 stampData.ErrorBox.Adornee = nil
1304 stampData.ErrorBox.Parent = Tool
1305 end
1306 end)
1307 end
1308
1309 local function DoStamperMouseDown(Mouse)
1310 if not Mouse then
1311 error("Error: RbxStamper.DoStamperMouseDown: Mouse is nil")
1312 return
1313 end
1314 if not Mouse:IsA("Mouse") then
1315 error("Error: RbxStamper.DoStamperMouseDown: Mouse is of type", Mouse.className,"should be of type Mouse")
1316 return
1317 end
1318 if not stampData then
1319 return
1320 end
1321
1322 if isMegaClusterPart() then
1323 if Mouse and HighScalabilityLine then
1324 local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube", true)
1325 local terrain = game.Workspace.Terrain
1326 if megaCube then
1327 HighScalabilityLine.Dimensions = 1
1328 local tempCell = terrain:WorldToCell(megaCube.CFrame.p)
1329 HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
1330 return
1331 else
1332 HighScalabilityLine.Dimensions = 1
1333 local tempCell = terrain:WorldToCell(stampData.CurrentParts.CFrame.p)
1334 HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z)
1335 return
1336 end
1337 end
1338 end
1339 end
1340
1341 local function loadSurfaceTypes(part, surfaces)
1342 part.TopSurface = surfaces[1]
1343 part.BottomSurface = surfaces[2]
1344 part.LeftSurface = surfaces[3]
1345 part.RightSurface = surfaces[4]
1346 part.FrontSurface = surfaces[5]
1347 part.BackSurface = surfaces[6]
1348 end
1349
1350 local function saveSurfaceTypes(part, myTable)
1351 local tempTable = {}
1352 tempTable[1] = part.TopSurface
1353 tempTable[2] = part.BottomSurface
1354 tempTable[3] = part.LeftSurface
1355 tempTable[4] = part.RightSurface
1356 tempTable[5] = part.FrontSurface
1357 tempTable[6] = part.BackSurface
1358
1359 myTable[part] = tempTable
1360 end
1361
1362 local function makeSurfaceUnjoinable(part, surface)
1363 -- TODO: FILL OUT!
1364 end
1365
1366 local function prepareModel(model)
1367 if not model then return nil end
1368
1369 local gDesiredTrans = 0.7
1370 local gStaticTrans = 1
1371
1372 local clone = model:Clone()
1373 local scripts = {}
1374 local parts = {}
1375 local decals = {}
1376
1377 stampData = {}
1378 stampData.DisabledScripts = {}
1379 stampData.TransparencyTable = {}
1380 stampData.MaterialTable = {}
1381 stampData.CanCollideTable = {}
1382 stampData.AnchoredTable = {}
1383 stampData.ArchivableTable = {}
1384 stampData.DecalTransparencyTable = {}
1385 stampData.SurfaceTypeTable = {}
1386
1387 collectParts(clone, parts, scripts, decals)
1388
1389 if #parts <= 0 then return nil, "no parts found in modelToStamp" end
1390
1391 for index,script in pairs(scripts) do
1392 if not(script.Disabled) then
1393 script.Disabled = true
1394 stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script
1395 end
1396 end
1397 for index, part in pairs(parts) do
1398 stampData.TransparencyTable[part] = part.Transparency
1399 part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency
1400 stampData.MaterialTable[part] = part.Material
1401 part.Material = Enum.Material.Plastic
1402 stampData.CanCollideTable[part] = part.CanCollide
1403 part.CanCollide = false
1404 stampData.AnchoredTable[part] = part.Anchored
1405 part.Anchored = true
1406 stampData.ArchivableTable[part] = part.Archivable
1407 part.Archivable = false
1408
1409 saveSurfaceTypes(part, stampData.SurfaceTypeTable)
1410
1411 local fadeInDelayTime = 0.5
1412 local transFadeInTime = 0.5
1413 delay(0,function()
1414 wait(fadeInDelayTime) -- give it some time to be completely transparent
1415
1416 local begTime = tick()
1417 local currTime = begTime
1418 while (currTime - begTime) < transFadeInTime and part and part:IsA("BasePart") and part.Transparency > gDesiredTrans do
1419 local newTrans = 1 - (((currTime - begTime)/transFadeInTime) * (gStaticTrans - gDesiredTrans))
1420 if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
1421 part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part]
1422 end
1423 wait(0.03)
1424 currTime = tick()
1425 end
1426 if part and part:IsA("BasePart") then
1427 if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then
1428 part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part]
1429 end
1430 end
1431 end)
1432 end
1433
1434 for index, decal in pairs(decals) do
1435 stampData.DecalTransparencyTable[decal] = decal.Transparency
1436 decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency
1437 end
1438
1439 -- disable all seats
1440 setSeatEnabledStatus(clone, true)
1441 setSeatEnabledStatus(clone, false)
1442
1443 stampData.CurrentParts = clone
1444
1445 -- if auto-alignable, we enforce a pre-rotation to the canonical "0-frame"
1446 if autoAlignToFace(clone) then
1447 stampData.CurrentParts:ResetOrientationToIdentity()
1448 gInitial90DegreeRotations = 0
1449 else -- pre-rotate if necessary
1450 local ry = gInitial90DegreeRotations * math.pi/2
1451 if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
1452 --stampData.CurrentParts:Rotate(0, ry, 0)
1453 modelRotate(stampData.CurrentParts, ry)
1454 else
1455 stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame
1456 end
1457 end
1458
1459 -- 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
1460 -- 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
1461 -- issue (fingers crossed) [HotThoth]
1462
1463 local clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1464 if clusterMaterial and clusterMaterial:IsA("Vector3Value") then
1465 clusterMaterial.Value = Vector3.new(clusterMaterial.Value.X, clusterMaterial.Value.Y, (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4)
1466 end
1467
1468 -- After rotating, update the position
1469 local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
1470 if configFound then
1471 stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts)
1472 end
1473
1474 -- to show joints during the mouse move
1475 game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts)
1476
1477 return clone, parts
1478 end
1479
1480 local function checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
1481 local cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld
1482 local cellCenter = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
1483 local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100)
1484
1485 local skipThisCell = false
1486
1487 for b = 1, #cellBlockingParts do
1488 if isBlocker(cellBlockingParts[b]) then skipThisCell = true break end
1489 end
1490
1491 if not skipThisCell then
1492 -- pop players up above any set cells
1493 local alreadyPushedUp = {}
1494 -- if no blocking model below, then see if stamping on top of a character
1495 for b = 1, #cellBlockingParts do
1496 if cellBlockingParts[b].Parent and
1497 not alreadyPushedUp[cellBlockingParts[b].Parent] and
1498 cellBlockingParts[b].Parent:FindFirstChild("Humanoid") and
1499 cellBlockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
1500 -----------------------------------------------------------------------------------
1501 local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild("Torso")
1502 alreadyPushedUp[cellBlockingParts[b].Parent] = true
1503
1504 if blockingPersonTorso then
1505 -- 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)
1506 local newY = cellCenter.Y + 5
1507 if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
1508 blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
1509 else
1510 -- if no space, we just skip this one
1511 skipThisCell = true
1512 break
1513 end
1514 end
1515 -----------------------------------------------------------------------------------
1516 end
1517 end
1518 end
1519
1520 if not skipThisCell then -- if we STILL aren't skipping... then we're good to go!
1521 local canSetCell = true
1522
1523 if checkHighScalabilityStamp then -- check to see if cell is in region, if not we'll skip set
1524 if allowedStampRegion then
1525 local cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z)
1526 if cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X/2 then
1527 canSetCell = false
1528 elseif cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X/2 then
1529 canSetCell = false
1530 elseif cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y/2 then
1531 canSetCell = false
1532 elseif cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y/2 then
1533 canSetCell = false
1534 elseif cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z/2 then
1535 canSetCell = false
1536 elseif cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z/2 then
1537 canSetCell = false
1538 end
1539 end
1540 end
1541
1542 return canSetCell
1543 end
1544 return false
1545 end
1546
1547
1548 local function ResolveMegaClusterStamp(checkHighScalabilityStamp)
1549 local cellSet = false
1550
1551 local cluser = game.Workspace.Terrain
1552
1553 local line = HighScalabilityLine.InternalLine
1554 local cMax = game.Workspace.Terrain.MaxExtents.Max
1555 local cMin = game.Workspace.Terrain.MaxExtents.Min
1556
1557 local clusterMaterial = 1 -- default is grass
1558 local clusterType = 0 -- default is brick
1559 local clusterOrientation = 0 -- default is 0 rotation
1560
1561 local autoWedgeClusterParts = false
1562 if stampData.CurrentParts:FindFirstChild("AutoWedge") then autoWedgeClusterParts = true end
1563
1564 if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then
1565 clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1566 if clusterMaterial:IsA("Vector3Value") then
1567 clusterType = clusterMaterial.Value.Y
1568 clusterOrientation = clusterMaterial.Value.Z
1569 clusterMaterial = clusterMaterial.Value.X
1570 elseif clusterMaterial:IsA("IntValue") then
1571 clusterMaterial = clusterMaterial.Value
1572 end
1573 end
1574
1575 if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) then
1576 local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start)
1577 local xInc = {0,0,0}
1578 local yInc = {0,0,0}
1579 local zInc = {0,0,0}
1580
1581 local cluster = game.Workspace.Terrain
1582
1583 local incrementVect = {nil, nil, nil}
1584 local stepVect = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 0), Vector3.new(0, 0, 0)}
1585
1586 local worldAxes = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
1587
1588 local lines = {}
1589 if HighScalabilityLine.Dimensions > 1 then table.insert(lines, HighScalabilityLine.MoreLines[1]) end
1590 if line and line.magnitude > 0 then table.insert(lines, line) end
1591 if HighScalabilityLine.Dimensions > 2 then table.insert(lines, HighScalabilityLine.MoreLines[2]) end
1592
1593 for i = 1, #lines do
1594 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
1595
1596 if lines[i].X > 0 then xInc[i] = 1 elseif lines[i].X < 0 then xInc[i] = -1 end
1597 if lines[i].Y > 0 then yInc[i] = 1 elseif lines[i].Y < 0 then yInc[i] = -1 end
1598 if lines[i].Z > 0 then zInc[i] = 1 elseif lines[i].Z < 0 then zInc[i] = -1 end
1599
1600 incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i])
1601 if incrementVect[i].magnitude < .9 then incrementVect[i] = nil end
1602 end
1603
1604
1605 if not lines[2] then lines[2] = Vector3.new(0, 0, 0) end
1606 if not lines[3] then lines[3] = Vector3.new(0, 0, 0) end
1607
1608 local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
1609 local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
1610
1611 while (stepVect[3].magnitude*4 <= lines[3].magnitude) do
1612 local outerStepVectIndex = 1
1613 while outerStepVectIndex < 4 do
1614 stepVect[2] = Vector3.new(0, 0, 0)
1615 while (stepVect[2].magnitude*4 <= lines[2].magnitude) do
1616 local innerStepVectIndex = 1
1617 while innerStepVectIndex < 4 do
1618 stepVect[1] = Vector3.new(0, 0, 0)
1619 while (stepVect[1].magnitude*4 <= lines[1].magnitude) do
1620 local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3]
1621 local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z)
1622 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
1623 -- check if overlaps player or part
1624 local okToStampTerrainBlock = checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp)
1625
1626 if okToStampTerrainBlock then
1627 if waterForceTag then
1628 cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
1629 else
1630 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
1631 end
1632 cellSet = true
1633
1634 -- auto-wedge it?
1635 if (autoWedgeClusterParts) then
1636 game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
1637 Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
1638 end
1639 end
1640 end
1641 stepVect[1] = stepVect[1] + incrementVect[1]
1642 end
1643 if incrementVect[2] then
1644 while innerStepVectIndex < 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do
1645 innerStepVectIndex = innerStepVectIndex + 1
1646 end
1647 if innerStepVectIndex < 4 then
1648 stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex] * worldAxes[innerStepVectIndex]:Dot(incrementVect[2])
1649 end
1650 innerStepVectIndex = innerStepVectIndex + 1
1651 else
1652 stepVect[2] = Vector3.new(1, 0, 0)
1653 innerStepVectIndex = 4 -- skip all remaining loops
1654 end
1655 if (stepVect[2].magnitude*4 > lines[2].magnitude) then innerStepVectIndex = 4 end
1656 end
1657 end
1658 if incrementVect[3] then
1659 while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do
1660 outerStepVectIndex = outerStepVectIndex + 1
1661 end
1662 if outerStepVectIndex < 4 then
1663 stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex] * worldAxes[outerStepVectIndex]:Dot(incrementVect[3])
1664 end
1665 outerStepVectIndex = outerStepVectIndex + 1
1666 else -- skip all remaining loops
1667 stepVect[3] = Vector3.new(1, 0, 0) outerStepVectIndex = 4
1668 end
1669 if (stepVect[3].magnitude*4 > lines[3].magnitude) then outerStepVectIndex = 4 end
1670 end
1671 end
1672 end
1673
1674 -- and also get rid of any HighScalabilityLine stuff if it's there
1675 HighScalabilityLine.Start = nil
1676 HighScalabilityLine.Adorn.Parent = nil
1677
1678 -- Mark for undo.
1679 if cellSet then
1680 stampData.CurrentParts.Parent = nil
1681 pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StamperMulti") end)
1682 end
1683
1684 return cellSet
1685 end
1686
1687 local function DoStamperMouseUp(Mouse)
1688 if not Mouse then
1689 error("Error: RbxStamper.DoStamperMouseUp: Mouse is nil")
1690 return false
1691 end
1692 if not Mouse:IsA("Mouse") then
1693 error("Error: RbxStamper.DoStamperMouseUp: Mouse is of type", Mouse.className,"should be of type Mouse")
1694 return false
1695 end
1696
1697 if not stampData.Dragger then
1698 error("Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil")
1699 return false
1700 end
1701
1702 if not HighScalabilityLine then
1703 return false
1704 end
1705
1706 local checkHighScalabilityStamp = nil
1707 if stampInModel then
1708 local canStamp = nil
1709 local isHSLPart = isMegaClusterPart()
1710
1711 if isHSLPart and
1712 HighScalabilityLine and
1713 HighScalabilityLine.Start and
1714 HighScalabilityLine.InternalLine and
1715 HighScalabilityLine.InternalLine.magnitude > 0 then -- we have an HSL line, test later
1716 canStamp = true
1717 checkHighScalabilityStamp = true
1718 else
1719 canStamp, checkHighScalabilityStamp = RBXStamper.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
1720 end
1721
1722 if not canStamp then
1723 if stampFailedFunc then
1724 stampFailedFunc()
1725 end
1726 return false
1727 end
1728 end
1729
1730 -- if unstampable face, then don't let us stamp there!
1731 if unstampableSurface then
1732 flashRedBox()
1733 return false
1734 end
1735
1736 -- recheck if we can stamp, as we just moved part
1737 canStamp, checkHighScalabilityStamp = RBXStamper.CanEditRegion(stampData.CurrentParts, allowedStampRegion)
1738 if not canStamp then
1739 if stampFailedFunc then
1740 stampFailedFunc()
1741 end
1742 return false
1743 end
1744
1745 -- Prevent part from being stamped on top of a player
1746
1747 local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts)
1748
1749 -- HotThoth's note: Now that above CurrentParts positioning has been commented out, to be truly correct, we would need to use the
1750 -- value of configFound from the previous onStamperMouseMove call which moved the CurrentParts
1751 -- Shouldn't this be true when lastTargetCFrame has been set and false otherwise?
1752 configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData)
1753
1754 if configFound and not HighScalabilityLine.Adorn.Parent then
1755 if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then
1756 flashRedBox()
1757 return false
1758 end
1759
1760 local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB + insertBoundingBoxOverlapVector,
1761 maxBB - insertBoundingBoxOverlapVector),
1762 stampData.CurrentParts,
1763 100)
1764
1765
1766 for b = 1, #blockingParts do
1767 if isBlocker(blockingParts[b]) then
1768 flashRedBox()
1769 return false
1770 end
1771 end
1772
1773 local alreadyPushedUp = {}
1774 -- if no blocking model below, then see if stamping on top of a character
1775 for b = 1, #blockingParts do
1776 if blockingParts[b].Parent and
1777 not alreadyPushedUp[blockingParts[b].Parent] and
1778 blockingParts[b].Parent:FindFirstChild("Humanoid") and
1779 blockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
1780 ---------------------------------------------------------------------------
1781 local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild("Torso")
1782 alreadyPushedUp[blockingParts[b].Parent] = true
1783
1784 if blockingPersonTorso then
1785 -- 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)
1786 local newY = maxBB.Y + 3
1787 if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then
1788 blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
1789 else
1790 -- if no space, we just error
1791 flashRedBox()
1792 return false
1793 end
1794 end
1795 ---------------------------------------------------------------------------
1796 end
1797 end
1798
1799 elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then -- if no config then only stamp if it's a real HSL!
1800 resetHighScalabilityLine()
1801 return false
1802 end
1803
1804 -- something will be stamped! so set the "StampedSomething" toggle to true
1805 if game:FindFirstChild("Players") then
1806 if game.Players["LocalPlayer"] then
1807 if game.Players.LocalPlayer["Character"] then
1808 local localChar = game.Players.LocalPlayer.Character
1809 local stampTracker = localChar:FindFirstChild("StampTracker")
1810 if stampTracker and not stampTracker.Value then
1811 stampTracker.Value = true
1812 end
1813 end
1814 end
1815 end
1816
1817 -- if we drew a line of mega parts, stamp them out
1818 if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then
1819 if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then
1820 -- kill the ghost part
1821 stampData.CurrentParts.Parent = nil
1822 return true
1823 end
1824 end
1825
1826 -- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff]
1827 HighScalabilityLine.Start = nil
1828 HighScalabilityLine.Adorn.Parent = nil
1829
1830 local cluster = game.Workspace.Terrain
1831
1832 -- if target point is in cluster, just use cluster:SetCell
1833 if isMegaClusterPart() then
1834 -- if targetCFrame is inside cluster, just set that cell to 1 and return
1835 --local cellPos = cluster:WorldToCell(targetCFrame.p)
1836
1837 local cellPos
1838 if stampData.CurrentParts:IsA("Model") then cellPos = cluster:WorldToCell(stampData.CurrentParts:GetModelCFrame().p)
1839 else cellPos = cluster:WorldToCell(stampData.CurrentParts.CFrame.p) end
1840
1841 local cMax = game.Workspace.Terrain.MaxExtents.Max
1842 local cMin = game.Workspace.Terrain.MaxExtents.Min
1843
1844 if checkTerrainBlockCollisions(cellPos, false) then
1845
1846 local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true)
1847 local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true)
1848 local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true)
1849
1850 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
1851
1852 if waterForceTag then
1853 cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value])
1854 elseif not clusterValues then
1855 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, cellInfo.Material, cellInfo.clusterType, gInitial90DegreeRotations % 4)
1856 elseif clusterValues:IsA("Vector3Value") then
1857 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value.X, clusterValues.Value.Y, clusterValues.Value.Z)
1858 else
1859 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0)
1860 end
1861
1862 local autoWedgeClusterParts = false
1863 if stampData.CurrentParts:FindFirstChild("AutoWedge") then autoWedgeClusterParts = true end
1864
1865 -- auto-wedge it
1866 if (autoWedgeClusterParts) then
1867 game.Workspace.Terrain:AutowedgeCells(
1868 Region3int16.new(
1869 Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1),
1870 Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)
1871 )
1872 )
1873 end
1874
1875 -- kill the ghost part
1876 stampData.CurrentParts.Parent = nil
1877
1878 -- Mark for undo. It has to happen here or the selection display will come back also.
1879 pcall(function() game:GetService("ChangeHistoryService"):SetWaypoint("StamperSingle") end)
1880 return true
1881 end
1882 else
1883 -- you tried to stamp a HSL-single part where one does not belong!
1884 flashRedBox()
1885 return false
1886 end
1887 end
1888
1889 -- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints
1890 if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then
1891 if stampData.CurrentParts:IsA("Model") then
1892 -- Tyler's magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don't ask X<
1893 local manualWeldTable = {}
1894 local manualWeldParentTable = {}
1895 saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable)
1896 stampData.CurrentParts:BreakJoints()
1897 stampData.CurrentParts:MakeJoints()
1898 restoreTheWelds(manualWeldTable, manualWeldParentTable)
1899 end
1900
1901 -- 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)
1902 if game:FindFirstChild("Players") and game.Players.LocalPlayer then
1903 playerIdTag = stampData.CurrentParts:FindFirstChild("PlayerIdTag")
1904 playerNameTag = stampData.CurrentParts:FindFirstChild("PlayerNameTag")
1905 if playerIdTag ~= nil then
1906 playerIdTag.Value = game.Players.LocalPlayer.userId
1907 end
1908 if playerNameTag ~= nil then
1909 playerNameTag.Value = game.Players.LocalPlayer.Name
1910 end
1911 end
1912 -- ...and tag all inserted models for subsequent origin identification
1913 -- if no RobloxModel tag already exists, then add it.
1914 if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then
1915 local stringTag = Instance.new("BoolValue", stampData.CurrentParts)
1916 stringTag.Name = "RobloxModel"
1917
1918 if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then
1919 local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts)
1920 stringTag2.Name = "RobloxStamper"
1921 end
1922 end
1923
1924 else
1925 stampData.CurrentParts:BreakJoints()
1926 if stampData.CurrentParts:FindFirstChild("RobloxStamper") == nil then
1927 local stringTag2 = Instance.new("BoolValue", stampData.CurrentParts)
1928 stringTag2.Name = "RobloxStamper"
1929 end
1930 end
1931
1932 -- make sure all the joints are activated before restoring anchor states
1933 if not createJoints then game.JointsService:CreateJoinAfterMoveJoints() end
1934
1935 -- Restore the original properties for all parts being stamped
1936 for part, transparency in pairs(stampData.TransparencyTable) do
1937 part.Transparency = transparency
1938 end
1939 for part, archivable in pairs(stampData.ArchivableTable) do
1940 part.Archivable = archivable
1941 end
1942 for part, material in pairs(stampData.MaterialTable) do
1943 part.Material = material
1944 end
1945 for part, collide in pairs(stampData.CanCollideTable) do
1946 part.CanCollide = collide
1947 end
1948 for part, anchored in pairs(stampData.AnchoredTable) do
1949 part.Anchored = anchored
1950 end
1951 for decal, transparency in pairs(stampData.DecalTransparencyTable) do
1952 decal.Transparency = transparency
1953 end
1954
1955 for part, surfaces in pairs(stampData.SurfaceTypeTable) do
1956 loadSurfaceTypes(part, surfaces)
1957 end
1958
1959 if isMegaClusterPart() then
1960 stampData.CurrentParts.Transparency = 0
1961 end
1962
1963 -- re-enable all seats
1964 setSeatEnabledStatus(stampData.CurrentParts, true)
1965
1966 stampData.TransparencyTable = nil
1967 stampData.ArchivableTable = nil
1968 stampData.MaterialTable = nil
1969 stampData.CanCollideTable = nil
1970 stampData.AnchoredTable = nil
1971 stampData.SurfaceTypeTable = nil
1972
1973 -- ...and tag all inserted models for subsequent origin identification
1974 -- if no RobloxModel tag already exists, then add it.
1975 if stampData.CurrentParts:FindFirstChild("RobloxModel") == nil then
1976 local stringTag = Instance.new("BoolValue", stampData.CurrentParts)
1977 stringTag.Name = "RobloxModel"
1978 end
1979
1980 -- and make sure we don't delete it, now that it's not a ghost part
1981 if ghostRemovalScript then ghostRemovalScript.Parent = nil end
1982
1983 --Re-enable the scripts
1984 for index,script in pairs(stampData.DisabledScripts) do
1985 script.Disabled = false
1986 end
1987
1988 --Now that they are all marked enabled, reinsert them into the world so they start running
1989 for index,script in pairs(stampData.DisabledScripts) do
1990 local oldParent = script.Parent
1991 script.Parent = nil
1992 script:Clone().Parent = oldParent
1993 end
1994
1995 -- clear out more data
1996 stampData.DisabledScripts = nil
1997 stampData.Dragger = nil
1998 stampData.CurrentParts = nil
1999
2000 pcall(function() game:GetService("ChangeHistoryService"): SetWaypoint("StampedObject") end)
2001 return true
2002 end
2003
2004 local function pauseStamper()
2005 for i = 1, #mouseCons do -- stop the mouse from doing anything
2006 mouseCons[i]:disconnect()
2007 mouseCons[i] = nil
2008 end
2009 mouseCons = {}
2010
2011 if stampData and stampData.CurrentParts then -- remove our ghost part
2012 stampData.CurrentParts.Parent = nil
2013 stampData.CurrentParts:Remove()
2014 end
2015
2016 resetHighScalabilityLine()
2017
2018 game.JointsService:ClearJoinAfterMoveJoints()
2019 end
2020
2021
2022 local function prepareUnjoinableSurfaces(modelCFrame, parts, whichSurface)
2023 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!
2024 local isPositive = 1
2025 if whichSurface < 0 then isPositive = isPositive * -1 whichSurface = whichSurface*-1 end
2026 local surfaceNormal = isPositive * modelCFrame:vectorToWorldSpace(AXIS_VECTORS[whichSurface])
2027
2028 for i = 1, #parts do
2029 local currPart = parts[i]
2030
2031 -- now just need to find which surface of currPart most closely match surfaceNormal and then set that to Unjoinable
2032 local surfaceNormalInLocalCoords = currPart.CFrame:vectorToObjectSpace(surfaceNormal)
2033 if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Y) then
2034 if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Z) then
2035 if surfaceNormalInLocalCoords.X > 0 then currPart.RightSurface = "Unjoinable" else currPart.LeftSurface = "Unjoinable" end
2036 else
2037 if surfaceNormalInLocalCoords.Z > 0 then currPart.BackSurface = "Unjoinable" else currPart.FrontSurface = "Unjoinable" end
2038 end
2039 else
2040 if math.abs(surfaceNormalInLocalCoords.Y) > math.abs(surfaceNormalInLocalCoords.Z) then
2041 if surfaceNormalInLocalCoords.Y > 0 then currPart.TopSurface = "Unjoinable" else currPart.BottomSurface = "Unjoinable" end
2042 else
2043 if surfaceNormalInLocalCoords.Z > 0 then currPart.BackSurface = "Unjoinable" else currPart.FrontSurface = "Unjoinable" end
2044 end
2045 end
2046 end
2047 end
2048
2049 local function resumeStamper()
2050
2051 clone, parts = prepareModel(modelToStamp)
2052
2053 if not clone or not parts then
2054 return
2055 end
2056
2057 -- if we have unjoinable faces, then we want to change those surfaces to be Unjoinable
2058 local unjoinableTag = clone:FindFirstChild("UnjoinableFaces", true)
2059 if unjoinableTag then
2060 for unjoinableSurface in string.gmatch(unjoinableTag.Value, "[^,]*") do
2061 if tonumber(unjoinableSurface) then
2062 if clone:IsA("Model") then
2063 prepareUnjoinableSurfaces(clone:GetModelCFrame(), parts, tonumber(unjoinableSurface))
2064 else
2065 prepareUnjoinableSurfaces(clone.CFrame, parts, tonumber(unjoinableSurface))
2066 end
2067 end
2068 end
2069 end
2070
2071 stampData.ErrorBox = errorBox
2072 if stampInModel then
2073 clone.Parent = stampInModel
2074 else
2075 clone.Parent = game.Workspace
2076 end
2077
2078 if clone:FindFirstChild("ClusterMaterial", true) then -- extract all info from vector
2079 clusterMaterial = clone:FindFirstChild("ClusterMaterial", true)
2080 if (clusterMaterial:IsA("Vector3Value")) then
2081 cellInfo.Material = clusterMaterial.Value.X
2082 cellInfo.clusterType = clusterMaterial.Value.Y
2083 cellInfo.clusterOrientation = clusterMaterial.Value.Z
2084 elseif clusterMaterial:IsA("IntValue") then
2085 cellInfo.Material = clusterMaterial.Value
2086 end
2087 end
2088
2089 pcall(function() mouseTarget = Mouse.Target end)
2090
2091 if mouseTarget and mouseTarget.Parent:FindFirstChild("RobloxModel") == nil then
2092 game.JointsService:SetJoinAfterMoveTarget(mouseTarget)
2093 else
2094 game.JointsService:SetJoinAfterMoveTarget(nil)
2095 end
2096 game.JointsService:ShowPermissibleJoints()
2097
2098 for index, object in pairs(stampData.DisabledScripts) do
2099 if object.Name == "GhostRemovalScript" then
2100 object.Parent = stampData.CurrentParts
2101 end
2102 end
2103
2104 stampData.Dragger = Instance.new("Dragger")
2105
2106 --Begin a movement by faking a MouseDown signal
2107 stampData.Dragger:MouseDown(parts[1], Vector3.new(0,0,0), parts)
2108 stampData.Dragger:MouseUp()
2109
2110 DoStamperMouseMove(Mouse)
2111
2112 table.insert(mouseCons,Mouse.Move:connect(function()
2113 if movingLock or stampUpLock then return end
2114 movingLock = true
2115 DoStamperMouseMove(Mouse)
2116 movingLock = false
2117 end))
2118
2119 table.insert(mouseCons,Mouse.Button1Down:connect(function()
2120 DoStamperMouseDown(Mouse)
2121 end))
2122
2123 table.insert(mouseCons,Mouse.Button1Up:connect(function()
2124 stampUpLock = true
2125 while movingLock do wait() end
2126 --added zach stuff
2127 wait(0)
2128 DoStamperMouseMove(Mouse)
2129 --end zach stuff
2130 stamped.Value = DoStamperMouseUp(Mouse)
2131 resetHighScalabilityLine()
2132 stampUpLock = false
2133 end))
2134
2135 stamped.Value = false
2136 end
2137
2138 local function resetStamperState(newModelToStamp)
2139
2140 -- if we have a new model, swap it out
2141 if newModelToStamp then
2142 if not newModelToStamp:IsA("Model") and not newModelToStamp:IsA("BasePart") then
2143 error("resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!")
2144 end
2145 modelToStamp = newModelToStamp
2146 end
2147
2148 -- first clear our state
2149 pauseStamper()
2150 -- now lets load in the new model
2151 resumeStamper()
2152
2153 end
2154
2155 -- load the model initially
2156 resetStamperState()
2157
2158
2159 -- setup the control table we pass back to the user
2160 control.Stamped = stamped -- BoolValue that fires when user stamps
2161 control.Paused = false
2162
2163 control.LoadNewModel = function(newStampModel) -- allows us to specify a new stamper model to be used with this stamper
2164 if newStampModel and not newStampModel:IsA("Model") and not newStampModel:IsA("BasePart") then
2165 error("Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!")
2166 return nil
2167 end
2168 resetStamperState(newStampModel)
2169 end
2170
2171 control.ReloadModel = function() -- will automatically set stamper to get a new model of current model and start stamping with new model
2172 resetStamperState()
2173 end
2174
2175 control.Pause = function() -- temporarily stops stamping, use resume to start up again
2176 if not control.Paused then
2177 pauseStamper()
2178 control.Paused = true
2179 else
2180 print("RbxStamper Warning: Tried to call Control.Pause() when already paused")
2181 end
2182 end
2183
2184 control.Resume = function() -- resumes stamping, if currently paused
2185 if control.Paused then
2186 resumeStamper()
2187 control.Paused = false
2188 else
2189 print("RbxStamper Warning: Tried to call Control.Resume() without Pausing First")
2190 end
2191 end
2192
2193 control.ResetRotation = function() -- resets the model rotation so new models are at default orientation
2194 -- gInitial90DegreeRotations = 0
2195 -- 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
2196 -- High-Scalability and with the new model orientation setting methods (model:ResetOrientationToIdentity()) [HotThoth]
2197 end
2198
2199 control.Destroy = function() -- Stops current Stamp operation and destroys control construct
2200 for i = 1, #mouseCons do
2201 mouseCons[i]:disconnect()
2202 mouseCons[i] = nil
2203 end
2204
2205 if keyCon then
2206 keyCon:disconnect()
2207 end
2208
2209 game.JointsService:ClearJoinAfterMoveJoints()
2210
2211 if adorn then adorn:Destroy() end
2212 if adornPart then adornPart:Destroy() end
2213 if errorBox then errorBox:Destroy() end
2214 if stampData then
2215 if stampData["Dragger"] then
2216 stampData.Dragger:Destroy()
2217 end
2218 if stampData.CurrentParts then
2219 stampData.CurrentParts:Destroy()
2220 end
2221 end
2222 if control and control["Stamped"] then
2223 control.Stamped:Destroy()
2224 end
2225 control = nil
2226 end
2227
2228 return control
2229 end
2230
2231 RBXStamper.Help =
2232 function(funcNameOrFunc)
2233 --input argument can be a string or a function. Should return a description (of arguments and expected side effects)
2234 if funcNameOrFunc == "GetStampModel" or funcNameOrFunc == t.GetStampModel then
2235 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"
2236 end
2237 if funcNameOrFunc == "SetupStamperDragger" or funcNameOrFunc == t.SetupStamperDragger then
2238 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."
2239 end
2240 end
2241
2242end
2243
2244
2245------------------------
2246--[[UTIL Functions]]--
2247------------------------
2248--Global functions used by all classes are wrapped in UTIL
2249--deal with it.
2250local UTIL = {}
2251
2252function UTIL.Class(tableIn,template)
2253 local mt = {
2254 __metatable = template;
2255 __index = template;
2256 }
2257 return setmetatable(tableIn, mt)
2258end
2259function UTIL.MakeClass(...)
2260 local arg = {...}
2261 assert(#arg>0, 'ERROR: class needs 1 argument or more')
2262 local members = arg[1]
2263 for i=2,#arg,1 do
2264 if type(arg[i])=='table' then
2265 for key,val in pairs(arg[i]) do
2266 if not members[key] then
2267 members[key] = val
2268 end
2269 end
2270 end
2271 end
2272 local function New(init)
2273 return UTIL.Class(init or {},members)
2274 end
2275 local function Copy(obj, ...)
2276 local newobj = obj:New(unpack(arg))
2277 for n,v in pairs(obj) do newobj[n] = v end
2278 return newobj
2279 end
2280 members.New = members.New or New
2281 members.Copy = members.Copy or Copy
2282 return mt
2283end
2284
2285function UTIL.Instantiate(guiType)
2286 return function(data)
2287 local obj = Instance.new(guiType)
2288 for k, v in pairs(data) do
2289 if type(k) == 'number' then
2290 v.Parent = obj
2291 else
2292 obj[k] = v
2293 end
2294 end
2295 return obj
2296 end
2297end
2298
2299function UTIL.RetroRegister(func,...)
2300 func()
2301 for _,i in ipairs({...}) do
2302 i:connect(func)
2303 end
2304end
2305
2306------------------------
2307--[[UTIL Variables]]--
2308------------------------
2309--Global variables used by all classes are wrapped in UTIL
2310--deal with it.
2311UTIL.InsertService = game:GetService('InsertService')
2312--[[WARNING giving all classes access to playermouse may be a bad idea]]--
2313--[[currently needed by scrollFrame]]--
2314UTIL.LocalPlayer = game:GetService('Players').LocalPlayer
2315UTIL.PlayerMouse = UTIL.LocalPlayer:GetMouse()
2316UTIL.AssetURL = game:GetService("ContentProvider").BaseUrl:lower()..'asset/?id='
2317UTIL.ProdAssetURL = 'http://www.roblox.com/asset/?id='
2318---------------
2319--[[Classes]]--
2320---------------
2321--[[Drop Down Class]]--
2322local DropDown =
2323{
2324 MainFrame = UTIL.Instantiate'Frame'
2325 {
2326 Name ='DropDown',
2327 Size = UDim2.new(1,0,1,0),
2328 BackgroundColor3 = Color3.new(.1,.1,.1),
2329 BackgroundTransparency = 0,
2330 ClipsDescendants = true,
2331 },
2332 Elements = {},
2333 Callbacks = {},
2334 IsOpen = false,
2335}
2336do
2337 UTIL.MakeClass(DropDown)
2338 function DropDown.New()
2339 local init = {}
2340 init = UTIL.Class(init,DropDown)
2341 init.MainFrame = DropDown.MainFrame:Clone()
2342 init.Elements = {}
2343 init.Callbacks = {}
2344 return init
2345 end
2346 function DropDown:Invalidate()
2347 for index,i in ipairs(self.Elements) do
2348 i.Parent= self.MainFrame
2349 i.Position = UDim2.new(0,0,0,i.Size.Y.Offset*(index-1))
2350 end
2351 if self.IsOpen then
2352 self.MainFrame.Size = UDim2.new(1,0,#self.Elements,0)
2353 else
2354 self.MainFrame.Size = UDim2.new(1,0,1,0)
2355 end
2356 end
2357 function DropDown:Open()
2358 self.IsOpen = true
2359 self:Invalidate()
2360 end
2361 function DropDown:Close(elementSelected)
2362 for index,i in ipairs(self.Elements) do
2363 if i==elementSelected and index ~=1 then
2364 table.remove(self.Elements,index)
2365 table.insert(self.Elements,1,elementSelected)
2366 self.IsOpen=false
2367 for _,i in ipairs(self.Callbacks) do
2368 i(elementSelected)
2369 end
2370 self:Invalidate()
2371 return
2372 end
2373 end
2374 self.IsOpen=false
2375 self:Invalidate()
2376 end
2377 function DropDown:AddElement(element)
2378 table.insert(self.Elements,element)
2379 element.MouseButton1Down:connect(function()
2380 if self.IsOpen then
2381 self:Close(element)
2382 else
2383 self:Open()
2384 end
2385 end)
2386 self:Invalidate()
2387 end
2388 function DropDown:OnButtonSelected(func)
2389 table.insert(self.Callbacks,func)
2390 end
2391 function DropDown:GetCurrentSelection()
2392 return self.Elements[1]
2393 end
2394end
2395--[[Scroll Frame Class]]--
2396local ScrollFrame =
2397{
2398 Columns = 1,
2399 MainFrame = UTIL.Instantiate'Frame'
2400 {
2401 Name = "ScrollFrame",
2402 Size = UDim2.new(1,0,1,0),
2403 BackgroundTransparency =1,
2404 ClipsDescendants = true,
2405 UTIL.Instantiate'ImageButton'
2406 {
2407 Name = 'ContentFrame',
2408 Active = true,
2409 BackgroundTransparency =1,
2410 Size = UDim2.new(1,-18,100,0),
2411 },
2412 UTIL.Instantiate'Frame'
2413 {
2414 Name = "ControlFrame",
2415 Size = UDim2.new(0,18,1,0),
2416 Position =UDim2.new(1,-18,0,0),
2417 BackgroundTransparency = 1,
2418 UTIL.Instantiate'ImageButton'
2419 {
2420 Name = 'DragTab',
2421 Size = UDim2.new(1,0,1,-36),
2422 Style = 'RobloxButton',
2423 },
2424 UTIL.Instantiate'ImageButton'
2425 {
2426 Name = "UpButton",
2427 Size = UDim2.new(0,18,0,18),
2428 Style = 'RobloxButton',
2429 UTIL.Instantiate'ImageLabel'
2430 {
2431 Image = UTIL.ProdAssetURL..112365584,
2432 Size = UDim2.new(0,16,0,16),
2433 Position = UDim2.new(.5,-8,.5,-8),
2434 BackgroundTransparency = 1,
2435 }
2436 },
2437 UTIL.Instantiate'ImageButton'
2438 {
2439 Name = "DownButton",
2440 Position = UDim2.new(0,0,1,-18),
2441 Size = UDim2.new(0,18,0,18),
2442 Style = 'RobloxButton',
2443 UTIL.Instantiate'ImageLabel'
2444 {
2445 Image = UTIL.ProdAssetURL..112365220,
2446 Size = UDim2.new(0,16,0,16),
2447 Position = UDim2.new(.5,-8,.5,-8),
2448 BackgroundTransparency = 1,
2449 }
2450 },
2451 },
2452 },
2453 ContentFrame = nil,
2454 ControlFrame = nil,
2455 UpButton = nil,
2456 DownButton = nil,
2457 Entries = nil,
2458 ScrollAmount = 0,
2459 ButtonScrollAmount = 40,
2460 WheelScrollAmount = 20,
2461 BottomElementPosition = 0,
2462}
2463do
2464 UTIL.MakeClass(ScrollFrame)
2465 --[[class local functions]]--
2466 local function UDim2Add(a,b)
2467 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)
2468 end
2469 --[[class functions]]--
2470 function ScrollFrame.New()
2471 local init ={}
2472 init = UTIL.Class(init,ScrollFrame)
2473 init.MainFrame= ScrollFrame.MainFrame:Clone()
2474 init.ContentFrame= init.MainFrame:WaitForChild('ContentFrame')
2475 init.ControlFrame= init.MainFrame:WaitForChild('ControlFrame')
2476 init.UpButton= init.ControlFrame:WaitForChild('UpButton')
2477 init.DownButton= init.ControlFrame:WaitForChild('DownButton')
2478 init.UpButton.MouseButton1Down:connect(function() init:Scroll(init.ButtonScrollAmount) end)
2479 init.DownButton.MouseButton1Down:connect(function() init:Scroll(-init.ButtonScrollAmount) end)
2480 init.Entries = {}
2481 --untill I can get scroll wheel, this wont work :(
2482 --[[
2483 init.ContentFrame.MouseEnter:connect(function()
2484 print('connected to mouse wheel')
2485 local scrollConnectUp= UTIL.PlayerMouse.WheelForward:connect(function()
2486 print('scrolled up')
2487 init:Scroll(init.WheelScrollAmount)
2488 end)
2489 local scrollConnectDown= UTIL.PlayerMouse.WheelForward:connect(function()
2490 init:Scroll(-init.WheelScrollAmount)
2491 end)
2492 init.ContentFrame.MouseLeave:connect(function()
2493 print('disconnected mouse wheel')
2494 scrollConnectUp:disconnect()
2495 scrollConnectDown:disconnect()
2496 end)
2497 end)
2498 --]]
2499 init.ControlFrame.DragTab.MouseButton1Down:connect(function(x,y)init:StartTabDrag(x,y) end)
2500 init.MainFrame.Changed:connect(function(prop)
2501 if prop=='Size' or prop=='AbsoluteSize' or prop=='Parent' then
2502 ScrollFrame.Invalidate(init)
2503 end
2504 end)
2505 return init
2506 end
2507 function ScrollFrame:AddEntry(nEntry,nIndex)
2508 if nIndex then
2509 table.insert(self.Entries,nIndex,nEntry)
2510 else
2511 table.insert(self.Entries,nEntry)
2512 end
2513 nEntry.Parent = self.ContentFrame
2514 self:Invalidate()
2515 end
2516 function ScrollFrame:RemoveEntry(entry)
2517
2518 end
2519 function ScrollFrame:ClearEntries()
2520
2521 end
2522 function ScrollFrame:SetScroll(position)
2523
2524 end
2525 function ScrollFrame:Scroll(amount)
2526 self.ScrollAmount= self.ScrollAmount+amount
2527 self:InvalidateScrolling()
2528 end
2529 function ScrollFrame:StartTabDrag(startx,starty)
2530 local dragTab = self.ControlFrame.DragTab
2531 --local StartTabPos = dragTab.Position.Y.Scale
2532 local startScrollAmount = self.ScrollAmount
2533 local leaveConnect
2534 local upConnect
2535 local moveConnect
2536 local function updateDrag(x,y)
2537 local dragTabScale = (self.ControlFrame.AbsoluteSize.Y-36-self.ControlFrame.DragTab.AbsoluteSize.Y)/(self.BottomElementPosition)
2538 self.ScrollAmount = startScrollAmount-((y-starty)/dragTabScale)
2539 self:InvalidateScrolling()
2540 end
2541 local function endDrag(x,y)
2542 updateDrag(x,y)
2543 if leaveConnect then leaveConnect:disconnect() end
2544 if upConnect then upConnect:disconnect() end
2545 if moveConnect then moveConnect:disconnect() end
2546 end
2547 leaveConnect = dragTab.MouseLeave:connect(endDrag)
2548 upConnect = dragTab.MouseButton1Up:connect(endDrag)
2549 moveConnect = dragTab.MouseMoved:connect(updateDrag)
2550 end
2551
2552 function ScrollFrame:InvalidateScrolling()
2553 --print('scrollamount:'..self.ScrollAmount..' BottomPos:'..self.BottomElementPosition)
2554 if self.BottomElementPosition>0 then
2555 self.ControlFrame.Visible = true
2556 else
2557 self.ControlFrame.Visible = false
2558 end
2559 local dragTabScale = self.ControlFrame.AbsoluteSize.Y/(self.BottomElementPosition+self.ControlFrame.AbsoluteSize.Y)
2560 self.ControlFrame.DragTab.Size = UDim2.new(1,0,dragTabScale,-36)
2561 self.ScrollAmount = math.min(0,math.max(self.ScrollAmount,-self.BottomElementPosition))
2562 self.ControlFrame.DragTab.Position = UDim2.new(0,0,((self.ScrollAmount/(-self.BottomElementPosition)))*(1-dragTabScale),18)
2563 self.ContentFrame.Position = UDim2.new(0,0,0,self.ScrollAmount)
2564 end
2565 function ScrollFrame:Invalidate()
2566 local nextPos = UDim2.new(0,0,0,0)
2567 local CurrentColumn = 1
2568 local bottomElement = nil
2569 for _, i in ipairs(self.Entries) do
2570 i.Position = nextPos
2571 CurrentColumn=CurrentColumn+1
2572 if self.Columns<=0 then
2573 --print(i.Parent.AbsoluteSize.x)
2574 if i.Position.X.Offset+i.Size.X.Offset>i.Parent.AbsoluteSize.x then
2575 nextPos = UDim2.new(0,0,nextPos.Y.Scale+i.Size.Y.Scale,nextPos.Y.Offset+i.Size.Y.Offset)
2576 i.Position = nextPos
2577 end
2578 nextPos = UDim2Add(nextPos,UDim2.new(i.Size.X.Scale,i.Size.X.Offset,0,0))
2579 elseif CurrentColumn>self.Columns then
2580 nextPos = UDim2.new(0,0,nextPos.Y.Scale+i.Size.Y.Scale,nextPos.Y.Offset+i.Size.Y.Offset)
2581 CurrentColumn = 1
2582 else
2583 nextPos = UDim2Add(nextPos,UDim2.new(i.Size.X.Scale,i.Size.X.Offset,0,0))
2584 end
2585 bottomElement= i
2586 end
2587 if bottomElement then
2588 self.BottomElementPosition= bottomElement.AbsolutePosition.Y+bottomElement.AbsoluteSize.Y- self.ContentFrame.Parent.AbsoluteSize.Y-self.ContentFrame.Parent.AbsolutePosition.Y
2589 self:InvalidateScrolling()
2590 end
2591 end
2592 function ScrollFrame:SetColumns(ncol)
2593 self.Columns=ncol
2594 self:Invalidate()
2595 end
2596end
2597--[[Stamp Object Class]]--
2598local StampObject =
2599{
2600 IconURL = '',
2601 PreviewURL = '',
2602 AssetId = '',
2603 Name = '',
2604 Asset = nil,
2605}
2606do
2607 UTIL.MakeClass(StampObject)
2608 --[[class static variables/functions]]--
2609 local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower()
2610 local ThumbnailBase = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid="
2611 local PreviewBase = BaseUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid="
2612 --[[Class functions]]--
2613 function StampObject.New(name,assetId)
2614 local init = {}
2615 init.Name = name
2616 init.IconURL = ThumbnailBase..assetId
2617 init.PreviewURL = PreviewBase..assetId
2618 init.AssetId = assetId
2619 init.Asset = nil
2620 return UTIL.Class(init,StampObject)
2621 end
2622end
2623--[[Model Object Class]]--
2624--[[
2625 old cludge, canidate for deletion
2626
2627local ModelObject =
2628{
2629
2630}
2631do
2632 UTIL.MakeClass(ModelObject,StampObject)
2633 function ModelObject.New(assetId)
2634 local init = StampObject.New(assetId)
2635 return Class(init,ModelObject)
2636 end
2637end
2638]]--
2639--[[StamperSet Class]]--
2640local StamperSet =
2641{
2642 Name = '',
2643 Objects = {},
2644}
2645do
2646 UTIL.MakeClass(StamperSet)
2647 function StamperSet.New(setName,setId)
2648 local init ={}
2649 init = UTIL.Class(init,StamperSet)
2650 init.Name = setName
2651 init:InitSet(setId)
2652 return init
2653 end
2654 function StamperSet:InitSet(setId)
2655 -- print(setId)
2656 self.Objects = {}
2657 if type(setId)=='number' then
2658 for _,i in pairs(UTIL.InsertService:GetCollection(setId)) do
2659 table.insert(self.Objects,StampObject.New(i.Name,i.AssetId))
2660 end
2661 elseif type(setId)=='table' then
2662 for _,i in pairs(setId) do
2663 table.insert(self.Objects,StampObject.New("",i))
2664 end
2665 elseif setId:IsA('Instance') then
2666 for _,i in pairs(setId:GetChildren()) do
2667 if i:IsA('IntValue') then
2668 table.insert(self.Objects,StampObject.New(i.Name,i.Value))
2669 end
2670 end
2671 end
2672 end
2673end
2674--[[SetManager Class]]--
2675local SetManager =
2676{
2677 Sets = {},
2678}
2679do
2680
2681 UTIL.MakeClass(SetManager)
2682 function SetManager.New()
2683 local init = {}
2684 init.Sets={}
2685 return UTIL.Class(init,SetManager)
2686 end
2687 function SetManager:AddModelSet(nmodel)
2688 self.Sets[nmodel.Name]=StamperSet.New(nmodel.Name,nmodel)
2689 return self.Sets[nmodel.Name]
2690 end
2691 function SetManager:AddSet(setName,setId)
2692 --print(setName,setId)
2693 self.Sets[setName]=StamperSet.New(setName,setId)
2694 return self.Sets[setName]
2695 end
2696 function SetManager:AddUserSets(userId)
2697 local userSets = UTIL.InsertService:GetUserSets(userId)
2698 local nsets = {}
2699 for _, i in pairs(userSets) do
2700 if tonumber(i.AssetSetId)>0 then
2701 table.insert(nsets,self:AddSet(i.Name,tonumber(i.AssetSetId)))
2702 end
2703 end
2704 return nsets
2705 end
2706end
2707--[[Stamper Gui Class]]--
2708local StamperGui =
2709{
2710 ScreenGui = UTIL.Instantiate'ScreenGui'
2711 {
2712 Name = 'StamperScreen',
2713 UTIL.Instantiate'Frame'
2714 {
2715 Name = 'MainFrame',
2716 Position = UDim2.new(-1,29, 0.100000001, 24),
2717 Size = UDim2.new(0.600000024, -58, 0.639999986, 0),
2718 Style = 'RobloxRound',
2719 UTIL.Instantiate'Frame'
2720 {
2721 Name = 'Catagory',
2722 BackgroundTransparency =1,
2723 Size = UDim2.new(.22,0,1,0),
2724 },
2725 UTIL.Instantiate 'Frame'
2726 {
2727 Name = 'DividerLine',
2728 BackgroundTransparency =.7,
2729 Size = UDim2.new(0, 3, 0.95, 0),
2730 Position = UDim2.new(.22,0,.025,0),
2731 BorderSizePixel = 0,
2732 },
2733 UTIL.Instantiate 'Frame'
2734 {
2735 Name = 'DividerLine',
2736 BackgroundTransparency =.7,
2737 Size = UDim2.new(0, 3, 0.95, 0),
2738 Position = UDim2.new(.71,0,.025,0),
2739 BorderSizePixel = 0,
2740 },
2741 UTIL.Instantiate'Frame'
2742 {
2743 Name = 'StampObjs',
2744 BackgroundTransparency =1,
2745 Position = UDim2.new(.22,0,0,0),
2746 Size = UDim2.new(.5,0,1,0),
2747 },
2748 UTIL.Instantiate'Frame'
2749 {
2750 Name = 'PreviewFrame',
2751 BackgroundTransparency = 1,
2752 Position = UDim2.new(.72,0,0,0),
2753 Size = UDim2.new(.28,0,1,0),
2754 UTIL.Instantiate'ImageLabel'
2755 {
2756 Name='PreviewImage',
2757 BackgroundTransparency = 1,
2758 Image ="",
2759 Position = UDim2.new(0,0,0,0),
2760 Size = UDim2.new(1,0,1,0),
2761 SizeConstraint ='RelativeXX',
2762 UTIL.Instantiate'TextLabel'
2763 {
2764 Name = 'PreviewName',
2765 BackgroundTransparency = 1,
2766 Text = "",
2767 Font = 'ArialBold',
2768 FontSize = 'Size24',
2769 TextWrapped = true,
2770 TextColor3 = Color3.new(1,1,1),
2771 Position = UDim2.new(0,0,1,0),
2772 Size = UDim2.new(1,0,0,50),
2773 },
2774 UTIL.Instantiate'Frame'
2775 {
2776 Name = 'TerrainSettings',
2777 BackgroundTransparency = 1,
2778 Position = UDim2.new(0,0,1,50),
2779 Size = UDim2.new(1,0,0,150),
2780 Visible = false,
2781 },
2782 },
2783
2784 },
2785 UTIL.Instantiate'Frame'
2786 {
2787 Name = 'OpenFrame',
2788 Style = 'RobloxRound',
2789 Size = UDim2.new(0,64,0,43),
2790 Position = UDim2.new(1,8,0,0),
2791 UTIL.Instantiate'ImageButton'
2792 {
2793 Name = 'OpenButton',
2794 Image = UTIL.ProdAssetURL..112989057,
2795 BackgroundTransparency = 1,
2796 Size = UDim2.new(0,32,0,32),
2797 Position = UDim2.new(.5,-20,.5,-16)
2798 },
2799 UTIL.Instantiate'ImageButton'
2800 {
2801 Name = 'OpenButtonArrow',
2802 Image = UTIL.ProdAssetURL..112989231,
2803 BackgroundTransparency = 1,
2804 Size = UDim2.new(0,32,0,32),
2805 Position = UDim2.new(.5,10,.5,-16)
2806 },
2807
2808 },
2809 }
2810 },
2811 CallBacks = {},
2812 CatagoryFrame = ScrollFrame.New(),
2813 --ObjectFrame = ScrollFrame.New(),
2814 SetMan = SetManager:New(),
2815 PreviewImage = nil,
2816 PreviewText = nil,
2817 OpenButton = nil,
2818 OpenButtonArrow = nil,
2819 CurrentSet=nil,
2820 State = 'Inactive',--can be Active, Inactive or Waiting
2821 InactivePosition = UDim2.new(-0.6, 58, 0.3, 24),
2822 ActivePosition = UDim2.new(0.2, 29, 0.1, 24),
2823 TerrainShapeData =
2824 {
2825 {Name = 'Block',Id = 0,},
2826 {Name = 'Vertical Ramp',Id = 1,},
2827 {Name = 'Corner',Id = 2,},
2828 {Name = 'Inverse Corner',Id = 3,},
2829 {Name = 'Horisontal Ramp',Id = 4,},
2830 {Name = 'Auto-Wedge',Id = 6,},
2831 },
2832 CurrentTerrainShape = -1,
2833}
2834do
2835 UTIL.MakeClass(StamperGui)
2836
2837 --[[class functions]]--
2838 function StamperGui.New(nplayergui)
2839 local init = StamperGui
2840 init.PreviewImage = init.ScreenGui.MainFrame.PreviewFrame.PreviewImage
2841 init.PreviewText = init.PreviewImage.PreviewName
2842 init.OpenButton = init.ScreenGui.MainFrame.OpenFrame.OpenButton
2843 init.OpenButtonArrow = init.ScreenGui.MainFrame.OpenFrame.OpenButtonArrow
2844 init.OpenButton.MouseButton1Down:connect(function()
2845 init:ToggleGui(nplayergui)
2846 end)
2847 init:InitTerrainSettings()
2848 return init
2849 end
2850 function StamperGui:SetTerrainShape(name,id)
2851 for _, i in pairs(self.PreviewImage.TerrainSettings:GetChildren()) do
2852 if i.Name == name then
2853 i.Style = 'RobloxButtonDefault'
2854 i.TextColor3= Color3.new(1,1,1)
2855 else
2856 i.Style = 'RobloxButton'
2857 i.TextColor3 = Color3.new(.85,.85,.85)
2858 end
2859 end
2860 self.CurrentTerrainShape = id
2861 end
2862 function StamperGui:InitTerrainSettings()
2863 for index,i in ipairs(self.TerrainShapeData) do
2864 local tbut =UTIL.Instantiate'TextButton'
2865 {
2866 Name = i.Name,
2867 Text = i.Name,
2868 Size = UDim2.new(1,0,.16,0),
2869 Position = UDim2.new(0,0,.16*(index-1),0),
2870 TextColor3 = Color3.new(1,1,1),
2871 Font = 'ArialBold',
2872 FontSize = 'Size14',
2873 Parent = self.PreviewImage.TerrainSettings,
2874 Style = 'RobloxButton',
2875 }
2876 tbut.MouseButton1Down:connect(function()
2877 self:SetTerrainShape(i.Name,i.Id)
2878 end)
2879 end
2880 end
2881 function StamperGui:CreateObjectButton(stampObj)
2882 game:GetService("ContentProvider"):Preload(stampObj.IconURL)
2883 local tbut =UTIL.Instantiate'ImageButton'
2884 {
2885 Name = stampObj.Name,
2886 Size = UDim2.new(0,64,0,64),
2887 BackgroundTransparency = 1,
2888 UTIL.Instantiate'TextButton'
2889 {
2890 Name = 'Button',
2891 Text = '',
2892 Size =UDim2.new(0,60,0,60),
2893 Position = UDim2.new(0,2,0,2),
2894 Style = 'RobloxButton',
2895
2896 },
2897 UTIL.Instantiate'ImageLabel'
2898 {
2899 Name = 'Image',
2900 Image = stampObj.IconURL,
2901 Active = false,
2902 BackgroundTransparency = 1,
2903 Size = UDim2.new(0,48,0,48),
2904 Position = UDim2.new(0,8,0,8),
2905 },
2906 }
2907 tbut.Button.MouseButton1Down:connect(function()
2908 for _, j in pairs(self.CallBacks) do
2909 j(stampObj,self.CurrentTerrainShape)
2910 end
2911 end)
2912 tbut.Button.MouseEnter:connect(function()
2913 self.PreviewImage.Image= stampObj.IconURL
2914 game:GetService("ContentProvider"):Preload(stampObj.PreviewURL)
2915 self.PreviewImage.Image= stampObj.PreviewURL
2916 self.PreviewText.Text = stampObj.Name
2917 end)
2918
2919 return tbut
2920 end
2921 function StamperGui:CreateCatagoryButton(setObj)
2922 local outputGui = self.ScreenGui.MainFrame.StampObjs
2923 local tbut =UTIL.Instantiate'TextButton'
2924 {
2925 Name = setObj.Name,
2926 Text = setObj.Name,
2927 Font = 'Arial',
2928 FontSize = 'Size18',
2929 TextXAlignment = 'Left',
2930 BackgroundTransparency=1,
2931 AutoButtonColor = false,
2932 TextColor3 = Color3.new(1,1,1),
2933 Size = UDim2.new(1,0,0,20),
2934 ClipsDescendants = true,
2935 }
2936 local ObjectFrame = ScrollFrame.New()
2937 ObjectFrame:SetColumns(-1)
2938 for _,i in ipairs(setObj.Objects) do
2939 local obut = self:CreateObjectButton(i)
2940 ObjectFrame:AddEntry(obut,1)
2941 end
2942 tbut.MouseEnter:connect(function()
2943 if self.CurrentSet~=tbut then
2944 tbut.BackgroundTransparency = 0
2945 tbut.BackgroundColor3 = Color3.new(1,1,1)
2946 tbut.TextColor3 = Color3.new(0,0,0)
2947 end
2948 self.PreviewImage.Image= ""
2949 self.PreviewText.Text = ""
2950 end)
2951 tbut.MouseLeave:connect(function()
2952 if self.CurrentSet~=tbut then
2953 tbut.BackgroundTransparency = 1
2954 tbut.TextColor3 = Color3.new(1,1,1)
2955 end
2956 end)
2957 local function OnMouseDown()
2958 if self.CurrentSet then
2959 self.CurrentSet.BackgroundTransparency = 1
2960 self.CurrentSet.TextColor3 = Color3.new(1,1,1)
2961 end
2962 self.CurrentSet=tbut
2963 tbut.BackgroundTransparency = 0
2964 tbut.BackgroundColor3 = Color3.new(0,204/255,0)
2965 tbut.TextColor3 = Color3.new(0,0,0)
2966 for _,i in pairs(outputGui:GetChildren()) do
2967 i.Parent = nil
2968 end
2969 --[[HACKITY HACK]]--
2970 if setObj.Name == 'High Scalability' then
2971 self.PreviewImage.TerrainSettings.Visible = true
2972 StamperGui:SetTerrainShape('Block',0)
2973 else
2974 self.PreviewImage.TerrainSettings.Visible = false
2975 StamperGui:SetTerrainShape('nill',-1)
2976 end
2977 --[[dont come back]]--
2978 ObjectFrame.MainFrame.Parent = outputGui
2979 end
2980 tbut.MouseButton1Down:connect(OnMouseDown)
2981 if #self.CatagoryFrame.Entries ==0 then
2982 OnMouseDown()
2983 end
2984 return tbut
2985 end
2986 function StamperGui:ToggleGui(nplayerGui)
2987 if self.State=='Active' then
2988 self:RemoveGui()
2989 elseif self.State=='Inactive' then
2990 self:AddGui(nplayerGui)
2991 end
2992 end
2993 function StamperGui:AddGui(nplayerGui)
2994 self.State='Waiting'
2995 assert(nplayerGui, 'ERROR, got bad playerGui')
2996 --setting up catagory frame
2997 self.CatagoryFrame.MainFrame.Parent = self.ScreenGui.MainFrame.Catagory
2998 self.OpenButtonArrow.Image = UTIL.ProdAssetURL..112990112
2999 self.ScreenGui.Parent = nplayerGui
3000 self.ScreenGui.MainFrame:TweenPosition(self.ActivePosition, "Out", "Linear", .25, true)
3001 Delay(.25,function() self.State='Active' end)
3002
3003 end
3004 function StamperGui:RemoveGui()
3005 self.State='Waiting'
3006 self.OpenButtonArrow.Image = UTIL.ProdAssetURL..112989231
3007 self.ScreenGui.MainFrame:TweenPosition(self.InactivePosition, "Out", "Linear", .25, true)
3008 Delay(.25,function()
3009 --self.ScreenGui.Parent = nil
3010 self.State='Inactive'
3011 end)
3012
3013 end
3014 function StamperGui:AddSet(nset)
3015 print('Adding set')
3016 if type(nset)=='number' then
3017 --[[WARNING, bottleneck here, fetching collection twice]]--
3018 -- create new path in setgui that accepts a collection to fix
3019 local tcollection = UTIL.InsertService:GetCollection(i)
3020 self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(self.SetMan:AddSet(tcollection.Name,nset)))
3021 else
3022 self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(self.SetMan:AddModelSet(nset)))
3023 end
3024 end
3025 function StamperGui:AddUserSets(nuser)
3026 local nsets = self.SetMan:AddUserSets(nuser)
3027 for _,i in pairs(nsets) do
3028 self.CatagoryFrame:AddEntry(self:CreateCatagoryButton(i))
3029 end
3030 end
3031 function StamperGui:RemoveSet(nset)
3032
3033 end
3034 function StamperGui:OnObjectSelected(callback)
3035 table.insert(self.CallBacks,callback)
3036 end
3037
3038
3039end
3040
3041--[[Water Gui Class]]--
3042local WaterGui =
3043{
3044 WaterMenu = UTIL.Instantiate'Frame'
3045 {
3046 Name='WaterFrame',
3047 Visible = false,
3048 Size = UDim2.new(0,120,0,110),
3049 Position = UDim2.new(0,55,0,0),
3050 BackgroundColor3 = Color3.new(0,0,0),
3051 BackgroundTransparency = .4,
3052 UTIL.Instantiate'TextLabel'
3053 {
3054 Text = 'Force',
3055 BackgroundTransparency = 1,
3056 Font = 'ArialBold',
3057 FontSize = 'Size18',
3058 TextColor3=Color3.new(1,1,1),
3059 Position = UDim2.new(0,10,0,20),
3060 TextYAlignment = 'Bottom',
3061 TextXAlignment = 'Left',
3062 },
3063 UTIL.Instantiate'Frame'
3064 {
3065 Name = 'ForceFrame',
3066 BackgroundTransparency = 1,
3067 Size = UDim2.new(1,0,0,25),
3068 Position = UDim2.new(0,0,0,25),
3069 },
3070 UTIL.Instantiate'TextLabel'
3071 {
3072 Text = 'Direction',
3073 BackgroundTransparency = 1,
3074 Font = 'ArialBold',
3075 FontSize = 'Size18',
3076 TextColor3=Color3.new(1,1,1),
3077 Position = UDim2.new(0,10,0,73),
3078 TextYAlignment = 'Bottom',
3079 TextXAlignment = 'Left',
3080 },
3081 UTIL.Instantiate'Frame'
3082 {
3083 Name = 'DirectionFrame',
3084 BackgroundTransparency = 1,
3085 Size = UDim2.new(1,0,0,25),
3086 Position = UDim2.new(0,0,0,75),
3087 },
3088 },
3089 WaterForceDropDown = DropDown.New(),
3090 WaterDirectionDropDown = DropDown.New(),
3091 ForceTypes = {'None','Small','Medium','Max','Strong'},
3092 DirectionTypes = {[' X']='X',['-X']='NegX',[' Y']='Y',['-Y']='NegY',[' Z']='Z',['-Z']='NegZ'},
3093}
3094do
3095 UTIL.MakeClass(WaterGui)
3096 function WaterGui.New(screenGui)
3097 local templatebut = UTIL.Instantiate'TextButton'
3098 {
3099 Size = UDim2.new(1,0,0,25),
3100 BackgroundTransparency =1,
3101 Font = 'ArialBold',
3102 FontSize = 'Size24',
3103 TextColor3=Color3.new(1,1,1),
3104 }
3105 for _, i in ipairs(WaterGui.ForceTypes) do
3106 local tbut = templatebut:Clone()
3107 tbut.ZIndex = 2
3108 tbut.Text = i
3109 WaterGui.WaterForceDropDown:AddElement(tbut)
3110 end
3111 for i, _ in pairs(WaterGui.DirectionTypes) do
3112 local tbut = templatebut:Clone()
3113 tbut.ZIndex = 1
3114 tbut.Text = i
3115 WaterGui.WaterDirectionDropDown:AddElement(tbut)
3116 end
3117 WaterGui.WaterMenu.Parent = screenGui
3118 WaterGui.WaterForceDropDown.MainFrame.Parent = WaterGui.WaterMenu.ForceFrame
3119 WaterGui.WaterForceDropDown.MainFrame.ZIndex = 2
3120 WaterGui.WaterDirectionDropDown.MainFrame.Parent = WaterGui.WaterMenu.DirectionFrame
3121 return WaterGui
3122 end
3123 function WaterGui:GetWaterData()
3124 return self.WaterForceDropDown:GetCurrentSelection().Text,
3125 self.DirectionTypes[self.WaterDirectionDropDown:GetCurrentSelection().Text]
3126 end
3127end
3128
3129--[[Recent Parts Gui Class]]--
3130
3131local RecentPartsGui =
3132{
3133 Gui = UTIL.Instantiate'Frame'
3134 {
3135 Name = 'RecentPartsFrame',
3136 Size = UDim2.new(0,50,0,0),
3137 Style = 'RobloxRound',
3138 Position = UDim2.new(0,-5,0.4, 70),
3139 },
3140 Objects = {},
3141 Callbacks = {},
3142 NumSaved = 5,
3143}
3144do
3145 UTIL.MakeClass(RecentPartsGui)
3146 function RecentPartsGui:MakeRecentIcon(stampObj)
3147 local tbut =UTIL.Instantiate'ImageButton'
3148 {
3149 Name = stampObj.Name,
3150 Size = UDim2.new(0,50,0,50),
3151 Position = UDim2.new(0,-5,0,-10),
3152 BackgroundTransparency = 1,
3153 UTIL.Instantiate'TextButton'
3154 {
3155 Name = 'Button',
3156 Text = '',
3157 Size =UDim2.new(.90909,0,.90909,0),
3158 Position = UDim2.new(.03636363,0,.03636363,0),
3159 Style = 'RobloxButton',
3160
3161 },
3162 UTIL.Instantiate'ImageLabel'
3163 {
3164 Name = 'Image',
3165 Image = stampObj.IconURL,
3166 Active = false,
3167 BackgroundTransparency = 1,
3168 Size = UDim2.new(.63636363,0,.63636363,0),
3169 Position = UDim2.new(.1454545,0,.1454545,0),
3170 },
3171 }
3172
3173 tbut.Button.MouseEnter:connect(function()
3174 tbut.Size = UDim2.new(0,55,0,55)
3175 tbut.Position = UDim2.new(0,0,tbut.Position.Y.Scale,tbut.Position.Y.Offset)
3176 end)
3177 tbut.Button.MouseLeave:connect(function()
3178 tbut.Size = UDim2.new(0,50,0,50)
3179 tbut.Position = UDim2.new(0,-5,tbut.Position.Y.Scale,tbut.Position.Y.Offset)
3180 end)
3181
3182 tbut.Button.MouseButton1Down:connect(function()
3183 for _, j in pairs(self.Callbacks) do
3184 j(stampObj)
3185 end
3186 end)
3187 return tbut
3188 end
3189 function RecentPartsGui:Open()
3190 --self.Gui:TweenPosition
3191 end
3192 function RecentPartsGui:Close()
3193
3194 end
3195 function RecentPartsGui:Invalidate()
3196 while #self.Objects>self.NumSaved do
3197 self.Objects[1][2].Parent = nil
3198 table.remove(self.Objects,1)
3199 end
3200 self.Gui.Size = UDim2.new(0,50,0,50*#self.Objects)
3201 for index= 1, #self.Objects, 1 do
3202 local i = self.Objects[#self.Objects-(index-1)]
3203 i[2].Parent = self.Gui
3204 i[2].Position = UDim2.new(0,i[2].Position.X.Offset,0,(i[2].Size.Y.Offset*(index-1))-10)
3205 end
3206 end
3207 function RecentPartsGui:AddStampObject(stampObj)
3208 for index,i in pairs(self.Objects) do
3209 if i[1]==stampObj then
3210 table.remove(self.Objects,index)
3211 end
3212 end
3213 table.insert(self.Objects,{stampObj,self:MakeRecentIcon(stampObj)})
3214 self:Invalidate()
3215 end
3216 function RecentPartsGui:OnPartSelected(func)
3217 table.insert(self.Callbacks,func)
3218 end
3219end
3220
3221--[[Stamper Main Class]]--
3222local StamperMain =
3223{
3224 Gui = StamperGui.New(UTIL.LocalPlayer.PlayerGui),
3225 WaterGui = nil,
3226 RecentPartsGui = RecentPartsGui.New(),
3227 CurrentObject = nil,
3228 StampArea = nil,
3229 Mouse = nil,
3230 RbxStamper = RBXStamper,
3231 StampMadeConnect = nil,
3232 ActiveStampController = nil,
3233 StampModel =nil,
3234 RestrictedRegion = nil,
3235 ObjectCache= {},
3236 ObjectCacheSize = 1,
3237 MouseIcons =
3238 {
3239 Normal = 112497861,
3240 Stamp = 112497874,
3241 Error = 112497904,
3242 Hourglass1 =112979217,--112978896,
3243 Hourglass2 = 112979274,--112978935,
3244 },
3245 WaterAssetId = 82717697,
3246 LastWaterObject = nil,
3247}
3248do
3249 UTIL.MakeClass(StamperMain)
3250 function StamperMain:New()
3251 local init = {}
3252 init = UTIL.Class(init,StamperMain)
3253 init.RecentPartsGui.Gui.Parent = init.Gui.ScreenGui
3254 init.RecentPartsGui:OnPartSelected(function(stampObj) init:SetStampObject(stampObj,0) end)
3255 init.WaterGui= WaterGui.New(init.Gui.ScreenGui)
3256 init.WaterGui.WaterForceDropDown:OnButtonSelected(function()
3257 print('got force changed') init:SetStampObject(init.LastWaterObject,0) end)
3258 init.WaterGui.WaterDirectionDropDown:OnButtonSelected(function()
3259 init:SetStampObject(init.LastWaterObject,0) end)
3260 init.Gui:OnObjectSelected(function (i,shape) init:SetStampObject(i,shape) end)
3261 init.ObjectCacheSize = 20
3262 return init
3263 end
3264 function StamperMain:SetMouse(nmouse)
3265 self.Mouse = nmouse
3266 self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
3267 end
3268 do
3269 local FlashId = 0
3270 function StamperMain:FlashMouse(nicon,length)
3271 FlashId=FlashId+1
3272 local myflash = FlashId
3273 self.Mouse.Icon = UTIL.ProdAssetURL..nicon
3274 Delay(length,function()
3275 if FlashId==myflash then
3276 self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
3277 end
3278 end)
3279 end
3280 end
3281 function StamperMain:SetRestrictedRegion(region)
3282 self.RestrictedRegion = region
3283 end
3284 function StamperMain:SetStampModel(nmodel)
3285 self.StampModel = nmodel
3286 end
3287 function StamperMain:OnMouseDown(x,y)
3288
3289 end
3290 function StamperMain:SetWaterProperties(asset,force,direction)
3291 print('setting props:'..force..' '..direction)
3292 if not asset:FindFirstChild('WaterForceTag') then
3293 local ntag = Instance.new('StringValue')
3294 ntag.Name = 'WaterForceTag'
3295 ntag.Value = force
3296 ntag.Parent = asset
3297 else
3298 asset.WaterForceTag.Value = force
3299 end
3300 if not asset:FindFirstChild('WaterForceDirectionTag') then
3301 local ntag = Instance.new('StringValue')
3302 ntag.Name = 'WaterForceDirectionTag'
3303 ntag.Value= direction
3304 ntag.Parent = asset
3305 else
3306 asset.WaterForceDirectionTag.Value = direction
3307 end
3308 end
3309
3310 function StamperMain:LoadStampObject(stampObj,terrainShape)
3311
3312 --[[WARNING, not using cacheing for terrain yet]]--
3313 if tonumber(stampObj.AssetId)==self.WaterAssetId then
3314 print('Loaded Water obj:'.. stampObj.AssetId)
3315 self.LastWaterObject = stampObj
3316 if not stampObj.Asset then
3317 stampObj.Asset = self.RbxStamper.GetStampModel(tonumber(stampObj.AssetId), terrainShape)
3318 end
3319 self:SetWaterProperties(stampObj.Asset,self.WaterGui:GetWaterData())
3320 elseif terrainShape and terrainShape>=0 then
3321 print('Loaded terrain obj:'.. stampObj.AssetId)
3322 stampObj.Asset = self.RbxStamper.GetStampModel(tonumber(stampObj.AssetId), terrainShape)
3323 elseif not stampObj.Asset then
3324 print('Loaded model obj:'.. stampObj.AssetId)
3325 stampObj.Asset = UTIL.InsertService:LoadAsset( stampObj.AssetId ):GetChildren()[1]
3326 table.insert(self.ObjectCache, stampObj)
3327 if #self.ObjectCache > self.ObjectCacheSize then
3328 self.ObjectCache[1].Asset:Remove()
3329 --[[WARNING, Order n operation, replace with stack]]--
3330 table.remove(self.ObjectCache,1)
3331 print('removed object!')
3332 end
3333 end
3334 end
3335
3336 function StamperMain:SetStampObject(stampObj,terrainShape)
3337 if tonumber(stampObj.AssetId)==self.WaterAssetId then
3338 self.WaterGui.WaterMenu.Visible=true
3339 else
3340 self.WaterGui.WaterMenu.Visible=false
3341 end
3342
3343 self.Gui:RemoveGui()
3344 local isLoading = true
3345 Spawn(function()
3346 while isLoading do
3347 self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Hourglass1
3348 wait(.4)
3349 if isLoading then
3350 self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Hourglass2
3351 wait(.4)
3352 end
3353 end
3354 end)
3355 self:LoadStampObject(stampObj,terrainShape)
3356 isLoading = false
3357 self.Mouse.Icon = UTIL.ProdAssetURL..self.MouseIcons.Normal
3358 self.RecentPartsGui:AddStampObject(stampObj)
3359
3360 if self.ActiveStampController then self.ActiveStampController.Destroy() end
3361 if self.StampMadeConnect then self.StampMadeConnect:disconnect() end
3362
3363
3364 local function stampFailed()
3365 self:FlashMouse(self.MouseIcons.Error,.1)
3366 end
3367 self.ActiveStampController = self.RbxStamper.SetupStamperDragger(stampObj.Asset,self.Mouse,
3368 self.StampModel,self.RestrictedRegion,stampFailed)
3369
3370 self.StampMadeConnect = self.ActiveStampController.Stamped.Changed:connect(function()
3371 if self.ActiveStampController.Stamped.Value then
3372 self:FlashMouse(self.MouseIcons.Stamp,.2)
3373 self.ActiveStampController.ReloadModel()
3374 end
3375 end)
3376
3377 end
3378 function StamperMain:Destroy()
3379 print('unconnected!')
3380 if self.ActiveStampController then self.ActiveStampController.Destroy() end
3381 if self.StampMadeConnect then self.StampMadeConnect:disconnect() end
3382 self.ActiveStampController=nil
3383 self.StampMadeConnect = nil
3384 self.Gui.ScreenGui.Parent = nil
3385 end
3386end
3387------------------
3388--[[Tool Logic]]--
3389------------------
3390do
3391 --[[Tool Related Varibles]]--
3392 local MyStamper = StamperMain.New()
3393 local Tool = script.Parent
3394 local Sets = script:WaitForChild('Sets')
3395 local Users = script:WaitForChild('Users')
3396 local MyStampModel = script:WaitForChild('MyStampModel')
3397 local RestrictedArea = script:WaitForChild('RestrictedArea')
3398 local MaxObjects = script:FindFirstChild('MaxObjects')
3399 --[[Tool Related functions]]--
3400 local function InitToolSets()
3401 print('finished adding sets')
3402 for _, i in pairs(Users:GetChildren()) do
3403 MyStamper.Gui:AddUserSets(i.Value)
3404 end
3405 print('in init tool sets')
3406 for _, i in pairs(Sets:GetChildren()) do
3407 print('In sets')
3408 if i:IsA('IntValue') then
3409 MyStamper.Gui:AddSet(i.Value)
3410 else
3411 print('Adding ' .. i.Name)
3412 MyStamper.Gui:AddSet(i)
3413 end
3414 end
3415 end
3416 local function InitMyStampModel()
3417 UTIL.RetroRegister(function() MyStamper:SetStampModel(MyStampModel.Value) end, MyStampModel.Changed)
3418 end
3419 local function InitRestrictedRegion()
3420 local function pushUpdate()
3421 if RestrictedArea:FindFirstChild('Low') and RestrictedArea:FindFirstChild('High') then
3422 MyStamper:SetRestrictedRegion(Region3.new(RestrictedArea.Low.Value,RestrictedArea.High.Value))
3423 else
3424 MyStamper:SetRestrictedRegion(nil)
3425 end
3426 end
3427 UTIL.RetroRegister(pushUpdate,RestrictedArea.ChildAdded,RestrictedArea.ChildRemoved)
3428 end
3429 --[[Running Logic]]--
3430
3431 InitMyStampModel()
3432 InitRestrictedRegion()
3433 Spawn(InitToolSets)
3434
3435 Tool.Equipped:connect(
3436 function(nmouse)
3437 local MyPlayer = Game.Players:GetPlayerFromCharacter(Tool.Parent)
3438 if not MyPlayer then return end
3439 MyStamper:SetMouse(nmouse)
3440 --[[SLIGHT HACK]]--
3441 nmouse.KeyDown:connect(function(key)
3442 if key =='q' then
3443 MyStamper.Gui:ToggleGui(UTIL.LocalPlayer.PlayerGui)
3444 end
3445 end)
3446 --[[END SLIGHT HACK]]--
3447 MyStamper.Gui:AddGui(MyPlayer.PlayerGui)
3448 if #MyStamper.RecentPartsGui.Objects>0 then
3449 MyStamper:SetStampObject(MyStamper.RecentPartsGui.Objects[#MyStamper.RecentPartsGui.Objects][1])
3450 end
3451 end
3452 )
3453
3454 Tool.Unequipped:connect(
3455 function()
3456 print('in unequip')
3457 if not MyStamper then return end
3458 print('past check')
3459 MyStamper:Destroy()
3460 print('past destory')
3461 end
3462 )
3463end
3464
3465end))
3466Model4.Name = "Sets"
3467Model4.Parent = LocalScript3
3468ObjectValue5.Name = "MyStampModel"
3469ObjectValue5.Parent = LocalScript3
3470Model6.Name = "RestrictedArea"
3471Model6.Parent = LocalScript3
3472Model7.Name = "Users"
3473Model7.Parent = LocalScript3
3474IntValue8.Name = "Default1"
3475IntValue8.Parent = Model7
3476IntValue8.Value = 11744447
3477IntValue9.Name = "Default2"
3478IntValue9.Parent = Model7
3479IntValue9.Value = 18881789
3480IntValue10.Name = "Default3"
3481IntValue10.Parent = Model7
3482IntValue10.Value = 18881808
3483IntValue11.Name = "nonRestrict1"
3484IntValue11.Parent = Model7
3485IntValue11.Value = 18881829
3486IntValue12.Name = "nonRestrict2"
3487IntValue12.Parent = Model7
3488IntValue12.Value = 18881853
3489IntValue13.Name = "nonRestrict3"
3490IntValue13.Parent = Model7
3491IntValue13.Value = 18881866
3492LocalScript14.Name = "README"
3493LocalScript14.Parent = LocalScript3
3494table.insert(cors,sandbox(LocalScript14,function()
3495--[[
3496 New Stamper
3497 UpdateLog:
3498 4/16/2013-initial release
3499
3500 How to add user sets:
3501 add user IDs as intValue's in the Users model to add all sets related to that user
3502 How to add induvidual sets:
3503 add set IDs as intValue's in the Sets model
3504 How to make custom sets:
3505 add models under the Sets model, containing intValue's with asset IDs
3506 How to limit stamping area:
3507 inside the RestrictedArea model, add 2 Vector3Value's named "High" and "Low", these will form the Region3 this tool can stamp in
3508 How to Stamp inside a model:
3509 Set the value of MyStampModel to the object you want to place stamped models inside
3510
3511 Contact fusroblox for any and all issues relating to Stamper
3512--]]
3513end))
3514for i,v in pairs(mas:GetChildren()) do
3515 v.Parent = script
3516 pcall(function() v:MakeJoints() end)
3517end
3518mas:Destroy()
3519for i,v in pairs(cors) do
3520 spawn(function()
3521 pcall(v)
3522 end)
3523end