Skip to content
RoRoScripts
NOVA preview
UnknownUniversal

NOVA

Script
Universal Scriptthenovaownerofical
Views
414
Likes
1
Dislikes
0
Added
Script
-- NOVA v3.38 / Native adapters and shared shooter controls.
local Players=game:GetService("Players")
local UIS=game:GetService("UserInputService")
local Run=game:GetService("RunService")
local Tween=game:GetService("TweenService")
local Http=game:GetService("HttpService")
local LP=Players.LocalPlayer
if not LP then return end
local FORCE_MOBILE=nil -- nil = automatic device detection.
local TEST_VIEWPORT=nil -- Internal layout test only.
local preferredTouch=false
pcall(function()preferredTouch=UIS.PreferredInput==Enum.PreferredInput.Touch end)
local mobile=UIS.TouchEnabled and (preferredTouch or not UIS.KeyboardEnabled or not UIS.MouseEnabled)
if FORCE_MOBILE~=nil then mobile=FORCE_MOBILE end
if not mobile and (not UIS.KeyboardEnabled or not UIS.MouseEnabled) then warn("NOVA requires touch or keyboard/mouse.");return end
local deviceName=mobile and "MOBILE" or "PC"
local pg=LP:WaitForChild("PlayerGui")
local saved={}
for _,name in ipairs({"NovaPC","NovaPC2"}) do local old=pg:FindFirstChild(name); if old then saved=old:GetAttributes(); old:Destroy() end end
local C={InfiniteAmmo=false,AutoReload=false,AutoClicker=false,ClickRate=10,NoCooldown=false,TriggerBot=false,AutoShoot=false,FireRate=6,FireDelay=80,FireAngle=2.5,FPSDetails=true,FPSGraph=true,FPSCorner="Top left",WatermarkDetails=true,WatermarkCorner="Top left",HUDScale=100,HUDOpacity=88,
    AimThroughWalls=false,PlayerTarget="",PlayerStandOff=5,AimPriority="Crosshair",AimFallback=true,AimResponse="Smooth",
    AimPrediction=false,AimLead=60,AimRetention=35,Fly=false,FlySpeed=50,FlyVerticalSpeed=35,FlyBoost=2,
    Hitboxes=false,HitboxDisplay=true,HitboxPart="Head",HitboxSize=18,HitboxOpacity=65,HitboxTeams=false,WallShots=false,
    TravelSlot="1",TravelHeight=0,TravelForward=0,TravelFacing=true,TravelStop=true,
    TravelRemember=false,TravelMarker=false,SpeedOverride=false,WalkSpeed=16,
    JumpOverride=false,JumpPower=50,JumpHeight=7,GravityOverride=false,Gravity=196.2,
    AutoRotateOverride=false,AutoRotate=true,ZoomOverride=false,MinZoom=0.5,MaxZoom=128,
    ViewMode="Game default",CameraOffsetOverride=false,CameraHeight=0,CameraSide=0,
    Aim=false,ESP=true,ESPAll=true,ESPUnlimited=true,ESPInterval=50,Names=true,Health=true,Distance=true,Through=true,Teams=false,Visibility=true,
    Circle=true,Sticky=true,FOV=180,Speed=16,Range=1500,Opacity=70,Part="Head",Mode="Hold RMB",
    Origin="Screen center",Color="Silver",Motion=true,Backdrop=68,
    AnimationSpeed=100,Parallax=true,Notifications=true,Background="Chrome",
    FullBright=false,CustomLight=false,LightBrightness=2,LightExposure=0,AmbientLevel=70,
    TimeLock=false,DayTime=14,NoShadows=false,NoFog=false,NoBloom=false,NoBlur=false,
    NoSunRays=false,NoColor=false,NoDOF=false,CameraOverride=false,CameraFOV=70,
    ShowStatus=true,ShowFPS=true,Watermark=true,QuickCursor=true,ShowAimPad=true,
    AimPadSize=76,UISize=100,HideRingMenu=true,Crosshair=false,CrosshairSize=8,CrosshairGap=4,
    CrosshairStyle="Plus",LabelSize=12,LabelHeight=3,LabelRange=1500,OutlineFade=5}
for k,v in pairs(C) do if typeof(saved[k])==typeof(v) then C[k]=saved[k] end end
for key,value in pairs(C)do if type(value)=="boolean"then C[key]=false end end
C.ViewMode="Game default"
C.Color="Background"
C.Fly=false;C.WallShots=false;C.TriggerBot=false;C.AutoShoot=false;C.NoCooldown=false;C.InfiniteAmmo=false;C.AutoReload=false;C.AutoClicker=false
if saved.Version~="3.14" and saved.Version~="3.15" and saved.Version~="3.16" and saved.Version~="3.17" and saved.Version~="3.18" and saved.Version~="3.19" then C.FPSCorner="Top left";C.WatermarkCorner="Top left" end
if C.HitboxPart=="HumanoidRootPart" then C.HitboxPart="Head" end
if saved.Version~="3.9" and saved.Version~="3.10" and saved.Version~="3.11" and saved.Version~="3.12" and saved.Version~="3.13" and saved.Version~="3.14" and saved.Version~="3.15" and saved.Version~="3.16" and saved.Version~="3.17" and saved.Version~="3.18" and saved.Version~="3.19" then C.HitboxSize=math.max(18,C.HitboxSize);C.Speed=32 end
if mobile then
    if C.Mode=="Hold RMB" then C.Mode="Hold AIM" elseif C.Mode=="Toggle RMB" then C.Mode="Toggle AIM" end
    C.Origin="Screen center"
elseif C.Mode=="Hold AIM" then C.Mode="Hold RMB"
elseif C.Mode=="Toggle AIM" then C.Mode="Toggle RMB" end
local backgroundList={
    {name="Chrome",file="liquid-metal.png"},
    {name="Pearl",file="pearl.png"},
    {name="Neon Tide",file="neon-tide.png"},
    {name="Violet Haze",file="violet-haze.png",stretch=true},
    {name="Ember",file="ember.png"},
    {name="Nebula",file="nebula.png"},
    {name="Lava Glow",file="lava-glow.png"}
}
local backgroundIndex={}
for i,entry in ipairs(backgroundList) do backgroundIndex[entry.name]=i end
if not backgroundIndex[C.Background] then C.Background="Chrome" end
local themeProfiles={
    Chrome={primary=Color3.fromRGB(234,237,244),muted=Color3.fromRGB(154,160,176),accent=Color3.fromRGB(223,226,233)},
    Pearl={primary=Color3.fromRGB(247,237,255),muted=Color3.fromRGB(195,179,211),accent=Color3.fromRGB(227,207,249)},
    ["Neon Tide"]={primary=Color3.fromRGB(143,248,222),muted=Color3.fromRGB(129,189,194),accent=Color3.fromRGB(83,232,202)},
    ["Violet Haze"]={primary=Color3.fromRGB(227,198,255),muted=Color3.fromRGB(177,150,204),accent=Color3.fromRGB(190,142,249)},
    Ember={primary=Color3.fromRGB(255,213,156),muted=Color3.fromRGB(207,166,125),accent=Color3.fromRGB(255,170,81)},
    Nebula={primary=Color3.fromRGB(250,185,232),muted=Color3.fromRGB(191,150,205),accent=Color3.fromRGB(237,131,213)},
    ["Lava Glow"]={primary=Color3.fromRGB(255,207,142),muted=Color3.fromRGB(205,149,178),accent=Color3.fromRGB(255,151,83)}
}
local currentTheme=themeProfiles[C.Background]
local palettes={Silver=Color3.fromRGB(223,226,233),Violet=Color3.fromRGB(174,153,255),
    Cyan=Color3.fromRGB(126,224,239),Rose=Color3.fromRGB(244,151,185),Lime=Color3.fromRGB(188,232,166)}
palettes.Background=currentTheme.accent
local ink=currentTheme.primary
local muted=currentTheme.muted
local bg=Color3.fromRGB(10,11,14)
local card=Color3.fromRGB(23,24,29)
local cons,records,paints={}, {}, {}
local applyWorld,restoreWorld
local notify
local switchBackground
local applyTheme
local tabNames={"Game","Aim","Combat","Visuals","Travel","Extras","Commands","Bot","World","Interface","Tools","Settings"}
local alive,free,focus,latch=true,true,true,false
local selected,drag,sliding
local aimTouch,touchHolding=nil,false
local menuOpen=true
local lastMouse,lastIcon=UIS.MouseBehavior,UIS.MouseIconEnabled
local uniqueId=tostring(LP.UserId).."_"..tostring(os.clock()):gsub("%W","")
pcall(function()uniqueId=Http:GenerateGUID(false)end)
local binding="Nova2_"..uniqueId
local traceError=(debug and debug.traceback) or function(err)return tostring(err)end
local function make(kind,props,parent)
    local o=Instance.new(kind);for k,v in pairs(props) do o[k]=v end;if o:IsA("GuiObject") and props.BackgroundColor3==ink then o:SetAttribute("ThemeFill","Primary") end
    o.Parent=parent;return o
end
local function on(signal,fn) local c=signal:Connect(fn);table.insert(cons,c);return c end
local function round(o,r) make("UICorner",{CornerRadius=UDim.new(0,r or 12)},o) end
local anims={}
local function motionDuration(value) return C.Motion and value/math.max(.5,C.AnimationSpeed/100) or 0 end
local function animate(o,props,duration,easing)
    if not alive or not o.Parent then return end
    local state=anims[o];if not state then state={};anims[o]=state end
    for property,value in pairs(props) do
        if state[property] then state[property]:Cancel() end
        if not C.Motion or duration==0 then
            o[property]=value;state[property]=nil
        else
            local tw=Tween:Create(o,TweenInfo.new(motionDuration(duration or .24),easing or Enum.EasingStyle.Quint,Enum.EasingDirection.Out),{[property]=value})
            anims[o]=state;state[property]=tw
            tw.Completed:Once(function()
                if state[property]==tw then state[property]=nil end
                if next(state)==nil and anims[o]==state then anims[o]=nil end
            end)
            tw:Play()
        end
    end
    if next(state)==nil then anims[o]=nil end
end
local function text(parent,value,size,color,pos,dimensions)
    local label=make("TextLabel",{BackgroundTransparency=1,Text=value,TextSize=size or 13,
        TextColor3=color or ink,Font=Enum.Font.GothamMedium,TextXAlignment=Enum.TextXAlignment.Left,
        TextTruncate=Enum.TextTruncate.AtEnd,Position=pos or UDim2.new(),Size=dimensions or UDim2.new(1,0,1,0)},parent)
    label:SetAttribute("ThemeRole",color==muted and "Muted" or "Primary")
    return label
end
local function outline(o,transparency)
    local edge=make("UIStroke",{Color=muted,Transparency=transparency or .85,Thickness=1},o)
    edge:SetAttribute("ThemeEdge",true);return edge
end
local function button(parent,name,value,pos,size)
    local b=make("TextButton",{Name=name,Text=value,Position=pos,Size=size,
        Font=Enum.Font.GothamMedium,TextSize=11,TextColor3=ink,AutoButtonColor=false,
        BackgroundColor3=card,BackgroundTransparency=.15,BorderSizePixel=0,ClipsDescendants=true},parent)
    round(b,10)
    b:SetAttribute("ThemeRole","Primary")
    if parent:IsA("ScreenGui") then b.ZIndex=10 end
    b:SetAttribute("RestColor",card);b:SetAttribute("RestAlpha",.15)
    local border=outline(b,.82);border.Name="ButtonEdge"
    local press=make("UIScale",{Name="Press",Scale=1},b)
    local hovered=false
    local function paintHover()
        local col=b:GetAttribute("RestColor") or card
        animate(b,{BackgroundColor3=hovered and col:Lerp(ink,.12) or col,
            BackgroundTransparency=hovered and .06 or (b:GetAttribute("RestAlpha") or .15)},.2)
        animate(border,{Transparency=hovered and .34 or .82},.2)
    end
    on(b.MouseEnter,function() hovered=true;paintHover() end)
    on(b.MouseLeave,function() hovered=false;paintHover();animate(press,{Scale=1},.25,Enum.EasingStyle.Back) end)
    on(b.InputBegan,function(i)
        if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then animate(press,{Scale=.96},.1) end
    end)
    on(b.InputEnded,function(i)
        if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then animate(press,{Scale=1},.3,Enum.EasingStyle.Back) end
    end)
    local ripple
    on(b.Activated,function(input)
        animate(press,{Scale=1},.3,Enum.EasingStyle.Back)
        if not C.Motion then return end
        if ripple then ripple:Destroy() end
        local p=Vector2.new(b.AbsoluteSize.X/2,b.AbsoluteSize.Y/2)
        if input then p=Vector2.new(input.Position.X,input.Position.Y)-b.AbsolutePosition end
        ripple=make("Frame",{Name="Ripple",AnchorPoint=Vector2.new(.5,.5),Position=UDim2.fromOffset(p.X,p.Y),
            Size=UDim2.fromOffset(2,2),BorderSizePixel=0,BackgroundColor3=ink,BackgroundTransparency=.82,
            ZIndex=3},b);round(ripple,1000)
        local current=ripple;local diameter=math.max(b.AbsoluteSize.X,b.AbsoluteSize.Y)*2.1
        animate(current,{Size=UDim2.fromOffset(diameter,diameter),BackgroundTransparency=1},.55)
        task.delay(motionDuration(.58),function() if current.Parent then current:Destroy() end end)
    end)
    return b
end
local gui=make("ScreenGui",{Name="NovaPC2",IgnoreGuiInset=true,ResetOnSpawn=false,
    DisplayOrder=1000,ZIndexBehavior=Enum.ZIndexBehavior.Sibling},pg)
if mobile then pcall(function()gui.ScreenInsets=Enum.ScreenInsets.CoreUISafeInsets end)end
local world=make("Folder",{Name=binding},workspace)
local overlay=make("Frame",{Name="Overlay",Size=UDim2.fromScale(1,1),BackgroundTransparency=1},gui)
local root=make("CanvasGroup",{Name="Window",AnchorPoint=Vector2.new(.5,.5),
    Position=UDim2.fromScale(.5,.5),Size=UDim2.fromOffset(880,680),GroupTransparency=1,
    BackgroundColor3=bg,BorderSizePixel=0,ZIndex=5},gui)
round(root,20)
local halo=make("Frame",{Name="ChromeHalo",BackgroundTransparency=1,AnchorPoint=Vector2.new(.5,.5),
    Size=UDim2.fromOffset(836,626),Position=root.Position,ZIndex=4,BorderSizePixel=0},gui);round(halo,25)
local haloEdge=outline(halo,.92);haloEdge.Thickness=8
local scale=make("UIScale",{Scale=1},root)
local reveal=make("NumberValue",{Name="Reveal",Value=.93},gui)
local border=outline(root,.32)
make("UIGradient",{Color=ColorSequence.new({
    ColorSequenceKeypoint.new(0,Color3.fromRGB(88,89,99)),
    ColorSequenceKeypoint.new(.28,Color3.fromRGB(245,245,250)),
    ColorSequenceKeypoint.new(.55,Color3.fromRGB(76,77,88)),
    ColorSequenceKeypoint.new(1,Color3.fromRGB(197,200,211))}),Rotation=25},border)
local assetCache={}
local function backgroundAsset(entry)
    if assetCache[entry.name] then return assetCache[entry.name] end
    local providers={getcustomasset,getsynasset,get_custom_asset}
    for i=1,3 do
        local provider=providers[i]
        if type(provider)=="function" then
            local ok,result=pcall(provider,"NOVA/"..entry.file)
            if ok and type(result)=="string" and result~="" then
                assetCache[entry.name]=result
                return result
            end
        end
    end
    gui:SetAttribute("CompatibilityNote","Custom image API or installed image unavailable. Matching colors remain enabled.")
    return ""
end
local initialBackground=backgroundList[backgroundIndex[C.Background]]
local image=backgroundAsset(initialBackground)
local liquid=make("ImageLabel",{Name="LiquidMetal",BackgroundTransparency=1,Image=image,
    ScaleType=Enum.ScaleType.Crop,ImageTransparency=.12,Position=UDim2.fromScale(-.04,-.035),
    Size=UDim2.new(1.08,0,0,280)},root)
local liquidSwap=make("ImageLabel",{Name="BackgroundBlend",BackgroundTransparency=1,Image=image,
    ScaleType=initialBackground.stretch and Enum.ScaleType.Stretch or Enum.ScaleType.Crop,
    ImageTransparency=1,Position=liquid.Position,Size=liquid.Size},root)
local backgroundMix=make("NumberValue",{Name="BackgroundMix",Value=1},gui)
local activeBackground=C.Background
local mist=make("ImageLabel",{Name="Reflections",BackgroundTransparency=1,Image=image,
    ImageTransparency=.91,Rotation=180,ScaleType=Enum.ScaleType.Crop,
    Position=UDim2.fromScale(-.03,.26),Size=UDim2.fromScale(1.08,.8)},root)
local veil=make("Frame",{Name="Vignette",BackgroundColor3=bg,BorderSizePixel=0,Size=UDim2.fromScale(1,1)},root)
make("UIGradient",{Rotation=90,Transparency=NumberSequence.new({
    NumberSequenceKeypoint.new(0,.68),NumberSequenceKeypoint.new(.17,.62),
    NumberSequenceKeypoint.new(.38,.04),NumberSequenceKeypoint.new(1,.02)})},veil)
local surface=make("Frame",{Name="GlassSweep",BackgroundColor3=ink,BackgroundTransparency=.94,
    BorderSizePixel=0,Size=UDim2.fromScale(1,1)},root)
local surfaceLight=make("UIGradient",{Rotation=24,Offset=Vector2.new(-1,0),Transparency=NumberSequence.new({
    NumberSequenceKeypoint.new(0,1),NumberSequenceKeypoint.new(.42,1),NumberSequenceKeypoint.new(.5,.15),
    NumberSequenceKeypoint.new(.58,1),NumberSequenceKeypoint.new(1,1)})},surface)
local topLine=make("Frame",{BackgroundColor3=ink,BorderSizePixel=0,Position=UDim2.fromOffset(22,0),
    Size=UDim2.new(1,-44,0,1)},root)
local shine=make("UIGradient",{Transparency=NumberSequence.new({
    NumberSequenceKeypoint.new(0,1),NumberSequenceKeypoint.new(.4,1),
    NumberSequenceKeypoint.new(.5,.05),NumberSequenceKeypoint.new(.6,1),NumberSequenceKeypoint.new(1,1)})},topLine)
local brand=text(root,"NOVA",35,ink,UDim2.fromOffset(25,27),UDim2.fromOffset(260,48))
brand.Font=Enum.Font.GothamBold
local chrome=make("UIGradient",{Rotation=75,Color=ColorSequence.new({
    ColorSequenceKeypoint.new(0,Color3.fromRGB(255,255,255)),
    ColorSequenceKeypoint.new(.45,Color3.fromRGB(182,185,195)),
    ColorSequenceKeypoint.new(.54,Color3.fromRGB(250,250,255)),
    ColorSequenceKeypoint.new(1,Color3.fromRGB(137,140,153))})},brand)
text(root,"S H O O T E R   E D I T I O N    /    3 . 3 8",9,Color3.fromRGB(188,191,201),
    UDim2.fromOffset(28,78),UDim2.fromOffset(360,22))
local liveDot=make("Frame",{BackgroundColor3=ink,BorderSizePixel=0,
    Position=UDim2.fromOffset(28,116),Size=UDim2.fromOffset(5,5)},root);round(liveDot,3)
local deviceBadge=text(root,deviceName.." CONNECTED",9,muted,UDim2.fromOffset(41,108),UDim2.fromOffset(190,20))
local header=make("Frame",{Name="Drag",BackgroundTransparency=1,Active=true,
    Size=UDim2.new(1,-300,0,135)},root)
local cursor=button(root,"Cursor","",UDim2.new(1,-270,0,28),UDim2.fromOffset(158,32))
local minimize=button(root,"Minimize","—",UDim2.new(1,-100,0,28),UDim2.fromOffset(32,32))
local unload=button(root,"Unload","×",UDim2.new(1,-56,0,28),UDim2.fromOffset(32,32))
local heroTag=text(root,C.Background:upper(),10,Color3.fromRGB(177,180,193),
    UDim2.new(1,-250,0,90),UDim2.fromOffset(225,24));heroTag.TextXAlignment=Enum.TextXAlignment.Right
make("Frame",{BackgroundColor3=ink,BackgroundTransparency=.9,BorderSizePixel=0,
    Position=UDim2.fromOffset(24,143),Size=UDim2.new(1,-48,0,1)},root)
local side=make("ScrollingFrame",{Name="Navigation",BackgroundTransparency=1,BorderSizePixel=0,CanvasSize=UDim2.fromOffset(0,#tabNames*54+40),ScrollBarThickness=2,ScrollingEnabled=true,ScrollingDirection=Enum.ScrollingDirection.Y,
    Position=UDim2.fromOffset(20,164),Size=UDim2.new(0,171,1,-224)},root)
text(side,"CONTROL ROOM",9,muted,UDim2.fromOffset(10,0),UDim2.fromOffset(150,20))
local rail=make("Frame",{BackgroundColor3=ink,BackgroundTransparency=.93,
    Position=UDim2.fromOffset(191,166),Size=UDim2.new(0,1,1,-222),BorderSizePixel=0},root)
local pages,tabs={},{}
local ui={}
function ui.pointerBlocked(position)
    for _,object in ipairs(pg:GetGuiObjectsAtPosition(position.X,position.Y)) do
        if object:IsDescendantOf(gui) then
            local node=object
            while node and node~=gui do
                if node:IsA("GuiButton") or node:IsA("TextBox") then return true end
                node=node.Parent
            end
        end
    end
    return false
end
local activeTab="Aim"
local indicator=make("Frame",{Name="ActiveRail",BackgroundColor3=ink,BorderSizePixel=0,
    Position=UDim2.fromOffset(1,44),Size=UDim2.fromOffset(2,20)},side);round(indicator,1)
local tabIndex={};for i,name in ipairs(tabNames) do tabIndex[name]=i end
local uiPagePosition=UDim2.fromOffset(214,208)
local compact=false
ui.searchBar=make("Frame",{Name="SearchBar",BackgroundColor3=Color3.fromRGB(19,22,29),BackgroundTransparency=.08,BorderSizePixel=0,
    Position=UDim2.fromOffset(214,157),Size=UDim2.new(1,-238,0,38)},root);round(ui.searchBar,11);outline(ui.searchBar,.7)
text(ui.searchBar,"⌕",20,muted,UDim2.fromOffset(12,0),UDim2.fromOffset(24,38))
ui.search=make("TextBox",{Name="SearchControls",BackgroundTransparency=1,Text="",ClearTextOnFocus=false,
    PlaceholderText="Search this tab...",PlaceholderColor3=muted,TextColor3=ink,Font=Enum.Font.Gotham,
    TextSize=12,TextXAlignment=Enum.TextXAlignment.Left,Position=UDim2.fromOffset(40,0),Size=UDim2.new(1,-162,1,0)},ui.searchBar)
ui.searchCount=text(ui.searchBar,"",10,muted,UDim2.new(1,-112,0,0),UDim2.fromOffset(70,38));ui.searchCount.TextXAlignment=Enum.TextXAlignment.Right
ui.clear=button(ui.searchBar,"ClearSearch","×",UDim2.new(1,-34,0,5),UDim2.fromOffset(28,28));ui.clear.Visible=false
ui.empty=text(root,"No matching controls.\nTry another word or clear the search.",13,muted,UDim2.fromOffset(230,235),UDim2.new(1,-270,0,66))
ui.empty.Name="SearchEmpty";ui.empty.Visible=false;ui.empty.TextWrapped=true
function ui.applyFilter()
    local page=pages[activeTab]
    if not page then return end
    local query=ui.search.Text:lower():match("^%s*(.-)%s*$")
    local terms={};for word in query:gmatch("%S+") do terms[#terms+1]=word end
    local count=0
    for _,item in ipairs(page:GetChildren()) do
        if item:IsA("GuiObject") then
            if item.Name=="Heading" then item.Visible=query=="" and (not mobile or overlay.AbsoluteSize.Y>460)
            else
                local content=item.Name
                if item:IsA("TextButton") then content..=" "..item.Text end
                for _,label in ipairs(item:GetDescendants()) do
                    if label:IsA("TextLabel") then content..=" "..label.Text end
                end
                content=content:lower()
                local match=item:GetAttribute("SearchBaseVisible")~=false
                for _,term in ipairs(terms) do if not content:find(term,1,true) then match=false;break end end
                item.Visible=match
                if match then count+=1 end
            end
        end
    end
    ui.searchCount.Text=tostring(count)..(query=="" and " controls" or " found")
    ui.clear.Visible=query~="";ui.empty.Visible=query~="" and count==0
    gui:SetAttribute("SearchResults",count);gui:SetAttribute("SearchQuery",ui.search.Text)
end
function ui.layoutSearch()
    ui.searchBar.Position=mobile and UDim2.fromOffset(14,140) or UDim2.fromOffset(214,157)
    ui.searchBar.Size=mobile and UDim2.new(1,-28,0,36) or UDim2.new(1,-238,0,38)
    ui.empty.Position=mobile and UDim2.fromOffset(20,210) or UDim2.fromOffset(230,235)
    ui.empty.Size=mobile and UDim2.new(1,-40,0,66) or UDim2.new(1,-270,0,66)
end
on(ui.search:GetPropertyChangedSignal("Text"),function()if pages[activeTab] then pages[activeTab].CanvasPosition=Vector2.zero end;ui.applyFilter()end)
on(ui.clear.Activated,function()ui.search.Text="";ui.search:ReleaseFocus();ui.applyFilter()end)
local function selectTab(name)
    activeTab=name
    ui.search.Text="";ui.search.PlaceholderText="Search "..name:lower().." controls..."
    heroTag.Text=name:upper().." / "..C.Background:upper()
    for n,p in pairs(pages) do
        p.Visible=n==name
        tabs[n].TextColor3=n==name and ink or muted
        tabs[n]:SetAttribute("RestAlpha",n==name and .08 or 1)
        tabs[n]:SetAttribute("RestColor",n==name and Color3.fromRGB(46,47,55) or card)
        animate(tabs[n],{BackgroundTransparency=n==name and .08 or 1,
            BackgroundColor3=n==name and Color3.fromRGB(46,47,55) or card},.24)
        for _,label in ipairs(tabs[n]:GetChildren()) do
            if label:IsA("TextLabel") then label:SetAttribute("ThemeRole",n==name and "Primary" or "Muted");animate(label,{TextColor3=n==name and ink or muted},.2) end
        end
    end
    if compact then
        animate(indicator,{Position=UDim2.fromOffset((tabIndex[name]-1)*112+6,43),Size=UDim2.fromOffset(100,2)},.3)
        side.CanvasPosition=Vector2.new(math.clamp((tabIndex[name]-1)*112-12,0,math.max(0,#tabNames*112-side.AbsoluteSize.X)),0)
    else
        side.CanvasPosition=Vector2.new(0,math.clamp(32+(tabIndex[name]-1)*54-80,0,math.max(0,#tabNames*54+40-side.AbsoluteSize.Y)))
        animate(indicator,{Position=UDim2.fromOffset(1,44+(tabIndex[name]-1)*54),Size=UDim2.fromOffset(2,20)},.3)
    end
    gui:SetAttribute("ActiveTab",name)
    local page=pages[name]
    local index=0
    for _,item in ipairs(page:GetChildren()) do
        if item:IsA("GuiObject") then
            local rowScale=item:FindFirstChild("RowReveal")
            if rowScale then rowScale.Scale=C.Motion and .978 or 1 end
            local visible=item.AbsolutePosition.Y+item.AbsoluteSize.Y>=page.AbsolutePosition.Y and item.AbsolutePosition.Y<page.AbsolutePosition.Y+page.AbsoluteSize.Y
            if visible and C.Motion then
                index+=1;local delayTime=math.min(index-1,5)*.028
                for _,label in ipairs(item:GetDescendants()) do if label:IsA("TextLabel") then label.TextTransparency=1 end end
                task.delay(motionDuration(delayTime),function()
                    if not alive or activeTab~=name or not item.Parent then return end
                    if rowScale then animate(rowScale,{Scale=1},.36,Enum.EasingStyle.Back) end
                    for _,label in ipairs(item:GetDescendants()) do if label:IsA("TextLabel") then animate(label,{TextTransparency=0},.3) end end
                end)
            else
                if rowScale then rowScale.Scale=1 end
                for _,label in ipairs(item:GetDescendants()) do if label:IsA("TextLabel") then label.TextTransparency=0 end end
            end
        end
    end
    page.Position=UDim2.new(uiPagePosition.X.Scale,uiPagePosition.X.Offset+12,uiPagePosition.Y.Scale,uiPagePosition.Y.Offset)
    animate(page,{Position=uiPagePosition},.4)
    ui.applyFilter()
end
for i,name in ipairs(tabNames) do
    local glyph=({"◈","◎","▣","◈","⌖","✦","›","◉","☼","◇","⌘","⚙"})[i]
    local tab=button(side,name,"",UDim2.fromOffset(8,32+(i-1)*54),UDim2.fromOffset(151,44))
    tabs[name]=tab
    local icon=text(tab,glyph,20,ink,UDim2.fromOffset(12,0),UDim2.fromOffset(28,44))
    text(tab,name:upper(),10,ink,UDim2.fromOffset(44,0),UDim2.fromOffset(100,44))
    tab.BackgroundTransparency=name==activeTab and .15 or 1
    local page=make("ScrollingFrame",{Name=name,BackgroundTransparency=1,BorderSizePixel=0,
        Position=uiPagePosition,Size=UDim2.new(1,-238,1,-270),
        ScrollBarThickness=2,ScrollBarImageColor3=Color3.fromRGB(160,164,178),
        ScrollBarImageTransparency=.5,CanvasSize=UDim2.new(),AutomaticCanvasSize=Enum.AutomaticSize.Y,
        ScrollingDirection=Enum.ScrollingDirection.Y,Visible=name==activeTab},root)
    make("UIListLayout",{Padding=UDim.new(0,8),SortOrder=Enum.SortOrder.LayoutOrder},page)
    make("UIPadding",{PaddingRight=UDim.new(0,9),PaddingBottom=UDim.new(0,12)},page)
    pages[name]=page
    on(tab.Activated,function() selectTab(name) end)
end
local keybox=make("Frame",{BackgroundColor3=Color3.fromRGB(18,19,23),BackgroundTransparency=.15,
    BorderSizePixel=0,Position=UDim2.fromOffset(8,239),Size=UDim2.fromOffset(151,132)},side);round(keybox,10);outline(keybox,.9)
keybox.Visible=false
text(keybox,"QUICK KEYS",9,muted,UDim2.fromOffset(12,10),UDim2.fromOffset(130,20))
for i,pair in ipairs({{"RMB","Aim"},{"U","Free cursor"},{"RSHIFT","Menu"}}) do
    text(keybox,pair[1],9,ink,UDim2.fromOffset(12,39+(i-1)*27),UDim2.fromOffset(56,18))
    text(keybox,pair[2],10,muted,UDim2.fromOffset(69,39+(i-1)*27),UDim2.fromOffset(80,18))
end
make("Frame",{BackgroundColor3=ink,BackgroundTransparency=.92,BorderSizePixel=0,
    Position=UDim2.new(0,24,1,-46),Size=UDim2.new(1,-48,0,1)},root)
local status=text(root,"Initializing...",10,muted,UDim2.new(0,37,1,-34),UDim2.new(1,-164,0,21))
local statusDot=make("Frame",{BackgroundColor3=ink,BorderSizePixel=0,
    Position=UDim2.new(0,25,1,-26),Size=UDim2.fromOffset(4,4)},root);round(statusDot,3)
text(root,"N / 03",10,muted,UDim2.new(1,-74,1,-34),UDim2.fromOffset(50,21))
local mini=button(gui,"Open","N O V A   /   OPEN",UDim2.fromOffset(20,20),UDim2.fromOffset(152,36))
mini.Visible=false
local floating=button(gui,"FreeMouse","",UDim2.fromOffset(183,20),UDim2.fromOffset(198,36))
local touchAim=button(gui,"TouchAim","HOLD AIM",UDim2.new(1,-98,1,-190),UDim2.fromOffset(76,76))
round(touchAim,38)
touchAim.Visible=mobile and not menuOpen and C.ShowAimPad
local function releaseTouch()
    aimTouch=nil;touchHolding=false;latch=false
    gui:SetAttribute("TouchHolding",false)
end
local function updateAimPad()
    touchAim.Visible=mobile and not menuOpen and C.ShowAimPad
    if mobile then touchAim.Size=UDim2.fromOffset(C.AimPadSize,C.AimPadSize);touchAim.AnchorPoint=Vector2.new(1,1) end
    touchAim.Text=not C.Aim and "AIM\nOFF" or C.Mode=="Toggle AIM" and (latch and "AIM\nON" or "TAP\nAIM")
        or C.Mode=="Always" and "AUTO\nAIM" or "HOLD\nAIM"
    touchAim.TextColor3=(touchHolding or latch) and Color3.fromRGB(140,235,180) or ink
end
on(touchAim.InputBegan,function(i)
    if mobile and C.Aim and not menuOpen and i.UserInputType==Enum.UserInputType.Touch and not aimTouch then
        aimTouch=i;touchHolding=true;gui:SetAttribute("TouchHolding",true);updateAimPad()
    end
end)
on(touchAim.InputEnded,function(i)
    if i==aimTouch then aimTouch=nil;touchHolding=false;gui:SetAttribute("TouchHolding",false);updateAimPad() end
end)
on(touchAim.Activated,function()
    if mobile and C.Mode=="Toggle AIM" and C.Aim and not menuOpen then latch=not latch;updateAimPad() end
end)
local function refresh()
    if ui.enforceCapabilities then ui.enforceCapabilities() end
    if switchBackground then switchBackground(C.Background) end
    for k,v in pairs(C) do gui:SetAttribute(k,v) end
    gui:SetAttribute("CursorFree",free)
    cursor.Text=mobile and "CLOSE MENU" or (free and "◉  CURSOR FREE  /  U" or "◎  UNLOCK MOUSE  /  U")
    floating.Text=free and "◉  MOUSE FREE  /  U TO LOCK" or "◎  UNLOCK MOUSE  /  U"
    cursor.Modal=not mobile and free and root.Visible;floating.Modal=not mobile and free
    floating.Visible=not mobile and C.QuickCursor
    updateAimPad()
    for _,fn in ipairs(paints) do fn() end
    status.Visible=C.ShowStatus;statusDot.Visible=C.ShowStatus
    if applyWorld then applyWorld() end
end
local toast=make("CanvasGroup",{Name="SettingToast",AnchorPoint=Vector2.new(.5,1),
    Position=UDim2.new(.5,0,1,-36),Size=UDim2.fromOffset(290,70),GroupTransparency=1,
    BackgroundColor3=Color3.fromRGB(23,24,30),BorderSizePixel=0,Visible=false,ZIndex=30},gui)
round(toast,13);outline(toast,.52)
local toastTitle=text(toast,"",13,ink,UDim2.fromOffset(16,10),UDim2.new(1,-32,0,23))
local toastValue=text(toast,"",11,muted,UDim2.fromOffset(16,34),UDim2.new(1,-32,0,20))
local toastLine=make("Frame",{BackgroundColor3=ink,BorderSizePixel=0,
    Position=UDim2.new(0,0,1,-2),Size=UDim2.new(1,0,0,2)},toast)
local toastSerial=0
notify=function(title,value)
    toastSerial+=1;local serial=toastSerial
    if not C.Notifications then toast.Visible=false;return end
    toastTitle.Text=title;toastValue.Text=value
    toast.Visible=true;toast.GroupTransparency=1;toast.Position=UDim2.new(.5,0,1,-16)
    toastLine.Size=UDim2.new(1,0,0,2)
    animate(toast,{GroupTransparency=0,Position=UDim2.new(.5,0,1,-36)},.28)
    animate(toastLine,{Size=UDim2.new(0,0,0,2)},1.6)
    task.delay(math.max(1,motionDuration(1.6)),function()
        if not alive or serial~=toastSerial then return end
        animate(toast,{GroupTransparency=1,Position=UDim2.new(.5,0,1,-20)},.24)
        task.delay(motionDuration(.26),function() if alive and serial==toastSerial then toast.Visible=false end end)
    end)
end
local function setFree(value)
    free=value;latch=false;selected=nil
    if not mobile then
        UIS.MouseBehavior=free and Enum.MouseBehavior.Default or Enum.MouseBehavior.LockCenter
        UIS.MouseIconEnabled=free or lastIcon
    end
    refresh()
end
local transition=0
local function showMenu(value)
    if value and C.EditorOpen then C.EditorOpen=false;local editor=gui:FindFirstChild("ScriptStudio");if editor then editor.Visible=false end end
    transition+=1;local current=transition
    menuOpen=value;gui:SetAttribute("MenuOpen",value);releaseTouch()
    mini.Visible=mobile or not value;setFree(value)
    if mobile then mini.Text=value and "CLOSE MENU" or "NOVA  /  MENU" end
    if value then
        root.Visible=true;root.GroupTransparency=1;reveal.Value=.94
        animate(root,{GroupTransparency=0},.34);animate(reveal,{Value=1},.5,Enum.EasingStyle.Back)
    else
        animate(root,{GroupTransparency=1},.2);animate(reveal,{Value=.95},.2)
        task.delay(motionDuration(.22),function() if alive and transition==current then root.Visible=false end end)
    end
end
on(cursor.Activated,function() if mobile then showMenu(false) else setFree(not free) end end)
on(floating.Activated,function() setFree(not free) end)
on(minimize.Activated,function() showMenu(false) end)
on(mini.Activated,function() if mobile then showMenu(not menuOpen) else showMenu(true) end end)
local function heading(page,titleValue,subtitle)
    local box=make("Frame",{Name="Heading",Size=UDim2.new(1,0,0,mobile and 66 or 72),BackgroundTransparency=1},page)
    text(box,titleValue,mobile and 20 or 23,ink,UDim2.fromOffset(0,0),UDim2.new(1,0,0,32)).Font=Enum.Font.GothamBold
    local sub=text(box,subtitle,11,muted,UDim2.fromOffset(1,34),UDim2.new(1,-4,0,30));sub.TextWrapped=true;sub.TextTruncate=Enum.TextTruncate.None
    text(box,"/",11,muted,UDim2.new(1,-16,0,0),UDim2.fromOffset(16,28))
end
local function row(page,key,titleValue,detail,height)
    local box=make("Frame",{Name=key.."Row",BackgroundColor3=Color3.fromRGB(22,23,28),BackgroundTransparency=.1,
        BorderSizePixel=0,Size=UDim2.new(1,0,0,mobile and math.max(82,height or 82) or (height or 78))},page);round(box,11)
    local stroke=outline(box,.89)
    local rowScale=make("UIScale",{Name="RowReveal",Scale=1},box)
    local rowTitle=text(box,titleValue,14,ink,UDim2.fromOffset(16,12),UDim2.new(1,-166,0,20));rowTitle.Name="RowTitle"
    local rowDetail=text(box,detail,11,muted,UDim2.fromOffset(16,mobile and 48 or 38),UDim2.new(1,-32,0,28));rowDetail.Name="Detail";rowDetail.TextWrapped=true;rowDetail.TextYAlignment=Enum.TextYAlignment.Top;rowDetail.TextTruncate=Enum.TextTruncate.None
    on(box.MouseEnter,function() animate(stroke,{Transparency=.56},.2);animate(rowScale,{Scale=1.004},.24) end)
    on(box.MouseLeave,function() animate(stroke,{Transparency=.89},.3);animate(rowScale,{Scale=1},.3) end)
    return box
end
local function toggle(page,key,titleValue,detail)
    C[key]=false -- Every toggle is opt-in on each fresh execution.
    local box=row(page,key,titleValue,detail)
    local b=button(box,key,"",UDim2.new(1,mobile and -78 or -64,0,mobile and 3 or 13),UDim2.fromOffset(mobile and 62 or 46,mobile and 44 or 24))
    local knob=make("Frame",{Name="Knob",BackgroundColor3=ink,BorderSizePixel=0,
        Position=UDim2.fromOffset(4,mobile and 10 or 4),Size=UDim2.fromOffset(mobile and 24 or 16,mobile and 24 or 16)},b);round(knob,12)
    box.RowTitle.Size=UDim2.new(1,-146,0,22)
    local stateText=text(box,"OFF",9,muted,UDim2.new(1,mobile and -115 or -101,0,16),UDim2.fromOffset(30,18));stateText.Name="ToggleState"
    local last
    local function paint()
        if last==C[key] then return end;last=C[key]
        stateText.Text=C[key] and "ON" or "OFF";stateText.TextColor3=C[key] and ink or muted
        local edge=box:FindFirstChildOfClass("UIStroke");if edge then edge.Color=C[key] and ink or muted;edge.Transparency=C[key] and .63 or .88 end
        animate(knob,{Position=UDim2.fromOffset(C[key] and (mobile and 34 or 26) or 4,mobile and 10 or 4),
            BackgroundColor3=C[key] and ink or Color3.fromRGB(100,103,116)},.34,Enum.EasingStyle.Back)
        b:SetAttribute("RestColor",C[key] and Color3.fromRGB(76,79,91) or card)
        animate(b,{BackgroundColor3=b:GetAttribute("RestColor")},.24)
    end
    table.insert(paints,paint)
    on(b.Activated,function() if ui.canToggle and not ui.canToggle(key) then return end;C[key]=not C[key];selected=nil;refresh();if notify then notify(titleValue,C[key] and "Enabled" or "Disabled") end end)
end
local function cycle(page,key,titleValue,detail,values)
    local box=row(page,key,titleValue,detail)
    local b=button(box,key,"",UDim2.new(1,-160,0,mobile and 0 or 9),UDim2.fromOffset(142,mobile and 44 or 32))
    table.insert(paints,function() b.Text=tostring(C[key]).."  ›";b.TextColor3=ink end)
    on(b.Activated,function()
        local at=table.find(values,C[key]) or 0;C[key]=values[at % #values+1];selected=nil;refresh();if notify then notify(titleValue,tostring(C[key])) end
    end)
end
local function slider(page,key,titleValue,detail,lo,hi,step)
    local box=row(page,key,titleValue,detail,95)
    if mobile then box.Detail.Position=UDim2.fromOffset(16,34);box.Detail.Size=UDim2.new(1,-32,0,26) end
    local valueLabel=text(box,"",12,ink,UDim2.new(1,-90,0,12),UDim2.fromOffset(71,20))
    valueLabel.TextXAlignment=Enum.TextXAlignment.Right
    local displayed=make("NumberValue",{Value=C[key]},box)
    valueLabel.Text=tostring(C[key])
    on(displayed.Changed,function(value) valueLabel.Text=step<1 and string.format("%.1f",value) or tostring(math.floor(value+.5)) end)
    local track=make("TextButton",{Name=key,Text="",AutoButtonColor=false,BackgroundColor3=Color3.fromRGB(51,53,63),
        BorderSizePixel=0,BackgroundTransparency=1,Position=UDim2.new(0,17,0,mobile and 52 or 60),Size=UDim2.new(1,-34,0,mobile and 44 or 32)},box)
    local trackLine=make("Frame",{BackgroundColor3=Color3.fromRGB(51,53,63),BorderSizePixel=0,
        AnchorPoint=Vector2.new(0,.5),Position=UDim2.fromScale(0,.5),Size=UDim2.new(1,0,0,5)},track);round(trackLine,3)
    local fill=make("Frame",{BackgroundColor3=ink,BorderSizePixel=0,AnchorPoint=Vector2.new(0,.5),Position=UDim2.fromScale(0,.5),Size=UDim2.new(0,0,0,5)},track);round(fill,3)
    make("UIGradient",{Color=ColorSequence.new(Color3.fromRGB(111,114,128),ink)},fill)
    local knob=make("Frame",{Name="Knob",AnchorPoint=Vector2.new(.5,.5),Size=UDim2.fromOffset(mobile and 20 or 12,mobile and 20 or 12),
        BackgroundColor3=ink,BorderSizePixel=0,Position=UDim2.fromScale(0,.5)},track);round(knob,6)
    outline(knob,.4)
    local last
    table.insert(paints,function()
        if last==C[key] then return end;last=C[key]
        local a=(C[key]-lo)/(hi-lo)
        animate(fill,{Size=UDim2.new(a,0,0,5)},.18);animate(knob,{Position=UDim2.fromScale(a,.5)},.18)
        animate(displayed,{Value=C[key]},.2)
    end)
    local function update(x)
        local a=math.clamp((x-track.AbsolutePosition.X)/math.max(1,track.AbsoluteSize.X),0,1)
        C[key]=math.clamp(math.floor((lo+a*(hi-lo))/step+.5)*step,lo,hi);refresh()
    end
    on(track.InputEnded,function(i)
        if sliding and sliding.input==i then
            sliding=nil;for _,p in pairs(pages) do p.ScrollingEnabled=true end
        end
    end)
    on(track.InputBegan,function(i) if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then
        sliding={update=update,input=i};page.ScrollingEnabled=false;update(i.Position.X)
    end end)
end
-- Travel slots are scoped to this place; disk persistence is opt-in.
local travel={spots={},previous=nil,marker=nil,folder="nova-spots",loaded=false}
pcall(function()
    local stash=saved.TravelSpots
    if type(stash)=="string" then travel.spots=Http:JSONDecode(stash) end
end)
travel.path=travel.folder.."/"..tostring(game.PlaceId)..".json"
function travel.character()
    local ch=LP.Character
    local hrp=ch and ch:FindFirstChild("HumanoidRootPart")
    local hum=ch and ch:FindFirstChildOfClass("Humanoid")
    if not hrp or not hum or hum.Health<=0 then return nil end
    return ch,hrp,hum
end
function travel.persist()
    if not C.TravelRemember then return end
    local ok,err=pcall(function()
        assert(type(writefile)=="function" and type(makefolder)=="function","File saving unavailable")
        if type(isfolder)~="function" or not isfolder(travel.folder) then pcall(makefolder,travel.folder) end
        writefile(travel.path,Http:JSONEncode(travel.spots))
    end)
    if not ok and notify then notify("Spot storage",tostring(err)) end
end
function travel.load()
    if travel.loaded or not C.TravelRemember then return end
    travel.loaded=true
    pcall(function()
        if type(readfile)~="function" then return end
        local parsed=Http:JSONDecode(readfile(travel.path))
        if type(parsed)~="table" then return end
        for i=1,8 do
            local value=parsed[tostring(i)]
            local valid=type(value)=="table" and #value==12
            if valid then for _,n in ipairs(value) do if type(n)~="number" or n~=n or math.abs(n)==math.huge then valid=false;break end end end
            if valid and not travel.spots[tostring(i)] then travel.spots[tostring(i)]=value end
        end
    end)
end
function travel.move(target,exact)
    local ch,hrp=travel.character()
    if not ch then notify("Teleport","Wait for your character to spawn.");return false end
    local destination=exact and target or target+Vector3.new(0,C.TravelHeight,0)+target.LookVector*C.TravelForward
    if not C.TravelFacing then destination=CFrame.new(destination.Position)*hrp.CFrame.Rotation end
    local old=hrp.CFrame
    local ok,err=pcall(function()
        ch:PivotTo(destination*hrp.CFrame:ToObjectSpace(ch:GetPivot()))
        if C.TravelStop then hrp.AssemblyLinearVelocity=Vector3.zero;hrp.AssemblyAngularVelocity=Vector3.zero end
    end)
    if not ok then notify("Teleport",tostring(err));return false end
    travel.previous=old
    notify("Teleport","Moved locally. The game server may adjust your position.")
    return true
end
function travel.action(name,label,callback)
    local b=button(pages.Travel,name,label,UDim2.new(),UDim2.new(1,0,0,48))
    on(b.Activated,callback)
end
-- Live player selector; identities use UserId so display-name changes are safe.
travel.playerRow=row(pages.Travel,"PlayerTarget","Choose player","Tap NEXT to cycle through players in this server.",96)
travel.playerRow.RowTitle.Size=UDim2.new(1,-110,0,24)
travel.playerNext=button(travel.playerRow,"NextPlayer","NEXT",UDim2.new(1,-94,0,8),UDim2.fromOffset(78,36))
function travel.targetPlayer()
    local id=tonumber(C.PlayerTarget)
    if not id then return nil end
    return Players:GetPlayerByUserId(id)
end
function travel.playerStatus()
    local p=travel.targetPlayer()
    local ch=p and p.Character
    local h=ch and ch:FindFirstChildOfClass("Humanoid")
    travel.playerRow.RowTitle.Text=p and p.DisplayName or "Choose player"
    travel.playerRow.Detail.Text=p and ("@"..p.Name.." / "..(h and h.Health>0 and "Ready" or "Waiting for respawn")) or "Tap NEXT to cycle through players in this server."
    gui:SetAttribute("TeleportTargetUserId",p and p.UserId or 0)
end
on(travel.playerNext.Activated,function()
    local list={}
    for _,p in ipairs(Players:GetPlayers()) do if p~=LP then table.insert(list,p) end end
    table.sort(list,function(a,b)return a.Name:lower()<b.Name:lower()end)
    if #list==0 then C.PlayerTarget="";travel.playerStatus();notify("Player travel","No other players are in this server.");return end
    local at=0
    for i,p in ipairs(list) do if tostring(p.UserId)==C.PlayerTarget then at=i;break end end
    C.PlayerTarget=tostring(list[at%#list+1].UserId);travel.playerStatus();refresh()
end)
travel.action("GoToPlayer","GO TO SELECTED PLAYER",function()
    local p=travel.targetPlayer()
    if not p or p==LP then notify("Player travel","Choose a player with NEXT first.");return end
    local ch=p.Character
    local root=ch and ch:FindFirstChild("HumanoidRootPart")
    local h=ch and ch:FindFirstChildOfClass("Humanoid")
    if not root or not h or h.Health<=0 then notify("Player travel","This player has not spawned yet.");return end
    travel.move(root.CFrame*CFrame.new(0,2,C.PlayerStandOff),true)
end)
slider(pages.Travel,"PlayerStandOff","Player arrival distance","Arrive behind the selected player, in studs.",2,25,1)

heading(pages.Travel,"Travel & movement","Save up to eight locations in this place. Options start disabled.")
cycle(pages.Travel,"TravelSlot","Saved spot slot","Select a slot before saving or travelling.",{"1","2","3","4","5","6","7","8"})
local spotInfo=row(pages.Travel,"SpotStatus","Selected location","Empty slot: save your current position first.")
travel.action("SaveTeleportSpot","SAVE TELEPORT SPOT",function()
    travel.load()
    local ch,hrp=travel.character()
    if not ch then notify("Save spot","Wait for your character to spawn.");return end
    travel.spots[C.TravelSlot]={hrp.CFrame:GetComponents()};travel.persist()
    refresh();notify("Spot saved","Slot "..C.TravelSlot.." updated.")
end)
travel.action("GoToTeleportSpot","GO TO TELEPORT SPOT",function()
    travel.load();local spot=travel.spots[C.TravelSlot]
    if not spot then notify("Empty slot","Save a spot in slot "..C.TravelSlot.." first.");return end
    travel.move(CFrame.new(table.unpack(spot)))
end)
travel.action("ReturnPreviousSpot","RETURN TO PREVIOUS POSITION",function()
    if not travel.previous then notify("Return","Use Go to teleport spot first.");return end
    travel.move(travel.previous,true)
end)
travel.action("ClearTeleportSpot","CLEAR SELECTED SPOT",function()
    travel.spots[C.TravelSlot]=nil;travel.persist();refresh();notify("Spot cleared","Slot "..C.TravelSlot)
end)
toggle(pages.Travel,"TravelRemember","Remember saved spots","Opt in to saving these eight slots to a local file per place.")
toggle(pages.Travel,"TravelMarker","Saved spot marker","Show a local neon marker at the selected saved location.")
toggle(pages.Travel,"TravelFacing","Use saved direction","Face the same direction as when you saved the spot.")
toggle(pages.Travel,"TravelStop","Stop teleport momentum","Clear linear and angular velocity after moving.")
slider(pages.Travel,"TravelHeight","Arrival height offset","Extra vertical distance above the saved location.",0,30,1)
slider(pages.Travel,"TravelForward","Arrival forward offset","Move along the saved facing direction in studs.",-20,20,1)
heading(pages.Travel,"Movement controls","Local overrides can be limited by the game server.")
toggle(pages.Travel,"SpeedOverride","Walk speed override","Enable the walk speed slider below.")
slider(pages.Travel,"WalkSpeed","Walk speed","Character walking speed in studs per second.",0,150,1)
toggle(pages.Travel,"JumpOverride","Jump override","Uses the jump mode selected by the game.")
slider(pages.Travel,"JumpPower","Jump power","Used when the character uses JumpPower.",0,150,5)
slider(pages.Travel,"JumpHeight","Jump height","Used when the character uses JumpHeight.",0,50,1)
toggle(pages.Travel,"GravityOverride","Gravity override","Adjust local gravity. Restore by disabling.")
slider(pages.Travel,"Gravity","Gravity","Lower values produce slower falls.",0,300,5)
toggle(pages.Travel,"AutoRotateOverride","Rotation override","Enable the auto-rotate option below.")
toggle(pages.Travel,"AutoRotate","Auto rotate character","Turn the character toward movement when override is enabled.")
heading(pages.Travel,"Camera controls","Zoom limits and camera offsets restore when disabled.")
cycle(pages.Travel,"ViewMode","Camera perspective","Use the game default or force a camera perspective.",{"Game default","First person","Third person"})
toggle(pages.Travel,"ZoomOverride","Zoom distance override","Apply minimum and maximum camera zoom distances.")
slider(pages.Travel,"MinZoom","Minimum zoom","Maximum zoom is clamped above this distance.",0.5,30,0.5)
slider(pages.Travel,"MaxZoom","Maximum zoom","Maximum camera distance in studs.",1,500,1)
toggle(pages.Travel,"CameraOffsetOverride","Camera offset override","Adjust character camera height and shoulder offset.")
slider(pages.Travel,"CameraHeight","Camera height offset","Vertical offset from the normal camera position.",-5,15,0.5)
slider(pages.Travel,"CameraSide","Camera shoulder offset","Horizontal camera offset in studs.",-10,10,0.5)
travel.action("ResetTravelOverrides","RESTORE MOVEMENT & CAMERA",function()
    for _,k in ipairs({"SpeedOverride","JumpOverride","GravityOverride","AutoRotateOverride","ZoomOverride","CameraOffsetOverride"}) do C[k]=false end
    C.ViewMode="Game default";C.TravelMarker=false;refresh()
end)
function travel.update(own)
    travel.playerStatus()
    travel.load()
    gui:SetAttribute("TravelSpots",Http:JSONEncode(travel.spots))
    local spot=travel.spots[C.TravelSlot]
    spotInfo.Detail.Text=spot and string.format("Slot %s: X %.1f / Y %.1f / Z %.1f",C.TravelSlot,spot[1],spot[2],spot[3]) or "Empty slot: save your current position first."
    gui:SetAttribute("TravelSlot",C.TravelSlot);gui:SetAttribute("TravelSpotSaved",spot~=nil)
    if C.TravelMarker and spot then
        if not travel.marker then
            travel.marker=make("Part",{Name="NovaSavedSpot",Anchored=true,CanCollide=false,CanTouch=false,CanQuery=false,
                Material=Enum.Material.Neon,Shape=Enum.PartType.Ball,Size=Vector3.new(1,1,1),Transparency=.35},workspace)
        end
        travel.marker.CFrame=CFrame.new(spot[1],spot[2],spot[3]);travel.marker.Color=ink
    elseif travel.marker then travel.marker:Destroy();travel.marker=nil end
    local ch,hrp,hum=travel.character()
    if hum then
        if C.SpeedOverride then own(hum,"WalkSpeed",C.WalkSpeed) end
        if C.JumpOverride then own(hum,hum.UseJumpPower and "JumpPower" or "JumpHeight",hum.UseJumpPower and C.JumpPower or C.JumpHeight) end
        if C.AutoRotateOverride then own(hum,"AutoRotate",C.AutoRotate) end
        if C.CameraOffsetOverride then own(hum,"CameraOffset",Vector3.new(C.CameraSide,C.CameraHeight,0)) end
    end
    if C.GravityOverride then own(workspace,"Gravity",C.Gravity) end
    if C.ZoomOverride then
        if LP.CameraMinZoomDistance>math.max(C.MinZoom,C.MaxZoom) then own(LP,"CameraMinZoomDistance",C.MinZoom) end
        own(LP,"CameraMaxZoomDistance",math.max(C.MinZoom,C.MaxZoom));own(LP,"CameraMinZoomDistance",C.MinZoom)
    end
    if C.ViewMode~="Game default" then own(LP,"CameraMode",C.ViewMode=="First person" and Enum.CameraMode.LockFirstPerson or Enum.CameraMode.Classic) end
end

-- Flight and local weapon adapters are isolated from normal ESP updates.
local flight={state=nil,up={},down={}}
function flight.setStatus(value)
    if not alive then return end
    if gui:GetAttribute("FlyStatus")~=value then gui:SetAttribute("FlyStatus",value) end
    if flight.status then flight.status.Detail.Text=value end
end
function flight.stop()
    local state=flight.state
    if not state then return end
    flight.state=nil
    for _,key in ipairs({"velocity","orientation","attachment"}) do if state[key] then pcall(function()state[key]:Destroy()end) end end
    pcall(function()
        if state.hum.PlatformStand then state.hum.PlatformStand=state.platform end
        if not state.hum.AutoRotate then state.hum.AutoRotate=state.rotate end
        if state.root.Parent then state.root.AssemblyLinearVelocity=Vector3.zero;state.root.AssemblyAngularVelocity=Vector3.zero end
    end)
    gui:SetAttribute("FlyActive",false)
end
flight.rise=button(gui,"FlyUp","FLY UP",UDim2.new(0,16,1,-184),UDim2.fromOffset(80,52))
flight.fall=button(gui,"FlyDown","FLY DOWN",UDim2.new(0,16,1,-122),UDim2.fromOffset(80,52))
flight.rise.Visible=false;flight.fall.Visible=false
for _,entry in ipairs({{flight.rise,flight.up},{flight.fall,flight.down}}) do
    on(entry[1].InputBegan,function(input)
        if input.UserInputType==Enum.UserInputType.Touch or input.UserInputType==Enum.UserInputType.MouseButton1 then entry[2][input]=true end
    end)
end
on(UIS.InputEnded,function(input) flight.up[input]=nil;flight.down[input]=nil end)
on(UIS.WindowFocusReleased,function() table.clear(flight.up);table.clear(flight.down) end)
function flight.step(dt,cam)
    flight.rise.Visible=mobile and C.Fly and not menuOpen
    flight.fall.Visible=flight.rise.Visible
    if not C.Fly then flight.stop();flight.waiting=false;flight.setStatus("Disabled");return end
    local ch,hrp,hum=travel.character()
    if not ch then
        flight.stop();flight.setStatus("Armed - waiting for your character to respawn.")
        if not flight.waiting then flight.waiting=true;notify("Fly","Waiting for a living character. Fly will resume after respawn.") end
        return
    end
    flight.waiting=false
    if hrp.Anchored then flight.stop();flight.setStatus("Waiting - the game has anchored your character.");return end
    local state=flight.state
    if state and (state.root~=hrp or not state.velocity.Parent or not state.orientation.Parent) then flight.stop();state=nil end
    if not state then
        state={root=hrp,hum=hum,platform=hum.PlatformStand,rotate=hum.AutoRotate}
        flight.state=state
        local created,createError=pcall(function()
        state.attachment=make("Attachment",{Name="NovaFlightAttachment"},hrp)
        state.velocity=make("LinearVelocity",{Name="NovaFlightVelocity",Attachment0=state.attachment,
            VelocityConstraintMode=Enum.VelocityConstraintMode.Vector,RelativeTo=Enum.ActuatorRelativeTo.World,
            ForceLimitsEnabled=false,VectorVelocity=Vector3.zero},hrp)
        state.orientation=make("AlignOrientation",{Name="NovaFlightOrientation",Attachment0=state.attachment,
            Mode=Enum.OrientationAlignmentMode.OneAttachment,MaxTorque=math.huge,Responsiveness=25,
            CFrame=hrp.CFrame.Rotation},hrp)
        end)
        if not created then flight.stop();C.Fly=false;flight.setStatus("Flight setup failed: "..tostring(createError));refresh();return end
    end
    hum.PlatformStand=true;hum.AutoRotate=false
    local flat=Vector3.new(cam.CFrame.LookVector.X,0,cam.CFrame.LookVector.Z)
    if flat.Magnitude<.001 then flat=Vector3.new(hrp.CFrame.LookVector.X,0,hrp.CFrame.LookVector.Z) end
    if flat.Magnitude<.001 then flat=Vector3.new(0,0,-1) end
    flat=flat.Unit
    local right=Vector3.new(-flat.Z,0,flat.X)
    local move=Vector3.zero
    local vertical,boost=0,1
    if focus and not menuOpen and not UIS:GetFocusedTextBox() then
        if mobile then move=hum.MoveDirection else
            if UIS:IsKeyDown(Enum.KeyCode.W) then move+=flat end
            if UIS:IsKeyDown(Enum.KeyCode.S) then move-=flat end
            if UIS:IsKeyDown(Enum.KeyCode.D) then move+=right end
            if UIS:IsKeyDown(Enum.KeyCode.A) then move-=right end
        end
        if next(flight.up) or UIS:IsKeyDown(Enum.KeyCode.Space) or UIS:IsKeyDown(Enum.KeyCode.E) then vertical+=1 end
        if next(flight.down) or UIS:IsKeyDown(Enum.KeyCode.LeftControl) or UIS:IsKeyDown(Enum.KeyCode.Q) then vertical-=1 end
        if UIS:IsKeyDown(Enum.KeyCode.LeftShift) then boost=C.FlyBoost end
    end
    if move.Magnitude>1 then move=move.Unit end
    state.velocity.VectorVelocity=(move*C.FlySpeed+Vector3.new(0,vertical*C.FlyVerticalSpeed,0))*boost
    state.orientation.CFrame=CFrame.lookAt(Vector3.zero,flat)
    gui:SetAttribute("FlyActive",true)
    flight.setStatus(menuOpen and "Hovering - close the menu to move." or not focus and "Hovering - game window is unfocused." or "Flying / Space or E: up / Ctrl or Q: down")
end
local combat={patches={},newPatch=nil,wasWall=false,module=nil,overlays={}}
combat.displayFolder=make("Folder",{Name="NovaHitboxDisplay_"..uniqueId},workspace)
function combat.drawBox(part,seen)
    if not C.HitboxDisplay then return end
    local visual=combat.overlays[part]
    if not visual then
        visual={}
        visual.fill=make("BoxHandleAdornment",{Name="HitboxFill",Adornee=part,
            AlwaysOnTop=true,ZIndex=4,Transparency=.86,Color3=Color3.fromRGB(40,230,255),
            Size=part.Size},combat.displayFolder)
        visual.edge=make("SelectionBox",{Name="HitboxOutline",Adornee=part,
            Color3=Color3.fromRGB(40,230,255),LineThickness=.045,SurfaceTransparency=1},combat.displayFolder)
        combat.overlays[part]=visual
    end
    visual.fill.Size=part.Size
    seen[part]=true
end
function combat.clearBoxes(seen)
    local count=0
    for part,visual in pairs(combat.overlays) do
        if not seen or not seen[part] or not part.Parent then
            visual.fill:Destroy();visual.edge:Destroy();combat.overlays[part]=nil
        else count+=1 end
    end
    gui:SetAttribute("HitboxDisplays",count)
end
function combat.hitPart(ch)
    local part=ch and ch:FindFirstChild(C.HitboxPart)
    if not part and ch and (C.HitboxPart=="Torso" or C.HitboxPart=="UpperTorso" or C.HitboxPart=="LowerTorso") then
        part=ch:FindFirstChild("UpperTorso") or ch:FindFirstChild("Torso") or ch:FindFirstChild("LowerTorso")
    end
    return part and part:IsA("BasePart") and part or nil
end
combat.targets={};combat.targetStamp=-math.huge;combat.targetCount=0
combat.rayParams=RaycastParams.new()
combat.rayParams.FilterType=Enum.RaycastFilterType.Include;combat.rayParams.IgnoreWater=true
pcall(function()combat.rayParams.CollisionGroup="Raycast"end)
function combat.refreshTargets(force)
    if not force and os.clock()-combat.targetStamp<.12 then return end
    combat.targetStamp=os.clock()
    local parts,seen,count={},{},0
    local function add(ch)
        if not ch or ch==LP.Character or seen[ch] or not ch:IsDescendantOf(workspace) then return end
        seen[ch]=true
        local hum=ch:FindFirstChildOfClass("Humanoid")
        if not hum or hum.Health<=0 then return end
        local found=false
        for _,part in ipairs(ch:GetChildren()) do
            if part:IsA("BasePart") and part.Name~="HumanoidRootPart" then parts[#parts+1]=part;found=true end
        end
        if found then count+=1 end
    end
    for _,p in ipairs(Players:GetPlayers()) do if p~=LP then add(p.Character) end end
    local characters=workspace:FindFirstChild("Characters")
    if characters then for _,ch in ipairs(characters:GetChildren()) do if ch:IsA("Model") then add(ch) end end end
    combat.targets=parts;combat.targetCount=count
    combat.rayParams.FilterDescendantsInstances=parts
    gui:SetAttribute("WallShotsTargets",count)
end
function combat.raycast(self,origin,direction)
    assert(typeof(origin)=="Vector3" and typeof(direction)=="Vector3","Invalid gun ray arguments")
    assert(origin.X==origin.X and origin.Y==origin.Y and origin.Z==origin.Z and direction.X==direction.X and direction.Y==direction.Y and direction.Z==direction.Z,"Invalid gun ray values")
    if direction.Magnitude<.001 then return nil end
    combat.refreshTargets(false)
    if #combat.targets==0 then return nil end
    local settings=type(self)=="table" and rawget(self,"Settings")
    local size=type(settings)=="table" and settings.BulletSize
    if typeof(size)=="Vector3" and size.X>0 and size.Y>0 and size.Z>0 then
        return workspace:Blockcast(CFrame.new(origin),size,direction,combat.rayParams)
    end
    return workspace:Raycast(origin,direction,combat.rayParams)
end
function combat.patchGun(gun)
    if type(gun)~="table" or type(rawget(gun,"Raycast"))~="function" or type(rawget(gun,"AttemptToHit"))~="function" or type(rawget(gun,"Settings"))~="table" then return end
    for _,entry in ipairs(combat.patches) do if entry.gun==gun then return end end
    local original=gun.Raycast
    local wrapper=function(self,origin,direction,...)
        if alive and C.WallShots then
            local ok,result=pcall(combat.raycast,self,origin,direction)
            if ok then
                gui:SetAttribute("WallShotsLastQuery",result and "Local target intersection" or "No local intersection")
                return result
            end
            gui:SetAttribute("WallShotsAdapterError",tostring(result))
            gui:SetAttribute("WallShotsFallbacks",(gui:GetAttribute("WallShotsFallbacks") or 0)+1)
        end
        return original(self,origin,direction,...)
    end
    local ok=pcall(function()gun.Raycast=wrapper end)
    if ok then table.insert(combat.patches,{gun=gun,original=original,wrapper=wrapper}) end
end
function combat.restore()
    for _,entry in ipairs(combat.patches) do
        pcall(function()if entry.gun.Raycast==entry.wrapper then entry.gun.Raycast=entry.original end end)
    end
    combat.patches={}
    local entry=combat.newPatch
    if entry then pcall(function()if entry.module.new==entry.wrapper then entry.module.new=entry.original end end) end
    combat.newPatch=nil;combat.wasWall=false;combat.module=nil
end
function combat.enableWalls()
    combat.wasWall=true
    local ok,err=pcall(function()
        local rs=game:GetService("ReplicatedStorage")
        local client=rs:FindFirstChild("Client")
        local modules=client and client:FindFirstChild("Modules")
        local managers=modules and modules:FindFirstChild("Managers")
        local guns=managers and managers:FindFirstChild("Guns")
        local script=guns and guns:FindFirstChild("GunManager")
        assert(script and script:IsA("ModuleScript"),"This game's weapon system is unsupported.")
        local module=require(script)
        assert(type(module)=="table" and type(module.Tools)=="table","Unsupported gun registry.")
        combat.module=module
        for _,gun in pairs(module.Tools) do combat.patchGun(gun) end
        gui:SetAttribute("WallShotsStatus",#combat.patches>0 and "Client raycast adapter active; server damage unverified" or "Adapter ready; equip a gun. Server damage unverified")
    end)
    if not ok then
        combat.restore();combat.wasWall=true;combat.module=nil
        gui:SetAttribute("WallShotsStatus","Unavailable: "..tostring(err));notify("Wall shots",tostring(err))
    else notify("Wall shots","Experimental client raycasts enabled. Server damage is unverified.") end
end
local cooldown={controlled={},registry=nil,retry=0,keys={"FireCooldown","BurstFireCooldown","BurstBulletCooldown","FireDelay"}}
function cooldown.restoreOne(settings,state)
    for key,record in pairs(state.fields) do
        pcall(function()if rawget(settings,key)==record.last then settings[key]=record.original end end)
    end
end
function cooldown.restore()
    for settings,state in pairs(cooldown.controlled) do cooldown.restoreOne(settings,state) end
    cooldown.controlled={}
end
function cooldown.status(value,tables,fields)
    if not alive then return end
    gui:SetAttribute("NoCooldownStatus",value)
    gui:SetAttribute("NoCooldownTables",tables or 0);gui:SetAttribute("NoCooldownFields",fields or 0)
    if cooldown.row then cooldown.row.Detail.Text=value end
end
function cooldown.findRegistry()
    if cooldown.registry then return cooldown.registry end
    if os.clock()<cooldown.retry then return nil end
    cooldown.retry=os.clock()+2
    local ok,value=pcall(function()
        local rs=game:GetService("ReplicatedStorage")
        local client=rs:FindFirstChild("Client")
        local modules=client and client:FindFirstChild("Modules")
        local managers=modules and modules:FindFirstChild("Managers")
        local guns=managers and managers:FindFirstChild("Guns")
        local script=guns and guns:FindFirstChild("GunManager")
        if not script then return nil end
        local registry=require(script)
        if type(registry)=="table" and type(registry.Tools)=="table" then return registry end
    end)
    if ok then cooldown.registry=value end
    return cooldown.registry
end
function cooldown.update()
    if not C.NoCooldown then
        if next(cooldown.controlled) then cooldown.restore() end
        cooldown.status("Disabled - original gun timing restored.")
        return
    end
    local registry=cooldown.findRegistry()
    if not registry then cooldown.restore();cooldown.status("Unsupported weapon system. Server cooldown cannot be overridden here.");return end
    local active={}
    for _,gun in pairs(registry.Tools) do
        local settings=type(gun)=="table" and rawget(gun,"Settings")
        if type(settings)=="table" then active[settings]=true end
    end
    local tables,fields,failures=0,0,0
    for settings in pairs(active) do
        local state=cooldown.controlled[settings]
        if not state then state={fields={}};cooldown.controlled[settings]=state end
        local handled=0
        for _,key in ipairs(cooldown.keys) do
            local value=rawget(settings,key)
            if type(value)=="number" and value>=0 and value<math.huge then
                local record=state.fields[key] or {original=value}
                if record.last~=nil and value~=record.last then record.original=value end
                local ok=pcall(function()settings[key]=0 end)
                if ok then record.last=rawget(settings,key);state.fields[key]=record;handled+=1;fields+=1
                else failures+=1 end
            end
        end
        if handled>0 then tables+=1 end
    end
    for settings,state in pairs(cooldown.controlled) do
        if not active[settings] then cooldown.restoreOne(settings,state);cooldown.controlled[settings]=nil end
    end
    local message
    if tables>0 then message=tables.." gun profiles / "..fields.." client delays removed. Server firing limits unverified."
    elseif not next(active) then message="Ready - equip a compatible gun. Server firing limits remain authoritative."
    elseif failures>0 then message="Unavailable - the gun timing settings are read-only."
    else message="Unsupported gun - no recognized cooldown settings were found." end
    if failures>0 and tables>0 then message..=" Some settings are read-only." end
    cooldown.status(message,tables,fields)
end

function combat.update(own)
    cooldown.update()
    local count=0
    local displayed={}
    if C.Hitboxes then
        for _,p in ipairs(Players:GetPlayers()) do
            local ch=p.Character
            local h=ch and ch:FindFirstChildOfClass("Humanoid")
            local part=combat.hitPart(ch)
            local teammate=not LP.Neutral and not p.Neutral and LP.Team~=nil and LP.Team==p.Team
            if p~=LP and h and h.Health>0 and part and part:IsA("BasePart") and (C.HitboxTeams or not teammate) then
                own(part,"Size",Vector3.new(C.HitboxSize,C.HitboxSize,C.HitboxSize))
                own(part,"CanCollide",false);own(part,"Massless",true);own(part,"CanQuery",true)
                own(part,"Transparency",C.HitboxOpacity/100);own(part,"Color",ink)
                combat.drawBox(part,displayed)
                count+=1
            end
        end
    end
    combat.clearBoxes(displayed)
    gui:SetAttribute("HitboxesChanged",count)
    if combat.hitStatus then combat.hitStatus.Detail.Text=not C.Hitboxes and "Resizing is OFF. Enable Change hitboxes above." or count==0 and "No eligible living characters loaded. Waiting for players / respawn." or (tostring(count).." resized / "..C.HitboxPart.." / "..C.HitboxSize.." studs / boxes "..(C.HitboxDisplay and "ON" or "OFF")..". Damage unverified.") end
    if C.WallShots and not combat.wasWall then combat.enableWalls()
    elseif not C.WallShots and combat.wasWall then combat.restore();gui:SetAttribute("WallShotsStatus","Disabled") end
    if C.WallShots and combat.module then
        combat.refreshTargets(false)
        local activeGuns={}
        for _,gun in pairs(combat.module.Tools) do if type(gun)=="table" then activeGuns[gun]=true end end
        for i=#combat.patches,1,-1 do
            local entry=combat.patches[i]
            if not activeGuns[entry.gun] or rawget(entry.gun,"Raycast")~=entry.wrapper then
                if not activeGuns[entry.gun] then pcall(function()if entry.gun.Raycast==entry.wrapper then entry.gun.Raycast=entry.original end end) end
                table.remove(combat.patches,i)
            end
        end
        for _,gun in pairs(combat.module.Tools) do combat.patchGun(gun) end
        gui:SetAttribute("WallShotsPatchedGuns",#combat.patches)
        gui:SetAttribute("WallShotsStatus",#combat.patches>0 and (#combat.patches.." guns / "..combat.targetCount.." living targets. Local adapter active; damage unverified.") or "Ready - equip a compatible gun. Server damage unverified.")
    end
    if combat.status then combat.status.Detail.Text=gui:GetAttribute("WallShotsStatus") or "Experimental adapter for this game's guns; server damage unverified." end
end
-- Compatibility checks never send damage or weapon remotes.
function combat.checkResize()
    local target
    for _,p in ipairs(Players:GetPlayers()) do
        local ch=p.Character
        local h=ch and ch:FindFirstChildOfClass("Humanoid")
        local teammate=not LP.Neutral and not p.Neutral and LP.Team~=nil and LP.Team==p.Team
        if p~=LP and h and h.Health>0 and (C.HitboxTeams or not teammate) then
            target=combat.hitPart(ch);if target then break end
        end
    end
    if not target then return "NOT TESTED - no eligible living target", "NOT TESTED" end
    local original=target.Size
    local desired=Vector3.new(C.HitboxSize,C.HitboxSize,C.HitboxSize)
    local query=false
    local ok,err=pcall(function()
        target.Size=desired
        assert((target.Size-desired).Magnitude<.01,"Game did not retain requested size")
        local params=RaycastParams.new()
        params.FilterType=Enum.RaycastFilterType.Include;params.FilterDescendantsInstances={target}
        pcall(function()params.CollisionGroup="Raycast"end)
        local origin=target.Position+target.CFrame.RightVector*(desired.X/2+8)
        local hit=workspace:Raycast(origin,target.Position-origin,params)
        query=hit~=nil and hit.Instance==target
    end)
    local restored,restoreError=pcall(function()target.Size=original end)
    if not restored then return "ERROR - could not restore test size: "..tostring(restoreError),"NOT TESTED" end
    if not ok then return "FAIL - "..tostring(err),"NOT TESTED" end
    return "PASS locally ("..C.HitboxPart..", "..C.HitboxSize.." studs)",query and "PASS locally" or "FAIL - part excluded from local rays"
end
function combat.checkWeapons()
    local rs=game:GetService("ReplicatedStorage")
    local client=rs:FindFirstChild("Client")
    local modules=client and client:FindFirstChild("Modules")
    local managers=modules and modules:FindFirstChild("Managers")
    local guns=managers and managers:FindFirstChild("Guns")
    local script=guns and guns:FindFirstChild("GunManager")
    if not script or not script:IsA("ModuleScript") then return "UNSUPPORTED weapon system",false end
    local ok,module=pcall(require,script)
    if not ok or type(module)~="table" or type(module.Tools)~="table" then return "UNSUPPORTED gun registry",false end
    local count=0
    for _,gun in pairs(module.Tools) do
        if type(gun)=="table" and type(rawget(gun,"Raycast"))=="function" and type(rawget(gun,"AttemptToHit"))=="function" then count+=1 end
    end
    return count>0 and ("READY - "..count.." compatible guns loaded") or "READY - equip a gun to attach",true
end
function combat.checkWallFilter()
    local characters=workspace:FindFirstChild("Characters")
    if not characters then return "NOT TESTED - character container unavailable" end
    local scratch=Instance.new("Folder")
    scratch.Name="NovaCompatibilityProbe_"..uniqueId;scratch.Parent=workspace
    local model=Instance.new("Model")
    model.Name="NovaCompatibilityTarget_"..uniqueId
    local ok,err=xpcall(function()
        local hum=Instance.new("Humanoid");hum.Parent=model
        local target=Instance.new("Part");target.Name="Head";target.Anchored=true
        target.CanCollide=false;target.CanTouch=false;target.Transparency=1
        target.Size=Vector3.new(4,4,4);target.Position=Vector3.new(64000,32000,64000);target.Parent=model
        model.Parent=characters
        local wall=Instance.new("Part");wall.Name="TestBarrier";wall.Anchored=true
        wall.CanCollide=false;wall.CanTouch=false;wall.Transparency=1
        wall.Size=Vector3.new(12,12,2);wall.Position=Vector3.new(64000,32000,64020);wall.Parent=scratch
        local origin=Vector3.new(64000,32000,64040);local direction=Vector3.new(0,0,-80)
        local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Include
        params.FilterDescendantsInstances={wall,target}
        local normal=workspace:Raycast(origin,direction,params)
        assert(normal and normal.Instance==wall,"Test barrier query failed")
        combat.refreshTargets(true)
        local bypass=combat.raycast(nil,origin,direction)
        assert(bypass and bypass.Instance==target,"Client wall filter did not reach the test target")
    end,traceError)
    model:Destroy();scratch:Destroy();combat.refreshTargets(true)
    return ok and "PASS on a local test target" or ("FAIL - "..tostring(err):sub(1,160))
end
function combat.checkCompatibility()
    if combat.checking then return end
    combat.checking=true;combat.report.Visible=true;combat.report:SetAttribute("SearchBaseVisible",true);combat.reportHide.Visible=true;combat.reportHide:SetAttribute("SearchBaseVisible",true);combat.checkButton.Text="CHECKING..."
    combat.report.Detail.Text="Checking local resize, ray queries, and the gun adapter..."
    task.defer(function()
        local ok,err=xpcall(function()
            local resize,query=combat.checkResize()
            local weapons,supported=combat.checkWeapons()
            local walls=supported and combat.checkWallFilter() or "UNSUPPORTED weapon system"
            gui:SetAttribute("CompatibilityResize",resize)
            gui:SetAttribute("CompatibilityRayQuery",query)
            gui:SetAttribute("CompatibilityWeapons",weapons)
            gui:SetAttribute("CompatibilityWallFilter",walls)
            gui:SetAttribute("CompatibilityServerDamage","UNVERIFIED")
            gui:SetAttribute("CompatibilityPlaceId",tostring(game.PlaceId))
            if alive then
                combat.report.Detail.Text="Place: "..game.PlaceId.." / "..os.date("!%H:%M:%S UTC")..
                    "\nHitbox size: "..resize.."\nHitbox ray query: "..query..
                    "\nGun adapter: "..weapons.."\nWall filter: "..walls..
                    "\nServer damage: UNVERIFIED. These checks do not fire shots or prove server acceptance."
            end
        end,traceError)
        combat.checking=false
        if alive then
            combat.checkButton.Text="CHECK GAME COMPATIBILITY"
            if not ok then combat.report.Detail.Text="Check failed: "..tostring(err):sub(1,300);gui:SetAttribute("CompatibilityCheckError",tostring(err))
            else gui:SetAttribute("CompatibilityCheckError",nil);notify("Compatibility check","Results are shown below the button. Server damage remains unverified.") end
        end
    end)
end
-- Use the supported weapon's normal fire function; no OS mouse holds or damage remotes.
local autofire={lastFire=-math.huge,target=nil,since=0,busy=false,nextRegistry=0,registry=nil}
function autofire.status(value)
    if not alive then return end
    if gui:GetAttribute("AutoFireStatus")~=value then gui:SetAttribute("AutoFireStatus",value) end
    if autofire.row then autofire.row.Detail.Text=value end
end
function autofire.reset(value)
    autofire.target=nil;autofire.since=0;autofire.status(value)
end
function autofire.playerForHit(hit)
    local object=hit and hit.Instance
    while object and object~=workspace do
        if object:IsA("Model") then
            local p=Players:GetPlayerFromCharacter(object)
            local h=object:FindFirstChildOfClass("Humanoid")
            if p and p~=LP and h and h.Health>0 then
                local teammate=not LP.Neutral and not p.Neutral and LP.Team~=nil and LP.Team==p.Team
                if C.Teams and teammate then return nil end
                return p
            end
        end
        object=object.Parent
    end
end
function autofire.registryForGame()
    if autofire.registry then return autofire.registry end
    if os.clock()<autofire.nextRegistry then return nil end
    autofire.nextRegistry=os.clock()+2
    local ok,value=pcall(function()
        local client=game:GetService("ReplicatedStorage"):FindFirstChild("Client")
        local modules=client and client:FindFirstChild("Modules")
        local managers=modules and modules:FindFirstChild("Managers")
        local guns=managers and managers:FindFirstChild("Guns")
        local script=guns and guns:FindFirstChild("GunManager")
        if not script then return nil end
        local registry=require(script)
        if type(registry)=="table" and type(registry.Tools)=="table" then return registry end
    end)
    if ok then autofire.registry=value end
    return autofire.registry
end
function autofire.crosshair(cam)
    local origin=cam.CFrame.Position
    local direction=cam.CFrame.LookVector*C.Range
    local hit
    if C.WallShots then
        local ok,value=pcall(combat.raycast,nil,origin,direction)
        if ok then hit=value end
    else
        local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Exclude
        params.FilterDescendantsInstances={LP.Character,cam}
        pcall(function()params.CollisionGroup="Raycast"end)
        hit=workspace:Raycast(origin,direction,params)
    end
    return autofire.playerForHit(hit)
end
function autofire.step(cam,chosen,point)
    if C.EditorOpen then autofire.reset("Paused for script editor");return end
    if C.Healing then autofire.reset("Paused for healing");return end
    if not alive or not (C.TriggerBot or C.AutoShoot) then autofire.reset("Disabled");return end
    if menuOpen or not mobile and free or not focus or UIS:GetFocusedTextBox() then autofire.reset("Paused - menu, cursor, or text input.");return end
    if type(iswindowactive)=="function" then
        local ok,active=pcall(iswindowactive)
        if not ok or not active then autofire.reset("Paused - game window is not active.");return end
    end
    local ch=LP.Character
    local hum=ch and ch:FindFirstChildOfClass("Humanoid")
    if not hum or hum.Health<=0 then autofire.reset("Waiting for a living character.");return end
    local tool=autofire.currentTool and autofire.currentTool() or ch:FindFirstChildOfClass("Tool")
    if not tool then autofire.reset("Equip a compatible gun.");return end
    if not tool.Enabled or tool:GetAttribute("Ammo")==0 then autofire.reset("Weapon unavailable or out of ammo.");return end
    local registry=autofire.registryForGame()
    local gun=registry and registry.Tools[tool]
    if type(gun)~="table" or type(gun.DoFire)~="function" then autofire.reset("Unsupported weapon - no native fire adapter.");return end
    local target
    local crosshair=autofire.crosshair(cam)
    if C.TriggerBot and crosshair then target=crosshair end
    if not target and C.AutoShoot and chosen and point then
        local direction=point-cam.CFrame.Position
        if direction.Magnitude>.001 and cam.CFrame.LookVector:Dot(direction.Unit)>=math.cos(math.rad(C.FireAngle)) and crosshair==chosen then target=chosen end
    end
    if not target then autofire.reset(C.AutoShoot and "Waiting for aim alignment or crosshair target." or "Waiting for a crosshair target.");return end
    local now=os.clock()
    if target~=autofire.target then autofire.target=target;autofire.since=now end
    if (now-autofire.since)*1000<C.FireDelay then autofire.status("Target acquired - waiting for firing delay.");return end
    if autofire.busy or now-autofire.lastFire<1/math.max(1,C.FireRate) then return end
    autofire.lastFire=now;autofire.busy=true;autofire.status("Native fire request / "..target.Name)
    task.spawn(function()
        if alive and tool.Parent==LP.Character and (C.TriggerBot or C.AutoShoot) and not menuOpen and focus then
            local ok,err=pcall(gun.DoFire,gun)
            if not ok then gui:SetAttribute("AutoFireError",tostring(err));autofire.status("Native fire failed: "..tostring(err):sub(1,100)) end
        end
        autofire.busy=false
    end)
end

-- Utilities own their state so reruns/unloading stop scheduled actions.
local utility={alive=true,nextStep=0,nextClick=0,nextReload=0,busy=false,reloadBusy=false,tools=setmetatable({},{__mode="k"})}
function utility.status(message)
    if not alive then return end
    if utility.row then utility.row.Detail.Text=message end
    gui:SetAttribute("AmmoStatus",message)
end
function utility.current()
    local ch=LP.Character
    local h=ch and ch:FindFirstChildOfClass("Humanoid")
    if not h or h.Health<=0 then return nil end
    return ch:FindFirstChildOfClass("Tool")
end
function utility.gun(tool)
    local registry=autofire.registryForGame()
    return registry and registry.Tools[tool]
end
function utility.capacity(tool)
    local gun=utility.gun(tool)
    local settings=type(gun)=="table" and gun.Settings
    if type(settings)=="table" and type(settings.Ammo)=="number" and settings.Ammo>0 and settings.Ammo<100000 then
        return math.floor(settings.Ammo),gun
    end
    return nil,gun
end
function utility.inspect()
    local tool=utility.current()
    if not tool then utility.status("Equip a gun. Ammo support is checked again automatically.");return end
    local ammo,mags=tool:GetAttribute("Ammo"),tool:GetAttribute("Mags")
    local cap=utility.capacity(tool)
    local server=tool:GetAttribute("ServerAmmo")
    gui:SetAttribute("AmmoWeapon",tool.Name)
    gui:SetAttribute("AmmoCapacity",cap or 0)
    gui:SetAttribute("AmmoServerVerified",false)
    if type(ammo)~="number" or not cap then
        utility.status(tool.Name..": unsupported ammo system. Local refill is unavailable.")
    elseif type(server)=="number" then
        utility.status(tool.Name.." | local "..tostring(ammo).."/"..cap.." | server "..tostring(server).." | reserve "..tostring(mags)..". Local refill cannot guarantee server ammo.")
    else
        utility.status(tool.Name.." | local "..tostring(ammo).."/"..cap.." | reserve "..tostring(mags)..". Server acceptance is unverified.")
    end
end
function utility.refill(tool)
    local cap=utility.capacity(tool)
    local ammo=tool:GetAttribute("Ammo")
    if not cap or type(ammo)~="number" then return end
    if ammo<cap then tool:SetAttribute("Ammo",cap) end
end
function utility.watch(tool)
    if utility.tools[tool] then return end
    utility.tools[tool]=true
    on(tool:GetAttributeChangedSignal("Ammo"),function()
        if not alive or not C.InfiniteAmmo then return end
        task.defer(function()
            if alive and C.InfiniteAmmo and tool==utility.current() then
                local ok,err=pcall(utility.refill,tool)
                if not ok then utility.status("Local refill failed: "..tostring(err):sub(1,90)) end
            end
        end)
    end)
end
function utility.reload()
    local tool=utility.current()
    if not tool then utility.status("Equip a gun before reloading.");return end
    local cap,gun=utility.capacity(tool)
    if not cap or type(gun)~="table" or type(gun.DoReload)~="function" then utility.status("Reload unavailable: unsupported weapon.");return end
    if utility.reloadBusy then return end
    if (tonumber(tool:GetAttribute("Mags")) or 0)<=0 then utility.status("No reserve ammo. Reload requires ammunition supplied by the game.");return end
    if (tonumber(tool:GetAttribute("Ammo")) or cap)>=cap then return end
    utility.reloadBusy=true
    task.spawn(function()
        local ok,err=pcall(function()if alive and tool==utility.current() then gun:DoReload() end end)
        utility.reloadBusy=false
        if not ok and alive then utility.status("Reload failed: "..tostring(err):sub(1,100)) end
    end)
end
function utility.click()
    if C.EditorOpen or C.Healing or C.Bot or not C.AutoClicker or utility.busy or menuOpen or not focus or UIS:GetFocusedTextBox() then return end
    if type(mouse1click)~="function" or type(iswindowactive)~="function" then
        C.AutoClicker=false;refresh();utility.clickRow.Detail.Text="Mouse input unavailable in this executor.";return
    end
    local ok,active=pcall(iswindowactive)
    if not ok or not active then return end
    local position=UIS:GetMouseLocation()
    if ui.pointerBlocked(position) then return end
    local now=os.clock()
    if now<utility.nextClick then return end
    utility.nextClick=now+1/math.clamp(C.ClickRate,1,20)
    utility.busy=true
    task.spawn(function()
        local success,err=pcall(function()
            if alive and C.AutoClicker and not menuOpen and focus and not UIS:GetFocusedTextBox() and iswindowactive() then mouse1click() end
        end)
        utility.busy=false
        if not success and alive then C.AutoClicker=false;refresh();utility.clickRow.Detail.Text="Click failed: "..tostring(err):sub(1,80) end
    end)
end
function utility.step()
    utility.click()
    local now=os.clock()
    if now<utility.nextStep then return end
    utility.nextStep=now+.2
    local tool=utility.current()
    if tool then
        utility.watch(tool)
        if C.InfiniteAmmo then utility.refill(tool) end
        if C.AutoReload and not C.InfiniteAmmo and not menuOpen and focus and not UIS:GetFocusedTextBox() and now>=utility.nextReload then
            if tool:GetAttribute("Ammo")==0 then utility.nextReload=now+1;utility.reload() end
        end
    end
    utility.inspect()
end
heading(pages.Combat,"Ammo and clicking","Ammo requires game support. Mouse clicking works independently of the weapon adapter.")
toggle(pages.Combat,"InfiniteAmmo","Infinite ammo (local only)","Experimental magazine refill. Server ammo and accepted shots are not guaranteed. Does not create reserve ammo.")
toggle(pages.Combat,"AutoReload","Automatic reload","Reload an empty supported gun using the game's reload function and existing reserve ammo.")
utility.row=row(pages.Combat,"AmmoDiagnostics","Ammo diagnostics","Equip a gun to check its ammo system.",mobile and 150 or 125)
utility.row.RowTitle.Size=UDim2.new(1,-32,0,22);utility.row.Detail.Size=UDim2.new(1,-32,1,-48)
local ammoCheck=button(pages.Combat,"CheckAmmo","CHECK AMMO SUPPORT",UDim2.new(),UDim2.new(1,0,0,44))
on(ammoCheck.Activated,function()utility.inspect()end)
local reloadNow=button(pages.Combat,"ReloadNow","RELOAD NOW",UDim2.new(),UDim2.new(1,0,0,44))
on(reloadNow.Activated,function()utility.reload()end)
toggle(pages.Combat,"AutoClicker","Auto clicker","Repeat left clicks while the menu is closed. Pauses over Nova, during typing, and when the game loses focus. F6 toggles.")
slider(pages.Combat,"ClickRate","Clicks per second","Independent of weapon cooldown and trigger bot.",1,20,1)
utility.clickRow=row(pages.Combat,"ClickerHelp","Auto clicker control","Close the menu to begin clicking. Press F6 to stop.",100)
utility.clickRow.RowTitle.Size=UDim2.new(1,-32,0,22);utility.clickRow.Detail.Size=UDim2.new(1,-32,1,-48)
local stopUtilities=button(pages.Combat,"StopUtilities","STOP AMMO AND AUTO CLICKER",UDim2.new(),UDim2.new(1,0,0,44))
on(stopUtilities.Activated,function()C.AutoClicker=false;C.InfiniteAmmo=false;C.AutoReload=false;refresh();utility.status("Ammo refill, automatic reload, and auto clicker stopped.")end)
on(UIS.InputBegan,function(key,processed)
    if not processed and not UIS:GetFocusedTextBox() and key.KeyCode==Enum.KeyCode.F6 then
        C.AutoClicker=not C.AutoClicker;refresh();notify("Auto clicker",C.AutoClicker and "Enabled; close the menu to click." or "Disabled")
    end
end)

heading(pages.Combat,"Combat controls","Local weapon support, visible hitboxes, and game compatibility.")
toggle(pages.Combat,"NoCooldown","No gun cooldown (local)","Remove supported client shot and burst delays. The server may still enforce cooldowns.")
cooldown.row=row(pages.Combat,"NoCooldownStatus","Gun cooldown status","Disabled",110)
cooldown.row.RowTitle.Size=UDim2.new(1,-32,0,22);cooldown.row.Detail.Size=UDim2.new(1,-32,1,-48);cooldown.row.Detail.TextWrapped=true
toggle(pages.Combat,"TriggerBot","Trigger bot","Fire the equipped gun when a living target is under the center crosshair.")
toggle(pages.Combat,"AutoShoot","Auto shoot","Fire when aim tracking has aligned with a target. Enable Aim in the Aim tab.")
slider(pages.Combat,"FireRate","Auto fire limit","Maximum requests per second; the weapon still controls its actual fire rate.",1,12,1)
slider(pages.Combat,"FireDelay","Target firing delay","Wait this many milliseconds after acquiring a target.",0,300,10)
slider(pages.Combat,"FireAngle","Aim alignment tolerance","Auto shoot requires the camera to align within this angle.",0.5,5,0.5)
autofire.row=row(pages.Combat,"AutoFireStatus","Automatic firing status","Disabled",110)
autofire.row.RowTitle.Size=UDim2.new(1,-32,0,22);autofire.row.Detail.Size=UDim2.new(1,-32,1,-48);autofire.row.Detail.TextWrapped=true
local godInfo=button(pages.Combat,"GodModeUnavailable","GOD MODE - SERVER SUPPORT REQUIRED",UDim2.new(),UDim2.new(1,0,0,44))
on(godInfo.Activated,function()notify("God mode unavailable","This client cannot grant server-side invulnerability.")end)
toggle(pages.Combat,"WallShots","Shoot through walls","Experimental local gun adapter. Server damage remains unverified.")
combat.status=row(pages.Combat,"WallShotsStatus","Weapon status","Disabled. Enable Shoot through walls above.",110)
combat.status.RowTitle.Size=UDim2.new(1,-32,0,22)
combat.status.Detail.Size=UDim2.new(1,-32,1,-48);combat.status.Detail.TextWrapped=true;combat.status.Detail.TextYAlignment=Enum.TextYAlignment.Top
toggle(pages.Combat,"Hitboxes","Change hitboxes","Resize the chosen body part locally. Disable to restore.")
toggle(pages.Combat,"HitboxDisplay","Show hitbox boxes","Cyan bounds show the selected part through walls.")
cycle(pages.Combat,"HitboxPart","Hitbox body part","Choose Head or Torso; torso supports both character rigs.",{"Head","Torso","UpperTorso","LowerTorso"})
slider(pages.Combat,"HitboxSize","Hitbox size","Local part size. Server damage is not guaranteed.",2,100,2)
slider(pages.Combat,"HitboxOpacity","Hitbox transparency","Independent of the cyan box display.",0,100,5)
toggle(pages.Combat,"HitboxTeams","Include teammates","Also resize players on your team.")
combat.hitStatus=row(pages.Combat,"HitboxStatus","Hitbox status","Waiting for a character.",96)
combat.hitStatus.RowTitle.Size=UDim2.new(1,-32,0,22)
combat.hitStatus.Detail.Size=UDim2.new(1,-32,1,-44);combat.hitStatus.Detail.TextWrapped=true
combat.checkButton=button(pages.Combat,"CheckCompatibility","CHECK GAME COMPATIBILITY",UDim2.new(),UDim2.new(1,0,0,48))
on(combat.checkButton.Activated,combat.checkCompatibility)
combat.report=row(pages.Combat,"CompatibilityReport","Local compatibility results","Run a check to see results.",mobile and 340 or 270)
combat.report.RowTitle.Size=UDim2.new(1,-32,0,22)
combat.report.Detail.Position=UDim2.fromOffset(16,42);combat.report.Detail.Size=UDim2.new(1,-32,1,-54)
combat.report.Detail.TextWrapped=true;combat.report.Detail.TextYAlignment=Enum.TextYAlignment.Top
combat.report.Visible=false;combat.report:SetAttribute("SearchBaseVisible",false)
combat.reportHide=button(pages.Combat,"HideCompatibility","HIDE CHECK RESULTS",UDim2.new(),UDim2.new(1,0,0,36))
combat.reportHide.Visible=false;combat.reportHide:SetAttribute("SearchBaseVisible",false)
on(combat.reportHide.Activated,function()combat.report.Visible=false;combat.report:SetAttribute("SearchBaseVisible",false);combat.reportHide.Visible=false;combat.reportHide:SetAttribute("SearchBaseVisible",false);ui.applyFilter()end)
heading(pages.Travel,"Flight","Close the menu to move. WASD; Space/E up; Ctrl/Q down; Shift boost.")
toggle(pages.Travel,"Fly","Fly","Hover and move freely. Touch uses the joystick and UP / DOWN buttons.")
slider(pages.Travel,"FlySpeed","Fly speed","Horizontal flight speed in studs per second.",5,150,5)
slider(pages.Travel,"FlyVerticalSpeed","Fly vertical speed","Ascend and descend speed in studs per second.",5,100,5)
slider(pages.Travel,"FlyBoost","Fly boost multiplier","Hold Left Shift on PC to temporarily increase flight speed.",1,4,0.5)
flight.status=row(pages.Travel,"FlyStatus","Flight status","Disabled",100)
flight.status.RowTitle.Size=UDim2.new(1,-32,0,22);flight.status.Detail.Size=UDim2.new(1,-32,1,-44);flight.status.Detail.TextWrapped=true

local extras={parts={},character=nil,checkpoint=nil,nextScan=0,nextHop=0,rows=0}
extras.defaults={Noclip=false,InfiniteJump=false,BunnyHop=false,Sprint=false,SlowFall=false,Spin=false,AutoWalk=false,
    SelfLight=false,Ghost=false,HideAccessories=false,HideTool=false,SelfOutline=false,Coordinates=false,Compass=false,AirPlatform=false,
    SprintSpeed=32,FallLimit=18,SpinRate=120,GhostOpacity=65,StepDistance=8}
for key,value in pairs(extras.defaults) do C[key]=value end
function extras.characterParts()
    local ch=LP.Character
    local hum=ch and ch:FindFirstChildOfClass("Humanoid")
    local hrp=ch and ch:FindFirstChild("HumanoidRootPart")
    if hum and hum.Health>0 and hrp then return ch,hum,hrp end
end
function extras.active()
    return alive and focus and not menuOpen and not UIS:GetFocusedTextBox()
end
function extras.message(value)
    if extras.statusRow then extras.statusRow.Detail.Text=value end
    gui:SetAttribute("ExtrasStatus",value)
end
function extras.scan(ch)
    if extras.character~=ch or os.clock()>=extras.nextScan then
        extras.character=ch;extras.nextScan=os.clock()+1;table.clear(extras.parts)
        if ch then for _,object in ipairs(ch:GetDescendants()) do if object:IsA("BasePart") then table.insert(extras.parts,object) end end end
    end
end
function extras.apply(own)
    extras.own=own
    local ch,hum,hrp=extras.characterParts()
    extras.scan(ch)
    if ch then
        for _,part in ipairs(extras.parts) do if part.Parent and part:IsDescendantOf(ch) then
            if C.Noclip then own(part,"CanCollide",false) end
            local hidden=C.HideAccessories and part:FindFirstAncestorOfClass("Accessory") or C.HideTool and part:FindFirstAncestorOfClass("Tool")
            if hidden then own(part,"LocalTransparencyModifier",1)
            elseif C.Ghost then own(part,"LocalTransparencyModifier",C.GhostOpacity/100) end
        end end
        if C.Sprint and extras.active() and (mobile and hum.MoveDirection.Magnitude>.1 or UIS:IsKeyDown(Enum.KeyCode.LeftShift)) and not C.Fly then
            own(hum,"WalkSpeed",C.SprintSpeed)
        end
        if C.Spin and extras.active() and not C.Fly then own(hum,"AutoRotate",false) end
    end
    if C.SelfLight and hrp then
        if not extras.light or extras.light.Parent~=hrp then
            if extras.light then extras.light:Destroy() end
            extras.light=make("PointLight",{Name="NovaHeadlight",Brightness=2,Range=40,Shadows=false,Color=Color3.new(1,1,1)},hrp)
        end
    elseif extras.light then extras.light:Destroy();extras.light=nil end
    if C.SelfOutline and ch then
        if not extras.outline then extras.outline=make("Highlight",{Name="NovaSelfOutline",FillTransparency=1,OutlineTransparency=.1,DepthMode=Enum.HighlightDepthMode.AlwaysOnTop},world) end
        extras.outline.Adornee=ch;extras.outline.OutlineColor=ink
    elseif extras.outline then extras.outline:Destroy();extras.outline=nil end
end
function extras.step(dt,cam)
    if extras.own then extras.apply(extras.own) end
    local ch,hum,hrp=extras.characterParts()
    local active=extras.active()
    if hrp then
        if active and not C.Fly then
            if C.SlowFall then
                local v=hrp.AssemblyLinearVelocity
                if v.Y < -C.FallLimit then hrp.AssemblyLinearVelocity=Vector3.new(v.X,-C.FallLimit,v.Z) end
            end
            if C.Spin then hrp.CFrame=hrp.CFrame*CFrame.Angles(0,math.rad(C.SpinRate)*math.min(dt,.1),0) end
            if C.AutoWalk then
                local direction=Vector3.new(cam.CFrame.LookVector.X,0,cam.CFrame.LookVector.Z)
                if direction.Magnitude>.01 then hum:Move(direction.Unit,false) end
            end
            if C.BunnyHop and hum.MoveDirection.Magnitude>.1 and hum.FloorMaterial~=Enum.Material.Air and os.clock()>=extras.nextHop then
                extras.nextHop=os.clock()+.2;hum.Jump=true
            end
        end
        if C.AirPlatform and not C.Noclip then
            if not extras.platform then
                extras.platform=make("Part",{Name="NovaAirPlatform",Anchored=true,CanCollide=true,CanTouch=false,CanQuery=false,Size=Vector3.new(7,.3,7),Material=Enum.Material.Neon,Transparency=.65},world)
            end
            extras.platform.Color=ink;extras.platform.CFrame=CFrame.new(hrp.Position-Vector3.new(0,hum.HipHeight+hrp.Size.Y/2+.25,0))
        elseif extras.platform then extras.platform:Destroy();extras.platform=nil end
        extras.coords.Text=string.format("X %.1f   Y %.1f   Z %.1f",hrp.Position.X,hrp.Position.Y,hrp.Position.Z)
    elseif extras.platform then extras.platform:Destroy();extras.platform=nil end
    extras.coords.Visible=C.Coordinates and hrp~=nil
    local look=cam.CFrame.LookVector
    local degrees=(math.deg(math.atan2(look.X,-look.Z))+360)%360
    extras.compass.Text=({"N","NE","E","SE","S","SW","W","NW"})[math.floor((degrees+22.5)/45)%8+1].."  "..math.floor(degrees).."°"
    extras.compass.Visible=C.Compass
end
function extras.resetMovement()
    for _,key in ipairs({"Noclip","InfiniteJump","BunnyHop","Sprint","SlowFall","Spin","AutoWalk","AirPlatform","Fly","SpeedOverride","JumpOverride","GravityOverride","AutoRotateOverride"}) do C[key]=false end
    flight.stop()
    local _,hum,hrp=extras.characterParts()
    if hum then hum:Move(Vector3.zero,false) end
    if hrp then hrp.AssemblyLinearVelocity=Vector3.zero;hrp.AssemblyAngularVelocity=Vector3.zero end
    refresh();extras.message("Movement controls stopped and original properties restored.")
end
function extras.panic()
    extras.resetMovement()
    for key,value in pairs(C) do if type(value)=="boolean" then C[key]=false end end
    C.ViewMode="Game default";C.Mode=mobile and "Hold AIM" or "Hold RMB"
    autofire.reset("Emergency stop");cooldown.restore();combat.restore();combat.clearBoxes()
    if restoreWorld then restoreWorld() end
    refresh();extras.message("Emergency stop: toggles disabled. F8 is always available.")
end
function extras.destroy()
    for _,key in ipairs({"light","outline","platform"}) do if extras[key] then extras[key]:Destroy();extras[key]=nil end end
end
function extras.action(key,label,fn)
    extras.rows+=1
    local b=button(pages.Extras,key,label,UDim2.new(),UDim2.new(1,0,0,44))
    on(b.Activated,function()
        local ok,err=pcall(fn)
        if not ok then extras.message("Action failed: "..tostring(err):sub(1,100)) end
    end)
end
function extras.toggle(key,titleValue,detail)
    extras.rows+=1;toggle(pages.Extras,key,titleValue,detail)
end
function extras.slider(key,titleValue,detail,lo,hi,step)
    extras.rows+=1;slider(pages.Extras,key,titleValue,detail,lo,hi,step)
end
function extras.move(offset)
    local _,_,hrp=extras.characterParts()
    if not hrp then extras.message("Wait for a living character.");return end
    hrp.CFrame=hrp.CFrame*CFrame.new(offset)
    hrp.AssemblyLinearVelocity=Vector3.zero;hrp.AssemblyAngularVelocity=Vector3.zero
    extras.message("Moved locally. The game server may correct movement.")
end
extras.coords=text(gui,"",11,ink,UDim2.new(1,-290,1,-92),UDim2.fromOffset(274,24));extras.coords.Name="CoordinatesHUD";extras.coords.Visible=false;extras.coords.TextXAlignment=Enum.TextXAlignment.Right
extras.compass=text(gui,"",15,ink,UDim2.new(.5,-80,0,12),UDim2.fromOffset(160,28));extras.compass.Name="CompassHUD";extras.compass.Visible=false
extras.ammo=button(gui,"QuickInfiniteAmmo","INF AMMO: OFF",UDim2.new(1,-16,.5,-22),UDim2.fromOffset(174,44))
extras.ammo.AnchorPoint=Vector2.new(1,0);extras.ammo.ZIndex=25
on(extras.ammo.Activated,function()
    C.InfiniteAmmo=not C.InfiniteAmmo;refresh();utility.inspect()
    notify("Infinite ammo",C.InfiniteAmmo and "Local refill enabled; check Combat for server ammo status." or "Disabled")
end)
table.insert(paints,function()
    extras.ammo.Text="INF AMMO: "..(C.InfiniteAmmo and "ON" or "OFF")
    extras.ammo.BackgroundColor3=C.InfiniteAmmo and Color3.fromRGB(28,133,94) or Color3.fromRGB(31,34,43)
end)
heading(pages.Extras,"Extra controls","Local movement, appearance, quick presets, and recovery. Features start disabled; servers may limit movement.")
extras.toggle("Noclip","Noclip","Disable local character collisions. Turn off to restore original collision properties.")
extras.toggle("InfiniteJump","Infinite jump","Request another jump while airborne. Close the menu first.")
extras.toggle("BunnyHop","Bunny hop","Automatically jump when moving on the ground.")
extras.toggle("Sprint","Sprint","Hold Left Shift to sprint. Touch: sprint while moving.")
extras.slider("SprintSpeed","Sprint speed","Applies while sprint is active.",16,100,2)
extras.toggle("SlowFall","Slow fall","Limit local falling speed while the menu is closed.")
extras.slider("FallLimit","Fall speed limit","Downward speed cap in studs per second.",5,80,1)
extras.toggle("Spin","Spin character","Rotate the character during play; disabled while flying.")
extras.slider("SpinRate","Spin speed","Degrees per second.",30,720,30)
extras.toggle("AutoWalk","Auto walk","Move forward in the camera direction while the menu is closed.")
extras.toggle("AirPlatform","Air platform","Create a visible local platform under your character. Paused while noclip is on.")
heading(pages.Extras,"Local appearance","These appearance controls affect your view.")
extras.toggle("SelfLight","Personal light","Attach a local light to your character.")
extras.toggle("Ghost","Transparent character","Fade your own character locally; does not make you invisible to other players.")
extras.slider("GhostOpacity","Character transparency","Amount of local fading.",10,100,5)
extras.toggle("HideAccessories","Hide accessories","Hide your accessories locally and restore them when disabled.")
extras.toggle("HideTool","Hide equipped tool","Hide the equipped tool model locally.")
extras.toggle("SelfOutline","Self outline","Outline your own character using the current theme color.")
extras.toggle("Coordinates","Coordinates HUD","Show your current X, Y, and Z at the bottom right.")
extras.toggle("Compass","Compass HUD","Show the camera compass heading at the top center.")
heading(pages.Extras,"Quick movement","Short moves and one-click presets. Teleports are local and may be corrected by the server.")
extras.slider("StepDistance","Step distance","Distance used by the five directional step buttons.",1,30,1)
extras.action("StepForward","STEP FORWARD",function()extras.move(Vector3.new(0,0,-C.StepDistance))end)
extras.action("StepBack","STEP BACK",function()extras.move(Vector3.new(0,0,C.StepDistance))end)
extras.action("StepLeft","STEP LEFT",function()extras.move(Vector3.new(-C.StepDistance,0,0))end)
extras.action("StepRight","STEP RIGHT",function()extras.move(Vector3.new(C.StepDistance,0,0))end)
extras.action("StepUp","STEP UP",function()extras.move(Vector3.new(0,C.StepDistance,0))end)
extras.action("Brake","STOP MOMENTUM",function()local _,_,r=extras.characterParts();if r then r.AssemblyLinearVelocity=Vector3.zero;r.AssemblyAngularVelocity=Vector3.zero;extras.message("Momentum stopped.")end end)
extras.action("SitDown","SIT DOWN",function()local _,h=extras.characterParts();if h then h.Sit=true end end)
extras.action("StandUp","STAND UP",function()local _,h=extras.characterParts();if h then h.Sit=false;h:ChangeState(Enum.HumanoidStateType.GettingUp)end end)
extras.action("SaveCheckpoint","SAVE QUICK CHECKPOINT",function()local _,_,r=extras.characterParts();if r then extras.checkpoint=r.CFrame;extras.message("Session checkpoint saved.")end end)
extras.action("ReturnCheckpoint","RETURN TO CHECKPOINT",function()local _,_,r=extras.characterParts();if r and extras.checkpoint then r.CFrame=extras.checkpoint;r.AssemblyLinearVelocity=Vector3.zero;extras.message("Returned to checkpoint.")else extras.message("Save a checkpoint first.")end end)
extras.action("ClearCheckpoint","CLEAR QUICK CHECKPOINT",function()extras.checkpoint=nil;extras.message("Checkpoint cleared.")end)
extras.action("FastWalkPreset","FAST WALK PRESET",function()C.SpeedOverride=true;C.WalkSpeed=40;refresh();extras.message("Walk speed set to 40.")end)
extras.action("SuperJumpPreset","SUPER JUMP PRESET",function()C.JumpOverride=true;C.JumpPower=85;C.JumpHeight=18;refresh();extras.message("Higher jump preset enabled.")end)
extras.action("MoonGravityPreset","MOON GRAVITY PRESET",function()C.GravityOverride=true;C.Gravity=55;refresh();extras.message("Local gravity set to 55.")end)
extras.action("ResetMovementExtras","RESET MOVEMENT",extras.resetMovement)
heading(pages.Extras,"Quick view presets","Presets use the existing World, Visuals, and Travel settings.")
extras.action("DayPreset","DAYTIME",function()C.TimeLock=true;C.DayTime=12;refresh()end)
extras.action("NightPreset","NIGHTTIME",function()C.TimeLock=true;C.DayTime=0;refresh()end)
extras.action("SunsetPreset","SUNSET",function()C.TimeLock=true;C.DayTime=18;refresh()end)
extras.action("ClearViewPreset","CLEAR VIEW PRESET",function()C.NoFog=true;C.NoBlur=true;C.NoDOF=true;C.NoBloom=true;refresh()end)
extras.action("WideFovPreset","WIDE CAMERA FOV",function()C.CameraOverride=true;C.CameraFOV=100;refresh()end)
extras.action("FirstPersonPreset","FIRST PERSON",function()C.ViewMode="First person";refresh()end)
extras.action("ThirdPersonPreset","THIRD PERSON",function()C.ViewMode="Third person";refresh()end)
extras.action("ResetCameraExtras","RESET CAMERA",function()C.ViewMode="Game default";C.CameraOverride=false;C.CameraOffsetOverride=false;C.ZoomOverride=false;refresh()end)
extras.action("OutlinePreset","OUTLINE ESP PRESET",function()C.ESP=true;C.Opacity=100;C.OutlineFade=0;C.Names=false;C.Health=false;C.Distance=false;refresh()end)
extras.action("LabelsPreset","PLAYER LABELS PRESET",function()C.ESP=true;C.Names=true;C.Health=true;C.Distance=true;C.Opacity=80;refresh()end)
extras.action("ResetAppearanceExtras","RESTORE LOCAL APPEARANCE",function()for _,k in ipairs({"Ghost","HideAccessories","HideTool","SelfLight","SelfOutline"})do C[k]=false end;refresh();extras.message("Local appearance restored.")end)
extras.action("PanicExtras","EMERGENCY STOP / F8",extras.panic)
extras.statusRow=row(pages.Extras,"ExtrasStatus","Extra controls status","Ready. F8 disables active toggles.",110)
extras.statusRow.RowTitle.Size=UDim2.new(1,-32,0,22);extras.statusRow.Detail.Size=UDim2.new(1,-32,1,-48)
gui:SetAttribute("ExtraControlCount",extras.rows)
on(UIS.JumpRequest,function()
    if C.InfiniteJump and extras.active() and not C.Fly then
        local _,hum=extras.characterParts()
        if hum then hum:ChangeState(Enum.HumanoidStateType.Jumping)end
    end
end)
on(UIS.InputBegan,function(key,processed)
    if not processed and not UIS:GetFocusedTextBox() and key.KeyCode==Enum.KeyCode.F8 then extras.panic()end
end)

local shooter={commands={},order={},history={},recoil={"RecoilForce","CameraRecoilForce"},kick={"RecoilFOV","CameraRecoilFOV"}}
C.NoRecoil=false;C.NoSpread=false;C.NoAimKick=false
C.InputSensitivity=false;C.AimSensitivity=1
function shooter.apply(own)
    if C.InputSensitivity and not mobile then own(UIS,"MouseDeltaSensitivity",C.AimSensitivity) end
    local count,fields=0,0
    if C.NoRecoil or C.NoSpread or C.NoAimKick then
        local registry=autofire.registryForGame()
        for _,gun in pairs(registry and registry.Tools or {}) do
            local settings=type(gun)=="table" and gun.Settings
            if type(settings)=="table" then
                local matched=false
                for _,group in ipairs({{enabled=C.NoRecoil,keys=shooter.recoil},{enabled=C.NoAimKick,keys=shooter.kick}}) do
                    if group.enabled then for _,key in ipairs(group.keys) do
                        if type(rawget(settings,key))=="number" then own(settings,key,0);fields+=1;matched=true end
                    end end
                end
                if C.NoSpread then
                    local spread=rawget(settings,"BulletSpread")
                    if type(spread)=="table" and type(rawget(spread,1))=="number" and type(rawget(spread,2))=="number" then
                        own(spread,1,0);own(spread,2,0);fields+=2;matched=true
                    elseif type(spread)=="number" then own(settings,"BulletSpread",0);fields+=1;matched=true end
                end
                if matched then count+=1 end
            end
        end
    end
    local message=not (C.NoRecoil or C.NoSpread or C.NoAimKick) and "Weapon modifiers off. Original settings restored."
        or count>0 and (count.." compatible gun profiles / "..fields.." recognized local fields. Server shot validation remains unverified.")
        or "Unsupported weapon settings. Equip a compatible gun; no universal weapon override is available."
    if shooter.weaponRow then shooter.weaponRow.Detail.Text=message end
    gui:SetAttribute("ShooterWeaponStatus",message);gui:SetAttribute("ShooterProfiles",count)
end
function shooter.log(message)
    table.insert(shooter.history,1,tostring(message):sub(1,240))
    while #shooter.history>4 do table.remove(shooter.history)end
    shooter.output.Detail.Text=table.concat(shooter.history,"\n")
    gui:SetAttribute("CommandResult",tostring(message))
end
function shooter.add(name,example,description,fn)
    shooter.commands[name]={run=fn,example=example,description=description}
    table.insert(shooter.order,name)
end
function shooter.number(value,lo,hi)
    local number=tonumber(value)
    if not number or number~=number or number<lo or number>hi then error("Expected a number from "..lo.." to "..hi,0)end
    return number
end
function shooter.switch(value)
    if value=="on" then return true elseif value=="off" then return false end
    error("Use on or off.",0)
end
function shooter.setting(name,key,description)
    shooter.add(name,name.." on",description,function(args)C[key]=shooter.switch(args[1]);return name.." "..args[1]end)
end
function shooter.execute(raw)
    if type(raw)~="string" or #raw>120 then shooter.log("Command must be 120 characters or fewer.");return end
    local tokens={};for word in raw:lower():gsub("^%s*[/%.]",""):gmatch("%S+") do table.insert(tokens,word)end
    local name=table.remove(tokens,1)
    local command=name and shooter.commands[name]
    if not command then shooter.log("Unknown command. Type help or use a button below.");return end
    local expected=0;for _ in command.example:gmatch("%S+")do expected+=1 end
    if #tokens~=expected-1 then shooter.log("Usage: "..command.example);return end
    local ok,result=pcall(command.run,tokens)
    if ok then refresh();shooter.log(result or (name.." applied."))else shooter.log("Command error: "..tostring(result))end
end
heading(pages.Commands,"Shooter commands","Run commands locally in Nova. Nothing is sent to game chat. Click preset buttons or enter a command.")
shooter.entry=row(pages.Commands,"CommandEntry","Command input","Examples: sniper, dot, norecoil on, cps 10, stopfire",140)
shooter.entry.RowTitle.Size=UDim2.new(1,-32,0,22)
shooter.input=make("TextBox",{Name="CommandInput",Text="",ClearTextOnFocus=false,PlaceholderText="Type a Nova command...",Font=Enum.Font.Gotham,TextSize=13,TextColor3=ink,
    PlaceholderColor3=muted,BackgroundColor3=Color3.fromRGB(13,15,20),BorderSizePixel=0,TextXAlignment=Enum.TextXAlignment.Left,
    Position=UDim2.fromOffset(16,83),Size=UDim2.new(1,-112,0,40)},shooter.entry);round(shooter.input,8)
make("UIPadding",{PaddingLeft=UDim.new(0,9),PaddingRight=UDim.new(0,6)},shooter.input)
shooter.run=button(shooter.entry,"RunCommand","RUN",UDim2.new(1,-87,0,83),UDim2.fromOffset(71,40))
on(shooter.run.Activated,function()shooter.execute(shooter.input.Text)end)
on(shooter.input.FocusLost,function(enter)if enter then shooter.execute(shooter.input.Text)end end)
shooter.output=row(pages.Commands,"CommandOutput","Recent command results","Ready. Use help to list commands.",210)
shooter.output.RowTitle.Size=UDim2.new(1,-32,0,22);shooter.output.Detail.Size=UDim2.new(1,-32,1,-48)
heading(pages.Commands,"Weapon controls","Compatible local settings only. Modifiers start disabled and restore when turned off or Nova unloads.")
toggle(pages.Commands,"NoRecoil","No recoil (local)","Set recognized recoil-force fields to zero on supported gun profiles.")
toggle(pages.Commands,"NoSpread","No spread (local)","Zero the recognized BulletSpread range. Server accuracy may still differ.")
toggle(pages.Commands,"NoAimKick","No FOV kick (local)","Remove recognized weapon camera FOV pulses.")
shooter.weaponRow=row(pages.Commands,"ShooterWeaponStatus","Weapon modifier status","Waiting for compatible gun profiles.",120)
shooter.weaponRow.RowTitle.Size=UDim2.new(1,-32,0,22);shooter.weaponRow.Detail.Size=UDim2.new(1,-32,1,-48)
toggle(pages.Commands,"InputSensitivity","Mouse sensitivity override","PC: scale camera mouse movement. Restore original sensitivity by disabling.")
slider(pages.Commands,"AimSensitivity","Mouse sensitivity multiplier","0.2 is slow; 1 is normal. Does not change the weapon's aim-assist code.",.1,2,.1)
shooter.add("help","help","Show help and command examples.",function()return #shooter.order.." commands. Browse the buttons below. Examples: sniper / dot / norecoil on / cps 10 / stopfire. Use on or off for toggles."end)
shooter.add("status","status","Show the equipped weapon's ammo diagnostics.",function()utility.inspect();return gui:GetAttribute("AmmoStatus")end)
shooter.setting("aim","Aim","Enable or disable aim tracking with the selected activation key.")
shooter.setting("esp","ESP","Enable or disable player highlights.")
shooter.setting("names","Names","Toggle player names.")
shooter.setting("health","Health","Toggle player health labels.")
shooter.setting("distance","Distance","Toggle player distance labels.")
shooter.setting("predict","AimPrediction","Toggle velocity lead for projectile aiming.")
shooter.setting("visible","Visibility","Toggle aim visibility checks.")
shooter.setting("teamfilter","Teams","Toggle teammate filtering for aim.")
shooter.setting("ring","Circle","Toggle the aim radius circle.")
shooter.setting("norecoil","NoRecoil","Toggle supported local recoil-force removal.")
shooter.setting("nospread","NoSpread","Toggle supported local spread removal.")
shooter.setting("nokick","NoAimKick","Toggle supported local camera FOV-kick removal.")
shooter.setting("ammo","InfiniteAmmo","Toggle local ammo refill. Server acceptance is unverified.")
shooter.setting("autoreload","AutoReload","Reload an empty supported weapon using existing reserve ammo.")
shooter.setting("clicker","AutoClicker","Toggle repeated mouse clicks. Close the menu to start; F6 stops.")
shooter.setting("trigger","TriggerBot","Toggle firing when a target is under the crosshair.")
shooter.setting("autoshoot","AutoShoot","Toggle firing when current aim is aligned with a target.")
shooter.add("smooth","smooth","Smooth aim preset; hold your chosen aim key during play.",function()C.Aim=true;C.AimResponse="Smooth";C.Speed=18;C.FOV=140;C.AimThroughWalls=false;C.Visibility=true;C.AimPrediction=false;C.Mode=mobile and "Hold AIM" or "Hold RMB"end)
shooter.add("sniper","sniper","Tight aim radius and slow response for precision weapons.",function()C.Aim=true;C.FOV=75;C.Speed=12;C.Part="Head";C.AimPrediction=false;C.Visibility=true;C.AimThroughWalls=false;C.AimResponse="Smooth";C.Mode=mobile and "Hold AIM" or "Hold RMB"end)
shooter.add("close","close","Wider aim radius and faster response for close range.",function()C.Aim=true;C.FOV=240;C.Speed=35;C.Part="UpperTorso";C.AimResponse="Smooth";C.Visibility=true;C.AimThroughWalls=false;C.Mode=mobile and "Hold AIM" or "Hold RMB"end)
shooter.add("tracking","tracking","Torso tracking with target retention and smooth response.",function()C.Aim=true;C.Part="UpperTorso";C.Sticky=true;C.AimRetention=50;C.Speed=24;C.FOV=180;C.AimResponse="Smooth";C.Visibility=true;C.AimThroughWalls=false;C.Mode=mobile and "Hold AIM" or "Hold RMB"end)
shooter.add("head","head","Select the head as the aim target.",function()C.Part="Head"end)
shooter.add("torso","torso","Select the upper torso as the aim target.",function()C.Part="UpperTorso"end)
shooter.add("fov","fov 120","Set aim radius: 40 to 600 pixels.",function(a)C.FOV=shooter.number(a[1],40,600)end)
shooter.add("response","response 24","Set aim response speed: 2 to 100.",function(a)C.Speed=shooter.number(a[1],2,100)end)
shooter.add("lead","lead 60","Set prediction lead: 0 to 200 milliseconds.",function(a)C.AimLead=shooter.number(a[1],0,200);C.AimPrediction=true end)
shooter.add("dot","dot","Use a small center-dot crosshair.",function()C.Crosshair=true;C.CrosshairStyle="Dot";C.CrosshairSize=4;C.CrosshairGap=0 end)
shooter.add("cross","cross","Use a compact plus crosshair.",function()C.Crosshair=true;C.CrosshairStyle="Plus";C.CrosshairSize=6;C.CrosshairGap=3 end)
shooter.add("outline","outline","Outline-only player ESP preset.",function()C.ESP=true;C.Opacity=100;C.OutlineFade=0;C.Names=false;C.Health=false;C.Distance=false end)
shooter.add("labels","labels","Player names, health, and distance preset.",function()C.ESP=true;C.Names=true;C.Health=true;C.Distance=true;C.Opacity=80 end)
shooter.add("clear","clear","Clear fog, blur, bloom, and depth of field locally.",function()C.NoFog=true;C.NoBlur=true;C.NoDOF=true;C.NoBloom=true end)
shooter.add("day","day","Lock the local scene to midday.",function()C.TimeLock=true;C.DayTime=12 end)
shooter.add("night","night","Lock the local scene to midnight.",function()C.TimeLock=true;C.DayTime=0 end)
shooter.add("camera","camera 90","Set camera FOV: 40 to 120 degrees.",function(a)C.CameraFOV=shooter.number(a[1],40,120);C.CameraOverride=true end)
shooter.add("scope","scope on","Use a narrow camera FOV. This does not add weapon zoom mechanics.",function(a)C.CameraOverride=shooter.switch(a[1]);if C.CameraOverride then C.CameraFOV=40 end end)
shooter.add("shoulder","shoulder right","Camera shoulder: left, right, or off.",function(a)if a[1]=="off"then C.CameraOffsetOverride=false elseif a[1]=="left"or a[1]=="right"then C.CameraOffsetOverride=true;C.CameraSide=a[1]=="left"and -2 or 2 else error("Use left, right, or off.",0)end end)
shooter.add("sensitivity","sensitivity 0.5","Set PC mouse sensitivity: 0.1 to 2.",function(a)if mobile then error("Mouse sensitivity requires PC mouse input.",0)end;C.AimSensitivity=shooter.number(a[1],.1,2);C.InputSensitivity=true end)
shooter.add("resetinput","resetinput","Restore original mouse sensitivity.",function()C.InputSensitivity=false end)
shooter.add("reload","reload","Request the equipped compatible weapon's native reload.",function()utility.reload();return "Reload requested; see Combat ammo status for compatibility."end)
shooter.add("cps","cps 10","Set auto-clicker rate: 1 to 20 clicks per second.",function(a)C.ClickRate=shooter.number(a[1],1,20)end)
shooter.add("mark","mark","Save a quick session checkpoint.",function()local _,_,r=extras.characterParts();if not r then error("No living character.",0)end;extras.checkpoint=r.CFrame end)
shooter.add("back","back","Return to the saved quick checkpoint.",function()local _,_,r=extras.characterParts();if not r or not extras.checkpoint then error("Save a checkpoint with mark first.",0)end;r.CFrame=extras.checkpoint;r.AssemblyLinearVelocity=Vector3.zero end)
shooter.add("walk","walk 32","Set a local walk-speed override: 0 to 100.",function(a)C.WalkSpeed=shooter.number(a[1],0,100);C.SpeedOverride=true end)
shooter.add("jump","jump 80","Set local jump power: 0 to 150, with jump-height fallback.",function(a)local n=shooter.number(a[1],0,150);C.JumpPower=n;C.JumpHeight=math.clamp(n*n/(2*math.max(1,workspace.Gravity)),0,50);C.JumpOverride=true end)
shooter.add("resetweapon","resetweapon","Restore local recoil, spread, camera-kick, and cooldown settings.",function()C.NoRecoil=false;C.NoSpread=false;C.NoAimKick=false;C.NoCooldown=false;cooldown.restore()end)
shooter.add("stopfire","stopfire","Disable all automatic firing and clicking immediately.",function()C.AutoClicker=false;C.TriggerBot=false;C.AutoShoot=false;autofire.reset("Stopped by command")end)
shooter.add("panic","panic","Disable active toggles and restore properties. F8 also works.",function()extras.panic()end)
heading(pages.Commands,"Command buttons","Each button runs the example shown. Use the command box to choose other values or turn options off.")
for _,name in ipairs(shooter.order) do
    local command=shooter.commands[name]
    local box=row(pages.Commands,"Command_"..name,command.example,command.description,100)
    box.RowTitle.Size=UDim2.new(1,-110,0,22)
    local run=button(box,"Cmd_"..name,"RUN",UDim2.new(1,-86,0,10),UDim2.fromOffset(70,32))
    on(run.Activated,function()shooter.execute(command.example)end)
end
gui:SetAttribute("ShooterCommandCount",#shooter.order)

local bot={epoch=0,active=false,scanAt=0,pathAt=0,moveAt=0,shotAt=0,reloadAt=0,target=nil,waypoints=nil,index=1,pathBusy=false,fireBusy=false,reloadBusy=false,strafesAt=0,strafeSign=1,goal=nil,smoothed=Vector3.zero,random=Random.new(),progressAt=0,unstuckAt=0,pauseUntil=0}
C.Bot=false;C.BotMove=true;C.BotEquip=true;C.BotReload=true;C.BotStrafe=true;C.BotSmooth=true;C.BotRange=1000;C.BotDistance=70;C.BotRate=6;C.BotAimSpeed=16
function bot.status(message)
    if not alive then return end
    gui:SetAttribute("BotStatus",message)
    if bot.row then bot.row.Detail.Text=message end
end
function bot.stopMovement()
    bot.goal=nil;bot.smoothed=Vector3.zero;bot.jumpUntil=0;bot.jumpPlan=nil
    local _,hum,root=extras.characterParts()
    if hum and root then hum:Move(Vector3.zero,false)end
end
function bot.stop(message)
    if bot.setADS then bot.setADS(nil,false)end
    bot.epoch+=1;bot.active=false;bot.target=nil;bot.waypoints=nil;bot.pathBusy=false;bot.fireBusy=false;bot.reloadBusy=false;bot.progressPosition=nil;bot.progressAt=0;bot.pauseUntil=0
    bot.stopMovement();gui:SetAttribute("BotTarget","");bot.status(message or "Disabled. F7 enables bot; F10 stops bot.")
end
function bot.team(p)
    return not LP.Neutral and not p.Neutral and LP.Team~=nil and LP.Team==p.Team
end
function bot.visible(ch,cam)
    local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances={LP.Character,world}
    pcall(function()params.CollisionGroup="Raycast"end)
    for _,name in ipairs({"Head","UpperTorso","Torso","HumanoidRootPart"})do
        local part=ch:FindFirstChild(name)
        if part and part:IsA("BasePart")then
            local direction=part.Position-cam.CFrame.Position
            if direction.Magnitude>.01 then
                local hit=workspace:Raycast(cam.CFrame.Position,direction,params)
                if not hit or hit.Instance:IsDescendantOf(ch)then return part end
            end
        end
    end
end
function bot.choose(root,cam)
    local best,chosen,part=math.huge,nil,nil
    for _,p in ipairs(Players:GetPlayers())do
        if p~=LP and not bot.team(p)then
            local ch=p.Character
            local h=ch and ch:FindFirstChildOfClass("Humanoid")
            local r=ch and ch:FindFirstChild("HumanoidRootPart")
            if h and h.Health>0 and r and not ch:FindFirstChildOfClass("ForceField")then
                local distance=(r.Position-root.Position).Magnitude
                if distance<=C.BotRange then
                    local visible=bot.visible(ch,cam)
                    local score=distance+(visible and 0 or C.BotRange)
                    if p==bot.target then score*=.85 end
                    if score<best then best=score;chosen=p;part=visible end
                end
            end
        end
    end
    return chosen,part
end
function bot.weapon(hum,ch)
    local registry=autofire.registryForGame()
    if not registry then return nil,nil,"Unsupported game: no native weapon adapter."end
    local current=ch:FindFirstChildOfClass("Tool")
    local gun=current and registry.Tools[current]
    if type(gun)=="table" and type(gun.DoFire)=="function" and ((tonumber(current:GetAttribute("Ammo"))or 0)>0 or (tonumber(current:GetAttribute("Mags"))or 0)>0)then return current,gun end
    if C.BotEquip then
        for tool,entry in pairs(registry.Tools)do
            if typeof(tool)=="Instance" and tool:IsA("Tool") and (tool.Parent==LP.Backpack or tool.Parent==ch)and type(entry)=="table" and type(entry.DoFire)=="function"then
                if (tonumber(tool:GetAttribute("Ammo"))or 0)>0 or (tonumber(tool:GetAttribute("Mags"))or 0)>0 then
                    hum:EquipTool(tool);return tool,entry
                end
            end
        end
    end
    return nil,nil,"Equip a supported gun with ammo or reserve magazines."
end
function bot.path(root,destination)
    if bot.pathBusy or os.clock()<bot.pathAt then return end
    bot.pathAt=os.clock()+1.3;bot.pathBusy=true
    local token=bot.epoch
    local target=bot.target
    local start=root.Position
    task.spawn(function()
        local ok,result=pcall(function()
            local path=game:GetService("PathfindingService"):CreatePath({AgentRadius=2,AgentHeight=5,AgentCanJump=true,WaypointSpacing=5})
            path:ComputeAsync(start,destination)
            return path.Status==Enum.PathStatus.Success and path:GetWaypoints()or nil
        end)
        if token~=bot.epoch or not alive or not C.Bot then return end
        bot.pathBusy=false
        if target~=bot.target then return end
        if ok and result and #result>1 then
            bot.waypoints=result;bot.index=2
            gui:SetAttribute("BotPathStatus","Ready: "..#result.." waypoints")
        else bot.waypoints=nil;gui:SetAttribute("BotPathStatus",ok and "No navigable path"or tostring(result):sub(1,120))end
    end)
end
function bot.walk(hum,root,enemy,visible)
    if not C.BotMove then bot.stopMovement();return end
    local now=os.clock()
    if now<bot.moveAt then return end
    bot.moveAt=now+.12
    if bot.jumpPlan then bot.goal=bot.jumpPlan.goal;return end
    if C.BotShortcut and now<(bot.shortcutUntil or 0)and bot.shortcutGoal then bot.goal=bot.shortcutGoal;return end
    local delta=enemy.Position-root.Position
    local horizontal=Vector3.new(delta.X,0,delta.Z)
    if visible and horizontal.Magnitude>.01 then
        local forward=horizontal.Unit
        local distance=horizontal.Magnitude
        local step=distance>C.BotDistance+10 and 9 or distance<C.BotDistance*.5 and -7 or 0
        if now>bot.strafesAt then
            bot.strafesAt=now+(C.BotSmooth and bot.random:NextNumber(.9,1.6)or 1.3)
            bot.strafeUntil=now+(C.BotSmooth and bot.random:NextNumber(.35,.7)or 1.3)
            bot.strafeSign=-bot.strafeSign
        end
        local side=C.BotStrafe and now<(bot.strafeUntil or 0)and Vector3.new(-forward.Z,0,forward.X)*(C.BotSmooth and 3 or 4)*bot.strafeSign or Vector3.zero
        local destination=root.Position+forward*step+side
        local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Exclude;params.FilterDescendantsInstances={LP.Character,world,enemy.Parent}
        local ground=workspace:Raycast(destination+Vector3.new(0,2,0),Vector3.new(0,-12,0),params)
        local block=workspace:Raycast(root.Position,destination-root.Position,params)
        if ground and not block then
            bot.waypoints=nil
            bot.goal=(destination-root.Position).Magnitude>1 and Vector3.new(destination.X,ground.Position.Y,destination.Z)or nil
            return
        end
    end
    if C.BotShortcut then
        local jumped,landing=bot.checkObstacle(hum,root,enemy.Position-root.Position)
        if jumped then bot.shortcutGoal=landing;bot.shortcutUntil=now+.7;bot.goal=landing;return end
    end
    bot.path(root,enemy.Position)
    local waypoint=bot.waypoints and bot.waypoints[bot.index]
    if waypoint then
        local flat=Vector3.new(root.Position.X-waypoint.Position.X,0,root.Position.Z-waypoint.Position.Z)
        if flat.Magnitude<2.5 and math.abs(root.Position.Y-waypoint.Position.Y)<6 then bot.index+=1;waypoint=bot.waypoints[bot.index]end
        if waypoint then
            if waypoint.Action==Enum.PathWaypointAction.Jump then bot.requestJump(hum,"Path waypoint")end
            bot.goal=waypoint.Position
        else bot.stopMovement()end
    else
        if horizontal.Magnitude>1 then bot.goal=root.Position+horizontal.Unit*math.min(8,horizontal.Magnitude)else bot.stopMovement()end
    end
end
function bot.drive(hum,root,dt)
    bot.updateJump(hum,root)
    local now=os.clock()
    local wanted=Vector3.zero
    local distance=0
    if bot.goal and C.BotMove then
        local delta=Vector3.new(bot.goal.X-root.Position.X,0,bot.goal.Z-root.Position.Z)
        distance=delta.Magnitude
        if distance>(bot.jumpPlan and .2 or 1)then wanted=delta.Unit end
    end
    local avoiding=false
    if wanted.Magnitude>.1 then bot.checkObstacle(hum,root,wanted);if not bot.jumpPlan then wanted,avoiding=bot.steer(hum,root,wanted)end end
    if bot.obstacleRow then bot.obstacleRow.Detail.Text=gui:GetAttribute("BotObstacleStatus")or "Ready: scanning low obstacles while moving."end
    if C.BotSmooth and now<bot.pauseUntil then wanted=Vector3.zero end
    if bot.jumpPlan or avoiding then bot.smoothed=wanted
    elseif C.BotSmooth then
        bot.smoothed=bot.smoothed:Lerp(wanted,1-math.exp(-9*math.min(dt,.1)))
    else bot.smoothed=wanted end
    if bot.smoothed.Magnitude<.025 then bot.smoothed=Vector3.zero end
    -- Apply after PlayerModule's render update; MoveTo is canceled by default controls.
    hum:Move(bot.smoothed,false)
    if now<(bot.jumpUntil or 0)and hum.FloorMaterial~=Enum.Material.Air then hum.Jump=true end
    if hum.FloorMaterial==Enum.Material.Air or root.AssemblyLinearVelocity.Y>2 then bot.jumpUntil=0 end
    gui:SetAttribute("BotMovementInput",bot.smoothed.Magnitude)
    gui:SetAttribute("BotPosition",root.Position)
    if now>=bot.progressAt then
        local progress=bot.progressPosition and (root.Position-bot.progressPosition).Magnitude or 10
        if wanted.Magnitude>.5 and distance>4 and progress<.8 and now>=bot.unstuckAt then
            bot.unstuckAt=now+2.5;bot.pathAt=0;bot.waypoints=nil
            if hum.FloorMaterial~=Enum.Material.Air then bot.requestJump(hum,"Obstacle recovery")end
            if C.BotSmooth then bot.pauseUntil=now+.18 end
            gui:SetAttribute("BotNavigation","Blocked: jumping once and computing a new path")
        else gui:SetAttribute("BotNavigation",wanted.Magnitude>.1 and "Following path / progress "..string.format("%.1f",progress).." studs"or "Holding position")end
        bot.progressPosition=root.Position;bot.progressAt=now+1
    end
end
function bot.step(dt,cam)
    if C.EditorOpen then bot.setADS(nil,false);bot.stopMovement();bot.status("Paused for script editor");return end
    if C.Healing then bot.setADS(nil,false);bot.stopMovement();bot.status("Paused for healing");return end
    if not C.Bot then if bot.active then bot.stop()end;return end
    if not bot.active then
        if ui.prepareBot and not ui.prepareBot() then C.Bot=false;bot.stop("Equip a firearm Tool before starting; this game may require a custom weapon adapter.");refresh();return end
        C.BotAvoid=true;C.BotMove=true;C.BotSmooth=true;C.BotStrafe=true;C.BotReload=true;C.BotADS=true;C.BotFollowCamera=true;C.BotObstacleJump=true;C.BotJumpAssist=true
        if ui.enforceCapabilities then ui.enforceCapabilities() end
        bot.active=true;bot.epoch+=1;bot.scanAt=0;bot.pathAt=0
        C.AutoClicker=false;C.TriggerBot=false;C.AutoShoot=false;C.Aim=false
        for _,key in ipairs({"Fly","Spin","AutoWalk","Noclip","AirPlatform","BunnyHop","Hitboxes"})do C[key]=false end
        if C.BotADS then C.CameraOverride=false end
        flight.stop();refresh();showMenu(false)
    end
    if not extras.active()then bot.setADS(nil,false);bot.stopMovement();bot.status("Paused: close the menu, focus the game, and finish typing. F10 stops bot.");return end
    if type(iswindowactive)=="function"then local ok,focused=pcall(iswindowactive);if not ok or not focused then bot.setADS(nil,false);bot.stopMovement();bot.status("Paused: game window is not focused.");return end end
    local ch,hum,root=extras.characterParts()
    if not ch then bot.setADS(nil,false);bot.status("Waiting for a living character.");return end
    if bot.character~=ch then bot.character=ch;bot.epoch+=1;bot.target=nil;bot.waypoints=nil;bot.pathBusy=false;bot.fireBusy=false;bot.reloadBusy=false end
    local tool,gun,message=bot.weapon(hum,ch)
    if not tool then bot.setADS(nil,false);bot.stopMovement();bot.status(message);return end
    if tool:GetAttribute("Ammo")==0 then
        bot.setADS(nil,false)
        bot.stopMovement()
        if C.BotReload and (tonumber(tool:GetAttribute("Mags"))or 0)>0 and type(gun.DoReload)=="function"then
            bot.status("Reloading "..tool.Name.." using available reserve ammo.")
            if not bot.reloadBusy and os.clock()>=bot.reloadAt then
                bot.reloadBusy=true;bot.reloadAt=os.clock()+1
                local token=bot.epoch
                task.spawn(function()
                    local ok,err=pcall(function()if alive and C.Bot and token==bot.epoch then gun:DoReload()end end)
                    if token==bot.epoch then bot.reloadBusy=false;if not ok then bot.status("Reload failed: "..tostring(err):sub(1,100))end end
                end)
            end
        else bot.status("Out of ammo. Bot cannot create server ammunition.")end
        return
    end
    local now=os.clock()
    if now>=bot.scanAt then
        bot.scanAt=now+.2
        local target=bot.choose(root,cam)
        if target~=bot.target then bot.target=target;bot.waypoints=nil;bot.pathAt=0 end
    end
    local enemy=bot.target and bot.target.Character
    local enemyHum=enemy and enemy:FindFirstChildOfClass("Humanoid")
    local enemyRoot=enemy and enemy:FindFirstChild("HumanoidRootPart")
    if not enemyRoot or not enemyHum or enemyHum.Health<=0 then bot.target=nil;bot.setADS(nil,false);bot.stopMovement();bot.status("Searching for a living opponent within "..C.BotRange.." studs.");return end
    gui:SetAttribute("BotTarget",bot.target.Name)
    local part=bot.visible(enemy,cam)
    bot.walk(hum,root,enemyRoot,part~=nil)
    bot.drive(hum,root,dt)
    if not part then
        bot.setADS(nil,false)
        bot.routeCamera(cam,root,dt,enemyRoot.Position)
        local navigation=gui:GetAttribute("BotNavigation")or gui:GetAttribute("BotPathStatus")or "Planning a route"
        bot.status("Seeking "..bot.target.Name.." / "..navigation..". No clear shot.");return
    end
    bot.setADS(gun,true)
    if bot.adsRow then bot.adsRow.Detail.Text=gui:GetAttribute("BotADSStatus")or "Ready"end
    local direction=part.Position-cam.CFrame.Position
    if direction.Magnitude<.01 then return end
    gui:SetAttribute("BotCameraStatus","Tracking visible target")
    cam.CFrame=cam.CFrame:Lerp(CFrame.lookAt(cam.CFrame.Position,part.Position),1-math.exp(-C.BotAimSpeed*dt))
    bot.status("Tracking "..bot.target.Name.." / "..tool.Name..". Native weapon cooldowns apply. F10 stops.")
    if bot.fireBusy or bot.reloadBusy or now<bot.shotAt or cam.CFrame.LookVector:Dot(direction.Unit)<math.cos(math.rad(3))then return end
    bot.shotAt=now+1/math.max(1,C.BotRate);bot.fireBusy=true
    local token=bot.epoch
    task.spawn(function()
        local ok,err=pcall(function()
            if alive and C.Bot and token==bot.epoch and extras.active()and tool.Parent==LP.Character and enemyHum.Health>0 and bot.visible(enemy,cam)then gun:DoFire()end
        end)
        if token==bot.epoch then bot.fireBusy=false;if not ok then bot.status("Native firing failed: "..tostring(err):sub(1,100))end end
    end)
end
heading(pages.Bot,"Auto-play bot","Opt-in target search, camera aiming, native firing, reloading, and pathfinding. Requires a compatible game weapon adapter.")
toggle(pages.Bot,"Bot","Auto-play bot / F7","Starts disabled. Starting enables movement, camera follow, jump assistance and supported ADS/reload, then closes the menu. Reopen the menu to pause. F10 stops only the bot; F8 stops everything.")
toggle(pages.Bot,"BotMove","Bot movement","Use paths when targets are behind cover; approach or retreat to your preferred distance.")
toggle(pages.Bot,"BotEquip","Auto equip compatible gun","Choose an owned, compatible gun with ammo or reserve magazines.")
toggle(pages.Bot,"BotReload","Bot reload","Use the weapon's native reload with existing reserve ammo.")
toggle(pages.Bot,"BotStrafe","Bot strafe","Alternate short side steps while fighting visible targets. Requires Bot movement.")
toggle(pages.Bot,"BotSmooth","Natural movement","Ease direction changes, take shorter side steps, and pause briefly when recovering from obstacles.")
slider(pages.Bot,"BotRange","Opponent search range","Nearest visible opponent is preferred. Teammates and spawn-protected players are skipped.",100,2000,50)
slider(pages.Bot,"BotDistance","Preferred fighting distance","Distance from a visible target, in studs.",15,200,5)
slider(pages.Bot,"BotRate","Bot fire request limit","Maximum attempts per second. Native weapon cooldowns still apply.",1,12,1)
slider(pages.Bot,"BotAimSpeed","Bot aim response","Higher values align the camera faster.",4,35,1)
bot.row=row(pages.Bot,"BotStatus","Bot status","Disabled. F7 toggles bot; F10 stops bot; F8 stops all active toggles.",140)
bot.row.RowTitle.Size=UDim2.new(1,-32,0,22);bot.row.Detail.Size=UDim2.new(1,-32,1,-48)
bot.stopButton=button(pages.Bot,"StopBot","STOP BOT / F10",UDim2.new(),UDim2.new(1,0,0,48))
on(bot.stopButton.Activated,function()C.Bot=false;bot.stop("Stopped by button. F7 enables bot again.");refresh()end)
on(UIS.InputBegan,function(key,processed)
    if processed or UIS:GetFocusedTextBox()then return end
    if key.KeyCode==Enum.KeyCode.F7 then C.Bot=not C.Bot;if not C.Bot then bot.stop("Stopped with F7.")end;refresh()
    elseif key.KeyCode==Enum.KeyCode.F10 then C.Bot=false;bot.stop("Stopped with F10. F8 remains emergency stop.");refresh()end
end)

heading(pages.Aim,"Precision control",mobile and "Close the menu, then hold or tap the AIM pad." or "Configure your targeting. Free cursor pauses aiming.")
toggle(pages.Aim,"Aim","Aim assist","Smooth camera tracking while activated.")
cycle(pages.Aim,"Mode","Activation","Choose hold, toggle, or always active.",mobile and {"Hold AIM","Toggle AIM","Always"} or {"Hold RMB","Toggle RMB","Always"})
cycle(pages.Aim,"Part","Target bone","Automatically falls back for custom rigs.",{"Head","HumanoidRootPart","UpperTorso"})
cycle(pages.Aim,"Origin","FOV origin","Track from your crosshair or cursor.",mobile and {"Screen center"} or {"Screen center","Mouse"})
slider(pages.Aim,"FOV","Field of view","Target selection radius in screen pixels.",40,600,10)
slider(pages.Aim,"Speed","Response speed","Higher values follow targets faster; independent of frame rate.",2,100,1)
cycle(pages.Aim,"AimPriority","Target priority","Choose the closest crosshair, nearest player, or lowest health.",{"Crosshair","Nearest","Lowest health"})
cycle(pages.Aim,"AimResponse","Tracking response","Smooth follows at your response speed. Instant points directly at the target.",{"Smooth","Instant"})
toggle(pages.Aim,"AimFallback","Visible body-part fallback","Try head and torso alternatives when the selected part is blocked.")
toggle(pages.Aim,"AimPrediction","Movement prediction","Optional velocity lead; leave disabled for hitscan guns.")
slider(pages.Aim,"AimLead","Prediction lead","Milliseconds of motion lead when prediction is enabled.",0,200,10)
slider(pages.Aim,"AimRetention","Target retention strength","Prefer your current target without permanently locking it.",0,80,5)
local responsive=button(pages.Aim,"ResponsiveAim","APPLY RESPONSIVE AIM PRESET",UDim2.new(),UDim2.new(1,0,0,48))
on(responsive.Activated,function()
    C.Aim=true;C.Speed=32;C.FOV=250;C.Sticky=true;C.AimRetention=35
    C.AimPriority="Crosshair";C.AimFallback=true;C.AimResponse="Smooth";C.AimPrediction=false
    selected=nil;refresh();notify("Aim preset","Enabled. Close the menu and use your selected activation mode.")
end)

slider(pages.Aim,"Range","Detection range","Maximum aim distance; ESP has separate distance controls.",100,5000,50)
toggle(pages.Aim,"AimThroughWalls","Aim through walls","Track players behind walls. Does not guarantee shots pass through them.")
toggle(pages.Aim,"Visibility","Visibility check","Skip blocked targets while Aim through walls is disabled.")
toggle(pages.Aim,"Teams","Ignore teammates","Neutral players remain eligible.")
toggle(pages.Aim,"Sticky","Target retention","Stay on one target while it remains valid.")
toggle(pages.Aim,"Circle","FOV circle","Display your targeting radius.")
heading(pages.Visuals,"Visual intelligence","Fine-tune character highlights and player information.")
toggle(pages.Visuals,"ESP","Character highlights","Outline player characters available on your client.")
toggle(pages.Visuals,"ESPAll","Show every player","Include teammates independently of your aim team filter.")
toggle(pages.Visuals,"ESPUnlimited","Unlimited ESP distance","Remove the aim and label distance limits from ESP.")
slider(pages.Visuals,"ESPInterval","ESP refresh interval","Milliseconds between text updates. Outlines follow players every frame.",25,200,25)
toggle(pages.Visuals,"Names","Identity labels","Display name and username.")
toggle(pages.Visuals,"Health","Health display","Health bar with current and maximum HP.")
toggle(pages.Visuals,"Distance","Distance markers","Player distance measured in studs.")
toggle(pages.Visuals,"Through","Through walls","Keep highlights and labels visible behind geometry.")
slider(pages.Visuals,"Opacity","Fill transparency","Solid fill at 0. Outline only at 100.",0,100,5)
cycle(pages.Visuals,"Color","Visual accent","Choose the highlight and FOV color.",{"Background","Silver","Violet","Cyan","Rose","Lime"})
heading(pages.Interface,"Interface","Liquid chrome / "..deviceName.." controls")


local appliedTheme=nil
applyTheme=function(name)
    if appliedTheme==name then return end
    local profile=themeProfiles[name] or themeProfiles.Chrome
    appliedTheme=name;currentTheme=profile
    ink=profile.primary;muted=profile.muted;palettes.Background=profile.accent
    gui:SetAttribute("ThemePrimary",ink);gui:SetAttribute("ThemeMuted",muted);gui:SetAttribute("ThemeAccent",profile.accent)
    for _,object in ipairs(gui:GetDescendants())do
        if object:IsA("TextLabel") or object:IsA("TextButton") then
            local role=object:GetAttribute("ThemeRole")
            if role then animate(object,{TextColor3=role=="Muted" and muted or ink},.34) end
        elseif object:IsA("GuiObject") and object:GetAttribute("ThemeFill")=="Primary" then
            animate(object,{BackgroundColor3=ink},.34)
        elseif object:IsA("UIStroke") and object:GetAttribute("ThemeEdge") then
            animate(object,{Color=profile.accent},.34)
        end
    end
    chrome.Color=ColorSequence.new({
        ColorSequenceKeypoint.new(0,ink),ColorSequenceKeypoint.new(.45,profile.accent:Lerp(Color3.new(0,0,0),.32)),
        ColorSequenceKeypoint.new(.54,ink),ColorSequenceKeypoint.new(1,profile.accent:Lerp(Color3.new(0,0,0),.48))})
    local gradient=border:FindFirstChildOfClass("UIGradient")
    if gradient then gradient.Color=ColorSequence.new({
        ColorSequenceKeypoint.new(0,profile.accent:Lerp(Color3.new(0,0,0),.65)),
        ColorSequenceKeypoint.new(.28,ink),
        ColorSequenceKeypoint.new(.55,profile.accent:Lerp(Color3.new(0,0,0),.7)),
        ColorSequenceKeypoint.new(1,profile.accent)}) end
end

local function chooseBackground(name)
    if not backgroundIndex[name] then return end
    C.Background=name;refresh()
    if notify then notify("Background",name) end
end
local function stepBackground(delta)
    local at=backgroundIndex[C.Background] or 1
    chooseBackground(backgroundList[(at-1+delta)%#backgroundList+1].name)
end
local prevBG=button(root,"PreviousBackground","‹",UDim2.new(1,-100,0,88),UDim2.fromOffset(32,28))
local nextBG=button(root,"NextBackground","›",UDim2.new(1,-56,0,88),UDim2.fromOffset(32,28))
prevBG.Visible=not mobile;nextBG.Visible=not mobile
heroTag.Position=UDim2.new(1,-290,0,90);heroTag.Size=UDim2.fromOffset(175,24)
on(prevBG.Activated,function()stepBackground(-1)end)
on(nextBG.Activated,function()stepBackground(1)end)
switchBackground=function(name)
    local entry=backgroundList[backgroundIndex[name] or 1]
    heroTag.Text=entry.name:upper()
    if applyTheme then applyTheme(entry.name) end
    if entry.name==activeBackground then return end
    local nextImage=backgroundAsset(entry)
    if nextImage=="" then
        gui:SetAttribute("BackgroundError","Background asset unavailable: "..entry.name)
        return
    end
    liquid.Image=liquidSwap.Image;liquid.ScaleType=liquidSwap.ScaleType
    liquidSwap.Image=nextImage
    liquidSwap.ScaleType=entry.stretch and Enum.ScaleType.Stretch or Enum.ScaleType.Crop
    mist.Image=nextImage;mist.ScaleType=liquidSwap.ScaleType
    image=nextImage;activeBackground=entry.name
    backgroundMix.Value=0;animate(backgroundMix,{Value=1},.5)
    gui:SetAttribute("BackgroundError",nil)
    gui:SetAttribute("BackgroundAsset",entry.file)
end
local columns=mobile and 2 or 3
local gridRows=math.ceil(#backgroundList/columns)
local gridHeight=gridRows*82+(gridRows-1)*8
local gallery=make("Frame",{Name="BackgroundGallery",BackgroundTransparency=1,
    Size=UDim2.new(1,0,0,gridHeight+32)},pages.Interface)
text(gallery,"CHOOSE YOUR BACKGROUND",10,muted,UDim2.new(),UDim2.new(1,0,0,22))
local grid=make("Frame",{Name="Previews",BackgroundTransparency=1,Position=UDim2.fromOffset(0,30),
    Size=UDim2.new(1,0,0,gridHeight)},gallery)
make("UIGridLayout",{CellSize=UDim2.new(1/columns,-8*(columns-1)/columns-2,0,82),
    CellPadding=UDim2.fromOffset(8,8),FillDirectionMaxCells=columns,
    SortOrder=Enum.SortOrder.LayoutOrder},grid)
for i,entry in ipairs(backgroundList)do
    local preview=button(grid,entry.name,"",UDim2.new(),UDim2.fromOffset(150,82))
    preview.LayoutOrder=i
    local photo=make("ImageLabel",{Name="PreviewImage",BackgroundTransparency=1,
        Image=backgroundAsset(entry),Size=UDim2.fromScale(1,1),
        ScaleType=entry.stretch and Enum.ScaleType.Stretch or Enum.ScaleType.Crop},preview)
    local strip=make("Frame",{BackgroundColor3=Color3.fromRGB(9,10,14),BackgroundTransparency=.12,
        BorderSizePixel=0,Position=UDim2.new(0,0,1,-25),Size=UDim2.new(1,0,0,25)},preview)
    local caption=text(strip,entry.name,11,ink,UDim2.fromOffset(9,0),UDim2.new(1,-30,1,0))
    local check=text(strip,"✓",12,ink,UDim2.new(1,-23,0,0),UDim2.fromOffset(20,25))
    local selectedFrame=make("Frame",{Name="Selection",BackgroundTransparency=1,
        Position=UDim2.fromOffset(1,1),Size=UDim2.new(1,-2,1,-2),ZIndex=2},preview);round(selectedFrame,9)
    local selectedEdge=outline(selectedFrame,1);selectedEdge.Color=ink;selectedEdge.Thickness=1.5
    table.insert(paints,function()
        local chosen=C.Background==entry.name
        preview:SetAttribute("Selected",chosen)
        check.Visible=chosen
        animate(selectedEdge,{Transparency=chosen and .05 or 1},.2)
        caption:SetAttribute("ThemeRole",chosen and "Primary" or "Muted")
        animate(caption,{TextColor3=chosen and ink or muted},.34)
    end)
    on(preview.Activated,function()chooseBackground(entry.name)end)
end
gui:SetAttribute("BackgroundAsset",initialBackground.file)

toggle(pages.Interface,"Motion","Interface animations","Background drift, chrome shimmer, and transitions.")
slider(pages.Interface,"Backdrop","Backdrop intensity","Adjust the brightness of your liquid-metal image.",0,100,5)
-- Visibility and label tuning.
slider(pages.Visuals,"OutlineFade","Outline transparency","0 = solid outline; 100 = hidden outline.",0,100,5)
slider(pages.Visuals,"LabelSize","Label text size","Resize player names, distance, and health text.",10,20,1)
slider(pages.Visuals,"LabelHeight","Label height","Move player labels above their characters.",2,8,.5)
slider(pages.Visuals,"LabelRange","Label distance","Limit information labels separately from highlights.",100,5000,100)
heading(pages.World,"World & lighting","Local visual adjustments. Original settings restore on unload.")
toggle(pages.World,"FullBright","Full Bright","Bright ambient light with shadows removed.")
toggle(pages.World,"NoFog","Clear fog & atmosphere","Remove fog, haze, density, and atmospheric glare.")
toggle(pages.World,"NoShadows","Remove shadows","Disable global shadows independently.")
toggle(pages.World,"TimeLock","Lock time of day","Keep the sun at your chosen time.")
slider(pages.World,"DayTime","Time of day","0 = midnight; 12 = noon; 18 = evening.",0,24,.5)
toggle(pages.World,"CustomLight","Custom lighting","Enable the three controls below. Full Bright takes priority.")
slider(pages.World,"LightBrightness","Brightness","Applied when Custom lighting is enabled.",0,10,.5)
slider(pages.World,"LightExposure","Exposure","Adjust scene exposure with Custom lighting enabled.",-3,3,.1)
slider(pages.World,"AmbientLevel","Ambient light","Ambient fill strength with Custom lighting enabled.",0,100,5)
toggle(pages.World,"NoBloom","Disable bloom","Remove bright bloom glow.")
toggle(pages.World,"NoBlur","Disable blur","Remove BlurEffect post-processing.")
toggle(pages.World,"NoDOF","Disable depth of field","Remove near / far camera blur.")
toggle(pages.World,"NoSunRays","Disable sun rays","Remove post-processing sun rays.")
toggle(pages.World,"NoColor","Disable color filters","Remove game ColorCorrectionEffect filters.")
toggle(pages.World,"CameraOverride","Camera FOV override","Use your selected camera field of view.")
slider(pages.World,"CameraFOV","Camera field of view","Camera zoom angle; separate from aim selection radius.",40,120,5)
local worldKeys={"FullBright","CustomLight","TimeLock","NoShadows","NoFog","NoBloom","NoBlur","NoSunRays","NoColor","NoDOF","CameraOverride"}
local restoreButton=button(pages.World,"RestoreWorld","RESTORE ORIGINAL WORLD",UDim2.new(),UDim2.new(1,0,0,44))
on(restoreButton.Activated,function()
    for _,key in ipairs(worldKeys) do C[key]=false end
    if restoreWorld then restoreWorld() end
    refresh()
end)
toggle(pages.Interface,"ShowStatus","Status footer","Show tracking and player-count information.")
toggle(pages.Interface,"ShowFPS","FPS performance panel","Live FPS, frame time, ping, and a recent performance graph.")
toggle(pages.Interface,"FPSDetails","FPS details","Show frame time and ping. Clicking the FPS panel also switches this.")
toggle(pages.Interface,"FPSGraph","FPS history graph","Show the most recent six seconds of sampled FPS.")
cycle(pages.Interface,"FPSCorner","FPS position","Choose the screen corner for your performance panel.",{"Top right","Top left","Bottom right","Bottom left"})
toggle(pages.Interface,"Watermark","NOVA watermark","Show the new Nova brand panel with version and session information.")
toggle(pages.Interface,"WatermarkDetails","Watermark details","Show device, player count, and session time. Click the watermark to switch.")
cycle(pages.Interface,"WatermarkCorner","Watermark position","Panels automatically stack when you select the same corner.",{"Top left","Top right","Bottom left","Bottom right"})
slider(pages.Interface,"HUDScale","Overlay size","Scale the FPS and watermark panels; automatically fit narrow screens.",75,125,5)
slider(pages.Interface,"HUDOpacity","Overlay opacity","Background opacity for both panels.",20,100,5)
toggle(pages.Interface,"QuickCursor","Floating cursor button","PC: show the extra mouse-unlock button.")
toggle(pages.Interface,"ShowAimPad","Touch AIM pad","Mobile: show or hide the gameplay AIM pad.")
slider(pages.Interface,"AimPadSize","Touch pad size","Mobile AIM button diameter in pixels.",56,112,4)
slider(pages.Interface,"UISize","Desktop panel size","Desktop scale percentage; stays within the screen.",75,125,5)
toggle(pages.Interface,"HideRingMenu","Hide FOV in menu","Keep the circle out of the way while adjusting controls.")
toggle(pages.Interface,"Crosshair","Custom crosshair","Show a crosshair at the camera center during play.")
cycle(pages.Interface,"CrosshairStyle","Crosshair style","Plus, diagonal cross, or a single center dot.",{"Plus","Cross","Dot"})
slider(pages.Interface,"CrosshairSize","Crosshair size","Length of the crosshair arms, in pixels.",3,24,1)
slider(pages.Interface,"CrosshairGap","Crosshair gap","Space between the center and crosshair arms.",0,12,1)
slider(pages.Interface,"AnimationSpeed","Animation speed","50 = slower; 100 = normal; 175 = faster.",50,175,25)
toggle(pages.Interface,"Parallax","Cursor parallax","Desktop: reflections gently follow the cursor.")
toggle(pages.Interface,"Notifications","Setting notifications","Show a small confirmation when changing a toggle.")
local preset={previous=nil,profile="Not applied"}
function preset.describe(message)
    preset.status.Detail.Text=message
    gui:SetAttribute("GamePresetStatus",message)
end
function preset.detect()
    local tool=utility.current()
    local gun=tool and utility.gun(tool)
    local settings=type(gun)=="table" and gun.Settings
    if type(settings)~="table"then
        local registry=autofire.registryForGame()
        for candidate,entry in pairs(registry and registry.Tools or {})do
            if typeof(candidate)=="Instance"and candidate:IsA("Tool")and candidate.Parent==LP.Backpack and type(entry)=="table"and type(entry.Settings)=="table"and type(entry.DoFire)=="function"then
                tool=candidate;gun=entry;settings=entry.Settings;break
            end
        end
    end
    local supported=type(settings)=="table"
    local kind="General shooter"
    if supported then
        local name=tool.Name:lower()
        if settings.ShotgunReload or settings.ShotgunPumpEnabled or (tonumber(settings.BulletAmount)or 1)>1 or name:find("shotgun",1,true)or name:find("sawn",1,true)or name:find("870",1,true)then kind="Shotgun"
        elseif name:find("barrett",1,true)or name:find("sniper",1,true)or name:find("awm",1,true)then kind="Precision"
        elseif type(settings.Modes)=="table"and table.find(settings.Modes,"Automatic")then kind="Automatic"
        else kind="Single fire"end
    end
    return tool,gun,settings,kind
end
function preset.apply()
    local tool,gun,settings,kind=preset.detect()
    local values={Aim=true,Mode="Always",AimResponse="Smooth",AimPrediction=false,
        AimFallback=true,AimPriority="Crosshair",Sticky=true,AimRetention=35,Visibility=true,AimThroughWalls=false,Teams=true,
        ESP=true,ESPAll=true,Names=true,Health=true,Distance=true,Through=true,Opacity=100,OutlineFade=10,
        Crosshair=true,CrosshairStyle="Plus",CrosshairSize=6,CrosshairGap=3,LabelSize=12,
        Hitboxes=true,HitboxDisplay=true,HitboxSize=12,HitboxPart="Head",HitboxOpacity=75,HitboxTeams=false,
        WallShots=false,NoCooldown=false,InfiniteAmmo=false,AutoShoot=false,TriggerBot=false,AutoClicker=false,Bot=false,
        NoRecoil=false,NoSpread=false,NoAimKick=false,AutoReload=false,FireRate=6,FireDelay=80,FireAngle=2.5,
        BotSmooth=true,BotStrafe=true,BotMove=true,BotReload=true,BotEquip=true,BotRange=1000,BotDistance=70,BotRate=6,
        InputSensitivity=false,FOV=150,Speed=20,Part="UpperTorso"}
    if type(settings)=="table"then
        values.NoRecoil=type(settings.RecoilForce)=="number"or type(settings.CameraRecoilForce)=="number"
        local spread=settings.BulletSpread
        values.NoSpread=type(spread)=="number"or type(spread)=="table"and type(spread[1])=="number"and type(spread[2])=="number"
        values.NoAimKick=type(settings.RecoilFOV)=="number"or type(settings.CameraRecoilFOV)=="number"
        values.AutoReload=type(gun.DoReload)=="function"
        values.AutoShoot=type(gun.DoFire)=="function";values.TriggerBot=values.AutoShoot
        values.InfiniteAmmo=type(settings.Ammo)=="number"and type(tool:GetAttribute("Ammo"))=="number"
        values.WallShots=type(gun.Raycast)=="function"
        values.AimThroughWalls=values.WallShots;values.Visibility=not values.WallShots
        for _,key in ipairs(cooldown.keys)do if type(settings[key])=="number"then values.NoCooldown=true;break end end
    end
    if kind=="Shotgun"then values.FOV=200;values.Speed=26;values.Part="UpperTorso";values.BotDistance=35;values.BotRate=3
    elseif kind=="Precision"then values.FOV=85;values.Speed=12;values.Part="Head";values.BotDistance=140;values.BotRate=2
    elseif kind=="Automatic"then values.FOV=165;values.Speed=25;values.BotDistance=60
    elseif kind=="Single fire"then values.FOV=125;values.Speed=18;values.Part="Head";values.BotDistance=80 end
    -- Keep the first pre-preset snapshot until restored; repeated clicks do not erase it.
    if not preset.previous then preset.previous={};for key in pairs(values)do preset.previous[key]=C[key]end end
    for key,value in pairs(values)do C[key]=value end
    bot.stop("Game preset applied; bot remains off.");autofire.reset("Game preset applied");cooldown.restore()
    refresh()
    preset.profile=(game.PlaceId==126210570846701 and "Sanguine Fields"or (game.PlaceId==120189115846709 or game.GameId==10090256806) and "TTK Testing"or "Detected game").." / "..kind
    gui:SetAttribute("GamePresetProfile",preset.profile)
    gui:SetAttribute("GamePresetWeapon",tool and tool.Name or "No equipped gun")
    local enabled={};for _,entry in ipairs({{"AutoShoot","auto shoot"},{"TriggerBot","trigger bot"},{"Hitboxes","hitboxes"},{"InfiniteAmmo","local ammo"},{"NoRecoil","no recoil"},{"NoSpread","no spread"},{"NoAimKick","no FOV kick"},{"NoCooldown","no cooldown"},{"WallShots","local wall shots"},{"AutoReload","auto reload"}})do if C[entry[1]]then table.insert(enabled,entry[2])end end
    gui:SetAttribute("GamePresetEnabled",table.concat(enabled,", "))
    preset.describe(preset.profile..(tool and " / "..tool.Name or "")..". ON: "..table.concat(enabled,", ")..". Equip a gun and close the menu to engage. Ammo and wall-shot server acceptance remain unverified.")
    notify("Combat preset applied",kind.." / "..#enabled.." combat controls enabled.")
end
function preset.restore()
    if not preset.previous then preset.describe("No preset snapshot yet. Apply Game Preset first.");return end
    for key,value in pairs(preset.previous)do C[key]=value end
    preset.previous=nil;preset.profile="Restored"
    refresh();gui:SetAttribute("GamePresetProfile","Restored")
    preset.describe("Your settings from before the preset have been restored.")
    notify("Settings restored","Returned to your pre-preset settings.")
end
preset.quick=button(gui,"ApplyGamePresetQuick","AIM/FIRE PRESET",UDim2.new(1,-16,.5,28),UDim2.fromOffset(174,44))
preset.quick.AnchorPoint=Vector2.new(1,0);preset.quick.ZIndex=25
on(preset.quick.Activated,function()preset.apply()end)
heading(pages.Settings,"Game preset","Enable supported combat features together, including automatic aiming and shooting. Close the menu to engage. Apply again after switching weapon types.")
preset.applyButton=button(pages.Settings,"ApplyGamePreset","APPLY AIM/FIRE SETTINGS (BOT STAYS OFF)",UDim2.new(),UDim2.new(1,0,0,48))
on(preset.applyButton.Activated,function()preset.apply()end)
preset.restoreButton=button(pages.Settings,"RestoreGamePreset","RESTORE PREVIOUS SETTINGS",UDim2.new(),UDim2.new(1,0,0,44))
on(preset.restoreButton.Activated,preset.restore)
preset.status=row(pages.Settings,"GamePresetStatus","Game preset status","Not applied. Press Game Preset to enable supported combat controls together.",230)
preset.status.RowTitle.Size=UDim2.new(1,-32,0,22);preset.status.Detail.Size=UDim2.new(1,-32,1,-48)

local healing={busy=false,epoch=0}
C.Healing=false
function healing.status(message)
    if not alive then return end
    gui:SetAttribute("HealStatus",message)
    if healing.row then healing.row.Detail.Text=message end
    if healing.quick then healing.quick.Text=C.Healing and "HEALING..."or "HEAL TO FULL"end
end
function healing.find()
    local best,score=nil,0
    for _,root in ipairs({LP.Backpack,LP.Character})do if root then for _,tool in ipairs(root:GetChildren())do
        if tool:IsA("Tool")then
            local name=tool.Name:lower()
            local rank=(name:find("first aid",1,true)or name:find("medkit",1,true))and 3 or name:find("healing potion",1,true)and 2 or name=="bandage"and 1 or 0
            if rank>score and tool:FindFirstChild("Settings")then best=tool;score=rank end
        end
    end end end
    return best
end
function healing.wait(seconds,token,hum,tool)
    local finish=os.clock()+seconds
    repeat
        if not alive or not C.Healing or healing.epoch~=token or hum.Health<=0 or hum.Parent~=LP.Character or tool and tool.Parent~=LP.Character then return false end
        task.wait(.05)
    until os.clock()>=finish
    return true
end
function healing.release()
    if healing.signal then pcall(function()healing.signal:Fire(false)end);healing.signal=nil end
    if healing.controller and type(healing.controller.DoStop)=="function"then pcall(healing.controller.DoStop,healing.controller)end
    healing.controller=nil
    Run:UnbindFromRenderStep(binding.."_HealSelf")
    if healing.camera and healing.camera.Parent and healing.cameraFrame then healing.camera.CFrame=healing.cameraFrame end
    healing.camera=nil;healing.cameraFrame=nil
end
function healing.run()
    if healing.busy then healing.status("Healing is already in progress. Use Cancel heal to stop.");return end
    local ch,hum=extras.characterParts()
    if not hum then healing.status("Wait for a living character. This button cannot revive a dead character.");return end
    if hum.Health>=hum.MaxHealth then healing.status("Already at full health: "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth));return end
    if game.PlaceId~=126210570846701 then healing.status("This game has no verified healing adapter. The button cannot grant server health.");return end
    if not healing.find()then healing.status("No healing items. Obtain a healing potion, first aid kit, or bandage first.");return end
    healing.busy=true;C.Healing=true;healing.epoch+=1;local token=healing.epoch
    local original=ch:FindFirstChildOfClass("Tool")
    local equipped
    task.spawn(function()
        local ok,err=pcall(function()
            local network=require(game.ReplicatedStorage.Shared.Modules.Dependencies.Network)
            local manager=require(game.ReplicatedStorage.Client.Modules.Managers.ConsumableManager)
            local attempted={}
            for attempt=1,8 do
                if not alive or not C.Healing or token~=healing.epoch or hum.Health<=0 then break end
                if hum.Health>=hum.MaxHealth then healing.status("Full health confirmed: "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth));return end
                local tool=healing.find()
                if not tool then healing.status("No healing items left. Health: "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth));return end
                if attempted[tool]then healing.status("The healing item was not consumed. Try using it manually or obtain another item.");return end
                attempted[tool]=true
                local settings=require(tool.Settings)
                local equip=math.clamp(tonumber(settings.EquipTime)or 1,.15,10)
                local use=math.clamp(tonumber(settings.UseSelfTime)or 3,.2,30)
                local before=hum.Health
                equipped=tool;hum:EquipTool(tool)
                healing.status("Using "..tool.Name.." on yourself. Health "..math.floor(before).." / "..math.floor(hum.MaxHealth))
                if not healing.wait(equip+.25,token,hum,tool)then healing.status("Healing canceled during equip.");return end
                local controller=manager.Tools[tool]
                if type(controller)~="table"or type(controller.DoStart)~="function"then healing.status("This item has no ready native consumable controller. Re-equip it and try again.");return end
                healing.controller=controller
                -- Aim at open space while the native controller chooses self vs another player.
                local cam=workspace.CurrentCamera
                healing.camera=cam;healing.cameraFrame=cam.CFrame
                local up=CFrame.lookAt(cam.CFrame.Position,cam.CFrame.Position+Vector3.new(0,1,.001))
                Run:BindToRenderStep(binding.."_HealSelf",Enum.RenderPriority.Last.Value+200,function()if alive and C.Healing then cam.CFrame=up end end)
                if not healing.wait(.1,token,hum,tool)then healing.status("Healing canceled.");return end
                healing.signal=network.Signals.MobileAttack
                healing.signal:Fire(true)
                if not healing.wait(.15,token,hum,nil)then healing.status("Healing canceled.");return end
                Run:UnbindFromRenderStep(binding.."_HealSelf");cam.CFrame=healing.cameraFrame;healing.camera=nil;healing.cameraFrame=nil
                local mouseGui=LP.PlayerGui:FindFirstChild("Mouse")
                local markers=mouseGui and mouseGui:FindFirstChild("Markers")
                local indicator=markers and markers:FindFirstChild("Consumable")
                if indicator and indicator.Visible and not indicator.Text:find(" ON "..LP.Name:upper(),1,true)then
                    healing.status("Native action selected another player. Healing canceled; aim at open space and retry.");return
                end
                if not healing.wait(use+.5,token,hum,nil)then healing.status("Healing canceled.");return end
                healing.release()
                if not healing.wait(3,token,hum,nil)then healing.status("Healing canceled.");return end
                if hum.Health<=before then healing.status("Healing was not confirmed by the game. Health "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth));return end
                if tool.Parent==ch and hum.Health<hum.MaxHealth then
                    healing.status("Healing applied. Wait for item removal before using another item.");return
                end
            end
            healing.status(hum.Health>=hum.MaxHealth and "Full health confirmed."or "Healing stopped. Health "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth))
        end)
        healing.release()
        C.Healing=false;healing.busy=false
        if not ok then healing.status("Healing unavailable: "..tostring(err):sub(1,140))end
        if alive and LP.Character==ch and hum.Health>0 then
            if not ch:FindFirstChildOfClass("Tool")or ch:FindFirstChildOfClass("Tool")==equipped then
                if original and original.Parent==LP.Backpack then hum:EquipTool(original)
                elseif equipped and equipped.Parent==ch then hum:UnequipTools()end
            end
        end
        if alive then healing.quick.Text="HEAL TO FULL"end
    end)
end
function healing.cancel()
    C.Healing=false;healing.epoch+=1;healing.release();healing.status("Healing canceled. No further items will be used.")
end
healing.quick=button(gui,"HealFullQuick","HEAL TO FULL",UDim2.new(1,-16,.5,78),UDim2.fromOffset(174,44))
healing.quick.AnchorPoint=Vector2.new(1,0);healing.quick.ZIndex=25
on(healing.quick.Activated,healing.run)
heading(pages.Combat,"Healing","Use owned healing items on yourself. Health is confirmed from the game; healing cannot be granted without game support.")
healing.button=button(pages.Combat,"HealFull","HEAL TO FULL",UDim2.new(),UDim2.new(1,0,0,48))
on(healing.button.Activated,healing.run)
healing.cancelButton=button(pages.Combat,"CancelHeal","CANCEL HEAL",UDim2.new(),UDim2.new(1,0,0,40))
on(healing.cancelButton.Activated,healing.cancel)
healing.row=row(pages.Combat,"HealStatus","Healing status","Ready. Healing uses items in your inventory; it cannot create health or supplies.",130)
healing.row.RowTitle.Size=UDim2.new(1,-32,0,22);healing.row.Detail.Size=UDim2.new(1,-32,1,-48)

local defense={elapsed=0,retryAt=0}
C.DefenseAssist=false;C.DefenseThreshold=85
function defense.status(message)
    if not alive then return end
    gui:SetAttribute("DefenseStatus",message)
    if defense.row then defense.row.Detail.Text=message end
end
function defense.toggle()
    C.DefenseAssist=not C.DefenseAssist;defense.retryAt=0;refresh()
    defense.status(C.DefenseAssist and "Enabled. Healing items will be used below "..C.DefenseThreshold.."% health. Incoming damage is unchanged."or "Disabled. No additional automatic healing will start.")
end
function defense.tick(dt)
    defense.elapsed+=dt;if defense.elapsed<.4 then return end;defense.elapsed=0
    if not C.DefenseAssist then defense.status("Disabled. This feature uses owned healing supplies; it does not grant damage immunity.");return end
    local _,hum=extras.characterParts()
    if not hum then defense.status("Waiting for a living character. Cannot prevent death or revive you.");return end
    if game.PlaceId~=126210570846701 then defense.status("Unavailable: no verified healing adapter for this game.");return end
    if C.Healing or healing.busy then defense.status("Healing in progress. Automatic firing and bot actions are paused.");return end
    local ratio=hum.Health/math.max(1,hum.MaxHealth)*100
    if ratio>=C.DefenseThreshold then
        defense.status("Ready: "..math.floor(hum.Health).." / "..math.floor(hum.MaxHealth).." HP. Heal trigger "..C.DefenseThreshold.."%. Incoming damage is unchanged.");return
    end
    if not healing.find()then defense.status("Low health, but no healing supplies remain. Obtain a first aid kit, potion, or bandage.");return end
    if os.clock()<defense.retryAt then return end
    defense.retryAt=os.clock()+10
    defense.status("Low health: using a healing item. This takes the game's normal use time.")
    healing.run()
end
defense.quick=button(gui,"DefenseAssistQuick","DEFENSE: OFF",UDim2.new(1,-16,.5,128),UDim2.fromOffset(174,44))
defense.quick.AnchorPoint=Vector2.new(1,0);defense.quick.ZIndex=25
on(defense.quick.Activated,defense.toggle)
table.insert(paints,function()
    defense.quick.Text="DEFENSE: "..(C.DefenseAssist and "ON"or "OFF")
    defense.quick.BackgroundColor3=C.DefenseAssist and Color3.fromRGB(28,133,94)or Color3.fromRGB(31,34,43)
end)
heading(pages.Combat,"Defense assist","Automatic use of owned healing supplies. Server damage and death rules are unchanged.")
toggle(pages.Combat,"DefenseAssist","Defense assist","Use a healing item when health falls below the selected percentage. F8 stops it.")
slider(pages.Combat,"DefenseThreshold","Auto-heal health threshold","Percentage of maximum health that triggers automatic healing.",25,95,5)
defense.row=row(pages.Combat,"DefenseStatus","Defense status","Disabled. No damage reduction or invulnerability is granted.",140)
defense.row.RowTitle.Size=UDim2.new(1,-32,0,22);defense.row.Detail.Size=UDim2.new(1,-32,1,-48)
on(Run.Heartbeat,function(dt)
    local ok,err=pcall(defense.tick,dt)
    if not ok and alive then C.DefenseAssist=false;refresh();defense.status("Defense assist stopped: "..tostring(err):sub(1,100))end
end)

local studio={job=0,running=false,thread=nil,lines={},layoutKey="",drag=nil}
C.EditorOpen=false
function studio.log(message)
    if not alive then return end
    table.insert(studio.lines,tostring(message):sub(1,1200))
    while #studio.lines>40 do table.remove(studio.lines,1)end
    studio.output.Text=table.concat(studio.lines,"\n")
    gui:SetAttribute("ExecutorOutput",studio.lines[#studio.lines])
end
function studio.layout(viewport)
    local key=tostring(viewport)
    if studio.layoutKey==key then return end;studio.layoutKey=key
    local width=math.max(280,math.min(880,viewport.X-24))
    local height=math.max(320,math.min(670,viewport.Y-36))
    studio.window.Size=UDim2.fromOffset(width,height)
end
function studio.show(value)
    if value then
        showMenu(false);C.EditorOpen=true;setFree(true);studio.window.Visible=true
        studio.window.Position=UDim2.fromScale(.5,.5)
        studio.layout(workspace.CurrentCamera.ViewportSize)
    else
        C.EditorOpen=false;studio.window.Visible=false;studio.code:ReleaseFocus(false);showMenu(true)
    end
    gui:SetAttribute("EditorOpen",C.EditorOpen)
end
function studio.run()
    if studio.running then studio.log("An editor task is already running. Stop it before running another.");return end
    if studio.code.Text:match("^%s*$")then studio.log("Paste your Luau code first.");return end
    if type(loadstring)~="function"then studio.log("This executor does not provide loadstring.");return end
    local fn,err=loadstring(studio.code.Text,"=Nova Script Studio")
    if not fn then studio.log("Compile error: "..tostring(err));return end
    studio.job+=1;local job=studio.job;studio.running=true
    studio.state.Text="RUNNING";gui:SetAttribute("ExecutorRunning",true)
    studio.log("Running script...")
    if type(setfenv)=="function"and type(getfenv)=="function"then
        local base=getfenv(fn)
        local function capture(prefix,...)
            local args=table.pack(...);local out={}
            for i=1,args.n do out[i]=tostring(args[i])end
            if studio.job==job then studio.log(prefix..table.concat(out,"\t"))end
        end
        local environment=setmetatable({print=function(...)capture("",...)end,warn=function(...)capture("WARN: ",...)end},{__index=base,__newindex=base})
        setfenv(fn,environment)
    end
    studio.thread=task.spawn(function()
        local result=table.pack(xpcall(fn,traceError))
        if job~=studio.job or not alive then return end
        studio.running=false;studio.thread=nil;studio.state.Text=result[1]and "FINISHED"or "ERROR"
        gui:SetAttribute("ExecutorRunning",false)
        if not result[1]then studio.log("Runtime error: "..tostring(result[2]))
        else
            local returned={};for i=2,result.n do returned[#returned+1]=tostring(result[i])end
            studio.log(#returned>0 and "Returned: "..table.concat(returned,"\t")or "Script finished.")
        end
    end)
end
function studio.stop()
    studio.job+=1
    if studio.thread and coroutine.status(studio.thread)~="dead"then pcall(task.cancel,studio.thread)end
    studio.thread=nil;studio.running=false;gui:SetAttribute("ExecutorRunning",false)
    if alive then studio.state.Text="STOPPED";studio.log("Editor task stopped. Existing script changes or spawned tasks can remain.")end
end
function studio.destroy()
    studio.job+=1
    if studio.thread and coroutine.status(studio.thread)~="dead"then pcall(task.cancel,studio.thread)end
    studio.thread=nil;studio.running=false
end
function studio.file(action)
    local ok,err=pcall(function()
        if action=="save"then
            assert(type(writefile)=="function","File saving is unavailable")
            if type(makefolder)=="function"then pcall(makefolder,"NOVA")end
            writefile("NOVA/editor.luau",studio.code.Text);studio.log("Saved: NOVA/editor.luau")
        else
            assert(type(readfile)=="function","File loading is unavailable")
            studio.code.Text=readfile("NOVA/editor.luau");studio.log("Loaded: NOVA/editor.luau")
        end
    end)
    if not ok then studio.log("File error: "..tostring(err))end
end
studio.window=make("Frame",{Name="ScriptStudio",AnchorPoint=Vector2.new(.5,.5),Position=UDim2.fromScale(.5,.5),Size=UDim2.fromOffset(860,650),BackgroundColor3=Color3.fromRGB(13,17,25),BorderSizePixel=0,Visible=false,ZIndex=50},gui)
round(studio.window,14);outline(studio.window,.35)
studio.header=make("Frame",{Name="StudioHeader",Size=UDim2.new(1,0,0,48),BackgroundColor3=Color3.fromRGB(22,29,42),BorderSizePixel=0,Active=true},studio.window);round(studio.header,14)
text(studio.header,"NOVA / SCRIPT STUDIO",15,Color3.fromRGB(225,235,250),UDim2.fromOffset(16,0),UDim2.new(1,-65,1,0))
studio.close=button(studio.header,"CloseStudio","×",UDim2.new(1,-44,0,8),UDim2.fromOffset(32,32))
on(studio.close.Activated,function()studio.show(false)end)
text(studio.window,"Paste Luau and run locally in Madium. Ctrl + Enter runs your script.",11,muted,UDim2.fromOffset(16,55),UDim2.new(1,-32,0,28))
studio.editor=make("ScrollingFrame",{Name="CodeScroll",BackgroundColor3=Color3.fromRGB(8,11,17),BorderSizePixel=0,Position=UDim2.fromOffset(16,90),Size=UDim2.new(1,-32,1,-315),AutomaticCanvasSize=Enum.AutomaticSize.XY,CanvasSize=UDim2.new(),ScrollBarThickness=5,ScrollingDirection=Enum.ScrollingDirection.XY},studio.window);round(studio.editor,8)
studio.code=make("TextBox",{Name="ScriptCode",Position=UDim2.fromOffset(10,8),Size=UDim2.new(1,-20,1,-16),AutomaticSize=Enum.AutomaticSize.XY,BackgroundTransparency=1,
    Text='print("Hello from Nova")\nreturn "Ready"',PlaceholderText="Paste your script here...",ClearTextOnFocus=false,MultiLine=true,TextEditable=true,RichText=false,TextWrapped=false,
    Font=Enum.Font.Code,TextSize=14,TextColor3=Color3.fromRGB(207,222,246),TextXAlignment=Enum.TextXAlignment.Left,TextYAlignment=Enum.TextYAlignment.Top},studio.editor)
studio.toolbar=make("ScrollingFrame",{Name="StudioToolbar",Position=UDim2.new(0,16,1,-214),Size=UDim2.new(1,-32,0,44),AutomaticCanvasSize=Enum.AutomaticSize.X,CanvasSize=UDim2.new(),BackgroundTransparency=1,BorderSizePixel=0,ScrollBarThickness=2,ScrollingDirection=Enum.ScrollingDirection.X},studio.window)
make("UIListLayout",{FillDirection=Enum.FillDirection.Horizontal,Padding=UDim.new(0,7),SortOrder=Enum.SortOrder.LayoutOrder},studio.toolbar)
studio.actions={{"RunScript","RUN",studio.run},{"StopScript","STOP TASK",studio.stop},{"ClearScript","CLEAR",function()studio.code.Text=""end},{"CopyScript","COPY",function()
    local copy=setclipboard or toclipboard
    if type(copy)=="function"then copy(studio.code.Text);studio.log("Code copied.")else studio.log("Clipboard API unavailable.")end
end},{"SaveScript","SAVE",function()studio.file("save")end},{"LoadScript","LOAD",function()studio.file("load")end},{"ClearOutput","CLEAR LOG",function()table.clear(studio.lines);studio.output.Text=""end}}
for index,entry in ipairs(studio.actions)do
    local control=button(studio.toolbar,entry[1],entry[2],UDim2.new(),UDim2.fromOffset(entry[2]=="STOP TASK"and 108 or 96,36));control.LayoutOrder=index
    on(control.Activated,function()local ok,err=pcall(entry[3]);if not ok then studio.log(tostring(err))end end)
end
studio.state=text(studio.window,"READY",10,muted,UDim2.new(0,16,1,-166),UDim2.new(1,-32,0,20));studio.state.Name="ExecutorState"
studio.outputScroll=make("ScrollingFrame",{Name="OutputScroll",Position=UDim2.new(0,16,1,-140),Size=UDim2.new(1,-32,0,122),BackgroundColor3=Color3.fromRGB(8,11,17),BorderSizePixel=0,AutomaticCanvasSize=Enum.AutomaticSize.Y,CanvasSize=UDim2.new(),ScrollBarThickness=4,ScrollingDirection=Enum.ScrollingDirection.Y},studio.window);round(studio.outputScroll,8)
studio.output=text(studio.outputScroll,"Output and errors appear here.",12,Color3.fromRGB(171,198,228),UDim2.fromOffset(10,8),UDim2.new(1,-24,0,100))
studio.output.Font=Enum.Font.Code;studio.output.TextWrapped=true;studio.output.TextYAlignment=Enum.TextYAlignment.Top;studio.output.AutomaticSize=Enum.AutomaticSize.Y
studio.quick=button(gui,"OpenScriptStudioQuick","SCRIPT STUDIO",UDim2.new(1,-16,.5,178),UDim2.fromOffset(174,44));studio.quick.AnchorPoint=Vector2.new(1,0);studio.quick.ZIndex=25
on(studio.quick.Activated,function()studio.show(not C.EditorOpen)end)
on(studio.header.InputBegan,function(input)
    if input.UserInputType==Enum.UserInputType.MouseButton1 or input.UserInputType==Enum.UserInputType.Touch then studio.drag={input=input,origin=input.Position,position=studio.window.Position}end
end)
on(UIS.InputChanged,function(input)
    if studio.drag and (input.UserInputType==Enum.UserInputType.MouseMovement or input==studio.drag.input)then
        local delta=input.Position-studio.drag.origin
        local viewport=workspace.CurrentCamera.ViewportSize;local size=studio.window.AbsoluteSize
        local origin=studio.drag.position
        local x=origin.X.Scale*viewport.X+origin.X.Offset+delta.X
        local y=origin.Y.Scale*viewport.Y+origin.Y.Offset+delta.Y
        studio.window.Position=UDim2.fromOffset(math.clamp(x,size.X/2,math.max(size.X/2,viewport.X-size.X/2)),math.clamp(y,size.Y/2,math.max(size.Y/2,viewport.Y-size.Y/2)))
    end
end)
on(UIS.InputEnded,function(input)if studio.drag and (input==studio.drag.input or input.UserInputType==Enum.UserInputType.MouseButton1)then studio.drag=nil end end)
on(UIS.InputBegan,function(input)
    if C.EditorOpen and input.KeyCode==Enum.KeyCode.Return and (UIS:IsKeyDown(Enum.KeyCode.LeftControl)or UIS:IsKeyDown(Enum.KeyCode.RightControl))then studio.code:ReleaseFocus(false);studio.run()end
end)
studio.layout(workspace.CurrentCamera.ViewportSize)

-- Useful session tools.
studio.toolCount=0
function studio.tool(name,label,callback)
    studio.toolCount+=1
    local control=button(pages.Tools,name,label,UDim2.new(),UDim2.new(1,0,0,44))
    on(control.Activated,function()
        local ok,result=pcall(callback)
        studio.toolStatus.Detail.Text=ok and (result or "Done.")or "Failed: "..tostring(result):sub(1,160)
    end)
end
function studio.copy(textValue)
    local copy=setclipboard or toclipboard;assert(type(copy)=="function","Clipboard API unavailable")
    copy(textValue);return "Copied to clipboard."
end
heading(pages.Tools,"Session tools","Script editor, local profiles, camera recovery, weapon switching, and quick diagnostics.")
studio.tool("OpenScriptStudio","OPEN SCRIPT STUDIO",function()studio.show(true);return "Script Studio opened."end)
studio.tool("SaveNovaProfile","SAVE GAME SETTINGS",function()
    assert(type(writefile)=="function","File saving unavailable")
    if type(makefolder)=="function"then pcall(makefolder,"NOVA")end
    writefile("NOVA/settings-"..game.PlaceId..".json",Http:JSONEncode({place=game.PlaceId,settings=C}));return "Settings saved for this game."
end)
studio.tool("LoadNovaProfile","LOAD GAME SETTINGS",function()
    local data=Http:JSONDecode(readfile("NOVA/settings-"..game.PlaceId..".json"))
    assert(data.place==game.PlaceId and type(data.settings)=="table","Invalid profile for this game")
    for key,value in pairs(data.settings)do
        if typeof(value)==typeof(C[key])and (type(value)~="number"or value==value and math.abs(value)<=1000000)then C[key]=value end
    end
    for _,key in ipairs({"Bot","AutoShoot","TriggerBot","AutoClicker","Healing","DefenseAssist","EditorOpen"})do C[key]=false end
    refresh();return "Settings loaded. Automatic actions are paused; enable them when ready."
end)
studio.tool("CopyCoordinates","COPY COORDINATES",function()local _,_,r=extras.characterParts();assert(r,"No character");return studio.copy(string.format("%.2f, %.2f, %.2f",r.Position.X,r.Position.Y,r.Position.Z))end)
studio.tool("CopyGameDetails","COPY GAME DETAILS",function()return studio.copy("Place: "..game.PlaceId.."\nServer: "..game.JobId.."\nPlayers: "..#Players:GetPlayers())end)
studio.tool("CycleWeapon","NEXT COMPATIBLE GUN",function()
    local ch,h=extras.characterParts();assert(h,"No living character")
    local reg=autofire.registryForGame();assert(reg,"No compatible gun adapter")
    local list={};for tool,gun in pairs(reg.Tools)do if typeof(tool)=="Instance"and (tool.Parent==LP.Backpack or tool.Parent==ch)and type(gun)=="table"and type(gun.DoFire)=="function"then table.insert(list,tool)end end
    table.sort(list,function(a,b)return a.Name<b.Name end);assert(#list>0,"No compatible guns owned")
    local index=table.find(list,ch:FindFirstChildOfClass("Tool"))or 0;local tool=list[index%#list+1];h:EquipTool(tool);return "Equipped "..tool.Name
end)
studio.tool("UnequipTools","UNEQUIP TOOL",function()local _,h=extras.characterParts();assert(h,"No living character");h:UnequipTools();return "Tool unequipped."end)
studio.tool("RepairCamera","RESTORE CAMERA FOLLOW",function()
    local _,h=extras.characterParts();assert(h,"No living character")
    C.CameraOverride=false;C.CameraOffsetOverride=false;C.ZoomOverride=false;C.ViewMode="Game default"
    workspace.CurrentCamera.CameraType=Enum.CameraType.Custom;workspace.CurrentCamera.CameraSubject=h;refresh();return "Camera follow restored."
end)
studio.tool("LowClutterPreset","LOW CLUTTER VIEW",function()C.Circle=false;C.Opacity=100;C.Distance=false;C.Health=false;C.Names=true;C.NoBlur=true;C.NoDOF=true;C.Motion=false;refresh();return "Low clutter view applied."end)
studio.tool("NightVisionPreset","BRIGHT CLEAR VIEW",function()C.FullBright=true;C.NoFog=true;C.NoShadows=true;refresh();return "Local bright, clear view applied."end)
studio.tool("ShowPerformance","SHOW PERFORMANCE HUD",function()C.ShowFPS=true;C.FPSDetails=true;C.FPSGraph=true;refresh();return "Performance HUD enabled."end)
studio.tool("WeaponDiagnostics","CHECK CURRENT WEAPON",function()utility.inspect();return gui:GetAttribute("AmmoStatus")end)
studio.tool("StopCombatTools","STOP COMBAT FEATURES",function()
    for _,key in ipairs({"Bot","Aim","AutoShoot","TriggerBot","AutoClicker","InfiniteAmmo","NoCooldown","NoRecoil","NoSpread","NoAimKick","Hitboxes","WallShots"})do C[key]=false end
    bot.stop("Stopped from session tools");autofire.reset("Stopped");refresh();return "Combat features stopped."
end)
studio.toolStatus=row(pages.Tools,"ToolsStatus","Tools status","Ready. Script Studio also has a shortcut on the right.",120)
studio.toolStatus.RowTitle.Size=UDim2.new(1,-32,0,22);studio.toolStatus.Detail.Size=UDim2.new(1,-32,1,-48)
gui:SetAttribute("SessionToolCount",studio.toolCount)

C.BotObstacleJump=true;C.BotShortcut=true;C.BotJumpLookAhead=6;C.BotMaxObstacle=4
function bot.checkObstacle(hum,root,direction)
    local now=os.clock()
    if not C.BotObstacleJump or bot.jumpPlan or hum.FloorMaterial==Enum.Material.Air or now<(bot.nextObstacleJump or 0)or now<(bot.nextObstacleScan or 0)then return false end
    bot.nextObstacleScan=now+.15
    direction=Vector3.new(direction.X,0,direction.Z)
    if direction.Magnitude<.01 then return false end;direction=direction.Unit
    local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances={LP.Character,world,bot.target and bot.target.Character}
    if typeof(root)=="Instance"and root.Parent then local exclude=params.FilterDescendantsInstances;table.insert(exclude,root.Parent);params.FilterDescendantsInstances=exclude end
    pcall(function()params.RespectCanCollide=true end)
    local floor=workspace:Raycast(root.Position,Vector3.new(0,-12,0),params)
    if not floor then return false end
    local gravity=math.max(1,workspace.Gravity)
    local jumpHeight=hum.UseJumpPower and hum.JumpPower*hum.JumpPower/(2*gravity)or hum.JumpHeight
    local velocity=math.sqrt(2*gravity*jumpHeight)
    local speed=math.max(1,hum.WalkSpeed)
    local reach=speed*(2*velocity/gravity)*.82
    local scan=math.min(C.BotJumpLookAhead,math.max(2,reach-2))
    local hit
    for _,height in ipairs({.7,1.6,2.5})do
        local origin=Vector3.new(root.Position.X,floor.Position.Y+height,root.Position.Z)
        local candidate=workspace:Raycast(origin,direction*scan,params)
        if candidate and (not hit or candidate.Distance<hit.Distance)then hit=candidate end
    end
    if not hit then return false end
    bot.failedBarriers=bot.failedBarriers or setmetatable({},{__mode="k"})
    if now<(bot.failedBarriers[hit.Instance]or 0)then gui:SetAttribute("BotObstacleStatus","Recent failed jump: using a route around this barrier");return false end
    local maximum=math.min(C.BotMaxObstacle,jumpHeight*.75)
    local probe=hit.Position+direction*.4
    local top=workspace:Raycast(Vector3.new(probe.X,floor.Position.Y+maximum+1,probe.Z),Vector3.new(0,-maximum-2,0),params)
    if not top or top.Instance~=hit.Instance or top.Position.Y-floor.Position.Y>maximum then gui:SetAttribute("BotObstacleStatus","Barrier exceeds jump clearance: routing around it");return false end
    local barrierHeight=top.Position.Y-floor.Position.Y
    if barrierHeight<.9 then return false end
    local landing
    local side=Vector3.new(-direction.Z,0,direction.X)
    for extra=3,math.max(3,reach-hit.Distance),.5 do
        local point=hit.Position+direction*extra
        local ground=workspace:Raycast(Vector3.new(point.X,floor.Position.Y+maximum+1,point.Z),Vector3.new(0,-maximum-8,0),params)
        if ground and ground.Instance~=hit.Instance and ground.Position.Y-floor.Position.Y<=maximum and ground.Position.Y-floor.Position.Y>=-4 then
            local clear=true
            for _,offset in ipairs({Vector3.zero,side,-side,direction,-direction})do
                local supported=workspace:Raycast(ground.Position+offset+Vector3.new(0,1.2,0),Vector3.new(0,-2.4,0),params)
                if not supported or math.abs(supported.Position.Y-ground.Position.Y)>.8 or workspace:Raycast(ground.Position+offset+Vector3.new(0,.2,0),Vector3.new(0,4.8,0),params)then clear=false;break end
            end
            if clear then landing=ground.Position;break end
        end
    end
    if not landing then gui:SetAttribute("BotObstacleStatus","No supported landing within jump range: routing around");return false end
    local rootOffset=root.Position.Y-floor.Position.Y
    local deltaY=landing.Y+rootOffset-root.Position.Y
    local discriminant=velocity*velocity-2*gravity*deltaY
    if discriminant<=0 then return false end
    local flight=(velocity+math.sqrt(discriminant))/gravity
    local horizontal=Vector3.new(landing.X-root.Position.X,0,landing.Z-root.Position.Z)
    local distance=horizontal.Magnitude
    if distance>speed*flight*.9 then gui:SetAttribute("BotObstacleStatus","Landing beyond current jump range");return false end
    local travel=horizontal.Unit
    local offsets={Vector3.new(0,-math.max(.5,rootOffset-.35),0),Vector3.zero,Vector3.new(0,1.8,0),side*.75,-side*.75}
    local previous=root.Position
    for index=1,12 do
        local t=flight*index/12
        local point=root.Position+travel*math.min(distance,speed*t)+Vector3.new(0,velocity*t-.5*gravity*t*t,0)
        for _,offset in ipairs(offsets)do
            local blocked=workspace:Raycast(previous+offset,point-previous,params)
            if blocked then gui:SetAttribute("BotJumpBlockedBy",blocked.Instance:GetFullName());gui:SetAttribute("BotObstacleStatus","Jump arc intersects cover or ceiling: routing around");return false end
        end
        previous=point
    end
    if not bot.requestJump(hum,"Planned barrier jump")then return false end
    bot.jumpPlan={goal=Vector3.new(landing.X,landing.Y+rootOffset,landing.Z),started=now,flight=flight,obstacle=hit.Instance,wasAir=false,start=root.Position}
    bot.goal=bot.jumpPlan.goal;bot.smoothed=travel;hum:Move(travel,false)
    bot.nextObstacleJump=now+flight+.3
    gui:SetAttribute("BotObstacleStatus",string.format("Committed jump: %.1f high / %.1f forward",barrierHeight,distance))
    return true,bot.jumpPlan.goal
end
function bot.updateJump(hum,root)
    local plan=bot.jumpPlan;if not plan then return end
    local now=os.clock()
    if hum.FloorMaterial==Enum.Material.Air or root.Position.Y-plan.start.Y>.5 then plan.wasAir=true end
    local landed=plan.wasAir and hum.FloorMaterial~=Enum.Material.Air and root.AssemblyLinearVelocity.Y<2 and now-plan.started>.18
    if landed or now-plan.started>plan.flight+1 then
        local gap=Vector3.new(root.Position.X-plan.goal.X,0,root.Position.Z-plan.goal.Z).Magnitude
        if not landed or gap>2 or math.abs(root.Position.Y-plan.goal.Y)>1.2 then
            bot.failedBarriers[plan.obstacle]=now+8;bot.waypoints=nil;bot.pathAt=0
            gui:SetAttribute("BotObstacleStatus","Jump did not reach landing: avoiding this barrier for 8 seconds")
        else gui:SetAttribute("BotObstacleStatus","Jump landed successfully")end
        bot.jumpPlan=nil;bot.shortcutUntil=0
    else bot.goal=plan.goal end
end
heading(pages.Bot,"Obstacle navigation","Plan jumps using the character's jump height, a supported landing, and body clearance along the full jump arc.")
toggle(pages.Bot,"BotObstacleJump","Obstacle jump","Jump checked low barriers. Movement stays committed to the landing while airborne.")
toggle(pages.Bot,"BotShortcut","Jump shortcuts","Try a short checked jump before taking a longer path around an obstacle.")
slider(pages.Bot,"BotJumpLookAhead","Obstacle detection distance","Scan ahead; actual movement speed and jump range limit the planner.",3,8,.5)
slider(pages.Bot,"BotMaxObstacle","Maximum barrier height","Actual jump height and ceiling clearance may impose a lower limit.",1,6,.5)
bot.obstacleRow=row(pages.Bot,"ObstacleNavigation","Obstacle status","Ready. Failed barriers are temporarily avoided rather than repeatedly jumped into.",130)
bot.obstacleRow.RowTitle.Size=UDim2.new(1,-32,0,22);bot.obstacleRow.Detail.Size=UDim2.new(1,-32,1,-48)

C.BotFollowCamera=true;C.BotCameraSpeed=8;C.BotJumpAssist=true
function bot.requestJump(hum,reason)
    local now=os.clock()
    if hum.FloorMaterial==Enum.Material.Air or now<(bot.nextNativeJump or 0)then return false end
    local ok,enabled=pcall(hum.GetStateEnabled,hum,Enum.HumanoidStateType.Jumping)
    if not ok or not enabled then gui:SetAttribute("BotObstacleStatus","The game currently disables jumping; computing a route around it.");return false end
    bot.nextNativeJump=now+.65;bot.jumpUntil=now+.25
    hum.Jump=true
    if C.BotJumpAssist then pcall(hum.ChangeState,hum,Enum.HumanoidStateType.Jumping)end
    gui:SetAttribute("BotJumpReason",reason or "Navigation")
    return true
end
function bot.routeCamera(cam,root,dt,fallback)
    if not C.BotFollowCamera then return end
    local direction=bot.smoothed
    if direction.Magnitude<.1 and bot.goal then direction=bot.goal-root.Position end
    if direction.Magnitude<.1 and fallback then direction=fallback-root.Position end
    direction=Vector3.new(direction.X,0,direction.Z)
    if direction.Magnitude<.1 then return end
    local desired=CFrame.lookAt(cam.CFrame.Position,cam.CFrame.Position+direction.Unit*30)
    cam.CFrame=cam.CFrame:Lerp(desired,1-math.exp(-C.BotCameraSpeed*math.min(dt,.1)))
    gui:SetAttribute("BotCameraStatus","Following travel direction")
end
heading(pages.Bot,"Camera and jump control","The camera follows travel direction and switches to visible targets during a fight.")
toggle(pages.Bot,"BotFollowCamera","Follow bot view","Turn your screen smoothly toward the route while the bot walks.")
slider(pages.Bot,"BotCameraSpeed","Route camera turn speed","Smooth camera response while navigating.",3,20,1)
toggle(pages.Bot,"BotJumpAssist","Jump input assist","Use the native jump state and briefly hold the jump request to avoid canceled inputs.")

C.BotADS=true
function bot.setADS(gun,enabled)
    enabled=enabled and C.BotADS
    if not enabled or gun~=bot.adsGun then
        if bot.adsGun and bot.adsOriginal then
            if bot.adsGun.DoAim==bot.adsWrapper then pcall(function()bot.adsGun.DoAim=bot.adsOriginal end)end
            pcall(bot.adsOriginal,bot.adsGun,false)
        end
        bot.adsGun=nil;bot.adsActive=false;bot.adsOriginal=nil;bot.adsWrapper=nil
    end
    if not enabled or not C.BotADS then gui:SetAttribute("BotADSStatus","Hip fire / traveling");return end
    if type(gun)~="table"or type(gun.DoAim)~="function"then gui:SetAttribute("BotADSStatus","Weapon does not expose native aim-down-sights");return end
    local hum=LP.Character and LP.Character:FindFirstChildOfClass("Humanoid")
    if not bot.adsWrapper then
        local original=gun.DoAim
        local wrapper=function(self,requested,...)
            local character=LP.Character
            local h=character and character:FindFirstChildOfClass("Humanoid")
            local held=alive and C.Bot and C.BotADS and bot.adsActive and bot.adsGun==self and extras.active()and not C.Healing and not C.EditorOpen and h and not h:GetAttribute("Reloading")
            return original(self,held and true or requested,...)
        end
        local ok=pcall(function()gun.DoAim=wrapper end)
        if not ok then gui:SetAttribute("BotADSStatus","Weapon aim controller is read-only");return end
        bot.adsOriginal=original;bot.adsWrapper=wrapper;bot.adsGun=gun;bot.adsActive=true
    end
    if gun==bot.adsGun and hum and hum:GetAttribute("Aiming")then return end
    if os.clock()<(bot.adsRetry or 0)then return end;bot.adsRetry=os.clock()+.3
    local ok,err=pcall(gun.DoAim,gun,true)
    if ok then bot.adsGun=gun;bot.adsActive=true;gui:SetAttribute("BotADSStatus","Native aim-down-sights requested")
    else gui:SetAttribute("BotADSStatus","Native aim unavailable: "..tostring(err):sub(1,90))end
end
toggle(pages.Bot,"BotADS","Aim down sights","Use the gun's native aim/scope action on visible targets. Release it during travel, reload, pause, or stop.")
bot.adsRow=row(pages.Bot,"BotADSStatus","Aim-down-sights status","Ready. Requires a compatible gun with a native DoAim action.",110)
bot.adsRow.RowTitle.Size=UDim2.new(1,-32,0,22);bot.adsRow.Detail.Size=UDim2.new(1,-32,1,-48)

-- TTK uses a controller-owned weapon model, not a Backpack Tool.
do
local ttk={name="TTK Testing",registry={Tools={}},controller=nil,weapon=nil,tool=nil,gun=nil}
function ttk.connect()
    if (game.PlaceId~=120189115846709 and game.GameId~=10090256806) then return nil end
    if ttk.controller then return ttk.controller end
    local rs=game:GetService("ReplicatedStorage")
    local modules=rs:FindFirstChild("Modules")
    local client=modules and modules:FindFirstChild("Client")
    local controllers=client and client:FindFirstChild("Controllers")
    local module=controllers and controllers:FindFirstChild("GunController")
    if not module or not module:IsA("ModuleScript") then return nil end
    local ok,value=pcall(require,module)
    if ok and type(value)=="table" and type(value.FireWeapon)=="function" and type(value.Reload)=="function" and type(value.StartAiming)=="function" and type(value.StopAiming)=="function" then ttk.controller=value;return value end
end
function ttk.current()
    local c=ttk.connect()
    local w=c and c.Weapon
    if type(w)~="table" or not c.Enabled or not w.IsEquipped or c._isGenericItem then
        ttk.weapon=nil;ttk.tool=nil;ttk.gun=nil;table.clear(ttk.registry.Tools)
        gui:SetAttribute("AdapterWeapon",nil)
        return nil
    end
    if ttk.weapon~=w then
        ttk.weapon=w
        local tool={Name=w.Name or "TTK weapon",Enabled=true,Parent=LP.Character}
        function tool:GetAttribute(key)
            if key=="Ammo" then return w.MagAmmo end
            if key=="Mags" then return type(w.GetReserve)=="function" and w:GetReserve() or 0 end
        end
        local gun={Settings={Modes={w.FireMode=="auto" and "Automatic" or "Semi"}}}
        function gun:DoFire()
            if not alive or c.Weapon~=w or not c.Enabled or w.IsReloading then return false end
            if type(c.ShouldBlockWeaponInput)=="function" and c:ShouldBlockWeaponInput() then return false end
            local fired=c:FireWeapon()
            -- A discrete native shot, including semi-auto trigger release.
            if fired and not UIS:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) and type(w.ReleaseTrigger)=="function" then w:ReleaseTrigger() end
            return fired
        end
        function gun:DoReload()
            if alive and c.Weapon==w and c.Enabled and not w.IsReloading then return c:Reload() end
        end
        function gun:DoAim(enabled)
            if c.Weapon~=w then return end
            if enabled and alive and c.Enabled and not w.IsReloading then c:StartAiming() else c:StopAiming() end
        end
        ttk.tool=tool;ttk.gun=gun;table.clear(ttk.registry.Tools);ttk.registry.Tools[tool]=gun
    end
    ttk.tool.Parent=LP.Character
    ttk.tool.Enabled=c.Enabled and not w.IsReloading
    gui:SetAttribute("WeaponAdapter",ttk.name)
    gui:SetAttribute("AdapterWeapon",ttk.tool.Name)
    return ttk.tool,ttk.gun
end
if (game.PlaceId==120189115846709 or game.GameId==10090256806) then
    gui:SetAttribute("WeaponAdapter",ttk.name)
    gui:SetAttribute("AdapterSupport","Native firing, aiming, reload, bot weapon selection. Ammo, wall shots, recoil overrides, healing and defense are unavailable.")
    autofire.registryForGame=function()ttk.current();return ttk.registry end
    autofire.currentTool=function()return ttk.current()end
    utility.current=function()return ttk.current()end
    utility.watch=function()end -- No Tool attribute signals exist in this game.
    utility.capacity=function(tool)
        local current,gun=ttk.current()
        if current and current==tool then return tonumber(ttk.weapon.MaxMagSize),gun end
    end
    utility.refill=function()utility.status("TTK Testing: infinite ammo is unsupported; native ammo rules remain active.")end
    utility.inspect=function()
        local tool=ttk.current()
        gui:SetAttribute("AmmoServerVerified",false)
        if not tool then gui:SetAttribute("AmmoWeapon",nil);gui:SetAttribute("AmmoCapacity",0);utility.status("TTK Testing: equip a firearm to use native fire, aim and reload.");return end
        local capacity=tonumber(ttk.weapon.MaxMagSize) or 0
        gui:SetAttribute("AmmoWeapon",tool.Name);gui:SetAttribute("AmmoCapacity",capacity)
        utility.status(C.InfiniteAmmo and "TTK Testing: infinite ammo is unsupported; native ammo rules remain active." or (tool.Name.." | "..tostring(tool:GetAttribute("Ammo")).."/"..capacity.." | reserve "..tostring(tool:GetAttribute("Mags"))..". Native firing / aiming / reload supported."))
    end
    bot.weapon=function()
        local tool,gun=ttk.current()
        if tool then return tool,gun end
        return nil,nil,"TTK Testing: equip a firearm; automatic loadout selection is unavailable."
    end
    combat.checkWeapons=function()local tool=ttk.current();return "TTK Testing - native fire / aim / reload; "..(tool and tool.Name or "equip a gun"),true end
    combat.checkWallFilter=function()return "UNSUPPORTED - TTK native ballistic rules apply"end
    local support=row(pages.Settings,"TTKSupport","TTK Testing compatibility",gui:GetAttribute("AdapterSupport"),130)
    support.RowTitle.Size=UDim2.new(1,-32,0,22);support.Detail.Size=UDim2.new(1,-32,1,-48)
    if bot.adsRow then bot.adsRow.Detail.Text="TTK Testing uses native StartAiming / StopAiming."end
    utility.inspect()
end
end

C.BotAvoid=true
function bot.steer(hum,root,wanted)
    if not C.BotAvoid or wanted.Magnitude<.1 or bot.jumpPlan or hum.FloorMaterial==Enum.Material.Air then return wanted,false end
    wanted=Vector3.new(wanted.X,0,wanted.Z).Unit
    local now=os.clock()
    local params=RaycastParams.new();params.FilterType=Enum.RaycastFilterType.Exclude
    params.FilterDescendantsInstances={LP.Character,world,root.Parent,bot.target and bot.target.Character}
    pcall(function()params.RespectCanCollide=true end)
    local floor=workspace:Raycast(root.Position,Vector3.new(0,-12,0),params)
    if not floor then return Vector3.zero,true end
    local rootHeight=root.Position.Y-floor.Position.Y
    local look=math.clamp(hum.WalkSpeed*.35,4,8)
    local radius=math.clamp(root.Size.X*.5,.8,1.5)
    local function clear(direction)
        local side=Vector3.new(-direction.Z,0,direction.X)*radius
        for _,offset in {Vector3.zero,Vector3.new(0,1.5,0),Vector3.new(0,-math.min(1.2,rootHeight-.4),0),side,-side} do
            if workspace:Raycast(root.Position+offset,direction*look,params) then return false end
        end
        for _,offset in {Vector3.zero,side*.7,-side*.7} do
            local ahead=root.Position+direction*math.min(look,4)+offset
            local support=workspace:Raycast(ahead+Vector3.new(0,2,0),Vector3.new(0,-rootHeight-5,0),params)
            if not support or support.Normal.Y<.55 or support.Position.Y-floor.Position.Y>1.5 or support.Position.Y-floor.Position.Y< -3 then return false end
        end
        return true
    end
    if clear(wanted) then
        gui:SetAttribute("BotAvoidance","Route clear")
        return wanted,false
    end
    local best,score,chosenSign=nil,-math.huge,0
    for _,angle in {35,-35,70,-70,105,-105,145,-145,180} do
        local direction=CFrame.fromAxisAngle(Vector3.yAxis,math.rad(angle)):VectorToWorldSpace(wanted)
        if clear(direction) then
            local sign=angle>0 and 1 or -1
            local value=direction:Dot(wanted)*2
            if now<(bot.avoidUntil or 0) and sign==bot.avoidSign then value+=.65 end
            if value>score then best=direction;score=value;chosenSign=sign end
        end
    end
    if best then
        bot.avoidSign=chosenSign;bot.avoidUntil=now+.8
        gui:SetAttribute("BotAvoidance","Steering around obstacle / checking ground clearance")
        gui:SetAttribute("BotObstacleStatus","Steering around blocked route")
        return best,true
    end
    if now>=(bot.avoidRepathAt or 0) then bot.avoidRepathAt=now+.8;bot.pathAt=0;bot.waypoints=nil end
    gui:SetAttribute("BotAvoidance","No safe side route: stopped for path replanning")
    gui:SetAttribute("BotObstacleStatus","Blocked on all sides: requesting a new path")
    return Vector3.zero,true
end
toggle(pages.Bot,"BotAvoid","Avoid walls and ledges","Scan body clearance and ground support, steer around obstacles, and stop for a new path when no safe direction is available. Start Bot enables this.")

-- Shared capability layer: explicit native adapters first, ordinary Tool input second.
do
local compat={name="NOVA Shooter",mode="Standard tools",registry={Tools={}},cache=setmetatable({},{__mode="k"}),held=nil}
local nativeRegistry=autofire.registryForGame
local nativeBotWeapon=bot.weapon
local nativeInspect=utility.inspect
C.GenericToolInput=false
function compat.detect()
    if (game.PlaceId==120189115846709 or game.GameId==10090256806) then compat.mode="TTK Testing";return end
    local reg=nativeRegistry()
    compat.mode=reg and "Native gun adapter" or "Standard tools"
end
function compat.reason(key)
    if compat.mode=="TTK Testing" then
        if table.find({"InfiniteAmmo","WallShots","NoCooldown","NoRecoil","NoSpread","NoAimKick","DefenseAssist","BotEquip","GenericToolInput"},key) then return "Unavailable in TTK Testing. Native firing, ADS and reload are supported." end
    elseif compat.mode=="Standard tools" then
        if table.find({"InfiniteAmmo","WallShots","NoCooldown","NoRecoil","NoSpread","NoAimKick","AutoReload","BotReload","BotADS","BotEquip","DefenseAssist"},key) then return "This game has no adapter for this feature. Standard Tool input supports activation only." end
    end
    if key=="DefenseAssist" and game.PlaceId~=126210570846701 then return "This game has no verified healing adapter." end
end
function compat.release()
    local held=compat.held;compat.held=nil;compat.serial=(compat.serial or 0)+1
    if held then pcall(held.Deactivate,held) end
end
function compat.armStandardInput()
    if compat.mode~="Standard tools" then return true end
    local ch=LP.Character
    local tool=ch and ch:FindFirstChildOfClass("Tool")
    if not tool then
        local hum=ch and ch:FindFirstChildOfClass("Humanoid")
        local backpack=LP:FindFirstChildOfClass("Backpack")
        if hum and backpack then for _,candidate in backpack:GetChildren() do
            if candidate:IsA("Tool") and candidate.Enabled and candidate:GetAttribute("Gun")==true then hum:EquipTool(candidate);tool=candidate;break end
        end end
    end
    if not tool then return false end
    C.GenericToolInput=true
    return true
end
ui.prepareBot=compat.armStandardInput
C.ToolFireMode="Auto"
function compat.inputMethod(tool)
    if C.ToolFireMode~="Auto" then return C.ToolFireMode end
    if mobile then return "Tool activation" end
    if type(getconnections)=="function" then
        local ok,connections=pcall(getconnections,tool.Activated)
        if ok and #connections>0 then return "Tool activation" end
        if ok and type(mouse1click)=="function" then return "Mouse click" end
    end
    if game.GameId==9741874508 and type(mouse1click)=="function" then return "Mouse click" end
    return "Tool activation"
end
function compat.current()
    local ch=LP.Character
    local hum=ch and ch:FindFirstChildOfClass("Humanoid")
    local tool=ch and ch:FindFirstChildOfClass("Tool")
    if not C.GenericToolInput or not hum or hum.Health<=0 or not tool or not tool.Enabled then table.clear(compat.registry.Tools);return nil end
    local gun=compat.cache[tool]
    if not gun then
        gun={Settings={}}
        function gun:DoFire()
            if not alive or not C.GenericToolInput or tool.Parent~=LP.Character or not tool.Enabled then return false end
            if menuOpen or not focus or UIS:GetFocusedTextBox() then return false end
            if type(iswindowactive)=="function" then local ok,active=pcall(iswindowactive);if not ok or not active then return false end end
            local method=compat.inputMethod(tool)
            gui:SetAttribute("SharedFireMethod",method)
            if method=="Mouse click" then
                if type(mouse1click)~="function" or type(iswindowactive)~="function" then return false end
                local position=UIS:GetMouseLocation()
                if ui.pointerBlocked(position) then gui:SetAttribute("SharedFireStatus","Paused: pointer is over a Nova control");return false end
                mouse1click();gui:SetAttribute("SharedFireStatus","Mouse click dispatched; native cooldown and game rules apply");return true
            end
            compat.release()
            tool:Activate();compat.held=tool;gui:SetAttribute("SharedFireStatus","Tool activated; native game rules apply")
            local serial=compat.serial
            task.delay(.05,function()
                if compat.held==tool and compat.serial==serial then compat.release() end
            end)
            return true
        end
        compat.cache[tool]=gun
    end
    table.clear(compat.registry.Tools);compat.registry.Tools[tool]=gun
    return tool,gun
end
compat.detect()
local applyNativePreset=preset.apply
preset.apply=function()
    if not compat.armStandardInput() then notify("Equip a weapon","Equip a Tool before applying a shooting preset in this game.");return end
    applyNativePreset()
end
if compat.mode=="Standard tools" then
    autofire.registryForGame=function()
        local reg=nativeRegistry()
        if reg then compat.mode="Native gun adapter";return reg end
        compat.current();return compat.registry
    end
    bot.weapon=function(hum,ch)
        if compat.mode~="Standard tools" then return nativeBotWeapon(hum,ch) end
        local tool,gun=compat.current()
        if tool then return tool,gun end
        return nil,nil,"Equip a gun represented by a Tool. This game may need a custom weapon adapter."
    end
    utility.inspect=function()
        if compat.mode~="Standard tools" then return nativeInspect() end
        local ch=LP.Character;local tool=ch and ch:FindFirstChildOfClass("Tool")
        utility.status("Standard Tool input: "..(tool and tool.Name or "equip a weapon")..". "..(C.GenericToolInput and "Activation enabled; firing depends on the game." or "Enable auto shoot or Start Bot with an equipped Tool to use standard activation.").." Ammo and reload overrides are unavailable.")
    end
end
ui.enforceCapabilities=function()
    for key,value in pairs(C) do if value==true and compat.reason(key) then C[key]=false end end
end
ui.canToggle=function(key)
    if not C[key] and (key=="Bot" or key=="AutoShoot" or key=="TriggerBot") and not compat.armStandardInput() then
        if notify then notify("Equip a weapon","This game uses Standard Tool input. Equip the gun you want to activate, then try again.") end
        return false
    end
    local reason=compat.reason(key)
    if reason then if notify then notify("Game compatibility",reason) end;return false end
    return true
end
heading(pages.Game,"NOVA / Shooter compatibility","Detect a native weapon adapter or use ordinary equipped-Tool input. Custom weapons and character rigs may require more support.")
compat.game=row(pages.Game,"DetectedGame","Detected game","",120)
compat.features=row(pages.Game,"SupportedFeatures","Feature compatibility","",215)
compat.botStatus=row(pages.Game,"LiveBotStatus","Bot status","Stopped. START BOT configures movement and supported weapon controls. Roblox must be focused.",125)
for _,box in {compat.game,compat.features,compat.botStatus} do box.RowTitle.Size=UDim2.new(1,-32,0,22);box.Detail.Size=UDim2.new(1,-32,1,-48) end
local start=button(pages.Game,"StartBot","START BOT / F7",UDim2.new(),UDim2.new(1,0,0,48))
on(start.Activated,function()C.Bot=true;refresh();showMenu(false);notify("Bot enabled","Movement and supported ADS/reload are configured automatically. Focus Roblox; F10 stops.")end)
toggle(pages.Game,"GenericToolInput","Standard Tool input","Normal equipped-Tool activation for other games. Enabling auto shoot, Start Bot, or the game preset arms this input mode. It cannot add an unsupported firing system.")
cycle(pages.Game,"ToolFireMode","Shared weapon input","Auto uses Tool activation when handlers exist, otherwise native mouse clicks when available. Choose a method manually if this game needs it.",{"Auto","Tool activation","Mouse click"})
local check=button(pages.Game,"CheckSupport","CHECK GAME SUPPORT",UDim2.new(),UDim2.new(1,0,0,44))
local combatPage=button(pages.Game,"OpenCombat","OPEN SHOOTING CONTROLS",UDim2.new(),UDim2.new(1,0,0,44))
on(combatPage.Activated,function()selectTab("Combat")end)
local botPage=button(pages.Game,"OpenBot","OPEN BOT AND MOVEMENT",UDim2.new(),UDim2.new(1,0,0,44))
on(botPage.Activated,function()selectTab("Bot")end)
for i,entry in {{"Aim","AIM"},{"AutoShoot","AUTO FIRE"},{"Bot","BOT PLAY"}} do
    local key,title=entry[1],entry[2]
    local quick=button(gui,"TTKQuick"..key,"",UDim2.new(1,-16,.5,-122+(i-1)*50),UDim2.fromOffset(174,44))
    quick.AnchorPoint=Vector2.new(1,0);quick.ZIndex=25
    on(quick.Activated,function()
        if not C[key] and ui.canToggle and not ui.canToggle(key) then return end
        if key=="Bot" then C.Bot=not C.Bot;if not C.Bot then bot.stop("Stopped")end else C[key]=not C[key]end
        refresh();notify(title,C[key] and "Enabled; close the menu to engage." or "Disabled")
    end)
    table.insert(paints,function()quick.Text=title..": "..(C[key] and "ON" or "OFF");quick.BackgroundColor3=C[key] and Color3.fromRGB(28,133,94)or Color3.fromRGB(31,34,43)end)
end
local botBadge=make("Frame",{Name="BotLiveBadge",AnchorPoint=Vector2.new(1,0),Position=UDim2.new(1,-16,.5,-194),Size=UDim2.fromOffset(174,66),BackgroundColor3=Color3.fromRGB(23,26,34),BorderSizePixel=0,ZIndex=25,Visible=false},gui)
round(botBadge,8)
local botBadgeText=text(botBadge,"",10,ink,UDim2.fromOffset(8,5),UDim2.new(1,-16,1,-10));botBadgeText.TextWrapped=true;botBadgeText.ZIndex=26
function compat.update()
    if not alive then return end
    botBadge.Visible=C.Bot
    botBadgeText.Text=gui:GetAttribute("BotStatus") or "Bot starting"
    local native=compat.mode=="TTK Testing" or compat.mode=="Native gun adapter"
    local tool=utility.current()
    local weapon=tool and tool.Name or "No equipped weapon"
    if compat.mode=="Standard tools" and tool then gui:SetAttribute("SharedFireMethod",compat.inputMethod(tool))end
    compat.game.Detail.Text=compat.mode.." / Place "..game.PlaceId.."\n"..weapon..(compat.mode=="Standard tools" and tool and (" / "..compat.inputMethod(tool)) or "").."\nBoot settings: every switch OFF."
    compat.botStatus.Detail.Text=(gui:GetAttribute("BotStatus") or "Stopped").."\nF7 toggles / F10 stops / F8 emergency stop. Reopening the menu pauses the bot."
    compat.features.Detail.Text=compat.mode=="TTK Testing" and "Native: firing, aim down sights, reload, bot weapon detection.\nShared: aim camera, player overlays, crosshair, navigation, clicker and interface tools.\nUnavailable: infinite ammo, wall shots, recoil overrides, healing and defense."
        or native and "Native gun adapter detected. Use Combat's compatibility check for weapon details.\nShared: camera, overlays, crosshair, navigation, clicker and interface tools.\nServer-controlled effects depend on this game's support."
        or "Shared: standard Humanoid player detection, aim camera, overlays, crosshair, navigation and clicker.\nOpt in: equipped Tool activation or mouse input for auto shoot / bot.\nUnavailable: native ADS, ammo, reload, wall shots, recoil, healing and defense. Custom characters and weapons require a game adapter."
    gui:SetAttribute("Edition","NOVA Shooter");gui:SetAttribute("CompatibilityMode",compat.mode)
    gui:SetAttribute("CompatibilityWeapon",weapon)
    for _,key in {"InfiniteAmmo","WallShots","NoCooldown","NoRecoil","NoSpread","NoAimKick","AutoReload","BotReload","BotADS","BotEquip","DefenseAssist","GenericToolInput"} do
        local b=gui:FindFirstChild(key,true)
        if b and b:IsA("GuiButton") then
            local reason=compat.reason(key);b:SetAttribute("UnavailableReason",reason)
            b.AutoButtonColor=reason==nil
            local label=b.Parent:FindFirstChild("ToggleState")
            if label and reason then label.Text="N/A";label.TextColor3=muted end
            if label and not reason and label.Text=="N/A" then label.Text=C[key] and "ON" or "OFF" end
        end
    end
    extras.ammo.Visible=compat.reason("InfiniteAmmo")==nil
    if compat.mode~="Native gun adapter" then extras.ammo.Visible=false end
    preset.quick.Position=UDim2.new(1,-16,.5,28)
    studio.quick.Position=UDim2.new(1,-16,.5,78)
    extras.ammo.Position=UDim2.new(1,-16,.5,128)
    healing.quick.Position=UDim2.new(1,-16,.5,178)
    defense.quick.Position=UDim2.new(1,-16,.5,228)
    healing.quick.Visible=game.PlaceId==126210570846701
    defense.quick.Visible=compat.reason("DefenseAssist")==nil
    healing.button.Visible=game.PlaceId==126210570846701
end
on(check.Activated,function()compat.detect();ui.enforceCapabilities();refresh();compat.update();notify("Game support",compat.mode.." / capabilities refreshed")end)
local elapsed=0
on(Run.Heartbeat,function(dt)
    elapsed+=dt;if elapsed<.5 then return end;elapsed=0
    if compat.held and (not C.GenericToolInput or not C.Bot and not C.AutoShoot and not C.TriggerBot or menuOpen or not focus) then compat.release() end
    compat.update()
end)
on(gui.Destroying,function()if compat.held then compat.release() end end)
compat.update()
end

heading(pages.Settings,"Session controls","Reset settings or unload. World changes restore automatically.")

local help=row(pages.Settings,"Help",mobile and "Touch controls" or "Cursor release",mobile and "MENU opens settings. Use the AIM pad during play." or "U unlocks your cursor. Right Shift toggles the menu.",124)
text(help,mobile and "Aim pauses while the menu is open.\nClose it to reveal the AIM control." or "Aim pauses while your cursor is free.\nMinimize the panel or press U to resume.",11,muted,
    UDim2.fromOffset(16,mobile and 85 or 65),UDim2.new(1,-32,0,32))
local reset=button(pages.Settings,"Reset","↺   RESET ALL SETTINGS",UDim2.new(),UDim2.new(1,0,0,40))
for key,value in pairs(C)do if type(value)=="boolean"then C[key]=false end end
C.ViewMode="Game default"
local defaults=table.clone(C)
defaults.Color="Background";defaults.Motion=true;defaults.Backdrop=68
on(reset.Activated,function() C=table.clone(defaults);C.Aim=false;selected=nil;latch=false;refresh() end)
local stop=button(pages.Settings,"Stop","×   UNLOAD NOVA",UDim2.new(),UDim2.new(1,0,0,40))
on(stop.Activated,function() gui:Destroy() end)
on(unload.Activated,function() gui:Destroy() end)
on(header.InputBegan,function(i)
    if i.UserInputType==Enum.UserInputType.MouseButton1 or i.UserInputType==Enum.UserInputType.Touch then drag={mouse=i.Position,pos=root.AbsolutePosition-overlay.AbsolutePosition,input=i} end
end)
on(UIS.InputChanged,function(i)
    if i.UserInputType~=Enum.UserInputType.MouseMovement and i.UserInputType~=Enum.UserInputType.Touch then return end
    if sliding and (i==sliding.input or i.UserInputType==Enum.UserInputType.MouseMovement and sliding.input.UserInputType==Enum.UserInputType.MouseButton1) then sliding.update(i.Position.X) end
    if drag and (i==drag.input or i.UserInputType==Enum.UserInputType.MouseMovement and drag.input.UserInputType==Enum.UserInputType.MouseButton1) then
        local camera=workspace.CurrentCamera;if not camera then return end
        local d=i.Position-drag.mouse;local size=root.AbsoluteSize;local v=overlay.AbsoluteSize
        root.Position=UDim2.fromOffset(math.clamp(drag.pos.X+d.X,0,math.max(0,v.X-size.X))+size.X/2,
            math.clamp(drag.pos.Y+d.Y,0,math.max(0,v.Y-size.Y))+size.Y/2)
    end
end)
on(UIS.InputEnded,function(i)
    if drag and (i==drag.input or i.UserInputType==Enum.UserInputType.MouseButton1 and drag.input.UserInputType==Enum.UserInputType.MouseButton1) then drag=nil end
    if sliding and (i==sliding.input or i.UserInputType==Enum.UserInputType.MouseButton1 and sliding.input.UserInputType==Enum.UserInputType.MouseButton1) then
        sliding=nil;for _,p in pairs(pages) do p.ScrollingEnabled=true end
    end
    if aimTouch==i then aimTouch=nil;touchHolding=false;gui:SetAttribute("TouchHolding",false);updateAimPad() end
end)
on(UIS.InputBegan,function(i)
    if UIS:GetFocusedTextBox() then return end
    if i.KeyCode==Enum.KeyCode.RightShift then showMenu(not menuOpen)
    elseif i.KeyCode==Enum.KeyCode.U and not mobile then setFree(not free)
    elseif not mobile and i.UserInputType==Enum.UserInputType.MouseButton2 and not free then latch=not latch end
end)
on(UIS.WindowFocusReleased,function() focus=false;releaseTouch();drag=nil;sliding=nil;for _,p in pairs(pages) do p.ScrollingEnabled=true end;updateAimPad() end)
on(UIS.WindowFocused,function() focus=true end)
local ring=make("Frame",{BackgroundTransparency=1,AnchorPoint=Vector2.new(.5,.5)},overlay)
make("UICorner",{CornerRadius=UDim.new(1,0)},ring)
local ringStroke=make("UIStroke",{Color=palettes[C.Color],Transparency=.5,Thickness=1},ring)
local motionTime=0
local parallax=Vector2.new()
local function animateChrome(dt)
    halo.Visible=root.Visible
    if root.Visible then
        if C.Motion then motionTime+=dt*C.AnimationSpeed/100 end
        local t=motionTime
        local target=Vector2.new()
        if C.Motion and C.Parallax and not mobile and free and focus then
            local mouse=UIS:GetMouseLocation()
            local center=root.AbsolutePosition+root.AbsoluteSize/2
            target=Vector2.new(math.clamp((mouse.X-center.X)/math.max(1,root.AbsoluteSize.X),-.5,.5),
                math.clamp((mouse.Y-center.Y)/math.max(1,root.AbsoluteSize.Y),-.5,.5))
        end
        if C.Motion then parallax=parallax:Lerp(target,1-math.exp(-dt*5)) else parallax=Vector2.new() end
        surfaceLight.Offset=Vector2.new((t*.13)%2.8-1.4,0)
        local edgeGradient=border:FindFirstChildOfClass("UIGradient")
        if edgeGradient then edgeGradient.Rotation=25+math.sin(t*.2)*35 end
        halo.Visible=root.Visible
        halo.Position=UDim2.fromOffset(root.AbsolutePosition.X-overlay.AbsolutePosition.X+root.AbsoluteSize.X/2,
            root.AbsolutePosition.Y-overlay.AbsolutePosition.Y+root.AbsoluteSize.Y/2)
        halo.Size=UDim2.fromOffset(root.AbsoluteSize.X+8,root.AbsoluteSize.Y+8)
        haloEdge.Transparency=.95+root.GroupTransparency*.05
        liquid.Position=UDim2.fromScale(-.04+math.sin(t*.14)*.018+parallax.X*.025,-.035+math.cos(t*.12)*.007+parallax.Y*.012)
        liquidSwap.Position=liquid.Position
        local mix=backgroundMix.Value
        liquid.ImageTransparency=1-C.Backdrop/100*(1-mix)
        liquidSwap.ImageTransparency=1-C.Backdrop/100*mix
        gui:SetAttribute("BackgroundLoaded",liquidSwap.IsLoaded)
        mist.Position=UDim2.fromScale(-.03+math.sin(t*.09)*.018,.26)
        mist.ImageTransparency=1-C.Backdrop/100*.15
        chrome.Offset=Vector2.new(math.sin(t*.25)*.3,0)
        shine.Offset=Vector2.new(math.sin(t*.3)*.65,0)
        liveDot.BackgroundTransparency=.15+(math.sin(t*1.7)+1)*.2
    end
end

local lastLayout=""
local function adaptiveLayout(view)
    if mobile then
        local w=math.max(260,math.min(600,view.X-24))
        local h=math.max(240,math.min(740,view.Y-24))
        local key=tostring(w).."x"..tostring(h)
        scale.Scale=reveal.Value
        if lastLayout==key then return end
        lastLayout=key;compact=true
        root.Size=UDim2.fromOffset(w,h);root.Position=UDim2.fromScale(.5,.5)
        brand.TextSize=24;brand.Position=UDim2.fromOffset(16,7);brand.Size=UDim2.fromOffset(180,34)
        heroTag.Visible=false;cursor.Visible=false
        liveDot.Position=UDim2.fromOffset(18,55)
        deviceBadge.Position=UDim2.fromOffset(30,47);deviceBadge.Size=UDim2.fromOffset(170,18)
        header.Size=UDim2.new(1,-112,0,76)
        minimize.Position=UDim2.new(1,-104,0,12);minimize.Size=UDim2.fromOffset(44,44)
        unload.Position=UDim2.new(1,-54,0,12);unload.Size=UDim2.fromOffset(44,44)
        for _,o in ipairs(root:GetChildren()) do
            if o:IsA("TextLabel") and o.Text:find("L I Q U I D",1,true) then o.Visible=false end
            if o:IsA("Frame") and o.Position.Y.Offset==143 then o.Position=UDim2.fromOffset(14,78);o.Size=UDim2.new(1,-28,0,1) end
        end
        side.Position=UDim2.fromOffset(12,88);side.Size=UDim2.new(1,-24,0,46)
        keybox.Visible=false;rail.Visible=false
        side.ScrollingEnabled=true;side.ScrollingDirection=Enum.ScrollingDirection.X;side.CanvasSize=UDim2.fromOffset(#tabNames*112,0)
        for _,o in ipairs(side:GetChildren()) do if o:IsA("TextLabel") then o.Visible=false end end
        for name,tab in pairs(tabs) do
            tab.Position=UDim2.fromOffset((tabIndex[name]-1)*112+4,0);tab.Size=UDim2.fromOffset(104,44)
            for _,o in ipairs(tab:GetChildren()) do
                if o:IsA("TextLabel") and o.Position.X.Offset==44 then o.Position=UDim2.fromOffset(36,0);o.Size=UDim2.new(1,-40,1,0) end
            end
        end
        indicator.Position=UDim2.fromOffset((tabIndex[activeTab]-1)*112+6,43);indicator.Size=UDim2.fromOffset(100,2)
        uiPagePosition=UDim2.fromOffset(14,187)
        for _,p in pairs(pages) do
            p.Position=uiPagePosition;p.Size=UDim2.new(1,-28,1,-233)
            if p:FindFirstChild("Heading") then p.Heading.Visible=view.Y>460 end
        end
        status.Position=UDim2.new(0,24,1,-34);status.Size=UDim2.new(1,-100,0,21);status.TextSize=9
        mini.Position=UDim2.new(1,-160,0,8);mini.Size=UDim2.fromOffset(148,44)
        touchAim.Position=UDim2.new(1,-100,1,-190)
        if view.Y<460 then touchAim.Position=UDim2.new(1,-104,1,-156) end
        gui:SetAttribute("LayoutWidth",w);gui:SetAttribute("LayoutHeight",h)
        gui:SetAttribute("LayoutOrientation",view.X>view.Y and "Landscape" or "Portrait")
    else
        scale.Scale=math.clamp(math.min((view.X-32)/880,(view.Y-90)/680,C.UISize/100),.3,1.25)*reveal.Value
    end
end
local function showDeviceWelcome()
    gui:SetAttribute("StartupMessage",deviceName.." / controls ready")
    local welcome=make("CanvasGroup",{Name="DeviceWelcome",AnchorPoint=Vector2.new(.5,.5),
        Position=UDim2.fromScale(.5,.5),Size=UDim2.fromOffset(270,160),GroupTransparency=1,
        BackgroundColor3=bg,BorderSizePixel=0,ZIndex=20},gui);round(welcome,16);outline(welcome,.35)
    local introScale=make("UIScale",{Scale=.92},welcome)
    animate(introScale,{Scale=1},.5,Enum.EasingStyle.Back)
    make("ImageLabel",{Image=image,BackgroundTransparency=1,ImageTransparency=.7,
        Size=UDim2.fromScale(1,1),ScaleType=Enum.ScaleType.Crop},welcome)
    text(welcome,"N O V A",11,ink,UDim2.fromOffset(22,17),UDim2.fromOffset(226,20))
    text(welcome,deviceName,36,ink,UDim2.fromOffset(22,46),UDim2.fromOffset(226,48)).Font=Enum.Font.GothamBold
    text(welcome,mobile and "Touch controls ready" or "Keyboard + mouse ready",12,ink,
        UDim2.fromOffset(22,109),UDim2.fromOffset(226,25))
    local progress=make("Frame",{Name="LoadingLine",BackgroundColor3=ink,BorderSizePixel=0,
        Position=UDim2.new(0,22,1,-12),Size=UDim2.new(0,0,0,2)},welcome)
    animate(progress,{Size=UDim2.new(1,-44,0,2)},.8)
    animate(welcome,{GroupTransparency=0},.28)
    task.delay(math.max(.7,motionDuration(1.05)),function()
        if not alive or not welcome.Parent then return end
        animate(welcome,{GroupTransparency=1},.25)
        task.delay(motionDuration(.3),function() if welcome.Parent then welcome:Destroy() end end)
    end)
end


-- Property ownership restores only changes still owned by this script.
local Lighting=game:GetService("Lighting")
local owned={}
local function own(instance,property,value)
    local ok,err=pcall(function()
        local state=owned[instance]
        if not state then state={};owned[instance]=state end
        local current=instance[property]
        local r=state[property]
        if not r then r={original=current};state[property]=r
        elseif r.last~=nil and current~=r.last then r.original=current end
        if current~=value then instance[property]=value end
        r.last=instance[property];r.used=true
    end)
    if not ok then gui:SetAttribute("WorldError",tostring(err)) end
end
local function releaseUnused(all)
    for instance,state in pairs(owned) do
        for property,r in pairs(state) do
            if all or not r.used then
                pcall(function() if instance[property]==r.last then instance[property]=r.original end end)
                state[property]=nil
            end
        end
        if next(state)==nil then owned[instance]=nil end
    end
end
restoreWorld=function() releaseUnused(true) end
applyWorld=function()
    for _,state in pairs(owned) do for _,r in pairs(state) do r.used=false end end
    if C.FullBright then
        own(Lighting,"Brightness",3)
        own(Lighting,"Ambient",Color3.new(1,1,1))
        own(Lighting,"OutdoorAmbient",Color3.new(1,1,1))
        own(Lighting,"ExposureCompensation",.25)
    elseif C.CustomLight then
        own(Lighting,"Brightness",C.LightBrightness)
        own(Lighting,"ExposureCompensation",C.LightExposure)
        local level=C.AmbientLevel/100
        own(Lighting,"Ambient",Color3.new(level,level,level))
        own(Lighting,"OutdoorAmbient",Color3.new(level,level,level))
    end
    if C.FullBright or C.NoShadows then own(Lighting,"GlobalShadows",false) end
    if C.TimeLock then own(Lighting,"ClockTime",C.DayTime%24) end
    if C.NoFog then own(Lighting,"FogStart",0);own(Lighting,"FogEnd",1000000) end
    local camera=workspace.CurrentCamera
    if C.CameraOverride and camera then own(camera,"FieldOfView",C.CameraFOV) end
    local effects={BloomEffect=C.NoBloom,BlurEffect=C.NoBlur,DepthOfFieldEffect=C.NoDOF,
        SunRaysEffect=C.NoSunRays,ColorCorrectionEffect=C.NoColor}
    if C.NoFog or C.NoBloom or C.NoBlur or C.NoDOF or C.NoSunRays or C.NoColor then
        local containers={Lighting};if camera then table.insert(containers,camera) end
        for _,container in ipairs(containers) do
            for _,effect in ipairs(container:GetChildren()) do
                if effect:IsA("Atmosphere") and C.NoFog then
                    own(effect,"Density",0);own(effect,"Haze",0);own(effect,"Glare",0)
                elseif effects[effect.ClassName] then own(effect,"Enabled",false) end
            end
        end
    end
    travel.update(own)
    extras.apply(own)
    shooter.apply(own)
    combat.update(own)
    releaseUnused(false)
end
local hud={frames=0,elapsed=0,started=os.clock(),history={},layoutKey="",ping=nil}
hud.fps=button(gui,"FPS","",UDim2.new(),UDim2.fromOffset(224,108))
hud.mark=button(gui,"Watermark","",UDim2.new(),UDim2.fromOffset(254,72))
for _,panel in ipairs({hud.fps,hud.mark}) do
    panel.BackgroundColor3=Color3.fromRGB(15,17,23);panel.ZIndex=8
    make("UIScale",{Name="HUDScale",Scale=1},panel)
    make("Frame",{Name="Accent",BackgroundColor3=ink,BorderSizePixel=0,
        Position=UDim2.fromOffset(0,12),Size=UDim2.new(0,3,1,-24),ZIndex=9},panel)
end
hud.fpsTitle=text(hud.fps,"PERFORMANCE",9,muted,UDim2.fromOffset(14,8),UDim2.fromOffset(150,16))
hud.fpsValue=text(hud.fps,"--",26,ink,UDim2.fromOffset(14,24),UDim2.fromOffset(100,30));hud.fpsValue.Name="Value";hud.fpsValue.Font=Enum.Font.GothamBold
hud.fpsUnit=text(hud.fps,"FPS",10,muted,UDim2.new(1,-50,0,33),UDim2.fromOffset(35,18))
hud.fpsDetail=text(hud.fps,"-- ms/frame   /   -- ms ping",10,muted,UDim2.fromOffset(14,57),UDim2.new(1,-28,0,18));hud.fpsDetail.Name="Details"
hud.graph=make("Frame",{Name="Graph",BackgroundTransparency=1,Position=UDim2.fromOffset(14,80),Size=UDim2.new(1,-28,0,20)},hud.fps)
hud.bars={}
for i=1,24 do
    hud.bars[i]=make("Frame",{Name="Sample"..i,BackgroundColor3=ink,BackgroundTransparency=.45,BorderSizePixel=0,
        AnchorPoint=Vector2.new(0,1),Position=UDim2.new((i-1)/24,0,1,0),Size=UDim2.new(1/24,-2,0,2)},hud.graph)
    round(hud.bars[i],2)
end
hud.logo=make("TextLabel",{Name="Logo",Text="N",Font=Enum.Font.GothamBold,TextSize=22,
    TextColor3=ink,BackgroundColor3=Color3.fromRGB(35,39,49),BorderSizePixel=0,
    Position=UDim2.fromOffset(13,12),Size=UDim2.fromOffset(34,34)},hud.mark);round(hud.logo,9)
hud.brand=text(hud.mark,"N O V A",14,ink,UDim2.fromOffset(58,10),UDim2.fromOffset(112,23));hud.brand.Name="Brand";hud.brand.Font=Enum.Font.GothamBold
hud.version=text(hud.mark,"v3.38",10,muted,UDim2.new(1,-60,0,13),UDim2.fromOffset(47,20));hud.version.Name="Version"
hud.markDetail=text(hud.mark,"",10,muted,UDim2.fromOffset(58,34),UDim2.new(1,-71,0,18));hud.markDetail.Name="Details"
for _,panel in ipairs({hud.fps,hud.mark}) do for _,child in ipairs(panel:GetDescendants()) do if child:IsA("GuiObject") then child.ZIndex=9 end end end
on(hud.fps.Activated,function() C.FPSDetails=not C.FPSDetails;refresh() end)
on(hud.mark.Activated,function() C.WatermarkDetails=not C.WatermarkDetails;refresh() end)
function hud.position(panel,corner,width,height,scaleValue,offset,view)
    local right=corner:find("right",1,true)~=nil
    local bottom=corner:find("Bottom",1,true)~=nil
    local reserve=not bottom and (not right or mobile) and 60 or 0
    panel.AnchorPoint=Vector2.new(right and 1 or 0,bottom and 1 or 0)
    panel.Position=UDim2.new(right and 1 or 0,right and -16 or 16,bottom and 1 or 0,bottom and -16-offset or 16+reserve+offset)
    panel.Size=UDim2.fromOffset(width,height);panel.HUDScale.Scale=scaleValue
end
function hud.layout(view)
    local key=table.concat({view.X,view.Y,C.HUDScale,C.HUDOpacity,tostring(C.ShowFPS),tostring(C.Watermark),tostring(C.FPSDetails),tostring(C.FPSGraph),tostring(C.WatermarkDetails),C.FPSCorner,C.WatermarkCorner},"|")
    if key==hud.layoutKey then return end;hud.layoutKey=key
    local scaleValue=math.min(C.HUDScale/100,math.max(.5,(view.X-32)/254))
    local fpsHeight=C.FPSDetails and (C.FPSGraph and 108 or 82) or 60
    local markHeight=C.WatermarkDetails and 64 or 48
    local stack=C.ShowFPS and C.Watermark and C.FPSCorner==C.WatermarkCorner
    local fpsOffset=stack and markHeight*scaleValue+10 or 0
    hud.position(hud.mark,C.WatermarkCorner,254,markHeight,scaleValue,0,view)
    hud.position(hud.fps,C.FPSCorner,224,fpsHeight,scaleValue,fpsOffset,view)
    hud.fps.Visible=C.ShowFPS;hud.mark.Visible=C.Watermark
    hud.fpsDetail.Visible=C.FPSDetails;hud.graph.Visible=C.FPSDetails and C.FPSGraph
    hud.markDetail.Visible=C.WatermarkDetails
    hud.logo.Size=UDim2.fromOffset(C.WatermarkDetails and 34 or 28,C.WatermarkDetails and 34 or 28)
    hud.logo.Position=UDim2.fromOffset(13,C.WatermarkDetails and 12 or 10)
    for _,panel in ipairs({hud.fps,hud.mark}) do
        panel:SetAttribute("RestAlpha",1-C.HUDOpacity/100);panel.BackgroundTransparency=1-C.HUDOpacity/100
    end
end
function hud.sample(dt,view)
    hud.layout(view)
    hud.frames+=1;hud.elapsed+=dt
    if hud.elapsed<.25 then return end
    local value=math.floor(hud.frames/math.max(.001,hud.elapsed)+.5)
    local ms=hud.elapsed/math.max(1,hud.frames)*1000
    hud.frames=0;hud.elapsed=0
    gui:SetAttribute("FPS",value);gui:SetAttribute("FrameTimeMs",ms)
    hud.fpsValue.Text=tostring(value)
    local color=value>=55 and Color3.fromRGB(105,231,177) or value>=30 and Color3.fromRGB(255,205,113) or Color3.fromRGB(255,119,137)
    hud.fpsValue.TextColor3=color
    table.insert(hud.history,value);if #hud.history>24 then table.remove(hud.history,1) end
    local maximum=60
    for _,n in ipairs(hud.history) do maximum=math.max(maximum,n) end
    for i,bar in ipairs(hud.bars) do
        local sample=hud.history[i-(24-#hud.history)]
        bar.Size=UDim2.new(1/24,-2,0,sample and math.max(2,sample/maximum*20) or 2)
        bar.BackgroundTransparency=sample and .2 or .8;bar.BackgroundColor3=color
    end
    hud.ping=nil
    pcall(function()
        local ping=game:GetService("Stats").Network.ServerStatsItem["Data Ping"]:GetValue()
        if type(ping)=="number" and ping>=0 then hud.ping=ping end
    end)
    if hud.ping then gui:SetAttribute("HUDPingMs",hud.ping) else gui:SetAttribute("HUDPingMs",nil) end
    hud.fpsDetail.Text=string.format("%.1f ms/frame  /  %s ms ping",ms,hud.ping and tostring(math.floor(hud.ping+.5)) or "--")
    local elapsed=math.max(0,math.floor(os.clock()-hud.started))
    hud.markDetail.Text=string.format("%s  /  %d players  /  %02d:%02d",deviceName,#Players:GetPlayers(),math.floor(elapsed/60),elapsed%60)
    hud.logo.TextColor3=ink;hud.brand.TextColor3=ink;hud.version.TextColor3=muted
    hud.fpsTitle.TextColor3=muted;hud.fpsUnit.TextColor3=muted;hud.fpsDetail.TextColor3=muted;hud.markDetail.TextColor3=muted
    hud.fps.Accent.BackgroundColor3=color;hud.mark.Accent.BackgroundColor3=ink
end
local cross=make("Frame",{Name="Crosshair",BackgroundTransparency=1,Size=UDim2.fromOffset(0,0)},overlay)
local arms={}
for i=1,4 do arms[i]=make("Frame",{BorderSizePixel=0,BackgroundColor3=ink},cross) end
local centerDot=make("Frame",{BorderSizePixel=0,BackgroundColor3=ink,AnchorPoint=Vector2.new(.5,.5),
    Size=UDim2.fromOffset(4,4)},cross);round(centerDot,2)
local worldTime=0
local function displayExtras(dt,camera)
    worldTime+=dt
    hud.sample(dt,camera.ViewportSize)
    if worldTime>=.12 then worldTime=0;applyWorld() end
    cross.Visible=C.Crosshair and focus and not menuOpen
    local p=camera.ViewportSize/2-overlay.AbsolutePosition
    cross.Position=UDim2.fromOffset(p.X,p.Y)
    cross.Rotation=C.CrosshairStyle=="Cross" and 45 or 0
    local length,gap=C.CrosshairSize,C.CrosshairGap
    local col=palettes[C.Color]
    for i,arm in ipairs(arms) do
        arm.Visible=C.CrosshairStyle~="Dot";arm.BackgroundColor3=col
        if i==1 then arm.Position=UDim2.fromOffset(-gap-length,-1);arm.Size=UDim2.fromOffset(length,2)
        elseif i==2 then arm.Position=UDim2.fromOffset(gap,-1);arm.Size=UDim2.fromOffset(length,2)
        elseif i==3 then arm.Position=UDim2.fromOffset(-1,-gap-length);arm.Size=UDim2.fromOffset(2,length)
        else arm.Position=UDim2.fromOffset(-1,gap);arm.Size=UDim2.fromOffset(2,length) end
    end
    centerDot.Visible=C.CrosshairStyle=="Dot";centerDot.BackgroundColor3=col
end

local ray=RaycastParams.new(); ray.FilterType=Enum.RaycastFilterType.Exclude
local function remove(p)
    local r=records[p]
    if r then r.highlight:Destroy(); r.billboard:Destroy(); records[p]=nil end
end
local elapsed=math.huge
local roster=Players:GetPlayers()
local function trackPlayer(p)
    on(p.CharacterAdded,function() remove(p);elapsed=math.huge end)
    on(p.CharacterRemoving,function() remove(p);elapsed=math.huge end)
end
for _,p in ipairs(roster) do trackPlayer(p) end
on(Players.PlayerAdded,function(p) table.insert(roster,p);trackPlayer(p);elapsed=math.huge end)
on(Players.PlayerRemoving,function(p)
    remove(p);local i=table.find(roster,p);if i then table.remove(roster,i) end
    elapsed=math.huge
end)
local function setESP(instance,key,value)
    if instance[key]~=value then instance[key]=value end
end
local function character(p)
    local ch=p.Character
    if not ch or not ch:IsDescendantOf(workspace) then return end
    local h=ch:FindFirstChildOfClass("Humanoid")
    local part=ch:FindFirstChild(C.Part) or ch:FindFirstChild("HumanoidRootPart") or ch:FindFirstChild("Head") or ch.PrimaryPart
    if h and h.Health>0 and part and part:IsA("BasePart") then return ch,h,part end
end
local function eligible(p)
    return p~=LP and (not C.Teams or LP.Neutral or p.Neutral or LP.Team==nil or p.Team~=LP.Team)
end
local function record(p,ch)
    local r=records[p]; if r and r.character~=ch then remove(p); r=nil end
    if r then return r end
    r={character=ch}
    r.highlight=make("Highlight",{Name="Player_"..p.UserId,Adornee=ch},world)
    r.billboard=make("BillboardGui",{Name="Label_"..p.UserId,Size=UDim2.fromOffset(220,66),
        StudsOffsetWorldSpace=Vector3.new(0,3,0),AlwaysOnTop=true,MaxDistance=C.Range},pg)
    r.label=text(r.billboard,"",12,ink,UDim2.new(),UDim2.new(1,0,0,48))
    r.label.TextXAlignment=Enum.TextXAlignment.Center; r.label.TextStrokeTransparency=.35
    r.bar=make("Frame",{BackgroundColor3=Color3.fromRGB(43,48,62),BorderSizePixel=0,
        Position=UDim2.fromOffset(50,54),Size=UDim2.fromOffset(120,4)},r.billboard); round(r.bar,2)
    r.fill=make("Frame",{BorderSizePixel=0,BackgroundColor3=Color3.fromRGB(103,230,160),Size=UDim2.fromScale(1,1)},r.bar); round(r.fill,2)
    records[p]=r; return r
end
local function cleanup()
    if not alive then return end
    alive=false;Run:UnbindFromRenderStep(binding);studio.destroy()
    for _,connection in ipairs(cons)do pcall(function()connection:Disconnect()end)end
    table.clear(cons)
    C.Healing=false;healing.epoch+=1;healing.release()
    C.Bot=false;bot.stop("Unloaded")
    extras.destroy()
    C.AutoClicker=false;C.InfiniteAmmo=false;C.AutoReload=false;utility.alive=false
    alive=false; Run:UnbindFromRenderStep(binding)
    C.Fly=false;C.WallShots=false;C.TriggerBot=false;C.AutoShoot=false;autofire.reset("Unloaded");cooldown.restore();flight.stop();combat.restore()
    combat.clearBoxes();combat.displayFolder:Destroy()
    for _,c in ipairs(cons) do c:Disconnect() end
    for _,state in pairs(anims) do for _,tw in pairs(state) do tw:Cancel() end end
    for p in pairs(records) do remove(p) end
    world:Destroy()
    if travel.marker then travel.marker:Destroy();travel.marker=nil end
    if restoreWorld then restoreWorld() end
    if not mobile then UIS.MouseBehavior=lastMouse; UIS.MouseIconEnabled=lastIcon end
end
on(gui.Destroying,cleanup)
local espVisible,espAvailable,lastCount,lastTotal=0,0,0,0
local aim={}
function aim.point(part)
    local point=part.Position
    if C.AimPrediction then
        local velocity=part.AssemblyLinearVelocity
        if velocity.Magnitude<200 then point+=velocity*(C.AimLead/1000) end
    end
    return point
end
function aim.candidate(ch,primary,cam,origin,ray)
    local candidates={primary}
    if C.AimFallback then
        for _,name in ipairs({"Head","UpperTorso","Torso","LowerTorso"}) do
            local part=ch:FindFirstChild(name)
            if part and part:IsA("BasePart") and part~=primary then table.insert(candidates,part) end
        end
    end
    for _,part in ipairs(candidates) do
        local point=aim.point(part)
        local projected,onScreen=cam:WorldToViewportPoint(point)
        local pixels=(Vector2.new(projected.X,projected.Y)-origin).Magnitude
        if onScreen and projected.Z>0 and pixels<C.FOV then
            local hit=C.Visibility and not C.AimThroughWalls and workspace:Raycast(cam.CFrame.Position,point-cam.CFrame.Position,ray)
            if not hit or hit.Instance:IsDescendantOf(ch) then return part,point,pixels end
        end
    end
end
function aim.score(p,h,distance,pixels)
    local score=pixels/math.max(1,C.FOV)
    if C.AimPriority=="Nearest" then score=distance/math.max(1,C.Range)
    elseif C.AimPriority=="Lowest health" then score=h.Health/math.max(1,h.MaxHealth)+pixels/math.max(1,C.FOV)*.01 end
    if C.Sticky and selected==p then score*=1-C.AimRetention/100 end
    return score
end
function aim.follow(current,point,dt)
    if (point-current.Position).Magnitude<.001 then return current end
    local goal=CFrame.lookAt(current.Position,point)
    local alpha=C.AimResponse=="Instant" and 1 or 1-math.exp(-C.Speed*math.clamp(dt,0,.1))
    return current:Lerp(goal,alpha)
end

local function frame(dt)
    local cam=workspace.CurrentCamera; if not cam then return end
    adaptiveLayout(TEST_VIEWPORT or (mobile and overlay.AbsoluteSize or cam.ViewportSize))
    studio.layout(cam.ViewportSize)
    if C.EditorOpen and not mobile and not free then setFree(true)end
    ui.layoutSearch()
    animateChrome(dt)
    displayExtras(dt,cam)
    flight.step(dt,cam)
    extras.step(dt,cam)
    if not mobile then
        if free then UIS.MouseBehavior=Enum.MouseBehavior.Default;UIS.MouseIconEnabled=true
        else UIS.MouseBehavior=Enum.MouseBehavior.LockCenter end
    end
    local origin=not mobile and C.Origin=="Mouse" and UIS:GetMouseLocation() or cam.ViewportSize/2
    ring.Position=UDim2.fromOffset(origin.X-overlay.AbsolutePosition.X,origin.Y-overlay.AbsolutePosition.Y); ring.Size=UDim2.fromOffset(C.FOV*2,C.FOV*2)
    ring.Visible=C.Circle and focus and (not C.HideRingMenu or not menuOpen); ringStroke.Color=palettes[C.Color]
    ray.FilterDescendantsInstances=LP.Character and {LP.Character,world} or {world}
    local localCh=LP.Character; local ownHum=localCh and localCh:FindFirstChildOfClass("Humanoid")
    local ownRoot=localCh and localCh:FindFirstChild("HumanoidRootPart")
    local localPosition=ownRoot and ownRoot.Position or cam.CFrame.Position
    local active=C.Aim and not C.EditorOpen and not C.Healing and not C.Bot and (mobile and not menuOpen or not mobile and not free) and focus and not UIS:GetFocusedTextBox() and ownHum and ownHum.Health>0
        and (C.Mode=="Always" or ((C.Mode=="Toggle RMB" or C.Mode=="Toggle AIM") and latch)
        or (mobile and C.Mode=="Hold AIM" and touchHolding)
        or (not mobile and C.Mode=="Hold RMB" and UIS:IsMouseButtonPressed(Enum.UserInputType.MouseButton2)))
    elapsed+=dt
    local update=elapsed>=C.ESPInterval/1000; if update then elapsed=0;espVisible=0;espAvailable=0 end
    local best,chosen,chosenPart,chosenPoint=math.huge,nil,nil,nil
    local count,total=lastCount,lastTotal
    if update or active then
    count,total=0,0
    for _,p in ipairs(roster) do
        if p~=LP then total+=1 end
        local ch,h,part=character(p)
        local valid=eligible(p) and ch~=nil
        local distance=valid and (part.Position-localPosition).Magnitude or math.huge
        valid=valid and distance<=C.Range
        if valid then count+=1 end
        if update then
            local espValid=p~=LP and ch~=nil and (C.ESPAll or eligible(p))
            local espDistance=ch and (part.Position-localPosition).Magnitude or math.huge
            if p~=LP and ch then espAvailable+=1 end
            espValid=espValid and (C.ESPUnlimited or espDistance<=C.Range)
            if espValid and C.ESP then
                espVisible+=1
                local r=record(p,ch); local col=palettes[C.Color]
                setESP(r.highlight,"Enabled",true)
                setESP(r.highlight,"FillColor",col);setESP(r.highlight,"OutlineColor",col)
                setESP(r.highlight,"FillTransparency",C.Opacity/100);setESP(r.highlight,"OutlineTransparency",C.OutlineFade/100)
                setESP(r.highlight,"DepthMode",C.Through and Enum.HighlightDepthMode.AlwaysOnTop or Enum.HighlightDepthMode.Occluded)
                setESP(r.billboard,"Adornee",ch:FindFirstChild("Head") or part)
                setESP(r.billboard,"AlwaysOnTop",C.Through)
                setESP(r.billboard,"MaxDistance",C.ESPUnlimited and 0 or math.min(C.Range,C.LabelRange))
                setESP(r.billboard,"StudsOffsetWorldSpace",Vector3.new(0,C.LabelHeight,0));setESP(r.label,"TextSize",C.LabelSize)
                setESP(r.billboard,"Enabled",C.Names or C.Health or C.Distance)
                local lines={}
                if C.Names then table.insert(lines,p.DisplayName==p.Name and p.Name or p.DisplayName.."  (@"..p.Name..")") end
                if C.Distance then table.insert(lines,math.floor(espDistance).." studs") end
                if C.Health then table.insert(lines,math.ceil(h.Health).." / "..math.ceil(h.MaxHealth).." HP") end
                setESP(r.label,"Text",table.concat(lines,"\n"));setESP(r.label,"TextColor3",ink);setESP(r.bar,"Visible",C.Health)
                local ratio=math.clamp(h.Health/math.max(1,h.MaxHealth),0,1)
                setESP(r.fill,"Size",UDim2.fromScale(ratio,1))
                setESP(r.fill,"BackgroundColor3",Color3.fromRGB(255,100,120):Lerp(Color3.fromRGB(103,230,160),ratio))
            else remove(p) end
        end
        if active and valid then
            local target,point,pixels=aim.candidate(ch,part,cam,origin,ray)
            if target then
                local score=aim.score(p,h,distance,pixels)
                if score<best then best=score;chosen=p;chosenPart=target;chosenPoint=point end
            end
        end
    end
    lastCount,lastTotal=count,total
    end
    if update then
        gui:SetAttribute("ESPVisiblePlayers",espVisible);gui:SetAttribute("ESPAvailablePlayers",espAvailable)
        gui:SetAttribute("ESPRefreshMs",C.ESPInterval)
    end
    selected=chosen
    if chosenPoint then
        cam.CFrame=aim.follow(cam.CFrame,chosenPoint,dt)
        ringStroke.Color=Color3.fromRGB(103,230,160)
    end
    local utilityOk,utilityErr=pcall(utility.step)
    if not utilityOk then gui:SetAttribute("UtilityError",tostring(utilityErr));utility.status("Utility error; see diagnostic attribute.") end
    if not C.Bot then autofire.step(cam,chosen,chosenPoint) else autofire.reset("Bot owns firing") end
    local botOk,botErr=xpcall(function()bot.step(dt,cam)end,traceError)
    if not botOk then gui:SetAttribute("LastBotError",tostring(botErr));C.Bot=false;bot.stop("Bot error: "..tostring(botErr):sub(1,240));refresh() end
    gui:SetAttribute("LastFrame",os.clock())
    gui:SetAttribute("EligiblePlayers",count); gui:SetAttribute("OtherPlayers",total)
    gui:SetAttribute("Tracking",chosen and chosen.Name or "")
    gui:SetAttribute("TrackingPart",chosenPart and chosenPart.Name or "")
    if update then
        local mode=mobile and (menuOpen and "Menu open / aim paused" or not C.Aim and "Aim disabled / open MENU" or chosen and "Tracking "..chosen.DisplayName or "Ready / "..C.Mode) or (free and "Cursor free - press U or minimize to aim" or not C.Aim and "Aim disabled" or chosen and ("Tracking "..chosen.DisplayName) or ("Aim ready - "..C.Mode))
        status.Text=mode.."    /    "..count.." eligible of "..total.." players"
    end
end
for key,value in pairs(C)do if type(value)=="boolean"then C[key]=false end end
C.ViewMode="Game default"
setFree(true)
gui:SetAttribute("Version","3.38")
gui:SetAttribute("CompatibilityProfile","Portable filesystem and custom-image fallbacks")
if type(NOVA_ASSET_STATUS)=="table" then
    gui:SetAttribute("AssetFilesReady",NOVA_ASSET_STATUS.ready)
    gui:SetAttribute("AssetFilesFailed",NOVA_ASSET_STATUS.failed)
end
if type(identifyexecutor)=="function" then
    pcall(function()gui:SetAttribute("Executor",tostring(identifyexecutor()))end)
end
gui:SetAttribute("DeviceMode",deviceName)
gui:SetAttribute("TouchHolding",false)
gui:SetAttribute("Theme","Liquid Chrome")
selectTab("Game")
showMenu(true)
showDeviceWelcome()
Run:BindToRenderStep(binding,Enum.RenderPriority.Last.Value+100,function(dt)
    if not alive then return end
    local ok,err=xpcall(function() frame(dt) end,traceError)
    if not ok then
        gui:SetAttribute("LastError",tostring(err)); status.Text="Runtime error - see console"
        warn("NOVA: "..tostring(err)); Run:UnbindFromRenderStep(binding)
    end
end)
return "NOVA SHOOTER / "..deviceName.." loaded."
NOVA · RoScripts