· 4 years ago · Apr 11, 2021, 11:12 PM
1-- basic functions
2
3function waitForChild(instance, name)
4 while not instance:findFirstChild(name) do
5 instance.ChildAdded:wait()
6 end
7end
8----------------------------------------------------------------------------------------
9
10-- Locals
11
12local Tool = script.Parent
13local lolmousehit = CFrame.new(0,0,0)
14local lolmousetarget = nil
15local lolmousetargetFilter
16local lolmousetargetSurface
17local Mouse
18local mouseMoveCon
19local mouseButton1DownCon
20local mouseButton1UpCon
21local cameraChangeCon
22
23local walking = false
24
25local pressedEsc = false
26
27local billBoardOwnerGui = nil
28
29local cluster = game.Workspace:FindFirstChild("Terrain")
30
31local gInitial90DegreeRotations = 0
32local gStaticTrans = 1
33local gDesiredTrans = 1
34local transFadeInTime = 0.5
35local fadeInDelayTime = 0.5
36local eyedropperOffGridTolerance = 0.01
37
38local insertBoundingBoxOverlapVector = Vector3.new(1, 1, 1) -- we can still stamp if our character extrudes into the target stamping space by 1 or fewer units
39
40local useAssetVersionId = false
41
42
43-- for high-scalability display
44local adornPart = Instance.new("Part")
45adornPart.Parent = nil
46adornPart.formFactor = "Custom"
47adornPart.Size = Vector3.new(4, 4, 4)
48adornPart.CFrame = CFrame.new()
49
50local adorn = Instance.new("SelectionBox")
51adorn.Color = BrickColor.new("Toothpaste")
52adorn.Adornee = adornPart
53adorn.Visible = true
54adorn.Transparency = 0
55adorn.Name = "HighScalabilityStamperLine"
56adorn.Parent = nil
57
58local terrainSelectionBox = Instance.new("Part")
59terrainSelectionBox.Parent = nil
60terrainSelectionBox.formFactor = "Custom"
61terrainSelectionBox.Size = Vector3.new(4, 4, 4)
62terrainSelectionBox.CFrame = CFrame.new()
63
64local HighScalabilityLine = {}
65HighScalabilityLine.Start = nil
66HighScalabilityLine.End = nil
67HighScalabilityLine.Adorn = adorn
68HighScalabilityLine.AdornPart = adornPart
69HighScalabilityLine.InternalLine = nil
70HighScalabilityLine.NewHint = true
71
72-- for higher dimensional megacluster part stamping
73HighScalabilityLine.MorePoints = {nil, nil}
74HighScalabilityLine.MoreLines = {nil, nil}
75HighScalabilityLine.Dimensions = 1
76
77waitForChild(Tool,"LuaGlobalVariables")
78local variables = Tool.LuaGlobalVariables
79waitForChild(variables,"ShowInvalidPlacement")
80waitForChild(variables, "Stamped")
81waitForChild(Tool,"ErrorBox")
82waitForChild(variables, "ShowAdminCategories")
83local errorBox = Tool.ErrorBox
84
85waitForChild(variables, "IsRestricted")
86waitForChild(variables, "MouseClick")
87click = variables.MouseClick
88
89local Data = {}
90Data.Stamp = {}
91Data.Loading = {}
92
93local guiScriptIsLoadingSomething = false
94local unstampableSurface = false
95
96local eyeDropperConnection, eyeDropperMoveConnection
97
98local playerModel
99local player
100
101local lastTargetCFrame = nil
102local lastTargetTerrainOrientation = 0
103
104-- For Restricting Stamper Tool
105local isRestricted = variables.IsRestricted.Value
106local adminAccess = variables.ShowAdminCategories.Value
107
108-- For Delete highlighting
109local selectionBox
110local currentSelection
111local currentSelectionColors = {}
112
113if isRestricted then waitForChild(game.Workspace, "BaseplateBumpers") end
114----------------------------------------------------------------------------------------
115
116-- Functions
117function hint(label)
118 -- Pass in a string, it shows a top hint. (Replaces previous hint, if exists)
119 _player = game.Players:GetPlayerFromCharacter(Tool.Parent)
120 if(_player.PlayerGui:FindFirstChild("topHint")~=nil) then
121 local topHint = _player.PlayerGui.topHint
122 topHint.Add.Label.Value = label
123 topHint.Add.Width.Value = 3 -- widest width
124 topHint.Add.Time.Value = 5
125 topHint.Add.Disabled = true
126 topHint.Add.Disabled = false
127 end
128end
129
130
131function getClosestColorToTerrainMaterial(terrainValue)
132 if terrainValue == 1 then
133 return BrickColor.new("Bright green")
134 elseif terrainValue == 2 then
135 return BrickColor.new("Bright yellow")
136 elseif terrainValue == 3 then
137 return BrickColor.new("Bright red")
138 elseif terrainValue == 4 then
139 return BrickColor.new("Medium stone grey")
140 else
141 return BrickColor.new("Bright green")
142 end
143end
144
145
146local manualWeldTable = {}
147local manualWeldParentTable = {}
148function saveTheWelds(object)
149 if object:IsA("ManualWeld") or object:IsA("Rotate") then
150 table.insert(manualWeldTable, object)
151 table.insert(manualWeldParentTable, object.Parent)
152 else
153 local children = object:GetChildren()
154 for i = 1, #children do
155 saveTheWelds(children[i])
156 end
157 end
158end
159
160function restoreTheWelds()
161 for i = 1, #manualWeldTable do
162 manualWeldTable[i].Parent = manualWeldParentTable[i]
163 end
164end
165
166
167function findSeatsInModel(parent, seatTable)
168 if not parent then return end
169
170 if parent.className == "Seat" or parent.className == "VehicleSeat" then
171 table.insert(seatTable, parent)
172 end
173 local myChildren = parent:GetChildren()
174 for j = 1, #myChildren do
175 findSeatsInModel(myChildren[j], seatTable)
176 end
177end
178
179function setSeatEnabledStatus(model, isEnabled)
180 local seatList = {}
181 findSeatsInModel(model, seatList)
182
183 if isEnabled then
184 -- remove any welds called "SeatWeld" in seats
185 for i = 1, #seatList do
186 local nextSeat = seatList[i]:FindFirstChild("SeatWeld")
187 while nextSeat do nextSeat:Remove() nextSeat = seatList[i]:FindFirstChild("SeatWeld") end
188 end
189 else
190 -- put a weld called "SeatWeld" in every seat
191 -- this tricks it into thinking there's already someone sitting there, and it won't make you sit XD
192 for i = 1, #seatList do
193 local fakeWeld = Instance.new("Weld")
194 fakeWeld.Name = "SeatWeld"
195 fakeWeld.Parent = seatList[i]
196 end
197 end
198end
199
200
201function UnlockInstances(object)
202
203 if object:IsA("BasePart") then
204 object.Locked = false
205 end
206 for index,child in pairs(object:GetChildren()) do
207 UnlockInstances(child)
208 end
209
210end
211
212function generateOwnerGui(playerName)
213 local gui = Instance.new("BillboardGui")
214 gui.Name = "PlayerStamperTagGui"
215 gui.StudsOffset = Vector3.new(0,1,0)
216 gui.ExtentsOffset = Vector3.new(0,1,0)
217 gui.Size = UDim2.new(5,0,2,0)
218 pcall(function() gui.PlayerToHideFrom = game.Players:GetPlayerFromCharacter(script.Parent.Parent) end)
219
220 local frame = Instance.new("Frame")
221 frame.BackgroundColor3 = Color3.new(0,0,0)
222 frame.BackgroundTransparency = 0.5
223 frame.Name = "OwnerFrame"
224 frame.Size = UDim2.new(1,0,1,0)
225 frame.Parent = gui
226
227 local ownerName = Instance.new("TextLabel")
228 ownerName.Name = "OwnerName"
229 ownerName.Size = UDim2.new(1,0,1,0)
230 ownerName.Text = playerName
231 ownerName.Font = Enum.Font.ArialBold
232 ownerName.FontSize = Enum.FontSize.Size14
233 ownerName.TextWrap = true
234 ownerName.TextColor3 = Color3.new(1,1,1)
235 ownerName.TextStrokeTransparency = 0
236 ownerName.BackgroundTransparency = 1
237 ownerName.Parent = frame
238
239 return gui
240end
241
242
243
244function getPlayer()
245 return game.Players:GetPlayerFromCharacter(script.Parent.Parent)
246end
247
248function beginInsertAssetStamp(assetName, assetId, image, stampMode)
249 -- trying to stop assets, gone back to Main Stamp Dialog
250 if assetId == 0 then
251 guiScriptIsLoadingSomething = true
252 cancelAssetPlacement()
253 return
254 end
255 if assetId < 0 then
256 guiScriptIsLoadingSomething = true
257 cancelAssetPlacement()
258 setupDraggableClone()
259 wait() -- need this so onInsertMouseMove() can sync up with setupDraggableClone()
260 onInsertMouseMove()
261 guiScriptIsLoadingSomething = false
262 return
263 end
264 -- This call will cause a "wait" until the data comes back
265 -- below we wait a max of 8 seconds before deciding to bail out on loading
266 local root
267 local loader
268 loading = true
269 if useAssetVersionId then
270 loader = coroutine.create(function()
271 root = game:GetService("InsertService"):LoadAssetVersion(assetId)
272 loading = false
273 end)
274 coroutine.resume(loader)
275 else
276 loader = coroutine.create(function()
277 root = game:GetService("InsertService"):LoadAsset(assetId)
278 loading = false
279 end)
280 coroutine.resume(loader)
281 end
282
283 local lastGameTime = 0
284 local totalTime = 0
285 local maxWait = 8
286 while loading and totalTime < maxWait do
287 lastGameTime = tick()
288 wait(1)
289 totalTime = totalTime + tick() - lastGameTime
290 end
291 loading = false
292
293 if totalTime >= maxWait or pressedEsc then
294 Data.Loading.Cancelled = true
295 pressedEsc = false
296 else
297 Data.Loading.Cancelled = false
298 end
299
300
301 if Data.Loading.Cancelled then
302 --The user got bored and wandered off
303 --Just delete the model from the world... a shame we loaded it when they got bored
304 -- 12/28/2010: Putting this inside a pcall (on gametest, first time equipping stamper, this was being called with root=nil.) (Jahr)
305 pcall(function() root:Remove() end)
306 signalInsertComplete("Main")
307 else
308 if root == nil then
309 signalInsertComplete("Main")
310 return
311 end
312
313 if not root:IsA("Model") then
314 signalInsertComplete("Main")
315 return
316 end
317
318 local instances = root:GetChildren()
319 if #instances == 0 then
320 root:Remove()
321 signalInsertComplete("Main")
322 return
323 end
324
325 --Unlock all parts that are inserted, to make sure they are editable
326 UnlockInstances(root)
327
328 --Continue the insert process
329 root.Name = "InsertedObject" .. assetId
330
331 --Examine the contents and decide what it looks like
332 for pos, instance in pairs(instances) do
333 --Single instance objects might be treated special, decals/skyboxes
334 if instance:IsA("Decal") then
335 --Current system here stops after finding one Decal (and gives you Decal tool)
336 --We should do the same (probably)
337 beginInsertDecal(instance)
338 root:Remove()
339
340 Window.Stamp.Frame.Visible = true
341 return
342 elseif instance:IsA("Team") then
343 instance.Parent = game:GetService("Teams")
344 elseif instance:IsA("SpawnLocation") then
345 -- uh.............
346 elseif instance:IsA("HopperBin") then
347 -- Must go into the starterPack, prompt user?
348 elseif instance:IsA("Tool") then
349 -- Ask them if it should go in StarterPack?
350 elseif instance:IsA("Sky") then
351 local lightingService = game:GetService("Lighting")
352 for index,child in pairs(lightingService:GetChildren()) do
353 if child:IsA("Sky") then
354 child:Remove();
355 end
356 end
357 instance.Parent = lightingService
358 return
359 else
360
361 end
362 end
363
364 if #root:GetChildren() == 0 then
365 root:Remove()
366 signalInsertComplete("Main")
367 return
368 end
369
370 signalInsertComplete("SideDialog")
371
372 cancelAssetPlacement()
373 root:MakeJoints()
374 Data.Stamp.Model = root
375 setupDraggableClone()
376 guiScriptIsLoadingSomething = false
377 end
378
379end
380
381function beginInsertDecal(decal)
382
383 Data.Stamp.DecalSelection = Instance.new("SurfaceSelection")
384 Data.Stamp.DecalSelection.Color = BrickColor.new("Bright orange")
385 Data.Stamp.DecalSelection.archivable = false
386 Data.Stamp.DecalSelection.Parent = getPlayer().PlayerGui
387
388 --Save the decal in our Lua code for later use
389 Data.Stamp.Decal = decal
390 Data.Stamp.Decal.Parent = nil
391
392end
393
394
395-- signal to gui to switch frames
396function signalInsertComplete(type)
397
398 if type == "SideDialog" then
399 variables.SwitchLoaderToDialog.DialogType.Value = "SideDialog"
400 variables.SwitchLoaderToDialog.Value = true
401 elseif type == "Main" then
402 variables.SwitchLoaderToDialog.DialogType.Value = "Main"
403 variables.SwitchLoaderToDialog.Value = true
404 elseif type == "EyeDropper" then
405 variables.SwitchLoaderToDialog.DialogType.Value = "EyeDropper"
406 variables.SwitchLoaderToDialog.Value = true
407 end
408
409 -- needed to make sure we disconnect eyedroper handler
410 if type ~= "EyeDropper" then
411 if eyeDropperConnection then
412 eyeDropperConnection:disconnect()
413 eyeDropperConnection = nil
414 end
415 if eyeDropperMoveConnection then eyeDropperMoveConnection:disconnect() end
416
417 if Mouse and not mouseButton1UpCon then mouseButton1UpCon = Mouse.Button1Up:connect(onInsertMouseButton1Up) end
418 end
419 clearSelection()
420
421end
422
423
424function p(assetName, assetId, image, stampMode)
425 -- trying to stop assets, gone back to Main Stamp Dialog
426 if assetId == 0 then
427 cancelAssetPlacement()
428 return
429 end
430
431 -- This call will cause a "wait" until the data comes back
432 -- below we wait a max of 8 seconds before deciding to bail out on loading
433 local root
434 local loader
435 loading = true
436 if useAssetVersionId then
437 loader = coroutine.create(function()
438 root = game:GetService("InsertService"):LoadAssetVersion(assetId)
439 loading = false
440 end)
441 coroutine.resume(loader)
442 else
443 loader = coroutine.create(function()
444 root = game:GetService("InsertService"):LoadAsset(assetId)
445 loading = false
446 end)
447 coroutine.resume(loader)
448 end
449
450 local lastGameTime = 0
451 local totalTime = 0
452 local maxWait = 8
453 while loading and totalTime < maxWait do
454 lastGameTime = tick()
455 wait(1)
456 totalTime = totalTime + tick() - lastGameTime
457 end
458 loading = false
459
460 if totalTime >= maxWait or pressedEsc then
461 Data.Loading.Cancelled = true
462 pressedEsc = false
463 else
464 Data.Loading.Cancelled = false
465 end
466
467
468 if Data.Loading.Cancelled then
469 --The user got bored and wandered off
470 --Just delete the model from the world... a shame we loaded it when they got bored
471 -- 12/28/2010: Putting this inside a pcall (on gametest, first time equipping stamper, this was being called with root=nil.) (Jahr)
472 pcall(function() root:Remove() end)
473 signalInsertComplete("Main")
474 else
475 local instances = root:GetChildren()
476 if #instances == 0 then
477 root:Remove()
478 signalInsertComplete("Main")
479 return
480 end
481
482 --Unlock all parts that are inserted, to make sure they are editable
483 UnlockInstances(root)
484
485 --Continue the insert process
486 root.Name = "InsertedObject" .. assetId
487
488 --Examine the contents and decide what it looks like
489 for pos, instance in pairs(instances) do
490 --Single instance objects might be treated special, decals/skyboxes
491 if instance:IsA("Decal") then
492 --Current system here stops after finding one Decal (and gives you Decal tool)
493 --We should do the same (probably)
494 beginInsertDecal(instance)
495 root:Remove()
496
497 Window.Stamp.Frame.Visible = true
498 return
499 elseif instance:IsA("Team") then
500 instance.Parent = game:GetService("Teams")
501 elseif instance:IsA("SpawnLocation") then
502 -- uh.............
503 elseif instance:IsA("HopperBin") then
504 -- Must go into the starterPack, prompt user?
505 elseif instance:IsA("Tool") then
506 -- Ask them if it should go in StarterPack?
507 elseif instance:IsA("Sky") then
508 local lightingService = game:GetService("Lighting")
509 for index,child in pairs(lightingService:GetChildren()) do
510 if child:IsA("Sky") then
511 child:Remove();
512 end
513 end
514 instance.Parent = lightingService
515 return
516 else
517
518 end
519 end
520
521 if #root:GetChildren() == 0 then
522 root:Remove()
523 signalInsertComplete("Main")
524 return
525 end
526
527 signalInsertComplete("SideDialog")
528
529 cancelAssetPlacement()
530 Data.Stamp.Model = root
531 setupDraggableClone()
532 end
533
534end
535
536
537function positionPartsAtCFrame3(partOrModel, aCFrame)
538
539 local insertCFrame
540 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
541 --insertCFrame = Data.Stamp.CurrentParts[1]:GetChildren()[1].CFrame
542 -- we assume model has at least one part in it; need to find first part
543 i = 1
544 while (i < (#Data.Stamp.CurrentParts[1]:GetChildren()) and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("Part") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("TrussPart") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("WedgePart") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("CornerWedgePart")) do
545 i = i + 1
546 end
547 insertCFrame = Data.Stamp.CurrentParts[1]:GetChildren()[i].CFrame
548
549 for i, object in pairs(Data.Stamp.CurrentParts[1]:GetChildren()) do
550 if object:IsA("Flag") then object = object.Handle end
551 if (object:IsA("Part") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("TrussPart") or object:IsA("Seat") or object:IsA("VehicleSeat")) then
552 local posPartInWorld = object.Position
553 local posPart1InWorld = insertCFrame.p
554 local newPosPartInWorld = posPartInWorld - posPart1InWorld + aCFrame.p
555
556 local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = object.CFrame:components()
557 object.CFrame = CFrame.new(newPosPartInWorld.x, newPosPartInWorld.y, newPosPartInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
558 end
559 end
560 else
561 Data.Stamp.CurrentParts[1].CFrame = aCFrame
562 end
563
564end
565
566
567-- For Restricting Stamper Tool (isRestricted)
568function inBounds(object)
569
570 for part, transparency in pairs(object) do
571 if part:IsA("Part") or part:IsA("WedgePart") or part:IsA("CornerWedgePart") or part:IsA("TrussPart") then
572 if not partInBounds(part) then return false end
573 elseif part:IsA("Model") then
574 local primPart = object.PrimaryPart
575 if not partInBounds(primPart) then return false end
576 end
577 end
578 return true
579
580end
581
582function partInBounds(part)
583
584 if part == nil then return false end
585
586 local xOne= buildingPlate.Position.x + buildingPlate.Size.x/2
587 local xTwo = buildingPlate.Position.x - buildingPlate.Size.x/2
588 local zOne = buildingPlate.Position.z + buildingPlate.Size.z/2
589 local zTwo = buildingPlate.Position.z - buildingPlate.Size.z/2
590
591 if part.Position.x > xOne or part.Position.x < xTwo then return false end
592 if part.Position.z > zOne or part.Position.z < zTwo then return false end
593
594 return true
595
596end
597
598
599
600function canSelectObject(part)
601 return part and not (part.Locked) and part:IsA("BasePart") and (part.Position - Tool.Parent.Head.Position).Magnitude < 60
602end
603
604function canEyeDropperObject(part)
605 local stamperTag = part.Parent:FindFirstChild("RobloxStamper")
606 if stamperTag == nil then stamperTag = part:FindFirstChild("RobloxStamper") end
607
608 return part and not (part.Locked) and part:IsA("BasePart") and (part.Position - Tool.Parent.Head.Position).Magnitude < 60 and stamperTag ~= nil
609end
610
611function isOnGrid(partOrModel)
612 -- first check to see if off-grid, and if so, prevent eyedropperage
613 local modelExtentsInWorldCoords
614 if partOrModel:IsA("Model") then
615 modelExtentsInWorldCoords = partOrModel:GetModelCFrame():vectorToWorldSpace(partOrModel:GetModelSize())
616 else
617 modelExtentsInWorldCoords = partOrModel.CFrame:vectorToWorldSpace(partOrModel.Size)
618 end
619
620 -- we now simply check to see if the above property fits in a 4x4x4 gridspace
621 offX = math.fmod(math.abs(modelExtentsInWorldCoords.X), 4)
622 offY = math.fmod(math.abs(modelExtentsInWorldCoords.Y), 4)
623 offZ = math.fmod(math.abs(modelExtentsInWorldCoords.Z), 4)
624
625 local numberOfAxesOffGrid = 0
626 if math.min(offX, 4 - offX) > eyedropperOffGridTolerance then numberOfAxesOffGrid = numberOfAxesOffGrid + 1 end
627 if math.min(offY, 4 - offY) > eyedropperOffGridTolerance then numberOfAxesOffGrid = numberOfAxesOffGrid + 1 end
628 if math.min(offZ, 4 - offZ) > eyedropperOffGridTolerance then numberOfAxesOffGrid = numberOfAxesOffGrid + 1 end
629
630 if numberOfAxesOffGrid > 1 then return false -- we allow one axis to be not fit to grid, since any rotation must necessarily affect >= 2 axes (some of the models are like 4x4x6 :( ).
631 else return true end
632end
633
634
635-- below is a helper function to help get the model surface instead of the part surface [for allowing a side to elect out of making joints automatically]
636function calcRayHitTime(rayStart, raySlope, intersectionPlane)
637 if math.abs(raySlope) < .01 then return 0 end -- 0 slope --> we just say intersection time is 0, and sidestep this dimension
638 -- rayStart + t*raySlope = intersectionPlane, so t = (intersectionPlane - rayStart) / raySlope
639 return (intersectionPlane - rayStart) / raySlope
640end
641
642
643function modelTargetSurface(partOrModel, rayStart, rayEnd)
644 if not partOrModel then
645 return 0
646 end
647
648 local modelCFrame = nil
649 local modelSize = nil
650 if partOrModel:IsA("Model") then
651 modelCFrame = partOrModel:GetModelCFrame()
652 modelSize = partOrModel:GetModelSize()
653 else
654 modelCFrame = partOrModel.CFrame
655 modelSize = partOrModel.Size
656 end
657
658 local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart)
659 local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd)
660 local mouseSlope = mouseRayEnd - mouseRayStart
661
662 local xPositive = 1
663 local yPositive = 1
664 local zPositive = 1
665 if mouseSlope.X > 0 then xPositive = -1 end
666 if mouseSlope.Y > 0 then yPositive = -1 end
667 if mouseSlope.Z > 0 then zPositive = -1 end
668
669 -- find which surface the transformed mouse ray hits (using modelSize):
670 local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X/2 * xPositive)
671 local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y/2 * yPositive)
672 local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z/2 * zPositive)
673
674 local hitFace = 0
675
676 --if xHitTime >= 0 and yHitTime >= 0 and zHitTime >= 0 then
677 if xHitTime > yHitTime then
678 if xHitTime > zHitTime then
679 -- xFace is hit
680 hitFace = 1*xPositive
681 else
682 -- zFace is hit
683 hitFace = 3*zPositive
684 end
685 else
686 if yHitTime > zHitTime then
687 -- yFace is hit
688 hitFace = 2*yPositive
689 else
690 -- zFace is hit
691 hitFace = 3*zPositive
692 end
693 end
694
695 return hitFace
696end
697
698
699-- helper function for truncating to 45-degree angles on a 2D plane
700function truncateToCircleEighth(bigValue, littleValue)
701 local big = math.abs(bigValue)
702 local little = math.abs(littleValue)
703 local hypotenuse = math.sqrt(big*big + little*little)
704 local frac = little / hypotenuse
705
706 local bigSign = 1
707 local littleSign = 1
708 if bigValue < 0 then bigSign = -1 end
709 if littleValue < 0 then littleSign = -1 end
710
711 if frac > .382683432 then
712 -- between 22.5 and 45 degrees, so truncate to 45-degree tilt
713 return .707106781 * hypotenuse * bigSign, .707106781 * hypotenuse * littleSign
714 else
715 -- between 0 and 22.5 degrees, so truncate to 0-degree tilt
716 return hypotenuse * bigSign, 0
717 end
718end
719
720function onInsertMouseMove()
721
722 if Data.Stamp.MovingLock then
723 return
724 end
725
726 -- check to see if mouse is still active, and return otherwise!
727 successs,errorresults = pcall(function () if lolmousetarget and lolmousetarget.Parent and lolmousetarget.Parent:FindFirstChild("RobloxModel") == nil then return true else return false end end)
728 if not successs then print("ERRORED OUT: "..errorresults) game.JointsService:SetJoinAfterMoveTarget(nil) Mouse = nil return end
729
730
731
732 Data.Stamp.MovingLock = true
733 if Data.Stamp.Dragger == nil then
734 if Data.Stamp.Model ~= nil then
735 if isRestricted then
736 if checkPartLimit() then
737 setupDraggableClone()
738 else
739 variables.ShowMaxedOut.Value = true
740 end
741 else
742 setupDraggableClone()
743 end
744 end
745 else
746 -- REM TL: Data.Stamp.Dragger:MouseMove(Mouse.UnitRay)
747 -- don't move with dragger - will move in one step on mouse down
748 -- draw ghost at acceptable positions
749 configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Data.Stamp.TransparencyTable)
750 if configFound then
751
752 if autoAlignToFace() and targetSurface ~= 1 and targetSurface ~= 4 then
753 -- pre-rotate the flag or portrait so it's aligned correctly
754 --local currCFrame
755 --if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then currCFrame = Data.Stamp.CurrentParts[1]:GetModelCFrame()
756 --else currCFrame = Data.Stamp.CurrentParts[1].CFrame end
757
758 local numRotations = 0 -- update this according to how many rotations you need to get it to target surface
759 if targetSurface == 3 then numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace()
760 elseif targetSurface == 0 then numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace()
761 elseif targetSurface == 5 then numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace()
762 elseif targetSurface == 2 then numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace() end
763
764
765 local ry = math.pi/2
766 local rotCF = CFrame.fromEulerAnglesXYZ(0, ry*numRotations, 0)
767 gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations
768 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
769 for i, object in pairs(Data.Stamp.CurrentParts[1]:GetChildren()) do
770 if object:IsA("Flag") then object = object.Handle end
771 if object:IsA("Part") or object:IsA("TrussPart") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("Seat") or object:IsA("VehicleSeat") then
772 object.CFrame = rotCF * object.CFrame
773 end
774 end
775 else
776 Data.Stamp.CurrentParts[1].CFrame = rotCF * Data.Stamp.CurrentParts[1].CFrame
777 end
778 end
779
780
781
782 -- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE
783 local minBB, maxBB = getBoundingBoxInWorldCoordinates(Data.Stamp.CurrentParts[1])
784
785 -- need to offset by distance to be dragged
786 local currModelCFrame
787 if Data.Stamp.CurrentParts[1]:IsA("Model") then
788 -- we assume model has at least one part in it; need to find first part
789 i = 1
790 while i < (#Data.Stamp.CurrentParts[1]:GetChildren()) and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("BasePart") do
791 i = i + 1
792 end
793 currModelCFrame = Data.Stamp.CurrentParts[1]:GetChildren()[i].CFrame
794 else currModelCFrame = Data.Stamp.CurrentParts[1].CFrame end
795 minBB = minBB + targetCFrame.p - currModelCFrame.p
796 maxBB = maxBB + targetCFrame.p - currModelCFrame.p
797
798 -- don't drag into terrain
799 if clusterPartsInRegion(minBB+insertBoundingBoxOverlapVector, maxBB-insertBoundingBoxOverlapVector) then
800 if lastTargetCFrame then
801 positionPartsAtCFrame3(thingToDrag, lastTargetCFrame)
802 if (Data.Stamp.CurrentParts[1].Name == "MegaClusterCube") and (Data.Stamp.CurrentParts[1]:FindFirstChild("ClusterMaterial")) and (Data.Stamp.CurrentParts[1].ClusterMaterial:IsA("Vector3Value")) then
803 local clusterMat = Data.Stamp.CurrentParts[1].ClusterMaterial
804 clusterMat.Value = Vector3.new(clusterMat.Value.X, clusterMat.Value.Y, lastTargetTerrainOrientation)
805 end
806 end
807 Data.Stamp.MovingLock = false
808 return
809 end
810
811 local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB+insertBoundingBoxOverlapVector, maxBB-insertBoundingBoxOverlapVector), currentSelection, 100)
812 for b = 1, #blockingParts do
813 -- put code back here if we want to prevent stamper from dragging ghost parts into other stamped models (once Region3 fix goes out)
814 end
815
816 positionPartsAtCFrame3(Data.Stamp.CurrentParts[1], targetCFrame)
817 lastTargetCFrame = targetCFrame -- successful positioning, so update 'dat cframe
818 findmatchingweld = false
819 partormodelpartformatchingweld = nil
820 if Data.Stamp.CurrentParts[1]:IsA("BasePart") then
821 partormodelpartformatchingweld = Data.Stamp.CurrentParts[1]
822 elseif Data.Stamp.CurrentParts[1]:IsA("Model") then
823 partormodelpartformatchingweld = Data.Stamp.CurrentParts[1].PrimaryPart
824 end
825 for i,v in pairs (Data.Stamp.CurrentParts[1]:GetDescendants()) do
826 if v:IsA("Weld") and v.Part0 == partormodelpartformatchingweld and v.Part1 == lolmousetarget then
827 findmatchingweld = true
828 end
829 if v:IsA("Weld") and v.Part1 == partormodelpartformatchingweld and v.Part0 == lolmousetarget then
830 findmatchingweld = true
831 end
832 end -- check so it doesnt screw up
833 if not findmatchingweld then
834 local asdfasdfs=Instance.new("Weld",Data.Stamp.CurrentParts[1])
835 asdfasdfs.Name = "Connector"
836 if Data.Stamp.CurrentParts[1]:IsA("BasePart") then
837 asdfasdfs.Part0 = Data.Stamp.CurrentParts[1]
838 elseif Data.Stamp.CurrentParts[1]:IsA("Model") then
839 asdfasdfs.Part0 = Data.Stamp.CurrentParts[1].PrimaryPart
840 end
841 asdfasdfs.Part1 = lolmousetarget
842 asdfasdfs.C0 = CFrame.new()
843 asdfasdfs.C1 = asdfasdfs.Part1.CFrame:toObjectSpace(asdfasdfs.Part0.CFrame)
844 end
845 Data.Stamp.CurrentParts[1]:MakeJoints()
846 Data.Stamp.CurrentParts[1]:MakeJoints()
847 Data.Stamp.CurrentParts[1]:MakeJoints()
848 Data.Stamp.CurrentParts[1]:MakeJoints()
849 Data.Stamp.CurrentParts[1]:MakeJoints()
850 Data.Stamp.CurrentParts[1]:MakeJoints()
851 Data.Stamp.CurrentParts[1]:MakeJoints()
852 Data.Stamp.CurrentParts[1]:MakeJoints()
853
854 if (Data.Stamp.CurrentParts[1].Name == "MegaClusterCube") and (Data.Stamp.CurrentParts[1]:FindFirstChild("ClusterMaterial")) and (Data.Stamp.CurrentParts[1].ClusterMaterial:IsA("Vector3Value")) then lastTargetTerrainOrientation = Data.Stamp.CurrentParts[1].ClusterMaterial.Value.Z end
855
856
857 -- auto break joints code
858 if Mouse and lolmousetarget and lolmousetarget.Parent then
859 local modelInfo = lolmousetarget:FindFirstChild("RobloxModel")
860 if not modelInfo then modelInfo = lolmousetarget.Parent:FindFirstChild("RobloxModel") end
861
862 local myModelInfo = Data.Stamp.CurrentParts[1]:FindFirstChild("UnstampableFaces")
863
864 --if (modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces")) or (modelInfo and myModelInfo) then -- need better targetSurface calcs
865 if (true) then
866 local breakingFaces = ""
867 local myBreakingFaces = ""
868 if modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces") then breakingFaces = modelInfo.Parent.UnstampableFaces.Value end
869 if myModelInfo then myBreakingFaces = myModelInfo.Value end
870 local hitFace = 0
871
872 if modelInfo then hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, lolmousehit.p) end
873
874 -- are we stamping TO an unstampable surface?
875 for bf in string.gmatch(breakingFaces, "[^,]+") do
876 if hitFace == tonumber(bf) then
877 -- return before we hit the JointsService code below!
878 unstampableSurface = true
879 game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
880 Data.Stamp.MovingLock = false
881 return
882 end
883 end
884
885 -- now we have to cast the ray back in the other direction to find the surface we're stamping FROM
886 hitFace = modelTargetSurface(Data.Stamp.CurrentParts[1], lolmousehit.p, game.Workspace.CurrentCamera.CoordinateFrame.p)
887
888 -- are we stamping WITH an unstampable surface?
889 for bf in string.gmatch(myBreakingFaces, "[^,]+") do
890 if hitFace == tonumber(bf) then
891 unstampableSurface = true
892 game.JointsService:ClearJoinAfterMoveJoints() -- clear the JointsService cache
893 Data.Stamp.MovingLock = false
894 return
895 end
896 end
897
898 -- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame
899 -- targetSurface: 1 is top, 4 is bottom,
900 end
901 end
902
903 end
904
905 -- to show joints during the mouse move
906 unstampableSurface = false
907 game.JointsService:SetJoinAfterMoveInstance(Data.Stamp.CurrentParts[1])
908
909 -- most common mouse inactive error occurs here, so check mouse active one more time in a pcall
910 if not pcall(function () if Mouse and lolmousetarget and lolmousetarget.Parent and lolmousetarget.Parent:FindFirstChild("RobloxModel") == nil then return true else return false end end) then print("ERRORED OUT") game.JointsService:ClearJoinAfterMoveJoints() Mouse = nil Data.Stamp.MovingLock = false return end
911
912 if Mouse and lolmousetarget and lolmousetarget.Parent and lolmousetarget.Parent:FindFirstChild("RobloxModel") == nil then
913 game.JointsService:SetJoinAfterMoveTarget(lolmousetarget)
914
915 else
916 game.JointsService:SetJoinAfterMoveTarget(nil)
917 end
918 game.JointsService:ShowPermissibleJoints()
919
920 -- here we allow for a line of high-scalability parts
921 if (Data.Stamp.CurrentParts[1] and Data.Stamp.CurrentParts[1].Name == "MegaClusterCube") and HighScalabilityLine.Start then
922 HighScalabilityLine.End = Data.Stamp.CurrentParts[1].CFrame.p
923
924 local line
925 local line2 = Vector3.new(0, 0, 0)
926 local line3 = Vector3.new(0, 0, 0)
927
928 if HighScalabilityLine.Dimensions == 1 then
929 -- extract the line from these positions and limit to a 2D plane made from 2 of the world axes
930 -- then use dominating axis to limit line to be at 45-degree intervals
931 -- will use this internal representation of the line for the actual stamping
932 line = (HighScalabilityLine.End - HighScalabilityLine.Start)
933
934 if math.abs(line.X) < math.abs(line.Y) then
935 if math.abs(line.X) < math.abs(line.Z) then
936 -- limit to Y/Z plane, domination unknown
937 local newY, newZ
938 if (math.abs(line.Y) > math.abs(line.Z)) then
939 newY, newZ = truncateToCircleEighth(line.Y, line.Z)
940 else
941 newZ, newY = truncateToCircleEighth(line.Z, line.Y)
942 end
943 line = Vector3.new(0, newY, newZ)
944 else
945 -- limit to X/Y plane, with Y dominating
946 local newY, newX = truncateToCircleEighth(line.Y, line.X)
947 line = Vector3.new(newX, newY, 0)
948 end
949 else
950 if math.abs(line.Y) < math.abs(line.Z) then
951 -- limit to X/Z plane, domination unknown
952 local newX, newZ
953 if math.abs(line.X) > math.abs(line.Z) then
954 newX, newZ = truncateToCircleEighth(line.X, line.Z)
955 else
956 newZ, newX = truncateToCircleEighth(line.Z, line.X)
957 end
958 line = Vector3.new(newX, 0, newZ)
959 else
960 -- limit to X/Y plane, with X dominating
961 local newX, newY = truncateToCircleEighth(line.X, line.Y)
962 line = Vector3.new(newX, newY, 0)
963 end
964 end
965 HighScalabilityLine.InternalLine = line
966
967 elseif HighScalabilityLine.Dimensions == 2 then
968 line = HighScalabilityLine.MoreLines[1]
969 line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1]
970
971 -- take out any component of line2 along line1, so you get perpendicular to line1 component
972 line2 = line2 - line.unit*line.unit:Dot(line2)
973
974 tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
975
976 -- then zero out whichever is the smaller component
977 local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0))
978 local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0))
979
980 local xComp = xAxis:Dot(line2)
981 local yComp = yAxis:Dot(line2)
982
983 if math.abs(yComp) > math.abs(xComp) then
984 line2 = line2 - xAxis*xComp
985 else
986 line2 = line2 - yAxis*yComp
987 end
988
989 HighScalabilityLine.InternalLine = line2
990 elseif HighScalabilityLine.Dimensions == 3 then
991 line = HighScalabilityLine.MoreLines[1]
992 line2 = HighScalabilityLine.MoreLines[2]
993 line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2]
994
995 -- zero out all components of previous lines
996 line3 = line3 - line.unit*line.unit:Dot(line3)
997 line3 = line3 - line2.unit*line2.unit:Dot(line3)
998
999 HighScalabilityLine.InternalLine = line3
1000 end
1001
1002 -- resize the "line" graphic to be the correct size and orientation
1003 tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line)
1004
1005 if HighScalabilityLine.Dimensions == 1 then -- faster calculation for line
1006 HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4)
1007 HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size/2)
1008 else
1009 local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3)
1010 HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z))
1011 HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize/2)
1012 end
1013
1014 -- make player able to see this ish
1015 if player then HighScalabilityLine.Adorn.Parent = player.PlayerGui end
1016 if HighScalabilityLine.NewHint then
1017 if HighScalabilityLine.Dimensions == 1 and line and (line.magnitude > 4) then hint("Press C to stamp in 2D") HighScalabilityLine.NewHint = false
1018 elseif HighScalabilityLine.Dimensions == 2 and line2 and (line2.magnitude > 4) then hint("Press C again to stamp in 3D") HighScalabilityLine.NewHint = false
1019 elseif HighScalabilityLine.Dimensions == 3 then HighScalabilityLine.NewHint = false end -- if we do cyclic, then let them know they're going back to line stamping
1020 end
1021 end
1022 end
1023
1024 Data.Stamp.MovingLock = false
1025
1026end
1027
1028
1029function onInsertMouseButton1Down()
1030
1031 if Data.Stamp.Dragger or Data.Stamp.Decal then
1032 Data.Stamp.MouseDown = true
1033 end
1034
1035 if (Data.Stamp.CurrentParts and Data.Stamp.CurrentParts[1] and Data.Stamp.CurrentParts[1].Name == "MegaClusterCube") and not eyeDropperConnection then
1036 -- only let them drag if they start dragging on the cluster!
1037 if Mouse and lolmousetarget and lolmousetarget:IsA("Terrain") then
1038 HighScalabilityLine.Dimensions = 1
1039 HighScalabilityLine.Start = Data.Stamp.CurrentParts[1].CFrame.p
1040 end
1041 end
1042end
1043
1044
1045function cancelAssetPlacement()
1046 HighScalabilityLine.Start = nil
1047 HighScalabilityLine.Adorn.Parent = nil
1048
1049 gInitial90DegreeRotations = 0
1050 Data.Stamp.Cancelled = true
1051
1052 if Data.Stamp["Model"] then
1053 Data.Stamp.Model.Parent = nil
1054 end
1055
1056 if Data.Stamp.CurrentParts then
1057 for index, object in pairs(Data.Stamp.CurrentParts) do
1058 object.Parent = nil
1059 end
1060
1061 Data.Stamp.CurrentParts.Parent = nil
1062 end
1063
1064 if Data.Stamp.DecalSelection then
1065 Data.Stamp.DecalSelection:Remove()
1066 Data.Stamp.DecalSelection = nil
1067 end
1068 if Data.Stamp.Decal then
1069 Data.Stamp.Decal:Remove()
1070 Data.Stamp.Decal = nil
1071 end
1072
1073 if Mouse then
1074 Mouse.Icon = "http://www.roblox.com/asset?id=66887745"
1075 end
1076 game.JointsService:ClearJoinAfterMoveJoints()
1077
1078end
1079
1080
1081function collectParts(object, baseParts, scripts, decals)
1082
1083 if object:IsA("BasePart") then
1084 baseParts[#baseParts+1] = object
1085 elseif object:IsA("Script") then
1086 scripts[#scripts+1] = object
1087 elseif object:IsA("Decal") then
1088 decals[#decals+1] = object
1089 end
1090
1091 for index,child in pairs(object:GetChildren()) do
1092 collectParts(child, baseParts, scripts, decals)
1093 end
1094
1095end
1096
1097function getTargetPartBoundingBox(targetPart)
1098
1099 if targetPart.Parent and targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
1100 return getBoundingBox2(targetPart.Parent)
1101 else
1102 return getBoundingBox2(targetPart)
1103 end
1104
1105end
1106
1107
1108function getBoundingBox2(partOrModel)
1109
1110 -- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes
1111 -- relative to the first part's coordinate frame.
1112
1113 local minVec = Vector3.new(math.huge, math.huge, math.huge)
1114 local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
1115
1116 if partOrModel:IsA("Part") or partOrModel:IsA("WedgePart") or partOrModel:IsA("CornerWedgePart") or partOrModel:IsA("TrussPart")then
1117 minVec = -0.5 * partOrModel.Size
1118 maxVec = -minVec
1119 elseif partOrModel:IsA("Terrain") then
1120 minVec = Vector3.new(-2, -2, -2)
1121 maxVec = Vector3.new(2, 2, 2)
1122 else
1123 local part1 = partOrModel:GetChildren()[1]
1124 if partOrModel:IsA("Tool") then part1 = partOrModel.Handle if not part1 then return end end
1125 if part1:IsA("Flag") then part1 = partOrModel:FindFirstChild("Part") if not part1 then return end end
1126 for i, object in pairs(partOrModel:GetChildren()) do
1127 if (object:IsA("Part") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("TrussPart")) then
1128 boxMinInWorld = object.CFrame:pointToWorldSpace(-0.5 * object.Size)
1129 boxMinInPart1 = part1.CFrame:pointToObjectSpace(boxMinInWorld)
1130 boxMaxInWorld = object.CFrame:pointToWorldSpace(0.5 * object.Size)
1131 boxMaxInPart1 = part1.CFrame:pointToObjectSpace(boxMaxInWorld)
1132
1133 local minX = minVec.x
1134 local minY = minVec.y
1135 local minZ = minVec.z
1136 local maxX = maxVec.x
1137 local maxY = maxVec.y
1138 local maxZ = maxVec.z
1139 if boxMinInPart1.x < minVec.x then
1140 minX = boxMinInPart1.x
1141 end
1142 if boxMinInPart1.y < minVec.y then
1143 minY = boxMinInPart1.y
1144 end
1145 if boxMinInPart1.z < minVec.z then
1146 minZ = boxMinInPart1.z
1147 end
1148 if boxMaxInPart1.x < minX then
1149 minX = boxMaxInPart1.x
1150 end
1151 if boxMaxInPart1.y < minY then
1152 minY = boxMaxInPart1.y
1153 end
1154 if boxMaxInPart1.z < minZ then
1155 minZ = boxMaxInPart1.z
1156 end
1157
1158 if boxMinInPart1.x > maxVec.x then
1159 maxX = boxMinInPart1.x
1160 end
1161 if boxMinInPart1.y > maxVec.y then
1162 maxY = boxMinInPart1.y
1163 end
1164 if boxMinInPart1.z > maxVec.z then
1165 maxZ = boxMinInPart1.z
1166 end
1167 if boxMaxInPart1.x > maxX then
1168 maxX = boxMaxInPart1.x
1169 end
1170 if boxMaxInPart1.y > maxY then
1171 maxY = boxMaxInPart1.y
1172 end
1173 if boxMaxInPart1.z > maxZ then
1174 maxZ = boxMaxInPart1.z
1175 end
1176
1177 minVec = Vector3.new(minX, minY, minZ)
1178 maxVec = Vector3.new(maxX, maxY, maxZ)
1179 end
1180 end
1181 end
1182
1183 -- Adjust bounding box to reflect what the model or part author wants in terms of justification
1184 local justifyValue = partOrModel:FindFirstChild("Justification")
1185 if justifyValue ~= nil then
1186 -- find the multiple of 4 that contains the model
1187 justify = justifyValue.Value
1188 two = Vector3.new(2, 2, 2)
1189 actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01)
1190 containingGridBox = Vector3.new(4 * math.ceil(actualBox.x/4), 4 * math.ceil(actualBox.y/4), 4 * math.ceil(actualBox.z/4))
1191 adjustment = containingGridBox - actualBox
1192 minVec = minVec - 0.5 * adjustment * justify
1193 maxVec = maxVec + 0.5 * adjustment * (two - justify)
1194 end
1195
1196 return minVec, maxVec
1197
1198end
1199
1200
1201function getBoundingBoxInWorldCoordinates(partOrModel)
1202 local minVec = Vector3.new(math.huge, math.huge, math.huge)
1203 local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge)
1204
1205 if partOrModel:IsA("BasePart")then
1206 vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size)
1207 vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size)
1208 minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z))
1209 maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z))
1210 elseif partOrModel:IsA("Terrain") then
1211 -- we shouldn't have to deal with this case
1212 --minVec = Vector3.new(-2, -2, -2)
1213 --maxVec = Vector3.new(2, 2, 2)
1214 else
1215 local part1 = partOrModel:GetChildren()[1]
1216 for i, object in pairs(partOrModel:GetChildren()) do
1217 if object:IsA("BasePart") then
1218 boxMinInWorld = object.CFrame:pointToWorldSpace(-0.5 * object.Size)
1219 boxMaxInWorld = object.CFrame:pointToWorldSpace(0.5 * object.Size)
1220
1221 local minX = minVec.x
1222 local minY = minVec.y
1223 local minZ = minVec.z
1224 local maxX = maxVec.x
1225 local maxY = maxVec.y
1226 local maxZ = maxVec.z
1227 if boxMinInWorld.x < minX then
1228 minX = boxMinInWorld.x
1229 end
1230 if boxMinInWorld.y < minY then
1231 minY = boxMinInWorld.y
1232 end
1233 if boxMinInWorld.z < minZ then
1234 minZ = boxMinInWorld.z
1235 end
1236 if boxMaxInWorld.x < minX then
1237 minX = boxMaxInWorld.x
1238 end
1239 if boxMaxInWorld.y < minY then
1240 minY = boxMaxInWorld.y
1241 end
1242 if boxMaxInWorld.z < minZ then
1243 minZ = boxMaxInWorld.z
1244 end
1245
1246 if boxMinInWorld.x > maxX then
1247 maxX = boxMinInWorld.x
1248 end
1249 if boxMinInWorld.y > maxY then
1250 maxY = boxMinInWorld.y
1251 end
1252 if boxMinInWorld.z > maxZ then
1253 maxZ = boxMinInWorld.z
1254 end
1255 if boxMaxInWorld.x > maxX then
1256 maxX = boxMaxInWorld.x
1257 end
1258 if boxMaxInWorld.y > maxY then
1259 maxY = boxMaxInWorld.y
1260 end
1261 if boxMaxInWorld.z > maxZ then
1262 maxZ = boxMaxInWorld.z
1263 end
1264
1265 minVec = Vector3.new(minX, minY, minZ)
1266 maxVec = Vector3.new(maxX, maxY, maxZ)
1267 end
1268 end
1269 end
1270
1271 return minVec, maxVec
1272end
1273
1274
1275function getClosestAlignedWorldDirection(aVector3InWorld)
1276
1277 local xDir = Vector3.new(1,0,0)
1278 local yDir = Vector3.new(0,1,0)
1279 local zDir = Vector3.new(0,0,1)
1280 local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z
1281 local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z
1282 local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z
1283
1284 if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs(zDot) then
1285 if xDot > 0 then
1286 return 0
1287 else
1288 return 3
1289 end
1290 elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs(zDot) then
1291 if yDot > 0 then
1292 return 1
1293 else
1294 return 4
1295 end
1296 else
1297 if zDot > 0 then
1298 return 2
1299 else
1300 return 5
1301 end
1302 end
1303
1304end
1305
1306
1307function getMouseTargetCFrame(targetPart)
1308
1309 if targetPart.Parent and targetPart.Parent:FindFirstChild("RobloxModel") ~= nil then
1310 if targetPart.Parent:IsA("Tool") then return targetPart.Parent.Handle.CFrame
1311 else return targetPart.Parent:GetChildren()[1].CFrame end
1312 else
1313 return targetPart.CFrame
1314 end
1315
1316end
1317
1318
1319function surfaceToVector(surf)
1320 local vect = 1
1321 if surf < 0 then
1322 surf = surf * -1
1323 vect = vect * -1
1324 end
1325 if surf == 1 then return vect*Vector3.new(1, 0, 0)
1326 elseif surf == 2 then return vect*Vector3.new(0, 1, 0)
1327 elseif surf == 3 then return vect*Vector3.new(0, 0, 1)
1328 elseif Mouse then return Vector3.FromNormalId(lolmousetargetSurface) end -- if we somehow got a "0", then we just revert to old behavior
1329 return Vector3.new(0,0,0)
1330end
1331
1332function findConfigAtMouseTarget(partsTable)
1333
1334 -- *Critical Assumption* :
1335 -- This function assumes the target CF axes are orthogonal with the target bounding box faces
1336 -- And, it assumes the insert CF axes are orthongonal with the insert bounding box faces
1337 -- Therefore, insertion will not work with angled faces on wedges or other "non-block" parts, nor
1338 -- will it work for parts in a model that are not orthogonally aligned with the model's CF.
1339
1340 local grid = 4.0
1341 local admissibleConfig = false
1342 local targetConfig = CFrame.new(0,0,0)
1343
1344 local minBB, maxBB = getBoundingBox2(Data.Stamp.CurrentParts[1])
1345 local diagBB = maxBB - minBB
1346
1347 local insertCFrame
1348 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
1349 i = 1
1350 while (i < (#Data.Stamp.CurrentParts[1]:GetChildren()) and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("Part") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("TrussPart") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("WedgePart") and not Data.Stamp.CurrentParts[1]:GetChildren()[i]:IsA("CornerWedgePart")) do
1351 i = i + 1
1352 end
1353 insertCFrame = Data.Stamp.CurrentParts[1]:GetChildren()[i].CFrame
1354 else
1355 insertCFrame = Data.Stamp.CurrentParts[1].CFrame
1356 end
1357
1358 if not isRestricted and Mouse then
1359 if Data.Stamp.CurrentParts[1]:IsA("Tool") then lolmousetargetFilter = Data.Stamp.CurrentParts[1].Handle
1360 else lolmousetargetFilter = Data.Stamp.CurrentParts[1] end
1361 end
1362
1363 local targetPart = nil
1364 local success = pcall(function() targetPart = lolmousetarget end)
1365
1366 if not success or targetPart == nil then
1367 return admissibleConfig, targetConfig
1368 end
1369
1370 -- test mouse hit location
1371 local minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart)
1372 local diagBBTarget = maxBBTarget - minBBTarget
1373 local targetCFrame = getMouseTargetCFrame(targetPart)
1374 local hitCFrame = CFrame.new(0,0,0)
1375 if Mouse then
1376 hitCFrame = lolmousehit
1377 end
1378 local mouseHitInWorld = hitCFrame.p
1379
1380 -- find which axis of the insertion objects should match with the target surface
1381 -- this should use targetPart CFrame, not the model CFrame
1382
1383 --[[ attempt at fixing lolmousetargetSurface below...
1384 local targetModel = targetPart
1385 if not targetPart:FindFirstChild("RobloxModel") and targetPart.Parent and targetPart.Parent:FindFirstChild("RobloxModel") then targetModel = targetPart.Parent end
1386 local correctedTargetSurfaceVector = surfaceToVector(modelTargetSurface(targetModel, game.Workspace.CurrentCamera.CoordinateFrame.p, mouseHitInWorld))
1387 local targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(correctedTargetSurfaceVector)
1388 --]]
1389
1390 if targetPart:IsA("Terrain") then
1391 if not cluster then cluster = game.Workspace.Terrain end
1392
1393 cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld)
1394 targetCFrame = CFrame.new(cluster:CellCenterToWorld(cellID.x, cellID.y, cellID.z))
1395 end
1396
1397 local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld)
1398 local targetVectorInWorld = Vector3.new(0,0,0)
1399 if Mouse then
1400 targetVectorInWorld = targetCFrame:vectorToWorldSpace(Vector3.FromNormalId(lolmousetargetSurface))
1401 end
1402
1403
1404 local targetRefPointInTarget
1405 local clampToSurface
1406
1407 if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then
1408 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
1409 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
1410 clampToSurface = Vector3.new(0,1,1)
1411 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then
1412 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
1413 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
1414 clampToSurface = Vector3.new(0,1,1)
1415 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then
1416 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
1417 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
1418 clampToSurface = Vector3.new(1,0,1)
1419 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then
1420 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
1421 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1))
1422 clampToSurface = Vector3.new(1,0,1)
1423 elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then
1424 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1))
1425 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1))
1426 clampToSurface = Vector3.new(1,1,0)
1427 else
1428 targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1))
1429 insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1))
1430 clampToSurface = Vector3.new(1,1,0)
1431 end
1432
1433 targetRefPointInTarget = targetRefPointInTarget * (0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget)
1434 insertRefPointInInsert = insertRefPointInInsert * (0.5 * diagBB) + 0.5 * (maxBB + minBB)
1435
1436 -- To Do: For cases that are not aligned to the world grid, account for the minimal rotation
1437 -- needed to bring the Insert part(s) into alignment with the Target Part
1438 -- Apply the rotation here
1439
1440 local delta = mouseHitInTarget - targetRefPointInTarget
1441 local deltaClamped = Vector3.new(grid * math.modf(delta.x/grid), grid * math.modf(delta.y/grid), grid * math.modf(delta.z/grid))
1442 deltaClamped = deltaClamped * clampToSurface
1443 local targetTouchInTarget = deltaClamped + targetRefPointInTarget
1444
1445 local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget)
1446 local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert)
1447 local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld
1448
1449 local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components()
1450 targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22)
1451 admissibleConfig = true
1452
1453 return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld)
1454
1455end
1456
1457function checkPartLimit()
1458
1459 local numPoints = player.PointsUsed.Value
1460 local maxPoints = player.MaxPoints.Value
1461
1462 if numPoints < maxPoints then
1463 return true
1464 else
1465 return false
1466 end
1467
1468 return true
1469end
1470
1471function setupDraggableClone()
1472 if eyeDropperConnection then eyeDropperConnection:disconnect() eyeDropperConnection = nil end
1473
1474 --if not lolmousetarget then return end
1475 click.Value = false
1476
1477 if Data.Stamp.CurrentParts then
1478 for i = 1, #Data.Stamp.CurrentParts do
1479 if Data.Stamp.CurrentParts[i].Parent ~= nil then
1480 Data.Stamp.CurrentParts[i].Parent = partModel
1481 end
1482 end
1483 end
1484
1485 if Data.Stamp["Model"] == nil then return end
1486
1487 local clone = Data.Stamp.Model:Clone()
1488 local scripts = {}
1489 local parts = {}
1490 local decals = {}
1491
1492 collectParts(clone, parts, scripts, decals)
1493
1494 if #parts > 0 then
1495 Data.Stamp.DisabledScripts = {}
1496 Data.Stamp.TransparencyTable = {}
1497 Data.Stamp.MaterialTable = {}
1498 Data.Stamp.CanCollideTable = {}
1499 Data.Stamp.AnchoredTable = {}
1500 Data.Stamp.DecalTransparencyTable = {}
1501
1502 for index,script in pairs(scripts) do
1503 if not(script.Disabled) then
1504 script.Disabled = true
1505 Data.Stamp.DisabledScripts[#Data.Stamp.DisabledScripts +1] = script
1506 end
1507 end
1508 for index, part in pairs(parts) do
1509 Data.Stamp.TransparencyTable[part] = part.Transparency
1510 part.Transparency = gStaticTrans + (1-gStaticTrans)*part.Transparency
1511 Data.Stamp.MaterialTable[part] = part.Material
1512 part.Material = Enum.Material.Plastic
1513 Data.Stamp.CanCollideTable[part] = part.CanCollide
1514 part.CanCollide = false
1515 Data.Stamp.AnchoredTable[part] = part.Anchored
1516 part.Anchored = true
1517 part.archivable = false
1518
1519 delay(0,function()
1520 local con = nil
1521 local exitLoop = false
1522 con = click.Changed:connect(function()
1523 if click.Value then
1524 con:disconnect()
1525 exitLoop = true
1526 end
1527 end)
1528 wait(fadeInDelayTime) -- give it some time to be completely transparent
1529
1530 if exitLoop then return end -- if we already stamped, we don't need to do the rest of this
1531
1532 local begTime = tick()
1533 local currTime = begTime
1534 while (currTime - begTime) < transFadeInTime and part and part:IsA("BasePart") and part.Transparency > gDesiredTrans and not exitLoop do
1535 local newTrans = 1 - (((currTime - begTime)/transFadeInTime) * (gStaticTrans - gDesiredTrans))
1536 if Data.Stamp.TransparencyTable[part] then part.Transparency = newTrans + (1-newTrans) * Data.Stamp.TransparencyTable[part] end
1537 wait(0.03)
1538 currTime = tick()
1539 end
1540 if part and part:IsA("BasePart") and not exitLoop then
1541 if Data.Stamp.TransparencyTable[part] then part.Transparency = gDesiredTrans + (1-gDesiredTrans)*Data.Stamp.TransparencyTable[part] end
1542 end
1543 if con then con:disconnect() end
1544 end)
1545 end
1546
1547 for index, decal in pairs(decals) do
1548 Data.Stamp.DecalTransparencyTable[decal] = decal.Transparency
1549 decal.Transparency = gDesiredTrans + (1-gDesiredTrans)*decal.Transparency
1550 end
1551
1552 clone.Parent = game.Workspace
1553
1554 -- For Restricting Stamper Tool
1555 if(isRestricted) then
1556
1557 -- mark a particular spot in BaseplateBumpers, so we can remove any stragglers later, when player leaves
1558 local cloneInsertionSpot = game.Workspace.BaseplateBumpers:FindFirstChild(player.Name)
1559 if cloneInsertionSpot == nil then
1560 cloneInsertionSpot = Instance.new("Model")
1561 cloneInsertionSpot.Name = player.Name
1562 cloneInsertionSpot.Parent = game.Workspace.BaseplateBumpers
1563 end
1564
1565 clone.Parent = cloneInsertionSpot
1566 end
1567
1568 -- disable all seats
1569 setSeatEnabledStatus(clone, true)
1570 setSeatEnabledStatus(clone, false)
1571
1572 Data.Stamp.CurrentParts = clone:GetChildren()
1573
1574 -- if auto-alignable, we enforce a pre-rotation to the canonical "0-frame"
1575 if autoAlignToFace() then
1576 if Data.Stamp.CurrentParts[1].PrimaryPart then modelInverse = Data.Stamp.CurrentParts[1].PrimaryPart.CFrame:inverse()
1577 else modelInverse = Data.Stamp.CurrentParts[1]:GetModelCFrame():inverse() end
1578 for i, object in pairs(Data.Stamp.CurrentParts[1]:GetChildren()) do
1579 if object:IsA("Flag") then object = object.Handle end
1580 if object:IsA("Part") or object:IsA("TrussPart") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("Seat") or object:IsA("VehicleSeat") then object.CFrame = modelInverse*object.CFrame end
1581 end
1582 gInitial90DegreeRotations = 0
1583 else
1584
1585 -- pre-rotate if necessary
1586 local ry = gInitial90DegreeRotations * math.pi/2
1587 local rotCF = CFrame.fromEulerAnglesXYZ(0, ry, 0)
1588 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
1589 for i, object in pairs(Data.Stamp.CurrentParts[1]:GetChildren()) do
1590 if object:IsA("Flag") then object = object.Handle end
1591 if object:IsA("Part") or object:IsA("TrussPart") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("Seat") or object:IsA("VehicleSeat") then object.CFrame = rotCF * object.CFrame end
1592 end
1593 else
1594 Data.Stamp.CurrentParts[1].CFrame = rotCF * Data.Stamp.CurrentParts[1].CFrame
1595 end
1596 end
1597
1598 -- After rotating, update the position
1599 configFound, targetCFrame = findConfigAtMouseTarget(Data.Stamp.TransparencyTable)
1600 if configFound then
1601 positionPartsAtCFrame3(Data.Stamp.TransparencyTable, targetCFrame)
1602 end
1603 -- to show joints during the mouse move
1604 game.JointsService:SetJoinAfterMoveInstance(Data.Stamp.CurrentParts[1])
1605
1606 local mouseTarget = nil
1607 pcall(function() mouseTarget = lolmousetarget end)
1608
1609 if mouseTarget and mouseTarget.Parent:FindFirstChild("RobloxModel") == nil then
1610 game.JointsService:SetJoinAfterMoveTarget(mouseTarget)
1611 else
1612 game.JointsService:SetJoinAfterMoveTarget(nil)
1613 end
1614 game.JointsService:ShowPermissibleJoints()
1615
1616 for index, object in pairs(Data.Stamp.CurrentParts) do
1617 object.Parent = clone.Parent
1618 end
1619
1620 clone:Remove()
1621
1622 lastTargetCFrame = nil
1623
1624
1625 if billBoardOwnerGui then
1626 if #parts == 1 then
1627 billBoardOwnerGui.Parent = parts[1]
1628 else
1629 billBoardOwnerGui.Parent = parts[1].Parent
1630 end
1631 end
1632
1633 HighScalabilityLine.NewHint = true -- show hint if they start dragging a high-scalability part
1634
1635 Data.Stamp.Dragger = Instance.new("Dragger")
1636 --Begin a movement by faking a MouseDown signal
1637 Data.Stamp.Dragger:MouseDown(parts[1], Vector3.new(0,0,0), parts)
1638 Data.Stamp.Dragger:MouseUp()
1639 else
1640 --Nothing draggable in the Model
1641 Data.Stamp.Model:Remove()
1642 Data.Stamp.Model = nil
1643 Data.Stamp.TransparencyTable = nil
1644 Data.Stamp.MaterialTable = nil
1645 Data.Stamp.CanCollideTable = nil
1646 Data.Stamp.AnchoredTable = nil
1647 Data.Stamp.DisabledScripts = nil
1648 end
1649
1650end
1651
1652
1653function noManualWelds(part)
1654 local partChildren = part:GetChildren()
1655 for i = 1, #partChildren do
1656 if partChildren[i]:IsA("ManualWeld") or partChildren[i]:IsA("Rotate") then
1657 return false
1658 end
1659 end
1660 return true
1661end
1662
1663local debris = game:GetService("Debris")
1664function flashRedBox()
1665 errorBox.Parent = player.PlayerGui
1666 if Data.Stamp.CurrentParts[1]:IsA("Tool") then errorBox.Adornee = Data.Stamp.CurrentParts[1].Handle
1667 else errorBox.Adornee = Data.Stamp.CurrentParts[1] end
1668
1669 delay(0,function()
1670 for i = 1, 3 do
1671 errorBox.Visible = true
1672 wait(0.13)
1673 errorBox.Visible = false
1674 wait(0.13)
1675 end
1676 errorBox.Adornee = nil
1677 errorBox.Parent = Tool
1678 end)
1679end
1680
1681
1682-- below function should work as a Region3 query, returning true if a single cluster part is within this region
1683function clusterPartsInRegion(startVector, endVector)
1684
1685 if not cluster then return false end
1686
1687 local startCell = cluster:WorldToCell(startVector)
1688 local endCell = cluster:WorldToCell(endVector)
1689
1690 local startX = startCell.X
1691 local startY = startCell.Y
1692 local startZ = startCell.Z
1693
1694 local endX = endCell.X
1695 local endY = endCell.Y
1696 local endZ = endCell.Z
1697
1698 if startX < cluster.MaxExtents.Min.X then startX = cluster.MaxExtents.Min.X end
1699 if startY < cluster.MaxExtents.Min.Y then startY = cluster.MaxExtents.Min.Y end
1700 if startZ < cluster.MaxExtents.Min.Z then startZ = cluster.MaxExtents.Min.Z end
1701
1702 if endX > cluster.MaxExtents.Max.X then endX = cluster.MaxExtents.Max.X end
1703 if endY > cluster.MaxExtents.Max.Y then endY = cluster.MaxExtents.Max.Y end
1704 if endZ > cluster.MaxExtents.Max.Z then endZ = cluster.MaxExtents.Max.Z end
1705
1706 for x = startX, endX do
1707 for y = startY, endY do
1708 for z = startZ, endZ do
1709 if (cluster:GetCell(x, y, z).Value) > 0 then return true end
1710 end
1711 end
1712 end
1713
1714 return false
1715end
1716
1717-- helper function to determine if a character can be pushed upwards by a certain amount
1718-- 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
1719function spaceAboveCharacter(charTorso, newTorsoY)
1720 local partsAboveChar = game.Workspace:FindPartsInRegion3(Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)), charTorso.Parent, 100)
1721 for j = 1, #partsAboveChar do
1722 if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(Data.Stamp.CurrentParts[1]) then return false end
1723 end
1724
1725 if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(.75, 2.75, .75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(.75, 1.75, .75)) then return false end
1726
1727 return true
1728end
1729
1730
1731-- returns whether or not we want to cancel the stamp because we're blocked by this part
1732function isBlocker(part)
1733 if not part then return false end
1734 if not part.Parent then return false end
1735 if part:FindFirstChild("Humanoid") then return false end
1736 if part:FindFirstChild("RobloxStamper") or part:FindFirstChild("RobloxModel") then return true end
1737 if part:IsA("Part") and not part.CanCollide then return false end
1738 if part == game.Workspace then return true end
1739 if part == game.Lighting then return false end
1740 return isBlocker(part.Parent)
1741end
1742
1743function onInsertMouseButton1Up()
1744 if guiScriptIsLoadingSomething or variables.InsertAsset.Updated.Value then return end -- don't try to stamp while we're loading!
1745
1746 if eyeDropperConnection then
1747 eyeDropperConnection:disconnect()
1748 eyeDropperConnection = nil
1749 return
1750 end
1751
1752 if Data.Stamp.MouseDown then
1753 Data.Stamp.MouseDown = false
1754 if Data.Stamp.Dragger then
1755
1756 --Place the object where the mouse is currently positioned
1757 -- For Restricting Stamper Tool
1758
1759 while Data.Stamp.MouseLock do wait() end
1760 onInsertMouseMove()
1761 Data.Stamp.MouseLock = true
1762
1763 if(isRestricted) then
1764 -- if player tries to stamp out of bounds, tell them they can't
1765 if not inBounds(Data.Stamp.TransparencyTable) then
1766 variables.ShowInvalidPlacement.Value = true
1767 Data.Stamp.MouseLock = false
1768 HighScalabilityLine.Start = nil
1769 HighScalabilityLine.Adorn.Parent = nil
1770 return
1771 end
1772 end
1773
1774 -- if unstampable face, then don't let us stamp there!
1775 if unstampableSurface then
1776 Data.Stamp.MouseLock = false
1777 HighScalabilityLine.Start = nil
1778 HighScalabilityLine.Adorn.Parent = nil
1779 flashRedBox()
1780 return
1781 end
1782
1783 -- Prevent part from being stamped on top of a player
1784
1785 local minBB, maxBB = getBoundingBoxInWorldCoordinates(Data.Stamp.CurrentParts[1])
1786 local configFound, targetCFrame = findConfigAtMouseTarget(Data.Stamp.TransparencyTable)
1787
1788 if configFound and not HighScalabilityLine.Adorn.Parent then
1789 if clusterPartsInRegion(minBB+insertBoundingBoxOverlapVector, maxBB-insertBoundingBoxOverlapVector) then Data.Stamp.MouseLock = false flashRedBox() return end
1790
1791 local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB+insertBoundingBoxOverlapVector, maxBB-insertBoundingBoxOverlapVector), Data.Stamp.CurrentParts[1], 100)
1792 for b = 1, #blockingParts do
1793 -- below if we only want to block stamping on self
1794 --if blockingParts[b].Parent == script.Parent.Parent then return end
1795
1796 -- below if we want to block stamping on self and also stamping that intersects a model we've previously stamped on our baseplate
1797 -- should work as soon as Region3 queries are fixed
1798 -- NOTE TO SELF: See if partModel applies when isRestricted is false
1799 --if blockingParts[b]:IsDescendantOf(partModel) or blockingParts[b].Parent == script.Parent.Parent then Data.Stamp.MouseLock = false flashRedBox() break end --return end
1800 if isBlocker(blockingParts[b]) then Data.Stamp.MouseLock = false flashRedBox() return end-- still error if overlapping another model [should return here too!]
1801 end
1802
1803 local alreadyPushedUp = {}
1804 -- if no blocking model below, then see if stamping on top of a character
1805 for b = 1, #blockingParts do
1806 --if blockingParts[b].Parent == script.Parent.Parent then
1807 -- local blockingPersonTorso = script.Parent.Parent:FindFirstChild("Torso")
1808 if blockingParts[b].Parent and not alreadyPushedUp[blockingParts[b].Parent] and blockingParts[b].Parent:FindFirstChild("Humanoid") and blockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
1809 local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild("Torso")
1810 alreadyPushedUp[blockingParts[b].Parent] = true
1811
1812 if blockingPersonTorso then
1813 -- 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)
1814 local newY = maxBB.Y + 3
1815 if spaceAboveCharacter(blockingPersonTorso, newY) then
1816 blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
1817 else
1818 -- if no space, we just error
1819 Data.Stamp.MouseLock = false
1820 flashRedBox()
1821 return
1822 -- should return here too!
1823 end
1824 end
1825
1826 --break
1827 end
1828 end
1829 elseif (not configFound) then
1830 HighScalabilityLine.Start = nil
1831 HighScalabilityLine.Adorn.Parent = nil
1832 Data.Stamp.MouseLock = false
1833 return
1834 end -- if no config then don't stamp!
1835
1836 -- something will be stamped! so set the "StampedSomething" toggle to true
1837 local localChar = game.Players:GetPlayerFromCharacter(script.Parent.Parent).Character
1838 if localChar then
1839 local stampTracker = localChar:FindFirstChild("StampTracker")
1840 if stampTracker and not stampTracker.Value then stampTracker.Value = true end
1841 end
1842
1843 -- also, show hints again if high scalability part
1844 HighScalabilityLine.NewHint = true
1845
1846 if Data.Stamp.Model and Data.Stamp.Model:GetChildren()[1] and Data.Stamp.Model:GetChildren()[1].Name == "MegaClusterCube" then
1847 local line = HighScalabilityLine.InternalLine
1848 local cMax = game.Workspace.Terrain.MaxExtents.Max
1849 local cMin = game.Workspace.Terrain.MaxExtents.Min
1850
1851 local clusterMaterial = 1 -- default is grass
1852 local clusterType = 0 -- default is brick
1853 local clusterOrientation = 0 -- default is 0 rotation
1854
1855 local autoWedgeClusterParts = false
1856 if Data.Stamp.CurrentParts[1]:FindFirstChild("AutoWedge") then autoWedgeClusterParts = true end
1857
1858 if Data.Stamp.CurrentParts[1]:FindFirstChild("ClusterMaterial") then
1859 clusterMaterial = Data.Stamp.CurrentParts[1].ClusterMaterial
1860 if (clusterMaterial:IsA("Vector3Value")) then
1861 -- extract all info from vector
1862 clusterType = clusterMaterial.Value.Y
1863 clusterOrientation = clusterMaterial.Value.Z
1864 clusterMaterial = clusterMaterial.Value.X
1865 else
1866 clusterMaterial = clusterMaterial.Value
1867 end
1868 end
1869
1870 if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) then
1871 local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start)
1872
1873 local xInc = {0,0,0}
1874 local yInc = {0,0,0}
1875 local zInc = {0,0,0}
1876
1877 local incrementVect = {nil, nil, nil}
1878 local stepVect = {Vector3.new(0, 0, 0), Vector3.new(0, 0, 0), Vector3.new(0, 0, 0)}
1879
1880 local worldAxes = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)}
1881
1882 local lines = {}
1883 if HighScalabilityLine.Dimensions > 1 then table.insert(lines, HighScalabilityLine.MoreLines[1]) end
1884 if line and line.magnitude > 0 then table.insert(lines, line) end
1885 if HighScalabilityLine.Dimensions > 2 then table.insert(lines, HighScalabilityLine.MoreLines[2]) end
1886
1887 for i = 1, #lines do
1888 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
1889
1890 if lines[i].X > 0 then xInc[i] = 1 elseif lines[i].X < 0 then xInc[i] = -1 end
1891 if lines[i].Y > 0 then yInc[i] = 1 elseif lines[i].Y < 0 then yInc[i] = -1 end
1892 if lines[i].Z > 0 then zInc[i] = 1 elseif lines[i].Z < 0 then zInc[i] = -1 end
1893
1894 incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i])
1895 if incrementVect[i].magnitude < .9 then incrementVect[i] = nil end
1896 end
1897
1898
1899 if not lines[2] then lines[2] = Vector3.new(0, 0, 0) end
1900 if not lines[3] then lines[3] = Vector3.new(0, 0, 0) end
1901
1902 while (stepVect[3].magnitude*4 <= lines[3].magnitude) do
1903 local outerStepVectIndex = 1
1904 while outerStepVectIndex < 4 do
1905 stepVect[2] = Vector3.new(0, 0, 0)
1906 while (stepVect[2].magnitude*4 <= lines[2].magnitude) do
1907 local innerStepVectIndex = 1
1908 while innerStepVectIndex < 4 do
1909 stepVect[1] = Vector3.new(0, 0, 0)
1910 while (stepVect[1].magnitude*4 <= lines[1].magnitude) do
1911 local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3]
1912 local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z)
1913 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
1914 -- check if overlaps player or part:
1915 local cellCenter = game.Workspace.Terrain:CellCenterToWorld(cellPos.X, cellPos.Y, cellPos.Z)
1916 local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), Data.Stamp.CurrentParts[1], 100)
1917
1918 local skipThisCell = false
1919 for b = 1, #cellBlockingParts do
1920 if isBlocker(cellBlockingParts[b]) then skipThisCell = true break end
1921 end
1922
1923 if not skipThisCell then
1924 -- pop players up above any set cells
1925 local alreadyPushedUp = {}
1926 -- if no blocking model below, then see if stamping on top of a character
1927 for b = 1, #cellBlockingParts do
1928 if cellBlockingParts[b].Parent and not alreadyPushedUp[cellBlockingParts[b].Parent] and cellBlockingParts[b].Parent:FindFirstChild("Humanoid") and cellBlockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then
1929 local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild("Torso")
1930 alreadyPushedUp[cellBlockingParts[b].Parent] = true
1931
1932 if blockingPersonTorso then
1933 -- 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)
1934 local newY = cellCenter.Y + 5
1935 if spaceAboveCharacter(blockingPersonTorso, newY) then
1936 blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0)
1937 else
1938 -- if no space, we just skip this one
1939 skipThisCell = true
1940 break
1941 end
1942 end
1943 end
1944 end
1945 end
1946
1947 if not skipThisCell then -- if we STILL aren't skipping... then we're good to go!
1948 -- set it
1949 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
1950
1951 -- auto-wedge it?
1952 if (autoWedgeClusterParts) then
1953 game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
1954 end
1955 end
1956 end
1957
1958 stepVect[1] = stepVect[1] + incrementVect[1]
1959 end
1960 if incrementVect[2] then
1961 while innerStepVectIndex < 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do innerStepVectIndex = innerStepVectIndex + 1 end
1962 if innerStepVectIndex < 4 then
1963 stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex]*worldAxes[innerStepVectIndex]:Dot(incrementVect[2])
1964 end
1965 innerStepVectIndex = innerStepVectIndex + 1
1966 else stepVect[2] = Vector3.new(1, 0, 0) innerStepVectIndex = 4 end -- skip all remaining loops
1967 if (stepVect[2].magnitude*4 > lines[2].magnitude) then innerStepVectIndex = 4 end
1968 end
1969 end
1970 wait()
1971 if incrementVect[3] then
1972 while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do outerStepVectIndex = outerStepVectIndex + 1 end
1973 if outerStepVectIndex < 4 then
1974 stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex]*worldAxes[outerStepVectIndex]:Dot(incrementVect[3])
1975 end
1976 outerStepVectIndex = outerStepVectIndex + 1
1977 else stepVect[3] = Vector3.new(1, 0, 0) outerStepVectIndex = 4 end -- skip all remaining loops
1978 if (stepVect[3].magnitude*4 > lines[3].magnitude) then outerStepVectIndex = 4 end
1979 end
1980 end
1981
1982 -- and also get rid of any HighScalabilityLine stuff if it's there
1983 HighScalabilityLine.Start = nil
1984 HighScalabilityLine.Adorn.Parent = nil
1985 Data.Stamp.MouseLock = false
1986 return
1987 end
1988
1989 -- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff]
1990 HighScalabilityLine.Start = nil
1991 HighScalabilityLine.Adorn.Parent = nil
1992
1993 -- if target point is in cluster, just use cluster:SetCell
1994 if cluster then
1995 -- if targetCFrame is inside cluster, just set that cell to 1 and return
1996 local cellPos = cluster:WorldToCell(targetCFrame.p)
1997
1998 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
1999 cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation)
2000
2001 -- auto-wedge it
2002 if (autoWedgeClusterParts) then
2003 game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1)))
2004 end
2005
2006 Data.Stamp.MouseLock = false
2007 return
2008 end
2009 end
2010 end
2011
2012 -- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints
2013 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
2014
2015 if Data.Stamp.CurrentParts[1]:IsA("Model") then
2016 -- Tyler's magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don't ask X<
2017 manualWeldTable = {}
2018 manualWeldParentTable = {}
2019 saveTheWelds(Data.Stamp.CurrentParts[1])
2020 Data.Stamp.CurrentParts[1]:BreakJoints()
2021 Data.Stamp.CurrentParts[1]:MakeJoints()
2022 restoreTheWelds()
2023 Data.Stamp.CurrentParts[1]:MakeJoints()
2024 Data.Stamp.CurrentParts[1]:MakeJoints()
2025 Data.Stamp.CurrentParts[1]:MakeJoints()
2026 Data.Stamp.CurrentParts[1]:MakeJoints()
2027 Data.Stamp.CurrentParts[1]:MakeJoints()
2028 Data.Stamp.CurrentParts[1]:MakeJoints()
2029 Data.Stamp.CurrentParts[1]:MakeJoints()
2030 end
2031
2032 -- 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)
2033 playerIdTag = Data.Stamp.CurrentParts[1]:FindFirstChild("PlayerIdTag")
2034 playerNameTag = Data.Stamp.CurrentParts[1]:FindFirstChild("PlayerNameTag")
2035 if playerIdTag ~= nil then
2036 tempPlayerValue = getPlayer()
2037 if tempPlayerValue ~= nil then playerIdTag.Value = tempPlayerValue.userId end
2038 end
2039 if playerNameTag ~= nil then
2040 tempPlayerValue = getPlayer()
2041 if tempPlayerValue ~= nil then playerNameTag.Value = tempPlayerValue.Name end
2042 end
2043 -- ...and tag all inserted models for subsequent origin identification
2044 -- if no RobloxModel tag already exists, then add it.
2045 if Data.Stamp.CurrentParts[1]:FindFirstChild("RobloxModel") == nil then
2046 local stringTag = Instance.new("BoolValue", Data.Stamp.CurrentParts[1])
2047 stringTag.Name = "RobloxModel"
2048
2049 if Data.Stamp.CurrentParts[1]:FindFirstChild("RobloxStamper") == nil then
2050 local stringTag2 = Instance.new("BoolValue", Data.Stamp.CurrentParts[1])
2051 stringTag2.Name = "RobloxStamper"
2052 end
2053 end
2054
2055 else
2056 Data.Stamp.CurrentParts[1]:BreakJoints()
2057 if Data.Stamp.CurrentParts[1]:FindFirstChild("RobloxStamper") == nil then
2058 local stringTag2 = Instance.new("BoolValue", Data.Stamp.CurrentParts[1])
2059 stringTag2.Name = "RobloxStamper"
2060 end
2061 end
2062
2063 -- make sure all the joints are activated before restoring anchor states
2064 game.JointsService:CreateJoinAfterMoveJoints()
2065
2066 click.Value = true
2067 --Fix the transparency and material of all the parts
2068 for part, transparency in pairs(Data.Stamp.TransparencyTable) do
2069 part.Transparency = transparency
2070 part.archivable = true
2071 end
2072 for part, material in pairs(Data.Stamp.MaterialTable) do
2073 part.Material = material
2074 end
2075 for part, collide in pairs(Data.Stamp.CanCollideTable) do
2076 part.CanCollide = collide
2077 end
2078 for part, anchored in pairs(Data.Stamp.AnchoredTable) do
2079 part.Anchored = anchored
2080 end
2081 for decal, transparency in pairs(Data.Stamp.DecalTransparencyTable) do
2082 decal.Transparency = transparency
2083 end
2084
2085 if (Data.Stamp.CurrentParts[1] and Data.Stamp.CurrentParts[1].Name == "MegaClusterCube") then
2086 Data.Stamp.CurrentParts[1].Transparency = 0
2087 end
2088
2089 -- re-enable all seats
2090 setSeatEnabledStatus(Data.Stamp.CurrentParts[1], true)
2091
2092 Data.Stamp.TransparencyTable = nil
2093 Data.Stamp.MaterialTable = nil
2094 Data.Stamp.CanCollideTable = nil
2095 Data.Stamp.AnchoredTable = nil
2096
2097 -- ...and tag all inserted models for subsequent origin identification
2098 -- if no RobloxModel tag already exists, then add it.
2099 if Data.Stamp.CurrentParts[1]:FindFirstChild("RobloxModel") == nil then
2100 local stringTag = Instance.new("BoolValue", Data.Stamp.CurrentParts[1])
2101 stringTag.Name = "RobloxModel"
2102 end
2103
2104 -- set our object back to the player's plate
2105 Data.Stamp.CurrentParts[1].Parent = partModel
2106 Data.Stamp.CurrentParts[1]:MakeJoints()
2107 --Re-enable the scripts
2108 for index,script in pairs(Data.Stamp.DisabledScripts) do
2109 script.Disabled = false
2110 end
2111 --Now that they are all marked enabled, reinsert them into the world so they start running
2112 for index,script in pairs(Data.Stamp.DisabledScripts) do
2113 local oldParent = script.Parent
2114 script.Parent = nil
2115 script:Clone().Parent = oldParent
2116 end
2117 Data.Stamp.DisabledScripts = nil
2118 Data.Stamp.Dragger = nil
2119 Data.Stamp.CurrentParts.Parent = nil
2120 Data.Stamp.CurrentParts = nil
2121
2122 Data.Stamp.MouseLock = false
2123
2124 --Now set up a new instance of the object to allow a second copy to be stamped down
2125 variables.Stamped.Value = true
2126 variables.Stamped.Value = false
2127 if isRestricted then
2128 if checkPartLimit() then
2129 setupDraggableClone()
2130 else
2131 variables.ShowMaxedOut.Value = true
2132 end
2133 else
2134 setupDraggableClone()
2135 end
2136
2137 end
2138 end
2139end
2140
2141
2142------------------------ EyeDropper Code -------------------------------------------
2143
2144function onEyeDropperMouseButton1Down()
2145 if eyeDropperMoveConnection then
2146 eyeDropperMoveConnection:disconnect()
2147 end
2148 clearSelection()
2149 if Mouse then
2150 Mouse.Icon = "http://www.roblox.com/asset?id=66887745"
2151 end
2152
2153 -- deny any attempt to clone something that wasn't stamped using the Stamper tool
2154 if not Mouse or not lolmousetarget then
2155 startEyeDropperOperation()
2156 return
2157 end
2158
2159 local isTerrainEyedroppering = false
2160 if lolmousetarget == game.Workspace.Terrain then
2161 -- want to eyedropper a terrain piece; gotta do a little extra footwork
2162 print("Eyedroppering Terrain Piece")
2163 local newTerrainPiece
2164
2165 -- gotta make the fake part here
2166 local hitCell = game.Workspace.Terrain:WorldToCellPreferSolid(lolmousehit.p)
2167 local cellMat, cellType, cellOrient = game.Workspace.Terrain:GetCell(hitCell.x, hitCell.y, hitCell.z)
2168
2169 if (cellType.Value == 1 or cellType.Value == 4) then newTerrainPiece = Instance.new("WedgePart") newTerrainPiece.formFactor = "Custom"
2170 elseif (cellType.Value == 2) then newTerrainPiece = Instance.new("CornerWedgePart")
2171 else newTerrainPiece = Instance.new("Part") newTerrainPiece.formFactor = "Custom" end
2172 newTerrainPiece.Name = "MegaClusterCube"
2173 newTerrainPiece.Size = Vector3.new(4, 4, 4)
2174 newTerrainPiece.BottomSurface = "Smooth"
2175 newTerrainPiece.TopSurface = "Smooth"
2176
2177 -- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors
2178 newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat.Value)
2179
2180 local sideways = 0
2181 local flipped = math.pi
2182 if cellType.Value == 4 then sideways = -math.pi/2 end
2183 if cellType.Value == 2 or cellType.Value == 3 then flipped = 0 end
2184 newTerrainPiece.CFrame = CFrame.Angles(0, math.pi/2*cellOrient.Value + flipped, sideways)
2185
2186 if cellType.Value == 3 then
2187 local inverseCornerWedgeMesh = Instance.new("SpecialMesh")
2188 inverseCornerWedgeMesh.MeshType = "FileMesh"
2189 inverseCornerWedgeMesh.MeshId = "http://www.roblox.com/asset?id=66832495"
2190 inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2)
2191 inverseCornerWedgeMesh.Parent = newTerrainPiece
2192 end
2193
2194 local materialTag = Instance.new("Vector3Value")
2195 materialTag.Value = Vector3.new(cellMat.Value, cellType.Value, cellOrient.Value)
2196 materialTag.Name = "ClusterMaterial"
2197 materialTag.Parent = newTerrainPiece
2198
2199 local tempModel = Instance.new("Model")
2200 newTerrainPiece.Parent = tempModel
2201
2202 Data.Stamp.Model = tempModel
2203 Data.Stamp.Model:BreakJoints()
2204
2205 isTerrainEyedroppering = true
2206 end
2207 local stamperTag
2208 if not pcall(function() a = lolmousetarget.Parent:FindFirstChild("RobloxStamper")end) then
2209 stamperTag = nil
2210 else
2211 stamperTag = lolmousetarget.Parent:FindFirstChild("RobloxStamper")
2212 end
2213 if stamperTag == nil then stamperTag = lolmousetarget:FindFirstChild("RobloxStamper") end
2214 if stamperTag == nil and not isTerrainEyedroppering then
2215 startEyeDropperOperation()
2216 return
2217 end
2218
2219 local eyeDropperInstance
2220 -- find out if the target part is part of a Roblox Set Model
2221 local robloxModelTag
2222 if not pcall(function() a = lolmousetarget.Parent:FindFirstChild("RobloxModel")end) then
2223 robloxModelTag = nil
2224 else
2225 robloxModelTag = lolmousetarget.Parent:FindFirstChild("RobloxModel")
2226 end
2227 if robloxModelTag ~= nil then
2228 eyeDropperInstance = lolmousetarget.Parent
2229 else
2230 eyeDropperInstance = lolmousetarget
2231 end
2232
2233 -- do not allow certain objects to be captured with eye-dropper
2234 -- for now, locked parts
2235 if eyeDropperInstance:IsA("Part") and eyeDropperInstance.Locked and not isTerrainEyedroppering then
2236 startEyeDropperOperation()
2237 else
2238 if not isTerrainEyedroppering then
2239 local cloneInstance = eyeDropperInstance:clone()
2240 local tempModel = Instance.new("Model")
2241 cloneInstance.Parent = tempModel
2242
2243 -- once more, we make sure it's on grid before eyedroppering
2244 if not isOnGrid(tempModel) then startEyeDropperOperation() return end
2245
2246 Data.Stamp.Model = tempModel
2247 Data.Stamp.Model:BreakJoints()
2248 end
2249
2250 -- will create and position clone without requiring user to move the mouse
2251
2252 if isRestricted then
2253 if checkPartLimit() then
2254 setupDraggableClone()
2255 if Mouse and not mouseButton1UpCon then mouseButton1UpCon = Mouse.Button1Up:connect(onInsertMouseButton1Up) end
2256 else
2257 variables.ShowMaxedOut.Value = true
2258 end
2259 else
2260 setupDraggableClone()
2261 if Mouse and not mouseButton1UpCon then mouseButton1UpCon = Mouse.Button1Up:connect(onInsertMouseButton1Up) end
2262 end
2263
2264 variables.SwitchLoaderToDialog.AssetImage.Value = "0"
2265 variables.SwitchLoaderToDialog.DialogType.Value = "SideDialog"
2266 variables.SwitchLoaderToDialog.Value = true
2267 end
2268end
2269
2270function onEyeDropperMouseMove()
2271 if not(inGui) and not(inPalette) then
2272 if not Mouse or not lolmousetarget then clearSelection() return end
2273 local part = lolmousetarget
2274 if part:IsA("Terrain") and lolmousehit then
2275 selectionBox.Color = BrickColor.Green()
2276 setTerrainSelection(lolmousehit.p)
2277 elseif canEyeDropperObject(part) then
2278 local model = findModel(part)
2279 if model and isOnGrid(model) then
2280 selectionBox.Color = BrickColor.Green()
2281 setSelection(model)
2282 elseif (not model) and isOnGrid(part) then
2283 selectionBox.Color = BrickColor.Green()
2284 setSelection(part)
2285 else
2286 clearSelection()
2287 end
2288 else
2289 clearSelection()
2290 end
2291 end
2292end
2293
2294function startEyeDropperOperation()
2295
2296 cancelAssetPlacement()
2297 pressedEsc = false
2298 signalInsertComplete("EyeDropper")
2299
2300 if mouseButton1UpCon then mouseButton1UpCon:disconnect() mouseButton1UpCon = nil end
2301
2302 if eyeDropperConnection then
2303 eyeDropperConnection:disconnect()
2304 eyeDropperConnection = nil
2305 end
2306 if eyeDropperMoveConnection then eyeDropperMoveConnection:disconnect() end
2307 if Mouse then
2308 --Mouse.Icon ="rbxasset://textures//DropperCursor.png"
2309 Mouse.Icon = "http://www.roblox.com/asset?id=67163166"
2310 eyeDropperConnection = Mouse.Button1Up:connect(onEyeDropperMouseButton1Down)
2311 eyeDropperMoveConnection = Mouse.Move:connect(onEyeDropperMouseMove)
2312 end
2313
2314end
2315
2316function findModel(part)
2317
2318 if isRestricted then
2319 while part ~= nil do
2320 if part.className == "Model" and part.Name ~= playerModel.Name and part.Name ~= "GarbageParts" then
2321 return part
2322 elseif part.Name == playerModel.Name or part.Name == "GarbageParts" then
2323 return nil
2324 end
2325 part = part.Parent
2326 end
2327 return nil
2328
2329 else
2330 while part ~= game.Workspace do
2331 if part:FindFirstChild("RobloxModel") then
2332 return part
2333 end
2334 part = part.Parent
2335 end
2336 return nil
2337 end
2338
2339end
2340
2341------------------------ End EyeDropper Code ---------------------------------------
2342
2343
2344
2345
2346------------------------ Start Selection Highlighting Code --------------------------
2347function setTerrainSelection(point)
2348 if selectionBox then
2349 local cell = game.Workspace.Terrain:WorldToCellPreferSolid(point)
2350 local cellCenter = game.Workspace.Terrain:CellCenterToWorld(cell.X, cell.Y, cell.Z)
2351 terrainSelectionBox.CFrame = CFrame.new(cellCenter)
2352 selectionBox.Adornee = terrainSelectionBox
2353 end
2354end
2355
2356function setSelection(partOrModel)
2357 if partOrModel ~= currentSelection then
2358 clearSelection()
2359 currentSelection = partOrModel
2360 selectionBox.Adornee = currentSelection
2361 end
2362end
2363
2364function clearSelection()
2365 if currentSelection ~= nil then
2366 for part, color in pairs(currentSelectionColors) do
2367 part.BrickColor = color
2368 end
2369 selectionBox.Adornee = nil
2370 end
2371 currentSelectionColors = {}
2372 -- I put these inside if statements, because we can't assume these exist. (Jahr, 12-29-2010)
2373 if currentSelection then currentSelection = nil end
2374 if selectionBox then selectionBox.Adornee = nil end
2375end
2376
2377------------------------ End Selection Highlighting Code --------------------------
2378
2379function autoAlignToFace()
2380 local aatf = Data.Stamp.CurrentParts[1]:FindFirstChild("AutoAlignToFace")
2381 if aatf then return aatf.Value else return false end
2382end
2383
2384
2385function autoAlignHelper()
2386 local model = Data.Stamp.CurrentParts[1]
2387 local aatfTag = model:FindFirstChild("AutoAlignToFace")
2388 if not aatfTag then
2389 aatfTag = Instance.new("IntValue")
2390 aatfTag.Name = "AutoAlignToFace"
2391 aatfTag.Parent = Data.Stamp.CurrentParts[1]
2392 aatfTag.Value = 3
2393 end
2394 aatfTag.Value = aatfTag.Value + 1
2395 if aatfTag.Value > 3 then aatfTag.Value = 0 end
2396end
2397
2398function unstampableFaceHelper()
2399 if not Mouse or not lolmousetarget then return end
2400 local model = lolmousetarget
2401 if not model then return end
2402 if not model:FindFirstChild("RobloxModel") then model = model.Parent end
2403 if not model then return end
2404 if not model:FindFirstChild("RobloxModel") then return end
2405
2406 local ufhTag = model:FindFirstChild("UnstampableFaces")
2407 if not ufhTag then
2408 ufhTag = Instance.new("StringValue")
2409 ufhTag.Name = "UnstampableFaces"
2410 ufhTag.Parent = model
2411 ufhTag.Value = ""
2412 end
2413
2414 local hitFace = modelTargetSurface(model, game.Workspace.CurrentCamera.CoordinateFrame.p, lolmousehit.p)
2415
2416 -- put string list into table form
2417 breakingFaceList = {}
2418 for bf = -3, 3 do
2419 breakingFaceList[bf] = false
2420 end
2421 for bf in string.gmatch(ufhTag.Value, "[^,]+") do
2422 breakingFaceList[tonumber(bf)] = true
2423 end
2424
2425 -- toggle value of hit face
2426 breakingFaceList[hitFace] = not breakingFaceList[hitFace]
2427
2428 -- put table form back into string value
2429 ufhTag.Value = ""
2430 local seenAValueSoFar = false
2431 for bf = -3, 3 do
2432 if bf ~= 0 then -- ignore 0 face, since that doesn't exist
2433 if breakingFaceList[bf] then
2434 if seenAValueSoFar then ufhTag.Value = ufhTag.Value .. "," .. tostring(bf)
2435 else ufhTag.Value = tostring(bf) seenAValueSoFar = true end
2436 end
2437 end
2438 end
2439end
2440
2441function justificationHelper(whichAxis)
2442 local model = Data.Stamp.CurrentParts[1]
2443 local justTag = model:FindFirstChild("Justification")
2444 if not justTag then
2445 justTag = Instance.new("Vector3Value")
2446 justTag.Name = "Justification"
2447 justTag.Parent = Data.Stamp.CurrentParts[1]
2448 justTag.Value = Vector3.new(1, 1, 1)
2449 end
2450 local oldValue = justTag.Value
2451 if whichAxis == 1 then
2452 if oldValue.X == 2 then justTag.Value = Vector3.new(0, oldValue.Y, oldValue.Z)
2453 else justTag.Value = Vector3.new(oldValue.X+1, oldValue.Y, oldValue.Z) end
2454 elseif whichAxis == 2 then
2455 if oldValue.Y == 2 then justTag.Value = Vector3.new(oldValue.X, 0, oldValue.Z)
2456 else justTag.Value = Vector3.new(oldValue.X, oldValue.Y+1, oldValue.Z) end
2457 elseif whichAxis == 3 then
2458 if oldValue.Z == 2 then justTag.Value = Vector3.new(oldValue.X, oldValue.Y, 0)
2459 else justTag.Value = Vector3.new(oldValue.X, oldValue.Y, oldValue.Z+1) end
2460 end
2461end
2462
2463function onInsertKeyDown(_,key)
2464 key = string.lower(key)
2465 if Data.Stamp.Dragger then
2466 if key == 'c' and Data.Stamp.CurrentParts[1].Name == "MegaClusterCube" and HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 and HighScalabilityLine.Dimensions < 3 then
2467 HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End
2468 HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine
2469 HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1
2470 HighScalabilityLine.NewHint = true
2471 end
2472
2473 if key == 'r' and not autoAlignToFace() then
2474 -- Update orientation value if this is a fake terrain part
2475 if Data.Stamp.CurrentParts[1].Name == "MegaClusterCube" then
2476 local clusterValues = Data.Stamp.CurrentParts[1]:FindFirstChild("ClusterMaterial")
2477 if clusterValues and clusterValues:IsA("Vector3Value") then
2478 clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z+1)%4)
2479 end
2480 end
2481
2482 -- Rotate the parts or all the parts in the model
2483 local ry = math.pi/2
2484 local rotCF = CFrame.fromEulerAnglesXYZ(0, ry, 0)
2485 gInitial90DegreeRotations = gInitial90DegreeRotations + 1
2486 if Data.Stamp.CurrentParts[1]:IsA("Model") or Data.Stamp.CurrentParts[1]:IsA("Tool") then
2487 for i, object in pairs(Data.Stamp.CurrentParts[1]:GetChildren()) do
2488 if object:IsA("Flag") then object = object.Handle end
2489 if object:IsA("Part") or object:IsA("TrussPart") or object:IsA("WedgePart") or object:IsA("CornerWedgePart") or object:IsA("Seat") or object:IsA("VehicleSeat") then
2490 object.CFrame = rotCF * object.CFrame
2491 end
2492 end
2493 else
2494 Data.Stamp.CurrentParts[1].CFrame = rotCF * Data.Stamp.CurrentParts[1].CFrame
2495 end
2496 -- After rotating, update the position
2497 configFound, targetCFrame = findConfigAtMouseTarget(Data.Stamp.TransparencyTable)
2498 if configFound then
2499 positionPartsAtCFrame3(Data.Stamp.CurrentParts[1], targetCFrame)
2500
2501 -- update everything else in MouseMove
2502 onInsertMouseMove()
2503 end
2504 end
2505 end
2506 if key == 'e' then
2507 startEyeDropperOperation()
2508 elseif key == 'q' then
2509 pressedEsc = true
2510 if eyeDropperConnection then
2511 eyeDropperConnection:disconnect()
2512 eyeDropperConnection = nil
2513 end
2514 if eyeDropperMoveConnection then eyeDropperMoveConnection:disconnect() end
2515
2516 if Mouse and not mouseButton1UpCon then mouseButton1UpCon = Mouse.Button1Up:connect(onInsertMouseButton1Up) end
2517
2518 clearSelection()
2519 cancelAssetPlacement()
2520 Data.Loading.Cancelled = true
2521 signalInsertComplete("Main")
2522 end
2523
2524 -- admin-only tools: helpful functions for construction purposes
2525 if adminAccess then
2526 if key == 'L' or key == 'l' then
2527 -- autoalign helper
2528 if Data.Stamp.Dragger then autoAlignHelper() onInsertMouseMove() end
2529 elseif key == 'U' or key == 'u' then
2530 -- unstampable face helper
2531 unstampableFaceHelper()
2532 --elseif key == 'C' or key == 'c' then
2533 -- if Data.Stamp.Dragger then justificationHelper() end -- the 'c' stands for "Centering" since "j" is already reserved by GuiScript
2534 --end
2535 elseif key == 'X' or key == 'x' then -- change x justification
2536 if Data.Stamp.Dragger then justificationHelper(1) onInsertMouseMove() end
2537 elseif key == 'Y' or key == 'y' then -- change y justification
2538 if Data.Stamp.Dragger then justificationHelper(2) onInsertMouseMove() end
2539 elseif key == 'Z' or key == 'z' then -- change z justification
2540 if Data.Stamp.Dragger then justificationHelper(3) onInsertMouseMove() end
2541 end
2542 end
2543end
2544
2545
2546function onEquippedLocal(newMouse)
2547 Mouse = newMouse
2548
2549 player = getPlayer()
2550
2551 -- if equip goes through while in backpack (so getPlayer() returns nil), we don't want to process anything else
2552 if not player then return end
2553
2554 --[[if game:FindFirstChild("NetworkClient") and game.CoreGui.Version >= 7 then only show billboards in online mode
2555 if not billBoardOwnerGui then billBoardOwnerGui = generateOwnerGui(player.Name) end
2556 end]]
2557
2558 if isRestricted then
2559 if game.Workspace:FindFirstChild("BuildingAreas") then
2560 local areas = game.Workspace.BuildingAreas:GetChildren()
2561 for i = 1, #areas do
2562 if areas[i]:FindFirstChild("Player") and areas[i].Player.Value == Tool.Parent.Name then
2563 playerModel = areas[i]:FindFirstChild("PlayerArea")
2564 break
2565 end
2566 end
2567 end
2568 else
2569 playerModel = game.Workspace
2570
2571 -- give them a topHint gui too, if they don't have it since the place won't already have it
2572 if not player.PlayerGui:FindFirstChild("topHint") then
2573 local topHintGui = script.Parent:FindFirstChild("topHint")
2574 if topHintGui then
2575 topHintGui:Clone().Parent = player.PlayerGui
2576 end
2577 end
2578 end
2579
2580 if not playerModel then return end
2581
2582 -- used to move object when walking
2583 cameraChangeCon = game.Players:GetPlayerFromCharacter(script.Parent.Parent).Character.Humanoid.Running:connect(function(speed)
2584 if speed > 0 then
2585 walking = true
2586 while walking and cameraChangeCon do
2587 onInsertMouseMove()
2588 wait(1.0/30.0)
2589 end
2590 else
2591 walking = false
2592 end
2593 end)
2594 if Mouse then
2595 mouseMoveCon = script.Parent.Move.OnServerEvent:connect(onInsertMouseMove)
2596 mouseButton1DownCon = script.Parent.Down.OnServerEvent:connect(function(_,hitt,tar,sur)
2597 lolmousehit = hitt
2598 lolmousetarget = tar
2599 onInsertMouseButton1Down()
2600 end)
2601 mouseButton1UpCon = script.Parent.Up.OnServerEvent:connect(function(_,hitt,tar,sur)
2602 lolmousehit = hitt
2603 lolmousetarget = tar
2604 onInsertMouseButton1Up()
2605 end)
2606 script.Parent.KeyDown.OnServerEvent:connect(function(_,k,hitt,tar)
2607 lolmousehit = hitt
2608 lolmousetarget = tar
2609 onInsertKeyDown(nil,k)
2610 end)
2611 end
2612
2613 if(isRestricted) then
2614 local takenAreas = game.Workspace.BuildingAreas:GetChildren()
2615
2616 waitForChild(player, "playerNumber")
2617
2618 if(player.playerNumber.Value == 0) then
2619 buildingPlate = nil
2620 partModel = nil
2621 else
2622 waitForChild(game.Workspace, "BuildingAreas")
2623 local buildingAreas = game.Workspace.BuildingAreas
2624 waitForChild(buildingAreas, "Area"..tostring(player.playerNumber.Value))
2625 local targetArea = buildingAreas:FindFirstChild("Area"..tostring(player.playerNumber.Value))
2626
2627 waitForChild(targetArea, "PlayerArea")
2628 waitForChild(targetArea.PlayerArea, "BasePlate")
2629
2630 buildingPlate = targetArea.PlayerArea.BasePlate
2631 partModel = targetArea.PlayerArea
2632 end
2633 else
2634 partModel = game.Workspace
2635 end
2636
2637 selectionBox = Instance.new("SelectionBox")
2638 selectionBox.Name = "Model Delete Selection"
2639 selectionBox.Color = BrickColor.Red()
2640 selectionBox.Adornee = nil
2641 selectionBox.Parent = player.PlayerGui
2642
2643 alreadyMoving = false
2644end
2645
2646
2647function onUnequipped()
2648 Mouse = nil
2649 if mouseMoveCon then mouseMoveCon:disconnect() end
2650 if mouseButton1DownCon then mouseButton1DownCon:disconnect() end
2651 if mouseButton1UpCon then mouseButton1UpCon:disconnect() end
2652 if cameraChangeCon then cameraChangeCon:disconnect() cameraChangeCon = nil end
2653 if billBoardOwnerGui then billBoardOwnerGui:Remove() end
2654
2655 cancelAssetPlacement()
2656
2657 clearSelection()
2658 if selectionBox then selectionBox:Remove() end
2659 bin=script.Parent
2660 bro=script.Parent.Parent
2661 game.Debris:AddItem(bin,0)
2662end
2663----------------------------------------------------------------------------------------
2664
2665
2666
2667
2668-- Lua Start Script
2669Tool.Equipped:connect(function(newMouse) onEquippedLocal(newMouse) end)
2670
2671waitForChild(variables,"InsertAsset")
2672waitForChild(variables.InsertAsset, "Updated")
2673variables.InsertAsset.Updated.Changed:connect(function(prop)
2674 if variables.InsertAsset.Updated.Value == true then
2675 pressedEsc = false
2676 beginInsertAssetStamp(variables.InsertAsset.AssetName.Value, variables.InsertAsset.AssetId.Value,
2677 variables.InsertAsset.Image.Value, variables.InsertAsset.StampMode.Value)
2678 variables.InsertAsset.Updated.Value = false
2679 end
2680end)
2681
2682waitForChild(variables, "SwitchMode")
2683waitForChild(variables.SwitchMode, "Mode")
2684variables.SwitchMode.Changed:connect(function()
2685 if variables.SwitchMode.Value == true then
2686 if variables.SwitchMode.Mode.Value == "Clone" then
2687 startEyeDropperOperation()
2688 end
2689 variables.SwitchMode.Value = false
2690 end
2691end)
2692
2693waitForChild(variables, "ReloadCurrentAsset")
2694variables.ReloadCurrentAsset.Changed:connect(function()
2695 if variables.ReloadCurrentAsset.Value == true then
2696 setupDraggableClone()
2697 variables.ReloadCurrentAsset.Value = false
2698 end
2699end)
2700script.Parent.Move.OnServerEvent:Connect(function(_,hitt,tar,sur)
2701 lolmousehit = hitt
2702 lolmousetarget = tar
2703 lolmousetargetSurface = sur
2704end)
2705Tool.Unequipped:connect(function() onUnequipped() end)
2706----------------------------------------------------------------------------------------
2707while wait() do
2708 if Data.Stamp.CurrentParts and Data.Stamp.CurrentParts[1] then
2709 Data.Stamp.CurrentParts[1].Parent = workspace
2710 for i,v in pairs (Data.Stamp.CurrentParts[1]:GetDescendants()) do
2711 if v and v:IsA("Part") then
2712 v.TopSurface = "Universal"
2713 v.BottomSurface = "Universal"
2714 v.LeftSurface = "Universal"
2715 v.RightSurface = "Universal"
2716 v.FrontSurface = "Universal"
2717 v.BackSurface = "Universal"
2718 end
2719 end
2720 Data.Stamp.CurrentParts[1].Parent = nil
2721 end
2722end
2723--cool fe stamper but it was glitchy so i had to sacrifice the selector for coordinator.
2724