
KeylessUniversal

-- 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)No comments yet for this script.



