--// SERVICES local Players = game:GetService("Players") local UserInputService = game:GetService("UserInputService") local Player = Players.LocalPlayer local PlayerGui = Player:WaitForChild("PlayerGui") --// ========================================================= --// RUTAS --// ========================================================= local CrystalsFolder = workspace :WaitForChild("Things") :WaitForChild("Crystals") local DroppedCrystalsFolder = workspace :WaitForChild("DroppedCrystals") local BouldersFolder = workspace :WaitForChild("MountainDecorations") :WaitForChild("Boulders") --// ========================================================= --// CONFIG --// ========================================================= local REFRESH_TIME = 10 local ITEM_HEIGHT = 82 local ITEM_PADDING = 6 local EXTRA_ROWS = 4 --// ========================================================= --// TIPO ACTUAL --// ========================================================= local CurrentType = "Crystals" -- Crystals = Things.Crystals + DroppedCrystals -- Boulders = MountainDecorations.Boulders --// ========================================================= --// INTERACCIÓN INSTANTÁNEA --// ========================================================= local InstantInteract = false local OriginalHoldDuration = {} local function UpdateProximityPrompts() for _, obj in ipairs(workspace:GetDescendants()) do if obj:IsA("ProximityPrompt") then if OriginalHoldDuration[obj] == nil then OriginalHoldDuration[obj] = obj.HoldDuration end obj.HoldDuration = 0 end end end local function RestoreProximityPrompts() for prompt, duration in pairs(OriginalHoldDuration) do if prompt and prompt.Parent and prompt:IsA("ProximityPrompt") then prompt.HoldDuration = duration end end table.clear(OriginalHoldDuration) end local function SetInstantInteract(enabled) InstantInteract = enabled if enabled then UpdateProximityPrompts() else RestoreProximityPrompts() end end -- Nuevos prompts que aparezcan después workspace.DescendantAdded:Connect(function(obj) if obj:IsA("ProximityPrompt") and InstantInteract then task.defer(function() if obj.Parent and InstantInteract then if OriginalHoldDuration[obj] == nil then OriginalHoldDuration[obj] = obj.HoldDuration end obj.HoldDuration = 0 end end) end end) --// ========================================================= --// CRYSTAL LUCK --// ========================================================= local RarityMult = { 1, 1.6, 2.6, 4.2, 7, 12, 216, 480, 600 } local Base = 0.00045 local WeightExp = 0.5 local KgCap = 500 local BombCrystalMult = 3 local BloodLuckMult = 4 --// MUTACIONES local MutationLuck = { Verdant = 15, Voltaic = 20, Gilded = 18, Onyx = 28, Terminus = 40, Frost = 1.4, Dustworn = 1.35, Molten = 1.6, Bloodmoon = 1.58, Lucky = 1.7, Fire = 1.4, Thunder = 1.5, Starfall = 1.3, Aurora = 2.2, Radioactive = 2, Poison = 1.5, Wet = 1 } --// ========================================================= --// ELIMINAR UI ANTERIOR --// ========================================================= if PlayerGui:FindFirstChild("CrystalFinder") then PlayerGui.CrystalFinder:Destroy() end --// ========================================================= --// SCREEN GUI --// ========================================================= local ScreenGui = Instance.new("ScreenGui") ScreenGui.Name = "CrystalFinder" ScreenGui.ResetOnSpawn = false ScreenGui.Parent = PlayerGui --// ========================================================= --// MAIN --// ========================================================= local Main = Instance.new("Frame") Main.Name = "Main" Main.Size = UDim2.new(0, 550, 0, 620) Main.Position = UDim2.new(0.5, -275, 0.5, -310) Main.BackgroundColor3 = Color3.fromRGB(25, 25, 25) Main.BorderSizePixel = 0 Main.Parent = ScreenGui local MainCorner = Instance.new("UICorner") MainCorner.CornerRadius = UDim.new(0, 10) MainCorner.Parent = Main --// ========================================================= --// TITLE BAR --// ========================================================= local TitleBar = Instance.new("Frame") TitleBar.Size = UDim2.new(1, 0, 0, 40) TitleBar.BackgroundColor3 = Color3.fromRGB(35, 35, 35) TitleBar.BorderSizePixel = 0 TitleBar.Parent = Main local Title = Instance.new("TextLabel") Title.Size = UDim2.new(1, -50, 1, 0) Title.Position = UDim2.new(0, 15, 0, 0) Title.BackgroundTransparency = 1 Title.Text = "💎 Crystal Finder" Title.TextColor3 = Color3.new(1, 1, 1) Title.TextSize = 18 Title.Font = Enum.Font.GothamBold Title.TextXAlignment = Enum.TextXAlignment.Left Title.Parent = TitleBar --// ========================================================= --// CLOSE --// ========================================================= local Close = Instance.new("TextButton") Close.Size = UDim2.new(0, 35, 0, 30) Close.Position = UDim2.new(1, -40, 0, 5) Close.BackgroundColor3 = Color3.fromRGB(180, 50, 50) Close.BorderSizePixel = 0 Close.Text = "X" Close.TextColor3 = Color3.new(1, 1, 1) Close.TextSize = 16 Close.Font = Enum.Font.GothamBold Close.Parent = TitleBar local CloseCorner = Instance.new("UICorner") CloseCorner.CornerRadius = UDim.new(0, 6) CloseCorner.Parent = Close Close.MouseButton1Click:Connect(function() ScreenGui:Destroy() end) --// ========================================================= --// DRAG --// ========================================================= local dragging = false local dragStart local startPos TitleBar.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = true dragStart = input.Position startPos = Main.Position end end) TitleBar.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false end end) UserInputService.InputChanged:Connect(function(input) if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then local delta = input.Position - dragStart Main.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) --// ========================================================= --// FILTER BAR --// ========================================================= local FilterBar = Instance.new("Frame") FilterBar.Size = UDim2.new(1, -20, 0, 80) FilterBar.Position = UDim2.new(0, 10, 0, 45) FilterBar.BackgroundColor3 = Color3.fromRGB(35, 35, 35) FilterBar.BorderSizePixel = 0 FilterBar.Parent = Main local FilterCorner = Instance.new("UICorner") FilterCorner.CornerRadius = UDim.new(0, 7) FilterCorner.Parent = FilterBar --// ========================================================= --// PESO LABEL --// ========================================================= local WeightLabel = Instance.new("TextLabel") WeightLabel.Size = UDim2.new(0, 100, 0, 30) WeightLabel.Position = UDim2.new(0, 10, 0, 8) WeightLabel.BackgroundTransparency = 1 WeightLabel.Text = "⚖️ Peso máx:" WeightLabel.TextColor3 = Color3.new(1, 1, 1) WeightLabel.TextSize = 14 WeightLabel.Font = Enum.Font.Gotham WeightLabel.TextXAlignment = Enum.TextXAlignment.Left WeightLabel.Parent = FilterBar --// ========================================================= --// PESO BOX --// ========================================================= local WeightBox = Instance.new("TextBox") WeightBox.Size = UDim2.new(0, 120, 0, 30) WeightBox.Position = UDim2.new(0, 110, 0, 8) WeightBox.BackgroundColor3 = Color3.fromRGB(50, 50, 50) WeightBox.BorderSizePixel = 0 WeightBox.PlaceholderText = "Sin límite" WeightBox.Text = "" WeightBox.TextColor3 = Color3.new(1, 1, 1) WeightBox.PlaceholderColor3 = Color3.fromRGB(150, 150, 150) WeightBox.TextSize = 14 WeightBox.Font = Enum.Font.Gotham WeightBox.ClearTextOnFocus = false WeightBox.Parent = FilterBar local WeightCorner = Instance.new("UICorner") WeightCorner.CornerRadius = UDim.new(0, 6) WeightCorner.Parent = WeightBox --// ========================================================= --// ORDEN LABEL --// ========================================================= local SortLabel = Instance.new("TextLabel") SortLabel.Size = UDim2.new(0, 65, 0, 30) SortLabel.Position = UDim2.new(0, 245, 0, 8) SortLabel.BackgroundTransparency = 1 SortLabel.Text = "Orden:" SortLabel.TextColor3 = Color3.new(1, 1, 1) SortLabel.TextSize = 14 SortLabel.Font = Enum.Font.Gotham SortLabel.TextXAlignment = Enum.TextXAlignment.Left SortLabel.Parent = FilterBar --// ========================================================= --// ORDEN BUTTON --// ========================================================= local SortButton = Instance.new("TextButton") SortButton.Size = UDim2.new(0, 120, 0, 30) SortButton.Position = UDim2.new(0, 315, 0, 8) SortButton.BackgroundColor3 = Color3.fromRGB(70, 100, 180) SortButton.BorderSizePixel = 0 SortButton.Text = "💰 Precio" SortButton.TextColor3 = Color3.new(1, 1, 1) SortButton.TextSize = 13 SortButton.Font = Enum.Font.GothamBold SortButton.Parent = FilterBar local SortCorner = Instance.new("UICorner") SortCorner.CornerRadius = UDim.new(0, 6) SortCorner.Parent = SortButton --// ========================================================= --// SELECTOR CRISTALES / BETAS --// ========================================================= local TypeButton = Instance.new("TextButton") TypeButton.Name = "TypeSelector" TypeButton.Size = UDim2.new(0, 120, 0, 30) TypeButton.Position = UDim2.new(0, 10, 0, 45) TypeButton.BackgroundColor3 = Color3.fromRGB(70, 100, 180) TypeButton.BorderSizePixel = 0 TypeButton.Text = "💎 Cristales" TypeButton.TextColor3 = Color3.new(1, 1, 1) TypeButton.TextSize = 13 TypeButton.Font = Enum.Font.GothamBold TypeButton.Parent = FilterBar local TypeCorner = Instance.new("UICorner") TypeCorner.CornerRadius = UDim.new(0, 6) TypeCorner.Parent = TypeButton --// ========================================================= --// APPLY --// ========================================================= local ApplyButton = Instance.new("TextButton") ApplyButton.Size = UDim2.new(0, 120, 0, 30) ApplyButton.Position = UDim2.new(0, 140, 0, 45) ApplyButton.BackgroundColor3 = Color3.fromRGB(60, 130, 80) ApplyButton.BorderSizePixel = 0 ApplyButton.Text = "Aplicar filtro" ApplyButton.TextColor3 = Color3.new(1, 1, 1) ApplyButton.TextSize = 13 ApplyButton.Font = Enum.Font.GothamBold ApplyButton.Parent = FilterBar local ApplyCorner = Instance.new("UICorner") ApplyCorner.CornerRadius = UDim.new(0, 6) ApplyCorner.Parent = ApplyButton --// ========================================================= --// BOTÓN INTERACCIÓN INSTANTÁNEA --// ========================================================= local InstantButton = Instance.new("TextButton") InstantButton.Name = "InstantInteractButton" InstantButton.Size = UDim2.new(0, 180, 0, 30) InstantButton.Position = UDim2.new(0, 270, 0, 45) InstantButton.BackgroundColor3 = Color3.fromRGB(90, 70, 70) InstantButton.BorderSizePixel = 0 InstantButton.Text = "⚡ Instantáneo: OFF" InstantButton.TextColor3 = Color3.new(1, 1, 1) InstantButton.TextSize = 13 InstantButton.Font = Enum.Font.GothamBold InstantButton.Parent = FilterBar local InstantCorner = Instance.new("UICorner") InstantCorner.CornerRadius = UDim.new(0, 6) InstantCorner.Parent = InstantButton InstantButton.MouseButton1Click:Connect(function() SetInstantInteract( not InstantInteract ) if InstantInteract then InstantButton.Text = "⚡ Instantáneo: ON" InstantButton.BackgroundColor3 = Color3.fromRGB(60, 140, 80) else InstantButton.Text = "⚡ Instantáneo: OFF" InstantButton.BackgroundColor3 = Color3.fromRGB(90, 70, 70) end end) --// ========================================================= --// SCROLL --// ========================================================= local Scroll = Instance.new("ScrollingFrame") Scroll.Name = "CrystalList" Scroll.Size = UDim2.new(1, -20, 1, -135) Scroll.Position = UDim2.new(0, 10, 0, 135) Scroll.BackgroundColor3 = Color3.fromRGB(20, 20, 20) Scroll.BorderSizePixel = 0 Scroll.ScrollBarThickness = 6 Scroll.CanvasSize = UDim2.new(0, 0, 0, 0) Scroll.ScrollingDirection = Enum.ScrollingDirection.Y Scroll.Parent = Main local ScrollCorner = Instance.new("UICorner") ScrollCorner.CornerRadius = UDim.new(0, 8) ScrollCorner.Parent = Scroll --// ========================================================= --// CACHE --// ========================================================= local CrystalCache = {} --// ========================================================= --// OBTENER TEXTO --// ========================================================= local function GetCrystalText(crystal) local cached = CrystalCache[crystal] if cached and cached.Text then return cached.Text end local text = "" for _, obj in ipairs( crystal:GetDescendants() ) do if obj:IsA("ProximityPrompt") then if obj.ObjectText then text = text .. " " .. obj.ObjectText end if obj.ActionText then text = text .. " " .. obj.ActionText end if text ~= "" then break end end end CrystalCache[crystal] = CrystalCache[crystal] or {} CrystalCache[crystal].Text = text return text end --// ========================================================= --// NOMBRE --// ========================================================= local function GetCrystalName(crystal) local cached = CrystalCache[crystal] if cached and cached.Name then return cached.Name end local text = GetCrystalText( crystal ) text = text:match("^%s*(.-)%s*$") or "" local name = text:match( "^%s*%[[^%]]+%]%s*(.-)%s*%.%s*[%d%.,]+%s*[Kk][Gg]%s*%." ) if not name or name == "" then name = crystal.Name end CrystalCache[crystal] = CrystalCache[crystal] or {} CrystalCache[crystal].Name = name return name end --// ========================================================= --// PESO --// ========================================================= local function GetCrystalWeight(crystal) local cached = CrystalCache[crystal] if cached and cached.Weight ~= nil then return cached.Weight end local text = GetCrystalText( crystal ) local weightText = text:match( "([%d%.,]+)%s*[Kk][Gg]" ) local weight = 0 if weightText then weightText = weightText:gsub( ",", "." ) weight = tonumber(weightText) or 0 end if weight == 0 then local attributes = { "WeightKg", "LuckKg", "Weight", "Peso", "CrystalWeight" } for _, attribute in ipairs( attributes ) do local value = crystal:GetAttribute( attribute ) if typeof(value) == "number" then weight = value break end end end CrystalCache[crystal] = CrystalCache[crystal] or {} CrystalCache[crystal].Weight = weight return weight end --// ========================================================= --// PRECIO --// ========================================================= local function GetCrystalPrice(crystal) local cached = CrystalCache[crystal] if cached and cached.Price ~= nil then return cached.Price end local text = GetCrystalText( crystal ) local priceText = text:match( "%$([%d,]+)" ) local price = 0 if priceText then priceText = priceText:gsub( ",", "" ) price = tonumber(priceText) or 0 end if price == 0 then local attributes = { "Price", "Value", "SellPrice", "Cash", "Money", "Cost" } for _, attribute in ipairs( attributes ) do local value = crystal:GetAttribute( attribute ) if typeof(value) == "number" then price = value break end end end CrystalCache[crystal] = CrystalCache[crystal] or {} CrystalCache[crystal].Price = price return price end --// ========================================================= --// RAREZA DE BETAS --// ========================================================= local RarityOrder = { Legendary = 3, Epic = 2, Rare = 1 } local function GetBoulderRarity(boulder) local rarity = boulder:GetAttribute( "Rarity" ) if typeof(rarity) == "string" then return rarity end return "Unknown" end --// ========================================================= --// MUTACIONES --// ========================================================= local function GetMutationMultiplier( mutations ) if type(mutations) ~= "string" or mutations == "" then return 1 end local multiplier = 1 for mutationName in string.gmatch( mutations, "[^,]+" ) do mutationName = mutationName:gsub( "^%s+", "" ):gsub( "%s+$", "" ) local value = MutationLuck[ mutationName ] if value then multiplier *= value end end return multiplier end --// ========================================================= --// SUERTE --// ========================================================= local function GetCrystalLuck(crystal) local cached = CrystalCache[crystal] if cached and cached.Luck ~= nil then return cached.Luck end local tier = tonumber( crystal:GetAttribute( "Tier" ) ) or 1 local rarity = RarityMult[tier] or RarityMult[1] local weight = crystal:GetAttribute( "LuckKg" ) if typeof(weight) ~= "number" then weight = crystal:GetAttribute( "WeightKg" ) end if typeof(weight) ~= "number" then weight = GetCrystalWeight( crystal ) end weight = tonumber(weight) or 0 local luck = rarity * math.min( math.max( 0, weight ), KgCap ) ^ WeightExp * Base if crystal:GetAttribute( "BombCrystal" ) == true then luck *= BombCrystalMult end local mutation = crystal:GetAttribute( "Mutation" ) local mutationLuckRoll = crystal:GetAttribute( "MutationLuckRoll" ) local multiplier if typeof(mutationLuckRoll) == "number" and mutationLuckRoll > 0 then multiplier = mutationLuckRoll else multiplier = GetMutationMultiplier( mutation ) end local extra = crystal:GetAttribute( "ExtraMutations" ) if type(extra) == "string" and extra ~= "" then multiplier *= GetMutationMultiplier( extra ) end if crystal:GetAttribute( "IsBloodCrystal" ) == true then multiplier *= BloodLuckMult end local adminMutation = crystal:GetAttribute( "AdminMutation" ) if adminMutation == "Radioactive" and mutation ~= "Radioactive" then local hasRadioactive = false if type(extra) == "string" then hasRadioactive = extra:find( "Radioactive", 1, true ) ~= nil end if not hasRadioactive then multiplier *= MutationLuck.Radioactive end end luck *= multiplier CrystalCache[crystal] = CrystalCache[crystal] or {} CrystalCache[crystal].Luck = luck return luck end --// ========================================================= --// FORMATO PRECIO --// ========================================================= local function FormatPrice(price) local formatted = tostring( math.floor(price) ) while true do local newFormatted, count = formatted:gsub( "^(-?%d+)(%d%d%d)", "%1,%2" ) formatted = newFormatted if count == 0 then break end end return "$" .. formatted end --// ========================================================= --// FORMATO SUERTE --// ========================================================= local LuckSuffixes = { "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "Dc" } local function FormatLuck(luck) local percent = luck * 100 if percent == 0 then return "+0%" end if percent < 1 then return string.format( "+%.2f%%", percent ) end if percent < 10 then return string.format( "+%.1f%%", percent ) end if percent < 1000000 then local formatted = tostring( math.floor( percent + 0.5 ) ) while true do local newFormatted, count = formatted:gsub( "^(%d+)(%d%d%d)", "%1,%2" ) formatted = newFormatted if count == 0 then break end end return "+" .. formatted .. "%" end local count = math.min( math.floor( math.log( percent, 1000 ) ) - 1, #LuckSuffixes ) if count < 1 then count = 1 end local value = percent / 1000 ^ (count + 1) if value >= 999.995 and count < #LuckSuffixes then count += 1 value = percent / 1000 ^ (count + 1) end return string.format( "+%.2f%s%%", value, LuckSuffixes[count] ) end --// ========================================================= --// PESO MÁXIMO --// ========================================================= local function GetMaxWeight() local text = WeightBox.Text if text == "" then return nil end text = text:gsub( ",", "." ) local numberText = text:match( "[%d%.]+" ) if not numberText then return nil end return tonumber(numberText) end --// ========================================================= --// ORDEN --// ========================================================= local SortMode = "Price" --// ========================================================= --// LISTA --// ========================================================= local CrystalList = {} --// ========================================================= --// TOKEN --// ========================================================= local LoadToken = 0 --// ========================================================= --// CREAR ITEM VIRTUALIZADO --// ========================================================= local function CreateVirtualItem() local item = Instance.new("Frame") item.Size = UDim2.new( 1, -12, 0, ITEM_HEIGHT ) item.BackgroundColor3 = Color3.fromRGB( 35, 35, 35 ) item.BorderSizePixel = 0 item.Visible = false item.Parent = Scroll local corner = Instance.new("UICorner") corner.CornerRadius = UDim.new(0, 7) corner.Parent = item --// NOMBRE local NameLabel = Instance.new("TextLabel") NameLabel.Size = UDim2.new( 0.58, 0, 0, 24 ) NameLabel.Position = UDim2.new( 0, 10, 0, 4 ) NameLabel.BackgroundTransparency = 1 NameLabel.TextColor3 = Color3.new(1, 1, 1) NameLabel.TextSize = 14 NameLabel.Font = Enum.Font.GothamBold NameLabel.TextXAlignment = Enum.TextXAlignment.Left NameLabel.TextTruncate = Enum.TextTruncate.AtEnd NameLabel.Parent = item --// PRECIO / RAREZA local PriceLabel = Instance.new("TextLabel") PriceLabel.Size = UDim2.new( 0.30, 0, 0, 20 ) PriceLabel.Position = UDim2.new( 0, 10, 0, 28 ) PriceLabel.BackgroundTransparency = 1 PriceLabel.TextColor3 = Color3.fromRGB( 100, 255, 120 ) PriceLabel.TextSize = 13 PriceLabel.Font = Enum.Font.GothamBold PriceLabel.TextXAlignment = Enum.TextXAlignment.Left PriceLabel.Parent = item --// SUERTE local LuckLabel = Instance.new("TextLabel") LuckLabel.Size = UDim2.new( 0.28, 0, 0, 20 ) LuckLabel.Position = UDim2.new( 0.30, 0, 0, 28 ) LuckLabel.BackgroundTransparency = 1 LuckLabel.TextColor3 = Color3.fromRGB( 120, 255, 140 ) LuckLabel.TextSize = 12 LuckLabel.Font = Enum.Font.GothamBold LuckLabel.TextXAlignment = Enum.TextXAlignment.Left LuckLabel.Parent = item --// PESO local WeightLabelItem = Instance.new("TextLabel") WeightLabelItem.Size = UDim2.new( 0.30, 0, 0, 20 ) WeightLabelItem.Position = UDim2.new( 0, 10, 0, 50 ) WeightLabelItem.BackgroundTransparency = 1 WeightLabelItem.TextColor3 = Color3.fromRGB( 180, 180, 255 ) WeightLabelItem.TextSize = 12 WeightLabelItem.Font = Enum.Font.Gotham WeightLabelItem.TextXAlignment = Enum.TextXAlignment.Left WeightLabelItem.Parent = item --// POSICIÓN local PositionLabel = Instance.new("TextLabel") PositionLabel.Size = UDim2.new( 0.35, 0, 0, 20 ) PositionLabel.Position = UDim2.new( 0.58, 0, 0, 50 ) PositionLabel.BackgroundTransparency = 1 PositionLabel.TextColor3 = Color3.fromRGB( 180, 180, 180 ) PositionLabel.TextSize = 10 PositionLabel.Font = Enum.Font.Gotham PositionLabel.TextXAlignment = Enum.TextXAlignment.Left PositionLabel.TextTruncate = Enum.TextTruncate.AtEnd PositionLabel.Parent = item --// TP local TeleportButton = Instance.new("TextButton") TeleportButton.Size = UDim2.new( 0, 65, 0, 32 ) TeleportButton.Position = UDim2.new( 1, -75, 0.5, -16 ) TeleportButton.BackgroundColor3 = Color3.fromRGB( 70, 100, 180 ) TeleportButton.BorderSizePixel = 0 TeleportButton.Text = "TP" TeleportButton.TextColor3 = Color3.new(1, 1, 1) TeleportButton.TextSize = 14 TeleportButton.Font = Enum.Font.GothamBold TeleportButton.Parent = item local TpCorner = Instance.new("UICorner") TpCorner.CornerRadius = UDim.new(0, 6) TpCorner.Parent = TeleportButton --// REFERENCIA ACTUAL local currentCrystal = nil TeleportButton.MouseButton1Click:Connect(function() local crystal = currentCrystal if not crystal then return end if not crystal.Parent then return end local character = Player.Character local root = character and character:FindFirstChild( "HumanoidRootPart" ) local targetPart if crystal:IsA("BasePart") then targetPart = crystal else targetPart = crystal:FindFirstChildWhichIsA( "BasePart", true ) end if root and targetPart and targetPart.Parent then root.CFrame = targetPart.CFrame + Vector3.new( 0, 3, 0 ) end end) --// ACTUALIZAR ITEM local function SetCrystal(crystal) currentCrystal = crystal if not crystal then item.Visible = false return end local name = GetCrystalName( crystal ) local weight = GetCrystalWeight( crystal ) if CurrentType == "Boulders" then local rarity = GetBoulderRarity( crystal ) NameLabel.Text = "🪨 " .. name PriceLabel.Text = "⭐ " .. rarity LuckLabel.Text = "" else local price = GetCrystalPrice( crystal ) local luck = GetCrystalLuck( crystal ) NameLabel.Text = "💎 " .. name PriceLabel.Text = "💰 " .. FormatPrice(price) LuckLabel.Text = "🍀 " .. FormatLuck(luck) end WeightLabelItem.Text = "⚖️ " .. string.format( "%.2fkg", weight ) local part if crystal:IsA("BasePart") then part = crystal else part = crystal:FindFirstChildWhichIsA( "BasePart", true ) end if part then local pos = part.Position PositionLabel.Text = string.format( "📍 %.0f, %.0f, %.0f", pos.X, pos.Y, pos.Z ) else PositionLabel.Text = "📍 ?" end item.Visible = true end return { Frame = item, SetCrystal = SetCrystal } end --// ========================================================= --// POOL --// ========================================================= local VirtualItems = {} local function GetRequiredPoolSize() local viewportHeight = Scroll.AbsoluteWindowSize.Y if viewportHeight <= 0 then viewportHeight = 450 end local visibleRows = math.ceil( viewportHeight / ( ITEM_HEIGHT + ITEM_PADDING ) ) return visibleRows + EXTRA_ROWS end local function EnsurePool() local required = GetRequiredPoolSize() while #VirtualItems < required do table.insert( VirtualItems, CreateVirtualItem() ) end end --// ========================================================= --// ACTUALIZAR LISTA VIRTUAL --// ========================================================= local UpdatingVirtual = false local function UpdateVirtualList() if UpdatingVirtual then return end UpdatingVirtual = true EnsurePool() local total = #CrystalList if total == 0 then for _, virtual in ipairs( VirtualItems ) do virtual.SetCrystal(nil) end UpdatingVirtual = false return end local rowHeight = ITEM_HEIGHT + ITEM_PADDING local canvasY = Scroll.CanvasPosition.Y local firstIndex = math.floor( canvasY / rowHeight ) + 1 firstIndex = math.max( 1, firstIndex - 2 ) local poolSize = #VirtualItems for poolIndex = 1, poolSize do local crystalIndex = firstIndex + poolIndex - 1 local virtual = VirtualItems[poolIndex] if crystalIndex <= total then local crystal = CrystalList[ crystalIndex ] virtual.Frame.Position = UDim2.new( 0, 0, 0, ( crystalIndex - 1 ) * rowHeight ) virtual.SetCrystal( crystal ) else virtual.SetCrystal(nil) end end UpdatingVirtual = false end --// ========================================================= --// SCROLL --// ========================================================= Scroll:GetPropertyChangedSignal( "CanvasPosition" ):Connect(function() UpdateVirtualList() end) --// ========================================================= --// CAMBIO TAMAÑO --// ========================================================= Scroll:GetPropertyChangedSignal( "AbsoluteWindowSize" ):Connect(function() EnsurePool() UpdateVirtualList() end) --// ========================================================= --// ACTUALIZAR LISTA --// ========================================================= local function UpdateCrystalList() LoadToken += 1 local CurrentToken = LoadToken if CurrentType == "Boulders" then Title.Text = "🪨 Analizando betas..." else Title.Text = "💎 Analizando cristales..." end --// ============================================= --// OBTENER OBJETOS --// ============================================= local allCrystals = {} if CurrentType == "Boulders" then for _, crystal in ipairs( BouldersFolder:GetChildren() ) do table.insert( allCrystals, crystal ) end else for _, crystal in ipairs( CrystalsFolder:GetChildren() ) do table.insert( allCrystals, crystal ) end for _, crystal in ipairs( DroppedCrystalsFolder:GetChildren() ) do table.insert( allCrystals, crystal ) end end --// ============================================= --// FILTRO --// ============================================= local maxWeight = GetMaxWeight() local filtered = {} for i, crystal in ipairs( allCrystals ) do if CurrentToken ~= LoadToken then return end local weight = GetCrystalWeight( crystal ) if maxWeight == nil then table.insert( filtered, crystal ) elseif weight > 0 and weight <= maxWeight then table.insert( filtered, crystal ) end if i % 150 == 0 then if CurrentType == "Boulders" then Title.Text = string.format( "🪨 Analizando... %d/%d", i, #allCrystals ) else Title.Text = string.format( "💎 Analizando... %d/%d", i, #allCrystals ) end task.wait() end end if CurrentToken ~= LoadToken then return end --// ============================================= --// ORDENAR --// ============================================= if CurrentType == "Boulders" then -- ========================================== -- BETAS: -- Legendary -- Epic -- Rare -- ========================================== table.sort( filtered, function(a, b) local rarityA = GetBoulderRarity(a) local rarityB = GetBoulderRarity(b) local orderA = RarityOrder[rarityA] or 0 local orderB = RarityOrder[rarityB] or 0 if orderA == orderB then local weightA = GetCrystalWeight(a) local weightB = GetCrystalWeight(b) if weightA == weightB then return GetCrystalName(a) < GetCrystalName(b) end return weightA > weightB end return orderA > orderB end ) elseif SortMode == "Price" then -- ========================================== -- CRISTALES: PRECIO -- ========================================== table.sort( filtered, function(a, b) local priceA = GetCrystalPrice(a) local priceB = GetCrystalPrice(b) if priceA == priceB then return GetCrystalLuck(a) > GetCrystalLuck(b) end return priceA > priceB end ) else -- ========================================== -- CRISTALES: SUERTE -- ========================================== table.sort( filtered, function(a, b) local luckA = GetCrystalLuck(a) local luckB = GetCrystalLuck(b) if luckA == luckB then return GetCrystalPrice(a) > GetCrystalPrice(b) end return luckA > luckB end ) end if CurrentToken ~= LoadToken then return end CrystalList = filtered --// ============================================= --// CANVAS --// ============================================= local total = #CrystalList local rowHeight = ITEM_HEIGHT + ITEM_PADDING local canvasHeight = math.max( 0, total * rowHeight ) Scroll.CanvasSize = UDim2.new( 0, 0, 0, canvasHeight ) EnsurePool() UpdateVirtualList() --// ============================================= --// TÍTULO --// ============================================= local sortText if CurrentType == "Boulders" then sortText = "⭐ Rareza" elseif SortMode == "Price" then sortText = "💰 Precio" else sortText = "🍀 Suerte" end local icon if CurrentType == "Boulders" then icon = "🪨" else icon = "💎" end if maxWeight then Title.Text = string.format( "%s %s | ≤ %.2fkg | %d", icon, sortText, maxWeight, total ) else if CurrentType == "Boulders" then Title.Text = string.format( "🪨 %s | %d betas", sortText, total ) else Title.Text = string.format( "💎 %s | %d cristales", sortText, total ) end end end --// ========================================================= --// CAMBIAR CRISTALES / BETAS --// ========================================================= TypeButton.MouseButton1Click:Connect(function() if CurrentType == "Crystals" then CurrentType = "Boulders" TypeButton.Text = "🪨 Betas" TypeButton.BackgroundColor3 = Color3.fromRGB( 130, 100, 65 ) SortMode = "Rarity" SortButton.Text = "⭐ Rareza" else CurrentType = "Crystals" TypeButton.Text = "💎 Cristales" TypeButton.BackgroundColor3 = Color3.fromRGB( 70, 100, 180 ) SortMode = "Price" SortButton.Text = "💰 Precio" end Scroll.CanvasPosition = Vector2.new( 0, 0 ) UpdateCrystalList() end) --// ========================================================= --// CAMBIAR PRECIO / SUERTE / RAREZA --// ========================================================= SortButton.MouseButton1Click:Connect(function() if CurrentType == "Boulders" then SortMode = "Rarity" SortButton.Text = "⭐ Rareza" else if SortMode == "Price" then SortMode = "Luck" SortButton.Text = "🍀 Suerte" else SortMode = "Price" SortButton.Text = "💰 Precio" end end UpdateCrystalList() end) --// ========================================================= --// APLICAR FILTRO --// ========================================================= ApplyButton.MouseButton1Click:Connect(function() UpdateCrystalList() end) --// ========================================================= --// ENTER --// ========================================================= WeightBox.FocusLost:Connect(function( enterPressed ) if enterPressed then UpdateCrystalList() end end) --// ========================================================= --// INICIALIZAR --// ========================================================= EnsurePool() task.spawn(function() UpdateCrystalList() end) --// ========================================================= --// REFRESH AUTOMÁTICO --// ========================================================= task.spawn(function() while ScreenGui.Parent do task.wait( REFRESH_TIME ) if ScreenGui.Parent then UpdateCrystalList() end end end)