--[[ UNO_LocoHub.lua v1 Place: 8571687919 — LOCOfficial! IA adaptative aux house rules · vise la victoire Remotes: PlayCard / ChooseColor / ChoosePlayer / CallOfficial / SlapOnSix Toggle UI: End ]] repeat task.wait() until game:IsLoaded() local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local TweenService = game:GetService("TweenService") local HttpService = game:GetService("HttpService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local CoreGui = game:GetService("CoreGui") local PLACE_ID = 8571687919 local LP = Players.LocalPlayer local PG = LP:WaitForChild("PlayerGui") local Remotes = ReplicatedStorage:WaitForChild("Remotes") local PlayCard = Remotes:WaitForChild("PlayCard") local ChooseColor = Remotes:WaitForChild("ChooseColor") local ChoosePlayer = Remotes:WaitForChild("ChoosePlayer") local CallOfficial = Remotes:WaitForChild("CallOfficial") local SlapOnSix = Remotes:WaitForChild("SlapOnSix") local Modules = ReplicatedStorage:FindFirstChild("Modules") local CanPlayCardMod = Modules and Modules:FindFirstChild("CanPlayCard") local CanStackCardMod = Modules and Modules:FindFirstChild("CanStackCard") local CanPlayCardFn = CanPlayCardMod and (select(1, pcall(require, CanPlayCardMod))) or nil local CanStackCardFn = CanStackCardMod and (select(1, pcall(require, CanStackCardMod))) or nil if type(CanPlayCardFn) ~= "function" then CanPlayCardFn = nil end if type(CanStackCardFn) ~= "function" then CanStackCardFn = nil end local Theme = { bg = Color3.fromRGB(14, 12, 18), panel = Color3.fromRGB(24, 18, 28), card = Color3.fromRGB(36, 28, 40), accent = Color3.fromRGB(224, 48, 96), good = Color3.fromRGB(70, 210, 120), warn = Color3.fromRGB(253, 169, 47), text = Color3.fromRGB(245, 236, 240), muted = Color3.fromRGB(150, 130, 145), off = Color3.fromRGB(52, 42, 55), danger = Color3.fromRGB(180, 45, 55), } local ScriptAlive = true local Connections = {} local GuiRef, ShellRef = nil, nil local UI = { toggles = {}, info = nil, status = nil, rules = nil } local State = { ai = false, jumpIn = true, slap = true, autoUno = true, autoCallout = false, aggressive = true, status = "prêt — AI OFF", lastPlay = 0, lastDraw = 0, lastSlap = 0, lastUno = 0, lastColor = 0, lastPick = 0, busy = false, } local PendingSwapUntil = 0 local COLORS = { "Red", "Green", "Yellow", "Blue" } local ACTION = { ["+2"] = true, ["Forward +2"] = true, ["Choice +2"] = true, Cancel = true, Reflect = true, Redirect = true, ["7"] = true, ["0"] = true, } local WILD = { Wild = true, ["Loco +5 - 1"] = true, ["Forward Loco +5 - 1"] = true, ["Loco +2"] = true, ["Loco +99"] = true, ["Draw Til Loco"] = true, ["Loco Smite +4"] = true, ["Loco 8"] = true, } local function track(c) table.insert(Connections, c) return c end local function hardUnload() local function wipe(parent) if not parent then return end for _, c in ipairs(parent:GetChildren()) do local n = c.Name if n:find("Hub", 1, true) or n:find("UNO_", 1, true) or n:find("ArenaAI", 1, true) or n:find("CAMELEON", 1, true) or n:find("PassOrExplode", 1, true) then pcall(function() c:Destroy() end) end end end wipe(PG) pcall(function() wipe(CoreGui) end) _G.UNO_LOCO_HUB = nil end hardUnload() if game.PlaceId ~= PLACE_ID then warn("[UNO Loco] placeId=", game.PlaceId, "attendu", PLACE_ID) end ---------------------------------------------------------------- -- Match / rules / hand ---------------------------------------------------------------- local function getMatchId() local id = LP:GetAttribute("MatchId") if id == nil then return nil end return tostring(id) end local function getMatch() local mid = getMatchId() if not mid then return nil end local ga = workspace:FindFirstChild("GameAssets") return ga and ga:FindFirstChild(mid) or nil end local function getPile(match) match = match or getMatch() return match and match:FindFirstChild("CardPile") or nil end local function attr(inst, key, fallback) if not inst then return fallback end local v = inst:GetAttribute(key) if v == nil then return fallback end return v end local function decodeRules() local raw = LP:GetAttribute("SavedHouseRules") local deck = "DefaultDeck" local rules = { Stacking = true, ["Anything Stacks"] = false, Defense = false, ["Jump In"] = true, ["7-0"] = true, ["Slap on 6"] = true, ["Force Play"] = true, ["No Bluffing"] = false, ["Draw Til Match"] = false, AllowEventPurchases = true, ["Starting Card Count"] = 5, ["Win Condition"] = 0, DeckName = deck, } if type(raw) == "string" and #raw > 2 then local ok, data = pcall(HttpService.JSONDecode, HttpService, raw) if ok and type(data) == "table" then local pack = data[deck] or data.DefaultDeck if type(pack) ~= "table" then for k, v in pairs(data) do if type(v) == "table" then pack = v deck = tostring(k) break end end end if type(pack) == "table" then for k, v in pairs(pack) do rules[k] = v end rules.DeckName = deck end end end -- UI house rules display overrides if present local gui = PG:FindFirstChild("cards") local list = gui and gui:FindFirstChild("Container") and gui.Container:FindFirstChild("HouseRulesDisplay") and gui.Container.HouseRulesDisplay:FindFirstChild("HouseRules") and gui.Container.HouseRulesDisplay.HouseRules:FindFirstChild("RulesList") if list then local known = { ["Jump In"] = true, ["Force Play"] = true, ["Slap on 6"] = true, ["7-0"] = true, Stacking = true, Defense = true, ["Anything Stacks"] = true, ["No Bluffing"] = true, ["Draw Til Match"] = true, } for name in pairs(known) do rules[name] = list:FindFirstChild(name) ~= nil end end return rules end local function getTop(match) match = match or getMatch() local pile = getPile(match) local color = attr(match, "Color") or attr(pile, "Color") local symbol = attr(match, "Symbol") or attr(pile, "Symbol") if color == nil or symbol == nil then return nil end return { Color = tostring(color), Symbol = tostring(symbol) } end local function mySeat(match) match = match or getMatch() if not match then return nil end for i = 1, 12 do local name = attr(match, "Player_" .. i) if name and tostring(name) == LP.Name then return i end end return nil end local function isMyTurn(match) match = match or getMatch() if not match then return false end local seat = mySeat(match) if not seat then return false end local turn = tonumber(attr(match, "CurrentTurn")) or tonumber(attr(match, "ActualCurrentTurn")) return turn == seat end local function canAct(match) match = match or getMatch() if not match or LP:GetAttribute("InMatch") ~= true then return false end return isMyTurn(match) end local function getHandConfigs() local folder = ReplicatedStorage:FindFirstChild("ClientCards") local out = {} if not folder then return out end for _, c in ipairs(folder:GetChildren()) do local color = c:GetAttribute("Color") local symbol = c:GetAttribute("Symbol") if color and symbol then table.insert(out, { inst = c, Color = tostring(color), Symbol = tostring(symbol), attrs = c:GetAttributes(), }) end end return out end local function getOpponentCounts(match) match = match or getMatch() local mid = getMatchId() local out = {} if not mid then return out end local cd = workspace:FindFirstChild("GameAssets") and workspace.GameAssets:FindFirstChild("CardDisplays/" .. mid) if not cd then return out end for _, folder in ipairs(cd:GetChildren()) do local seat, name = string.match(folder.Name, "^(%d+)/(.+)$") if seat and name then table.insert(out, { seat = tonumber(seat), name = name, count = tonumber(folder:GetAttribute("CurrentCardCount")) or 99, isMe = name == LP.Name, }) end end table.sort(out, function(a, b) return a.count < b.count end) return out end local function lowestEnemy(counts) for _, p in ipairs(counts) do if not p.isMe then return p end end return nil end local function highestEnemy(counts) local best = nil for _, p in ipairs(counts) do if not p.isMe then best = p end end return best end local function myCount(counts) for _, p in ipairs(counts) do if p.isMe then return p.count end end return #getHandConfigs() end local function canPlayLocal(top, card) if not top or not card then return false end if CanPlayCardFn then local ok, res = pcall(CanPlayCardFn, top, card) if ok then return res and true or false end end if card.Color == "Wild" or top.Color == "Wild" then return true end return card.Color == top.Color or tostring(card.Symbol) == tostring(top.Symbol) end local function canStackLocal(stackSymbol, top, card, rules) if CanStackCardFn then local ok, res = pcall(CanStackCardFn, stackSymbol, top, card, rules) if ok then return res and true or false end end if not rules.Stacking and not rules["Anything Stacks"] then return false end if tostring(card.Symbol) == tostring(stackSymbol) then return canPlayLocal(top, card) end if rules.Defense and (card.Symbol == "Reflect" or card.Symbol == "Cancel" or card.Symbol == "Redirect") then return canPlayLocal(top, card) end if rules["Anything Stacks"] and type(card.Symbol) == "string" and string.find(card.Symbol, "+", 1, true) then return canPlayLocal(top, card) end return false end local function colorCounts(hand) local t = { Red = 0, Green = 0, Yellow = 0, Blue = 0 } for _, c in ipairs(hand) do if t[c.Color] ~= nil then t[c.Color] += 1 end end return t end local function bestColor(hand) local counts = colorCounts(hand) local best, n = "Red", -1 for _, col in ipairs(COLORS) do if counts[col] > n then best, n = col, counts[col] end end return best end local function isWildCard(card) return card.Color == "Wild" or WILD[tostring(card.Symbol)] == true end local function isDrawAction(sym) sym = tostring(sym) return string.find(sym, "+", 1, true) ~= nil end local function scoreCard(card, hand, top, rules, counts, stacking) local score = 10 local sym = tostring(card.Symbol) local mine = myCount(counts) local threat = lowestEnemy(counts) local threatN = threat and threat.count or 99 if stacking then score += 40 end if isWildCard(card) then score -= (mine <= 2) and -25 or 35 elseif ACTION[sym] or isDrawAction(sym) then score += 18 if threatN <= 2 then score += 30 end if sym == "Cancel" or sym == "Reflect" or sym == "Redirect" then score += 8 end else local cc = colorCounts(hand) score += (cc[card.Color] or 0) * 3 local num = tonumber(sym) if num then score += (9 - math.abs(num - 4)) * 0.2 end end if rules["7-0"] then if sym == "7" then if mine >= 4 and threatN <= 3 then score += 45 elseif mine <= 2 then score -= 10 else score += 12 end elseif sym == "0" then score += (mine >= 5) and 15 or 5 end end if rules["Slap on 6"] and sym == "6" then score += State.slap and 6 or -4 end if State.aggressive and threatN <= 1 and (isDrawAction(sym) or sym == "Cancel") then score += 50 end -- dump same color as top to keep control if top and card.Color == top.Color and not isWildCard(card) then score += 6 end return score end local function pickBestPlay(match, rules) local hand = getHandConfigs() local top = getTop(match) if not top or #hand == 0 then return nil, hand, top end local stacking = attr(match, "IsStacking") == true local stackCount = tonumber(attr(match, "StackCount")) or 0 local stackSym = tostring(top.Symbol) local counts = getOpponentCounts(match) local best, bestScore = nil, -1e9 for _, card in ipairs(hand) do local legal = false if stacking and stackCount > 0 and (rules.Stacking or rules["Anything Stacks"] or rules.Defense) then legal = canStackLocal(stackSym, top, card, rules) else legal = canPlayLocal(top, card) end if legal then local s = scoreCard(card, hand, top, rules, counts, stacking) if s > bestScore then bestScore = s best = card end end end return best, hand, top, counts end local function exactJumpCandidates(match, rules) if not rules["Jump In"] then return {} end local pile = getPile(match) if attr(match, "CanJumpIn") ~= true and attr(pile, "CanJumpIn") ~= true then return {} end local top = getTop(match) if not top then return {} end local out = {} for _, card in ipairs(getHandConfigs()) do if tostring(card.Symbol) == tostring(top.Symbol) and (card.Color == top.Color or card.Color == "Wild") then table.insert(out, card) end end return out end ---------------------------------------------------------------- -- Actions ---------------------------------------------------------------- local function firePlay(attrs) return pcall(function() PlayCard:FireServer(attrs) end) end local function playCard(card) if not card or not card.attrs then return false end local now = os.clock() if now - State.lastPlay < 0.28 then return false end State.lastPlay = now local ok = firePlay(card.attrs) if ok then State.status = ("joue %s/%s"):format(card.Color, card.Symbol) if tostring(card.Symbol) == "7" then PendingSwapUntil = os.clock() + 2.5 end end return ok end local function drawCard() local now = os.clock() if now - State.lastDraw < 0.45 then return false end State.lastDraw = now local ok = firePlay({ DrawCard = true }) if ok then State.status = "pioche…" end return ok end local function keepDrawn() return firePlay("Keep") end local function tryPlayDrawnForce(rules) local gui = PG:FindFirstChild("cards") local frame = gui and gui:FindFirstChild("Container") and gui.Container:FindFirstChild("PlayDrawnCard") if not frame or not frame.Visible then return false end -- Force Play: jouer la carte piochée si possible local hand = getHandConfigs() local last = hand[#hand] if last and (rules["Force Play"] or true) then return playCard(last) end return false end local function doSlap() if not State.slap then return false end local now = os.clock() if now - State.lastSlap < 0.15 then return false end local gui = PG:FindFirstChild("cards") local slap = gui and gui.Container and gui.Container:FindFirstChild("SlapButton") local match = getMatch() local need = (slap and slap.Visible) or attr(match, "IsSlapping") == true if not need then local top = getTop(match) if top and tostring(top.Symbol) == "6" and decodeRules()["Slap on 6"] then need = true end end if not need then return false end State.lastSlap = now local ok = pcall(function() SlapOnSix:FireServer() end) if slap and slap:FindFirstChild("Button") then pcall(function() slap.Button:Activate() end) end if ok then State.status = "SLAP 6!" end return ok end local function doUno() if not State.autoUno then return false end local now = os.clock() if now - State.lastUno < 0.35 then return false end local gui = PG:FindFirstChild("cards") local btn = gui and gui.Container and gui.Container:FindFirstChild("OfficialButton") local counts = getOpponentCounts() local mine = myCount(counts) local visible = btn and btn.Visible if not visible and mine > 1 then return false end if mine > 1 and not visible then return false end State.lastUno = now local ok = pcall(function() CallOfficial:FireServer() end) if btn and btn:FindFirstChild("Button") then pcall(function() btn.Button:Activate() end) end if ok then State.status = "OFFICIAL!" end return ok end local function doCallout() if not State.autoCallout then return false end local gui = PG:FindFirstChild("cards") local btn = gui and gui.Container and gui.Container:FindFirstChild("CalloutButton") local challenge = gui and gui.Container and gui.Container:FindFirstChild("ChallengingFrame") if challenge and challenge.Visible and challenge:FindFirstChild("Challenge") then pcall(function() challenge.Challenge:Activate() end) firePlay("Callout") State.status = "challenge +4" return true end if btn and btn.Visible then firePlay("Callout") pcall(function() if btn.Button then btn.Button:Activate() end end) State.status = "callout" return true end return false end local function doChooseColor(hand) if workspace:GetAttribute("PickingColor") ~= true then return false end local now = os.clock() if now - State.lastColor < 0.25 then return false end State.lastColor = now local col = bestColor(hand or getHandConfigs()) local ok = pcall(function() ChooseColor:FireServer(col) end) if ok then State.status = "couleur " .. col end return ok end local function pickSwapTarget(counts) local mine = myCount(counts) if mine >= 4 then return lowestEnemy(counts) end return highestEnemy(counts) end local function doChoosePlayer(counts) local now = os.clock() if now - State.lastPick < 0.35 then return false end local picking = workspace:GetAttribute("PickingPlayer") == true or workspace:GetAttribute("ChoosingPlayer") == true or now < PendingSwapUntil if not picking then return false end local target = pickSwapTarget(counts or getOpponentCounts()) if not target then return false end State.lastPick = now local ok = pcall(function() ChoosePlayer:FireServer(target.name) end) if ok then State.status = "swap → " .. target.name PendingSwapUntil = 0 end return ok end track(ChooseColor.OnClientEvent:Connect(function() if not State.ai or not ScriptAlive then return end task.defer(function() task.wait(0.05) doChooseColor() end) end)) ---------------------------------------------------------------- -- AI tick ---------------------------------------------------------------- local function aiTick() if not State.ai or not ScriptAlive or State.busy then return end if LP:GetAttribute("InMatch") ~= true then State.status = "hors partie" return end State.busy = true local ok, err = pcall(function() local match = getMatch() local rules = decodeRules() local hand = getHandConfigs() local counts = getOpponentCounts(match) doSlap() doUno() doCallout() doChooseColor(hand) doChoosePlayer(counts) -- Jump-in if State.jumpIn and rules["Jump In"] and not isMyTurn(match) then local jumps = exactJumpCandidates(match, rules) if #jumps > 0 then local best, bs = jumps[1], -1e9 for _, c in ipairs(jumps) do local s = scoreCard(c, hand, getTop(match), rules, counts, false) + 20 if s > bs then best, bs = c, s end end playCard(best) doUno() return end end tryPlayDrawnForce(rules) if not canAct(match) and not isMyTurn(match) then State.status = "attente tour" return end local stacking = attr(match, "IsStacking") == true local best = pickBestPlay(match, rules) if best then playCard(best) task.delay(0.2, function() if ScriptAlive then doUno() doChooseColor() doChoosePlayer() end end) else if stacking then drawCard() elseif isMyTurn(match) or canAct(match) then drawCard() end end end) State.busy = false if not ok then State.status = "err" warn("[UNO Loco]", err) end end ---------------------------------------------------------------- -- UI ---------------------------------------------------------------- local function corner(o, r) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, r or 10) c.Parent = o return c end local function stroke(o, col, th, tr) local s = Instance.new("UIStroke") s.Color = col or Theme.muted s.Thickness = th or 1 s.Transparency = tr or 0.45 s.Parent = o return s end local function setToggleVisual(sw, on) sw.BackgroundColor3 = on and Theme.good or Theme.off local knob = sw:FindFirstChild("Knob") if knob then TweenService:Create(knob, TweenInfo.new(0.15), { Position = on and UDim2.new(1, -22, 0.5, -9) or UDim2.new(0, 4, 0.5, -9), BackgroundColor3 = on and Color3.fromRGB(240, 255, 245) or Color3.fromRGB(180, 170, 185), }):Play() end end local function unload() ScriptAlive = false for _, c in ipairs(Connections) do pcall(function() c:Disconnect() end) end table.clear(Connections) if GuiRef then pcall(function() GuiRef:Destroy() end) end GuiRef, ShellRef = nil, nil _G.UNO_LOCO_HUB = nil end local function buildUI() local gui = Instance.new("ScreenGui") gui.Name = "UNO_LocoHub" gui.ResetOnSpawn = false gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling gui.DisplayOrder = 120 pcall(function() gui.Parent = CoreGui end) if not gui.Parent then gui.Parent = PG end GuiRef = gui local shell = Instance.new("Frame") shell.Name = "Shell" shell.Size = UDim2.fromOffset(320, 470) shell.Position = UDim2.new(0, 18, 0.5, -235) shell.BackgroundColor3 = Theme.bg shell.BorderSizePixel = 0 shell.Active = true shell.Parent = gui corner(shell, 16) stroke(shell, Theme.accent, 1.2, 0.35) ShellRef = shell do local dragging, dragStart, startPos track(shell.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragStart = input.Position startPos = shell.Position end end)) track(shell.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = false end end)) track(UserInputService.InputChanged:Connect(function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then local d = input.Position - dragStart shell.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + d.X, startPos.Y.Scale, startPos.Y.Offset + d.Y) end end)) end local header = Instance.new("Frame") header.Size = UDim2.new(1, 0, 0, 64) header.BackgroundColor3 = Theme.panel header.BorderSizePixel = 0 header.Parent = shell corner(header, 16) local cover = Instance.new("Frame") cover.Size = UDim2.new(1, 0, 0, 20) cover.Position = UDim2.new(0, 0, 1, -20) cover.BackgroundColor3 = Theme.panel cover.BorderSizePixel = 0 cover.Parent = header local brand = Instance.new("TextLabel") brand.BackgroundTransparency = 1 brand.Position = UDim2.fromOffset(16, 10) brand.Size = UDim2.new(1, -90, 0, 22) brand.Font = Enum.Font.GothamBold brand.TextSize = 18 brand.TextXAlignment = Enum.TextXAlignment.Left brand.TextColor3 = Theme.accent brand.Text = "UNO LOCO" brand.Parent = header local sub = Instance.new("TextLabel") sub.BackgroundTransparency = 1 sub.Position = UDim2.fromOffset(16, 34) sub.Size = UDim2.new(1, -90, 0, 18) sub.Font = Enum.Font.Gotham sub.TextSize = 12 sub.TextXAlignment = Enum.TextXAlignment.Left sub.TextColor3 = Theme.muted sub.Text = "Hub v1 · End pour cacher" sub.Parent = header local closeBtn = Instance.new("TextButton") closeBtn.Size = UDim2.fromOffset(32, 32) closeBtn.Position = UDim2.new(1, -44, 0, 16) closeBtn.BackgroundColor3 = Theme.danger closeBtn.Text = "×" closeBtn.Font = Enum.Font.GothamBold closeBtn.TextSize = 18 closeBtn.TextColor3 = Theme.text closeBtn.Parent = header corner(closeBtn, 8) track(closeBtn.Activated:Connect(unload)) local infoCard = Instance.new("Frame") infoCard.Size = UDim2.new(1, -32, 0, 72) infoCard.Position = UDim2.fromOffset(16, 76) infoCard.BackgroundColor3 = Theme.card infoCard.BorderSizePixel = 0 infoCard.Parent = shell corner(infoCard, 10) local info = Instance.new("TextLabel") info.BackgroundTransparency = 1 info.Size = UDim2.new(1, -16, 1, -12) info.Position = UDim2.fromOffset(8, 6) info.Font = Enum.Font.Gotham info.TextSize = 12 info.TextColor3 = Theme.muted info.TextXAlignment = Enum.TextXAlignment.Left info.TextYAlignment = Enum.TextYAlignment.Top info.TextWrapped = true info.Text = "…" info.Parent = infoCard UI.info = info local rulesLbl = Instance.new("TextLabel") rulesLbl.BackgroundTransparency = 1 rulesLbl.Position = UDim2.fromOffset(16, 156) rulesLbl.Size = UDim2.new(1, -32, 0, 36) rulesLbl.Font = Enum.Font.Gotham rulesLbl.TextSize = 11 rulesLbl.TextColor3 = Theme.warn rulesLbl.TextXAlignment = Enum.TextXAlignment.Left rulesLbl.TextYAlignment = Enum.TextYAlignment.Top rulesLbl.TextWrapped = true rulesLbl.Text = "Règles…" rulesLbl.Parent = shell UI.rules = rulesLbl local y = 198 local function sectionLabel(text) local l = Instance.new("TextLabel") l.BackgroundTransparency = 1 l.Position = UDim2.fromOffset(18, y) l.Size = UDim2.new(1, -36, 0, 16) l.Font = Enum.Font.GothamBold l.TextSize = 11 l.TextColor3 = Theme.accent l.TextXAlignment = Enum.TextXAlignment.Left l.Text = string.upper(text) l.Parent = shell y += 20 end local function toggleRow(label, key, defaultOn, onChange) local row = Instance.new("Frame") row.Size = UDim2.new(1, -32, 0, 34) row.Position = UDim2.fromOffset(16, y) row.BackgroundColor3 = Theme.card row.BorderSizePixel = 0 row.Parent = shell corner(row, 8) local lbl = Instance.new("TextLabel") lbl.BackgroundTransparency = 1 lbl.Position = UDim2.fromOffset(12, 0) lbl.Size = UDim2.new(1, -70, 1, 0) lbl.Font = Enum.Font.GothamMedium lbl.TextSize = 13 lbl.TextColor3 = Theme.text lbl.TextXAlignment = Enum.TextXAlignment.Left lbl.Text = label lbl.Parent = row local sw = Instance.new("TextButton") sw.Size = UDim2.fromOffset(44, 22) sw.Position = UDim2.new(1, -54, 0.5, -11) sw.BackgroundColor3 = Theme.off sw.Text = "" sw.Parent = row corner(sw, 11) local knob = Instance.new("Frame") knob.Name = "Knob" knob.Size = UDim2.fromOffset(18, 18) knob.Position = UDim2.new(0, 4, 0.5, -9) knob.BackgroundColor3 = Color3.fromRGB(180, 170, 185) knob.BorderSizePixel = 0 knob.Parent = sw corner(knob, 9) local on = defaultOn and true or false setToggleVisual(sw, on) UI.toggles[key] = { get = function() return on end, set = function(v) on = v and true or false setToggleVisual(sw, on) end, } track(sw.Activated:Connect(function() on = not on setToggleVisual(sw, on) pcall(onChange, on) end)) y += 40 end sectionLabel("Intelligence") toggleRow("Auto-play (gagner)", "ai", false, function(on) State.ai = on State.status = on and "AI ON — chasse la win" or "AI OFF" end) toggleRow("À la volée (Jump-in)", "jump", true, function(on) State.jumpIn = on end) toggleRow("Frappe le 6", "slap", true, function(on) State.slap = on end) toggleRow("Call Official (UNO)", "uno", true, function(on) State.autoUno = on end) toggleRow("Challenge bluff (+4)", "callout", false, function(on) State.autoCallout = on end) toggleRow("Mode agressif", "aggro", true, function(on) State.aggressive = on end) local status = Instance.new("TextLabel") status.BackgroundTransparency = 1 status.Position = UDim2.fromOffset(16, y + 4) status.Size = UDim2.new(1, -32, 0, 36) status.Font = Enum.Font.GothamMedium status.TextSize = 12 status.TextColor3 = Theme.good status.TextXAlignment = Enum.TextXAlignment.Left status.TextWrapped = true status.Text = State.status status.Parent = shell UI.status = status track(UserInputService.InputBegan:Connect(function(input, gp) if gp then return end if input.KeyCode == Enum.KeyCode.End then shell.Visible = not shell.Visible end end)) end local function refreshUI() if not UI.info then return end local match = getMatch() local rules = decodeRules() local top = getTop(match) local hand = getHandConfigs() local counts = getOpponentCounts(match) local seat = mySeat(match) local turn = match and attr(match, "CurrentTurn") local parts = {} table.insert(parts, ("Match %s · siège %s · tour %s"):format(tostring(getMatchId() or "?"), tostring(seat or "?"), tostring(turn or "?"))) if top then table.insert(parts, ("Pile: %s/%s · main: %d"):format(top.Color, top.Symbol, #hand)) else table.insert(parts, ("Main: %d"):format(#hand)) end local opp = {} for _, p in ipairs(counts) do if not p.isMe then table.insert(opp, ("%s:%d"):format(p.name, p.count)) end end if #opp > 0 then table.insert(parts, table.concat(opp, " ")) end if attr(match, "IsStacking") then table.insert(parts, ("STACK x%s"):format(tostring(attr(match, "StackCount")))) end UI.info.Text = table.concat(parts, "\n") local rbits = {} for _, key in ipairs({ "Stacking", "Jump In", "7-0", "Slap on 6", "Force Play", "Defense", "Anything Stacks", "No Bluffing" }) do if rules[key] then table.insert(rbits, key) end end UI.rules.Text = "Règles: " .. (#rbits > 0 and table.concat(rbits, " · ") or "aucune") if UI.status then UI.status.Text = State.status end end buildUI() track(RunService.Heartbeat:Connect(function() if not ScriptAlive then return end end)) task.spawn(function() while ScriptAlive do refreshUI() if State.ai then aiTick() end task.wait(0.18) end end) _G.UNO_LOCO_HUB = { unload = unload, state = State, version = "1.0.0", } print("[UNO Loco] Hub v1 prêt — active Auto-play")