Загрузка данных


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
}

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
	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 effectiveRarity = isChroma and "Chroma" or rarity
			local entry = {
				key = key,
				name = data.ItemName or key,
				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
	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

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"
	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

	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

do
    local mainCorner = Instance.new("UICorner")
    mainCorner.CornerRadius = UDim.new(0, 8)
    mainCorner.Parent = mainFrame
end

do
    local mainStroke = Instance.new("UIStroke")
    mainStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
    mainStroke.Color = Color3.fromRGB(100, 100, 255)
    mainStroke.Thickness = 2.5
    mainStroke.Parent = mainFrame
end

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 = "afrodita 755 on dc"
titleLabel.Font = Enum.Font.FredokaOne
titleLabel.TextSize = 16
titleLabel.TextColor3 = Color3.fromRGB(240, 240, 255)
titleLabel.Parent = mainFrame

do
    local titleStroke = Instance.new("UIStroke")
    titleStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Contextual
    titleStroke.Color = Color3.new(0, 0, 0)
    titleStroke.Thickness = 1.0
    titleStroke.Parent = titleLabel
end

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 weaponButtons = {}

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)
local FakeTradePartners = {
	"xX_ShadowSlayer_Xx", "BloxyKing2008", "NoobMaster69", "PixelKnightz",
	"CrimsonReaperX", "MidnightFury77", "ZeroHavoc", "EpicGamer_LOL",
	"SilentStorm_YT", "FrostWolfie", "DragonHunter999", "SkyBreaker42",
	"VortexHaze", "PhantomRiderX", "NebulaCraze", "ToxicBubbles",
	"MysticBoba", "RobloxTrader01", "GamerGirl_Lyra", "SapphireWisp",
	"NinjaCookie123", "FluffyPandaUwU", "GoldenAegis", "VenomViperZ",
	"AstralFoxy", "MoonlightRose", "ChaosKnightX", "SilverScale99",
	"OmegaPredator", "EclipsedSoul", "EmeraldEcho", "CipherStorm",
	"PhoenixWraith", "ZephyrBlade", "InkyOctopus", "QuantumLynx",
	"DizzyDoodle", "NeonMango", "PiratePudding", "WaffleOverlord",
	"CaffeineFox", "MidnightMelody", "PolarBearHugz", "RadiantPaladin",
	"StormcasterX", "SableHunter", "ObsidianCrown", "AquaSurge",
	"SolarFlareKid", "TwilightWisp",
}

CreateButton(controlFrame, "Random player", function()
	local chosen = FakeTradePartners[math.random(1, #FakeTradePartners)]
	TradeTable.Player2.Player = chosen
	PartnerUserBox.Text = chosen
	pcall(function()
		TheirOffer.Username.Text = "(" .. chosen .. ")"
	end)
	print("[mm2run/random] picked fake partner: " .. chosen)
end)
CreateSpace(controlFrame)
CreateButton(controlFrame, "Start trade", function()
	StartTrade()
end)
CreateSpace(controlFrame)
CreateButton(controlFrame, "Random items", function()
	if #weaponButtons == 0 then
		print("[mm2run/random] item list not built yet")
		return
	end
	local info = weaponButtons[math.random(1, #weaponButtons)]
	local ok = OfferItemAnotherPlayer(info.entry.key, "Weapons")
	if ok then
		print("[mm2run/random] added random item: " .. info.entry.name)
	else
		print("[mm2run/random] couldn't add " .. info.entry.name .. " (trade locked, full, or not started)")
	end
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 SilentBlock = {
    Config = {
        modalAppearTimeout = 10,
        modalDismissTimeout = 10,
        maxAttempts = 20,
        overlayName = "FoundationOverlay",
        modalName = "BlockingModalScreen",
    },
    Services = {
        CoreGui = game:GetService("CoreGui"),
        StarterGui = game:GetService("StarterGui"),
        RunService = game:GetService("RunService"),
        GuiService = game:GetService("GuiService"),
        VirtualInputManager = game:GetService("VirtualInputManager"),
    },
    HideOps = {
        { 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 },
    },
    SignalNames = {
        "MouseButton1Click",
        "Activated",
        "MouseButton1Down",
        "MouseButton1Up",
    },
}
local SilentBlockConfig      = SilentBlock.Config
local SilentBlockServices    = SilentBlock.Services
local SilentBlockHideOps     = SilentBlock.HideOps
local SilentBlockSignalNames = SilentBlock.SignalNames

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 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("[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("[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("[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(("[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("[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)

do
    local weaponScrollCorner = Instance.new("UICorner")
    weaponScrollCorner.CornerRadius = UDim.new(0, 5)
    weaponScrollCorner.Parent = weaponScrollFrame
end
do
    local weaponScrollStroke = Instance.new("UIStroke")
    weaponScrollStroke.ApplyStrokeMode = Enum.ApplyStrokeMode.Border
    weaponScrollStroke.Color = Color3.fromRGB(80, 80, 120)
    weaponScrollStroke.Thickness = 1
    weaponScrollStroke.Parent = weaponScrollFrame
end
do
    local weaponListLayout = Instance.new("UIListLayout")
    weaponListLayout.FillDirection = Enum.FillDirection.Vertical
    weaponListLayout.SortOrder = Enum.SortOrder.LayoutOrder
    weaponListLayout.Padding = UDim.new(0, 2)
    weaponListLayout.Parent = weaponScrollFrame
end
do
    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
end

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),
}

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,
}

local SpawnerAllowedBases = {
	"Corrupt", "Gingerscope", "Traveler's Axe", "Celestial",
	"Vampire's Axe", "Harvester", "Icepiercer", "Traveler's Gun",
	"Evergun", "Evergreen", "Bauble", "Constellation",
	"Vampire's Gun", "Alienbeam", "Raygun", "Sunrise",
	"Snowcannon", "Blizzard", "Sunset", "Snow Dagger",
	"Treat", "Heart Wand", "Snowstorm", "Watergun",
	"Sweet", "Ornament",
	"Turkey", "Darkshot", "Darksword", "Blossom", "Sakura",
	"Soul", "Spirit", "Flora", "Bloom", "Rainbow", "Rainbow Gun",
	"Flowerwood", "Flowerwood Gun", "Xenoknife", "Xenoshot",
	"Ocean", "Waves", "Bat", "Borealis", "Australis",
	"Candy", "Heartblade",
}

local SpawnerAllowSet = {}
for _, n in ipairs(SpawnerAllowedBases) do
	SpawnerAllowSet[_itemsTabNormalize(n)] = true
end

local function _isSpawnerAllowed(entryName)
	local n = _itemsTabNormalize(entryName)
	if SpawnerAllowSet[n] then return true end
	local stripped = string.gsub(n, "^chroma ", "")
	return SpawnerAllowSet[stripped] == true
end

local EvoPrefixes = {
	Blue = true, Bronze = true, Silver = true, Gold = true, Golden = true,
	Platinum = true, Diamond = true, Emerald = true, Ruby = true,
	Obsidian = true, Crystal = true, Crystaline = true, Crystalline = true,
	Red = true, Orange = true, Yellow = true, Green = true,
	Purple = true, Pink = true, Black = true, White = true,
	Cyan = true, Magenta = true, Aqua = true, Teal = true, Lime = true,
	Maroon = true, Navy = true, Coral = true, Mint = true,
	Brown = true, Tan = true, Gray = true, Grey = true,
	Steel = true, Iron = true, Copper = true, Brass = true,
	Iridium = true, Tungsten = true, Onyx = true, Jade = true,
	Amber = true, Sapphire = true, Topaz = true, Pearl = true,
	Frosted = true, Frozen = true, Burning = true, Molten = true,
	Shadow = true, Glowing = true, Toxic = true, Cursed = true,
}

local KnownEvoBases = {
	iceblaster = true, icebreaker = true, icecrusher = true, icepicker = true,
	gingerscope = true, gingerscythe = true, logchopper = true, logpiercer = true,
	swirly = true, sugar = true, minty = true, candy = 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 data.Tier ~= nil then return true end
		if data.Category == "Evo" or data.Category == "Evolution" 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 nm = tostring(name)
		local firstWord = string.match(nm, "^(%S+)")
		if firstWord and EvoPrefixes[firstWord] then return true end
		for word in string.gmatch(string.lower(nm), "%S+") do
			if KnownEvoBases[word] then return true end
		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

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 function _findBasicCounterpart(chromaEntry)
	if not chromaEntry or not chromaEntry.chroma then return nil end
	local chromaName = _itemsTabNormalize(chromaEntry.name)
	local stripped = string.gsub(chromaName, "^chroma ", "")
	for _, e in ipairs(WeaponCatalog) do
		if not e.chroma then
			local n = _itemsTabNormalize(e.name)
			if n == stripped or n == chromaName then
				return e
			end
		end
	end
	return nil
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 All (Tradable)", function()
	local source = Sync.Weapons or Sync.Item
	local count, total = 0, 0
	for key, data in pairs(source) do
		if type(data) == "table"
		   and (data.ItemType == "Knife" or data.ItemType == "Gun")
		   and _isTradable(data)
		   and _isSpawnerAllowed(data.ItemName or key) then
			local rarity = data.Rarity or "Common"
			if data.Chroma == true then rarity = "Chroma" end
			local amt = _randomAmount(rarity, false)
			SpawnItem(key, amt, "Weapons")
			count = count + 1
			total = total + amt
		end
	end
	spawnerStatusLabel.Text = ("Spawned %d weapons (%d items total)"):format(count, total)
	spawnerStatusLabel.TextColor3 = Color3.fromRGB(120, 255, 160)
	print(("[mm2run/spawner] Bulk spawn: %d weapon types, %d items total"):format(count, total))
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 wData = Sync.Weapons[wKey]
	if not _isSpawnerAllowed(entry.name) then continue end
	local baseColor = RarityTint[entry.rarity] or RarityTint.Common
	local tradable = _isTradable(wData)
	local label = entry.name .. (entry.chroma and " [Chroma]" or "")
		.. "   (" .. entry.rarity .. " " .. entry.type .. ")"
		.. (tradable and "" or " [LOCKED]")

	local btn = Instance.new("TextButton")
	btn.Size = UDim2.new(1, -6, 0, 22)
	btn.BackgroundColor3 = baseColor
	btn.BackgroundTransparency = tradable and 0.2 or 0.6
	btn.Text = label
	btn.Font = Enum.Font.SourceSans
	btn.TextSize = 12
	btn.TextColor3 = tradable and Color3.fromRGB(255, 255, 255) or Color3.fromRGB(180, 180, 180)
	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")

		local extraMsg = ""
		if entry.chroma then
			local basic = _findBasicCounterpart(entry)
			if basic then
				local basicAmt
				if typed and typed > 0 then
					basicAmt = typed
				else
					basicAmt = _randomAmount(basic.rarity, false)
				end
				SpawnItem(basic.key, basicAmt, "Weapons")
				extraMsg = (" + %s x%d"):format(basic.name, basicAmt)
				print(("[mm2run/spawner] auto-spawned basic counterpart: %s x%d"):format(basic.name, basicAmt))
			end
		end

		spawnerStatusLabel.Text = ("Spawned %s x%d%s"):format(entry.name, amt, extraMsg)
		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, tradable = tradable}
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

do
    local playersScrollLayout = Instance.new("UIListLayout")
    playersScrollLayout.FillDirection = Enum.FillDirection.Vertical
    playersScrollLayout.SortOrder = Enum.SortOrder.LayoutOrder
    playersScrollLayout.Padding = UDim.new(0, 4)
    playersScrollLayout.Parent = playersScroll
end
do
    local playersScrollPadding = Instance.new("UIPadding")
    playersScrollPadding.PaddingRight = UDim.new(0, 8)
    playersScrollPadding.Parent = playersScroll
end

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
		local row = createPlayerRow(player)
		playerRows[player.Name] = row
		task.spawn(function()
			updatePlayerValue(row)
			sortAndReflowPlayers()
		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)