From 2abd4357119039a614f9e6c73a171c830540baf7 Mon Sep 17 00:00:00 2001 From: Lewin Kelly Date: Sat, 22 Apr 2023 10:46:33 +0100 Subject: [PATCH] Finish porting corescripts to Yuescript --- lua/73157242.lua | 2 +- yue/48488235.lua | 1134 +++++++++++++++++++--- yue/48488235.yue | 1429 +++++++++++++++++++++++---- yue/59002209.lua | 0 yue/73157242.lua | 1944 +++++++++++++++++++++++++++++++++++++ yue/73157242.yue | 2390 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 6600 insertions(+), 299 deletions(-) create mode 100644 yue/59002209.lua create mode 100644 yue/73157242.lua diff --git a/lua/73157242.lua b/lua/73157242.lua index da41bb3..6dd5e2a 100644 --- a/lua/73157242.lua +++ b/lua/73157242.lua @@ -538,7 +538,7 @@ local function findConfigAtMouseTarget(Mouse, stampData) end]] end - local targetRefPointInTarget + local targetRefPointInTarget, insertRefPointInInsert local clampToSurface if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then diff --git a/yue/48488235.lua b/yue/48488235.lua index 64e0325..3847cdc 100644 --- a/yue/48488235.lua +++ b/yue/48488235.lua @@ -111,7 +111,6 @@ MakePopupButton = function(nparent, ntext, index, last) BackgroundTransparency = 1, Position = UDim2.new(0.07, 0, 0.07, 0), Size = UDim2.new(0.86, 0, 0.86, 0), - Parent = HeaderFrame, Font = "ArialBold", Text = ntext, FontSize = "Size14", @@ -140,6 +139,7 @@ MakePopupButton = function(nparent, ntext, index, last) end)() return tobj end +local debugOutput local DebugPrintEnabled = true local debugprint debugprint = function(str) @@ -396,7 +396,8 @@ local ReportAbuseFrame = New("Frame", "Settings", { Active = true, Parent = ReportAbuseShield }) -local AbuseSettingsFrame = New("Frame", "ReportAbuseStyle", { +local AbuseSettingsFrame +AbuseSettingsFrame = New("Frame", "ReportAbuseStyle", { Size = UDim2.new(1, 0, 1, 0), Active = true, BackgroundTransparency = 1, @@ -577,12 +578,13 @@ local NormalAbuseBox = New("Frame", "AbuseFeedbackBox", { Image = "http://www.roblox.com/asset/?id=96507959" }) }) -local BigButton = Instance.new("ImageButton") -BigButton.Size = UDim2.new(1, 0, 1, 0) -BigButton.BackgroundTransparency = 1 -BigButton.ZIndex = 8 -BigButton.Visible = false -BigButton.Parent = ScreenGui +local BigButton = New("ImageButton", { + Size = UDim2.new(1, 0, 1, 0), + BackgroundTransparency = 1, + ZIndex = 8, + Visible = false, + Parent = ScreenGui +}) local debugFrame = New("Frame", "debugframe", { BackgroundTransparency = 1, Position = UDim2.new(0.25, 0, 0.3, 0), @@ -604,7 +606,7 @@ local debugplayers = New("TextLabel", { TextXAlignment = "Right", TextYAlignment = "Center" }) -local debugOutput = New("TextLabel", { +debugOutput = New("TextLabel", { BackgroundTransparency = 0.8, Position = UDim2.new(0, 0, 0.5, 0), Size = UDim2.new(1, 0, 0.5, 0), @@ -756,12 +758,6 @@ SetPrivilegeRank = function(player, nrank) game:GetService("PersonalServerService"):Demote(player) end end -local OnPrivilegeLevelSelect -OnPrivilegeLevelSelect = function(player, nlevel, BanPlayerButton, VisitorButton, MemberButton, AdminButton) - debugprint("setting privilege level") - SetPrivilegeRank(player, nlevel) - return HighlightMyRank(player, BanPlayerButton, VisitorButton, MemberButton, AdminButton) -end local assetid = "http://www.roblox.com/asset/?id=" local HighlightMyRank HighlightMyRank = function(player, BanPlayerButton, VisitorButton, MemberButton, AdminButton) @@ -780,6 +776,23 @@ HighlightMyRank = function(player, BanPlayerButton, VisitorButton, MemberButton, AdminButton.Image = assetid .. Images["DarkBluePopupBottom"] end end +local OnPrivilegeLevelSelect +OnPrivilegeLevelSelect = function(player, nlevel, BanPlayerButton, VisitorButton, MemberButton, AdminButton) + debugprint("setting privilege level") + SetPrivilegeRank(player, nlevel) + return HighlightMyRank(player, BanPlayerButton, VisitorButton, MemberButton, AdminButton) +end +local CloseAbuseDialog +CloseAbuseDialog = function() + AbuseName = nil + SubmitReportButton.Active = false + SubmitReportButton.Image = "http://www.roblox.com/asset/?id=96502438" + AbuseDescriptionBox:Destroy() + CalmingAbuseBox.Parent = nil + NormalAbuseBox.Parent = nil + ReportAbuseShield.Parent = nil + AbuseSettingsFrame.Visible = true +end local OnSubmitAbuse OnSubmitAbuse = function() if SubmitReportButton.Active then @@ -797,6 +810,19 @@ OnSubmitAbuse = function() end end end +local ClosePopUpPanel +ClosePopUpPanel = function() + if SelectedPlayerEntry then + local tframe = SelectedPlayerEntry["Frame"] + Spawn(function() + return TweenProperty(tframe, "BackgroundTransparency", 0.5, 1, BASE_TWEEN) + end) + end + PopUpPanel:TweenPosition(UDim2.new(1, 0, 0, 0), "Out", "Linear", BASE_TWEEN, true) + wait(0.1) + InPopupWaitForClick = false + SelectedPlayerEntry = nil +end local OpenAbuseDialog OpenAbuseDialog = function() debugprint("adding report dialog") @@ -807,17 +833,6 @@ OpenAbuseDialog = function() ReportAbuseShield.Parent = ScreenGui return ClosePopUpPanel() end -local CloseAbuseDialog -CloseAbuseDialog = function() - AbuseName = nil - SubmitReportButton.Active = false - SubmitReportButton.Image = "http://www.roblox.com/asset/?id=96502438" - AbuseDescriptionBox:Destroy() - CalmingAbuseBox.Parent = nil - NormalAbuseBox.Parent = nil - ReportAbuseShield.Parent = nil - AbuseSettingsFrame.Visible = true -end local InitReportAbuse InitReportAbuse = function() UpdateAbuseFunction = function(abuseText) @@ -882,6 +897,23 @@ PlayerSortFunction = function(a, b) end return a["Score"] < b["Score"] end +local UpdateMinimize +local Tabify +Tabify = function() + IsTabified.Value = true + IsMaximized.Value = false + IsMinimized.Value = true + UpdateMinimize() + IsTabified.Value = true + return ScreenGui:TweenPosition(UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) +end +local UnTabify +UnTabify = function() + if IsTabified.Value then + IsTabified.Value = false + return ScreenGui:TweenPosition(UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + end +end local BlowThisPopsicleStand BlowThisPopsicleStand = function() return Tabify() @@ -896,10 +928,25 @@ StatSort = function(a, b) end return a.Priority < b.Priority end +local BaseUpdate local StatChanged StatChanged = function(_, _) return BaseUpdate() end +local CreateStatTitle +CreateStatTitle = function(statName) + local _with_0 = MiddleTemplate:FindFirstChild("PlayerScore"):Clone() + _with_0.Name = statName + _with_0.Text = statName + if IsMaximized.Value then + _with_0.TextTransparency = 0 + else + _with_0.TextTransparency = 1 + end + _with_0.Parent = StatTitles + return _with_0 +end +local UpdateMaximize local StatAdded StatAdded = function(nchild, playerEntry) while AddingStatLock do @@ -907,7 +954,7 @@ StatAdded = function(nchild, playerEntry) wait(1 / 30) end AddingStatLock = true - if not (nchild:IsA("StringValue" or nchild:IsA("IntValue" or nchild:IsA("BoolValue" or nchild:IsA("NumberValue" or nchild:IsA("DoubleConstrainedValue" or nchild:IsA("IntConstrainedValue"))))))) then + if not (nchild:IsA("StringValue") or nchild:IsA("IntValue") or nchild:IsA("BoolValue") or nchild:IsA("NumberValue") or nchild:IsA("DoubleConstrainedValue") or nchild:IsA("IntConstrainedValue")) then BlowThisPopsicleStand() else local haveScore = false @@ -1041,17 +1088,36 @@ MakeScoreEntry = function(entry, scoreval, panel) end) return nscoretxt end -local CreateStatTitle -CreateStatTitle = function(statName) - local ntitle = MiddleTemplate:FindFirstChild("PlayerScore"):Clone() - ntitle.Name = statName - ntitle.Text = statName - if IsMaximized.Value then - ntitle.TextTransparency = 0 - else - ntitle.TextTransparency = 1 - end - ntitle.Parent = StatTitles +local FONT_SIZES = { + "Size8", + "Size9", + "Size10", + "Size11", + "Size12", + "Size14", + "Size24", + "Size36", + "Size48" +} +local UpdateHeaderNameSize +UpdateHeaderNameSize = function() + local tHeader = HeaderName:Clone() + tHeader.Position = UDim2.new(2, 0, 2, 0) + tHeader.Parent = ScreenGui + local fSize = 7 + tHeader.FontSize = FONT_SIZES[fSize] + return Delay(0.2, function() + while tHeader.TextBounds.x == 0 do + wait(1 / 30) + end + while tHeader.TextBounds.x - NormalBounds.X.Offset > 1 do + fSize = fSize - 1 + tHeader.FontSize = FONT_SIZES[fSize] + wait(0.2) + end + HeaderName.FontSize = tHeader.FontSize + return tHeader:Destroy() + end) end local RecreateScoreColumns RecreateScoreColumns = function(ptable) @@ -1068,7 +1134,6 @@ RecreateScoreColumns = function(ptable) MaxSizeColumn = 0 for _, entry in ipairs(ptable) do local panel = entry["Frame"] - local tplayer = entry["Player"] if not panel:FindFirstChild(scoreval["Name"]) then local nentry = MakeScoreEntry(entry, scoreval, panel) if nentry then @@ -1092,7 +1157,7 @@ RecreateScoreColumns = function(ptable) StatTitles[scoreval["Name"]]:TweenPosition(UDim2.new((0.4 + ((0.6 / #ScoreNames) * (j - 1))) - 1, 0, 0, 0), "Out", "Linear", BASE_TWEEN, true) end scoreval["ColumnSize"] = MaxSizeColumn - Xoffset = Xoffset + SpacingPerStat + MaxSizeColumn + Xoffset = Xoffset + (SpacingPerStat + MaxSizeColumn) maxXOffset = math.max(Xoffset, maxXOffset) end NormalBounds = UDim2.new(0, BaseScreenXSize + maxXOffset - SpacingPerStat, 0, 800) @@ -1101,6 +1166,44 @@ RecreateScoreColumns = function(ptable) UpdateMaximize() AddingStatLock = false end +local ExpandNames +ExpandNames = function() + if #ScoreNames ~= 0 then + for _, i in pairs(StatTitles:GetChildren()) do + Spawn(function() + return TweenProperty(i, "TextTransparency", i.TextTransparency, 0, BASE_TWEEN) + end) + end + HeaderFrameHeight = 0.09 + HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), HeaderFrame.Position, "Out", "Linear", BASE_TWEEN * 1.2, true) + TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + return BottomShiftFrame:TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + end +end +local CloseNames +CloseNames = function() + if #ScoreNames ~= 0 then + HeaderFrameHeight = 0.07 + if not IsMaximized.Value then + for _, i in pairs(StatTitles:GetChildren()) do + Spawn(function() + return TweenProperty(i, "TextTransparency", i.TextTransparency, 1, BASE_TWEEN) + end) + end + end + BottomShiftFrame:TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), HeaderFrame.Position, "Out", "Linear", BASE_TWEEN * 1.2, true) + return TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + end +end +local UpdateStatNames +UpdateStatNames = function() + if not AreNamesExpanded.Value or IsMinimized.Value then + return CloseNames() + else + return ExpandNames() + end +end local ToggleMinimize ToggleMinimize = function() IsMinimized.Value = not IsMinimized.Value @@ -1111,23 +1214,16 @@ ToggleMaximize = function() IsMaximized.Value = not IsMaximized.Value return RecreateScoreColumns(PlayerFrames) end -local Tabify -Tabify = function() - IsTabified.Value = true - IsMaximized.Value = false - IsMinimized.Value = true - UpdateMinimize() - IsTabified.Value = true - return ScreenGui:TweenPosition(UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) +local UpdateScrollPosition +UpdateScrollPosition = function() + local minPos = GetMinScroll() + local maxPos = GetMaxScroll() + local scrollLength = maxPos - minPos + local yscrollpos = math.max(math.min(ListFrame.Position.Y.Scale, maxPos), minPos) + ListFrame.Position = UDim2.new(ListFrame.Position.X.Scale, ListFrame.Position.X.Offset, yscrollpos, ListFrame.Position.Y.Offset) + local adjustedLength = 1 - ScrollBar.Size.Y.Scale + ScrollBar.Position = UDim2.new(0, 0, adjustedLength - (adjustedLength * ((ListFrame.Position.Y.Scale - minPos) / scrollLength)), 0) end -local UnTabify -UnTabify = function() - if IsTabified.Value then - IsTabified.Value = false - return ScreenGui:TweenPosition(UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - end -end -local UpdateMinimize UpdateMinimize = function() if IsMinimized.Value then if IsMaximized.Value then @@ -1146,7 +1242,7 @@ UpdateMinimize = function() if not IsMaximized.Value then MainFrame:TweenSizeAndPosition(NormalBounds, NormalPosition, "Out", "Linear", BASE_TWEEN * 1.2, true) end - DefaultBottomClipPos = math.min(math.max(DefaultBottomClipPos, -1), -1 + (#MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale)) + DefaultBottomClipPos = math.min(math.max(DefaultBottomClipPos, -1), -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale) UpdateScrollPosition() BottomClipFrame.Position = UDim2.new(0, 0, DefaultBottomClipPos, 0) local bottomPositon = DefaultBottomClipPos + BottomClipFrame.Size.Y.Scale @@ -1155,7 +1251,6 @@ UpdateMinimize = function() ExtendTab.Image = "http://www.roblox.com/asset/?id=94825585" end end -local UpdateMaximize UpdateMaximize = function() if IsMaximized.Value then for j = 1, #ScoreNames, 1 do @@ -1173,11 +1268,13 @@ UpdateMaximize = function() HeaderFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeHeader"] BottomFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeBottom"] for index, i in ipairs(MiddleFrameBackgrounds) do - if index % 2 ~= 1 then - i.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeDark"] - else - i.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeLight"] - end + i.Background.Image = "http://www.roblox.com/asset/?id=" .. (function() + if index % 2 ~= 1 then + return Images["LargeDark"] + else + return Images["LargeLight"] + end + end)() end for _, i in ipairs(MiddleFrames) do if i:FindFirstChild("ClickListener") then @@ -1232,44 +1329,6 @@ UpdateMaximize = function() end end end -local ExpandNames -ExpandNames = function() - if #ScoreNames ~= 0 then - for _, i in pairs(StatTitles:GetChildren()) do - Spawn(function() - return TweenProperty(i, "TextTransparency", i.TextTransparency, 0, BASE_TWEEN) - end) - end - HeaderFrameHeight = 0.09 - HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), HeaderFrame.Position, "Out", "Linear", BASE_TWEEN * 1.2, true) - TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - return BottomShiftFrame:TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - end -end -local CloseNames -CloseNames = function() - if #ScoreNames ~= 0 then - HeaderFrameHeight = 0.07 - if not IsMaximized.Value then - for _, i in pairs(StatTitles:GetChildren()) do - Spawn(function() - return TweenProperty(i, "TextTransparency", i.TextTransparency, 1, BASE_TWEEN) - end) - end - end - BottomShiftFrame:TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - HeaderFrame:TweenSizeAndPosition(UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), HeaderFrame.Position, "Out", "Linear", BASE_TWEEN * 1.2, true) - return TopClipFrame:TweenPosition(UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - end -end -local UpdateStatNames -UpdateStatNames = function() - if not AreNamesExpanded.Value or IsMinimized.Value then - return CloseNames() - else - return ExpandNames() - end -end local OnScrollWheelMove OnScrollWheelMove = function(direction) if not (IsTabified.Value or IsMinimized.Value or InPopupWaitForClick) then @@ -1309,6 +1368,76 @@ end) FocusFrame.MouseLeave:connect(function() return DetachScrollWheel() end) +local UpdateScrollBarSize +UpdateScrollBarSize = function() + local entryListSize = #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale + local shownAreaSize = BottomClipFrame.Position.Y.Scale + 1 + ScrollBar.Size = UDim2.new(1, 0, shownAreaSize / entryListSize, 0) +end +local InitMovingPanel +InitMovingPanel = function(entry, player) + PopUpClipFrame.Parent = ScreenGui + if PopUpPanel ~= nil then + PopUpPanel:Destroy() + end + PopUpPanel = PopUpPanelTemplate:Clone() + PopUpPanel.Parent = PopUpClipFrame + local nextIndex = 2 + local friendStatus = GetFriendStatus(player) + debugprint(tostring(friendStatus)) + local showRankMenu = IsPersonalServer and LocalPlayer.PersonalServerRank >= PrivilegeLevel["Admin"] and LocalPlayer.PersonalServerRank > SelectedPlayer.PersonalServerRank + local ReportPlayerButton = MakePopupButton(PopUpPanel, "Report Player", 0) + ReportPlayerButton.MouseButton1Click:connect(function() + return OpenAbuseDialog() + end) + local FriendPlayerButton = MakePopupButton(PopUpPanel, "Friend", 1, not showRankMenu and friendStatus ~= Enum.FriendStatus.FriendRequestReceived) + FriendPlayerButton.MouseButton1Click:connect(OnFriendButtonSelect) + if friendStatus == Enum.FriendStatus.Friend then + FriendPlayerButton:FindFirstChild("ButtonText").Text = "UnFriend Player" + elseif friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend then + FriendPlayerButton:FindFirstChild("ButtonText").Text = "Send Request" + elseif friendStatus == Enum.FriendStatus.FriendRequestSent then + FriendPlayerButton:FindFirstChild("ButtonText").Text = "Revoke Request" + elseif friendStatus == Enum.FriendStatus.FriendRequestReceived then + FriendPlayerButton:FindFirstChild("ButtonText").Text = "Accept Friend" + local FriendRefuseButton = MakePopupButton(PopUpPanel, "Decline Friend", 2, not showRankMenu) + FriendRefuseButton.MouseButton1Click:connect(OnFriendRefuseButtonSelect) + nextIndex = nextIndex + 1 + end + if showRankMenu then + local BanPlayerButton = MakePopupButton(PopUpPanel, "Ban", nextIndex) + local VisitorButton = MakePopupButton(PopUpPanel, "Visitor", nextIndex + 1) + local MemberButton = MakePopupButton(PopUpPanel, "Member", nextIndex + 2) + local AdminButton = MakePopupButton(PopUpPanel, "Admin", nextIndex + 3, true) + BanPlayerButton.MouseButton1Click:connect(function() + return OnPrivilegeLevelSelect(player, PrivilegeLevel["Banned"], BanPlayerButton, VisitorButton, MemberButton, AdminButton) + end) + VisitorButton.MouseButton1Click:connect(function() + return OnPrivilegeLevelSelect(player, PrivilegeLevel["Visitor"], BanPlayerButton, VisitorButton, MemberButton, AdminButton) + end) + MemberButton.MouseButton1Click:connect(function() + return OnPrivilegeLevelSelect(player, PrivilegeLevel["Member"], BanPlayerButton, VisitorButton, MemberButton, AdminButton) + end) + AdminButton.MouseButton1Click:connect(function() + return OnPrivilegeLevelSelect(player, PrivilegeLevel["Admin"], BanPlayerButton, VisitorButton, MemberButton, AdminButton) + end) + HighlightMyRank(SelectedPlayer, BanPlayerButton, VisitorButton, MemberButton, AdminButton) + end + PopUpPanel:TweenPosition(UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN, true) + Delay(0, function() + local tconnection = Mouse.Button1Down:connect(function() + tconnection:disconnect() + return ClosePopUpPanel() + end) + end) + local myFrame = entry["Frame"] + return Spawn(function() + while InPopupWaitForClick do + PopUpClipFrame.Position = UDim2.new(0, myFrame.AbsolutePosition.X - PopUpClipFrame.Size.X.Offset, 0, myFrame.AbsolutePosition.Y) + wait() + end + end) +end local UpdateScrollBarVisibility UpdateScrollBarVisibility = function() if AreAllEntriesOnScreen() then @@ -1318,21 +1447,14 @@ UpdateScrollBarVisibility = function() return UpdateScrollBarSize() end end -local UpdateScrollBarSize -UpdateScrollBarSize = function() - local entryListSize = #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale - local shownAreaSize = BottomClipFrame.Position.Y.Scale + 1 - ScrollBar.Size = UDim2.new(1, 0, shownAreaSize / entryListSize, 0) -end -local UpdateScrollPosition -UpdateScrollPosition = function() - local minPos = GetMinScroll() - local maxPos = GetMaxScroll() - local scrollLength = maxPos - minPos - local yscrollpos = math.max(math.min(ListFrame.Position.Y.Scale, maxPos), minPos) - ListFrame.Position = UDim2.new(ListFrame.Position.X.Scale, ListFrame.Position.X.Offset, yscrollpos, ListFrame.Position.Y.Offset) - local adjustedLength = 1 - ScrollBar.Size.Y.Scale - ScrollBar.Position = UDim2.new(0, 0, adjustedLength - (adjustedLength * ((ListFrame.Position.Y.Scale - minPos) / scrollLength)), 0) +local ActivatePlayerEntryPanel +ActivatePlayerEntryPanel = function(entry) + entry["Frame"].BackgroundColor3 = Color3.new(0, 1, 1) + Spawn(function() + return TweenProperty(entry["Frame"], "BackgroundTransparency", 1, 0.5, 0.5) + end) + InPopupWaitForClick = true + return InitMovingPanel(entry, entry["Player"]) end local StartDrag StartDrag = function(entry, startx, starty) @@ -1387,7 +1509,7 @@ StartMinimizeDrag = function() startY = AbsoluteToPercent(nx, ny).Y end local nowY = AbsoluteToPercent(nx, ny).Y - local newFrameY = math.min(math.max(StartFrame + (nowY - startY), -1), -1 + (#MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale)) + local newFrameY = math.min(math.max(StartFrame + (nowY - startY), -1), -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale) DefaultBottomClipPos = newFrameY UpdateMinimize() ScrollBarFrame.Size = UDim2.new(ScrollBarFrame.Size.X.Scale, 0, (DefaultBottomClipPos + BottomClipFrame.Size.Y.Scale), 0) @@ -1402,3 +1524,781 @@ StartMinimizeDrag = function() end) end) end +IsMaximized.Value = false +IsMinimized.Value = false +IsMaximized.Changed:connect(UpdateMaximize) +IsMinimized.Changed:connect(UpdateMinimize) +ExtendButton.MouseButton1Down:connect(function() + if (time() - LastClick < ButtonCooldown) or InPopupWaitForClick then + return + end + LastClick = time() + if IsTabified.Value then + return UnTabify() + else + return StartMinimizeDrag() + end +end) +MaximizeButton.MouseButton1Click:connect(function() + if (time() - LastClick < ButtonCooldown) or InPopupWaitForClick then + return + end + LastClick = time() + if IsTabified.Value then + return UnTabify() + elseif not AreNamesExpanded.Value then + AreNamesExpanded.Value = true + return BaseUpdate() + else + return ToggleMaximize() + end +end) +MaximizeButton.MouseButton2Click:connect(function() + if (time() - LastClick < ButtonCooldown) or InPopupWaitForClick then + return + end + LastClick = time() + if IsTabified.Value then + return UnTabify() + elseif IsMaximized.Value then + return ToggleMaximize() + elseif AreNamesExpanded.Value then + AreNamesExpanded.Value = false + return BaseUpdate() + else + return Tabify() + end +end) +local AddMiddleBGFrame +AddMiddleBGFrame = function() + local nBGFrame = MiddleBGTemplate:Clone() + nBGFrame.Position = UDim2.new(0.5, 0, (#MiddleFrameBackgrounds * nBGFrame.Size.Y.Scale), 0) + nBGFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. (function() + if (#MiddleFrameBackgrounds + 1) % 2 ~= 1 then + if IsMaximized.Value then + return Images["LargeDark"] + else + return Images["midDark"] + end + else + if IsMaximized.Value then + return Images["LargeLight"] + else + return Images["midLight"] + end + end + end)() + nBGFrame.Parent = ListFrame + table.insert(MiddleFrameBackgrounds, nBGFrame) + if #MiddleFrameBackgrounds < DefaultListSize and not DidMinimizeDrag then + DefaultBottomClipPos = -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale + end + if not IsMinimized.Value then + return UpdateMinimize() + end +end +local RemoveMiddleBGFrame +RemoveMiddleBGFrame = function() + MiddleFrameBackgrounds[#MiddleFrameBackgrounds]:Destroy() + table.remove(MiddleFrameBackgrounds, #MiddleFrameBackgrounds) + if not IsMinimized.Value then + return UpdateMinimize() + end +end +local ChangeHeaderName +ChangeHeaderName = function(nname) + HeaderName.Text = nname + return UpdateHeaderNameSize() +end +ScreenGui.Changed:connect(UpdateHeaderNameSize) +local LeaderstatsAdded +LeaderstatsAdded = function(playerEntry) + local nplayer = playerEntry["Player"] + for _, i in pairs(nplayer.leaderstats:GetChildren()) do + StatAdded(i, playerEntry) + end + nplayer.leaderstats.ChildAdded:connect(function(nchild) + return StatAdded(nchild, playerEntry) + end) + return nplayer.leaderstats.ChildRemoved:connect(function(nchild) + return StatRemoved(nchild, playerEntry) + end) +end +local LeaderstatsRemoved +LeaderstatsRemoved = function(_, playerEntry) + while AddingFrameLock do + debugprint("waiting to insert " .. tostring(playerEntry["Player"].Name)) + wait(1 / 30) + end + AddingFrameLock = true + RemoveAllStats(playerEntry) + AddingFrameLock = false +end +local OnPlayerEntrySelect +OnPlayerEntrySelect = function(entry, startx, starty) + if not InPopupWaitForClick then + SelectedPlayerEntry = entry + SelectedPlayer = entry["Player"] + return StartDrag(entry, startx, starty) + end +end +local PlayerListModeUpdate +PlayerListModeUpdate = function() + RecreateScoreColumns(PlayerFrames) + table.sort(PlayerFrames, PlayerSortFunction) + for i, val in ipairs(PlayerFrames) do + MiddleFrames[i] = val["Frame"] + end + for i = #PlayerFrames + 1, #MiddleFrames, 1 do + MiddleFrames[i] = nil + end + return UpdateMinimize() +end +local RemoveNeutralTeam +RemoveNeutralTeam = function() + while NeutralTeamLock do + debugprint("in neutral team lock") + wait() + end + NeutralTeamLock = true + NeutralTeam["Frame"]:Destroy() + NeutralTeam = nil + RemoveMiddleBGFrame() + NeutralTeamLock = false +end +local RemovePlayerFromTeam +RemovePlayerFromTeam = function(teamEntry, index) + table.remove(teamEntry["MyPlayers"], index) + if teamEntry == NeutralTeam and #teamEntry["MyPlayers"] == 0 then + return RemoveNeutralTeam() + end +end +local FindRemovePlayerFromTeam +FindRemovePlayerFromTeam = function(entry) + if entry["MyTeam"] then + for j, oldEntry in ipairs(entry["MyTeam"]["MyPlayers"]) do + if oldEntry["Player"] == entry["Player"] then + RemovePlayerFromTeam(entry["MyTeam"], j) + return + end + end + elseif NeutralTeam then + for j, oldEntry in ipairs(NeutralTeam["MyPlayers"]) do + if oldEntry["Player"] == entry["Player"] then + RemovePlayerFromTeam(NeutralTeam, j) + return + end + end + end +end +local AddPlayerToTeam +AddPlayerToTeam = function(teamEntry, entry) + FindRemovePlayerFromTeam(entry) + table.insert(teamEntry["MyPlayers"], entry) + entry["MyTeam"] = teamEntry + if teamEntry["IsHidden"] then + teamEntry["Frame"].Parent = ListFrame + AddMiddleBGFrame() + end + teamEntry["IsHidden"] = false +end +local AddNeutralTeam +AddNeutralTeam = function() + while NeutralTeamLock do + debugprint("in neutral team 2 lock") + wait() + end + NeutralTeamLock = true + local defaultTeam = Instance.new("Team") + defaultTeam.TeamColor = BrickColor.new("White") + defaultTeam.Name = "Neutral" + local nentry = { } + nentry["MyTeam"] = defaultTeam + nentry["MyPlayers"] = { } + nentry["Frame"] = MiddleTemplate:Clone() + WaitForChild(WaitForChild(nentry["Frame"], "TitleFrame"), "Title").Text = defaultTeam.Name + nentry["Frame"].TitleFrame.Position = UDim2.new(nentry["Frame"].TitleFrame.Position.X.Scale, nentry["Frame"].TitleFrame.Position.X.Offset, 0.1, 0) + nentry["Frame"].TitleFrame.Size = UDim2.new(nentry["Frame"].TitleFrame.Size.X.Scale, nentry["Frame"].TitleFrame.Size.X.Offset, 0.8, 0) + nentry["Frame"].TitleFrame.Title.Font = "ArialBold" + nentry["Frame"].Position = UDim2.new(1, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0) + WaitForChild(nentry["Frame"], "ClickListener").MouseButton1Down:connect(function(nx, ny) + return StartDrag(nentry, nx, ny) + end) + nentry["Frame"].ClickListener.BackgroundColor3 = Color3.new(1, 1, 1) + nentry["Frame"].ClickListener.BackgroundTransparency = 0.7 + nentry["Frame"].ClickListener.AutoButtonColor = false + nentry["AutoHide"] = true + nentry["IsHidden"] = true + for _, i in pairs(PlayerFrames) do + if i["Player"].Neutral or not i["MyTeam"] then + AddPlayerToTeam(nentry, i) + end + end + if #nentry["MyPlayers"] > 0 then + NeutralTeam = nentry + UpdateMinimize() + BaseUpdate() + end + NeutralTeamLock = false +end +local SetPlayerToTeam +SetPlayerToTeam = function(entry) + FindRemovePlayerFromTeam(entry) + local setToTeam = false + for _, tframe in ipairs(TeamFrames) do + if tframe["MyTeam"].TeamColor == entry["Player"].TeamColor then + AddPlayerToTeam(tframe, entry) + setToTeam = true + end + end + if not setToTeam and #(game.Teams:GetTeams()) > 0 then + debugprint(tostring(entry["Player"].Name) .. " could not find team") + entry["MyTeam"] = nil + if not NeutralTeam then + return AddNeutralTeam() + else + return AddPlayerToTeam(NeutralTeam, entry) + end + end +end +local PlayerChanged +PlayerChanged = function(entry, property) + while PlayerChangedLock do + debugprint("in playerchanged lock") + wait(1 / 30) + end + PlayerChangedLock = true + if property == "Neutral" then + if entry["Player"].Neutral and #game.Teams:GetTeams() > 0 then + debugprint(tostring(entry["Player"].Name) .. " setting to neutral") + FindRemovePlayerFromTeam(entry) + entry["MyTeam"] = nil + if not NeutralTeam then + debugprint(tostring(entry["Player"].Name) .. " creating neutral team") + AddNeutralTeam() + else + debugprint(tostring(entry["Player"].Name) .. " adding to neutral team") + AddPlayerToTeam(NeutralTeam, entry) + end + elseif #(game.Teams:GetTeams()) > 0 then + debugprint(tostring(entry["Player"].Name) .. " has been set non-neutral") + SetPlayerToTeam(entry) + end + BaseUpdate() + elseif property == "TeamColor" and not entry["Player"].Neutral and entry["Player"] ~= entry["MyTeam"] then + debugprint(tostring(entry["Player"].Name) .. " setting to new team") + SetPlayerToTeam(entry) + BaseUpdate() + elseif property == "Name" or property == "MembershipType" then + entry["Frame"]:FindFirstChild("BCLabel").Image = getMembershipTypeIcon(entry["Player"].MembershipType, entry["Player"].Name) + entry["Frame"].Name = entry["Player"].Name + entry["Frame"].TitleFrame.Title.Text = entry["Player"].Name + if entry["Frame"].BCLabel.Image ~= "" then + entry["Frame"].TitleFrame.Title.Position = UDim2.new(0.01, 30, 0.1, 0) + end + if entry["Player"] == LocalPlayer then + entry["Frame"].TitleFrame.DropShadow.Text = entry["Player"].Name + ChangeHeaderName(entry["Player"].Name) + end + BaseUpdate() + end + PlayerChangedLock = false +end +local InsertPlayerFrame +InsertPlayerFrame = function(nplayer) + while AddingFrameLock do + debugprint("waiting to insert " .. tostring(nplayer.Name)) + wait(1 / 30) + end + AddingFrameLock = true + local nFrame = MiddleTemplate:Clone() + WaitForChild(WaitForChild(nFrame, "TitleFrame"), "Title").Text = nplayer.Name + nFrame.Position = UDim2.new(1, 0, (#MiddleFrames * nFrame.Size.Y.Scale), 0) + local nfriendstatus = GetFriendStatus(nplayer) + nFrame:FindFirstChild("BCLabel").Image = getMembershipTypeIcon(nplayer.MembershipType, nplayer.Name) + nFrame:FindFirstChild("FriendLabel").Image = getFriendStatusIcon(nfriendstatus) + nFrame.Name = nplayer.Name + WaitForChild(WaitForChild(nFrame, "TitleFrame"), "Title").Text = nplayer.Name + nFrame.FriendLabel.Position = nFrame.FriendLabel.Position + UDim2.new(0, 17, 0, 0) + nFrame.TitleFrame.Title.Position = nFrame.TitleFrame.Title.Position + UDim2.new(0, 17, 0, 0) + if nFrame:FindFirstChild("FriendLabel").Image ~= "" then + nFrame.TitleFrame.Title.Position = nFrame.TitleFrame.Title.Position + UDim2.new(0, 17, 0, 0) + end + if nplayer.Name == LocalPlayer.Name then + nFrame.TitleFrame.Title.Font = "ArialBold" + nFrame.PlayerScore.Font = "ArialBold" + ChangeHeaderName(nplayer.Name) + do + local _with_0 = nFrame.TitleFrame.Title:Clone() + _with_0.TextColor3 = Color3.new(0, 0, 0) + _with_0.TextTransparency = 0 + _with_0.ZIndex = 2 + _with_0.Position = nFrame.TitleFrame.Title.Position + UDim2.new(0, 1, 0, 1) + _with_0.Name = "DropShadow" + _with_0.Parent = nFrame.TitleFrame + end + end + nFrame.TitleFrame.Title.Font = "ArialBold" + nFrame.Parent = ListFrame + nFrame:TweenPosition(UDim2.new(0.5, 0, (#MiddleFrames * nFrame.Size.Y.Scale), 0), "Out", "Linear", BASE_TWEEN, true) + UpdateMinimize() + local nentry = { } + nentry["Frame"] = nFrame + nentry["Player"] = nplayer + nentry["ID"] = AddId + AddId = AddId + 1 + table.insert(PlayerFrames, nentry) + if #TeamFrames ~= 0 then + if nplayer.Neutral then + nentry["MyTeam"] = nil + if not NeutralTeam then + AddNeutralTeam() + else + AddPlayerToTeam(NeutralTeam, nentry) + end + else + local addedToTeam = false + for _, tval in ipairs(TeamFrames) do + if tval["MyTeam"].TeamColor == nplayer.TeamColor then + AddPlayerToTeam(tval, nentry) + nentry["MyTeam"] = tval + addedToTeam = true + end + end + if not addedToTeam then + nentry["MyTeam"] = nil + if not NeutralTeam then + AddNeutralTeam() + else + AddPlayerToTeam(NeutralTeam, nentry) + end + nentry["MyTeam"] = NeutralTeam + end + end + end + if nplayer:FindFirstChild("leaderstats") then + LeaderstatsAdded(nentry) + end + nplayer.ChildAdded:connect(function(nchild) + if nchild.Name == "leaderstats" then + while AddingFrameLock do + debugprint("in adding leaderstats lock") + wait(1 / 30) + end + AddingFrameLock = true + LeaderstatsAdded(nentry) + AddingFrameLock = false + end + end) + nplayer.ChildRemoved:connect(function(nchild) + if nplayer == LocalPlayer and nchild.Name == "leaderstats" then + return LeaderstatsRemoved(nchild, nentry) + end + end) + nplayer.Changed:connect(function(prop) + return PlayerChanged(nentry, prop) + end) + local listener = WaitForChild(nFrame, "ClickListener") + listener.Active = true + listener.MouseButton1Down:connect(function(nx, ny) + return OnPlayerEntrySelect(nentry, nx, ny) + end) + AddMiddleBGFrame() + BaseUpdate() + AddingFrameLock = false +end +local RemovePlayerFrame +RemovePlayerFrame = function(tplayer) + while AddingFrameLock do + debugprint("in removing player frame lock") + wait(1 / 30) + end + AddingFrameLock = true + local tteam + for i, key in ipairs(PlayerFrames) do + if tplayer == key["Player"] then + if PopUpClipFrame.Parent == key["Frame"] then + PopUpClipFrame.Parent = nil + end + key["Frame"]:Destroy() + tteam = key["MyTeam"] + table.remove(PlayerFrames, i) + end + end + if tteam then + for j, tentry in ipairs(tteam["MyPlayers"]) do + if tentry["Player"] == tplayer then + RemovePlayerFromTeam(tteam, j) + end + end + end + RemoveMiddleBGFrame() + UpdateMinimize() + BaseUpdate() + AddingFrameLock = false +end +Players.ChildRemoved:connect(RemovePlayerFrame) +local UnrollTeams +UnrollTeams = function(tframes, outframes) + local numEntries = 0 + if NeutralTeam and not NeutralTeam["IsHidden"] then + for _, val in ipairs(NeutralTeam["MyPlayers"]) do + numEntries = numEntries + 1 + outframes[numEntries] = val["Frame"] + end + numEntries = numEntries + 1 + outframes[numEntries] = NeutralTeam["Frame"] + end + for _, val in ipairs(tframes) do + if not val["IsHidden"] then + for _, pval in ipairs(val.MyPlayers) do + numEntries = numEntries + 1 + outframes[numEntries] = pval["Frame"] + end + numEntries = numEntries + 1 + outframes[numEntries] = val["Frame"] + end + end + for i = numEntries + 1, #outframes, 1 do + outframes[i] = nil + end +end +local TeamSortFunc +TeamSortFunc = function(a, b) + if a["TeamScore"] == b["TeamScore"] then + return a["ID"] < b["ID"] + end + if not a["TeamScore"] then + return false + end + if not b["TeamScore"] then + return true + end + return a["TeamScore"] < b["TeamScore"] +end +local AddTeamScores +AddTeamScores = function(team) + for j = 1, #ScoreNames, 1 do + local i = ScoreNames[j] + local tscore = 0 + for _, j in ipairs(team["MyPlayers"]) do + local tval = j["Player"]:FindFirstChild("leaderstats" and j["Player"].leaderstats:FindFirstChild(i["Name"])) + if tval and not tval:IsA("StringValue") then + tscore = tscore + GetScoreValue((j["Player"].leaderstats)[i["Name"]]) + end + end + if team["Frame"]:FindFirstChild(i["Name"]) then + team["Frame"][i["Name"]].Text = tostring(tscore) + end + end + return UpdateMinimize() +end +local SortTeams +SortTeams = function(tentries) + for _, val in ipairs(tentries) do + table.sort(val["MyPlayers"], PlayerSortFunction) + AddTeamScores(val) + end + return table.sort(tentries, TeamSortFunc) +end +local TeamListModeUpdate +TeamListModeUpdate = function() + RecreateScoreColumns(PlayerFrames) + SortTeams(TeamFrames) + if NeutralTeam then + AddTeamScores(NeutralTeam) + end + return UnrollTeams(TeamFrames, MiddleFrames) +end +local OnFriendshipChanged +OnFriendshipChanged = function(player, friendStatus) + return Delay(0.5, function() + debugprint("friend status changed for: " .. tostring(player.Name) .. " " .. tostring(friendStatus) .. " vs " .. tostring(GetFriendStatus(player))) + for _, entry in ipairs(PlayerFrames) do + if entry["Player"] == player then + local nicon = getFriendStatusIcon(friendStatus) + if nicon == "" and entry["Frame"].FriendLabel.Image ~= "" then + entry["Frame"].TitleFrame.Title.Position = entry["Frame"].TitleFrame.Title.Position - UDim2.new(0, 17, 0, 0) + elseif nicon ~= "" and entry["Frame"].FriendLabel.Image == "" then + entry["Frame"].TitleFrame.Title.Position = entry["Frame"].TitleFrame.Title.Position + UDim2.new(0, 17, 0, 0) + debugprint("confirmed status: " .. tostring(player.Name)) + end + entry["Frame"].FriendLabel.Image = nicon + return + end + end + end) +end +LocalPlayer.FriendStatusChanged:connect(OnFriendshipChanged) +local TeamScoreChanged +TeamScoreChanged = function(entry, nscore) + WaitForChild(entry["Frame"], "PlayerScore").Text = tostring(nscore) + entry["TeamScore"] = nscore +end +local TeamChildAdded +TeamChildAdded = function(entry, nchild) + if nchild.Name == "AutoHide" then + entry["AutoHide"] = true + elseif nchild.Name == "TeamScore" then + WaitForChild(entry["Frame"], "PlayerScore").Text = tostring(nchild.Value) + entry["TeamScore"] = nchild.Value + return nchild.Changed:connect(function() + return TeamScoreChanged(entry, nchild.Value) + end) + end +end +local TeamChildRemoved +TeamChildRemoved = function(entry, nchild) + if nchild.Name == "AutoHide" then + entry["AutoHide"] = false + elseif nchild.Name == "TeamScore" then + WaitForChild(entry["Frame"], "PlayerScore").Text = "" + entry["TeamScore"] = nil + end +end +local RemoveTeamFrame +RemoveTeamFrame = function(nteam) + while AddingFrameLock do + debugprint("in removing team frame lock") + wait(1 / 30) + end + AddingFrameLock = true + local myEntry + for i, key in ipairs(TeamFrames) do + if nteam == key["MyTeam"] then + myEntry = key + key["Frame"]:Destroy() + table.remove(TeamFrames, i) + end + end + if #TeamFrames == 0 then + debugprint("removeteamframe, remove neutral") + if NeutralTeam then + RemoveNeutralTeam() + end + end + for i, key in ipairs(myEntry["MyPlayers"]) do + RemovePlayerFromTeam(myEntry, i) + PlayerChanged(key, "TeamColor") + end + RemoveMiddleBGFrame() + BaseUpdate() + AddingFrameLock = false +end +local TeamChanged +TeamChanged = function(entry, property) + if property == "Name" then + WaitForChild(WaitForChild(entry["Frame"], "TitleFrame"), "Title").Text = entry["MyTeam"].Name + elseif property == "TeamColor" then + entry["Frame"].ClickListener.BackgroundColor3 = entry["MyTeam"].TeamColor.Color + for _, i in pairs(TeamFrames) do + if i["MyTeam"].TeamColor == entry["MyTeam"] then + RemoveTeamFrame(entry["MyTeam"]) + end + end + entry["MyPlayers"] = { } + for _, i in pairs(PlayerFrames) do + SetPlayerToTeam(i) + end + return BaseUpdate() + end +end +local InsertTeamFrame +InsertTeamFrame = function(nteam) + while AddingFrameLock do + debugprint("in adding team frame lock") + wait(1 / 30) + end + AddingFrameLock = true + local nentry = { } + nentry["MyTeam"] = nteam + nentry["MyPlayers"] = { } + nentry["Frame"] = MiddleTemplate:Clone() + WaitForChild(WaitForChild(nentry["Frame"], "TitleFrame"), "Title").Text = nteam.Name + nentry["Frame"].TitleFrame.Title.Font = "ArialBold" + nentry["Frame"].TitleFrame.Title.FontSize = "Size18" + nentry["Frame"].TitleFrame.Position = UDim2.new(nentry["Frame"].TitleFrame.Position.X.Scale, nentry["Frame"].TitleFrame.Position.X.Offset, 0.1, 0) + nentry["Frame"].TitleFrame.Size = UDim2.new(nentry["Frame"].TitleFrame.Size.X.Scale, nentry["Frame"].TitleFrame.Size.X.Offset, 0.8, 0) + nentry["Frame"].Position = UDim2.new(1, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0) + WaitForChild(nentry["Frame"], "ClickListener").MouseButton1Down:connect(function(nx, ny) + return StartDrag(nentry, nx, ny) + end) + nentry["Frame"].ClickListener.BackgroundColor3 = nteam.TeamColor.Color + nentry["Frame"].ClickListener.BackgroundTransparency = 0.7 + nentry["Frame"].ClickListener.AutoButtonColor = false + AddId = AddId + 1 + nentry["ID"] = AddId + nentry["AutoHide"] = false + if nteam:FindFirstChild("AutoHide") then + nentry["AutoHide"] = true + end + if nteam:FindFirstChild("TeamScore") then + TeamChildAdded(nentry, nteam.TeamScore) + end + nteam.ChildAdded:connect(function(nchild) + return TeamChildAdded(nentry, nchild) + end) + nteam.ChildRemoved:connect(function(nchild) + return TeamChildRemoved(nentry, nchild) + end) + nteam.Changed:connect(function(prop) + return TeamChanged(nentry, prop) + end) + for _, i in pairs(PlayerFrames) do + if not i["Player"].Neutral and i["Player"].TeamColor == nteam.TeamColor then + AddPlayerToTeam(nentry, i) + end + end + nentry["IsHidden"] = false + if not nentry["AutoHide"] or #nentry["MyPlayers"] > 0 then + nentry["Frame"].Parent = ListFrame + nentry["Frame"]:TweenPosition(UDim2.new(0.5, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0), "Out", "Linear", BASE_TWEEN, true) + AddMiddleBGFrame() + else + nentry["IsHidden"] = true + nentry["Frame"].Parent = nil + end + table.insert(TeamFrames, nentry) + UpdateMinimize() + BaseUpdate() + if #TeamFrames == 1 and not NeutralTeam then + AddNeutralTeam() + end + AddingFrameLock = false +end +local TeamAdded +TeamAdded = function(nteam) + return InsertTeamFrame(nteam) +end +local TeamRemoved +TeamRemoved = function(nteam) + return RemoveTeamFrame(nteam) +end +BaseUpdate = function() + while BaseUpdateLock do + debugprint("in baseupdate lock") + wait(1 / 30) + end + BaseUpdateLock = true + UpdateStatNames() + if #TeamFrames == 0 and not NeutralTeam then + PlayerListModeUpdate() + else + TeamListModeUpdate() + end + for i, key in ipairs(MiddleFrames) do + if not (key.Parent ~= nil) then + key:TweenPosition(UDim2.new(0.5, 0, ((#MiddleFrames - i) * key.Size.Y.Scale), 0), "Out", "Linear", BASE_TWEEN, true) + end + end + if not IsMinimized.Value and #MiddleFrames > DefaultEntriesOnScreen then + UpdateScrollPosition() + end + UpdateMinimize() + UpdateScrollBarSize() + UpdateScrollPosition() + UpdateScrollBarVisibility() + BaseUpdateLock = false +end +game.GuiService:AddKey("\t") +local LastTabTime = time() +game.GuiService.KeyPressed:connect(function(key) + if key == "\t" then + debugprint("caught tab key") + local modalCheck, isModal + modalCheck, isModal = pcall(function() + return game.GuiService.IsModalDialog + end) + if modalCheck == false or (modalCheck and isModal == false) then + if time() - LastTabTime > 0.4 then + LastTabTime = time() + if IsTabified.Value then + if not IsMaximized.Value then + ScreenGui:TweenPosition(UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + IsMaximized.Value = true + else + ScreenGui:TweenPosition(UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) + IsMaximized.Value = false + IsMinimized.Value = true + end + else + return ToggleMaximize() + end + end + end + end +end) +local debugPlayerAdd +debugPlayerAdd = function(p) + return InsertPlayerFrame(p) +end +local PlayersChildAdded +PlayersChildAdded = function(tplayer) + if tplayer:IsA("Player") then + return Spawn(function() + return debugPlayerAdd(tplayer) + end) + else + return BlowThisPopsicleStand() + end +end +local coreGuiChanged +coreGuiChanged = function(coreGuiType, enabled) + if coreGuiType == Enum.CoreGuiType.All or coreGuiType == Enum.CoreGuiType.PlayerList then + MainFrame.Visible = enabled + end +end +local TeamsChildAdded +TeamsChildAdded = function(nteam) + if nteam:IsA("Team") then + return TeamAdded(nteam) + else + return BlowThisPopsicleStand() + end +end +local TeamsChildRemoved +TeamsChildRemoved = function(nteam) + if nteam:IsA("Team") then + return TeamRemoved(nteam) + else + return BlowThisPopsicleStand() + end +end +pcall(function() + coreGuiChanged(Enum.CoreGuiType.PlayerList, Game.StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType.PlayerList)) + return Game.StarterGui.CoreGuiChangedSignal:connect(coreGuiChanged) +end) +while not game:GetService("Teams") do + wait(1 / 30) + debugprint("Waiting For Teams") +end +for _, i in pairs(game.Teams:GetTeams()) do + TeamAdded(i) +end +for _, i in pairs(Players:GetPlayers()) do + Spawn(function() + return debugPlayerAdd(i) + end) +end +game.Teams.ChildAdded:connect(TeamsChildAdded) +game.Teams.ChildRemoved:connect(TeamsChildRemoved) +Players.ChildAdded:connect(PlayersChildAdded) +InitReportAbuse() +AreNamesExpanded.Value = true +BaseUpdate() +wait(2) +IsPersonalServer = not not game.Workspace:FindFirstChild("PSVariable") +if LocalPlayer.Name == "newplayerlistisbad" or LocalPlayer.Name == "imtotallyadmin" then + debugFrame.Parent = ScreenGui + return Spawn(function() + while true do + local str_players + str_players = "" + for _, i in pairs(game.Players:GetPlayers()) do + str_players = str_players .. " " .. tostring(i.Name) + end + debugplayers.Text = str_players + wait(0.5) + end + end) +end diff --git a/yue/48488235.yue b/yue/48488235.yue index 65cb40b..c4e67ea 100644 --- a/yue/48488235.yue +++ b/yue/48488235.yue @@ -191,7 +191,6 @@ MakeBackgroundGuiObj = (imgName) -> --[[ turns 255 integer color value to a color3 --]] Color3I = (r, g, b) -> Color3.new r / 255, g / 255, b / 255 - --[[ Gets correct icon for builder's club status to display by name @Args: @@ -229,8 +228,6 @@ getFriendStatusIcon = (friendStatus) -> else error "Unknown FriendStatus: #{friendStatus}" - - --[[ Utility function to create buttons for the popup menus @Args: @@ -252,7 +249,6 @@ MakePopupButton = (nparent, ntext, index, last) -> BackgroundTransparency: 1 Position: UDim2.new 0.07, 0, 0.07, 0 Size: UDim2.new 0.86, 0, 0.86, 0 - Parent: HeaderFrame Font: "ArialBold" Text: ntext FontSize: "Size14" @@ -279,6 +275,7 @@ MakePopupButton = (nparent, ntext, index, last) -> --[[ simple function to toggle the display of debug output --]] +local debugOutput DebugPrintEnabled = true debugprint = (str) -> --print(str) @@ -564,6 +561,7 @@ ReportAbuseFrame = New "Frame", "Settings" Active: true Parent: ReportAbuseShield +local AbuseSettingsFrame AbuseSettingsFrame = New "Frame", "ReportAbuseStyle" Size: UDim2.new 1, 0, 1, 0 Active: true @@ -749,13 +747,13 @@ NormalAbuseBox = New "Frame", "AbuseFeedbackBox" AutoButtonColor: true Image: "http://www.roblox.com/asset/?id=96507959" -BigButton = Instance.new "ImageButton" -BigButton.Size = UDim2.new 1, 0, 1, 0 -BigButton.BackgroundTransparency = 1 -BigButton.ZIndex = 8 -BigButton.Visible = false ---BigButton.Active=false -BigButton.Parent = ScreenGui +BigButton = New "ImageButton" + Size: UDim2.new 1, 0, 1, 0 + BackgroundTransparency: 1 + ZIndex: 8 + Visible: false + --Active: false + Parent: ScreenGui debugFrame = New "Frame", "debugframe" -- Position: UDim2.new 0, 0, 0, 0 @@ -983,7 +981,7 @@ WaitForClick = (frameParent, polledFunction, exitFunction) -> connection\disconnect! connection2?\disconnect! - --debugprint('mouse up!') + --debugprint 'mouse up!' connection2 = BigButton.MouseMoved\connect (nx, ny) -> polledFunction nx, ny @@ -1042,25 +1040,12 @@ SetPrivilegeRank = (player, nrank) -> while player.PersonalServerRank > nrank game\GetService"PersonalServerService"\Demote player - ---[[ - called when player selects new privilege level from popup menu - @Args: - player player to set privileges on - nlevel new privilege level for this player ---]] -OnPrivilegeLevelSelect = (player, nlevel, BanPlayerButton, VisitorButton, MemberButton, AdminButton) -> - debugprint "setting privilege level" - SetPrivilegeRank player, nlevel - HighlightMyRank player, BanPlayerButton, VisitorButton, MemberButton, AdminButton - +assetid = "http://www.roblox.com/asset/?id=" --[[ Highlights current rank of this player in the popup menu @Args: player Player to check for rank on --]] - -assetid = "http://www.roblox.com/asset/?id=" HighlightMyRank = (player, BanPlayerButton, VisitorButton, MemberButton, AdminButton) -> BanPlayerButton.Image = assetid .. Images["LightPopupMid"] VisitorButton.Image = assetid .. Images["DarkPopupMid"] @@ -1077,9 +1062,33 @@ HighlightMyRank = (player, BanPlayerButton, VisitorButton, MemberButton, AdminBu elseif rank <= PrivilegeLevel["Admin"] AdminButton.Image = assetid .. Images["DarkBluePopupBottom"] +--[[ + called when player selects new privilege level from popup menu + @Args: + player player to set privileges on + nlevel new privilege level for this player +--]] +OnPrivilegeLevelSelect = (player, nlevel, BanPlayerButton, VisitorButton, MemberButton, AdminButton) -> + debugprint "setting privilege level" + SetPrivilegeRank player, nlevel + HighlightMyRank player, BanPlayerButton, VisitorButton, MemberButton, AdminButton + -------------------------- -- Report abuse handling -------------------------- +--[[ + resets and closes abuse dialog +--]] +CloseAbuseDialog = -> + AbuseName = nil + SubmitReportButton.Active = false + SubmitReportButton.Image = "http://www.roblox.com/asset/?id=96502438" -- 96501119' + AbuseDescriptionBox\Destroy! + CalmingAbuseBox.Parent = nil + NormalAbuseBox.Parent = nil + ReportAbuseShield.Parent = nil + AbuseSettingsFrame.Visible = true + --[[ does final reporting of abuse on selected player, calls closeAbuseDialog --]] @@ -1087,7 +1096,7 @@ OnSubmitAbuse = -> if SubmitReportButton.Active if AbuseName and SelectedPlayer AbuseSettingsFrame.Visible = false - game.Players\ReportAbuse(SelectedPlayer, AbuseName, AbuseDescriptionBox.Text) + game.Players\ReportAbuse SelectedPlayer, AbuseName, AbuseDescriptionBox.Text if AbuseName == "Rude or Mean Behavior" or AbuseName == "False Reporting Me" CalmingAbuseBox.Parent = ReportAbuseShield else @@ -1096,6 +1105,17 @@ OnSubmitAbuse = -> else CloseAbuseDialog! +ClosePopUpPanel = -> + if SelectedPlayerEntry + tframe = SelectedPlayerEntry["Frame"] + Spawn -> + TweenProperty tframe, "BackgroundTransparency", 0.5, 1, BASE_TWEEN + + PopUpPanel\TweenPosition UDim2.new(1, 0, 0, 0), "Out", "Linear", BASE_TWEEN, true + wait 0.1 + InPopupWaitForClick = false + SelectedPlayerEntry = nil + --[[ opens the abuse dialog, initialises text to display selectedplayer --]] @@ -1109,19 +1129,6 @@ OpenAbuseDialog = -> ReportAbuseShield.Parent = ScreenGui ClosePopUpPanel! ---[[ - resets and closes abuse dialog ---]] -CloseAbuseDialog = -> - AbuseName = nil - SubmitReportButton.Active = false - SubmitReportButton.Image = "http://www.roblox.com/asset/?id=96502438" -- 96501119' - AbuseDescriptionBox\Destroy! - CalmingAbuseBox.Parent = nil - NormalAbuseBox.Parent = nil - ReportAbuseShield.Parent = nil - AbuseSettingsFrame.Visible = true - --[[ creates dropdownbox, registers all listeners for abuse dialog @@ -1214,6 +1221,26 @@ PlayerSortFunction = (a, b) -> --------------------------------- -- Stat Handling --------------------------------- +local UpdateMinimize +Tabify = -> + IsTabified.Value = true + IsMaximized.Value = false + IsMinimized.Value = true + UpdateMinimize! + IsTabified.Value = true + ScreenGui\TweenPosition( + UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + +UnTabify = -> + if IsTabified.Value + IsTabified.Value = false + ScreenGui\TweenPosition UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true + -- removes and closes all leaderboard stuffs BlowThisPopsicleStand = -> --ScreenGui\Destroy! @@ -1235,6 +1262,7 @@ StatSort = (a, b) -> a.Priority < b.Priority +local BaseUpdate --[[ doing WAAY too much here, for optimization update only your team @Args: @@ -1248,12 +1276,25 @@ StatChanged = (_, _) -> --playerEntry, property) BaseUpdate! -- end +CreateStatTitle = (statName) -> + with MiddleTemplate\FindFirstChild"PlayerScore"\Clone! + .Name = statName + .Text = statName + -- ntitle + .TextTransparency = if IsMaximized.Value + 0 + else + 1 + + .Parent = StatTitles + +local UpdateMaximize --[[ Called when stat is added if playerEntry is localplayer, will add to score names and re-sort the stats, and resize the width of the leaderboard for all players, will add a listener for if this stat changes if stat is a string value, crashes the leaderboard - Note:change crash to a 'tabify' leaderboard later + Note: change crash to a 'tabify' leaderboard later @Args: nchild new child value to leaderstats playerEntry entry this stat was added to @@ -1266,12 +1307,13 @@ StatAdded = (nchild, playerEntry) -> AddingStatLock = true if not ( - nchild\IsA "StringValue" or - nchild\IsA "IntValue" or - nchild\IsA "BoolValue" or - nchild\IsA "NumberValue" or - nchild\IsA "DoubleConstrainedValue" or - nchild\IsA "IntConstrainedValue") + nchild\IsA"StringValue" or + nchild\IsA"IntValue" or + nchild\IsA"BoolValue" or + nchild\IsA"NumberValue" or + nchild\IsA"DoubleConstrainedValue" or + nchild\IsA"IntConstrainedValue" + ) BlowThisPopsicleStand! else @@ -1293,7 +1335,7 @@ StatAdded = (nchild, playerEntry) -> nstat["IsPrimary"] = true nstat.AddId = AddId - AddId = AddId + 1 + AddId += 1 table.insert ScoreNames, nstat table.sort ScoreNames, StatSort if not StatTitles\FindFirstChild nstat["Name"] @@ -1361,7 +1403,7 @@ RemoveAllStats = (playerEntry) -> StatRemoved val, playerEntry GetScoreValue = (score) -> - return if score\IsA"DoubleConstrainedValue" or score\IsA "IntConstrainedValue" + if score\IsA"DoubleConstrainedValue" or score\IsA "IntConstrainedValue" score.ConstrainedValue elseif score\IsA "BoolValue" if score.Value @@ -1413,19 +1455,29 @@ MakeScoreEntry = (entry, scoreval, panel) -> nscoretxt +FONT_SIZES = { "Size8", "Size9", "Size10", "Size11", "Size12", "Size14", "Size24", "Size36", "Size48" } +--[[ + Will fit the player's name to the bounds of the header + called on resize of the window and playedr name change events + HACK: cannot use 'Textscaled' due to unable to find text bounds when scaled +--]] +UpdateHeaderNameSize = -> + tHeader = HeaderName\Clone! + tHeader.Position = UDim2.new 2, 0, 2, 0 + tHeader.Parent = ScreenGui + fSize = 7 --Size24 in table + tHeader.FontSize = FONT_SIZES[fSize] + Delay 0.2, -> + while tHeader.TextBounds.x == 0 + wait 1 / 30 -CreateStatTitle = (statName) -> - ntitle = MiddleTemplate\FindFirstChild"PlayerScore"\Clone! - ntitle.Name = statName - ntitle.Text = statName - -- ntitle - ntitle.TextTransparency = if IsMaximized.Value - 0 - else - 1 - - ntitle.Parent = StatTitles + while tHeader.TextBounds.x - NormalBounds.X.Offset > 1 + fSize -= 1 + tHeader.FontSize = FONT_SIZES[fSize] + wait 0.2 + HeaderName.FontSize = tHeader.FontSize + tHeader\Destroy! RecreateScoreColumns = (ptable) -> while AddingStatLock @@ -1445,11 +1497,11 @@ RecreateScoreColumns = (ptable) -> -- for each entry in this player table for _, entry in ipairs ptable panel = entry["Frame"] - tplayer = entry["Player"] + -- tplayer = entry["Player"] -- if this panel does not have an element named after this stat if not panel\FindFirstChild scoreval["Name"] -- make an entry for this object - nentry = MakeScoreEntry(entry, scoreval, panel) + nentry = MakeScoreEntry entry, scoreval, panel if nentry debugprint "adding #{nentry.Name} to #{entry["Player"].Name}" nentry.Parent = panel @@ -1488,7 +1540,7 @@ RecreateScoreColumns = (ptable) -> ) scoreval["ColumnSize"] = MaxSizeColumn - Xoffset = Xoffset + SpacingPerStat + MaxSizeColumn + Xoffset += SpacingPerStat + MaxSizeColumn maxXOffset = math.max Xoffset, maxXOffset NormalBounds = UDim2.new 0, BaseScreenXSize + maxXOffset - SpacingPerStat, 0, 800 @@ -1501,36 +1553,90 @@ RecreateScoreColumns = (ptable) -> --------------------------- -- Minimizing and maximizing --------------------------- +ExpandNames = -> + if #ScoreNames ~= 0 + for _, i in pairs StatTitles\GetChildren! + Spawn -> + TweenProperty i, "TextTransparency", i.TextTransparency, 0, BASE_TWEEN + + + HeaderFrameHeight = 0.09 + --as of writing, this and 'CloseNames' are the only places headerframe is resized + HeaderFrame\TweenSizeAndPosition( + UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), + HeaderFrame.Position, + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + TopClipFrame\TweenPosition( + UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + BottomShiftFrame\TweenPosition UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true + + + +CloseNames = -> + if #ScoreNames ~= 0 + HeaderFrameHeight = 0.07 + if not IsMaximized.Value + for _, i in pairs StatTitles\GetChildren! + Spawn -> + TweenProperty i, "TextTransparency", i.TextTransparency, 1, BASE_TWEEN + + + + BottomShiftFrame\TweenPosition UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true + HeaderFrame\TweenSizeAndPosition( + UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), + HeaderFrame.Position, + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + TopClipFrame\TweenPosition( + UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + +UpdateStatNames = -> + if not AreNamesExpanded.Value or IsMinimized.Value + CloseNames! + else + ExpandNames! ToggleMinimize = -> IsMinimized.Value = not IsMinimized.Value UpdateStatNames! - ToggleMaximize = -> IsMaximized.Value = not IsMaximized.Value RecreateScoreColumns PlayerFrames --done to re-position stat names NOTE: optimize-able +--[[ + updates position of listframe so that no gaps at the bottom or top of the list are visible + updates position of scrollbar to match what parts of the list are visible +--]] +UpdateScrollPosition = -> + minPos = GetMinScroll! + maxPos = GetMaxScroll! + scrollLength = maxPos - minPos -Tabify = -> - IsTabified.Value = true - IsMaximized.Value = false - IsMinimized.Value = true - UpdateMinimize! - IsTabified.Value = true - ScreenGui\TweenPosition( - UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), - "Out", - "Linear", - BASE_TWEEN * 1.2, - true - ) + yscrollpos = math.max math.min(ListFrame.Position.Y.Scale, maxPos), minPos + ListFrame.Position = UDim2.new ListFrame.Position.X.Scale, ListFrame.Position.X.Offset, yscrollpos, ListFrame.Position.Y.Offset + adjustedLength = 1 - ScrollBar.Size.Y.Scale + ScrollBar.Position = UDim2.new 0, 0, adjustedLength - (adjustedLength * ((ListFrame.Position.Y.Scale - minPos) / scrollLength)), 0 -UnTabify = -> - if IsTabified.Value - IsTabified.Value = false - ScreenGui\TweenPosition UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true --[[ Does more than it looks like @@ -1564,7 +1670,7 @@ UpdateMinimize = -> MainFrame\TweenSizeAndPosition NormalBounds, NormalPosition, "Out", "Linear", BASE_TWEEN * 1.2, true --do limiting - DefaultBottomClipPos = math.min math.max(DefaultBottomClipPos, -1), -1 + (#MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale) + DefaultBottomClipPos = math.min math.max(DefaultBottomClipPos, -1), -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale UpdateScrollPosition! BottomClipFrame.Position = UDim2.new 0, 0, DefaultBottomClipPos, 0 @@ -1574,7 +1680,6 @@ UpdateMinimize = -> ExtendTab.Image = "http://www.roblox.com/asset/?id=94825585" - --[[ Manages the position/size of the mainFrame, swaps out different resolution images for the frame fades in and out the stat names, moves position of headername and header score @@ -1591,14 +1696,13 @@ UpdateMaximize = -> true ) - if IsMinimized.Value ToggleMinimize! else UpdateMinimize! - MainFrame\TweenSizeAndPosition(MaximizedBounds, MaximizedPosition, "Out", "Linear", BASE_TWEEN * 1.2, true) + MainFrame\TweenSizeAndPosition MaximizedBounds, MaximizedPosition, "Out", "Linear", BASE_TWEEN * 1.2, true HeaderScore\TweenPosition( UDim2.new(0, 0, HeaderName.Position.Y.Scale, 0), "Out", @@ -1616,10 +1720,10 @@ UpdateMaximize = -> HeaderFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeHeader"] BottomFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeBottom"] for index, i in ipairs MiddleFrameBackgrounds - if index % 2 ~= 1 - i.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeDark"] + i.Background.Image = "http://www.roblox.com/asset/?id=" .. if index % 2 ~= 1 + Images["LargeDark"] else - i.Background.Image = "http://www.roblox.com/asset/?id=" .. Images["LargeLight"] + Images["LargeLight"] for _, i in ipairs MiddleFrames @@ -1709,69 +1813,6 @@ UpdateMaximize = -> 0 ) -ExpandNames = -> - if #ScoreNames ~= 0 - for _, i in pairs StatTitles\GetChildren! - Spawn -> - TweenProperty i, "TextTransparency", i.TextTransparency, 0, BASE_TWEEN - - - HeaderFrameHeight = 0.09 - --as of writing, this and 'CloseNames' are the only places headerframe is resized - HeaderFrame\TweenSizeAndPosition( - UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), - HeaderFrame.Position, - "Out", - "Linear", - BASE_TWEEN * 1.2, - true - ) - TopClipFrame\TweenPosition( - UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), - "Out", - "Linear", - BASE_TWEEN * 1.2, - true - ) - BottomShiftFrame\TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - - - -CloseNames = -> - if #ScoreNames ~= 0 - HeaderFrameHeight = 0.07 - if not IsMaximized.Value - for _, i in pairs StatTitles\GetChildren! - Spawn -> - TweenProperty i, "TextTransparency", i.TextTransparency, 1, BASE_TWEEN - - - - BottomShiftFrame\TweenPosition(UDim2.new(0, 0, HeaderFrameHeight, 0), "Out", "Linear", BASE_TWEEN * 1.2, true) - HeaderFrame\TweenSizeAndPosition( - UDim2.new(HeaderFrame.Size.X.Scale, HeaderFrame.Size.X.Offset, HeaderFrameHeight, 0), - HeaderFrame.Position, - "Out", - "Linear", - BASE_TWEEN * 1.2, - true - ) - TopClipFrame\TweenPosition( - UDim2.new(TopClipFrame.Position.X.Scale, 0, HeaderFrameHeight, 0), - "Out", - "Linear", - BASE_TWEEN * 1.2, - true - ) - - -UpdateStatNames = -> - if not AreNamesExpanded.Value or IsMinimized.Value - CloseNames! - else - ExpandNames! - - OnScrollWheelMove = (direction) -> if not (IsTabified.Value or IsMinimized.Value or InPopupWaitForClick) StartFrame = ListFrame.Position @@ -1816,6 +1857,123 @@ FocusFrame.MouseLeave\connect -> ------------------------ -- Scroll Bar functions ------------------------ +--[[ + updates size of scrollbar depending on how many entries exist +--]] +UpdateScrollBarSize = -> + entryListSize = #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale + shownAreaSize = BottomClipFrame.Position.Y.Scale + 1 + ScrollBar.Size = UDim2.new 1, 0, shownAreaSize / entryListSize, 0 + +--[[ + prepares the needed popup to be tweened on screen, and updates the position of the popup clip + frame to match the selected player frame's position +--]] +InitMovingPanel = (entry, player) -> + PopUpClipFrame.Parent = ScreenGui + + PopUpPanel?\Destroy! + + PopUpPanel = PopUpPanelTemplate\Clone! + PopUpPanel.Parent = PopUpClipFrame + + nextIndex = 2 + friendStatus = GetFriendStatus player + debugprint "#{friendStatus}" + showRankMenu = IsPersonalServer and + LocalPlayer.PersonalServerRank >= PrivilegeLevel["Admin"] and + LocalPlayer.PersonalServerRank > SelectedPlayer.PersonalServerRank + + ReportPlayerButton = MakePopupButton PopUpPanel, "Report Player", 0 + ReportPlayerButton.MouseButton1Click\connect -> + OpenAbuseDialog! + + FriendPlayerButton = MakePopupButton( + PopUpPanel, + "Friend", + 1, + not showRankMenu and friendStatus ~= Enum.FriendStatus.FriendRequestReceived + ) + FriendPlayerButton.MouseButton1Click\connect OnFriendButtonSelect + + if friendStatus == Enum.FriendStatus.Friend + FriendPlayerButton\FindFirstChild"ButtonText".Text = "UnFriend Player" + elseif friendStatus == Enum.FriendStatus.Unknown or friendStatus == Enum.FriendStatus.NotFriend + FriendPlayerButton\FindFirstChild"ButtonText".Text = "Send Request" + elseif friendStatus == Enum.FriendStatus.FriendRequestSent + FriendPlayerButton\FindFirstChild"ButtonText".Text = "Revoke Request" + elseif friendStatus == Enum.FriendStatus.FriendRequestReceived + FriendPlayerButton\FindFirstChild"ButtonText".Text = "Accept Friend" + FriendRefuseButton = MakePopupButton PopUpPanel, "Decline Friend", 2, not showRankMenu + FriendRefuseButton.MouseButton1Click\connect OnFriendRefuseButtonSelect + nextIndex += 1 + + if showRankMenu + BanPlayerButton = MakePopupButton PopUpPanel, "Ban", nextIndex + VisitorButton = MakePopupButton PopUpPanel, "Visitor", nextIndex + 1 + MemberButton = MakePopupButton PopUpPanel, "Member", nextIndex + 2 + AdminButton = MakePopupButton PopUpPanel, "Admin", nextIndex + 3, true + + BanPlayerButton.MouseButton1Click\connect -> + OnPrivilegeLevelSelect( + player, + PrivilegeLevel["Banned"], + BanPlayerButton, + VisitorButton, + MemberButton, + AdminButton + ) + + VisitorButton.MouseButton1Click\connect -> + OnPrivilegeLevelSelect( + player, + PrivilegeLevel["Visitor"], + BanPlayerButton, + VisitorButton, + MemberButton, + AdminButton + ) + + MemberButton.MouseButton1Click\connect -> + OnPrivilegeLevelSelect( + player, + PrivilegeLevel["Member"], + BanPlayerButton, + VisitorButton, + MemberButton, + AdminButton + ) + + AdminButton.MouseButton1Click\connect -> + OnPrivilegeLevelSelect( + player, + PrivilegeLevel["Admin"], + BanPlayerButton, + VisitorButton, + MemberButton, + AdminButton + ) + + HighlightMyRank SelectedPlayer, BanPlayerButton, VisitorButton, MemberButton, AdminButton + + PopUpPanel\TweenPosition UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN, true + Delay 0, -> + tconnection = Mouse.Button1Down\connect -> + tconnection\disconnect! + ClosePopUpPanel! + + myFrame = entry["Frame"] + -- THIS IS GARBAGE. + -- if I parent to frame to auto update position, it gets clipped + -- sometimes garbage is the only option. + Spawn -> + while InPopupWaitForClick + PopUpClipFrame.Position = UDim2.new( + 0, myFrame.AbsolutePosition.X - PopUpClipFrame.Size.X.Offset, + 0, myFrame.AbsolutePosition.Y + ) + wait! + --[[ updates whether the scroll bar should be showing, if it is showing, updates the size of it @@ -1827,40 +1985,23 @@ UpdateScrollBarVisibility = -> ScrollBar.BackgroundTransparency = 0 UpdateScrollBarSize! +ActivatePlayerEntryPanel = (entry) -> + entry["Frame"].BackgroundColor3 = Color3.new 0, 1, 1 + Spawn -> + TweenProperty entry["Frame"], "BackgroundTransparency", 1, 0.5, 0.5 + + InPopupWaitForClick = true + InitMovingPanel entry, entry["Player"] --[[ - updates size of scrollbar depending on how many entries exist ---]] -UpdateScrollBarSize = -> - entryListSize = #MiddleFrameBackgrounds * MiddleTemplate.Size.Y.Scale - shownAreaSize = BottomClipFrame.Position.Y.Scale + 1 - ScrollBar.Size = UDim2.new 1, 0, shownAreaSize / entryListSize, 0 - ---[[ - updates position of listframe so that no gaps at the bottom or top of the list are visible - updates position of scrollbar to match what parts of the list are visible ---]] -UpdateScrollPosition = -> - minPos = GetMinScroll! - maxPos = GetMaxScroll! - scrollLength = maxPos - minPos - - yscrollpos = math.max math.min(ListFrame.Position.Y.Scale, maxPos), minPos - ListFrame.Position = UDim2.new ListFrame.Position.X.Scale, ListFrame.Position.X.Offset, yscrollpos, ListFrame.Position.Y.Offset - - adjustedLength = 1 - ScrollBar.Size.Y.Scale - ScrollBar.Position = UDim2.new 0, 0, adjustedLength - (adjustedLength * ((ListFrame.Position.Y.Scale - minPos) / scrollLength)), 0 - - ---[[ - WARNING:this is in a working state, but uses massive hacks + WARNING: this is in a working state, but uses massive hacks revize when global input is available Manages scrolling of the playerlist on mouse drag --]] StartDrag = (entry, startx, starty) -> openPanel = true --[[draggedFrame = ]] - WaitForChild(entry["Frame"], "ClickListener") + WaitForChild entry["Frame"], "ClickListener" dragExit = -> -- stopDrag = true @@ -1918,19 +2059,945 @@ StartMinimizeDrag = -> nowY = AbsoluteToPercent(nx, ny).Y newFrameY = math.min( math.max(StartFrame + (nowY - startY), -1), - -1 + (#MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale) + -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale ) DefaultBottomClipPos = newFrameY UpdateMinimize! - ScrollBarFrame.Size = UDim2.new(ScrollBarFrame.Size.X.Scale, 0, (DefaultBottomClipPos + BottomClipFrame.Size.Y.Scale), 0) - ScrollBarFrame.Position = UDim2.new(ScrollBarFrame.Position.X.Scale, 0, 1 - ScrollBarFrame.Size.Y.Scale, 0) + ScrollBarFrame.Size = UDim2.new ScrollBarFrame.Size.X.Scale, 0, (DefaultBottomClipPos + BottomClipFrame.Size.Y.Scale), 0 + ScrollBarFrame.Position = UDim2.new ScrollBarFrame.Position.X.Scale, 0, 1 - ScrollBarFrame.Size.Y.Scale, 0 UpdateScrollBarSize! UpdateScrollPosition! UpdateScrollBarVisibility! - Spawn -> WaitForClick ScreenGui, dragpoll, dragExit +------------------------------- +-- Input Callback functions +------------------------------- +IsMaximized.Value = false +IsMinimized.Value = false +IsMaximized.Changed\connect UpdateMaximize +IsMinimized.Changed\connect UpdateMinimize + +ExtendButton.MouseButton1Down\connect -> + return if (time! - LastClick < ButtonCooldown) or InPopupWaitForClick + + LastClick = time! + if IsTabified.Value + UnTabify! + else + StartMinimizeDrag! +MaximizeButton.MouseButton1Click\connect -> + return if (time! - LastClick < ButtonCooldown) or InPopupWaitForClick + + LastClick = time! + if IsTabified.Value + UnTabify! + elseif not AreNamesExpanded.Value + AreNamesExpanded.Value = true + BaseUpdate! + else + ToggleMaximize! + + +MaximizeButton.MouseButton2Click\connect -> + return if (time! - LastClick < ButtonCooldown) or InPopupWaitForClick + + LastClick = time! + if IsTabified.Value + UnTabify! + elseif IsMaximized.Value + ToggleMaximize! + elseif AreNamesExpanded.Value + AreNamesExpanded.Value = false + BaseUpdate! + else + Tabify! + +------------------------------- +-- MiddleFrames management +------------------------------- +--[[ + adds a background frame to the listframe +--]] +AddMiddleBGFrame = -> + nBGFrame = MiddleBGTemplate\Clone! + nBGFrame.Position = UDim2.new 0.5, 0, (#MiddleFrameBackgrounds * nBGFrame.Size.Y.Scale), 0 + nBGFrame.Background.Image = "http://www.roblox.com/asset/?id=" .. if (#MiddleFrameBackgrounds + 1) % 2 ~= 1 + if IsMaximized.Value + Images["LargeDark"] + else + Images["midDark"] + + else + if IsMaximized.Value + Images["LargeLight"] + else + Images["midLight"] + + + nBGFrame.Parent = ListFrame + table.insert MiddleFrameBackgrounds, nBGFrame + + if #MiddleFrameBackgrounds < DefaultListSize and not DidMinimizeDrag + --print('readjusting bottom clip') + DefaultBottomClipPos = -1 + #MiddleFrameBackgrounds * MiddleBGTemplate.Size.Y.Scale + + + if not IsMinimized.Value + UpdateMinimize! + + +--[[ + removes a background from from the listframe +--]] +RemoveMiddleBGFrame = -> + MiddleFrameBackgrounds[#MiddleFrameBackgrounds]\Destroy! + table.remove MiddleFrameBackgrounds, #MiddleFrameBackgrounds + if not IsMinimized.Value + UpdateMinimize! + + +------------------------------- +-- Player Callback functions +------------------------------- +--[[ + note: should probably set to something other than mainFrame.AbsoluteSize, should work for now + if textbounds ever works on textscaled, switch to that :( +--]] +ChangeHeaderName = (nname) -> + HeaderName.Text = nname + UpdateHeaderNameSize! + +ScreenGui.Changed\connect UpdateHeaderNameSize + +--[[ + called only when the leaderstats object is added to a given player entry + removes old stats, adds any existing stats, and sets up listeners for new stats + @Args: + playerEntry A reference to the ENTRY(table) of the player who had leaderstats added +--]] +LeaderstatsAdded = (playerEntry) -> + --RemoveAllStats(playerEntry) + nplayer = playerEntry["Player"] + for _, i in pairs nplayer.leaderstats\GetChildren! + StatAdded i, playerEntry + + nplayer.leaderstats.ChildAdded\connect (nchild) -> + StatAdded nchild, playerEntry + + nplayer.leaderstats.ChildRemoved\connect (nchild) -> + StatRemoved nchild, playerEntry + + +--[[ + called when leaderstats object is removed from play in player entry + Note: may not be needed, might be able to just rely on leaderstats added + @Args: + oldLeaderstats leaderstats object to be removed + playerEntry A reference to the ENTRY(table) of the player +--]] +LeaderstatsRemoved = (_, playerEntry) -> + while AddingFrameLock + debugprint "waiting to insert #{playerEntry["Player"].Name}" + wait 1 / 30 + + AddingFrameLock = true + RemoveAllStats playerEntry + AddingFrameLock = false + +--[[ + Called when a player entry in the leaderboard is clicked + either will highlight entry and start the drag event, or open a popup menu + @Args: + entry the player entry clicked +--]] +OnPlayerEntrySelect = (entry, startx, starty) -> + if not InPopupWaitForClick + SelectedPlayerEntry = entry + SelectedPlayer = entry["Player"] + + StartDrag entry, startx, starty + +--[[ + the basic update for the playerlist mode's state, + assures the order and length of the player frames +--]] +PlayerListModeUpdate = -> + RecreateScoreColumns(PlayerFrames) + table.sort(PlayerFrames, PlayerSortFunction) + for i, val in ipairs PlayerFrames + MiddleFrames[i] = val["Frame"] + + for i = #PlayerFrames + 1, #MiddleFrames, 1 + MiddleFrames[i] = nil + + UpdateMinimize! + +RemoveNeutralTeam = -> + while NeutralTeamLock + debugprint "in neutral team lock" + wait! + + NeutralTeamLock = true + NeutralTeam["Frame"]\Destroy! + NeutralTeam = nil + RemoveMiddleBGFrame! + NeutralTeamLock = false + +--[[ + removes a single player from a given team (not usually called directly) + @Args: + teamEntry team entry to remove player from + index index of player in 'MyPlayers' list to remove +--]] +RemovePlayerFromTeam = (teamEntry, index) -> + table.remove(teamEntry["MyPlayers"], index) + --if teamEntry['AutoHide'] and #teamEntry['MyPlayers'] == 0 + if teamEntry == NeutralTeam and #teamEntry["MyPlayers"] == 0 + RemoveNeutralTeam! + +--[[ + finds previous team this player was on, and if it exists calls removeplayerfromteam + @Args + entry Player entry +--]] +FindRemovePlayerFromTeam = (entry) -> + if entry["MyTeam"] + for j, oldEntry in ipairs entry["MyTeam"]["MyPlayers"] + if oldEntry["Player"] == entry["Player"] + RemovePlayerFromTeam entry["MyTeam"], j + return + + elseif NeutralTeam + for j, oldEntry in ipairs NeutralTeam["MyPlayers"] + if oldEntry["Player"] == entry["Player"] + RemovePlayerFromTeam NeutralTeam, j + return + +--[[ + adds player entry entry to teamentry + removes them from any previous team + @Args: + teamEntry entry of team to add player to + entry player entry to add to this team +--]] +AddPlayerToTeam = (teamEntry, entry) -> + FindRemovePlayerFromTeam entry + table.insert teamEntry["MyPlayers"], entry + entry["MyTeam"] = teamEntry + if teamEntry["IsHidden"] + teamEntry["Frame"].Parent = ListFrame + AddMiddleBGFrame! + + teamEntry["IsHidden"] = false + +--[[ + adds a neutral team if nessisary + Note: a lot of redundant code here, might want to refactor to share a function with insertteamframe +--]] +AddNeutralTeam = -> + while NeutralTeamLock + debugprint "in neutral team 2 lock" + wait! + + NeutralTeamLock = true + + defaultTeam = Instance.new "Team" + defaultTeam.TeamColor = BrickColor.new "White" + defaultTeam.Name = "Neutral" + nentry = {} + nentry["MyTeam"] = defaultTeam + nentry["MyPlayers"] = {} + nentry["Frame"] = MiddleTemplate\Clone! + WaitForChild(WaitForChild(nentry["Frame"], "TitleFrame"), "Title").Text = defaultTeam.Name + nentry["Frame"].TitleFrame.Position = UDim2.new( + nentry["Frame"].TitleFrame.Position.X.Scale, + nentry["Frame"].TitleFrame.Position.X.Offset, + 0.1, + 0 + ) + nentry["Frame"].TitleFrame.Size = UDim2.new( + nentry["Frame"].TitleFrame.Size.X.Scale, + nentry["Frame"].TitleFrame.Size.X.Offset, + 0.8, + 0 + ) + nentry["Frame"].TitleFrame.Title.Font = "ArialBold" + nentry["Frame"].Position = UDim2.new 1, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0 + WaitForChild(nentry["Frame"], "ClickListener").MouseButton1Down\connect (nx, ny) -> + StartDrag nentry, nx, ny + + nentry["Frame"].ClickListener.BackgroundColor3 = Color3.new 1, 1, 1 + nentry["Frame"].ClickListener.BackgroundTransparency = 0.7 + nentry["Frame"].ClickListener.AutoButtonColor = false + nentry["AutoHide"] = true + nentry["IsHidden"] = true + for _, i in pairs PlayerFrames + if i["Player"].Neutral or not i["MyTeam"] + AddPlayerToTeam nentry, i + + if #nentry["MyPlayers"] > 0 + NeutralTeam = nentry + UpdateMinimize! + BaseUpdate! + + NeutralTeamLock = false + +SetPlayerToTeam = (entry) -> + FindRemovePlayerFromTeam entry + -- check to see if team exists, if it does add to that team + setToTeam = false + for _, tframe in ipairs TeamFrames + -- add my entry on the new team + if tframe["MyTeam"].TeamColor == entry["Player"].TeamColor + AddPlayerToTeam tframe, entry + setToTeam = true + + -- if player was set to an invalid team, then set it back to neutral + if not setToTeam and #(game.Teams\GetTeams!) > 0 + debugprint "#{entry["Player"].Name} could not find team" + entry["MyTeam"] = nil + if not NeutralTeam + AddNeutralTeam! + else + AddPlayerToTeam NeutralTeam, entry + +--[[ + Note: another big one, consiter breaking up + called when any children of player changes + handles 'Neutral', teamColor, Name and MembershipType changes + @Args + entry Player entry changed + property name of property changed +--]] +PlayerChanged = (entry, property) -> + while PlayerChangedLock + debugprint "in playerchanged lock" + wait 1 / 30 + + PlayerChangedLock = true + if property == "Neutral" + -- if player changing to neutral + if entry["Player"].Neutral and #game.Teams\GetTeams! > 0 + debugprint "#{entry["Player"].Name} setting to neutral" + FindRemovePlayerFromTeam(entry) + entry["MyTeam"] = nil + if not NeutralTeam + debugprint "#{entry["Player"].Name} creating neutral team" + AddNeutralTeam! + else + debugprint "#{entry["Player"].Name} adding to neutral team" + AddPlayerToTeam NeutralTeam, entry + + elseif #(game.Teams\GetTeams!) > 0 then -- else player switching to a team, or a weird edgecase + debugprint "#{entry["Player"].Name} has been set non-neutral" + SetPlayerToTeam entry + + BaseUpdate! + elseif property == "TeamColor" and not entry["Player"].Neutral and entry["Player"] ~= entry["MyTeam"] + debugprint "#{entry["Player"].Name} setting to new team" + SetPlayerToTeam entry + BaseUpdate! + elseif property == "Name" or property == "MembershipType" + entry["Frame"]\FindFirstChild"BCLabel".Image = getMembershipTypeIcon( + entry["Player"].MembershipType, entry["Player"].Name + ) + entry["Frame"].Name = entry["Player"].Name + entry["Frame"].TitleFrame.Title.Text = entry["Player"].Name + if entry["Frame"].BCLabel.Image ~= "" + entry["Frame"].TitleFrame.Title.Position = UDim2.new 0.01, 30, 0.1, 0 + + if entry["Player"] == LocalPlayer + entry["Frame"].TitleFrame.DropShadow.Text = entry["Player"].Name + ChangeHeaderName entry["Player"].Name + + BaseUpdate! + + PlayerChangedLock = false + +--[[ + this one's a doozie, happens when a player is added to the game + inits their player frame and player entry, assigns them to a team if possible, + and hooks up their leaderstats + @Args: + nplayer new player object to insert +--]] +InsertPlayerFrame = (nplayer) -> + while AddingFrameLock + debugprint "waiting to insert #{nplayer.Name}" + wait 1 / 30 + + AddingFrameLock = true + + nFrame = MiddleTemplate\Clone! + WaitForChild(WaitForChild(nFrame, "TitleFrame"), "Title").Text = nplayer.Name + + nFrame.Position = UDim2.new 1, 0, (#MiddleFrames * nFrame.Size.Y.Scale), 0 + + nfriendstatus = GetFriendStatus nplayer + + nFrame\FindFirstChild"BCLabel".Image = getMembershipTypeIcon(nplayer.MembershipType, nplayer.Name) + nFrame\FindFirstChild"FriendLabel".Image = getFriendStatusIcon(nfriendstatus) + nFrame.Name = nplayer.Name + WaitForChild(WaitForChild(nFrame, "TitleFrame"), "Title").Text = nplayer.Name + + --move for bc label + nFrame.FriendLabel.Position = nFrame.FriendLabel.Position + UDim2.new 0, 17, 0, 0 + nFrame.TitleFrame.Title.Position = nFrame.TitleFrame.Title.Position + UDim2.new 0, 17, 0, 0 + + if nFrame\FindFirstChild"FriendLabel".Image ~= "" + nFrame.TitleFrame.Title.Position = nFrame.TitleFrame.Title.Position + UDim2.new 0, 17, 0, 0 + + + if nplayer.Name == LocalPlayer.Name + nFrame.TitleFrame.Title.Font = "ArialBold" + nFrame.PlayerScore.Font = "ArialBold" + ChangeHeaderName nplayer.Name + with nFrame.TitleFrame.Title\Clone! + .TextColor3 = Color3.new 0, 0, 0 + .TextTransparency = 0 + .ZIndex = 2 + .Position = nFrame.TitleFrame.Title.Position + UDim2.new 0, 1, 0, 1 + .Name = "DropShadow" + .Parent = nFrame.TitleFrame + -- else + -- --Delay 2, -> OnFriendshipChanged(nplayer,LocalPlayer\GetFriendStatus(nplayer)) end) + + nFrame.TitleFrame.Title.Font = "ArialBold" + + nFrame.Parent = ListFrame + nFrame\TweenPosition UDim2.new(0.5, 0, (#MiddleFrames * nFrame.Size.Y.Scale), 0), "Out", "Linear", BASE_TWEEN, true + UpdateMinimize! + nentry = {} + nentry["Frame"] = nFrame + nentry["Player"] = nplayer + nentry["ID"] = AddId + AddId += 1 + table.insert PlayerFrames, nentry + if #TeamFrames ~= 0 + if nplayer.Neutral + nentry["MyTeam"] = nil + if not NeutralTeam + AddNeutralTeam! + else + AddPlayerToTeam NeutralTeam, nentry + + else + addedToTeam = false + for _, tval in ipairs TeamFrames + if tval["MyTeam"].TeamColor == nplayer.TeamColor + AddPlayerToTeam tval, nentry + nentry["MyTeam"] = tval + addedToTeam = true + + if not addedToTeam + nentry["MyTeam"] = nil + if not NeutralTeam + AddNeutralTeam! + else + AddPlayerToTeam NeutralTeam, nentry + + nentry["MyTeam"] = NeutralTeam + + + if nplayer\FindFirstChild "leaderstats" + LeaderstatsAdded nentry + + + nplayer.ChildAdded\connect (nchild) -> + if nchild.Name == "leaderstats" + while AddingFrameLock + debugprint "in adding leaderstats lock" + wait 1 / 30 + + AddingFrameLock = true + LeaderstatsAdded nentry + AddingFrameLock = false + + + + nplayer.ChildRemoved\connect (nchild) -> + if nplayer == LocalPlayer and nchild.Name == "leaderstats" + LeaderstatsRemoved nchild, nentry + + + nplayer.Changed\connect (prop) -> + PlayerChanged nentry, prop + + + listener = WaitForChild nFrame, "ClickListener" + listener.Active = true + listener.MouseButton1Down\connect (nx, ny) -> + OnPlayerEntrySelect nentry, nx, ny + + + AddMiddleBGFrame! + BaseUpdate! + AddingFrameLock = false + + +--[[ + Note: major optimization can be done here + removes this player's frame if it exists, calls base update +--]] +RemovePlayerFrame = (tplayer) -> + while AddingFrameLock + debugprint "in removing player frame lock" + wait 1 / 30 + + AddingFrameLock = true + + local tteam + for i, key in ipairs PlayerFrames + if tplayer == key["Player"] + if PopUpClipFrame.Parent == key["Frame"] + PopUpClipFrame.Parent = nil + + key["Frame"]\Destroy! + tteam = key["MyTeam"] + table.remove PlayerFrames, i + + if tteam + for j, tentry in ipairs tteam["MyPlayers"] + if tentry["Player"] == tplayer + RemovePlayerFromTeam tteam, j + + RemoveMiddleBGFrame! + UpdateMinimize! + BaseUpdate! + AddingFrameLock = false + + +Players.ChildRemoved\connect RemovePlayerFrame + +---------------------------- +-- Team Callback Functions +---------------------------- +--[[ + turns a list of team entries with sub lists of players into a single ordered + list, in the correct order,and of the correct length + @Args: + tframes the team entries to unroll + outframes the list to unroll these entries into +--]] +UnrollTeams = (tframes, outframes) -> + numEntries = 0 + if NeutralTeam and not NeutralTeam["IsHidden"] + for _, val in ipairs(NeutralTeam["MyPlayers"]) + numEntries += 1 + outframes[numEntries] = val["Frame"] + + numEntries += 1 + outframes[numEntries] = NeutralTeam["Frame"] + + for _, val in ipairs tframes + if not val["IsHidden"] + for _, pval in ipairs(val.MyPlayers) + numEntries += 1 + outframes[numEntries] = pval["Frame"] + + numEntries += 1 + outframes[numEntries] = val["Frame"] + + -- clear any additional entries from outframes + for i = numEntries + 1, #outframes, 1 + outframes[i] = nil + + +--[[ + uses lua's table.sort to sort the teams +--]] +TeamSortFunc = (a, b) -> + if a["TeamScore"] == b["TeamScore"] + return a["ID"] < b["ID"] + + if not a["TeamScore"] + return false + + if not b["TeamScore"] + return true + + a["TeamScore"] < b["TeamScore"] + +--[[ + adds up all the score of this team's players to form the team score + @Args: + team team entry to sum the scores of +--]] +AddTeamScores = (team) -> + for j = 1, #ScoreNames, 1 + i = ScoreNames[j] + tscore = 0 + for _, j in ipairs(team["MyPlayers"]) + tval = j["Player"]\FindFirstChild "leaderstats" and j["Player"].leaderstats\FindFirstChild i["Name"] + if tval and not tval\IsA "StringValue" + tscore += GetScoreValue (j["Player"].leaderstats)[i["Name"]] + + if team["Frame"]\FindFirstChild i["Name"] + --team['Frame'][i['Name'] ].Size = UDim2.new(1 - (ScrollBarFrame.Size.X.Scale * 2),- ((j-1) * SpacingPerStat),1,0) + team["Frame"][i["Name"]].Text = "#{tscore}" + + UpdateMinimize! + +--[[ + consider adding lock with wait for performance + sorts each of the team's player lists induvidually, adds up the team scores. + @Args: + tentries table of team entries +--]] +SortTeams = (tentries) -> + for _, val in ipairs tentries + table.sort val["MyPlayers"], PlayerSortFunction + AddTeamScores val + + table.sort tentries, TeamSortFunc + +--[[ + base update for team mode, adds up the scores of all teams, sorts them, + then unrolls them into middleframes +--]] +TeamListModeUpdate = -> + RecreateScoreColumns PlayerFrames + SortTeams TeamFrames + if NeutralTeam + AddTeamScores NeutralTeam + --RecreateScoreColumns(NeutralTeam['MyPlayers']) + + UnrollTeams TeamFrames, MiddleFrames + +OnFriendshipChanged = (player, friendStatus) -> + Delay 0.5, -> + debugprint "friend status changed for: #{player.Name} #{friendStatus} vs #{GetFriendStatus player}" + + for _, entry in ipairs PlayerFrames + if entry["Player"] == player + nicon = getFriendStatusIcon friendStatus + if nicon == "" and entry["Frame"].FriendLabel.Image ~= "" + entry["Frame"].TitleFrame.Title.Position = entry["Frame"].TitleFrame.Title.Position - + UDim2.new 0, 17, 0, 0 + elseif nicon ~= "" and entry["Frame"].FriendLabel.Image == "" + entry["Frame"].TitleFrame.Title.Position = entry["Frame"].TitleFrame.Title.Position + + UDim2.new 0, 17, 0, 0 + debugprint "confirmed status: #{player.Name}" + + entry["Frame"].FriendLabel.Image = nicon + return + +LocalPlayer.FriendStatusChanged\connect OnFriendshipChanged + +--[[ + +--]] +TeamScoreChanged = (entry, nscore) -> + WaitForChild(entry["Frame"], "PlayerScore").Text = "#{nscore}" + entry["TeamScore"] = nscore + +--[[ + called when child added to a team, used for autohide functionality + Note: still has teamscore, consiter removing +--]] +TeamChildAdded = (entry, nchild) -> + if nchild.Name == "AutoHide" + entry["AutoHide"] = true + elseif nchild.Name == "TeamScore" + WaitForChild(entry["Frame"], "PlayerScore").Text = "#{nchild.Value}" + entry["TeamScore"] = nchild.Value + nchild.Changed\connect -> + TeamScoreChanged entry, nchild.Value + +--[[ + called when child added to a team, used for autohide functionality + Note: still has teamscore, consiter removing +--]] +TeamChildRemoved = (entry, nchild) -> + if nchild.Name == "AutoHide" + entry["AutoHide"] = false + elseif nchild.Name == "TeamScore" + WaitForChild(entry["Frame"], "PlayerScore").Text = "" + entry["TeamScore"] = nil + +--[[ + removes team from team list + @Args: + nteam Teamobject to remove +--]] +RemoveTeamFrame = (nteam) -> + while AddingFrameLock + debugprint "in removing team frame lock" + wait 1 / 30 + + AddingFrameLock = true + -- if IsMinimized.Value + -- end + local myEntry + for i, key in ipairs TeamFrames + if nteam == key["MyTeam"] + myEntry = key + key["Frame"]\Destroy! + table.remove TeamFrames, i + + if #TeamFrames == 0 + debugprint "removeteamframe, remove neutral" + if NeutralTeam + RemoveNeutralTeam! + + for i, key in ipairs myEntry["MyPlayers"] + RemovePlayerFromTeam myEntry, i + PlayerChanged key, "TeamColor" + + RemoveMiddleBGFrame! + BaseUpdate! + AddingFrameLock = false + +TeamChanged = (entry, property) -> + if property == "Name" + WaitForChild(WaitForChild(entry["Frame"], "TitleFrame"), "Title").Text = entry["MyTeam"].Name + elseif property == "TeamColor" + entry["Frame"].ClickListener.BackgroundColor3 = entry["MyTeam"].TeamColor.Color + + for _, i in pairs TeamFrames + if i["MyTeam"].TeamColor == entry["MyTeam"] + RemoveTeamFrame entry["MyTeam"] --NO DUPLICATE TEAMS! + + entry["MyPlayers"] = {} + + for _, i in pairs PlayerFrames + SetPlayerToTeam i + + BaseUpdate! + +--[[ + creates team entry and frame for this team, sets up listeners for this team + adds any players intended for this team,Creates neutral team if this is the first team added + Note: might be best to break this into multiple functions to simplify + @Args: + nteam new team object added +--]] +InsertTeamFrame = (nteam) -> + while AddingFrameLock + debugprint "in adding team frame lock" + wait 1 / 30 + + AddingFrameLock = true + --for _,i in pairs TeamFrames + nentry = {} + nentry["MyTeam"] = nteam + nentry["MyPlayers"] = {} + nentry["Frame"] = MiddleTemplate\Clone! + WaitForChild(WaitForChild(nentry["Frame"], "TitleFrame"), "Title").Text = nteam.Name + nentry["Frame"].TitleFrame.Title.Font = "ArialBold" + nentry["Frame"].TitleFrame.Title.FontSize = "Size18" + nentry["Frame"].TitleFrame.Position = UDim2.new( + nentry["Frame"].TitleFrame.Position.X.Scale, + nentry["Frame"].TitleFrame.Position.X.Offset, + 0.1, + 0 + ) + nentry["Frame"].TitleFrame.Size = UDim2.new( + nentry["Frame"].TitleFrame.Size.X.Scale, + nentry["Frame"].TitleFrame.Size.X.Offset, + 0.8, + 0 + ) + nentry["Frame"].Position = UDim2.new(1, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0) + WaitForChild(nentry["Frame"], "ClickListener").MouseButton1Down\connect (nx, ny) -> + StartDrag nentry, nx, ny + + nentry["Frame"].ClickListener.BackgroundColor3 = nteam.TeamColor.Color + nentry["Frame"].ClickListener.BackgroundTransparency = 0.7 + nentry["Frame"].ClickListener.AutoButtonColor = false + AddId += 1 + nentry["ID"] = AddId + nentry["AutoHide"] = false + if nteam\FindFirstChild "AutoHide" + nentry["AutoHide"] = true + + if nteam\FindFirstChild "TeamScore" + TeamChildAdded nentry, nteam.TeamScore + + nteam.ChildAdded\connect (nchild) -> TeamChildAdded nentry, nchild + nteam.ChildRemoved\connect (nchild) -> TeamChildRemoved nentry, nchild + nteam.Changed\connect (prop) -> TeamChanged nentry, prop + + for _, i in pairs PlayerFrames + if not i["Player"].Neutral and i["Player"].TeamColor == nteam.TeamColor + AddPlayerToTeam nentry, i + + nentry["IsHidden"] = false + if not nentry["AutoHide"] or #nentry["MyPlayers"] > 0 + nentry["Frame"].Parent = ListFrame + nentry["Frame"]\TweenPosition( + UDim2.new(0.5, 0, (#MiddleFrames * nentry["Frame"].Size.Y.Scale), 0), + "Out", + "Linear", + BASE_TWEEN, + true + ) + AddMiddleBGFrame! + else + nentry["IsHidden"] = true + nentry["Frame"].Parent = nil + + table.insert TeamFrames, nentry + UpdateMinimize! + BaseUpdate! + if #TeamFrames == 1 and not NeutralTeam + AddNeutralTeam! + + AddingFrameLock = false + +TeamAdded = (nteam) -> InsertTeamFrame nteam +TeamRemoved = (nteam) -> RemoveTeamFrame nteam + +--------------------------------- +--[[ + called when ANYTHING changes the state of the playerlist + re-sorts everything,assures correct positions of all elements +--]] +BaseUpdate = -> + while BaseUpdateLock + debugprint "in baseupdate lock" + wait 1 / 30 + + BaseUpdateLock = true + --print ('baseupdate') + UpdateStatNames! + + if #TeamFrames == 0 and not NeutralTeam + PlayerListModeUpdate! + else + TeamListModeUpdate! + + for i, key in ipairs MiddleFrames + if not key.Parent? + key\TweenPosition( + UDim2.new(0.5, 0, ((#MiddleFrames - i) * key.Size.Y.Scale), 0), + "Out", + "Linear", + BASE_TWEEN, + true + ) + + if not IsMinimized.Value and #MiddleFrames > DefaultEntriesOnScreen + UpdateScrollPosition! + + UpdateMinimize! + + UpdateScrollBarSize! + UpdateScrollPosition! + + UpdateScrollBarVisibility! + --debugprint('EndBaseUpdate') + BaseUpdateLock = false + + +--[[ + code for attaching tab key to maximizing player list +--]] +game.GuiService\AddKey "\t" +LastTabTime = time! +game.GuiService.KeyPressed\connect (key) -> + if key == "\t" + debugprint "caught tab key" + local modalCheck, isModal = try + return game.GuiService.IsModalDialog + + if modalCheck == false or (modalCheck and isModal == false) + if time! - LastTabTime > 0.4 + LastTabTime = time! + if IsTabified.Value + if not IsMaximized.Value + ScreenGui\TweenPosition UDim2.new(0, 0, 0, 0), "Out", "Linear", BASE_TWEEN * 1.2, true + IsMaximized.Value = true + else + ScreenGui\TweenPosition( + UDim2.new(NormalBounds.X.Scale, NormalBounds.X.Offset - 10, 0, 0), + "Out", + "Linear", + BASE_TWEEN * 1.2, + true + ) + IsMaximized.Value = false + IsMinimized.Value = true + else + ToggleMaximize! + +debugPlayerAdd = (p) -> + InsertPlayerFrame p + +PlayersChildAdded = (tplayer) -> + if tplayer\IsA "Player" + Spawn -> + debugPlayerAdd tplayer + + else + BlowThisPopsicleStand! + +coreGuiChanged = (coreGuiType, enabled) -> + if coreGuiType == Enum.CoreGuiType.All or coreGuiType == Enum.CoreGuiType.PlayerList + MainFrame.Visible = enabled + + +TeamsChildAdded = (nteam) -> + if nteam\IsA "Team" + TeamAdded nteam + else + BlowThisPopsicleStand! + + +TeamsChildRemoved = (nteam) -> + if nteam\IsA "Team" + TeamRemoved nteam + else + BlowThisPopsicleStand! + +---------------------------- +-- Hookups and initialization +---------------------------- + +try + coreGuiChanged Enum.CoreGuiType.PlayerList, Game.StarterGui\GetCoreGuiEnabled Enum.CoreGuiType.PlayerList + Game.StarterGui.CoreGuiChangedSignal\connect coreGuiChanged + +while not game\GetService "Teams" + wait 1 / 30 + debugprint "Waiting For Teams" + +for _, i in pairs game.Teams\GetTeams! + TeamAdded i + +for _, i in pairs Players\GetPlayers! + Spawn -> + debugPlayerAdd i + +game.Teams.ChildAdded\connect TeamsChildAdded +game.Teams.ChildRemoved\connect TeamsChildRemoved +Players.ChildAdded\connect PlayersChildAdded + +InitReportAbuse! +AreNamesExpanded.Value = true +BaseUpdate! + +--UGGGLY,find a better way later +wait 2 +IsPersonalServer = not not game.Workspace\FindFirstChild "PSVariable" + +---------------------------- +-- Running Logic +---------------------------- + +--debug stuffs, will only run for 'newplayerlistisbad' +if LocalPlayer.Name == "newplayerlistisbad" or LocalPlayer.Name == "imtotallyadmin" + debugFrame.Parent = ScreenGui + Spawn -> + while true + local str_players = "" + for _, i in pairs game.Players\GetPlayers! + str_players ..= " #{i.Name}" + + debugplayers.Text = str_players + wait 0.5 diff --git a/yue/59002209.lua b/yue/59002209.lua new file mode 100644 index 0000000..e69de29 diff --git a/yue/73157242.lua b/yue/73157242.lua new file mode 100644 index 0000000..db6eb07 --- /dev/null +++ b/yue/73157242.lua @@ -0,0 +1,1944 @@ +local t = { } +local PlaneIntersection +PlaneIntersection = function(vectorPos) + local hit = false + local currCamera = game.Workspace.CurrentCamera + local startPos + do + local _with_0 = currCamera.CoordinateFrame.p + startPos = Vector3.new(_with_0.X, _with_0.Y, _with_0.Z) + end + local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z) + local normal = Vector3.new(0, 1, 0) + local p3 = Vector3.new(0, 0, 0) + local startEndDot = normal:Dot(endPos - startPos) + local cellPos = vectorPos + if startEndDot ~= 0 then + t = normal:Dot(p3 - startPos) / startEndDot + if t >= 0 and t <= 1 then + local intersection = ((endPos - startPos) * t) + startPos + cellPos = game.Workspace.Terrain:WorldToCell(intersection) + hit = true + end + end + return cellPos, hit +end +local GetTerrainForMouse +GetTerrainForMouse = function(mouse) + local cell = game.Workspace.Terrain:WorldToCellPreferSolid(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z)) + local planeLoc + if 0 == game.Workspace.Terrain:GetCell(cell.X, cell.Y, cell.Z).Value then + cell = nil + local hit + planeLoc, hit = PlaneIntersection(Vector3.new(mouse.hit.x, mouse.hit.y, mouse.hit.z)) + if hit then + cell = planeLoc + end + end + return cell +end +local insertBoundingBoxOverlapVector = Vector3.new(0.3, 0.3, 0.3) +local rotatePartAndChildren +rotatePartAndChildren = function(part, rotCF, offsetFromOrigin) + if part:IsA("BasePart") then + part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin + end + local partChildren = part:GetChildren() + for c = 1, #partChildren do + rotatePartAndChildren(partChildren[c], rotCF, offsetFromOrigin) + end +end +local modelRotate +modelRotate = function(model, yAngle) + local rotCF = CFrame.Angles(0, yAngle, 0) + local offsetFromOrigin = model:GetModelCFrame().p + return rotatePartAndChildren(model, rotCF, offsetFromOrigin) +end +local collectParts +collectParts = function(object, baseParts, scripts, decals) + if object:IsA("BasePart") then + baseParts[#baseParts + 1] = object + elseif object:IsA("Script") then + scripts[#scripts + 1] = object + elseif object:IsA("Decal") then + decals[#decals + 1] = object + end + for _, child in pairs(object:GetChildren()) do + collectParts(child, baseParts, scripts, decals) + end +end +local clusterPartsInRegion +clusterPartsInRegion = function(startVector, endVector) + local cluster = game.Workspace:FindFirstChild("Terrain") + local startCell = cluster:WorldToCell(startVector) + local endCell = cluster:WorldToCell(endVector) + local startX = startCell.X + local startY = startCell.Y + local startZ = startCell.Z + local endX = endCell.X + local endY = endCell.Y + local endZ = endCell.Z + if startX < cluster.MaxExtents.Min.X then + startX = cluster.MaxExtents.Min.X + end + if startY < cluster.MaxExtents.Min.Y then + startY = cluster.MaxExtents.Min.Y + end + if startZ < cluster.MaxExtents.Min.Z then + startZ = cluster.MaxExtents.Min.Z + end + if endX > cluster.MaxExtents.Max.X then + endX = cluster.MaxExtents.Max.X + end + if endY > cluster.MaxExtents.Max.Y then + endY = cluster.MaxExtents.Max.Y + end + if endZ > cluster.MaxExtents.Max.Z then + endZ = cluster.MaxExtents.Max.Z + end + for x = startX, endX do + for y = startY, endY do + for z = startZ, endZ do + if cluster:GetCell(x, y, z).Value > 0 then + return true + end + end + end + end + return false +end +local findSeatsInModel +findSeatsInModel = function(parent, seatTable) + if not parent then + return + end + if parent.className == "Seat" or parent.className == "VehicleSeat" then + table.insert(seatTable, parent) + end + local myChildren = parent:GetChildren() + for j = 1, #myChildren do + findSeatsInModel(myChildren[j], seatTable) + end +end +local setSeatEnabledStatus +setSeatEnabledStatus = function(model, isEnabled) + local seatList = { } + findSeatsInModel(model, seatList) + if isEnabled then + for i = 1, #seatList do + local nextSeat = seatList[i]:FindFirstChild("SeatWeld") + while nextSeat do + nextSeat:Remove() + nextSeat = seatList[i]:FindFirstChild("SeatWeld") + end + end + else + for i = 1, #seatList do + local fakeWeld = Instance.new("Weld") + fakeWeld.Name = "SeatWeld" + fakeWeld.Parent = seatList[i] + end + end +end +local autoAlignToFace +autoAlignToFace = function(parts) + local aatf = parts:FindFirstChild("AutoAlignToFace") + if aatf then + return aatf.Value + else + return false + end +end +local getClosestAlignedWorldDirection +getClosestAlignedWorldDirection = function(aVector3InWorld) + local xDir = Vector3.new(1, 0, 0) + local yDir = Vector3.new(0, 1, 0) + local zDir = Vector3.new(0, 0, 1) + local xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z + local yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z + local zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z + if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs(zDot) then + if xDot > 0 then + return 0 + else + return 3 + end + elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs(zDot) then + if yDot > 0 then + return 1 + else + return 4 + end + else + if zDot > 0 then + return 2 + else + return 5 + end + end +end +local positionPartsAtCFrame3 +positionPartsAtCFrame3 = function(aCFrame, currentParts) + local insertCFrame + if not currentParts then + return currentParts + end + if currentParts and (currentParts:IsA("Model") or currentParts:IsA("Tool")) then + insertCFrame = currentParts:GetModelCFrame() + currentParts:TranslateBy(aCFrame.p - insertCFrame.p) + else + currentParts.CFrame = aCFrame + end + return currentParts +end +local calcRayHitTime +calcRayHitTime = function(rayStart, raySlope, intersectionPlane) + if math.abs(raySlope) < 0.01 then + return 0 + end + return (intersectionPlane - rayStart) / raySlope +end +local modelTargetSurface +modelTargetSurface = function(partOrModel, rayStart, rayEnd) + if not partOrModel then + return 0 + end + local modelCFrame, modelSize + if partOrModel:IsA("Model") then + modelCFrame = partOrModel:GetModelCFrame() + modelSize = partOrModel:GetModelSize() + else + modelCFrame = partOrModel.CFrame + modelSize = partOrModel.Size + end + local mouseRayStart = modelCFrame:pointToObjectSpace(rayStart) + local mouseRayEnd = modelCFrame:pointToObjectSpace(rayEnd) + local mouseSlope = mouseRayEnd - mouseRayStart + local xPositive = 1 + local yPositive = 1 + local zPositive = 1 + if mouseSlope.X > 0 then + xPositive = -1 + end + if mouseSlope.Y > 0 then + yPositive = -1 + end + if mouseSlope.Z > 0 then + zPositive = -1 + end + local xHitTime = calcRayHitTime(mouseRayStart.X, mouseSlope.X, modelSize.X / 2 * xPositive) + local yHitTime = calcRayHitTime(mouseRayStart.Y, mouseSlope.Y, modelSize.Y / 2 * yPositive) + local zHitTime = calcRayHitTime(mouseRayStart.Z, mouseSlope.Z, modelSize.Z / 2 * zPositive) + local hitFace = 0 + if xHitTime > yHitTime then + if xHitTime > zHitTime then + hitFace = 1 * xPositive + else + hitFace = 3 * zPositive + end + else + if yHitTime > zHitTime then + hitFace = 2 * yPositive + else + hitFace = 3 * zPositive + end + end + return hitFace +end +local getBoundingBox2 +getBoundingBox2 = function(partOrModel) + local minVec = Vector3.new(math.huge, math.huge, math.huge) + local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge) + if partOrModel:IsA("Terrain") then + minVec = Vector3.new(-2, -2, -2) + maxVec = Vector3.new(2, 2, 2) + elseif partOrModel:IsA("BasePart") then + minVec = -0.5 * partOrModel.Size + maxVec = -minVec + else + maxVec = partOrModel:GetModelSize() * 0.5 + minVec = -maxVec + end + local justifyValue = partOrModel:FindFirstChild("Justification") + if (justifyValue ~= nil) then + local justify = justifyValue.Value + local two = Vector3.new(2, 2, 2) + local actualBox = maxVec - minVec - Vector3.new(0.01, 0.01, 0.01) + local containingGridBox = Vector3.new(4 * math.ceil(actualBox.x / 4), 4 * math.ceil(actualBox.y / 4), 4 * math.ceil(actualBox.z / 4)) + local adjustment = containingGridBox - actualBox + minVec = minVec - (0.5 * adjustment * justify) + maxVec = maxVec + (0.5 * adjustment * (two - justify)) + end + return minVec, maxVec +end +local getBoundingBoxInWorldCoordinates +getBoundingBoxInWorldCoordinates = function(partOrModel) + local minVec = Vector3.new(math.huge, math.huge, math.huge) + local maxVec = Vector3.new(-math.huge, -math.huge, -math.huge) + if partOrModel:IsA("BasePart") and not partOrModel:IsA("Terrain") then + local vec1 = partOrModel.CFrame:pointToWorldSpace(-0.5 * partOrModel.Size) + local vec2 = partOrModel.CFrame:pointToWorldSpace(0.5 * partOrModel.Size) + minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z)) + maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z)) + elseif not partOrModel:IsA("Terrain") then + local vec1 = partOrModel:GetModelCFrame():pointToWorldSpace(-0.5 * partOrModel:GetModelSize()) + local vec2 = partOrModel:GetModelCFrame():pointToWorldSpace(0.5 * partOrModel:GetModelSize()) + minVec = Vector3.new(math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min(vec1.Z, vec2.Z)) + maxVec = Vector3.new(math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max(vec1.Z, vec2.Z)) + end + return minVec, maxVec +end +local getTargetPartBoundingBox +getTargetPartBoundingBox = function(targetPart) + return getBoundingBox2((function() + if (targetPart.Parent:FindFirstChild("RobloxModel") ~= nil) then + return targetPart.Parent + else + return targetPart + end + end)()) +end +local getMouseTargetCFrame +getMouseTargetCFrame = function(targetPart) + if (targetPart.Parent:FindFirstChild("RobloxModel") ~= nil) then + if targetPart.Parent:IsA("Tool") then + return targetPart.Parent.Handle.CFrame + else + return targetPart.Parent:GetModelCFrame() + end + else + return targetPart.CFrame + end +end +local isBlocker +isBlocker = function(part) + if not part then + return false + end + if not part.Parent then + return false + end + if part:FindFirstChild("Humanoid") then + return false + end + if part:FindFirstChild("RobloxStamper" or part:FindFirstChild("RobloxModel")) then + return true + end + if part:IsA("Part") and not part.CanCollide then + return false + end + if part == game.Lighting then + return false + end + return isBlocker(part.Parent) +end +local spaceAboveCharacter +spaceAboveCharacter = function(charTorso, newTorsoY, stampData) + local partsAboveChar = game.Workspace:FindPartsInRegion3(Region3.new(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75)), charTorso.Parent, 100) + for j = 1, #partsAboveChar do + if partsAboveChar[j].CanCollide and not partsAboveChar[j]:IsDescendantOf(stampData.CurrentParts) then + return false + end + end + if clusterPartsInRegion(Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75)) then + return false + end + return true +end +local findConfigAtMouseTarget +findConfigAtMouseTarget = function(Mouse, stampData) + if not Mouse then + return + end + if not stampData then + return error("findConfigAtMouseTarget: stampData is nil") + end + if not stampData["CurrentParts"] then + return + end + local grid = 4.0 + local admissibleConfig = false + local targetConfig = CFrame.new(0, 0, 0) + local minBB, maxBB = getBoundingBox2(stampData.CurrentParts) + local diagBB = maxBB - minBB + local insertCFrame + if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then + insertCFrame = stampData.CurrentParts:GetModelCFrame() + else + insertCFrame = stampData.CurrentParts.CFrame + end + if Mouse then + if stampData.CurrentParts:IsA("Tool") then + Mouse.TargetFilter = stampData.CurrentParts.Handle + else + Mouse.TargetFilter = stampData.CurrentParts + end + end + local hitPlane = false + local targetPart + local success = pcall(function() + targetPart = Mouse.Target + end) + if not success then + return admissibleConfig, targetConfig + end + local mouseHitInWorld = Vector3.new(0, 0, 0) + if Mouse then + mouseHitInWorld = Vector3.new(Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z) + end + local cellPos + if nil == targetPart then + cellPos = GetTerrainForMouse(Mouse) + if nil == cellPos then + hitPlane = false + return admissibleConfig, targetConfig + else + targetPart = game.Workspace.Terrain + hitPlane = true + cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z) + mouseHitInWorld = game.Workspace.Terrain:CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z) + end + end + local minBBTarget, maxBBTarget + minBBTarget, maxBBTarget = getTargetPartBoundingBox(targetPart) + local diagBBTarget = maxBBTarget - minBBTarget + local targetCFrame = getMouseTargetCFrame(targetPart) + if targetPart:IsA("Terrain") then + if not cluster then + cluster = game.Workspace:FindFirstChild("Terrain") + end + local cellID = cluster:WorldToCellPreferSolid(mouseHitInWorld) + if hitPlane then + cellID = cellPos + end + targetCFrame = CFrame.new(game.Workspace.Terrain:CellCenterToWorld(cellID.x, cellID.y, cellID.z)) + end + local mouseHitInTarget = targetCFrame:pointToObjectSpace(mouseHitInWorld) + local targetVectorInWorld = Vector3.new(0, 0, 0) + if Mouse then + targetVectorInWorld = targetPart.CFrame:vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) + end + local targetRefPointInTarget, insertRefPointInInsert + local clampToSurface + if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 then + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1)) + clampToSurface = Vector3.new(0, 1, 1) + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 then + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1)) + clampToSurface = Vector3.new(0, 1, 1) + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 then + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1)) + clampToSurface = Vector3.new(1, 0, 1) + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 then + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, 1, 1)) + clampToSurface = Vector3.new(1, 0, 1) + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 then + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(-1, -1, 1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(-1, -1, -1)) + clampToSurface = Vector3.new(1, 1, 0) + else + targetRefPointInTarget = targetCFrame:vectorToObjectSpace(Vector3.new(1, -1, -1)) + insertRefPointInInsert = insertCFrame:vectorToObjectSpace(Vector3.new(1, -1, 1)) + clampToSurface = Vector3.new(1, 1, 0) + end + targetRefPointInTarget = targetRefPointInTarget * ((0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget)) + insertRefPointInInsert = insertRefPointInInsert * ((0.5 * diagBB) + 0.5 * (maxBB + minBB)) + local delta = mouseHitInTarget - targetRefPointInTarget + local deltaClamped = Vector3.new(grid * math.modf(delta.x / grid), grid * math.modf(delta.y / grid), grid * math.modf(delta.z / grid)) + deltaClamped = deltaClamped * clampToSurface + local targetTouchInTarget = deltaClamped + targetRefPointInTarget + local TargetTouchRelToWorld = targetCFrame:pointToWorldSpace(targetTouchInTarget) + local InsertTouchInWorld = insertCFrame:vectorToWorldSpace(insertRefPointInInsert) + local posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld + local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 + _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame:components() + targetConfig = CFrame.new(posInsertOriginInWorld.x, posInsertOriginInWorld.y, posInsertOriginInWorld.z, R00, R01, R02, R10, R11, R12, R20, R21, R22) + admissibleConfig = true + return admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld) +end +local truncateToCircleEighth +truncateToCircleEighth = function(bigValue, littleValue) + local big = math.abs(bigValue) + local little = math.abs(littleValue) + local hypotenuse = math.sqrt(big * big + little * little) + local frac = little / hypotenuse + local bigSign = 1 + local littleSign = 1 + if bigValue < 0 then + bigSign = -1 + end + if littleValue < 0 then + littleSign = -1 + end + if frac > 0.382683432 then + return 0.707106781 * hypotenuse * bigSign, 0.707106781 * hypotenuse * littleSign + else + return hypotenuse * bigSign, 0 + end +end +local saveTheWelds +saveTheWelds = function(object, manualWeldTable, manualWeldParentTable) + if object:IsA("ManualWeld") or object:IsA("Rotate") then + table.insert(manualWeldTable, object) + return table.insert(manualWeldParentTable, object.Parent) + else + local children = object:GetChildren() + for i = 1, #children do + saveTheWelds(children[i], manualWeldTable, manualWeldParentTable) + end + end +end +local restoreTheWelds +restoreTheWelds = function(manualWeldTable, manualWeldParentTable) + for i = 1, #manualWeldTable do + manualWeldTable[i].Parent = manualWeldParentTable[i] + end +end +t.CanEditRegion = function(partOrModel, EditRegion) + if not EditRegion then + return true, false + end + local minBB, maxBB + minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel) + if minBB.X < EditRegion.CFrame.p.X - EditRegion.Size.X / 2 or minBB.Y < EditRegion.CFrame.p.Y - EditRegion.Size.Y / 2 or minBB.Z < EditRegion.CFrame.p.Z - EditRegion.Size.Z / 2 then + return false, false + end + if maxBB.X > EditRegion.CFrame.p.X + EditRegion.Size.X / 2 or maxBB.Y > EditRegion.CFrame.p.Y + EditRegion.Size.Y / 2 or maxBB.Z > EditRegion.CFrame.p.Z + EditRegion.Size.Z / 2 then + return false, false + end + return true, false +end +t.GetStampModel = function(assetId, terrainShape, useAssetVersionId) + if assetId == 0 then + return nil, "No Asset" + end + if assetId < 0 then + return nil, "Negative Asset" + end + local UnlockInstances + UnlockInstances = function(object) + if object:IsA("BasePart") then + object.Locked = false + end + for _, child in pairs(object:GetChildren()) do + UnlockInstances(child) + end + end + local getClosestColorToTerrainMaterial + getClosestColorToTerrainMaterial = function(terrainValue) + return BrickColor.new((function() + if 1 == terrainValue then + return "Bright green" + elseif 2 == terrainValue then + return "Bright yellow" + elseif 3 == terrainValue then + return "Bright red" + elseif 4 == terrainValue then + return "Sand red" + elseif 5 == terrainValue then + return "Black" + elseif 6 == terrainValue then + return "Dark stone grey" + elseif 7 == terrainValue then + return "Sand blue" + elseif 8 == terrainValue then + return "Deep orange" + elseif 9 == terrainValue then + return "Dark orange" + elseif 10 == terrainValue then + return "Reddish brown" + elseif 11 == terrainValue then + return "Light orange" + elseif 12 == terrainValue then + return "Light stone grey" + elseif 13 == terrainValue then + return "Sand green" + elseif 14 == terrainValue then + return "Medium stone grey" + elseif 15 == terrainValue then + return "Really red" + elseif 16 == terrainValue then + return "Really blue" + elseif 17 == terrainValue then + return "Bright blue" + else + return "Bright green" + end + end)()) + end + local setupFakeTerrainPart + setupFakeTerrainPart = function(cellMat, cellType, cellOrient) + local newTerrainPiece + if cellType == 1 or cellType == 4 then + newTerrainPiece = Instance.new("WedgePart") + newTerrainPiece.formFactor = "Custom" + elseif cellType == 2 then + newTerrainPiece = Instance.new("CornerWedgePart") + else + newTerrainPiece = Instance.new("Part") + newTerrainPiece.formFactor = "Custom" + end + newTerrainPiece.Name = "MegaClusterCube" + newTerrainPiece.Size = Vector3.new(4, 4, 4) + newTerrainPiece.BottomSurface = "Smooth" + newTerrainPiece.TopSurface = "Smooth" + newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial(cellMat) + local sideways = 0 + local flipped = math.pi + if cellType == 4 then + sideways = -math.pi / 2 + end + if cellType == 2 or cellType == 3 then + flipped = 0 + end + newTerrainPiece.CFrame = CFrame.Angles(0, math.pi / 2 * cellOrient + flipped, sideways) + if cellType == 3 then + local inverseCornerWedgeMesh = Instance.new("SpecialMesh") + inverseCornerWedgeMesh.MeshType = "FileMesh" + inverseCornerWedgeMesh.MeshId = "http://www.roblox.com/asset?id=66832495" + inverseCornerWedgeMesh.Scale = Vector3.new(2, 2, 2) + inverseCornerWedgeMesh.Parent = newTerrainPiece + end + local materialTag = Instance.new("Vector3Value") + materialTag.Value = Vector3.new(cellMat, cellType, cellOrient) + materialTag.Name = "ClusterMaterial" + materialTag.Parent = newTerrainPiece + return newTerrainPiece + end + local root + local loader + local loading = true + if useAssetVersionId then + loader = coroutine.create(function() + root = game:GetService("InsertService"):LoadAssetVersion(assetId) + loading = false + end) + coroutine.resume(loader) + else + loader = coroutine.create(function() + root = game:GetService("InsertService"):LoadAsset(assetId) + loading = false + end) + coroutine.resume(loader) + end + local lastGameTime = 0 + local totalTime = 0 + local maxWait = 8 + while loading and totalTime < maxWait do + lastGameTime = tick() + wait(1) + totalTime = totalTime + (tick() - lastGameTime) + end + loading = false + if totalTime >= maxWait then + return nil, "Load Time Fail" + end + if root == nil then + return nil, "Load Asset Fail" + end + if not root:IsA("Model") then + return nil, "Load Type Fail" + end + local instances = root:GetChildren() + if #instances == 0 then + return nil, "Empty Model Fail" + end + UnlockInstances(root) + root = root:GetChildren()[1] + for _, instance in pairs(instances) do + if instance:IsA("Team") then + instance.Parent = game:GetService("Teams") + elseif instance:IsA("Sky") then + local lightingService = game:GetService("Lighting") + for _, child in pairs(lightingService:GetChildren()) do + if child:IsA("Sky") then + child:Remove() + end + end + instance.Parent = lightingService + return + end + end + if not (root:FindFirstChild("RobloxModel") ~= nil) then + local stringTag = Instance.new("BoolValue") + stringTag.Name = "RobloxModel" + stringTag.Parent = root + if not (root:FindFirstChild("RobloxStamper") ~= nil) then + local stringTag2 = Instance.new("BoolValue") + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = root + end + end + if terrainShape then + if root.Name == "MegaClusterCube" then + if terrainShape == 6 then + local autowedgeTag = Instance.new("BoolValue") + autowedgeTag.Name = "AutoWedge" + autowedgeTag.Parent = root + else + local clusterTag = root:FindFirstChild("ClusterMaterial") + if clusterTag then + if clusterTag:IsA("Vector3Value") then + root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z) + else + root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0) + end + else + root = setupFakeTerrainPart(1, terrainShape, 0) + end + end + end + end + return root +end +t.SetupStamperDragger = function(modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc) + if not modelToStamp then + error("SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model") + return nil + end + if not modelToStamp:IsA("Model") and not modelToStamp:IsA("BasePart") then + error("SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!") + return nil + end + if not Mouse then + error("SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object") + return nil + end + if not Mouse:IsA("Mouse") then + error("SetupStamperDragger: Mouse (second arg) is not of type Mouse!") + return nil + end + local stampInModel + local allowedStampRegion + local stampFailedFunc + if StampInModel then + if not StampInModel:IsA("Model") then + error("SetupStamperDragger: StampInModel (optional third arg) is not of type 'Model'") + return nil + end + if not AllowedStampRegion then + error("SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined") + return nil + end + stampFailedFunc = StampFailedFunc + stampInModel = StampInModel + allowedStampRegion = AllowedStampRegion + end + local gInitial90DegreeRotations = 0 + local stampData + local mouseTarget + local errorBox = Instance.new("SelectionBox") + errorBox.Color = BrickColor.new("Bright red") + errorBox.Transparency = 0 + errorBox.Archivable = false + local adornPart = Instance.new("Part") + adornPart.Parent = nil + adornPart.formFactor = "Custom" + adornPart.Size = Vector3.new(4, 4, 4) + adornPart.CFrame = CFrame.new() + adornPart.Archivable = false + local adorn = Instance.new("SelectionBox") + adorn.Color = BrickColor.new("Toothpaste") + adorn.Adornee = adornPart + adorn.Visible = true + adorn.Transparency = 0 + adorn.Name = "HighScalabilityStamperLine" + adorn.Archivable = false + local HighScalabilityLine = { } + HighScalabilityLine.Start = nil + HighScalabilityLine.End = nil + HighScalabilityLine.Adorn = adorn + HighScalabilityLine.AdornPart = adornPart + HighScalabilityLine.InternalLine = nil + HighScalabilityLine.NewHint = true + HighScalabilityLine.MorePoints = { + nil, + nil + } + HighScalabilityLine.MoreLines = { + nil, + nil + } + HighScalabilityLine.Dimensions = 1 + local control = { } + local movingLock = false + local stampUpLock = false + local unstampableSurface = false + local mouseCons = { } + local keyCon + local stamped = Instance.new("BoolValue") + stamped.Archivable = false + stamped.Value = false + local lastTarget = { } + lastTarget.TerrainOrientation = 0 + lastTarget.CFrame = 0 + local cellInfo = { } + cellInfo.Material = 1 + cellInfo.clusterType = 0 + cellInfo.clusterOrientation = 0 + local isMegaClusterPart + isMegaClusterPart = function() + if not stampData then + return false + end + if not stampData.CurrentParts then + return false + end + return stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) or (stampData.CurrentParts.Name == "MegaClusterCube") + end + local DoHighScalabilityRegionSelect + DoHighScalabilityRegionSelect = function() + local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube") + if not megaCube then + if not stampData.CurrentParts.Name == "MegaClusterCube" then + return + else + megaCube = stampData.CurrentParts + end + end + HighScalabilityLine.End = megaCube.CFrame.p + local line + local line2 = Vector3.new(0, 0, 0) + local line3 = Vector3.new(0, 0, 0) + if HighScalabilityLine.Dimensions == 1 then + line = (HighScalabilityLine.End - HighScalabilityLine.Start) + if math.abs(line.X) < math.abs(line.Y) then + if math.abs(line.X) < math.abs(line.Z) then + local newY, newZ + if math.abs(line.Y) > math.abs(line.Z) then + newY, newZ = truncateToCircleEighth(line.Y, line.Z) + else + newZ, newY = truncateToCircleEighth(line.Z, line.Y) + end + line = Vector3.new(0, newY, newZ) + else + local newY, newX + newY, newX = truncateToCircleEighth(line.Y, line.X) + line = Vector3.new(newX, newY, 0) + end + else + if math.abs(line.Y) < math.abs(line.Z) then + local newX, newZ + if math.abs(line.X) > math.abs(line.Z) then + newX, newZ = truncateToCircleEighth(line.X, line.Z) + else + newZ, newX = truncateToCircleEighth(line.Z, line.X) + end + line = Vector3.new(newX, 0, newZ) + else + local newX, newY + newX, newY = truncateToCircleEighth(line.X, line.Y) + line = Vector3.new(newX, newY, 0) + end + end + HighScalabilityLine.InternalLine = line + elseif HighScalabilityLine.Dimensions == 2 then + line = HighScalabilityLine.MoreLines[1] + line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1] + line2 = line2 - (line.unit * line.unit:Dot(line2)) + local tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) + local yAxis = tempCFrame:vectorToWorldSpace(Vector3.new(0, 1, 0)) + local xAxis = tempCFrame:vectorToWorldSpace(Vector3.new(1, 0, 0)) + local xComp = xAxis:Dot(line2) + local yComp = yAxis:Dot(line2) + if math.abs(yComp) > math.abs(xComp) then + line2 = line2 - (xAxis * xComp) + else + line2 = line2 - (yAxis * yComp) + end + HighScalabilityLine.InternalLine = line2 + elseif HighScalabilityLine.Dimensions == 3 then + line = HighScalabilityLine.MoreLines[1] + line2 = HighScalabilityLine.MoreLines[2] + line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2] + line3 = line3 - (line.unit * line.unit:Dot(line3)) + line3 = line3 - (line2.unit * line2.unit:Dot(line3)) + HighScalabilityLine.InternalLine = line3 + end + local tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) + if HighScalabilityLine.Dimensions == 1 then + HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4) + HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size / 2) + else + local boxSize = tempCFrame:vectorToObjectSpace(line + line2 + line3) + HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z)) + HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame:vectorToWorldSpace(boxSize / 2) + end + local gui + if game.Players["LocalPlayer"] then + gui = game.Players.LocalPlayer:FindFirstChild("PlayerGui") + if gui and gui:IsA("PlayerGui") then + if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or HighScalabilityLine.Dimensions > 1 then + HighScalabilityLine.Adorn.Parent = gui + end + end + end + if not (gui ~= nil) then + gui = game:GetService("CoreGui") + if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or HighScalabilityLine.Dimensions > 1 then + HighScalabilityLine.Adorn.Parent = gui + end + end + end + local DoStamperMouseMove + DoStamperMouseMove = function(Mouse) + if not Mouse then + error("Error: RbxStamper.DoStamperMouseMove: Mouse is nil") + return + end + if not Mouse:IsA("Mouse") then + error("Error: RbxStamper.DoStamperMouseMove: Mouse is of type", Mouse.className, "should be of type Mouse") + return + end + if not Mouse.Target then + local cellPos = GetTerrainForMouse(Mouse) + if nil == cellPos then + return + end + end + if not stampData then + return + end + local configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData) + if not configFound then + error("RbxStamper.DoStamperMouseMove No configFound, returning") + return + end + local numRotations = 0 + if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 then + if targetSurface == 3 then + numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 0 then + numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 5 then + numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 2 then + numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + end + end + local ry = math.pi / 2 + gInitial90DegreeRotations = gInitial90DegreeRotations + numRotations + if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then + modelRotate(stampData.CurrentParts, ry * numRotations) + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry * numRotations, 0)({ + stampData.CurrentParts.CFrame + }) + end + local minBB, maxBB + minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) + local currModelCFrame + if stampData.CurrentParts:IsA("Model") then + currModelCFrame = stampData.CurrentParts:GetModelCFrame() + else + currModelCFrame = stampData.CurrentParts.CFrame + end + minBB = minBB + (targetCFrame.p - currModelCFrame.p) + maxBB = maxBB + (targetCFrame.p - currModelCFrame.p) + if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then + if lastTarget.CFrame then + if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then + local theClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if theClusterMaterial:IsA("Vector3Value") then + local stampClusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if stampClusterMaterial then + stampClusterMaterial = clusterMat + end + end + end + end + return + end + if isMegaClusterPart() then + local cellToStamp = game.Workspace.Terrain:WorldToCell(targetCFrame.p) + local newCFramePosition = game.Workspace.Terrain:CellCenterToWorld(cellToStamp.X, cellToStamp.Y, cellToStamp.Z) + local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 + _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame:components() + targetCFrame = CFrame.new(newCFramePosition.X, newCFramePosition.Y, newCFramePosition.Z, R00, R01, R02, R10, R11, R12, R20, R21, R22) + end + positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) + lastTarget.CFrame = targetCFrame + if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then + local clusterMat = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if clusterMat:IsA("Vector3Value") then + lastTarget.TerrainOrientation = clusterMat.Value.Z + end + end + if Mouse and Mouse.Target and Mouse.Target.Parent then + local modelInfo = Mouse.Target:FindFirstChild("RobloxModel") + if not modelInfo then + modelInfo = Mouse.Target.Parent:FindFirstChild("RobloxModel") + end + local myModelInfo = stampData.CurrentParts:FindFirstChild("UnstampableFaces") + if true then + local breakingFaces = "" + local myBreakingFaces = "" + if modelInfo and modelInfo.Parent:FindFirstChild("UnstampableFaces") then + breakingFaces = modelInfo.Parent.UnstampableFaces.Value + end + if myModelInfo then + myBreakingFaces = myModelInfo.Value + end + local hitFace = 0 + if modelInfo then + hitFace = modelTargetSurface(modelInfo.Parent, game.Workspace.CurrentCamera.CoordinateFrame.p, Mouse.Hit.p) + end + for bf in string.gmatch(breakingFaces, "[^,]+") do + if hitFace == tonumber(bf) then + unstampableSurface = true + game.JointsService:ClearJoinAfterMoveJoints() + return + end + end + hitFace = modelTargetSurface(stampData.CurrentParts, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) + for bf in string.gmatch(myBreakingFaces, "[^,]+") do + if hitFace == tonumber(bf) then + unstampableSurface = true + game.JointsService:ClearJoinAfterMoveJoints() + return + end + end + end + end + unstampableSurface = false + game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts) + if (not pcall(function() + if Mouse and Mouse.Target and not (Mouse.Target.Parent:FindFirstChild("RobloxModel") ~= nil) then + return + else + return + end + end)) then + error("Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check") + game.JointsService:ClearJoinAfterMoveJoints() + Mouse = nil + return + end + if Mouse and Mouse.Target and not (Mouse.Target.Parent:FindFirstChild("RobloxModel") ~= nil) then + game.JointsService:SetJoinAfterMoveTarget(Mouse.Target) + else + game.JointsService:SetJoinAfterMoveTarget(nil) + end + game.JointsService:ShowPermissibleJoints() + if isMegaClusterPart() and HighScalabilityLine and HighScalabilityLine.Start then + return DoHighScalabilityRegionSelect() + end + end + local setupKeyListener + setupKeyListener = function(key, Mouse) + if control and control["Paused"] then + return + end + key = string.lower(key) + if key == "r" and not autoAlignToFace(stampData.CurrentParts) then + gInitial90DegreeRotations = gInitial90DegreeRotations + 1 + local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if clusterValues and clusterValues:IsA("Vector3Value") then + clusterValues.Value = Vector3.new(clusterValues.Value.X, clusterValues.Value.Y, (clusterValues.Value.Z + 1) % 4) + end + local ry = math.pi / 2 + if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then + modelRotate(stampData.CurrentParts, ry) + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame + end + local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) + if configFound then + positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) + return DoStamperMouseMove(Mouse) + end + elseif key == "c" then + if HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 and HighScalabilityLine.Dimensions < 3 then + HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End + HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine + HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1 + HighScalabilityLine.NewHint = true + end + end + end + keyCon = Mouse.KeyDown:connect(function(key) + return setupKeyListener(key, Mouse) + end) + local resetHighScalabilityLine + resetHighScalabilityLine = function() + if HighScalabilityLine then + HighScalabilityLine.Start = nil + HighScalabilityLine.End = nil + HighScalabilityLine.InternalLine = nil + HighScalabilityLine.NewHint = true + end + end + local flashRedBox + flashRedBox = function() + local gui = game.CoreGui + if game:FindFirstChild("Players") then + if game.Players["LocalPlayer"] then + if game.Players.LocalPlayer:FindFirstChild("PlayerGui") then + gui = game.Players.LocalPlayer.PlayerGui + end + end + end + if not stampData["ErrorBox"] then + return + end + stampData.ErrorBox.Parent = gui + if stampData.CurrentParts:IsA("Tool") then + stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle + else + stampData.ErrorBox.Adornee = stampData.CurrentParts + end + return delay(0, function() + for _ = 1, 3 do + if stampData["ErrorBox"] then + stampData.ErrorBox.Visible = true + end + wait(0.13) + if stampData["ErrorBox"] then + stampData.ErrorBox.Visible = false + end + wait(0.13) + end + if stampData["ErrorBox"] then + stampData.ErrorBox.Adornee = nil + stampData.ErrorBox.Parent = Tool + end + end) + end + local DoStamperMouseDown + DoStamperMouseDown = function(Mouse) + if not Mouse then + error("Error: RbxStamper.DoStamperMouseDown: Mouse is nil") + return + end + if not Mouse:IsA("Mouse") then + error("Error: RbxStamper.DoStamperMouseDown: Mouse is of type", Mouse.className, "should be of type Mouse") + return + end + if not stampData then + return + end + if isMegaClusterPart() then + if Mouse and HighScalabilityLine then + local megaCube = stampData.CurrentParts:FindFirstChild("MegaClusterCube", true) + local terrain = game.Workspace.Terrain + if megaCube then + HighScalabilityLine.Dimensions = 1 + local tempCell = terrain:WorldToCell(megaCube.CFrame.p) + HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z) + return + else + HighScalabilityLine.Dimensions = 1 + local tempCell = terrain:WorldToCell(stampData.CurrentParts.CFrame.p) + HighScalabilityLine.Start = terrain:CellCenterToWorld(tempCell.X, tempCell.Y, tempCell.Z) + return + end + end + end + end + local loadSurfaceTypes + loadSurfaceTypes = function(part, surfaces) + part.TopSurface = surfaces[1] + part.BottomSurface = surfaces[2] + part.LeftSurface = surfaces[3] + part.RightSurface = surfaces[4] + part.FrontSurface = surfaces[5] + part.BackSurface = surfaces[6] + return part + end + local saveSurfaceTypes + saveSurfaceTypes = function(part, myTable) + local tempTable = { } + tempTable[1] = part.TopSurface + tempTable[2] = part.BottomSurface + tempTable[3] = part.LeftSurface + tempTable[4] = part.RightSurface + tempTable[5] = part.FrontSurface + tempTable[6] = part.BackSurface + myTable[part] = tempTable + end + local prepareModel + prepareModel = function(model) + if not model then + return nil + end + local gDesiredTrans = 0.7 + local gStaticTrans = 1 + local clone = model:Clone() + local scripts = { } + local parts = { } + local decals = { } + stampData = { } + stampData.DisabledScripts = { } + stampData.TransparencyTable = { } + stampData.MaterialTable = { } + stampData.CanCollideTable = { } + stampData.AnchoredTable = { } + stampData.ArchivableTable = { } + stampData.DecalTransparencyTable = { } + stampData.SurfaceTypeTable = { } + collectParts(clone, parts, scripts, decals) + if #parts <= 0 then + return nil, "no parts found in modelToStamp" + end + for _, script in pairs(scripts) do + if not script.Disabled then + script.Disabled = true + stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script + end + end + for _, part in pairs(parts) do + stampData.TransparencyTable[part] = part.Transparency + part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency + stampData.MaterialTable[part] = part.Material + part.Material = Enum.Material.Plastic + stampData.CanCollideTable[part] = part.CanCollide + part.CanCollide = false + stampData.AnchoredTable[part] = part.Anchored + part.Anchored = true + stampData.ArchivableTable[part] = part.Archivable + part.Archivable = false + saveSurfaceTypes(part, stampData.SurfaceTypeTable) + local fadeInDelayTime = 0.5 + local transFadeInTime = 0.5 + delay(0, function() + wait(fadeInDelayTime) + local begTime = tick() + local currTime = begTime + while (currTime - begTime) < transFadeInTime and part and part:IsA("BasePart") and part.Transparency > gDesiredTrans do + local newTrans = 1 - (((currTime - begTime) / transFadeInTime) * (gStaticTrans - gDesiredTrans)) + if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then + part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part] + end + wait(0.03) + currTime = tick() + end + if part and part:IsA("BasePart") then + if stampData["TransparencyTable"] and stampData.TransparencyTable[part] then + part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part] + end + end + end) + end + for _, decal in pairs(decals) do + stampData.DecalTransparencyTable[decal] = decal.Transparency + decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency + end + setSeatEnabledStatus(clone, true) + setSeatEnabledStatus(clone, false) + stampData.CurrentParts = clone + if autoAlignToFace(clone) then + stampData.CurrentParts:ResetOrientationToIdentity() + gInitial90DegreeRotations = 0 + else + local ry = gInitial90DegreeRotations * math.pi / 2 + if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then + modelRotate(stampData.CurrentParts, ry) + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame + end + end + local clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if clusterMaterial and clusterMaterial:IsA("Vector3Value") then + clusterMaterial.Value = Vector3.new(clusterMaterial.Value.X, clusterMaterial.Value.Y, (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4) + end + local configFound, targetCFrame + configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) + if configFound then + stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) + end + game.JointsService:SetJoinAfterMoveInstance(stampData.CurrentParts) + return clone, parts + end + local checkTerrainBlockCollisions + checkTerrainBlockCollisions = function(cellPos, checkHighScalabilityStamp) + local cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld + local cellCenter = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z) + local cellBlockingParts = game.Workspace:FindPartsInRegion3(Region3.new(cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100) + local skipThisCell = false + for b = 1, #cellBlockingParts do + if isBlocker(cellBlockingParts[b]) then + skipThisCell = true + break + end + end + if not skipThisCell then + local alreadyPushedUp = { } + for b = 1, #cellBlockingParts do + if cellBlockingParts[b].Parent and not alreadyPushedUp[cellBlockingParts[b].Parent] and cellBlockingParts[b].Parent:FindFirstChild("Humanoid" and cellBlockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid")) then + local blockingPersonTorso = cellBlockingParts[b].Parent:FindFirstChild("Torso") + alreadyPushedUp[cellBlockingParts[b].Parent] = true + if blockingPersonTorso then + local newY = cellCenter.Y + 5 + if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then + blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0) + else + skipThisCell = true + break + end + end + end + end + end + if not skipThisCell then + local canSetCell = true + if checkHighScalabilityStamp then + if allowedStampRegion then + cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z) + if (cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X / 2) or (cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X / 2) or (cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y / 2) or (cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y / 2) or (cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z / 2) or (cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z / 2) then + canSetCell = false + end + end + end + return canSetCell + end + return false + end + local ResolveMegaClusterStamp + ResolveMegaClusterStamp = function(checkHighScalabilityStamp) + local cellSet = false + local cluster = game.Workspace.Terrain + local line = HighScalabilityLine.InternalLine + local cMax = game.Workspace.Terrain.MaxExtents.Max + local cMin = game.Workspace.Terrain.MaxExtents.Min + local clusterMaterial = 1 + local clusterType = 0 + local clusterOrientation = 0 + local autoWedgeClusterParts = false + if stampData.CurrentParts:FindFirstChild("AutoWedge") then + autoWedgeClusterParts = true + end + if stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) then + clusterMaterial = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + if clusterMaterial:IsA("Vector3Value") then + clusterType = clusterMaterial.Value.Y + clusterOrientation = clusterMaterial.Value.Z + clusterMaterial = clusterMaterial.Value.X + elseif clusterMaterial:IsA("IntValue") then + clusterMaterial = clusterMaterial.Value + end + end + if HighScalabilityLine.Adorn.Parent and HighScalabilityLine.Start and ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) then + local startCell = game.Workspace.Terrain:WorldToCell(HighScalabilityLine.Start) + local xInc = { + 0, + 0, + 0 + } + local yInc = { + 0, + 0, + 0 + } + local zInc = { + 0, + 0, + 0 + } + local incrementVect = { + nil, + nil, + nil + } + local stepVect = { + Vector3.new(0, 0, 0), + Vector3.new(0, 0, 0), + Vector3.new(0, 0, 0) + } + local worldAxes = { + Vector3.new(1, 0, 0), + Vector3.new(0, 1, 0), + Vector3.new(0, 0, 1) + } + local lines = { } + if HighScalabilityLine.Dimensions > 1 then + table.insert(lines, HighScalabilityLine.MoreLines[1]) + end + if line and line.magnitude > 0 then + table.insert(lines, line) + end + if HighScalabilityLine.Dimensions > 2 then + table.insert(lines, HighScalabilityLine.MoreLines[2]) + end + for i = 1, #lines do + lines[i] = Vector3.new(math.floor(lines[i].X + 0.5), math.floor(lines[i].Y + 0.5), math.floor(lines[i].Z + 0.5)) + if lines[i].X > 0 then + xInc[i] = 1 + elseif lines[i].X < 0 then + xInc[i] = -1 + end + if lines[i].Y > 0 then + yInc[i] = 1 + elseif lines[i].Y < 0 then + yInc[i] = -1 + end + if lines[i].Z > 0 then + zInc[i] = 1 + elseif lines[i].Z < 0 then + zInc[i] = -1 + end + incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i]) + if incrementVect[i].magnitude < 0.9 then + incrementVect[i] = nil + end + end + if not lines[2] then + lines[2] = Vector3.new(0, 0, 0) + end + if not lines[3] then + lines[3] = Vector3.new(0, 0, 0) + end + local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true) + local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true) + while stepVect[3].magnitude * 4 <= lines[3].magnitude do + local outerStepVectIndex = 1 + while outerStepVectIndex < 4 do + stepVect[2] = Vector3.new(0, 0, 0) + while stepVect[2].magnitude * 4 <= lines[2].magnitude do + local innerStepVectIndex = 1 + while innerStepVectIndex < 4 do + stepVect[1] = Vector3.new(0, 0, 0) + while stepVect[1].magnitude * 4 <= lines[1].magnitude do + local stepVectSum = stepVect[1] + stepVect[2] + stepVect[3] + local cellPos = Vector3int16.new(startCell.X + stepVectSum.X, startCell.Y + stepVectSum.Y, startCell.Z + stepVectSum.Z) + if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then + local okToStampTerrainBlock = checkTerrainBlockCollisions(cellPos, checkHighScalabilityStamp) + if okToStampTerrainBlock then + if waterForceTag then + cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value]) + else + cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterMaterial, clusterType, clusterOrientation) + end + cellSet = true + if autoWedgeClusterParts then + game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1))) + end + end + end + stepVect[1] = stepVect[1] + incrementVect[1] + end + if incrementVect[2] then + while innerStepVectIndex < 4 and worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) == 0 do + innerStepVectIndex = innerStepVectIndex + 1 + end + if innerStepVectIndex < 4 then + stepVect[2] = stepVect[2] + worldAxes[innerStepVectIndex] * worldAxes[innerStepVectIndex]:Dot(incrementVect[2]) + end + innerStepVectIndex = innerStepVectIndex + 1 + else + stepVect[2] = Vector3.new(1, 0, 0) + innerStepVectIndex = 4 + end + if stepVect[2].magnitude * 4 > lines[2].magnitude then + innerStepVectIndex = 4 + end + end + end + if incrementVect[3] then + while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) == 0 do + outerStepVectIndex = outerStepVectIndex + 1 + end + if outerStepVectIndex < 4 then + stepVect[3] = stepVect[3] + worldAxes[outerStepVectIndex] * worldAxes[outerStepVectIndex]:Dot(incrementVect[3]) + end + outerStepVectIndex = outerStepVectIndex + 1 + else + stepVect[3] = Vector3.new(1, 0, 0) + outerStepVectIndex = 4 + end + if stepVect[3].magnitude * 4 > lines[3].magnitude then + outerStepVectIndex = 4 + end + end + end + end + HighScalabilityLine.Start = nil + HighScalabilityLine.Adorn.Parent = nil + if cellSet then + stampData.CurrentParts.Parent = nil +pcall(function() + return game:GetService("ChangeHistoryService"):SetWaypoint("StamperMulti") + end) + end + return cellSet + end + local DoStamperMouseUp + DoStamperMouseUp = function(Mouse) + if not Mouse then + error("Error: RbxStamper.DoStamperMouseUp: Mouse is nil") + return false + end + if not Mouse:IsA("Mouse") then + error("Error: RbxStamper.DoStamperMouseUp: Mouse is of type", Mouse.className, "should be of type Mouse") + return false + end + if not stampData.Dragger then + error("Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil") + return false + end + if not HighScalabilityLine then + return false + end + local checkHighScalabilityStamp + if stampInModel then + local canStamp + local isHSLPart = isMegaClusterPart() + if isHSLPart and HighScalabilityLine and HighScalabilityLine.Start and HighScalabilityLine.InternalLine and HighScalabilityLine.InternalLine.magnitude > 0 then + canStamp = true + checkHighScalabilityStamp = true + else + canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion) + end + if not canStamp then + if stampFailedFunc then + stampFailedFunc() + end + return false + end + end + if unstampableSurface then + flashRedBox() + return false + end + local canStamp + canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion) + if not canStamp then + if stampFailedFunc then + stampFailedFunc() + end + return false + end + local minBB, maxBB + minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) + local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) + if configFound and not HighScalabilityLine.Adorn.Parent then + if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) then + flashRedBox() + return false + end + local blockingParts = game.Workspace:FindPartsInRegion3(Region3.new(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector), stampData.CurrentParts, 100) + for b = 1, #blockingParts do + if isBlocker(blockingParts[b]) then + flashRedBox() + return false + end + end + local alreadyPushedUp = { } + for b = 1, #blockingParts do + if blockingParts[b].Parent and not alreadyPushedUp[blockingParts[b].Parent] and blockingParts[b].Parent:FindFirstChild("Humanoid") and blockingParts[b].Parent:FindFirstChild("Humanoid"):IsA("Humanoid") then + local blockingPersonTorso = blockingParts[b].Parent:FindFirstChild("Torso") + alreadyPushedUp[blockingParts[b].Parent] = true + if blockingPersonTorso then + local newY = maxBB.Y + 3 + if spaceAboveCharacter(blockingPersonTorso, newY, stampData) then + blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + Vector3.new(0, newY - blockingPersonTorso.CFrame.p.Y, 0) + else + flashRedBox() + return false + end + end + end + end + elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) then + resetHighScalabilityLine() + return false + end + if game:FindFirstChild("Players") then + if game.Players["LocalPlayer"] then + if game.Players.LocalPlayer["Character"] then + local localChar = game.Players.LocalPlayer.Character + local stampTracker = localChar:FindFirstChild("StampTracker") + if stampTracker and not stampTracker.Value then + stampTracker.Value = true + end + end + end + end + if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart() then + if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp then + stampData.CurrentParts.Parent = nil + return true + end + end + HighScalabilityLine.Start = nil + HighScalabilityLine.Adorn.Parent = nil + local cluster = game.Workspace.Terrain + if isMegaClusterPart() then + local cellPos + if stampData.CurrentParts:IsA("Model") then + cellPos = cluster:WorldToCell(stampData.CurrentParts:GetModelCFrame().p) + else + cellPos = cluster:WorldToCell(stampData.CurrentParts.CFrame.p) + end + local cMax = game.Workspace.Terrain.MaxExtents.Max + local cMin = game.Workspace.Terrain.MaxExtents.Min + if checkTerrainBlockCollisions(cellPos, false) then + local clusterValues = stampData.CurrentParts:FindFirstChild("ClusterMaterial", true) + local waterForceTag = stampData.CurrentParts:FindFirstChild("WaterForceTag", true) + local waterForceDirectionTag = stampData.CurrentParts:FindFirstChild("WaterForceDirectionTag", true) + if cellPos.X >= cMin.X and cellPos.Y >= cMin.Y and cellPos.Z >= cMin.Z and cellPos.X < cMax.X and cellPos.Y < cMax.Y and cellPos.Z < cMax.Z then + if waterForceTag then + cluster:SetWaterCell(cellPos.X, cellPos.Y, cellPos.Z, Enum.WaterForce[waterForceTag.Value], Enum.WaterDirection[waterForceDirectionTag.Value]) + elseif not clusterValues then + cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, cellInfo.Material, cellInfo.clusterType, gInitial90DegreeRotations % 4) + elseif clusterValues:IsA("Vector3Value") then + cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value.X, clusterValues.Value.Y, clusterValues.Value.Z) + else + cluster:SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0) + end + local autoWedgeClusterParts = false + if stampData.CurrentParts:FindFirstChild("AutoWedge") then + autoWedgeClusterParts = true + end + if autoWedgeClusterParts then + game.Workspace.Terrain:AutowedgeCells(Region3int16.new(Vector3int16.new(cellPos.x - 1, cellPos.y - 1, cellPos.z - 1), Vector3int16.new(cellPos.x + 1, cellPos.y + 1, cellPos.z + 1))) + end + stampData.CurrentParts.Parent = nil +pcall(function() + return game:GetService("ChangeHistoryService"):SetWaypoint("StamperSingle") + end) + return true + end + else + flashRedBox() + return false + end + end + local getPlayer + getPlayer = function() + if game:FindFirstChild("Players") then + if game.Players["LocalPlayer"] then + return game.Players.LocalPlayer + end + end + return nil + end + if stampData.CurrentParts:IsA("Model") or stampData.CurrentParts:IsA("Tool") then + if stampData.CurrentParts:IsA("Model") then + local manualWeldTable = { } + local manualWeldParentTable = { } + saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable) + stampData.CurrentParts:BreakJoints() + stampData.CurrentParts:MakeJoints() + restoreTheWelds(manualWeldTable, manualWeldParentTable) + end + local playerIdTag = stampData.CurrentParts:FindFirstChild("PlayerIdTag") + local playerNameTag = stampData.CurrentParts:FindFirstChild("PlayerNameTag") + if (playerIdTag ~= nil) then + local tempPlayerValue = getPlayer() + if (tempPlayerValue ~= nil) then + playerIdTag.Value = tempPlayerValue.userId + end + end + if (playerNameTag ~= nil) then + if game:FindFirstChild("Players" and game.Players["LocalPlayer"]) then + local tempPlayerValue = game.Players.LocalPlayer + if (tempPlayerValue ~= nil) then + playerNameTag.Value = tempPlayerValue.Name + end + end + end + if not (stampData.CurrentParts:FindFirstChild("RobloxModel") ~= nil) then + local stringTag = Instance.new("BoolValue") + stringTag.Name = "RobloxModel" + stringTag.Parent = stampData.CurrentParts + if not (stampData.CurrentParts:FindFirstChild("RobloxStamper") ~= nil) then + local stringTag2 = Instance.new("BoolValue") + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = stampData.CurrentParts + end + end + else + stampData.CurrentParts:BreakJoints() + if not (stampData.CurrentParts:FindFirstChild("RobloxStamper") ~= nil) then + local stringTag2 = Instance.new("BoolValue") + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = stampData.CurrentParts + end + end + if not createJoints then + game.JointsService:CreateJoinAfterMoveJoints() + end + for part, transparency in pairs(stampData.TransparencyTable) do + part.Transparency = transparency + end + for part, archivable in pairs(stampData.ArchivableTable) do + part.Archivable = archivable + end + for part, material in pairs(stampData.MaterialTable) do + part.Material = material + end + for part, collide in pairs(stampData.CanCollideTable) do + part.CanCollide = collide + end + for part, anchored in pairs(stampData.AnchoredTable) do + part.Anchored = anchored + end + for decal, transparency in pairs(stampData.DecalTransparencyTable) do + decal.Transparency = transparency + end + for part, surfaces in pairs(stampData.SurfaceTypeTable) do + loadSurfaceTypes(part, surfaces) + end + if isMegaClusterPart() then + stampData.CurrentParts.Transparency = 0 + end + setSeatEnabledStatus(stampData.CurrentParts, true) + stampData.TransparencyTable = nil + stampData.ArchivableTable = nil + stampData.MaterialTable = nil + stampData.CanCollideTable = nil + stampData.AnchoredTable = nil + stampData.SurfaceTypeTable = nil + if not (stampData.CurrentParts:FindFirstChild("RobloxModel") ~= nil) then + local stringTag = Instance.new("BoolValue") + stringTag.Name = "RobloxModel" + stringTag.Parent = stampData.CurrentParts + end + if ghostRemovalScript then + ghostRemovalScript.Parent = nil + end + for _, script in pairs(stampData.DisabledScripts) do + script.Disabled = false + end + for _, script in pairs(stampData.DisabledScripts) do + local oldParent = script.Parent + script.Parent = nil + script:Clone().Parent = oldParent + end + stampData.DisabledScripts = nil + stampData.Dragger = nil + stampData.CurrentParts = nil +pcall(function() + return game:GetService("ChangeHistoryService"):SetWaypoint("StampedObject") + end) + return true + end + local pauseStamper + pauseStamper = function() + for i = 1, #mouseCons do + mouseCons[i]:disconnect() + mouseCons[i] = nil + end + mouseCons = { } + if stampData and stampData.CurrentParts then + stampData.CurrentParts.Parent = nil + stampData.CurrentParts:Remove() + end + resetHighScalabilityLine() + return game.JointsService:ClearJoinAfterMoveJoints() + end + local prepareUnjoinableSurfaces + prepareUnjoinableSurfaces = function(modelCFrame, parts, whichSurface) + local AXIS_VECTORS = { + Vector3.new(1, 0, 0), + Vector3.new(0, 1, 0), + Vector3.new(0, 0, 1) + } + local isPositive = 1 + if whichSurface < 0 then + isPositive = isPositive * (-1) + whichSurface = whichSurface * (-1) + end + local surfaceNormal = isPositive * modelCFrame:vectorToWorldSpace(AXIS_VECTORS[whichSurface]) + for i = 1, #parts do + local currPart = parts[i] + local surfaceNormalInLocalCoords = currPart.CFrame:vectorToObjectSpace(surfaceNormal) + if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Y) then + if math.abs(surfaceNormalInLocalCoords.X) > math.abs(surfaceNormalInLocalCoords.Z) then + if surfaceNormalInLocalCoords.X > 0 then + currPart.RightSurface = "Unjoinable" + else + currPart.LeftSurface = "Unjoinable" + end + else + if surfaceNormalInLocalCoords.Z > 0 then + currPart.BackSurface = "Unjoinable" + else + currPart.FrontSurface = "Unjoinable" + end + end + else + if math.abs(surfaceNormalInLocalCoords.Y) > math.abs(surfaceNormalInLocalCoords.Z) then + if surfaceNormalInLocalCoords.Y > 0 then + currPart.TopSurface = "Unjoinable" + else + currPart.BottomSurface = "Unjoinable" + end + else + if surfaceNormalInLocalCoords.Z > 0 then + currPart.BackSurface = "Unjoinable" + else + currPart.FrontSurface = "Unjoinable" + end + end + end + end + end + local resumeStamper + resumeStamper = function() + local clone, parts = prepareModel(modelToStamp) + if not clone or not parts then + return + end + local unjoinableTag = clone:FindFirstChild("UnjoinableFaces", true) + if unjoinableTag then + for unjoinableSurface in string.gmatch(unjoinableTag.Value, "[^,]*") do + if tonumber(unjoinableSurface) then + if clone:IsA("Model") then + prepareUnjoinableSurfaces(clone:GetModelCFrame(), parts, tonumber(unjoinableSurface)) + else + prepareUnjoinableSurfaces(clone.CFrame, parts, tonumber(unjoinableSurface)) + end + end + end + end + stampData.ErrorBox = errorBox + if stampInModel then + clone.Parent = stampInModel + else + clone.Parent = game.Workspace + end + if clone:FindFirstChild("ClusterMaterial", true) then + local clusterMaterial = clone:FindFirstChild("ClusterMaterial", true) + if clusterMaterial:IsA("Vector3Value") then + cellInfo.Material = clusterMaterial.Value.X + cellInfo.clusterType = clusterMaterial.Value.Y + cellInfo.clusterOrientation = clusterMaterial.Value.Z + elseif clusterMaterial:IsA("IntValue") then + cellInfo.Material = clusterMaterial.Value + end + end +pcall(function() + mouseTarget = Mouse.Target + end) + if mouseTarget and not (mouseTarget.Parent:FindFirstChild("RobloxModel") ~= nil) then + game.JointsService:SetJoinAfterMoveTarget(mouseTarget) + else + game.JointsService:SetJoinAfterMoveTarget(nil) + end + game.JointsService:ShowPermissibleJoints() + for _, object in pairs(stampData.DisabledScripts) do + if object.Name == "GhostRemovalScript" then + object.Parent = stampData.CurrentParts + end + end + stampData.Dragger = Instance.new("Dragger") + stampData.Dragger:MouseDown(parts[1], Vector3.new(0, 0, 0, parts)) + stampData.Dragger:MouseUp() + DoStamperMouseMove(Mouse) + table.insert(mouseCons, Mouse.Move:connect(function() + if movingLock or stampUpLock then + return + end + movingLock = true + DoStamperMouseMove(Mouse) + movingLock = false + end)) + table.insert(mouseCons, Mouse.Button1Down:connect(function() + return DoStamperMouseDown(Mouse) + end)) + table.insert(mouseCons, Mouse.Button1Up:connect(function() + stampUpLock = true + while movingLock do + wait() + end + stamped.Value = DoStamperMouseUp(Mouse) + resetHighScalabilityLine() + stampUpLock = false + end)) + stamped.Value = false + end + local resetStamperState + resetStamperState = function(newModelToStamp) + if newModelToStamp then + if not newModelToStamp:IsA("Model") and not newModelToStamp:IsA("BasePart") then + error("resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!") + end + modelToStamp = newModelToStamp + end + pauseStamper() + return resumeStamper() + end + resetStamperState() + control.Stamped = stamped + control.Paused = false + control.LoadNewModel = function(newStampModel) + if newStampModel and not newStampModel:IsA("Model") and not newStampModel:IsA("BasePart") then + error("Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!") + return nil + end + return resetStamperState(newStampModel) + end + control.ReloadModel = function() + return resetStamperState() + end + control.Pause = function() + if not control.Paused then + pauseStamper() + control.Paused = true + else + return print("RbxStamper Warning: Tried to call Control.Pause! when already paused") + end + end + control.Resume = function() + if control.Paused then + resumeStamper() + control.Paused = false + else + return print("RbxStamper Warning: Tried to call Control.Resume! without Pausing First") + end + end + control.ResetRotation = function() end + control.Destroy = function() + for i = 1, #mouseCons do + mouseCons[i]:disconnect() + mouseCons[i] = nil + end + if keyCon ~= nil then + keyCon:disconnect() + end + game.JointsService:ClearJoinAfterMoveJoints() + if adorn ~= nil then + adorn:Destroy() + end + if adornPart ~= nil then + adornPart:Destroy() + end + if errorBox ~= nil then + errorBox:Destroy() + end + if stampData ~= nil then + do + local _obj_0 = stampData.Dragger + if _obj_0 ~= nil then + _obj_0:Destroy() + end + end + end + if stampData ~= nil then + do + local _obj_0 = stampData.CurrentParts + if _obj_0 ~= nil then + _obj_0:Destroy() + end + end + end + if control and control["Stamped"] then + control.Stamped:Destroy() + end + control = nil + end + return control +end +t.Help = function(funcNameOrFunc) + if "GetStampModel" == funcNameOrFunc or t.GetStampModel == funcNameOrFunc then + return "Function GetStampModel. Arguments: assetId, useAssetVersionId. assetId is the asset to load in, define useAssetVersionId as true if assetId is a version id instead of a relative assetId. Side effect: returns a model of the assetId, or a string with error message if something fails" + elseif "SetupStamperDragger" == funcNameOrFunc or t.SetupStamperDragger == funcNameOrFunc then + return "Function SetupStamperDragger. Side Effect: Creates 4x4 stamping mechanism for building out parts quickly. Arguments: ModelToStamp, Mouse, LegalStampCheckFunction. ModelToStamp should be a Model or Part, preferrably loaded from RbxStamper.GetStampModel and should have extents that are multiples of 4. Mouse should be a mouse object (obtained from things such as Tool.OnEquipped), used to drag parts around 'stamp' them out. LegalStampCheckFunction is optional, used as a callback with a table argument (table is full of instances about to be stamped). Function should return either true or false, false stopping the stamp action." + end +end +return t diff --git a/yue/73157242.yue b/yue/73157242.yue index e69de29..541054a 100644 --- a/yue/73157242.yue +++ b/yue/73157242.yue @@ -0,0 +1,2390 @@ +t = {} + +-- waitForChild = (instance, name) -> +-- while not instance\FindFirstChild(name) +-- instance.ChildAdded\wait! +-- end +-- end + +-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0) +-- +-- vectorPos - End point of the line. +-- +-- Return: +-- cellPos - The terrain cell intersection point if there is one, vectorPos if there isn't. +-- hit - Whether there was a plane intersection. Value is true if there was, false if not. +PlaneIntersection = (vectorPos) -> + hit = false + currCamera = game.Workspace.CurrentCamera + local startPos + with currCamera.CoordinateFrame.p + startPos = Vector3.new .X, .Y, .Z + + endPos = Vector3.new vectorPos.X, vectorPos.Y, vectorPos.Z + normal = Vector3.new(0, 1, 0) + p3 = Vector3.new 0, 0, 0 + startEndDot = normal\Dot(endPos - startPos) + cellPos = vectorPos + if startEndDot ~= 0 + t = normal\Dot(p3 - startPos) / startEndDot + if t >= 0 and t <= 1 + intersection = ((endPos - startPos) * t) + startPos + cellPos = game.Workspace.Terrain\WorldToCell(intersection) + hit = true + + cellPos, hit + +-- Purpose: +-- Checks for terrain touched by the mouse hit. +-- Will do a plane intersection if no terrain is touched. +-- +-- mouse - Mouse to check the .hit for. +-- +-- Return: +-- cellPos - Cell position hit. Nil if none. +GetTerrainForMouse = (mouse) -> + -- There was no target, so all it could be is a plane intersection. + -- Check for a plane intersection. If there isn't one then nothing will get hit. + cell = game.Workspace.Terrain\WorldToCellPreferSolid Vector3.new mouse.hit.x, mouse.hit.y, mouse.hit.z + local planeLoc + -- If nothing was hit, do the plane intersection. + if 0 == game.Workspace.Terrain\GetCell(cell.X, cell.Y, cell.Z).Value + cell = nil + planeLoc, hit = PlaneIntersection Vector3.new mouse.hit.x, mouse.hit.y, mouse.hit.z + if hit + cell = planeLoc + cell + +-- setup helper functions +insertBoundingBoxOverlapVector = Vector3.new 0.3, 0.3, 0.3 -- we can still stamp if our character extrudes into the target stamping space by .3 or fewer units + +-- rotates a model by yAngle radians about the global y-axis +rotatePartAndChildren = (part, rotCF, offsetFromOrigin) -> + -- rotate this thing, if it's a part + if part\IsA "BasePart" + part.CFrame = (rotCF * (part.CFrame - offsetFromOrigin)) + offsetFromOrigin + + + -- recursively do the same to all children + partChildren = part\GetChildren! + for c = 1, #partChildren + rotatePartAndChildren partChildren[c], rotCF, offsetFromOrigin + +modelRotate = (model, yAngle) -> + rotCF = CFrame.Angles 0, yAngle, 0 + offsetFromOrigin = model\GetModelCFrame!.p + + rotatePartAndChildren model, rotCF, offsetFromOrigin + +collectParts = (object, baseParts, scripts, decals) -> + if object\IsA "BasePart" + baseParts[] = object + elseif object\IsA "Script" + scripts[] = object + elseif object\IsA "Decal" + decals[] = object + + for _, child in pairs object\GetChildren! + collectParts child, baseParts, scripts, decals + +clusterPartsInRegion = (startVector, endVector) -> + cluster = game.Workspace\FindFirstChild "Terrain" + + startCell = cluster\WorldToCell startVector + endCell = cluster\WorldToCell endVector + + startX = startCell.X + startY = startCell.Y + startZ = startCell.Z + + endX = endCell.X + endY = endCell.Y + endZ = endCell.Z + + if startX < cluster.MaxExtents.Min.X + startX = cluster.MaxExtents.Min.X + + if startY < cluster.MaxExtents.Min.Y + startY = cluster.MaxExtents.Min.Y + + if startZ < cluster.MaxExtents.Min.Z + startZ = cluster.MaxExtents.Min.Z + + if endX > cluster.MaxExtents.Max.X + endX = cluster.MaxExtents.Max.X + + if endY > cluster.MaxExtents.Max.Y + endY = cluster.MaxExtents.Max.Y + + if endZ > cluster.MaxExtents.Max.Z + endZ = cluster.MaxExtents.Max.Z + + + for x = startX, endX + for y = startY, endY + for z = startZ, endZ + if cluster\GetCell(x, y, z).Value > 0 + return true + false + +findSeatsInModel = (parent, seatTable) -> + return if not parent + + if parent.className == "Seat" or parent.className == "VehicleSeat" + table.insert seatTable, parent + + myChildren = parent\GetChildren! + for j = 1, #myChildren + findSeatsInModel myChildren[j], seatTable + + + +setSeatEnabledStatus = (model, isEnabled) -> + seatList = {} + findSeatsInModel model, seatList + + if isEnabled + -- remove any welds called "SeatWeld" in seats + for i = 1, #seatList + nextSeat = seatList[i]\FindFirstChild "SeatWeld" + while nextSeat + nextSeat\Remove! + nextSeat = seatList[i]\FindFirstChild "SeatWeld" + + + else + -- put a weld called "SeatWeld" in every seat + -- this tricks it into thinking there's already someone sitting there, and it won't make you sit XD + for i = 1, #seatList + fakeWeld = Instance.new "Weld" + fakeWeld.Name = "SeatWeld" + fakeWeld.Parent = seatList[i] + + + + +autoAlignToFace = (parts) -> + aatf = parts\FindFirstChild "AutoAlignToFace" + if aatf + aatf.Value + else + false + + + +getClosestAlignedWorldDirection = (aVector3InWorld) -> + xDir = Vector3.new 1, 0, 0 + yDir = Vector3.new 0, 1, 0 + zDir = Vector3.new 0, 0, 1 + xDot = aVector3InWorld.x * xDir.x + aVector3InWorld.y * xDir.y + aVector3InWorld.z * xDir.z + yDot = aVector3InWorld.x * yDir.x + aVector3InWorld.y * yDir.y + aVector3InWorld.z * yDir.z + zDot = aVector3InWorld.x * zDir.x + aVector3InWorld.y * zDir.y + aVector3InWorld.z * zDir.z + + if math.abs(xDot) > math.abs(yDot) and math.abs(xDot) > math.abs zDot + if xDot > 0 + 0 + else + 3 + + elseif math.abs(yDot) > math.abs(xDot) and math.abs(yDot) > math.abs zDot + if yDot > 0 + 1 + else + 4 + + else + if zDot > 0 + 2 + else + 5 + + +positionPartsAtCFrame3 = (aCFrame, currentParts) -> + local insertCFrame + if not currentParts + return currentParts + + if currentParts and (currentParts\IsA"Model" or currentParts\IsA "Tool") + insertCFrame = currentParts\GetModelCFrame! + currentParts\TranslateBy aCFrame.p - insertCFrame.p + else + currentParts.CFrame = aCFrame + + currentParts + + +calcRayHitTime = (rayStart, raySlope, intersectionPlane) -> + if math.abs(raySlope) < 0.01 + return 0 + -- 0 slope --> we just say intersection time is 0, and sidestep this dimension + (intersectionPlane - rayStart) / raySlope + + +modelTargetSurface = (partOrModel, rayStart, rayEnd) -> + if not partOrModel + return 0 + + local modelCFrame, modelSize + if partOrModel\IsA "Model" + modelCFrame = partOrModel\GetModelCFrame! + modelSize = partOrModel\GetModelSize! + else + modelCFrame = partOrModel.CFrame + modelSize = partOrModel.Size + + + mouseRayStart = modelCFrame\pointToObjectSpace rayStart + mouseRayEnd = modelCFrame\pointToObjectSpace rayEnd + mouseSlope = mouseRayEnd - mouseRayStart + + xPositive = 1 + yPositive = 1 + zPositive = 1 + if mouseSlope.X > 0 + xPositive = -1 + + if mouseSlope.Y > 0 + yPositive = -1 + + if mouseSlope.Z > 0 + zPositive = -1 + + + -- find which surface the transformed mouse ray hits (using modelSize): + xHitTime = calcRayHitTime mouseRayStart.X, mouseSlope.X, modelSize.X / 2 * xPositive + yHitTime = calcRayHitTime mouseRayStart.Y, mouseSlope.Y, modelSize.Y / 2 * yPositive + zHitTime = calcRayHitTime mouseRayStart.Z, mouseSlope.Z, modelSize.Z / 2 * zPositive + + hitFace = 0 + + --if xHitTime >= 0 and yHitTime >= 0 and zHitTime >= 0 + if xHitTime > yHitTime + if xHitTime > zHitTime + -- xFace is hit + hitFace = 1 * xPositive + else + -- zFace is hit + hitFace = 3 * zPositive + + else + if yHitTime > zHitTime + -- yFace is hit + hitFace = 2 * yPositive + else + -- zFace is hit + hitFace = 3 * zPositive + + hitFace + + +getBoundingBox2 = (partOrModel) -> + -- for models, the bounding box is defined as the minimum and maximum individual part bounding boxes + -- relative to the first part's coordinate frame. + minVec = Vector3.new math.huge, math.huge, math.huge + maxVec = Vector3.new -math.huge, -math.huge, -math.huge + + if partOrModel\IsA "Terrain" + minVec = Vector3.new -2, -2, -2 + maxVec = Vector3.new 2, 2, 2 + elseif partOrModel\IsA "BasePart" + minVec = -0.5 * partOrModel.Size + maxVec = -minVec + else + maxVec = partOrModel\GetModelSize! * 0.5 + minVec = -maxVec + + + -- Adjust bounding box to reflect what the model or part author wants in terms of justification + justifyValue = partOrModel\FindFirstChild "Justification" + if justifyValue? + -- find the multiple of 4 that contains the model + justify = justifyValue.Value + two = Vector3.new(2, 2, 2) + actualBox = maxVec - minVec - Vector3.new 0.01, 0.01, 0.01 + containingGridBox = Vector3.new( + 4 * math.ceil(actualBox.x / 4), + 4 * math.ceil(actualBox.y / 4), + 4 * math.ceil actualBox.z / 4 + ) + adjustment = containingGridBox - actualBox + minVec -= 0.5 * adjustment * justify + maxVec += 0.5 * adjustment * (two - justify) + + minVec, maxVec + + +getBoundingBoxInWorldCoordinates = (partOrModel) -> + minVec = Vector3.new math.huge, math.huge, math.huge + maxVec = Vector3.new -math.huge, -math.huge, -math.huge + + if partOrModel\IsA"BasePart" and not partOrModel\IsA "Terrain" + vec1 = partOrModel.CFrame\pointToWorldSpace -0.5 * partOrModel.Size + vec2 = partOrModel.CFrame\pointToWorldSpace 0.5 * partOrModel.Size + minVec = Vector3.new math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min vec1.Z, vec2.Z + maxVec = Vector3.new math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max vec1.Z, vec2.Z + elseif not partOrModel\IsA "Terrain" + -- we shouldn't have to deal with this case + --minVec = Vector3.new(-2, -2, -2) + --maxVec = Vector3.new(2, 2, 2) + -- else + vec1 = partOrModel\GetModelCFrame!\pointToWorldSpace -0.5 * partOrModel\GetModelSize! + vec2 = partOrModel\GetModelCFrame!\pointToWorldSpace 0.5 * partOrModel\GetModelSize! + minVec = Vector3.new math.min(vec1.X, vec2.X), math.min(vec1.Y, vec2.Y), math.min vec1.Z, vec2.Z + maxVec = Vector3.new math.max(vec1.X, vec2.X), math.max(vec1.Y, vec2.Y), math.max vec1.Z, vec2.Z + + minVec, maxVec + +getTargetPartBoundingBox = (targetPart) -> + getBoundingBox2 if targetPart.Parent\FindFirstChild"RobloxModel"? + targetPart.Parent + else + targetPart + +getMouseTargetCFrame = (targetPart) -> with targetPart + return if .Parent\FindFirstChild"RobloxModel"? + if .Parent\IsA "Tool" + .Parent.Handle.CFrame + else + .Parent\GetModelCFrame! + else + .CFrame + + +isBlocker = (part) -> -- returns whether or not we want to cancel the stamp because we're blocked by this part + if not part + return false + + if not part.Parent + return false + + if part\FindFirstChild "Humanoid" + return false + + if part\FindFirstChild "RobloxStamper" or part\FindFirstChild "RobloxModel" + return true + + if part\IsA"Part" and not part.CanCollide + return false + + if part == game.Lighting + return false + + isBlocker part.Parent + +-- helper function to determine if a character can be pushed upwards by a certain amount +-- character is 5 studs tall, we'll check a 1.5 x 1.5 x 4.5 box around char, with center .5 studs below torsocenter +spaceAboveCharacter = (charTorso, newTorsoY, stampData) -> + partsAboveChar = game.Workspace\FindPartsInRegion3( + Region3.new( + Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), + Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75) + ), + charTorso.Parent, + 100 + ) + + for j = 1, #partsAboveChar + if partsAboveChar[j].CanCollide and not partsAboveChar[j]\IsDescendantOf stampData.CurrentParts + return false + + if clusterPartsInRegion( + Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) - Vector3.new(0.75, 2.75, 0.75), + Vector3.new(charTorso.Position.X, newTorsoY, charTorso.Position.Z) + Vector3.new(0.75, 1.75, 0.75) + ) + + return false + + true + + +findConfigAtMouseTarget = (Mouse, stampData) -> + -- *Critical Assumption* : + -- This function assumes the target CF axes are orthogonal with the target bounding box faces + -- And, it assumes the insert CF axes are orthongonal with the insert bounding box faces + -- Therefore, insertion will not work with angled faces on wedges or other "non-block" parts, nor + -- will it work for parts in a model that are not orthogonally aligned with the model's CF. + + return if not Mouse + + -- This can happen sometimes, return if so + return if not stampData + error "findConfigAtMouseTarget: stampData is nil" + + return if not stampData["CurrentParts"] + + grid = 4.0 + admissibleConfig = false + targetConfig = CFrame.new 0, 0, 0 + + minBB, maxBB = getBoundingBox2 stampData.CurrentParts + diagBB = maxBB - minBB + + local insertCFrame + if stampData.CurrentParts\IsA"Model" or stampData.CurrentParts\IsA "Tool" + insertCFrame = stampData.CurrentParts\GetModelCFrame! + else + insertCFrame = stampData.CurrentParts.CFrame + + + if Mouse + if stampData.CurrentParts\IsA "Tool" + Mouse.TargetFilter = stampData.CurrentParts.Handle + else + Mouse.TargetFilter = stampData.CurrentParts + + hitPlane = false + local targetPart + success = try + targetPart = Mouse.Target + + + if not success -- or targetPart == nil + return admissibleConfig, targetConfig + + + mouseHitInWorld = Vector3.new 0, 0, 0 + if Mouse + mouseHitInWorld = Vector3.new Mouse.Hit.x, Mouse.Hit.y, Mouse.Hit.z + + + local cellPos + + -- Nothing was hit, so check for the default plane. + if nil == targetPart + cellPos = GetTerrainForMouse Mouse + if nil == cellPos + hitPlane = false + return admissibleConfig, targetConfig + else + targetPart = game.Workspace.Terrain + hitPlane = true + -- Take into account error that will occur. + cellPos = Vector3.new(cellPos.X - 1, cellPos.Y, cellPos.Z) + mouseHitInWorld = game.Workspace.Terrain\CellCenterToWorld(cellPos.x, cellPos.y, cellPos.z) + + + + -- test mouse hit location + local minBBTarget, maxBBTarget = getTargetPartBoundingBox targetPart + diagBBTarget = maxBBTarget - minBBTarget + targetCFrame = getMouseTargetCFrame targetPart + + if targetPart\IsA "Terrain" + if not cluster + global cluster = game.Workspace\FindFirstChild "Terrain" + + cellID = cluster\WorldToCellPreferSolid mouseHitInWorld + if hitPlane + cellID = cellPos + + + targetCFrame = CFrame.new game.Workspace.Terrain\CellCenterToWorld cellID.x, cellID.y, cellID.z + + + mouseHitInTarget = targetCFrame\pointToObjectSpace mouseHitInWorld + targetVectorInWorld = Vector3.new 0, 0, 0 + if Mouse + -- DON'T WANT THIS IN TERMS OF THE MODEL CFRAME! (.TargetSurface is in terms of the part CFrame, so this would break, right? [HotThoth]) + -- (ideally, we would want to make the Mouse.TargetSurface a model-targetsurface instead, but for testing will be using the converse) + --targetVectorInWorld = targetCFrame\vectorToWorldSpace(Vector3.FromNormalId(Mouse.TargetSurface)) + targetVectorInWorld = targetPart.CFrame\vectorToWorldSpace Vector3.FromNormalId Mouse.TargetSurface -- better, but model cframe would be best + --[[if targetPart.Parent\IsA("Model") + hitFace = modelTargetSurface(targetPart.Parent, Mouse.Hit.p, game.Workspace.CurrentCamera.CoordinateFrame.p) -- best, if you get it right + WORLD_AXES = {Vector3.new(1, 0, 0), Vector3.new(0, 1, 0), Vector3.new(0, 0, 1)} + if hitFace > 0 + targetVectorInWorld = targetCFrame\vectorToWorldSpace(WORLD_AXES[hitFace]) + elseif hitFace < 0 + targetVectorInWorld = targetCFrame\vectorToWorldSpace(-WORLD_AXES[-hitFace]) + + end]] + + + local targetRefPointInTarget, insertRefPointInInsert + local clampToSurface + + if getClosestAlignedWorldDirection(targetVectorInWorld) == 0 + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new 1, -1, 1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new -1, -1, 1 + clampToSurface = Vector3.new 0, 1, 1 + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 3 + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new -1, -1, -1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new 1, -1, -1 + clampToSurface = Vector3.new 0, 1, 1 + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 1 + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new -1, 1, 1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new -1, -1, 1 + clampToSurface = Vector3.new 1, 0, 1 + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 4 + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new -1, -1, 1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new -1, 1, 1 + clampToSurface = Vector3.new 1, 0, 1 + elseif getClosestAlignedWorldDirection(targetVectorInWorld) == 2 + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new -1, -1, 1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new -1, -1, -1 + clampToSurface = Vector3.new 1, 1, 0 + else + targetRefPointInTarget = targetCFrame\vectorToObjectSpace Vector3.new 1, -1, -1 + insertRefPointInInsert = insertCFrame\vectorToObjectSpace Vector3.new 1, -1, 1 + clampToSurface = Vector3.new 1, 1, 0 + + targetRefPointInTarget *= (0.5 * diagBBTarget) + 0.5 * (maxBBTarget + minBBTarget) + insertRefPointInInsert *= (0.5 * diagBB) + 0.5 * (maxBB + minBB) + + -- To Do: For cases that are not aligned to the world grid, account for the minimal rotation + -- needed to bring the Insert part(s) into alignment with the Target Part + -- Apply the rotation here + + delta = mouseHitInTarget - targetRefPointInTarget + deltaClamped = Vector3.new( + grid * math.modf(delta.x / grid), + grid * math.modf(delta.y / grid), + grid * math.modf delta.z / grid + ) + deltaClamped *= clampToSurface + targetTouchInTarget = deltaClamped + targetRefPointInTarget + + TargetTouchRelToWorld = targetCFrame\pointToWorldSpace targetTouchInTarget + InsertTouchInWorld = insertCFrame\vectorToWorldSpace insertRefPointInInsert + posInsertOriginInWorld = TargetTouchRelToWorld - InsertTouchInWorld + + local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = insertCFrame\components! + targetConfig = CFrame.new( + posInsertOriginInWorld.x, + posInsertOriginInWorld.y, + posInsertOriginInWorld.z, + R00, + R01, + R02, + R10, + R11, + R12, + R20, + R21, + R22 + ) + admissibleConfig = true + + admissibleConfig, targetConfig, getClosestAlignedWorldDirection(targetVectorInWorld) + +truncateToCircleEighth = (bigValue, littleValue) -> + big = math.abs bigValue + little = math.abs littleValue + hypotenuse = math.sqrt big * big + little * little + frac = little / hypotenuse + + bigSign = 1 + littleSign = 1 + if bigValue < 0 + bigSign = -1 + + if littleValue < 0 + littleSign = -1 + + if frac > 0.382683432 + -- between 22.5 and 45 degrees, so truncate to 45-degree tilt + return 0.707106781 * hypotenuse * bigSign, 0.707106781 * hypotenuse * littleSign + else + -- between 0 and 22.5 degrees, so truncate to 0-degree tilt + return hypotenuse * bigSign, 0 + + + +saveTheWelds = (object, manualWeldTable, manualWeldParentTable) -> + if object\IsA"ManualWeld" or object\IsA "Rotate" + table.insert manualWeldTable, object + table.insert manualWeldParentTable, object.Parent + else + children = object\GetChildren! + for i = 1, #children + saveTheWelds children[i], manualWeldTable, manualWeldParentTable + + +restoreTheWelds = (manualWeldTable, manualWeldParentTable) -> + for i = 1, #manualWeldTable + manualWeldTable[i].Parent = manualWeldParentTable[i] + + +t.CanEditRegion = (partOrModel, EditRegion) -> -- todo: use model and stamper metadata + if not EditRegion + return true, false + + + local minBB, maxBB = getBoundingBoxInWorldCoordinates(partOrModel) + + if minBB.X < EditRegion.CFrame.p.X - EditRegion.Size.X / 2 or + minBB.Y < EditRegion.CFrame.p.Y - EditRegion.Size.Y / 2 or + minBB.Z < EditRegion.CFrame.p.Z - EditRegion.Size.Z / 2 + + return false, false + + + if maxBB.X > EditRegion.CFrame.p.X + EditRegion.Size.X / 2 or + maxBB.Y > EditRegion.CFrame.p.Y + EditRegion.Size.Y / 2 or + maxBB.Z > EditRegion.CFrame.p.Z + EditRegion.Size.Z / 2 + + return false, false + + true, false + +t.GetStampModel = (assetId, terrainShape, useAssetVersionId) -> + if assetId == 0 + return nil, "No Asset" + + if assetId < 0 + return nil, "Negative Asset" + + UnlockInstances = (object) -> + if object\IsA "BasePart" + object.Locked = false + + for _, child in pairs object\GetChildren! + UnlockInstances child + + getClosestColorToTerrainMaterial = (terrainValue) -> + BrickColor.new switch terrainValue + when 1 then "Bright green" + when 2 then "Bright yellow" + when 3 then "Bright red" + when 4 then "Sand red" + when 5 then "Black" + when 6 then "Dark stone grey" + when 7 then "Sand blue" + when 8 then "Deep orange" + when 9 then "Dark orange" + when 10 then "Reddish brown" + when 11 then "Light orange" + when 12 then "Light stone grey" + when 13 then "Sand green" + when 14 then "Medium stone grey" + when 15 then "Really red" + when 16 then "Really blue" + when 17 then "Bright blue" + else "Bright green" + + setupFakeTerrainPart = (cellMat, cellType, cellOrient) -> + local newTerrainPiece + if cellType == 1 or cellType == 4 + newTerrainPiece = Instance.new "WedgePart" + newTerrainPiece.formFactor = "Custom" + elseif cellType == 2 + newTerrainPiece = Instance.new "CornerWedgePart" + else + newTerrainPiece = Instance.new "Part" + newTerrainPiece.formFactor = "Custom" + + newTerrainPiece.Name = "MegaClusterCube" + newTerrainPiece.Size = Vector3.new 4, 4, 4 + newTerrainPiece.BottomSurface = "Smooth" + newTerrainPiece.TopSurface = "Smooth" + + -- can add decals or textures here if feeling particularly adventurous... for now, can make a table of look-up colors + newTerrainPiece.BrickColor = getClosestColorToTerrainMaterial cellMat + + sideways = 0 + flipped = math.pi + if cellType == 4 + sideways = -math.pi / 2 + + if cellType == 2 or cellType == 3 + flipped = 0 + + newTerrainPiece.CFrame = CFrame.Angles 0, math.pi / 2 * cellOrient + flipped, sideways + + if cellType == 3 + inverseCornerWedgeMesh = Instance.new "SpecialMesh" + inverseCornerWedgeMesh.MeshType = "FileMesh" + inverseCornerWedgeMesh.MeshId = "http://www.roblox.com/asset?id=66832495" + inverseCornerWedgeMesh.Scale = Vector3.new 2, 2, 2 + inverseCornerWedgeMesh.Parent = newTerrainPiece + + materialTag = Instance.new "Vector3Value" + materialTag.Value = Vector3.new cellMat, cellType, cellOrient + materialTag.Name = "ClusterMaterial" + materialTag.Parent = newTerrainPiece + + return newTerrainPiece + + + -- This call will cause a "wait" until the data comes back + -- below we wait a max of 8 seconds before deciding to bail out on loading + local root + local loader + loading = true + if useAssetVersionId + loader = coroutine.create -> + root = game\GetService"InsertService"\LoadAssetVersion assetId + loading = false + + coroutine.resume loader + else + loader = coroutine.create -> + root = game\GetService"InsertService"\LoadAsset assetId + loading = false + + coroutine.resume loader + + + lastGameTime = 0 + totalTime = 0 + maxWait = 8 + while loading and totalTime < maxWait + lastGameTime = tick! + wait 1 + totalTime += tick! - lastGameTime + + loading = false + + if totalTime >= maxWait + return nil, "Load Time Fail" + + + if root == nil + return nil, "Load Asset Fail" + + + if not root\IsA "Model" + return nil, "Load Type Fail" + + + instances = root\GetChildren! + if #instances == 0 + return nil, "Empty Model Fail" + + + --Unlock all parts that are inserted, to make sure they are editable + UnlockInstances(root) + + --Continue the insert process + root = root\GetChildren![1] + + --Examine the contents and decide what it looks like + for _, instance in pairs instances + if instance\IsA "Team" + instance.Parent = game\GetService "Teams" + elseif instance\IsA "Sky" + lightingService = game\GetService "Lighting" + for _, child in pairs lightingService\GetChildren! + if child\IsA "Sky" + child\Remove! + + + instance.Parent = lightingService + return + + + + -- ...and tag all inserted models for subsequent origin identification + -- if no RobloxModel tag already exists, then add it. + if not root\FindFirstChild"RobloxModel"? + stringTag = Instance.new "BoolValue" + stringTag.Name = "RobloxModel" + stringTag.Parent = root + + if not root\FindFirstChild"RobloxStamper"? + stringTag2 = Instance.new "BoolValue" + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = root + + + + if terrainShape + if root.Name == "MegaClusterCube" + if terrainShape == 6 -- insert an autowedging tag + autowedgeTag = Instance.new "BoolValue" + autowedgeTag.Name = "AutoWedge" + autowedgeTag.Parent = root + else + clusterTag = root\FindFirstChild "ClusterMaterial" + if clusterTag + if clusterTag\IsA "Vector3Value" + root = setupFakeTerrainPart(clusterTag.Value.X, terrainShape, clusterTag.Value.Z) + else + root = setupFakeTerrainPart(clusterTag.Value, terrainShape, 0) + + else + root = setupFakeTerrainPart(1, terrainShape, 0) + + + + + + return root + + +t.SetupStamperDragger = (modelToStamp, Mouse, StampInModel, AllowedStampRegion, StampFailedFunc) -> + if not modelToStamp + error "SetupStamperDragger: modelToStamp (first arg) is nil! Should be a stamper model" + return nil + + if not modelToStamp\IsA"Model" and not modelToStamp\IsA "BasePart" + error "SetupStamperDragger: modelToStamp (first arg) is neither a Model or Part!" + return nil + + if not Mouse + error "SetupStamperDragger: Mouse (second arg) is nil! Should be a mouse object" + return nil + + if not Mouse\IsA "Mouse" + error "SetupStamperDragger: Mouse (second arg) is not of type Mouse!" + return nil + + + local stampInModel + local allowedStampRegion + local stampFailedFunc + if StampInModel + if not StampInModel\IsA "Model" + error "SetupStamperDragger: StampInModel (optional third arg) is not of type 'Model'" + return nil + + if not AllowedStampRegion + error "SetupStamperDragger: AllowedStampRegion (optional fourth arg) is nil when StampInModel (optional third arg) is defined" + return nil + + stampFailedFunc = StampFailedFunc + stampInModel = StampInModel + allowedStampRegion = AllowedStampRegion + + + -- Init all state variables + gInitial90DegreeRotations = 0 + local stampData + local mouseTarget + + errorBox = Instance.new "SelectionBox" + errorBox.Color = BrickColor.new "Bright red" + errorBox.Transparency = 0 + errorBox.Archivable = false + + -- for megacluster MEGA STAMPING + adornPart = Instance.new "Part" + adornPart.Parent = nil + adornPart.formFactor = "Custom" + adornPart.Size = Vector3.new(4, 4, 4) + adornPart.CFrame = CFrame.new! + adornPart.Archivable = false + + adorn = Instance.new "SelectionBox" + adorn.Color = BrickColor.new "Toothpaste" + adorn.Adornee = adornPart + adorn.Visible = true + adorn.Transparency = 0 + adorn.Name = "HighScalabilityStamperLine" + adorn.Archivable = false + + HighScalabilityLine = {} + HighScalabilityLine.Start = nil + HighScalabilityLine.End = nil + HighScalabilityLine.Adorn = adorn + HighScalabilityLine.AdornPart = adornPart + HighScalabilityLine.InternalLine = nil + HighScalabilityLine.NewHint = true + + HighScalabilityLine.MorePoints = { nil, nil } + HighScalabilityLine.MoreLines = { nil, nil } + HighScalabilityLine.Dimensions = 1 + + control = {} + movingLock = false + stampUpLock = false + unstampableSurface = false + mouseCons = {} + local keyCon + + stamped = Instance.new "BoolValue" + stamped.Archivable = false + stamped.Value = false + + lastTarget = {} + lastTarget.TerrainOrientation = 0 + lastTarget.CFrame = 0 + + cellInfo = {} + cellInfo.Material = 1 + cellInfo.clusterType = 0 + cellInfo.clusterOrientation = 0 + + isMegaClusterPart = -> + if not stampData + return false + + if not stampData.CurrentParts + return false + + stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) or + (stampData.CurrentParts.Name == "MegaClusterCube") + + DoHighScalabilityRegionSelect = -> + megaCube = stampData.CurrentParts\FindFirstChild "MegaClusterCube" + if not megaCube + if not stampData.CurrentParts.Name == "MegaClusterCube" + return + else + megaCube = stampData.CurrentParts + + + + HighScalabilityLine.End = megaCube.CFrame.p + local line + line2 = Vector3.new 0, 0, 0 + line3 = Vector3.new 0, 0, 0 + + if HighScalabilityLine.Dimensions == 1 + -- extract the line from these positions and limit to a 2D plane made from 2 of the world axes + -- then use dominating axis to limit line to be at 45-degree intervals + -- will use this internal representation of the line for the actual stamping + line = (HighScalabilityLine.End - HighScalabilityLine.Start) + + if math.abs(line.X) < math.abs(line.Y) + if math.abs(line.X) < math.abs(line.Z) + -- limit to Y/Z plane, domination unknown + local newY, newZ + if math.abs(line.Y) > math.abs(line.Z) + newY, newZ = truncateToCircleEighth(line.Y, line.Z) + else + newZ, newY = truncateToCircleEighth(line.Z, line.Y) + + line = Vector3.new(0, newY, newZ) + else + -- limit to X/Y plane, with Y dominating + local newY, newX = truncateToCircleEighth(line.Y, line.X) + line = Vector3.new(newX, newY, 0) + + else + if math.abs(line.Y) < math.abs(line.Z) + -- limit to X/Z plane, domination unknown + local newX, newZ + if math.abs(line.X) > math.abs(line.Z) + newX, newZ = truncateToCircleEighth(line.X, line.Z) + else + newZ, newX = truncateToCircleEighth(line.Z, line.X) + + line = Vector3.new(newX, 0, newZ) + else + -- limit to X/Y plane, with X dominating + local newX, newY = truncateToCircleEighth(line.X, line.Y) + line = Vector3.new(newX, newY, 0) + + + HighScalabilityLine.InternalLine = line + elseif HighScalabilityLine.Dimensions == 2 + line = HighScalabilityLine.MoreLines[1] + line2 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[1] + + -- take out any component of line2 along line1, so you get perpendicular to line1 component + line2 -= line.unit * line.unit\Dot(line2) + + tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) + + -- then zero out whichever is the smaller component + yAxis = tempCFrame\vectorToWorldSpace(Vector3.new(0, 1, 0)) + xAxis = tempCFrame\vectorToWorldSpace(Vector3.new(1, 0, 0)) + + xComp = xAxis\Dot(line2) + yComp = yAxis\Dot(line2) + + if math.abs(yComp) > math.abs(xComp) + line2 -= xAxis * xComp + else + line2 -= yAxis * yComp + + + HighScalabilityLine.InternalLine = line2 + elseif HighScalabilityLine.Dimensions == 3 + line = HighScalabilityLine.MoreLines[1] + line2 = HighScalabilityLine.MoreLines[2] + line3 = HighScalabilityLine.End - HighScalabilityLine.MorePoints[2] + + -- zero out all components of previous lines + line3 -= line.unit * line.unit\Dot(line3) + line3 -= line2.unit * line2.unit\Dot(line3) + + HighScalabilityLine.InternalLine = line3 + + + -- resize the "line" graphic to be the correct size and orientation + tempCFrame = CFrame.new(HighScalabilityLine.Start, HighScalabilityLine.Start + line) + + if HighScalabilityLine.Dimensions == 1 -- faster calculation for line + HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, line.magnitude + 4) + HighScalabilityLine.AdornPart.CFrame = tempCFrame + + tempCFrame\vectorToWorldSpace Vector3.new(2, 2, 2) - HighScalabilityLine.AdornPart.Size / 2 + else + boxSize = tempCFrame\vectorToObjectSpace(line + line2 + line3) + HighScalabilityLine.AdornPart.Size = Vector3.new(4, 4, 4) + + Vector3.new(math.abs(boxSize.X), math.abs(boxSize.Y), math.abs(boxSize.Z)) + HighScalabilityLine.AdornPart.CFrame = tempCFrame + tempCFrame\vectorToWorldSpace boxSize / 2 + + + -- make player able to see this ish + + local gui + if game.Players["LocalPlayer"] + gui = game.Players.LocalPlayer\FindFirstChild "PlayerGui" + if gui and gui\IsA "PlayerGui" + if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or + HighScalabilityLine.Dimensions > 1 + + -- don't show if mouse hasn't moved enough + HighScalabilityLine.Adorn.Parent = gui + + + if not gui? -- we are in studio + gui = game\GetService "CoreGui" + if (HighScalabilityLine.Dimensions == 1 and line.magnitude > 3) or HighScalabilityLine.Dimensions > 1 -- don't show if mouse hasn't moved enough + HighScalabilityLine.Adorn.Parent = gui + + + + + DoStamperMouseMove = (Mouse) -> + if not Mouse + error "Error: RbxStamper.DoStamperMouseMove: Mouse is nil" + return + + if not Mouse\IsA "Mouse" + error("Error: RbxStamper.DoStamperMouseMove: Mouse is of type", Mouse.className, "should be of type Mouse") + return + + + -- There wasn't a target (no part or terrain), so check for plane intersection. + if not Mouse.Target + cellPos = GetTerrainForMouse(Mouse) + if nil == cellPos + return + + + + if not stampData + return + + + -- don't move with dragger - will move in one step on mouse down + -- draw ghost at acceptable positions + configFound, targetCFrame, targetSurface = findConfigAtMouseTarget(Mouse, stampData) + if not configFound + error "RbxStamper.DoStamperMouseMove No configFound, returning" + return + + + numRotations = 0 -- update this according to how many rotations you need to get it to target surface + if autoAlignToFace(stampData.CurrentParts) and targetSurface ~= 1 and targetSurface ~= 4 -- pre-rotate the flag or portrait so it's aligned correctly + if targetSurface == 3 + numRotations = 0 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 0 + numRotations = 2 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 5 + numRotations = 3 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + elseif targetSurface == 2 + numRotations = 1 - gInitial90DegreeRotations + autoAlignToFace(stampData.CurrentParts) + + + + ry = math.pi / 2 + gInitial90DegreeRotations += numRotations + if stampData.CurrentParts\IsA"Model" or stampData.CurrentParts\IsA "Tool" + --stampData.CurrentParts\Rotate(0, ry*numRotations, 0) + modelRotate(stampData.CurrentParts, ry * numRotations) + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry * numRotations, 0) + * stampData.CurrentParts.CFrame + + + -- CODE TO CHECK FOR DRAGGING GHOST PART INTO A COLLIDING STATE + local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) + + -- need to offset by distance to be dragged + local currModelCFrame + if stampData.CurrentParts\IsA "Model" + currModelCFrame = stampData.CurrentParts\GetModelCFrame! + else + currModelCFrame = stampData.CurrentParts.CFrame + + + minBB += targetCFrame.p - currModelCFrame.p + maxBB += targetCFrame.p - currModelCFrame.p + + -- don't drag into terrain + if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) + if lastTarget.CFrame + if stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + theClusterMaterial = stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + if theClusterMaterial\IsA "Vector3Value" + stampClusterMaterial = stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + if stampClusterMaterial + stampClusterMaterial = clusterMat + + + + + return + + + -- if we are stamping a terrain part, make sure it goes on the grid! Otherwise preview block could be placed off grid, but stamped on grid + if isMegaClusterPart! + cellToStamp = game.Workspace.Terrain\WorldToCell targetCFrame.p + newCFramePosition = game.Workspace.Terrain\CellCenterToWorld cellToStamp.X, cellToStamp.Y, cellToStamp.Z + local _, _, _, R00, R01, R02, R10, R11, R12, R20, R21, R22 = targetCFrame\components! + targetCFrame = CFrame.new( + newCFramePosition.X, + newCFramePosition.Y, + newCFramePosition.Z, + R00, + R01, + R02, + R10, + R11, + R12, + R20, + R21, + R22 + ) + + + positionPartsAtCFrame3 targetCFrame, stampData.CurrentParts + lastTarget.CFrame = targetCFrame -- successful positioning, so update 'dat cframe + if stampData.CurrentParts\FindFirstChild "ClusterMaterial", true + clusterMat = stampData.CurrentParts\FindFirstChild "ClusterMaterial", true + if clusterMat\IsA "Vector3Value" + lastTarget.TerrainOrientation = clusterMat.Value.Z + + + + -- auto break joints code + if Mouse and Mouse.Target and Mouse.Target.Parent + modelInfo = Mouse.Target\FindFirstChild "RobloxModel" + if not modelInfo + modelInfo = Mouse.Target.Parent\FindFirstChild "RobloxModel" + + + myModelInfo = stampData.CurrentParts\FindFirstChild "UnstampableFaces" + + --if (modelInfo and modelInfo.Parent\FindFirstChild("UnstampableFaces")) or (modelInfo and myModelInfo) then -- need better targetSurface calcs + if true + breakingFaces = "" + myBreakingFaces = "" + if modelInfo and modelInfo.Parent\FindFirstChild "UnstampableFaces" + breakingFaces = modelInfo.Parent.UnstampableFaces.Value + + if myModelInfo + myBreakingFaces = myModelInfo.Value + + hitFace = 0 + + if modelInfo + hitFace = modelTargetSurface( + modelInfo.Parent, + game.Workspace.CurrentCamera.CoordinateFrame.p, + Mouse.Hit.p + ) + + -- are we stamping TO an unstampable surface? + for bf in string.gmatch breakingFaces, "[^,]+" + if hitFace == tonumber bf + -- return before we hit the JointsService code below! + unstampableSurface = true + game.JointsService\ClearJoinAfterMoveJoints! -- clear the JointsService cache + return + + -- now we have to cast the ray back in the other direction to find the surface we're stamping FROM + hitFace = modelTargetSurface( + stampData.CurrentParts, + Mouse.Hit.p, + game.Workspace.CurrentCamera.CoordinateFrame.p + ) + + -- are we stamping WITH an unstampable surface? + for bf in string.gmatch myBreakingFaces, "[^,]+" + if hitFace == tonumber bf + unstampableSurface = true + game.JointsService\ClearJoinAfterMoveJoints! -- clear the JointsService cache + return + + -- just need to match breakingFace against targetSurface using rotation supplied by modelCFrame + -- targetSurface: 1 is top, 4 is bottom, + + -- to show joints during the mouse move + unstampableSurface = false + game.JointsService\SetJoinAfterMoveInstance stampData.CurrentParts + + -- most common mouse inactive error occurs here, so check mouse active one more time in a pcall + if (not try + if Mouse and Mouse.Target and not Mouse.Target.Parent\FindFirstChild"RobloxModel"? + return + else + return + -- ? + ) + + error "Error: RbxStamper.DoStamperMouseMove Mouse is nil on second check" + game.JointsService\ClearJoinAfterMoveJoints! + Mouse = nil + return + + + if Mouse and Mouse.Target and not Mouse.Target.Parent\FindFirstChild"RobloxModel"? + game.JointsService\SetJoinAfterMoveTarget Mouse.Target + else + game.JointsService\SetJoinAfterMoveTarget nil + + game.JointsService\ShowPermissibleJoints! + + -- here we allow for a line of high-scalability parts + if isMegaClusterPart! and HighScalabilityLine and HighScalabilityLine.Start + DoHighScalabilityRegionSelect! + + + + setupKeyListener = (key, Mouse) -> + return if control and control["Paused"] + -- don't do this if we have no stamp + + key = string.lower(key) + if key == "r" and not autoAlignToFace(stampData.CurrentParts) -- rotate the model + gInitial90DegreeRotations += 1 + + -- Update orientation value if this is a fake terrain part + clusterValues = stampData.CurrentParts\FindFirstChild "ClusterMaterial", true + if clusterValues and clusterValues\IsA "Vector3Value" + clusterValues.Value = Vector3.new( + clusterValues.Value.X, + clusterValues.Value.Y, + (clusterValues.Value.Z + 1) % 4 + ) + + + -- Rotate the parts or all the parts in the model + ry = math.pi / 2 + if stampData.CurrentParts\IsA"Model" or stampData.CurrentParts\IsA "Tool" + --stampData.CurrentParts\Rotate(0, ry, 0) + modelRotate stampData.CurrentParts, ry + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame + + + -- After rotating, update the position + configFound, targetCFrame = findConfigAtMouseTarget Mouse, stampData + if configFound + positionPartsAtCFrame3 targetCFrame, stampData.CurrentParts + + -- update everything else in MouseMove + DoStamperMouseMove Mouse + + elseif key == "c" -- try to expand our high scalability dragger dimension + if HighScalabilityLine.InternalLine and + HighScalabilityLine.InternalLine.magnitude > 0 and + HighScalabilityLine.Dimensions < 3 + + HighScalabilityLine.MorePoints[HighScalabilityLine.Dimensions] = HighScalabilityLine.End + HighScalabilityLine.MoreLines[HighScalabilityLine.Dimensions] = HighScalabilityLine.InternalLine + HighScalabilityLine.Dimensions = HighScalabilityLine.Dimensions + 1 + HighScalabilityLine.NewHint = true + + keyCon = Mouse.KeyDown\connect (key) -> -- init key connection (keeping code close to func) + setupKeyListener key, Mouse + + resetHighScalabilityLine = -> + if HighScalabilityLine + HighScalabilityLine.Start = nil + HighScalabilityLine.End = nil + HighScalabilityLine.InternalLine = nil + HighScalabilityLine.NewHint = true + + flashRedBox = -> + gui = game.CoreGui + if game\FindFirstChild "Players" + if game.Players["LocalPlayer"] + if game.Players.LocalPlayer\FindFirstChild "PlayerGui" + gui = game.Players.LocalPlayer.PlayerGui + + return if not stampData["ErrorBox"] + + stampData.ErrorBox.Parent = gui + if stampData.CurrentParts\IsA "Tool" + stampData.ErrorBox.Adornee = stampData.CurrentParts.Handle + else + stampData.ErrorBox.Adornee = stampData.CurrentParts + + delay 0, -> + for _ = 1, 3 + if stampData["ErrorBox"] + stampData.ErrorBox.Visible = true + + wait 0.13 + if stampData["ErrorBox"] + stampData.ErrorBox.Visible = false + + wait 0.13 + + if stampData["ErrorBox"] + stampData.ErrorBox.Adornee = nil + stampData.ErrorBox.Parent = Tool + + DoStamperMouseDown = (Mouse) -> + if not Mouse + error "Error: RbxStamper.DoStamperMouseDown: Mouse is nil" + return + + if not Mouse\IsA "Mouse" + error "Error: RbxStamper.DoStamperMouseDown: Mouse is of type", Mouse.className, "should be of type Mouse" + return + + if not stampData + return + + + if isMegaClusterPart! + if Mouse and HighScalabilityLine + megaCube = stampData.CurrentParts\FindFirstChild("MegaClusterCube", true) + terrain = game.Workspace.Terrain + if megaCube + HighScalabilityLine.Dimensions = 1 + tempCell = terrain\WorldToCell(megaCube.CFrame.p) + HighScalabilityLine.Start = terrain\CellCenterToWorld tempCell.X, tempCell.Y, tempCell.Z + return + else + HighScalabilityLine.Dimensions = 1 + tempCell = terrain\WorldToCell(stampData.CurrentParts.CFrame.p) + HighScalabilityLine.Start = terrain\CellCenterToWorld tempCell.X, tempCell.Y, tempCell.Z + return + + loadSurfaceTypes = (part, surfaces) -> + with part + .TopSurface = surfaces[1] + .BottomSurface = surfaces[2] + .LeftSurface = surfaces[3] + .RightSurface = surfaces[4] + .FrontSurface = surfaces[5] + .BackSurface = surfaces[6] + + + saveSurfaceTypes = (part, myTable) -> + tempTable = {} + tempTable[1] = part.TopSurface + tempTable[2] = part.BottomSurface + tempTable[3] = part.LeftSurface + tempTable[4] = part.RightSurface + tempTable[5] = part.FrontSurface + tempTable[6] = part.BackSurface + + myTable[part] = tempTable + + + -- makeSurfaceUnjoinable = (part, surface) -> + -- -- TODO: FILL OUT! + -- end + + prepareModel = (model) -> + if not model + return nil + + + gDesiredTrans = 0.7 + gStaticTrans = 1 + + clone = model\Clone! + scripts = {} + parts = {} + decals = {} + + stampData = {} + stampData.DisabledScripts = {} + stampData.TransparencyTable = {} + stampData.MaterialTable = {} + stampData.CanCollideTable = {} + stampData.AnchoredTable = {} + stampData.ArchivableTable = {} + stampData.DecalTransparencyTable = {} + stampData.SurfaceTypeTable = {} + + collectParts(clone, parts, scripts, decals) + + if #parts <= 0 + return nil, "no parts found in modelToStamp" + + + for _, script in pairs scripts + if not script.Disabled + script.Disabled = true + stampData.DisabledScripts[#stampData.DisabledScripts + 1] = script + + + for _, part in pairs parts + stampData.TransparencyTable[part] = part.Transparency + part.Transparency = gStaticTrans + (1 - gStaticTrans) * part.Transparency + stampData.MaterialTable[part] = part.Material + part.Material = Enum.Material.Plastic + stampData.CanCollideTable[part] = part.CanCollide + part.CanCollide = false + stampData.AnchoredTable[part] = part.Anchored + part.Anchored = true + stampData.ArchivableTable[part] = part.Archivable + part.Archivable = false + + saveSurfaceTypes(part, stampData.SurfaceTypeTable) + + fadeInDelayTime = 0.5 + transFadeInTime = 0.5 + delay 0, -> + wait fadeInDelayTime -- give it some time to be completely transparent + + begTime = tick! + currTime = begTime + while (currTime - begTime) < transFadeInTime and + part and + part\IsA"BasePart" and + part.Transparency > gDesiredTrans + + newTrans = 1 - (((currTime - begTime) / transFadeInTime) * (gStaticTrans - gDesiredTrans)) + if stampData["TransparencyTable"] and stampData.TransparencyTable[part] + part.Transparency = newTrans + (1 - newTrans) * stampData.TransparencyTable[part] + + wait 0.03 + currTime = tick! + + if part and part\IsA "BasePart" + if stampData["TransparencyTable"] and stampData.TransparencyTable[part] + part.Transparency = gDesiredTrans + (1 - gDesiredTrans) * stampData.TransparencyTable[part] + + for _, decal in pairs decals + stampData.DecalTransparencyTable[decal] = decal.Transparency + decal.Transparency = gDesiredTrans + (1 - gDesiredTrans) * decal.Transparency + + + -- disable all seats + setSeatEnabledStatus clone, true + setSeatEnabledStatus clone, false + + stampData.CurrentParts = clone + + -- if auto-alignable, we enforce a pre-rotation to the canonical "0-frame" + if autoAlignToFace clone + stampData.CurrentParts\ResetOrientationToIdentity! + gInitial90DegreeRotations = 0 + else -- pre-rotate if necessary + ry = gInitial90DegreeRotations * math.pi / 2 + if stampData.CurrentParts\IsA"Model" or stampData.CurrentParts\IsA "Tool" + --stampData.CurrentParts\Rotate(0, ry, 0) + modelRotate stampData.CurrentParts, ry + else + stampData.CurrentParts.CFrame = CFrame.fromEulerAnglesXYZ(0, ry, 0) * stampData.CurrentParts.CFrame + + + + -- since we're cloning the old model instead of the new one, we will need to update the orientation based on the original value AND how many more + -- rotations we expect since then [either that or we need to store the just-stamped clusterMaterial.Value.Z somewhere]. This should fix the terrain rotation + -- issue (fingers crossed) [HotThoth] + + clusterMaterial = stampData.CurrentParts\FindFirstChild "ClusterMaterial", true + if clusterMaterial and clusterMaterial\IsA"Vector3Value" + clusterMaterial.Value = Vector3.new( + clusterMaterial.Value.X, + clusterMaterial.Value.Y, + (clusterMaterial.Value.Z + gInitial90DegreeRotations) % 4 + ) + + + -- After rotating, update the position + local configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) + if configFound + stampData.CurrentParts = positionPartsAtCFrame3(targetCFrame, stampData.CurrentParts) + + + -- to show joints during the mouse move + game.JointsService\SetJoinAfterMoveInstance(stampData.CurrentParts) + + return clone, parts + + + checkTerrainBlockCollisions = (cellPos, checkHighScalabilityStamp) -> + cellCenterToWorld = game.Workspace.Terrain.CellCenterToWorld + cellCenter = cellCenterToWorld game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z + cellBlockingParts = game.Workspace\FindPartsInRegion3( + Region3.new( + cellCenter - Vector3.new(2, 2, 2) + insertBoundingBoxOverlapVector, + cellCenter + Vector3.new(2, 2, 2) - insertBoundingBoxOverlapVector + ), + stampData.CurrentParts, + 100 + ) + + skipThisCell = false + + for b = 1, #cellBlockingParts + if isBlocker cellBlockingParts[b] + skipThisCell = true + break + + + + if not skipThisCell + -- pop players up above any set cells + alreadyPushedUp = {} + -- if no blocking model below, then see if stamping on top of a character + for b = 1, #cellBlockingParts + if cellBlockingParts[b].Parent and + not alreadyPushedUp[cellBlockingParts[b].Parent] and + cellBlockingParts[b].Parent\FindFirstChild "Humanoid" and + cellBlockingParts[b].Parent\FindFirstChild"Humanoid"\IsA "Humanoid" + + ----------------------------------------------------------------------------------- + blockingPersonTorso = cellBlockingParts[b].Parent\FindFirstChild "Torso" + alreadyPushedUp[cellBlockingParts[b].Parent] = true + + if blockingPersonTorso + -- 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) + newY = cellCenter.Y + 5 + if spaceAboveCharacter blockingPersonTorso, newY, stampData + blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + + Vector3.new 0, newY - blockingPersonTorso.CFrame.p.Y, 0 + else + -- if no space, we just skip this one + skipThisCell = true + break + + + ----------------------------------------------------------------------------------- + + + if not skipThisCell -- if we STILL aren't skipping... then we're good to go! + canSetCell = true + + if checkHighScalabilityStamp -- check to see if cell is in region, if not we'll skip set + if allowedStampRegion + cellPos = cellCenterToWorld(game.Workspace.Terrain, cellPos.X, cellPos.Y, cellPos.Z) + if (cellPos.X + 2 > allowedStampRegion.CFrame.p.X + allowedStampRegion.Size.X / 2) or + (cellPos.X - 2 < allowedStampRegion.CFrame.p.X - allowedStampRegion.Size.X / 2) or + (cellPos.Y + 2 > allowedStampRegion.CFrame.p.Y + allowedStampRegion.Size.Y / 2) or + (cellPos.Y - 2 < allowedStampRegion.CFrame.p.Y - allowedStampRegion.Size.Y / 2) or + (cellPos.Z + 2 > allowedStampRegion.CFrame.p.Z + allowedStampRegion.Size.Z / 2) or + (cellPos.Z - 2 < allowedStampRegion.CFrame.p.Z - allowedStampRegion.Size.Z / 2) + + canSetCell = false + return canSetCell + + false + + ResolveMegaClusterStamp = (checkHighScalabilityStamp) -> + cellSet = false + + cluster = game.Workspace.Terrain + + line = HighScalabilityLine.InternalLine + cMax = game.Workspace.Terrain.MaxExtents.Max + cMin = game.Workspace.Terrain.MaxExtents.Min + + clusterMaterial = 1 -- default is grass + clusterType = 0 -- default is brick + clusterOrientation = 0 -- default is 0 rotation + + autoWedgeClusterParts = false + if stampData.CurrentParts\FindFirstChild "AutoWedge" + autoWedgeClusterParts = true + + + if stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + clusterMaterial = stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + if clusterMaterial\IsA "Vector3Value" + clusterType = clusterMaterial.Value.Y + clusterOrientation = clusterMaterial.Value.Z + clusterMaterial = clusterMaterial.Value.X + elseif clusterMaterial\IsA "IntValue" + clusterMaterial = clusterMaterial.Value + + + + if HighScalabilityLine.Adorn.Parent and + HighScalabilityLine.Start and + ((HighScalabilityLine.Dimensions > 1) or (line and line.magnitude > 0)) + + startCell = game.Workspace.Terrain\WorldToCell(HighScalabilityLine.Start) + xInc = { 0, 0, 0 } + yInc = { 0, 0, 0 } + zInc = { 0, 0, 0 } + + incrementVect = { nil, nil, nil } + stepVect = + * Vector3.new 0, 0, 0 + * Vector3.new 0, 0, 0 + * Vector3.new 0, 0, 0 + + worldAxes = + * Vector3.new 1, 0, 0 + * Vector3.new 0, 1, 0 + * Vector3.new 0, 0, 1 + + lines = {} + if HighScalabilityLine.Dimensions > 1 + table.insert(lines, HighScalabilityLine.MoreLines[1]) + + if line and line.magnitude > 0 + table.insert(lines, line) + + if HighScalabilityLine.Dimensions > 2 + table.insert(lines, HighScalabilityLine.MoreLines[2]) + + + for i = 1, #lines + lines[i] = Vector3.new( + math.floor(lines[i].X + 0.5), + math.floor(lines[i].Y + 0.5), + math.floor(lines[i].Z + 0.5) + ) -- round to integers + + if lines[i].X > 0 + xInc[i] = 1 + elseif lines[i].X < 0 + xInc[i] = -1 + + if lines[i].Y > 0 + yInc[i] = 1 + elseif lines[i].Y < 0 + yInc[i] = -1 + + if lines[i].Z > 0 + zInc[i] = 1 + elseif lines[i].Z < 0 + zInc[i] = -1 + + + incrementVect[i] = Vector3.new(xInc[i], yInc[i], zInc[i]) + if incrementVect[i].magnitude < 0.9 + incrementVect[i] = nil + + + + if not lines[2] + lines[2] = Vector3.new 0, 0, 0 + + if not lines[3] + lines[3] = Vector3.new 0, 0, 0 + + + waterForceTag = stampData.CurrentParts\FindFirstChild("WaterForceTag", true) + waterForceDirectionTag = stampData.CurrentParts\FindFirstChild("WaterForceDirectionTag", true) + + while stepVect[3].magnitude * 4 <= lines[3].magnitude + outerStepVectIndex = 1 + while outerStepVectIndex < 4 + stepVect[2] = Vector3.new 0, 0, 0 + while stepVect[2].magnitude * 4 <= lines[2].magnitude + innerStepVectIndex = 1 + while innerStepVectIndex < 4 + stepVect[1] = Vector3.new 0, 0, 0 + while stepVect[1].magnitude * 4 <= lines[1].magnitude + stepVectSum = stepVect[1] + stepVect[2] + stepVect[3] + cellPos = Vector3int16.new( + startCell.X + stepVectSum.X, + startCell.Y + stepVectSum.Y, + startCell.Z + stepVectSum.Z + ) + if cellPos.X >= cMin.X and + cellPos.Y >= cMin.Y and + cellPos.Z >= cMin.Z and + cellPos.X < cMax.X and + cellPos.Y < cMax.Y and + cellPos.Z < cMax.Z + + -- check if overlaps player or part + okToStampTerrainBlock = checkTerrainBlockCollisions cellPos, checkHighScalabilityStamp + + if okToStampTerrainBlock + if waterForceTag + cluster\SetWaterCell( + cellPos.X, + cellPos.Y, + cellPos.Z, + Enum.WaterForce[waterForceTag.Value], + Enum.WaterDirection[waterForceDirectionTag.Value] + ) + else + cluster\SetCell( + cellPos.X, + cellPos.Y, + cellPos.Z, + clusterMaterial, + clusterType, + clusterOrientation + ) + + cellSet = true + + -- auto-wedge it? + if autoWedgeClusterParts + 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 + ) + ) + + stepVect[1] = stepVect[1] + incrementVect[1] + + if incrementVect[2] + while innerStepVectIndex < 4 and + worldAxes[innerStepVectIndex]\Dot(incrementVect[2]) == 0 + + innerStepVectIndex += 1 + + if innerStepVectIndex < 4 + stepVect[2] = stepVect[2] + + worldAxes[innerStepVectIndex] * + worldAxes[innerStepVectIndex]\Dot incrementVect[2] + + innerStepVectIndex += 1 + else + stepVect[2] = Vector3.new(1, 0, 0) + innerStepVectIndex = 4 -- skip all remaining loops + + if stepVect[2].magnitude * 4 > lines[2].magnitude + innerStepVectIndex = 4 + + if incrementVect[3] + while outerStepVectIndex < 4 and worldAxes[outerStepVectIndex]\Dot(incrementVect[3]) == 0 + outerStepVectIndex += 1 + + if outerStepVectIndex < 4 + stepVect[3] = stepVect[3] + + worldAxes[outerStepVectIndex] * + worldAxes[outerStepVectIndex]\Dot incrementVect[3] + + outerStepVectIndex += 1 + else -- skip all remaining loops + stepVect[3] = Vector3.new 1, 0, 0 + outerStepVectIndex = 4 + + if stepVect[3].magnitude * 4 > lines[3].magnitude + outerStepVectIndex = 4 + + + -- and also get rid of any HighScalabilityLine stuff if it's there + HighScalabilityLine.Start = nil + HighScalabilityLine.Adorn.Parent = nil + + -- Mark for undo. + if cellSet + stampData.CurrentParts.Parent = nil + try + game\GetService"ChangeHistoryService"\SetWaypoint "StamperMulti" + + cellSet + + DoStamperMouseUp = (Mouse) -> + if not Mouse + error "Error: RbxStamper.DoStamperMouseUp: Mouse is nil" + return false + + if not Mouse\IsA "Mouse" + error("Error: RbxStamper.DoStamperMouseUp: Mouse is of type", Mouse.className, "should be of type Mouse") + return false + + + if not stampData.Dragger + error "Error: RbxStamper.DoStamperMouseUp: stampData.Dragger is nil" + return false + + + if not HighScalabilityLine + return false + + + local checkHighScalabilityStamp + if stampInModel + local canStamp + isHSLPart = isMegaClusterPart! + + if isHSLPart and + HighScalabilityLine and + HighScalabilityLine.Start and + HighScalabilityLine.InternalLine and + HighScalabilityLine.InternalLine.magnitude > 0 + + -- we have an HSL line, test later + canStamp = true + checkHighScalabilityStamp = true + else + canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion) + + + if not canStamp + if stampFailedFunc + stampFailedFunc! + + return false + + + + -- if unstampable face, then don't let us stamp there! + if unstampableSurface + flashRedBox! + return false + + + -- recheck if we can stamp, as we just moved part + canStamp, checkHighScalabilityStamp = t.CanEditRegion(stampData.CurrentParts, allowedStampRegion) + if not canStamp + if stampFailedFunc + stampFailedFunc! + + return false + + + -- Prevent part from being stamped on top of a player + + local minBB, maxBB = getBoundingBoxInWorldCoordinates(stampData.CurrentParts) + + -- HotThoth's note: Now that above CurrentParts positioning has been commented out, to be truly correct, we would need to use the + -- value of configFound from the previous onStamperMouseMove call which moved the CurrentParts + -- Shouldn't this be true when lastTargetCFrame has been set and false otherwise? + configFound, targetCFrame = findConfigAtMouseTarget(Mouse, stampData) + + if configFound and not HighScalabilityLine.Adorn.Parent + if clusterPartsInRegion(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector) + flashRedBox! + return false + + + blockingParts = game.Workspace\FindPartsInRegion3( + Region3.new(minBB + insertBoundingBoxOverlapVector, maxBB - insertBoundingBoxOverlapVector), + stampData.CurrentParts, + 100 + ) + + for b = 1, #blockingParts + if isBlocker(blockingParts[b]) + flashRedBox! + return false + + + + alreadyPushedUp = {} + -- if no blocking model below, then see if stamping on top of a character + for b = 1, #blockingParts + if blockingParts[b].Parent and + not alreadyPushedUp[blockingParts[b].Parent] and + blockingParts[b].Parent\FindFirstChild"Humanoid" and + blockingParts[b].Parent\FindFirstChild"Humanoid"\IsA "Humanoid" + + --------------------------------------------------------------------------- + blockingPersonTorso = blockingParts[b].Parent\FindFirstChild "Torso" + alreadyPushedUp[blockingParts[b].Parent] = true + + if blockingPersonTorso + -- 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) + newY = maxBB.Y + 3 + if spaceAboveCharacter blockingPersonTorso, newY, stampData + blockingPersonTorso.CFrame = blockingPersonTorso.CFrame + + Vector3.new 0, newY - blockingPersonTorso.CFrame.p.Y, 0 + else + -- if no space, we just error + flashRedBox! + return false + + + --------------------------------------------------------------------------- + + + elseif (not configFound) and not (HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent) -- if no config then only stamp if it's a real HSL! + resetHighScalabilityLine! + return false + + + -- something will be stamped! so set the "StampedSomething" toggle to true + if game\FindFirstChild "Players" + if game.Players["LocalPlayer"] + if game.Players.LocalPlayer["Character"] + localChar = game.Players.LocalPlayer.Character + stampTracker = localChar\FindFirstChild "StampTracker" + if stampTracker and not stampTracker.Value + stampTracker.Value = true + + + + + + -- if we drew a line of mega parts, stamp them out + if HighScalabilityLine.Start and HighScalabilityLine.Adorn.Parent and isMegaClusterPart! + if ResolveMegaClusterStamp(checkHighScalabilityStamp) or checkHighScalabilityStamp + -- kill the ghost part + stampData.CurrentParts.Parent = nil + return true + + + + -- not High-Scalability-Line-Based, so behave normally [and get rid of any HSL stuff] + HighScalabilityLine.Start = nil + HighScalabilityLine.Adorn.Parent = nil + + cluster = game.Workspace.Terrain + + -- if target point is in cluster, just use cluster\SetCell + if isMegaClusterPart! + -- if targetCFrame is inside cluster, just set that cell to 1 and return + --cellPos = cluster\WorldToCell(targetCFrame.p) + + local cellPos + if stampData.CurrentParts\IsA "Model" + cellPos = cluster\WorldToCell(stampData.CurrentParts\GetModelCFrame!.p) + else + cellPos = cluster\WorldToCell(stampData.CurrentParts.CFrame.p) + + + cMax = game.Workspace.Terrain.MaxExtents.Max + cMin = game.Workspace.Terrain.MaxExtents.Min + + if checkTerrainBlockCollisions(cellPos, false) + clusterValues = stampData.CurrentParts\FindFirstChild("ClusterMaterial", true) + waterForceTag = stampData.CurrentParts\FindFirstChild("WaterForceTag", true) + waterForceDirectionTag = stampData.CurrentParts\FindFirstChild("WaterForceDirectionTag", true) + + if cellPos.X >= cMin.X and + cellPos.Y >= cMin.Y and + cellPos.Z >= cMin.Z and + cellPos.X < cMax.X and + cellPos.Y < cMax.Y and + cellPos.Z < cMax.Z + + if waterForceTag + cluster\SetWaterCell( + cellPos.X, + cellPos.Y, + cellPos.Z, + Enum.WaterForce[waterForceTag.Value], + Enum.WaterDirection[waterForceDirectionTag.Value] + ) + elseif not clusterValues + cluster\SetCell( + cellPos.X, + cellPos.Y, + cellPos.Z, + cellInfo.Material, + cellInfo.clusterType, + gInitial90DegreeRotations % 4 + ) + elseif clusterValues\IsA "Vector3Value" + cluster\SetCell( + cellPos.X, + cellPos.Y, + cellPos.Z, + clusterValues.Value.X, + clusterValues.Value.Y, + clusterValues.Value.Z + ) + else + cluster\SetCell(cellPos.X, cellPos.Y, cellPos.Z, clusterValues.Value, 0, 0) + + + autoWedgeClusterParts = false + if stampData.CurrentParts\FindFirstChild "AutoWedge" + autoWedgeClusterParts = true + + + -- auto-wedge it + if autoWedgeClusterParts + 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) + ) + ) + + + -- kill the ghost part + stampData.CurrentParts.Parent = nil + + -- Mark for undo. It has to happen here or the selection display will come back also. + try + game\GetService"ChangeHistoryService"\SetWaypoint "StamperSingle" + + return true + + else + -- you tried to stamp a HSL-single part where one does not belong! + flashRedBox! + return false + + + + getPlayer = -> + if game\FindFirstChild "Players" + if game.Players["LocalPlayer"] + return game.Players.LocalPlayer + + + return nil + + + -- Post process: after positioning the part or model, restore transparency, material, anchored and collide states and create joints + if stampData.CurrentParts\IsA"Model" or stampData.CurrentParts\IsA "Tool" + if stampData.CurrentParts\IsA "Model" + -- Tyler's magical hack-code for allowing/preserving clones of both Surface and Manual Welds... just don't ask X< + manualWeldTable = {} + manualWeldParentTable = {} + saveTheWelds(stampData.CurrentParts, manualWeldTable, manualWeldParentTable) + stampData.CurrentParts\BreakJoints! + stampData.CurrentParts\MakeJoints! + restoreTheWelds(manualWeldTable, manualWeldParentTable) + + + -- 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) + playerIdTag = stampData.CurrentParts\FindFirstChild "PlayerIdTag" + playerNameTag = stampData.CurrentParts\FindFirstChild "PlayerNameTag" + if playerIdTag? + tempPlayerValue = getPlayer! + if tempPlayerValue? + playerIdTag.Value = tempPlayerValue.userId + + + if playerNameTag? + if game\FindFirstChild "Players" and game.Players["LocalPlayer"] + tempPlayerValue = game.Players.LocalPlayer + if tempPlayerValue? + playerNameTag.Value = tempPlayerValue.Name + + + + -- ...and tag all inserted models for subsequent origin identification + -- if no RobloxModel tag already exists, then add it. + if not stampData.CurrentParts\FindFirstChild"RobloxModel"? + stringTag = Instance.new "BoolValue" + stringTag.Name = "RobloxModel" + stringTag.Parent = stampData.CurrentParts + + if not stampData.CurrentParts\FindFirstChild"RobloxStamper"? + stringTag2 = Instance.new "BoolValue" + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = stampData.CurrentParts + + + else + stampData.CurrentParts\BreakJoints! + if not stampData.CurrentParts\FindFirstChild"RobloxStamper"? + stringTag2 = Instance.new "BoolValue" + stringTag2.Name = "RobloxStamper" + stringTag2.Parent = stampData.CurrentParts + + + + -- make sure all the joints are activated before restoring anchor states + if not createJoints + game.JointsService\CreateJoinAfterMoveJoints! + + + -- Restore the original properties for all parts being stamped + for part, transparency in pairs stampData.TransparencyTable + part.Transparency = transparency + + for part, archivable in pairs stampData.ArchivableTable + part.Archivable = archivable + + for part, material in pairs stampData.MaterialTable + part.Material = material + + for part, collide in pairs stampData.CanCollideTable + part.CanCollide = collide + + for part, anchored in pairs stampData.AnchoredTable + part.Anchored = anchored + + for decal, transparency in pairs stampData.DecalTransparencyTable + decal.Transparency = transparency + + + for part, surfaces in pairs stampData.SurfaceTypeTable + loadSurfaceTypes(part, surfaces) + + + if isMegaClusterPart! + stampData.CurrentParts.Transparency = 0 + + + -- re-enable all seats + setSeatEnabledStatus(stampData.CurrentParts, true) + + stampData.TransparencyTable = nil + stampData.ArchivableTable = nil + stampData.MaterialTable = nil + stampData.CanCollideTable = nil + stampData.AnchoredTable = nil + stampData.SurfaceTypeTable = nil + + -- ...and tag all inserted models for subsequent origin identification + -- if no RobloxModel tag already exists, then add it. + if not stampData.CurrentParts\FindFirstChild"RobloxModel"? + stringTag = Instance.new "BoolValue" + stringTag.Name = "RobloxModel" + stringTag.Parent = stampData.CurrentParts + + + -- and make sure we don't delete it, now that it's not a ghost part + if ghostRemovalScript + ghostRemovalScript.Parent = nil + + + --Re-enable the scripts + for _, script in pairs stampData.DisabledScripts + script.Disabled = false + + + --Now that they are all marked enabled, reinsert them into the world so they start running + for _, script in pairs stampData.DisabledScripts + oldParent = script.Parent + script.Parent = nil + script\Clone!.Parent = oldParent + + + -- clear out more data + stampData.DisabledScripts = nil + stampData.Dragger = nil + stampData.CurrentParts = nil + + try + game\GetService"ChangeHistoryService"\SetWaypoint "StampedObject" + + true + + pauseStamper = -> + for i = 1, #mouseCons do -- stop the mouse from doing anything + mouseCons[i]\disconnect! + mouseCons[i] = nil + + mouseCons = {} + + if stampData and stampData.CurrentParts -- remove our ghost part + stampData.CurrentParts.Parent = nil + stampData.CurrentParts\Remove! + + + resetHighScalabilityLine! + + game.JointsService\ClearJoinAfterMoveJoints! + + prepareUnjoinableSurfaces = (modelCFrame, parts, whichSurface) -> + AXIS_VECTORS = + * Vector3.new 1, 0, 0 + * Vector3.new 0, 1, 0 + * Vector3.new 0, 0, 1 + + -- maybe last one is negative? TODO: check this! + isPositive = 1 + if whichSurface < 0 + isPositive *= -1 + whichSurface *= -1 + + surfaceNormal = isPositive * modelCFrame\vectorToWorldSpace(AXIS_VECTORS[whichSurface]) + + for i = 1, #parts + currPart = parts[i] + + -- now just need to find which surface of currPart most closely match surfaceNormal and then set that to Unjoinable + surfaceNormalInLocalCoords = currPart.CFrame\vectorToObjectSpace surfaceNormal + if math.abs(surfaceNormalInLocalCoords.X) > math.abs surfaceNormalInLocalCoords.Y + if math.abs(surfaceNormalInLocalCoords.X) > math.abs surfaceNormalInLocalCoords.Z + if surfaceNormalInLocalCoords.X > 0 + currPart.RightSurface = "Unjoinable" + else + currPart.LeftSurface = "Unjoinable" + + else + if surfaceNormalInLocalCoords.Z > 0 + currPart.BackSurface = "Unjoinable" + else + currPart.FrontSurface = "Unjoinable" + + else + if math.abs(surfaceNormalInLocalCoords.Y) > math.abs surfaceNormalInLocalCoords.Z + if surfaceNormalInLocalCoords.Y > 0 + currPart.TopSurface = "Unjoinable" + else + currPart.BottomSurface = "Unjoinable" + + else + if surfaceNormalInLocalCoords.Z > 0 + currPart.BackSurface = "Unjoinable" + else + currPart.FrontSurface = "Unjoinable" + + + + + + + resumeStamper = -> + clone, parts = prepareModel modelToStamp + + if not clone or not parts + return + + + -- if we have unjoinable faces, then we want to change those surfaces to be Unjoinable + unjoinableTag = clone\FindFirstChild "UnjoinableFaces", true + if unjoinableTag + for unjoinableSurface in string.gmatch unjoinableTag.Value, "[^,]*" + if tonumber unjoinableSurface + if clone\IsA "Model" + prepareUnjoinableSurfaces clone\GetModelCFrame!, parts, tonumber unjoinableSurface + else + prepareUnjoinableSurfaces clone.CFrame, parts, tonumber unjoinableSurface + + + + + + stampData.ErrorBox = errorBox + clone.Parent = if stampInModel + stampInModel + else + game.Workspace + + if clone\FindFirstChild "ClusterMaterial", true -- extract all info from vector + clusterMaterial = clone\FindFirstChild "ClusterMaterial", true + if clusterMaterial\IsA "Vector3Value" + cellInfo.Material = clusterMaterial.Value.X + cellInfo.clusterType = clusterMaterial.Value.Y + cellInfo.clusterOrientation = clusterMaterial.Value.Z + elseif clusterMaterial\IsA "IntValue" + cellInfo.Material = clusterMaterial.Value + + try + mouseTarget = Mouse.Target + + + if mouseTarget and not mouseTarget.Parent\FindFirstChild"RobloxModel"? + game.JointsService\SetJoinAfterMoveTarget mouseTarget + else + game.JointsService\SetJoinAfterMoveTarget nil + + game.JointsService\ShowPermissibleJoints! + + for _, object in pairs stampData.DisabledScripts + if object.Name == "GhostRemovalScript" + object.Parent = stampData.CurrentParts + + stampData.Dragger = Instance.new "Dragger" + + --Begin a movement by faking a MouseDown signal + stampData.Dragger\MouseDown parts[1], Vector3.new 0, 0, 0, parts + stampData.Dragger\MouseUp! + + DoStamperMouseMove Mouse + + table.insert( + mouseCons, + Mouse.Move\connect -> + return if movingLock or stampUpLock + + movingLock = true + DoStamperMouseMove Mouse + movingLock = false + + ) + + table.insert( + mouseCons, + Mouse.Button1Down\connect -> + DoStamperMouseDown Mouse + + ) + + table.insert( + mouseCons, + Mouse.Button1Up\connect -> + stampUpLock = true + while movingLock + wait! + + stamped.Value = DoStamperMouseUp Mouse + resetHighScalabilityLine! + stampUpLock = false + + ) + + stamped.Value = false + + + resetStamperState = (newModelToStamp) -> + -- if we have a new model, swap it out + if newModelToStamp + if not newModelToStamp\IsA"Model" and not newModelToStamp\IsA "BasePart" + error "resetStamperState: newModelToStamp (first arg) is not nil, but not a model or part!" + + modelToStamp = newModelToStamp + + + -- first clear our state + pauseStamper! + -- now lets load in the new model + resumeStamper! + + + -- load the model initially + resetStamperState! + + -- setup the control table we pass back to the user + control.Stamped = stamped -- BoolValue that fires when user stamps + control.Paused = false + + control.LoadNewModel = (newStampModel) -> + -- allows us to specify a new stamper model to be used with this stamper + if newStampModel and not newStampModel\IsA"Model" and not newStampModel\IsA "BasePart" + error "Control.LoadNewModel: newStampModel (first arg) is not a Model or Part!" + return nil + + resetStamperState newStampModel + + control.ReloadModel = -> + -- will automatically set stamper to get a new model of current model and start stamping with new model + resetStamperState! + + control.Pause = -> -- temporarily stops stamping, use resume to start up again + if not control.Paused + pauseStamper! + control.Paused = true + else + print "RbxStamper Warning: Tried to call Control.Pause! when already paused" + + control.Resume = -> -- resumes stamping, if currently paused + if control.Paused + resumeStamper! + control.Paused = false + else + print "RbxStamper Warning: Tried to call Control.Resume! without Pausing First" + + control.ResetRotation = -> -- resets the model rotation so new models are at default orientation + -- gInitial90DegreeRotations = 0 + -- Note: This function will not always work quite the way we want it to; we will have to build this out further so it works with + -- High-Scalability and with the new model orientation setting methods (model\ResetOrientationToIdentity!) [HotThoth] + + control.Destroy = -> -- Stops current Stamp operation and destroys control construct + for i = 1, #mouseCons + mouseCons[i]\disconnect! + mouseCons[i] = nil + + keyCon?\disconnect! + + game.JointsService\ClearJoinAfterMoveJoints! + + adorn?\Destroy! + adornPart?\Destroy! + errorBox?\Destroy! + + stampData?.Dragger?\Destroy! + stampData?.CurrentParts?\Destroy! + + if control and control["Stamped"] + control.Stamped\Destroy! + + control = nil + control + + +t.Help = (funcNameOrFunc) -> + --input argument can be a string or a function. Should return a description (of arguments and expected side effects) + switch funcNameOrFunc + when "GetStampModel", t.GetStampModel + "Function GetStampModel. Arguments: assetId, useAssetVersionId. assetId is the asset to load in, define useAssetVersionId as true if assetId is a version id instead of a relative assetId. Side effect: returns a model of the assetId, or a string with error message if something fails" + + when "SetupStamperDragger", t.SetupStamperDragger + "Function SetupStamperDragger. Side Effect: Creates 4x4 stamping mechanism for building out parts quickly. Arguments: ModelToStamp, Mouse, LegalStampCheckFunction. ModelToStamp should be a Model or Part, preferrably loaded from RbxStamper.GetStampModel and should have extents that are multiples of 4. Mouse should be a mouse object (obtained from things such as Tool.OnEquipped), used to drag parts around 'stamp' them out. LegalStampCheckFunction is optional, used as a callback with a table argument (table is full of instances about to be stamped). Function should return either true or false, false stopping the stamp action." + +t