local Players = game:GetService("Players") local TweenService = game:GetService("TweenService") local UserInputService = game:GetService("UserInputService") local LastTradePartner = nil local function FormatValue(v) if v == nil then return "?" end if type(v) == "number" then local s = tostring(math.floor(v)) local k repeat s, k = string.gsub(s, "^(-?%d+)(%d%d%d)", "%1,%2") until k == 0 return s end return tostring(v) end setthreadidentity(2) local ProfileData = require(game.ReplicatedStorage.Modules.ProfileData) local InventoryModule = require(game.ReplicatedStorage.Modules.InventoryModule) local ItemModule = require(game.ReplicatedStorage.Modules.ItemModule) local Sync = require(game.ReplicatedStorage.Database.Sync) local ItemPopupService = require(game.ReplicatedStorage.ClientServices.ItemPopupService) setthreadidentity(8) local TradeRemotes = game.ReplicatedStorage.Trade local TradeGUI = game.Players.LocalPlayer.PlayerGui.TradeGUI local TheirOffer = TradeGUI.Container.Trade.TheirOffer local YourOffer = TradeGUI.Container.Trade.YourOffer local SearchTextSignal local TradeInventory local functions = {} local Config = { ["item"] = "", ["in_trade"] = false, ["player2"] = nil } -- ============================================================ -- Untradable weapon filter -- Sourced from the MM2 wiki: -- * Unique rarity (the orange label) -- Corrupt is the ONLY tradable -- Unique. Every other one is an event-leaderboard prize and is -- permanently untradable. -- * The four Evo gamepass weapons -- Reaver, Gingerscythe, Icecrusher -- and Synthwave -- are untradable at EVERY evolution stage -- (Rare -> Legendary -> Godly -> Ancient), so they are matched by -- family name rather than by rarity. -- * Unreleased "???" placeholder entries -- i.e. anything sitting in the -- inventory that has no entry in Sync.Weapons, which is exactly why the -- trade GUI renders them as "???". -- NOTE: the weapon data carries NO tradability flag (Meta only has Rarity, -- ItemName, ItemType, Chroma, FX, Event, Year), so untradables cannot be -- detected automatically -- the name list below is the source of truth. -- Matches never enter WeaponCatalog, SpawnItem refuses their keys, and -- PurgeBlockedFromInventory strips any that are already owned. -- ============================================================ local UntradableRarities = { Unique = true } local UntradableRarityExceptions = { corrupt = true } local UntradableFamilies = { "reaver", "gingerscythe", "icecrusher", "synthwave", } local EvoPrefixes = { Blue = true, Bronze = true, Silver = true, Gold = true, Platinum = true, Diamond = true, Emerald = true, Ruby = true, Obsidian = true, Crystal = true, } local function _isEvoWeapon(name, data) if type(data) == "table" then if data.Evo == true or data.Evolution == true then return true end if data.IsEvo == true or data.EvoTier ~= nil then return true end if type(data.MaxStack) == "number" and data.MaxStack <= 1 then return true end if type(data.MaxAmount) == "number" and data.MaxAmount <= 1 then return true end end if name then local firstWord = string.match(tostring(name), "^(%S+)") if firstWord and EvoPrefixes[firstWord] then return true end end return false end local function _isTradable(data) if type(data) ~= "table" then return false end if data.Tradable == false then return false end if data.CanTrade == false then return false end if data.Untradable == true then return false end if data.NonTradable == true then return false end if data.Locked == true then return false end return true end -- drops spaces, apostrophes and punctuation so "Red Icecrusher", -- "C. Synthwave" and "Traveler's Gun" all flatten consistently local function _blockKey(s) return (string.gsub(string.lower(tostring(s or "")), "[^%a%d]", "")) end -- nil when the weapon is fine, otherwise a reason string local function WeaponBlockReason(name, rarity, data) local flat = _blockKey(name) if flat == "" or string.find(tostring(name or ""), "?", 1, true) then return "unreleased placeholder" end for _, family in ipairs(UntradableFamilies) do if string.find(flat, family, 1, true) then return "Evo gamepass weapon (untradable at every stage)" end end if UntradableRarities[rarity] and not UntradableRarityExceptions[flat] then return "untradable " .. tostring(rarity) end if not _isTradable(data) then return "flagged untradable by the game data" end if _isEvoWeapon(name, data) then return "Evo / leaderboard variant" end return nil end local BlockedWeaponKeys = {} local WeaponCatalog = {} local WeaponByKey = {} local WeaponByName = {} local RareWeaponKeys = {} local RareRarities = { Godly = true, Ancient = true, Unique = true, Chroma = true, Legendary = true, Classic = true } do local source = Sync.Weapons or Sync.Item local blockedCount = 0 for key, data in pairs(source) do if type(data) == "table" and (data.ItemType == "Knife" or data.ItemType == "Gun") then local rarity = data.Rarity or "Common" local isChroma = data.Chroma == true local name = data.ItemName or key local blockReason = WeaponBlockReason(name, rarity, data) if blockReason then BlockedWeaponKeys[key] = blockReason blockedCount = blockedCount + 1 print(("[mm2run/filter] [x] %s (%s) -- %s"):format(name, rarity, blockReason)) else local effectiveRarity = isChroma and "Chroma" or rarity local entry = { key = key, name = name, rarity = effectiveRarity, type = data.ItemType, chroma = isChroma, } table.insert(WeaponCatalog, entry) WeaponByKey[key] = entry WeaponByName[string.lower(entry.name)] = entry if RareRarities[effectiveRarity] then table.insert(RareWeaponKeys, key) end end end end print(("[mm2run/filter] %d weapons spawnable, %d untradable weapons removed"):format(#WeaponCatalog, blockedCount)) local rarityOrder = { Chroma = 1, Godly = 2, Ancient = 3, Unique = 4, Legendary = 5, Classic = 6, Vintage = 7, Rare = 8, Uncommon = 9, Common = 10, } table.sort(WeaponCatalog, function(a, b) local ra = rarityOrder[a.rarity] or 99 local rb = rarityOrder[b.rarity] or 99 if ra ~= rb then return ra < rb end if a.type ~= b.type then return a.type < b.type end return a.name < b.name end) end -- resolves whatever shape ProfileData.Weapons.Owned uses into (itemKey, amount) local function _ownedEntry(k, v) if type(k) == "number" then if type(v) == "string" then return v, 1 end if type(v) == "table" then return (v.Name or v.ItemName or v.Key or v.Id), (tonumber(v.Amount) or 1) end return nil, 0 end if type(v) == "number" then return k, v end if type(v) == "table" then return k, (tonumber(v.Amount) or 1) end return k, 1 end -- Strips untradable / unresolvable weapons out of the LOCAL ProfileData copy so -- they cannot show up in the trade inventory or be put into an offer. This is -- client-side only -- rejoining restores whatever you actually own. -- Also prints every owned weapon so the console shows exactly what the game -- calls each item, which is what the blocklist above has to be written against. local function PurgeBlockedFromInventory() local removed = 0 pcall(function() local owned = ProfileData.Weapons and ProfileData.Weapons.Owned if type(owned) ~= "table" then return end local kill = {} print("[mm2run/filter] ----- owned weapons -----") for k, v in pairs(owned) do local itemKey, amount = _ownedEntry(k, v) if itemKey then local data = Sync.Weapons and Sync.Weapons[itemKey] local displayName = (type(data) == "table" and data.ItemName) or tostring(itemKey) local rarity = (type(data) == "table" and (data.Rarity or "Common")) or "?" local reason if type(data) ~= "table" then reason = "no entry in the weapon database (renders as ???)" else reason = WeaponBlockReason(displayName, data.Rarity or "Common", data) end print(("[mm2run/filter] %s %s x%s (%s)%s"):format( reason and "[x]" or "[ ]", displayName, tostring(amount), rarity, reason and (" <- " .. reason) or "")) if reason then table.insert(kill, { k = k, name = displayName, reason = reason }) end end end for _, item in ipairs(kill) do owned[item.k] = nil removed = removed + 1 end print(("[mm2run/filter] ----- removed %d untradable weapon(s) from the inventory -----"):format(removed)) end) if removed > 0 then pcall(function() game.ReplicatedStorage.Remotes.Inventory.InventoryDataChanged:Fire() end) end return removed end local function CheckForItem(ItemName, Type) local Owned = ProfileData[Type].Owned for Index, Value in pairs(Owned) do if Index == ItemName then return true, Value end if Value == ItemName then return true, 1 end end return false end local function CheckForItem2(ItemName, Type) return true, math.huge end local v18 = {} local function v22(v19) for _, v21 in pairs(v19:GetChildren()) do if v21:IsA("Frame") then v21.Visible = false if v18[v21] then v18[v21]:Disconnect() v18[v21] = nil end end end end local TradeTable = { ["LastOffer"] = os.time(), ["Locked"] = false, ["Player1"] = { ["Player"] = game.Players.LocalPlayer, ["Accepted"] = false, ["Offer"] = {} }, ["Player2"] = { ["Player"] = "m0_3a", ["Accepted"] = false, ["Offer"] = {} }, } local function SpawnItem(ItemName, Amount, ItemType) Amount = Amount or 1 ItemType = ItemType or "Weapons" if ItemType == "Weapons" and BlockedWeaponKeys[ItemName] then warn(("[mm2run/filter] refused to spawn %s -- %s"):format(tostring(ItemName), BlockedWeaponKeys[ItemName])) return end pcall(function() if ProfileData[ItemType].Owned[ItemName] == nil then ProfileData[ItemType].Owned[ItemName] = Amount else ProfileData[ItemType].Owned[ItemName] = ProfileData[ItemType].Owned[ItemName] + Amount end game.ReplicatedStorage.Remotes.Inventory.InventoryDataChanged:Fire() end) end local function GiveItem(ItemName, Amount, ItemType) pcall(function() if ProfileData[ItemType].Owned[ItemName] == nil then ProfileData[ItemType].Owned[ItemName] = Amount else ProfileData[ItemType].Owned[ItemName] = ProfileData[ItemType].Owned[ItemName] + Amount end ItemPopupService.ItemReceived:Fire(ItemName, ItemType) game.ReplicatedStorage.Remotes.Inventory.InventoryDataChanged:Fire() end) end local function RemoveItem(ItemName, Amount, ItemType) pcall(function() local owned = ProfileData[ItemType].Owned[ItemName] if not owned then print("doesn't have the item") return end if owned - Amount > 0 then ProfileData[ItemType].Owned[ItemName] = owned - Amount else ProfileData[ItemType].Owned[ItemName] = nil end game.ReplicatedStorage.Remotes.Inventory.InventoryDataChanged:Fire() end) end local function AcceptTrade() if not TradeTable then return end if TradeTable["Player1"]["Accepted"] == true and TradeTable["Player2"]["Accepted"] == true then TradeTable["Locked"] = true task.wait(0.2) if TradeTable["Player1"]["Offer"] and next(TradeTable["Player1"]["Offer"]) ~= nil then for _, item in pairs(TradeTable["Player1"]["Offer"]) do local itemName = item[1] local amount = item[2] local itemType = item[3] pcall(function() RemoveItem(itemName, amount, itemType) end) end end if TradeTable["Player2"]["Offer"] and next(TradeTable["Player2"]["Offer"]) ~= nil then for _, item in pairs(TradeTable["Player2"]["Offer"]) do local itemName = item[1] local amount = item[2] local itemType = item[3] pcall(function() GiveItem(itemName, amount, itemType) end) pcall(function() _G.NewItem(itemName, "You Got...", nil, itemType, amount) end) end end pcall(function() TradeGUI.Enabled = false end) local partner = "m0_3a" if TradeTable.Player2 and TradeTable.Player2.Player then partner = TradeTable.Player2.Player end if partner and partner ~= "" and partner ~= "m0_3a" then LastTradePartner = partner pcall(function() if PartnerUserBox then PartnerUserBox.Text = partner end end) end TradeTable = { ["LastOffer"] = os.time(), ["Locked"] = false, ["Player1"] = { ["Player"] = game.Players.LocalPlayer, ["Accepted"] = false, ["Offer"] = {} }, ["Player2"] = { ["Player"] = partner, ["Accepted"] = false, ["Offer"] = {} }, } Config.in_trade = false end end local v84 = false local function OfferItemLocalPlayer(ItemName,ItemType) if not TradeTable then return end if TradeTable["Locked"] == true then return end local AlreadyOffered = 0 for _,Item in pairs(TradeTable["Player1"]["Offer"]) do if Item[1] == ItemName and Item[3] == ItemType then AlreadyOffered = Item[2] end end local HasItem,Amount = CheckForItem(ItemName,ItemType) if HasItem and Amount-AlreadyOffered > 0 then if AlreadyOffered == 0 then if #TradeTable["Player1"]["Offer"] < 4 then table.insert(TradeTable["Player1"]["Offer"], {ItemName,1,ItemType}) end else for Index,Item in pairs(TradeTable["Player1"]["Offer"]) do if Item[1] == ItemName then TradeTable["Player1"]["Offer"][Index][2] = TradeTable["Player1"]["Offer"][Index][2] + 1 break end end end end TradeTable["LastOffer"] = os.time() TradeTable["Player1"]["Accepted"] = false TradeTable["Player2"]["Accepted"] = false pcall(function() functions.UpdateTrade() end) end local function RemoveItemLocalPlayer(ItemName, ItemType) if not TradeTable then return end if TradeTable["Locked"] == true then return end if TradeTable["Player1"]["Accepted"] then return end TradeTable["LastOffer"] = os.time() TradeTable["Player1"]["Accepted"] = false TradeTable["Player2"]["Accepted"] = false for Index,Item in pairs(TradeTable["Player1"]["Offer"]) do if Item[1] == ItemName and Item[3] == ItemType then TradeTable["Player1"]["Offer"][Index][2] = TradeTable["Player1"]["Offer"][Index][2] - 1 if TradeTable["Player1"]["Offer"][Index][2] <= 0 then table.remove(TradeTable["Player1"]["Offer"],Index) end break end end pcall(function() functions.UpdateTrade() end) end local function FindItemInDatabase(itemName, itemType) if not Sync[itemType] then return nil end if Sync[itemType][itemName] then return itemName, Sync[itemType][itemName] end return nil, nil end local function OfferItemAnotherPlayer(ItemName, ItemType) if not ItemName or ItemName == "" then return false end if not TradeTable then return false end if TradeTable["Locked"] == true then return false end if #TradeTable["Player2"]["Offer"] >= 4 then local foundExisting = false for _, Item in pairs(TradeTable["Player2"]["Offer"]) do if Item[1] == ItemName and Item[3] == ItemType then foundExisting = true break end end if not foundExisting then return false end end local AlreadyOffered = 0 for _, Item in pairs(TradeTable["Player2"]["Offer"]) do if Item[1] == ItemName and Item[3] == ItemType then AlreadyOffered = Item[2] end end if AlreadyOffered == 0 then table.insert(TradeTable["Player2"]["Offer"], {ItemName, 1, ItemType}) else for Index, Item in pairs(TradeTable["Player2"]["Offer"]) do if Item[1] == ItemName and Item[3] == ItemType then TradeTable["Player2"]["Offer"][Index][2] = TradeTable["Player2"]["Offer"][Index][2] + 1 break end end end TradeTable["LastOffer"] = os.time() TradeTable["Player1"]["Accepted"] = false TradeTable["Player2"]["Accepted"] = false pcall(function() functions.UpdateTrade() end) return true end local function RemoveItemAnotherPlayer() if not TradeTable then return end if not TradeTable["Player2"] then return end if not TradeTable["Player2"]["Offer"] then return end if #TradeTable["Player2"]["Offer"] > 0 then if TradeTable["Player2"]["Accepted"] then return end local LastIndex = #TradeTable["Player2"]["Offer"] TradeTable["Player2"]["Offer"][LastIndex][2] = TradeTable["Player2"]["Offer"][LastIndex][2] - 1 if TradeTable["Player2"]["Offer"][LastIndex][2] <= 0 then table.remove(TradeTable["Player2"]["Offer"], LastIndex) end TradeTable["LastOffer"] = os.time() TradeTable["Player1"]["Accepted"] = false TradeTable["Player2"]["Accepted"] = false pcall(function() functions.UpdateTrade() end) end end local function v34(v23, v24) for v25, v26 in v24 do local ItemID = v26[1] or v26.ItemID local Amount = v26[2] or v26.Amount local ItemType = v26[3] or v26.ItemType local v33 = v23.Container["NewItem" .. v25] if not v33 then continue end local success = pcall(function() if Sync[ItemType] and Sync[ItemType][ItemID] then local v30 = {} for v31, v32 in pairs(Sync[ItemType][ItemID]) do v30[v31] = v32 end v30.DataType = ItemType v30.Amount = Amount ItemModule.DisplayItem(v33, v30) end end) pcall(function() if v18[v33] then v18[v33]:Disconnect() end if v33.Container and v33.Container:FindFirstChild("ActionButton") then v18[v33] = v33.Container.ActionButton.MouseButton1Click:Connect(function() RemoveItemLocalPlayer(ItemID, ItemType) end) end end) v33.Visible = true end end local v85 = 6 local function ResetCooldown(arg1) if arg1 then TradeGUI.Container.Trade.Actions.Accept.Cooldown.Visible = false v85 = 0 v84 = false return else TradeGUI.Container.Trade.Actions.Accept.Cooldown.Visible = true v85 = 6 TradeGUI.Container.Trade.Actions.Accept.Cooldown.Title.Text = " Please wait (" .. v85 .. ") before accepting." if not v84 then TradeGUI.Container.Trade.Actions.Accept.Cooldown.Visible = true v84 = true repeat wait(1) v85 = v85 - 1 TradeGUI.Container.Trade.Actions.Accept.Cooldown.Title.Text = " Please wait (" .. v85 .. ") before accepting." until v85 <= 0 v84 = false TradeGUI.Container.Trade.Actions.Accept.Cooldown.Visible = false return else v85 = 6 return end end end local function UpdateTradeInventory() pcall(function() if not TradeInventory or not TradeInventory.Data then return end local l_Offer_2 = TradeTable["Player1"].Offer for v63, v64 in pairs(TradeInventory.Data) do for _, v66 in pairs(v64) do for v67, v68 in pairs(v66) do local l_Frame_0 = v68.Frame local l_Amount_0 = v68.Amount for _, v72 in pairs(l_Offer_2) do local v73 = v72[1] or v72.ItemID local v74 = v72[2] or v72.Amount local v75 = v72[3] or v72.ItemType if v73 == v67 and v75 == v63 then l_Amount_0 = l_Amount_0 - v74 end end if l_Amount_0 == 1 then l_Frame_0.Container.Amount.Text = "" l_Frame_0.Visible = true elseif l_Amount_0 > 1 then l_Frame_0.Container.Amount.Text = "x" .. l_Amount_0 l_Frame_0.Visible = true elseif l_Amount_0 < 1 then l_Frame_0.Visible = false end end end end end) end local v35 = "Accept" functions.UpdateTrade = function() pcall(function() local Offer1 = TradeTable.Player1.Offer local Offer2 = TradeTable.Player2.Offer v22(YourOffer.Container) v22(TheirOffer.Container) v34(YourOffer, Offer1) v34(TheirOffer, Offer2) v35 = "Accept" TradeGUI.Container.Trade.Actions.Accept.Confirm.Visible = false TradeGUI.Container.Trade.Actions.Accept.Cancel.Visible = false YourOffer.Accepted.Visible = false TheirOffer.Accepted.Visible = false local l_AddItem_0 = TradeGUI.Container.Trade.Actions.Accept.AddItem local v44 = false if #Offer1 < 1 then v44 = #Offer2 < 1 end l_AddItem_0.Visible = v44 UpdateTradeInventory() l_AddItem_0 = ResetCooldown v44 = false if #Offer1 < 1 then v44 = #Offer2 < 1 end l_AddItem_0(v44) end) end function DeclineTrade() pcall(function() TradeGUI.Enabled = false end) local partner = "m0_3a" if TradeTable and TradeTable.Player2 and TradeTable.Player2.Player then partner = TradeTable.Player2.Player end TradeTable = { ["LastOffer"] = os.time(), ["Locked"] = false, ["Player1"] = { ["Player"] = game.Players.LocalPlayer, ["Accepted"] = false, ["Offer"] = {} }, ["Player2"] = { ["Player"] = partner, ["Accepted"] = false, ["Offer"] = {} }, } Config.in_trade = false pcall(function() UnConnections() end) end local v87 = time() local Connections = {} function SetupConnections(v76) pcall(function() if v76 and v76.Data then for v77, v78 in pairs(v76.Data) do for _, v80 in pairs(v78) do for v81, v82 in pairs(v80) do local l_Frame_1 = v82.Frame if l_Frame_1 then Connections.Connection0 = l_Frame_1.Container.ActionButton.MouseButton1Click:Connect(function() OfferItemLocalPlayer(v81, v77) end) end end end end end end) pcall(function() Connections.Connection1 = TradeGUI.Container.Trade.Actions.Accept.ActionButton.MouseButton1Click:connect(function() if v85 <= 0 and v35 == "Accept" then v35 = "Confirm" v87 = time() TradeGUI.Container.Trade.Actions.Accept.Confirm.Visible = true end end) end) pcall(function() Connections.Connection2 = TradeGUI.Container.Trade.Actions.Accept.Confirm.ActionButton.MouseButton1Click:connect(function() if v85 <= 0 and time() - v87 >= 0.4 and v35 == "Confirm" then v35 = "Waiting" YourOffer.Accepted.Visible = true TradeGUI.Container.Trade.Actions.Accept.Cancel.Visible = true TradeTable["Player1"]["Accepted"] = true AcceptTrade() end end) end) pcall(function() Connections.Connection3 = TradeGUI.Container.Trade.Actions.Accept.Cancel.ActionButton.MouseButton1Click:connect(function() TradeTable["LastOffer"] = os.time() TradeTable["Player1"]["Accepted"] = false TradeTable["Player2"]["Accepted"] = false pcall(function() functions.UpdateTrade() end) end) end) pcall(function() Connections.Connection4 = TradeGUI.Container.Trade.Actions.Decline.ActionButton.MouseButton1Click:connect(function() DeclineTrade() end) end) end function UnConnections() pcall(function() for i,v in pairs(Connections) do v:disconnect() end end) end function StartTrade() if Config.in_trade == true then return end Config.in_trade = true -- must run before GenerateInventory, or blocked items get frames built for them PurgeBlockedFromInventory() pcall(function() for _, v49 in pairs({"Weapons", "Pets"}) do for v50, _ in pairs(InventoryModule.CreateBlankTradeInventoryTable()[v49]) do TradeGUI.Container.Items.Main:FindFirstChild(v49).Items.Container:FindFirstChild(v50).Container:ClearAllChildren() end end end) pcall(function() TradeInventory = InventoryModule.GenerateInventory(TradeGUI.Container.Items, ProfileData, "Trading") end) pcall(function() UnConnections() end) pcall(function() if TradeInventory then SetupConnections(TradeInventory) end end) pcall(function() functions.UpdateTrade(TradeTable) end) pcall(function() TheirOffer.Username.Text = "(" .. tostring(TradeTable.Player2.Player) .. ")" end) TradeGUI.Enabled = true pcall(function() if SearchTextSignal then SearchTextSignal:disconnect() end local SearchText = TradeGUI.Container.Items.Tabs.Search.Container.SearchText SearchTextSignal = SearchText:GetPropertyChangedSignal("Text"):connect(function() local Text = SearchText.Text Text = string.gsub(Text, "S", "") for _, v55 in pairs(TradeInventory.Data) do for _, v57 in pairs(v55.Current) do v57.Frame.Visible = string.find(string.lower(v57.Name), string.lower(Text)) if v57.Frame.Parent.Parent:IsA("ScrollingFrame") then v57.Frame.Parent.Parent.CanvasPosition = Vector2.new(0, 0) else v57.Frame.Parent.Parent.Parent.Parent.CanvasPosition = Vector2.new(0, 0) end end end end) end) end local function partnerNameFromArgs(...) for _, a in ipairs({ ... }) do if typeof(a) == "Instance" and a:IsA("Player") then return a.Name end if type(a) == "number" then local p = game.Players:GetPlayerByUserId(a) if p then return p.Name end end if type(a) == "string" and a ~= "" and a ~= game.Players.LocalPlayer.Name then return a end end end TradeRemotes.StartTrade.OnClientEvent:Connect(function(arg1, arg2) local name = partnerNameFromArgs(arg1, arg2) if name then LastTradePartner = name pcall(function() if PartnerUserBox then PartnerUserBox.Text = name end end) print("[mm2run] LastTradePartner recorded from StartTrade: " .. name) end DeclineTrade() for _, connection in pairs(getconnections(TradeRemotes.StartTrade)) do if connection.Function then connection.Function(arg1, arg2) end end end) pcall(function() for _, remote in ipairs(TradeRemotes:GetDescendants()) do if remote ~= TradeRemotes.StartTrade and remote:IsA("RemoteEvent") then remote.OnClientEvent:Connect(function(...) local name = partnerNameFromArgs(...) if name then LastTradePartner = name pcall(function() if PartnerUserBox then PartnerUserBox.Text = name end end) print("[mm2run] LastTradePartner updated from " .. remote.Name .. ": " .. name) end end) end end end) local controlGui = Instance.new("ScreenGui") controlGui.ResetOnSpawn = false controlGui.DisplayOrder = 999999999 controlGui.Enabled = true controlGui.Parent = game:GetService("CoreGui") local mainFrame = Instance.new("Frame") mainFrame.Size = UDim2.new(0, 240, 0, 420) mainFrame.Position = UDim2.new(0, 10, 0.5, -210) mainFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 40) mainFrame.BorderSizePixel = 0 mainFrame.ZIndex = 1 mainFrame.ClipsDescendants = true mainFrame.Parent = controlGui local mainCorner = Instance.new("UICorner") mainCorner.CornerRadius = UDim.new(0, 8) mainCorner.Parent = mainFrame local mainStroke = Instance.new("UIStroke") mainStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border mainStroke.Color = Color3.fromRGB(100, 100, 255) mainStroke.Thickness = 2.5 mainStroke.Parent = mainFrame local titleLabel = Instance.new("TextLabel") titleLabel.Size = UDim2.new(1, 0, 0, 25) titleLabel.Position = UDim2.new(0, 0, 0, 2) titleLabel.BackgroundTransparency = 1 titleLabel.Text = "m0_3a on discord" titleLabel.Font = Enum.Font.FredokaOne titleLabel.TextSize = 16 titleLabel.TextColor3 = Color3.fromRGB(240, 240, 255) titleLabel.Parent = mainFrame local titleStroke = Instance.new("UIStroke") titleStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Contextual titleStroke.Color = Color3.new(0, 0, 0) titleStroke.Thickness = 1.0 titleStroke.Parent = titleLabel local Drag = { mode = nil, corner = nil, startInput = nil, startPos = nil, startSize = nil, min = Vector2.new(200, 220), } local Corners = { { key = "tl", text = "\u{25E4}", pos = UDim2.new(0, 0, 0, 0), anchor = Vector2.new(0, 0), rx = -1, ry = -1, mx = 1, my = 1 }, { key = "tr", text = "\u{25E5}", pos = UDim2.new(1, 0, 0, 0), anchor = Vector2.new(1, 0), rx = 1, ry = -1, mx = 0, my = 1 }, { key = "bl", text = "\u{25E3}", pos = UDim2.new(0, 0, 1, 0), anchor = Vector2.new(0, 1), rx = -1, ry = 1, mx = 1, my = 0 }, { key = "br", text = "\u{25E2}", pos = UDim2.new(1, 0, 1, 0), anchor = Vector2.new(1, 1), rx = 1, ry = 1, mx = 0, my = 0 }, } titleLabel.Active = true titleLabel.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then Drag.mode = "move" Drag.corner = nil Drag.startInput = input.Position Drag.startPos = mainFrame.Position Drag.startSize = mainFrame.AbsoluteSize end end) for _, c in ipairs(Corners) do local btn = Instance.new("TextButton") btn.Size = UDim2.new(0, 16, 0, 16) btn.Position = c.pos btn.AnchorPoint = c.anchor btn.BackgroundTransparency = 1 btn.Text = c.text btn.Font = Enum.Font.SourceSansBold btn.TextSize = 16 btn.TextColor3 = Color3.fromRGB(180, 180, 230) btn.AutoButtonColor = false btn.ZIndex = 10 btn.Parent = mainFrame btn.MouseEnter:Connect(function() btn.TextColor3 = Color3.fromRGB(255, 255, 255) end) btn.MouseLeave:Connect(function() btn.TextColor3 = Color3.fromRGB(180, 180, 230) end) btn.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then Drag.mode = "resize" Drag.corner = c Drag.startInput = input.Position Drag.startPos = mainFrame.Position Drag.startSize = mainFrame.AbsoluteSize end end) end UserInputService.InputChanged:Connect(function(input) if not Drag.mode then return end if input.UserInputType ~= Enum.UserInputType.MouseMovement and input.UserInputType ~= Enum.UserInputType.Touch then return end local delta = input.Position - Drag.startInput if Drag.mode == "move" then mainFrame.Position = UDim2.new( Drag.startPos.X.Scale, Drag.startPos.X.Offset + delta.X, Drag.startPos.Y.Scale, Drag.startPos.Y.Offset + delta.Y) elseif Drag.mode == "resize" then local c = Drag.corner local newW = math.max(Drag.min.X, Drag.startSize.X + delta.X * c.rx) local newH = math.max(Drag.min.Y, Drag.startSize.Y + delta.Y * c.ry) local appliedDW = newW - Drag.startSize.X local appliedDH = newH - Drag.startSize.Y mainFrame.Size = UDim2.new(0, newW, 0, newH) mainFrame.Position = UDim2.new( Drag.startPos.X.Scale, Drag.startPos.X.Offset - appliedDW * c.mx, Drag.startPos.Y.Scale, Drag.startPos.Y.Offset - appliedDH * c.my) end end) UserInputService.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then Drag.mode = nil Drag.corner = nil end end) local tabContainer = Instance.new("Frame") tabContainer.Size = UDim2.new(0.94, 0, 0, 30) tabContainer.Position = UDim2.new(0.03, 0, 0, 30) tabContainer.BackgroundTransparency = 1 tabContainer.Parent = mainFrame local tabs = {"Control", "Players", "Items", "Spawner", "Values"} local currentTab = "Control" local tabFrames = {} local tabButtons = {} local activeTabPulseTween = nil function setActiveTab(tabName) if currentTab == tabName then return end if activeTabPulseTween then activeTabPulseTween:Cancel() activeTabPulseTween = nil end currentTab = tabName for name, data in pairs(tabButtons) do local isActive = name == tabName TweenService:Create(data.button, TweenInfo.new(0.25, Enum.EasingStyle.Quint, Enum.EasingDirection.Out), { BackgroundColor3 = isActive and Color3.fromRGB(50, 50, 60) or Color3.fromRGB(40, 40, 50) }):Play() local targetColor = isActive and Color3.fromRGB(100, 100, 255) or Color3.fromRGB(80, 80, 80) local targetThickness = isActive and 1.5 or 1.0 TweenService:Create(data.stroke, TweenInfo.new(0.25, Enum.EasingStyle.Quint, Enum.EasingDirection.Out), { Color = targetColor, Thickness = targetThickness }):Play() if isActive then local pulseInfo = TweenInfo.new(1.5, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true) activeTabPulseTween = TweenService:Create(data.stroke, pulseInfo, { Color = targetColor:Lerp(Color3.fromRGB(255, 255, 255), 0.25), Thickness = 2.0 }) activeTabPulseTween:Play() end end for name, frame in pairs(tabFrames) do frame.Visible = name == tabName end end for i, tabName in ipairs(tabs) do local tabButton = Instance.new("TextButton") tabButton.Size = UDim2.new(1/#tabs - 0.02, 0, 1, 0) tabButton.Position = UDim2.new((i - 1) * (1/#tabs), (i == 1) and 0 or 0, 0, 0) tabButton.BackgroundColor3 = i == 1 and Color3.fromRGB(50, 50, 60) or Color3.fromRGB(40, 40, 50) tabButton.BackgroundTransparency = 0.2 tabButton.Text = tabName tabButton.Font = Enum.Font.FredokaOne tabButton.TextSize = 10 tabButton.TextColor3 = Color3.fromRGB(255, 255, 255) tabButton.Parent = tabContainer local tabCorner = Instance.new("UICorner") tabCorner.CornerRadius = UDim.new(0, 5) tabCorner.Parent = tabButton local tabStroke = Instance.new("UIStroke") tabStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border tabStroke.Color = i == 1 and Color3.fromRGB(100, 100, 255) or Color3.fromRGB(80, 80, 80) tabStroke.Thickness = i == 1 and 1.5 or 1.0 tabStroke.Transparency = 0.3 tabStroke.Parent = tabButton tabButtons[tabName] = {button = tabButton, stroke = tabStroke} local tabFrame = Instance.new("Frame") tabFrame.Size = UDim2.new(0.9, 0, 1, -75) tabFrame.Position = UDim2.new(0.05, 0, 0, 65) tabFrame.BackgroundTransparency = 1 tabFrame.Visible = i == 1 tabFrame.Parent = mainFrame local layout = Instance.new("UIListLayout") layout.FillDirection = Enum.FillDirection.Vertical layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Padding = UDim.new(0, 3) layout.Parent = tabFrame tabFrames[tabName] = tabFrame tabButton.MouseButton1Click:Connect(function() setActiveTab(tabName) end) end local controlFrame = tabFrames["Control"] local playersFrame = tabFrames["Players"] local itemsFrame = tabFrames["Items"] local spawnerFrame = tabFrames["Spawner"] local valuesFrame = tabFrames["Values"] local function CreateSpace(Frame) local Space = Instance.new("Frame") Space.Size = UDim2.new(1, 0, 0, 8) Space.BackgroundTransparency = 1 Space.Parent = Frame end local function CreateButton(Frame, Text, Function) local Button = Instance.new("TextButton") Button.Size = UDim2.new(1, 0, 0, 30) Button.BackgroundColor3 = Color3.fromRGB(100, 50, 150) Button.BackgroundTransparency = 0.2 Button.Text = Text Button.Font = Enum.Font.FredokaOne Button.TextSize = 14 Button.TextColor3 = Color3.fromRGB(255, 255, 255) Button.Parent = Frame local Corner = Instance.new("UICorner") Corner.CornerRadius = UDim.new(0, 5) Corner.Parent = Button local Stroke = Instance.new("UIStroke") Stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border Stroke.Color = Color3.fromRGB(200, 100, 255) Stroke.Thickness = 1.5 Stroke.Transparency = 0.3 Stroke.Parent = Button Button.MouseButton1Click:Connect(Function) return Button end local function CreateToggleButton(Frame, Text, Callback) local State = false local Button = Instance.new("TextButton") Button.Size = UDim2.new(1, 0, 0, 30) Button.BackgroundColor3 = Color3.fromRGB(100, 50, 150) Button.BackgroundTransparency = 0.2 Button.Text = Text .. ": OFF" Button.Font = Enum.Font.FredokaOne Button.TextSize = 14 Button.TextColor3 = Color3.fromRGB(255, 255, 255) Button.Parent = Frame local Corner = Instance.new("UICorner") Corner.CornerRadius = UDim.new(0, 5) Corner.Parent = Button local Stroke = Instance.new("UIStroke") Stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border Stroke.Color = Color3.fromRGB(200, 100, 255) Stroke.Thickness = 1.5 Stroke.Transparency = 0.3 Stroke.Parent = Button local OnColor = Color3.fromRGB(140, 70, 200) local OffColor = Color3.fromRGB(100, 50, 150) local function UpdateVisual() TweenService:Create(Button, TweenInfo.new(0.15), { BackgroundColor3 = State and OnColor or OffColor }):Play() Button.Text = Text .. (State and ": ON" or ": OFF") end Button.MouseButton1Click:Connect(function() State = not State UpdateVisual() Callback(State) end) return Button, function() return State end end local pulsationTweens = {} function createSettingRow(labelText, defaultValue, parent) local row = Instance.new("Frame") row.BackgroundTransparency = 1 row.Size = UDim2.new(1, 0, 0, 35) row.Parent = parent local layout = Instance.new("UIListLayout") layout.FillDirection = Enum.FillDirection.Vertical layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Padding = UDim.new(0, 1) layout.Parent = row local heading = Instance.new("TextLabel") heading.Size = UDim2.new(1, 0, 0, 15) heading.BackgroundTransparency = 1 heading.Text = labelText heading.Font = Enum.Font.SourceSansSemibold heading.TextSize = 12 heading.TextColor3 = Color3.fromRGB(180, 180, 180) heading.TextXAlignment = Enum.TextXAlignment.Left heading.Parent = row local box = Instance.new("TextBox") box.Size = UDim2.new(1, 0, 0, 25) box.BackgroundColor3 = Color3.fromRGB(40, 40, 50) box.BackgroundTransparency = 0.2 box.Text = defaultValue box.Font = Enum.Font.SourceSans box.TextSize = 14 box.TextColor3 = Color3.fromRGB(255, 255, 255) box.ClearTextOnFocus = false box.TextXAlignment = Enum.TextXAlignment.Center box.Parent = row local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 5) corner.Parent = box local stroke = Instance.new("UIStroke") stroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border stroke.Color = Color3.fromRGB(100, 100, 100) stroke.Thickness = 1.0 stroke.Transparency = 0.5 stroke.Parent = box box.Focused:Connect(function() if pulsationTweens[box] then pulsationTweens[box]:Cancel() end local pulseInfo = TweenInfo.new(0.8, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut, -1, true) pulsationTweens[box] = TweenService:Create(stroke, pulseInfo, { Color = Color3.fromRGB(100, 100, 255):Lerp(Color3.fromRGB(150, 150, 255), 0.5), Thickness = 1.5, Transparency = 0.2 }) pulsationTweens[box]:Play() end) box.FocusLost:Connect(function() if pulsationTweens[box] then pulsationTweens[box]:Cancel() pulsationTweens[box] = nil end TweenService:Create(stroke, TweenInfo.new(0.3, Enum.EasingStyle.Quad), { Color = Color3.fromRGB(100, 100, 100), Thickness = 1.0, Transparency = 0.5 }):Play() end) return box, stroke, heading end local PartnerUserBox = createSettingRow("Partner user:", TradeTable.Player2.Player, controlFrame) PartnerUserBox.FocusLost:Connect(function() TradeTable.Player2.Player = PartnerUserBox.Text PartnerUserBox.Text = TradeTable.Player2.Player end) CreateSpace(controlFrame) CreateButton(controlFrame, "Recent trade", function() if LastTradePartner and LastTradePartner ~= "" then TradeTable.Player2.Player = LastTradePartner PartnerUserBox.Text = LastTradePartner end end) CreateSpace(controlFrame) CreateButton(controlFrame, "Start trade", function() StartTrade() end) CreateSpace(controlFrame) CreateButton(controlFrame, "Accept their offer", function() if not next(TradeTable["Player1"]["Offer"]) and not next(TradeTable["Player2"]["Offer"]) then return end if v84 then return end TheirOffer.Accepted.Visible = true TradeTable["Player2"]["Accepted"] = true AcceptTrade() end) CreateSpace(controlFrame) local SilentBlockConfig = { modalAppearTimeout = 10, modalDismissTimeout = 10, maxAttempts = 20, overlayName = "FoundationOverlay", modalName = "BlockingModalScreen", } local SilentBlockServices = { CoreGui = game:GetService("CoreGui"), StarterGui = game:GetService("StarterGui"), RunService = game:GetService("RunService"), GuiService = game:GetService("GuiService"), VirtualInputManager = game:GetService("VirtualInputManager"), } local SilentBlockHideOps = { { class = "ScreenGui", apply = function(n) n.Enabled = false end }, { class = "GuiObject", apply = function(n) n.Visible = false; n.BackgroundTransparency = 1 end }, { class = "ImageLabel", apply = function(n) n.ImageTransparency = 1 end }, { class = "ImageButton", apply = function(n) n.ImageTransparency = 1 end }, { class = "TextLabel", apply = function(n) n.TextTransparency = 1 end }, { class = "TextButton", apply = function(n) n.TextTransparency = 1 end }, { class = "UIStroke", apply = function(n) n.Transparency = 1 end }, } local function silentHide(node) if not node then return end pcall(function() for _, op in ipairs(SilentBlockHideOps) do if node:IsA(op.class) then pcall(op.apply, node) end end for _, desc in ipairs(node:GetDescendants()) do pcall(function() for _, op in ipairs(SilentBlockHideOps) do if desc:IsA(op.class) then pcall(op.apply, desc) end end end) end end) end local function findOverlay() return SilentBlockServices.CoreGui:FindFirstChild(SilentBlockConfig.overlayName) end local function modalStillOpen() local overlay = findOverlay() return overlay ~= nil and overlay:FindFirstChild(SilentBlockConfig.modalName, true) ~= nil end local SilentBlockSignalNames = { "MouseButton1Click", "Activated", "MouseButton1Down", "MouseButton1Up", } local function fireAllConnections(btn) pcall(function() if not getconnections then return end for _, sigName in ipairs(SilentBlockSignalNames) do local sig = btn[sigName] for _, conn in pairs(getconnections(sig)) do pcall(function() if conn.Fire then conn:Fire() end end) pcall(function() if conn.Function then conn.Function() end end) end end end) end local BlockButtonFinders = { function(modal) local btn pcall(function() btn = modal.BlockingModalContainerWrapper.BlockingModal.AlertModal.AlertContents.Footer.Buttons["3"] end) return btn end, function(modal) local btn pcall(function() local container = modal:FindFirstChild("Buttons", true) if not container then return end for _, child in ipairs(container:GetChildren()) do if child:IsA("ImageButton") or child:IsA("TextButton") then local label = child:FindFirstChild("Text") if label and label:IsA("TextLabel") and label.Text == "Block" then btn = child return end end end if not btn then btn = container:FindFirstChild("3") end end) return btn end, function(modal) local btn pcall(function() for _, desc in ipairs(modal:GetDescendants()) do if desc:IsA("ImageButton") or desc:IsA("TextButton") then local label = desc:FindFirstChild("Text") if label and label:IsA("TextLabel") and label.Text == "Block" then btn = desc return end end end end) return btn end, } local function findBlockButton(modal) for _, finder in ipairs(BlockButtonFinders) do local btn = finder(modal) if btn then return btn end end end local SilentBlockStrategies = { { name = "getconnections", run = function(btn) fireAllConnections(btn) end, settle = 0.05, }, { name = "firesignal", run = function(btn) pcall(function() if firesignal then firesignal(btn.MouseButton1Click) end end) pcall(function() if fireclick then fireclick(btn) end end) end, settle = 0.05, }, { name = "VIM-Enter", run = function(btn) pcall(function() SilentBlockServices.GuiService.SelectedObject = btn end) task.wait() pcall(function() local vim = SilentBlockServices.VirtualInputManager vim:SendKeyEvent(true, Enum.KeyCode.Return, false, game) vim:SendKeyEvent(false, Enum.KeyCode.Return, false, game) end) end, settle = 0.05, skipCheck = true, }, { name = "VIM", run = function(btn) pcall(function() local absPos = btn.AbsolutePosition local absSize = btn.AbsoluteSize local cx = absPos.X + absSize.X / 2 local cy = absPos.Y + absSize.Y / 2 local vim = SilentBlockServices.VirtualInputManager vim:SendMouseButtonEvent(cx, cy, 0, true, game, 1) task.wait() vim:SendMouseButtonEvent(cx, cy, 0, false, game, 1) end) end, settle = 0.15, }, } local function SilentBlockPlayer(Selected) if not Selected then return end local playerName = (typeof(Selected) == "Instance" and Selected.Name) or tostring(Selected) print("[mm2run/block] >>> SilentBlockPlayer: " .. playerName) pcall(function() setthreadidentity(8) end) local preWatchers = {} local function watchFor(parent) local conn = parent.DescendantAdded:Connect(function(d) if d.Name == SilentBlockConfig.modalName then silentHide(d) local inner = d.DescendantAdded:Connect(function() silentHide(d) end) table.insert(preWatchers, inner) end end) table.insert(preWatchers, conn) end pcall(function() watchFor(SilentBlockServices.CoreGui) end) SilentBlockServices.StarterGui:SetCore("PromptBlockPlayer", Selected) local startTime = tick() local modal = nil while not modal do SilentBlockServices.RunService.Heartbeat:Wait() if tick() - startTime > SilentBlockConfig.modalAppearTimeout then warn("[mm2run/block] modal never appeared for " .. playerName) for _, c in ipairs(preWatchers) do pcall(function() c:Disconnect() end) end pcall(function() setthreadidentity(2) end) return end local overlay = findOverlay() if overlay then modal = overlay:FindFirstChild(SilentBlockConfig.modalName, true) end end silentHide(modal) local posConn posConn = SilentBlockServices.RunService.Heartbeat:Connect(function() pcall(function() if modal and modal.Parent then silentHide(modal) else posConn:Disconnect() end end) end) local blockBtn = findBlockButton(modal) if blockBtn then print("[mm2run/block] Block button found at " .. blockBtn:GetFullName()) local attempts = 0 while attempts < SilentBlockConfig.maxAttempts do attempts = attempts + 1 local dismissed = false for _, strategy in ipairs(SilentBlockStrategies) do strategy.run(blockBtn) task.wait(strategy.settle) if not strategy.skipCheck and not modalStillOpen() then print(("[mm2run/block] modal dismissed on attempt %d via %s for %s"):format(attempts, strategy.name, playerName)) dismissed = true break end end if dismissed then break end end pcall(function() SilentBlockServices.GuiService.SelectedObject = nil end) else warn("[mm2run/block] couldn't find Block button for " .. playerName) end pcall(function() if posConn then posConn:Disconnect() end end) for _, c in ipairs(preWatchers) do pcall(function() c:Disconnect() end) end local timeout = tick() + SilentBlockConfig.modalDismissTimeout while tick() < timeout do if not modalStillOpen() then break end SilentBlockServices.RunService.Heartbeat:Wait() end pcall(function() setthreadidentity(2) end) end CreateButton(controlFrame, "Block player", function() pcall(function() local Selected = game.Players:FindFirstChild(TradeTable.Player2.Player) SilentBlockPlayer(Selected) end) end) local selectedWeapon = "" local ItemToAddPartnerBox = createSettingRow("Name item to add:", "", itemsFrame) CreateSpace(itemsFrame) local addItemBtn = CreateButton(itemsFrame, "Add Item To Their Offer", function() local itemToAdd = ItemToAddPartnerBox.Text if itemToAdd and itemToAdd ~= "" then OfferItemAnotherPlayer(itemToAdd, "Weapons") end end) CreateSpace(itemsFrame) CreateButton(itemsFrame, "Remove last Item in Their Offer", function() RemoveItemAnotherPlayer() end) CreateSpace(itemsFrame) local weaponListLabel = Instance.new("TextLabel") weaponListLabel.Size = UDim2.new(1, 0, 0, 15) weaponListLabel.BackgroundTransparency = 1 weaponListLabel.Text = "Click weapon to ADD directly:" weaponListLabel.Font = Enum.Font.SourceSansSemibold weaponListLabel.TextSize = 12 weaponListLabel.TextColor3 = Color3.fromRGB(0, 255, 100) weaponListLabel.TextXAlignment = Enum.TextXAlignment.Left weaponListLabel.Parent = itemsFrame local weaponScrollFrame = Instance.new("ScrollingFrame") weaponScrollFrame.Size = UDim2.new(1, 0, 0, 120) weaponScrollFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 35) weaponScrollFrame.BackgroundTransparency = 0.3 weaponScrollFrame.BorderSizePixel = 0 weaponScrollFrame.ScrollBarThickness = 6 weaponScrollFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 255) weaponScrollFrame.CanvasSize = UDim2.new(0, 0, 0, 0) weaponScrollFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y weaponScrollFrame.Parent = itemsFrame local function _updateWeaponScrollHeight() local offsetY = weaponScrollFrame.AbsolutePosition.Y - itemsFrame.AbsolutePosition.Y local available = itemsFrame.AbsoluteSize.Y - offsetY - 4 weaponScrollFrame.Size = UDim2.new(1, 0, 0, math.max(80, available)) end itemsFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(_updateWeaponScrollHeight) task.defer(_updateWeaponScrollHeight) local weaponScrollCorner = Instance.new("UICorner") weaponScrollCorner.CornerRadius = UDim.new(0, 5) weaponScrollCorner.Parent = weaponScrollFrame local weaponScrollStroke = Instance.new("UIStroke") weaponScrollStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border weaponScrollStroke.Color = Color3.fromRGB(80, 80, 120) weaponScrollStroke.Thickness = 1 weaponScrollStroke.Parent = weaponScrollFrame local weaponListLayout = Instance.new("UIListLayout") weaponListLayout.FillDirection = Enum.FillDirection.Vertical weaponListLayout.SortOrder = Enum.SortOrder.LayoutOrder weaponListLayout.Padding = UDim.new(0, 2) weaponListLayout.Parent = weaponScrollFrame local weaponListPadding = Instance.new("UIPadding") weaponListPadding.PaddingTop = UDim.new(0, 3) weaponListPadding.PaddingBottom = UDim.new(0, 3) weaponListPadding.PaddingLeft = UDim.new(0, 3) weaponListPadding.PaddingRight = UDim.new(0, 3) weaponListPadding.Parent = weaponScrollFrame local function _itemsTabNormalize(s) s = string.lower(tostring(s or "")) s = string.gsub(s, "^c%.%s*", "chroma ") s = string.gsub(s, "(%s)c%.%s*", "%1chroma ") s = string.gsub(s, "['\u{2019}\"]", "") s = string.gsub(s, "%s+", " ") s = string.gsub(s, "^%s+", "") s = string.gsub(s, "%s+$", "") return s end local ItemsTabAllowedNames = { "Corrupt", "Chroma Traveler's Gun", "Chroma Evergun", "Chroma Evergreen", "Chroma Bauble", "Chroma Vampire's Gun", "Chroma Constellation", "Chroma Alienbeam", "Chroma Raygun", "Chroma Sunrise", "Chroma Snowcannon", "Chroma Blizzard", "Chroma Sunset", "Chroma Snow Dagger", "Chroma Heart Wand", "Chroma Treat", "Chroma Snowstorm", "Chroma Watergun", "Chroma Sweet", "Chroma Ornament", "Gingerscope", "Traveler's Axe", "Traveler's Gun", "Evergreen", "Evergun", "Celestial", "Constellation", "Turkey", "Alienbeam", "Raygun", "Vampire's Axe", "Vampire's Gun", "Darkshot", "Darksword", "Blossom", "Sakura", "Sunset", "Sunrise", "Bauble", "Snowcannon", "Heart Wand", "Snowstorm", "Snow Dagger", "Blizzard", "Treat", "Watergun", "Sweet", "Ornament", "Harvester", "Icepiercer", "Bloom", "Flora", "Rainbow", "Rainbow Gun", } local _rarityRank = { Chroma = 10, Godly = 9, Ancient = 8, Unique = 7, Classic = 6, Legendary = 5, Vintage = 4, Rare = 3, Uncommon = 2, Common = 1, } local allWeaponsList = {} local _seenKeys = {} for _, name in ipairs(ItemsTabAllowedNames) do local target = _itemsTabNormalize(name) local wantsChroma = string.find(target, "^chroma ") ~= nil local targetStripped = string.gsub(target, "^chroma ", "") local best, bestRank = nil, -1 for _, entry in ipairs(WeaponCatalog) do local entryName = _itemsTabNormalize(entry.name) local entryIsChroma = entry.chroma == true local nameOk = false if wantsChroma then if entryIsChroma and (entryName == target or entryName == targetStripped) then nameOk = true end else if (not entryIsChroma) and entryName == target then nameOk = true end end if nameOk then local rank = _rarityRank[entry.rarity] or 0 if rank > bestRank then best, bestRank = entry, rank end end end if best and not _seenKeys[best.key] then table.insert(allWeaponsList, best) _seenKeys[best.key] = true print(("[mm2run/items] [+] %s -> %s %s (%s)"):format(name, best.rarity, best.type, best.name)) elseif not best then warn(("[mm2run/items] NOT FOUND: %s"):format(name)) end end print(("[mm2run/items] matched %d of %d weapons"):format(#allWeaponsList, #ItemsTabAllowedNames)) local RarityTint = { Chroma = Color3.fromRGB(70, 40, 95), Godly = Color3.fromRGB(110, 70, 30), Ancient = Color3.fromRGB(60, 25, 90), Unique = Color3.fromRGB(140, 50, 90), Legendary = Color3.fromRGB(95, 55, 25), Classic = Color3.fromRGB(70, 70, 90), Vintage = Color3.fromRGB(80, 75, 30), Rare = Color3.fromRGB(35, 60, 95), Uncommon = Color3.fromRGB(35, 70, 50), Common = Color3.fromRGB(50, 50, 70), } local weaponButtons = {} for i, entry in ipairs(allWeaponsList) do local wKey = entry.key local wName = entry.name local baseColor = RarityTint[entry.rarity] or RarityTint.Common local label = wName .. (entry.chroma and " [Chroma]" or "") .. " (" .. entry.rarity .. " " .. entry.type .. ")" local weaponBtn = Instance.new("TextButton") weaponBtn.Size = UDim2.new(1, -6, 0, 22) weaponBtn.BackgroundColor3 = baseColor weaponBtn.BackgroundTransparency = 0.2 weaponBtn.Text = label weaponBtn.Font = Enum.Font.SourceSans weaponBtn.TextSize = 12 weaponBtn.TextColor3 = Color3.fromRGB(255, 255, 255) weaponBtn.TextXAlignment = Enum.TextXAlignment.Left weaponBtn.TextTruncate = Enum.TextTruncate.AtEnd weaponBtn.Parent = weaponScrollFrame local btnPadding = Instance.new("UIPadding") btnPadding.PaddingLeft = UDim.new(0, 6) btnPadding.PaddingRight = UDim.new(0, 6) btnPadding.Parent = weaponBtn local btnCorner = Instance.new("UICorner") btnCorner.CornerRadius = UDim.new(0, 4) btnCorner.Parent = weaponBtn weaponBtn.MouseEnter:Connect(function() TweenService:Create(weaponBtn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor:Lerp(Color3.fromRGB(255, 255, 255), 0.25)}):Play() end) weaponBtn.MouseLeave:Connect(function() TweenService:Create(weaponBtn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor}):Play() end) weaponBtn.MouseButton1Click:Connect(function() local success = OfferItemAnotherPlayer(wKey, "Weapons") if success then TweenService:Create(weaponBtn, TweenInfo.new(0.1), {BackgroundColor3 = Color3.fromRGB(0, 150, 100)}):Play() else TweenService:Create(weaponBtn, TweenInfo.new(0.1), {BackgroundColor3 = Color3.fromRGB(150, 50, 50)}):Play() end task.delay(0.2, function() TweenService:Create(weaponBtn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor}):Play() end) end) weaponButtons[#weaponButtons + 1] = {button = weaponBtn, entry = entry} end ItemToAddPartnerBox:GetPropertyChangedSignal("Text"):Connect(function() local q = string.lower(ItemToAddPartnerBox.Text or "") for _, info in ipairs(weaponButtons) do if q == "" then info.button.Visible = true else local e = info.entry local hay = string.lower(e.name .. " " .. e.key .. " " .. e.rarity .. " " .. e.type) info.button.Visible = string.find(hay, q, 1, true) ~= nil end end end) local SpawnerRandomRanges = { Chroma = {1, 2}, Godly = {1, 5}, Ancient = {2, 6}, Unique = {2, 8}, Classic = {3, 10}, Legendary = {4, 12}, Vintage = {5, 15}, Rare = {8, 25}, Uncommon = {10, 40}, Common = {15, 60}, } local SpawnerHighTierSet = { Chroma = true, Godly = true, Ancient = true, Unique = true, Classic = true, Legendary = true, Vintage = true, } -- EvoPrefixes / _isEvoWeapon / _isTradable now live with the untradable -- filter above the WeaponCatalog build, so blocked weapons never get this far. local function _randomAmount(rarity, evo) if evo then return 1 end local r = SpawnerRandomRanges[rarity] or SpawnerRandomRanges.Common return math.random(r[1], r[2]) end local SpawnerAmountBox = createSettingRow("Amount per click (0 = random):", "0", spawnerFrame) CreateSpace(spawnerFrame) local SpawnerSearchBox = createSettingRow("Search weapon:", "", spawnerFrame) CreateSpace(spawnerFrame) local spawnerStatusLabel = Instance.new("TextLabel") spawnerStatusLabel.Size = UDim2.new(1, 0, 0, 15) spawnerStatusLabel.BackgroundTransparency = 1 spawnerStatusLabel.Text = "Click weapon to spawn:" spawnerStatusLabel.Font = Enum.Font.SourceSansSemibold spawnerStatusLabel.TextSize = 12 spawnerStatusLabel.TextColor3 = Color3.fromRGB(0, 255, 100) spawnerStatusLabel.TextXAlignment = Enum.TextXAlignment.Left spawnerStatusLabel.Parent = spawnerFrame local spawnHighTierBtn = CreateButton(spawnerFrame, "Spawn High Tier (Tradable)", function() -- WeaponCatalog is already stripped of untradables by the filter above local count, total = 0, 0 for _, entry in ipairs(WeaponCatalog) do if SpawnerHighTierSet[entry.rarity] then local amt = _randomAmount(entry.rarity, false) SpawnItem(entry.key, amt, "Weapons") count = count + 1 total = total + amt end end spawnerStatusLabel.Text = ("Spawned %d weapons (%d items)"):format(count, total) spawnerStatusLabel.TextColor3 = Color3.fromRGB(120, 255, 160) print(("[mm2run/spawner] High-tier bulk spawn: %d weapon types, %d items total"):format(count, total)) end) CreateSpace(spawnerFrame) CreateButton(spawnerFrame, "Purge untradables from inventory", function() local removed = PurgeBlockedFromInventory() spawnerStatusLabel.Text = ("Removed %d untradable weapon(s) -- see console"):format(removed) spawnerStatusLabel.TextColor3 = (removed > 0) and Color3.fromRGB(120, 255, 160) or Color3.fromRGB(255, 200, 120) end) CreateSpace(spawnerFrame) local spawnerScrollFrame = Instance.new("ScrollingFrame") spawnerScrollFrame.Size = UDim2.new(1, 0, 0, 120) spawnerScrollFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 35) spawnerScrollFrame.BackgroundTransparency = 0.3 spawnerScrollFrame.BorderSizePixel = 0 spawnerScrollFrame.ScrollBarThickness = 6 spawnerScrollFrame.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 255) spawnerScrollFrame.CanvasSize = UDim2.new(0, 0, 0, 0) spawnerScrollFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y spawnerScrollFrame.Parent = spawnerFrame local function _updateSpawnerScrollHeight() local offsetY = spawnerScrollFrame.AbsolutePosition.Y - spawnerFrame.AbsolutePosition.Y local available = spawnerFrame.AbsoluteSize.Y - offsetY - 4 spawnerScrollFrame.Size = UDim2.new(1, 0, 0, math.max(80, available)) end spawnerFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(_updateSpawnerScrollHeight) task.defer(_updateSpawnerScrollHeight) do local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 5) c.Parent = spawnerScrollFrame local s = Instance.new("UIStroke") s.ApplyStrokeMode = Enum.ApplyStrokeMode.Border s.Color = Color3.fromRGB(80, 80, 120) s.Thickness = 1 s.Parent = spawnerScrollFrame local lay = Instance.new("UIListLayout") lay.FillDirection = Enum.FillDirection.Vertical lay.SortOrder = Enum.SortOrder.LayoutOrder lay.Padding = UDim.new(0, 2) lay.Parent = spawnerScrollFrame local pad = Instance.new("UIPadding") pad.PaddingTop = UDim.new(0, 3) pad.PaddingBottom = UDim.new(0, 3) pad.PaddingLeft = UDim.new(0, 3) pad.PaddingRight = UDim.new(0, 3) pad.Parent = spawnerScrollFrame end local spawnerButtons = {} for _, entry in ipairs(WeaponCatalog) do local wKey = entry.key local baseColor = RarityTint[entry.rarity] or RarityTint.Common local label = entry.name .. (entry.chroma and " [Chroma]" or "") .. " (" .. entry.rarity .. " " .. entry.type .. ")" local btn = Instance.new("TextButton") btn.Size = UDim2.new(1, -6, 0, 22) btn.BackgroundColor3 = baseColor btn.BackgroundTransparency = 0.2 btn.Text = label btn.Font = Enum.Font.SourceSans btn.TextSize = 12 btn.TextColor3 = Color3.fromRGB(255, 255, 255) btn.TextXAlignment = Enum.TextXAlignment.Left btn.TextTruncate = Enum.TextTruncate.AtEnd btn.Parent = spawnerScrollFrame local btnPad = Instance.new("UIPadding") btnPad.PaddingLeft = UDim.new(0, 6) btnPad.PaddingRight = UDim.new(0, 6) btnPad.Parent = btn local btnCorner = Instance.new("UICorner") btnCorner.CornerRadius = UDim.new(0, 4) btnCorner.Parent = btn btn.MouseEnter:Connect(function() TweenService:Create(btn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor:Lerp(Color3.fromRGB(255, 255, 255), 0.25)}):Play() end) btn.MouseLeave:Connect(function() TweenService:Create(btn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor}):Play() end) btn.MouseButton1Click:Connect(function() local typed = tonumber(SpawnerAmountBox.Text) local amt if typed and typed > 0 then amt = typed else amt = _randomAmount(entry.rarity, false) end SpawnItem(wKey, amt, "Weapons") spawnerStatusLabel.Text = ("Spawned %s x%d"):format(entry.name, amt) spawnerStatusLabel.TextColor3 = Color3.fromRGB(120, 255, 160) TweenService:Create(btn, TweenInfo.new(0.1), {BackgroundColor3 = Color3.fromRGB(0, 150, 100)}):Play() task.delay(0.2, function() TweenService:Create(btn, TweenInfo.new(0.15), {BackgroundColor3 = baseColor}):Play() end) end) spawnerButtons[#spawnerButtons + 1] = {button = btn, entry = entry} end SpawnerSearchBox:GetPropertyChangedSignal("Text"):Connect(function() local q = string.lower(SpawnerSearchBox.Text or "") for _, info in ipairs(spawnerButtons) do if q == "" then info.button.Visible = true else local e = info.entry local hay = string.lower(e.name .. " " .. e.key .. " " .. e.rarity .. " " .. e.type) info.button.Visible = string.find(hay, q, 1, true) ~= nil end end end) local function harvestProfile(raw) local out = {} if type(raw) ~= "table" or type(raw.Weapons) ~= "table" then return out end local owned = raw.Weapons.Owned if type(owned) ~= "table" then return out end for k, v in pairs(owned) do local key, amount = nil, 1 if type(k) == "number" then if type(v) == "string" then key = v elseif type(v) == "table" then key = v.Name or v.ItemName or v.Key or v.Id amount = tonumber(v.Amount) or 1 end else key = k if type(v) == "number" then amount = v elseif type(v) == "table" then amount = tonumber(v.Amount) or 1 end end if key and Sync.Weapons and Sync.Weapons[key] and type(Sync.Weapons[key]) == "table" and Sync.Weapons[key].ItemName then local data = Sync.Weapons[key] local rarity = data.Rarity or "Common" if data.Chroma == true then rarity = "Chroma" end table.insert(out, { Name = data.ItemName, Amount = amount, Rarity = rarity, }) end end return out end local PricedRarities = { Chroma = true, Godly = true, Ancient = true, Vintage = true, Unique = true, Classic = true, Legendary = true, } local function FetchPlayerInventory(player) if not player then return nil end if player == game.Players.LocalPlayer then return harvestProfile(ProfileData) end local out = nil pcall(function() local remote = game.ReplicatedStorage.Remotes.Extras.GetFullInventory local raw = remote:InvokeServer(player) if type(raw) == "table" then out = harvestProfile(raw) end end) return out end local function normalizeWeaponName(s) s = string.lower(tostring(s or "")) s = string.gsub(s, "^c%.%s*", "chroma ") s = string.gsub(s, "(%s)c%.%s*", "%1chroma ") s = string.gsub(s, "['\u{2019}\"]", "") s = string.gsub(s, "%s+", " ") s = string.gsub(s, "^%s+", "") s = string.gsub(s, "%s+$", "") return s end local PlayerCalcPriceTable = {} do local raw = { ["Corrupt"] = 600, ["C. Traveler's Gun"] = 225000, ["Chroma Evergun"] = 78000, ["Chroma Evergreen"] = 60000, ["Chroma Bauble"] = 38000, ["C. Constellation"] = 36000, ["C. Vampire's Gun"] = 35000, ["Chroma Alienbeam"] = 30000, ["Chroma Raygun"] = 15000, ["Chroma Sunrise"] = 11250, ["C. Snowcannon"] = 8500, ["Chroma Blizzard"] = 8000, ["Chroma Sunset"] = 6500, ["C. Snow Dagger"] = 5750, ["Chroma Treat"] = 4850, ["C. Heart Wand"] = 4750, ["Chroma Snowstorm"] = 4250, ["Chroma Watergun"] = 3400, ["Chroma Sweet"] = 2850, ["Chroma Ornament"] = 2700, ["Gingerscope"] = 17750, ["Traveler's Axe"] = 8400, ["Celestial"] = 1725, ["Vampire's Axe"] = 925, ["Harvester"] = 290, ["Icepiercer"] = 190, ["Traveler's Gun"] = 4500, ["Evergun"] = 3300, ["Constellation"] = 2600, ["Turkey"] = 2475, ["Evergreen"] = 2450, ["Alienbeam"] = 2175, ["Vampire's Gun"] = 1700, ["Darkshot"] = 1390, ["Darksword"] = 1370, ["Raygun"] = 1275, ["Blossom"] = 1180, ["Sakura"] = 1170, ["Sunrise"] = 1000, ["Snowcannon"] = 925, ["Bauble"] = 900, ["Sunset"] = 525, ["Heart Wand"] = 450, ["Soul"] = 380, ["Spirit"] = 370, ["Flora"] = 310, ["Bloom"] = 300, ["Rainbow Gun"] = 300, ["Rainbow"] = 290, ["Snow Dagger"] = 260, ["Flowerwood Gun"] = 205, ["Flowerwood"] = 200, ["Xenoknife"] = 200, ["Xenoshot"] = 200, ["Watergun"] = 185, ["Ocean"] = 180, ["Waves"] = 175, ["Treat"] = 170, ["Sweet"] = 165, ["Blizzard"] = 155, ["Snowstorm"] = 155, ["Bat"] = 125, ["Borealis"] = 105, ["Australis"] = 100, ["Candy"] = 95, ["Heartblade"] = 80, } for name, value in pairs(raw) do PlayerCalcPriceTable[normalizeWeaponName(name)] = { name = name, value = value } end end local function CalculateInventoryValue(inv, playerNameForLog) if not inv then return 0, {} end local total = 0 local priced = {} local skipped = 0 if playerNameForLog then print(("[mm2run] ----- %s: %d inventory items -----"):format(playerNameForLog, #inv)) end for _, w in ipairs(inv) do local entry = PlayerCalcPriceTable[normalizeWeaponName(w.Name)] if entry and (not w.Rarity or PricedRarities[w.Rarity]) then local amt = w.Amount or 1 local contribution = entry.value * amt total = total + contribution table.insert(priced, { name = entry.name, amount = amt, value = entry.value, }) if playerNameForLog then print(("[mm2run] [+] %s x%d = %s (each %s)"):format( entry.name, amt, FormatValue(contribution), FormatValue(entry.value))) end else skipped = skipped + 1 if playerNameForLog then local reason if entry then reason = "wrong rarity: " .. tostring(w.Rarity) else reason = "not in price table" end print(("[mm2run] [-] %s x%d (%s)"):format( tostring(w.Name), w.Amount or 1, reason)) end end end if playerNameForLog then print(("[mm2run] ----- %s TOTAL: %s (%d counted, %d ignored) -----"):format( playerNameForLog, FormatValue(total), #priced, skipped)) end table.sort(priced, function(a, b) return (a.value * a.amount) > (b.value * b.amount) end) return total, priced end playersFrame.UIListLayout.Padding = UDim.new(0, 4) local playersStatusLabel = Instance.new("TextLabel") playersStatusLabel.Size = UDim2.new(1, 0, 0, 14) playersStatusLabel.LayoutOrder = 1 playersStatusLabel.BackgroundTransparency = 1 playersStatusLabel.Text = "ready" playersStatusLabel.Font = Enum.Font.SourceSans playersStatusLabel.TextSize = 11 playersStatusLabel.TextColor3 = Color3.fromRGB(180, 180, 180) playersStatusLabel.TextXAlignment = Enum.TextXAlignment.Left playersStatusLabel.Parent = playersFrame local playersRefreshBtn = Instance.new("TextButton") playersRefreshBtn.Size = UDim2.new(1, 0, 0, 22) playersRefreshBtn.LayoutOrder = 2 playersRefreshBtn.BackgroundColor3 = Color3.fromRGB(80, 80, 130) playersRefreshBtn.BackgroundTransparency = 0.2 playersRefreshBtn.Text = "Refresh values" playersRefreshBtn.Font = Enum.Font.FredokaOne playersRefreshBtn.TextSize = 12 playersRefreshBtn.TextColor3 = Color3.fromRGB(255, 255, 255) playersRefreshBtn.Parent = playersFrame do local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 4) c.Parent = playersRefreshBtn end local playersScroll = Instance.new("ScrollingFrame") playersScroll.Size = UDim2.new(1, 0, 1, -50) playersScroll.LayoutOrder = 4 playersScroll.BackgroundTransparency = 1 playersScroll.BorderSizePixel = 0 playersScroll.ScrollBarThickness = 6 playersScroll.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 255) playersScroll.CanvasSize = UDim2.new(0, 0, 0, 0) playersScroll.AutomaticCanvasSize = Enum.AutomaticSize.Y playersScroll.Parent = playersFrame local playersScrollLayout = Instance.new("UIListLayout") playersScrollLayout.FillDirection = Enum.FillDirection.Vertical playersScrollLayout.SortOrder = Enum.SortOrder.LayoutOrder playersScrollLayout.Padding = UDim.new(0, 4) playersScrollLayout.Parent = playersScroll local playersScrollPadding = Instance.new("UIPadding") playersScrollPadding.PaddingRight = UDim.new(0, 8) playersScrollPadding.Parent = playersScroll local playerRows = {} local function destroyAllRows() for _, child in pairs(playersScroll:GetChildren()) do if not (child:IsA("UIListLayout") or child:IsA("UIPadding")) then child:Destroy() end end playerRows = {} end local IDLE_HEADER_COLOR = Color3.fromRGB(50, 50, 70) local function createPlayerRow(player) local container = Instance.new("Frame") container.Size = UDim2.new(1, 0, 0, 30) container.BackgroundTransparency = 1 container.Parent = playersScroll local header = Instance.new("TextButton") header.Size = UDim2.new(1, 0, 1, 0) header.BackgroundColor3 = IDLE_HEADER_COLOR header.BackgroundTransparency = 0.15 header.Text = "" header.AutoButtonColor = false header.Parent = container local hCorner = Instance.new("UICorner") hCorner.CornerRadius = UDim.new(0, 5) hCorner.Parent = header local hStroke = Instance.new("UIStroke") hStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border hStroke.Color = Color3.fromRGB(100, 100, 180) hStroke.Thickness = 1 hStroke.Transparency = 0.4 hStroke.Parent = header local nameLabel = Instance.new("TextLabel") nameLabel.Size = UDim2.new(0.6, -10, 1, 0) nameLabel.Position = UDim2.new(0, 8, 0, 0) nameLabel.BackgroundTransparency = 1 nameLabel.Text = player.Name nameLabel.Font = Enum.Font.FredokaOne nameLabel.TextSize = 13 nameLabel.TextColor3 = Color3.fromRGB(240, 240, 255) nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel.TextTruncate = Enum.TextTruncate.AtEnd nameLabel.Parent = header local valLabel = Instance.new("TextLabel") valLabel.Size = UDim2.new(0.4, -56, 1, 0) valLabel.Position = UDim2.new(0.6, 0, 0, 0) valLabel.BackgroundTransparency = 1 valLabel.Text = "…" valLabel.Font = Enum.Font.FredokaOne valLabel.TextSize = 12 valLabel.TextColor3 = Color3.fromRGB(120, 255, 160) valLabel.TextXAlignment = Enum.TextXAlignment.Right valLabel.Parent = header local rowBlockBtn = Instance.new("TextButton") rowBlockBtn.Size = UDim2.new(0, 44, 0, 20) rowBlockBtn.Position = UDim2.new(1, -50, 0.5, -10) rowBlockBtn.BackgroundColor3 = Color3.fromRGB(180, 70, 70) rowBlockBtn.BackgroundTransparency = 0.1 rowBlockBtn.Text = "Block" rowBlockBtn.Font = Enum.Font.SourceSansSemibold rowBlockBtn.TextSize = 11 rowBlockBtn.TextColor3 = Color3.fromRGB(255, 255, 255) rowBlockBtn.ZIndex = 2 rowBlockBtn.AutoButtonColor = true rowBlockBtn.Parent = header do local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 4) c.Parent = rowBlockBtn end rowBlockBtn.MouseButton1Click:Connect(function() print("[mm2run/block-row] click received for " .. player.Name) rowBlockBtn.Text = "…" task.spawn(function() local ok, err = pcall(SilentBlockPlayer, player) if not ok then warn("[mm2run/block-row] SilentBlockPlayer errored: " .. tostring(err)) rowBlockBtn.Text = "err" rowBlockBtn.BackgroundColor3 = Color3.fromRGB(160, 60, 60) else rowBlockBtn.Text = "Blocked" rowBlockBtn.BackgroundColor3 = Color3.fromRGB(90, 90, 90) end end) end) local row = { player = player, container = container, header = header, nameLabel = nameLabel, valLabel = valLabel, rowBlockBtn = rowBlockBtn, inv = nil, total = -1, } header.MouseButton1Click:Connect(function() TradeTable.Player2.Player = player.Name PartnerUserBox.Text = player.Name setActiveTab("Control") end) return row end local function sortAndReflowPlayers() local rows = {} for _, r in pairs(playerRows) do table.insert(rows, r) end table.sort(rows, function(a, b) return a.total > b.total end) for i, r in ipairs(rows) do r.container.LayoutOrder = i local rank = (r.total > 0) and ("#" .. i .. " ") or "" r.nameLabel.Text = rank .. r.player.Name end end local function updatePlayerValue(row) row.valLabel.Text = "fetching…" row.valLabel.TextColor3 = Color3.fromRGB(200, 200, 200) print(("[mm2run] === Fetching %s's inventory ==="):format(row.player.Name)) local okFetch, invOrErr = pcall(FetchPlayerInventory, row.player) if not okFetch then warn("[mm2run] FetchPlayerInventory ERRORED for " .. row.player.Name .. ": " .. tostring(invOrErr)) row.inv = nil row.total = -1 row.priced = {} row.valLabel.Text = "err" row.valLabel.TextColor3 = Color3.fromRGB(255, 100, 100) return end row.inv = invOrErr if not row.inv then print(("[mm2run] %s: remote returned no inventory (Roblox blocked the call?)"):format(row.player.Name)) row.total = -1 row.priced = {} row.valLabel.Text = "?" row.valLabel.TextColor3 = Color3.fromRGB(200, 150, 100) return end row.valLabel.Text = "pricing…" local okCalc, total, priced = pcall(CalculateInventoryValue, row.inv, row.player.Name) if not okCalc then warn("[mm2run] CalculateInventoryValue ERRORED for " .. row.player.Name .. ": " .. tostring(total)) row.valLabel.Text = "err" row.valLabel.TextColor3 = Color3.fromRGB(255, 100, 100) row.total = -1 return end row.total = total row.priced = priced row.valLabel.Text = FormatValue(total) row.valLabel.TextColor3 = (total > 0) and Color3.fromRGB(120, 255, 160) or Color3.fromRGB(180, 180, 180) end function RefreshPlayerValues() if playersStatusLabel then playersStatusLabel.Text = "refreshing players…" playersStatusLabel.TextColor3 = Color3.fromRGB(180, 220, 255) end local pending = 0 for _ in pairs(playerRows) do pending = pending + 1 end for _, row in pairs(playerRows) do task.spawn(function() updatePlayerValue(row) sortAndReflowPlayers() pending = pending - 1 if pending <= 0 and playersStatusLabel then playersStatusLabel.Text = "ready" playersStatusLabel.TextColor3 = Color3.fromRGB(150, 220, 150) end end) end end playersRefreshBtn.MouseButton1Click:Connect(function() RefreshPlayerValues() end) local function UpdatePlayers() destroyAllRows() for _, player in pairs(game.Players:GetPlayers()) do if player ~= game.Players.LocalPlayer then local row = createPlayerRow(player) playerRows[player.Name] = row task.spawn(function() updatePlayerValue(row) sortAndReflowPlayers() end) end end end task.defer(UpdatePlayers) game.Players.PlayerAdded:Connect(function() UpdatePlayers() end) game.Players.PlayerRemoving:Connect(function() UpdatePlayers() end) game.Players.ChildRemoved:Connect(function() UpdatePlayers() end) local Values = { cache = nil, byName = nil, fetchedAt = 0, fetching = false, } local HttpService = game:GetService("HttpService") local function urlEncode(s) return (tostring(s):gsub("([^%w%-_%.~])", function(c) return string.format("%%%02X", string.byte(c)) end)) end local httpRequest = (syn and syn.request) or (http and http.request) or (fluxus and fluxus.request) or http_request or request local function withTimeout(fn, timeoutSeconds) local done, status, value = false, nil, nil task.spawn(function() local ok, res = pcall(fn) done = true if ok then status, value = "ok", res else status, value = "err", res end end) local t0 = tick() while not done and (tick() - t0) < timeoutSeconds do task.wait(0.05) end if not done then return "timeout", nil end return status, value end local function HttpGetJSON(url) if httpRequest then local status, res = withTimeout(function() return httpRequest({ Url = url, Method = "GET", Headers = { ["Accept"] = "*/*", ["Referer"] = "https://mm2.cosmicvalues.gg/calculator", ["User-Agent"] = "Mozilla/5.0", }, }) end, 8) if status == "ok" and res and res.Body then local okD, decoded = pcall(HttpService.JSONDecode, HttpService, res.Body) if okD then return decoded end warn("[mm2run/http] JSON decode failed for " .. url) elseif status == "timeout" then warn("[mm2run/http] httpRequest TIMED OUT after 8s for " .. url) elseif status == "err" then warn("[mm2run/http] httpRequest errored: " .. tostring(res)) end end local status, txt = withTimeout(function() return game:HttpGet(url) end, 8) if status == "ok" and txt then local okD, decoded = pcall(HttpService.JSONDecode, HttpService, txt) if okD then return decoded end warn("[mm2run/http] JSON decode failed on HttpGet fallback for " .. url) elseif status == "timeout" then warn("[mm2run/http] HttpGet TIMED OUT after 8s for " .. url) elseif status == "err" then warn("[mm2run/http] HttpGet errored: " .. tostring(txt)) end return nil end local function FetchValuesPage(page, limit, query) page = page or 1 limit = limit or 100 local url = "https://mm2.cosmicvalues.gg/api/v1/values?sortBy=position&limit=" .. tostring(limit) .. "&page=" .. tostring(page) if query and query ~= "" then url = url .. "&search=" .. urlEncode(query) end return HttpGetJSON(url) end local function FetchAllValues(onProgress) local all = {} local page = 1 while true do print(("[mm2run/catalog] fetching page %d (have %d items)"):format(page, #all)) local data = FetchValuesPage(page, 100, nil) if not data or not data.items then print(("[mm2run/catalog] page %d failed, retrying once"):format(page)) task.wait(0.5) data = FetchValuesPage(page, 100, nil) if not data or not data.items then print(("[mm2run/catalog] page %d failed twice, returning partial (%d items)"):format(page, #all)) return all, false end end for _, item in ipairs(data.items) do item._numericValue = tonumber(item.value) or 0 table.insert(all, item) end local hasMore = data.pagination and data.pagination.hasMore if onProgress then onProgress(#all, hasMore) end if not hasMore then break end page = page + 1 if page > 100 then break end end return all, true end local function RebuildValuesIndex() Values.byName = {} if not Values.cache then return end for _, item in ipairs(Values.cache) do Values.byName[string.lower(tostring(item.name or ""))] = item end end local valueSearchBox = createSettingRow("Search weapon:", "", valuesFrame) CreateSpace(valuesFrame) local valueStatusLabel = Instance.new("TextLabel") valueStatusLabel.Size = UDim2.new(1, 0, 0, 18) valueStatusLabel.BackgroundTransparency = 1 valueStatusLabel.Text = "Loading values..." valueStatusLabel.Font = Enum.Font.SourceSans valueStatusLabel.TextSize = 12 valueStatusLabel.TextColor3 = Color3.fromRGB(200, 200, 200) valueStatusLabel.TextXAlignment = Enum.TextXAlignment.Left valueStatusLabel.Parent = valuesFrame local resultsScroll = Instance.new("ScrollingFrame") resultsScroll.Size = UDim2.new(1, 0, 0, 200) resultsScroll.BackgroundColor3 = Color3.fromRGB(25, 25, 35) resultsScroll.BackgroundTransparency = 0.3 resultsScroll.BorderSizePixel = 0 resultsScroll.ScrollBarThickness = 6 resultsScroll.ScrollBarImageColor3 = Color3.fromRGB(100, 100, 255) resultsScroll.CanvasSize = UDim2.new(0, 0, 0, 0) resultsScroll.AutomaticCanvasSize = Enum.AutomaticSize.Y resultsScroll.Parent = valuesFrame do local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, 5) c.Parent = resultsScroll local lay = Instance.new("UIListLayout") lay.FillDirection = Enum.FillDirection.Vertical lay.SortOrder = Enum.SortOrder.LayoutOrder lay.Padding = UDim.new(0, 2) lay.Parent = resultsScroll local pad = Instance.new("UIPadding") pad.PaddingTop = UDim.new(0, 3) pad.PaddingBottom = UDim.new(0, 3) pad.PaddingLeft = UDim.new(0, 3) pad.PaddingRight = UDim.new(0, 3) pad.Parent = resultsScroll end local function _updateValuesScrollHeight() local offsetY = resultsScroll.AbsolutePosition.Y - valuesFrame.AbsolutePosition.Y local available = valuesFrame.AbsoluteSize.Y - offsetY - 40 resultsScroll.Size = UDim2.new(1, 0, 0, math.max(80, available)) end valuesFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(_updateValuesScrollHeight) task.defer(_updateValuesScrollHeight) CreateSpace(valuesFrame) local function RenderValueResults(items) for _, c in ipairs(resultsScroll:GetChildren()) do if c:IsA("Frame") then c:Destroy() end end if not items then return end for _, item in ipairs(items) do local row = Instance.new("Frame") row.Size = UDim2.new(1, -6, 0, 40) row.BackgroundColor3 = Color3.fromRGB(40, 40, 55) row.BackgroundTransparency = 0.2 row.Parent = resultsScroll local rowCorner = Instance.new("UICorner") rowCorner.CornerRadius = UDim.new(0, 4) rowCorner.Parent = row local nameLbl = Instance.new("TextLabel") nameLbl.Size = UDim2.new(0.62, -8, 0, 20) nameLbl.Position = UDim2.new(0, 8, 0, 2) nameLbl.BackgroundTransparency = 1 nameLbl.Text = tostring(item.name) nameLbl.Font = Enum.Font.SourceSansSemibold nameLbl.TextSize = 13 nameLbl.TextColor3 = Color3.fromRGB(240, 240, 255) nameLbl.TextXAlignment = Enum.TextXAlignment.Left nameLbl.TextTruncate = Enum.TextTruncate.AtEnd nameLbl.Parent = row local metaParts = {} if item.rarity then table.insert(metaParts, tostring(item.rarity)) end if item.demand then table.insert(metaParts, "Demand " .. tostring(item.demand)) end if item.trend then table.insert(metaParts, tostring(item.trend)) end local metaLbl = Instance.new("TextLabel") metaLbl.Size = UDim2.new(0.62, -8, 0, 16) metaLbl.Position = UDim2.new(0, 8, 0, 22) metaLbl.BackgroundTransparency = 1 metaLbl.Text = table.concat(metaParts, " | ") metaLbl.Font = Enum.Font.SourceSans metaLbl.TextSize = 11 metaLbl.TextColor3 = Color3.fromRGB(160, 160, 200) metaLbl.TextXAlignment = Enum.TextXAlignment.Left metaLbl.TextTruncate = Enum.TextTruncate.AtEnd metaLbl.Parent = row local valLbl = Instance.new("TextLabel") valLbl.Size = UDim2.new(0.38, -8, 1, 0) valLbl.Position = UDim2.new(0.62, 0, 0, 0) valLbl.BackgroundTransparency = 1 valLbl.Text = FormatValue(item.value) valLbl.Font = Enum.Font.FredokaOne valLbl.TextSize = 14 valLbl.TextColor3 = Color3.fromRGB(120, 255, 160) valLbl.TextXAlignment = Enum.TextXAlignment.Right valLbl.Parent = row end end local function FilterCachedValues(query) if not Values.cache then return nil end if query == nil or query == "" then return Values.cache end local q = string.lower(query) local out = {} for _, item in ipairs(Values.cache) do local name = string.lower(tostring(item.name or "")) local rarity = string.lower(tostring(item.rarity or "")) if string.find(name, q, 1, true) or string.find(rarity, q, 1, true) then table.insert(out, item) end end return out end local function RenderFilteredResults(query) local items = FilterCachedValues(query) if not items then RenderValueResults({}) return 0, 0 end local total = #items local capped = items if total > 250 then capped = {} for i = 1, 250 do capped[i] = items[i] end end RenderValueResults(capped) return #capped, total end local function UpdateValuesStatus(query) if not Values.cache then return end local shown, total = RenderFilteredResults(query) if total == 0 then valueStatusLabel.Text = "No matches in " .. #Values.cache .. " items" valueStatusLabel.TextColor3 = Color3.fromRGB(255, 150, 100) elseif shown < total then valueStatusLabel.Text = "Showing " .. shown .. " of " .. total .. " matches (type more to narrow)" valueStatusLabel.TextColor3 = Color3.fromRGB(180, 220, 255) else valueStatusLabel.Text = total .. " match" .. (total == 1 and "" or "es") .. " (catalog: " .. #Values.cache .. ")" valueStatusLabel.TextColor3 = Color3.fromRGB(120, 255, 160) end end local function LoadFullCatalog(force) if Values.fetching then valueStatusLabel.Text = "already fetching... (be patient)" valueStatusLabel.TextColor3 = Color3.fromRGB(255, 220, 100) return end if (not force) and Values.cache and (tick() - Values.fetchedAt) < 600 then UpdateValuesStatus(valueSearchBox.Text) return end Values.fetching = true task.spawn(function() valueStatusLabel.Text = "Fetching catalog..." valueStatusLabel.TextColor3 = Color3.fromRGB(255, 220, 100) local items, ok = FetchAllValues(function(loaded, hasMore) valueStatusLabel.Text = ("Loaded %d items%s"):format(loaded, hasMore and "... (more pages)" or " -- finalizing") end) Values.fetching = false if items and #items > 0 then Values.cache = items Values.fetchedAt = tick() RebuildValuesIndex() UpdateValuesStatus(valueSearchBox.Text) else valueStatusLabel.Text = "API request failed -- executor blocked http? (see console)" valueStatusLabel.TextColor3 = Color3.fromRGB(255, 100, 100) warn("[mm2run] cosmic values API returned nothing. httpRequest=" .. tostring(httpRequest ~= nil)) end end) end CreateButton(valuesFrame, "Refresh catalog", function() LoadFullCatalog(true) end) valueSearchBox:GetPropertyChangedSignal("Text"):Connect(function() if Values.cache then UpdateValuesStatus(valueSearchBox.Text) end end) task.spawn(function() LoadFullCatalog(false) end)