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