LocoOfficial Uno Script FIRST SCRIPT WORK OPENSOURCE
Key Required

LocoOfficial Uno Script FIRST SCRIPT WORK OPENSOURCE

LOCOfficial!PotatOsWInPotatOsWIn 487 views 1.4K executesUpdated Aug 11, 2026
Unrated
00
Lua Script•1197 lines (29301 chars)
--[[
  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")

Features & Information

Free ! Boycot ObF AutoPlay !! Free Win ! <3 By Fr
#Uno#free#BoycotObfuscate

Community Comments (0)

No comments yet for this script.

More Scripts for LOCOfficial!