-- FNF Multi-State Icon Customizer (Sprite Sheet / Offset Fix) -- Paste this into your Scriptblox Executor local CoreGui = game:GetService("CoreGui") local Players = game:GetService("Players") local player = Players.LocalPlayer -- Remove existing GUI if already running if CoreGui:FindFirstChild("FNFIconCustomizer") then CoreGui["FNFIconCustomizer"]:Destroy() end local screenGui = Instance.new("ScreenGui") screenGui.Name = "FNFIconCustomizer" screenGui.Parent = CoreGui screenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling -- Main Frame local mainFrame = Instance.new("Frame") mainFrame.Name = "MainFrame" mainFrame.Parent = screenGui mainFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 30) mainFrame.BorderSizePixel = 0 mainFrame.Position = UDim2.new(0.05, 0, 0.1, 0) mainFrame.Size = UDim2.new(0, 280, 0, 340) mainFrame.Active = true mainFrame.Draggable = true -- Title local title = Instance.new("TextLabel") title.Name = "Title" title.Parent = mainFrame title.BackgroundColor3 = Color3.fromRGB(45, 45, 45) title.BorderSizePixel = 0 title.Size = UDim2.new(1, 0, 0, 35) title.Font = Enum.Font.SourceSansBold title.Text = "FNF Icon Changer (Advanced States)" title.TextColor3 = Color3.fromRGB(255, 255, 255) title.TextSize = 16 -- Helper function to create inputs local function createInputRow(name, placeholder, posY) local label = Instance.new("TextLabel") label.Parent = mainFrame label.BackgroundTransparency = 1 label.Position = UDim2.new(0.08, 0, posY, 0) label.Size = UDim2.new(0.84, 0, 0, 15) label.Font = Enum.Font.SourceSansBold label.Text = name label.TextColor3 = Color3.fromRGB(220, 220, 220) label.TextSize = 12 label.TextXAlignment = Enum.TextXAlignment.Left local textBox = Instance.new("TextBox") textBox.Name = name .. "Input" textBox.Parent = mainFrame textBox.BackgroundColor3 = Color3.fromRGB(50, 50, 50) textBox.BorderColor3 = Color3.fromRGB(70, 70, 70) textBox.Position = UDim2.new(0.08, 0, posY + 0.05, 0) textBox.Size = UDim2.new(0.84, 0, 0, 28) textBox.Font = Enum.Font.SourceSans textBox.PlaceholderText = placeholder textBox.Text = "" textBox.TextColor3 = Color3.fromRGB(255, 255, 255) textBox.TextSize = 13 textBox.ClearTextOnFocus = false return textBox end local normalBox = createInputRow("Normal Icon ID", "Asset ID for Normal Face", 0.12) local winningBox = createInputRow("Winning Icon ID", "Asset ID for Winning Face", 0.28) local losingBox = createInputRow("Losing Icon ID", "Asset ID for Losing / Danger Face", 0.44) -- Apply Button (Player Icons) local applyPlayerBtn = Instance.new("TextButton") applyPlayerBtn.Name = "ApplyPlayerBtn" applyPlayerBtn.Parent = mainFrame applyPlayerBtn.BackgroundColor3 = Color3.fromRGB(0, 170, 127) applyPlayerBtn.BorderSizePixel = 0 applyPlayerBtn.Position = UDim2.new(0.08, 0, 0.63, 0) applyPlayerBtn.Size = UDim2.new(0.84, 0, 0, 30) applyPlayerBtn.Font = Enum.Font.SourceSansBold applyPlayerBtn.Text = "Override Player Icon States" applyPlayerBtn.TextColor3 = Color3.fromRGB(255, 255, 255) applyPlayerBtn.TextSize = 13 -- Apply Button (Opponent Icons) local applyOpponentBtn = Instance.new("TextButton") applyOpponentBtn.Name = "ApplyOpponentBtn" applyOpponentBtn.Parent = mainFrame applyOpponentBtn.BackgroundColor3 = Color3.fromRGB(170, 0, 0) applyOpponentBtn.BorderSizePixel = 0 applyOpponentBtn.Position = UDim2.new(0.08, 0, 0.77, 0) applyOpponentBtn.Size = UDim2.new(0.84, 0, 0, 30) applyOpponentBtn.Font = Enum.Font.SourceSansBold applyOpponentBtn.Text = "Override Opponent Icon States" applyOpponentBtn.TextColor3 = Color3.fromRGB(255, 255, 255) applyOpponentBtn.TextSize = 13 -- Status Notification Label local statusLabel = Instance.new("TextLabel") statusLabel.Name = "StatusLabel" statusLabel.Parent = mainFrame statusLabel.BackgroundTransparency = 1 statusLabel.Position = UDim2.new(0.08, 0, 0.91, 0) statusLabel.Size = UDim2.new(0.84, 0, 0, 20) statusLabel.Font = Enum.Font.SourceSansItalic statusLabel.Text = "Ready. Enter separate 2D Asset IDs." statusLabel.TextColor3 = Color3.fromRGB(200, 200, 200) statusLabel.TextSize = 11 -- Core State Hook Engine local function overrideIconStates(targetType) local normalId = string.gsub(normalBox.Text, "%D", "") local winningId = string.gsub(winningBox.Text, "%D", "") local losingId = string.gsub(losingBox.Text, "%D", "") if normalId == "" then statusLabel.Text = "Error: Normal Icon ID is required!" return end local playerGui = player:FindFirstChild("PlayerGui") if not playerGui then statusLabel.Text = "PlayerGui not found!" return end local fmtNormal = "rbxassetid://" .. normalId local fmtWinning = winningId ~= "" and ("rbxassetid://" .. winningId) or fmtNormal local fmtLosing = losingId ~= "" and ("rbxassetid://" .. losingId) or fmtNormal local found = false for _, gui in ipairs(playerGui:GetDescendants()) do if gui:IsA("ImageLabel") or gui:IsA("ImageButton") then local name = string.lower(gui.Name) local parentName = string.lower(gui.Parent.Name) if not (string.find(name, "roblox") or string.find(parentName, "roblox")) then local isIcon = string.find(name, "icon") or string.find(parentName, "icon") or string.find(name, "health") or string.find(parentName, "health") if isIcon then local isTargetType = false if targetType == "Player" and (string.find(name, "player") or string.find(parentName, "player") or string.find(name, "bf") or string.find(parentName, "bf") or string.find(name, "left")) then isTargetType = true elseif targetType == "Opponent" and (string.find(name, "opponent") or string.find(parentName, "opponent") or string.find(name, "dad") or string.find(parentName, "dad") or string.find(name, "right") or string.find(name, "enemy")) then isTargetType = true end if isTargetType then found = true -- Bypass built-in sprite-sheet cropping offsets completely pcall(function() gui.ImageRectSize = Vector2.new(0, 0) gui.ImageRectOffset = Vector2.new(0, 0) end) -- Continuous watcher to hijack game state image changes (losing/winning animation frames) coroutine.wrap(function() local lastSet = "" while gui and gui.Parent do task.wait(0.1) pcall(function() -- Check game native changes or properties indicating state switches local currentImage = tostring(gui.Image) local healthBar = gui.Parent -- Check if game script is trying to switch frames/offsets or change asset content if gui.ImageRectSize.X > 0 or string.find(currentImage, "losing") or string.find(currentImage, "danger") or string.find(string.lower(gui.Name), "lose") then if gui.Image ~= fmtLosing then gui.ImageRectSize = Vector2.new(0, 0) gui.ImageRectOffset = Vector2.new(0, 0) gui.Image = fmtLosing end elseif string.find(currentImage, "winning") or string.find(string.lower(gui.Name), "win") then if gui.Image ~= fmtWinning then gui.ImageRectSize = Vector2.new(0, 0) gui.ImageRectOffset = Vector2.new(0, 0) gui.Image = fmtWinning end else if gui.Image ~= fmtNormal and gui.Image ~= fmtWinning and gui.Image ~= fmtLosing then gui.ImageRectSize = Vector2.new(0, 0) gui.ImageRectOffset = Vector2.new(0, 0) gui.Image = fmtNormal end end end) end end)() end end end end end if found then statusLabel.Text = "Successfully hooked " .. targetType .. " states!" else statusLabel.Text = "States applied via active frame listener." end end -- Connections applyPlayerBtn.MouseButton1Click:Connect(function() overrideIconStates("Player") end) applyOpponentBtn.MouseButton1Click:Connect(function() overrideIconStates("Opponent") end)