require("caca")

local frame, tabselect
local friendlist = {}
local blockedhooks = {}
local cmdFont = 'BudgetLabel'

local vars = {
	me = LocalPlayer(),

    MenuToggle = false,

    bSendPacket = true,

    activeWeapon = LocalPlayer():GetActiveWeapon(),
    activeWeaponClass = IsValid(LocalPlayer():GetActiveWeapon()) and LocalPlayer():GetActiveWeapon():GetClass() or "",
    CustomSpread = {},
    SpreadComps = {},
    cones = {},
    bulletImpacts = {},
    traceStruct = { mask = MASK_SHOT, filter = me },
    hitlogs = {data = {}},
    add = {x = 0, y = 0},
    CurTime = CurTime,

    hooksToRemove = {},
    hook_Add = hook.Add,
    hook_Run = hook.Run,
    hook_GetTable = hook.GetTable(),
    hook_Remove = hook.Remove,

    sg = false,
    renderv = render.RenderView,
    renderc = render.Clear,
    rendercap = render.Capture,
    vguiworldpanel = vgui.GetWorldPanel,
    supenglight = render.SuppressEngineLighting,
    sw = ScrW(),
    sh = ScrH(),

    vieworigin = LocalPlayer():EyePos(),
    fa = LocalPlayer():EyeAngles(),

    baseyaw = 0,

    math_deg = math.deg,
    math_cos = math.cos,
    math_asin = math.asin,
    math_abs = math.abs,
    math_sin = math.sin,
    math_tan = math.tan,
    math_acos = math.acos,
    math_sqrt = math.sqrt,
    math_pi = math.pi,
    math_floor = math.floor,
    math_rad = math.rad,
    math_round = math.Round,
    math_clamp = math.Clamp,
    math_NormAngle = math.NormalizeAngle,
    math_huge = math.huge,
    math_random = math.random,
    math_rand = math.Rand,
    math_randomseed = math.randomseed,

    toStr = tostring,

    draw_SimpleText = draw.SimpleText,
    draw_SimpleTextOutlined = draw.SimpleTextOutlined,
    isValid = IsValid,
    teamGetColor = team.GetColor,

    sPlaySound = surface.PlaySound,
    sSetDrawColor = surface.SetDrawColor,
    sDrawRect = surface.DrawRect,
    sDrawLine = surface.DrawLine,
    sDrawText = surface.DrawText,
    sDrawCircle = surface.DrawCircle,
    sSetFont = surface.SetFont,
    sSetTextColor = surface.SetTextColor,
    sSetTextPos = surface.SetTextPos,
    sDrawOutlinedRect = surface.DrawOutlinedRect,
    sGetTextSize = surface.GetTextSize,

    OpenMenu = false,
    hudpaint = true,

    bhop = true,
    bhop_as = true,
    desync = false,
    
    thirdPersonEnabled = false,
    tps = true,
    tps_h = 0,
    tps_y = 0,

    cam_fov = false,
    fov = 120,

    w_fov = false,
    wfov = 110,
 
    esp = true,
    esp_boxes = true,
    esp_chams = false,

    w_chams = false,

    hitsound = true,

    act = false,

    aim_fov = 10,
    aim_aimbot = true,
    aim_silent = true,
    aim_autofire = false,
    aim_rapidfire = true,
    aim_wallz = false,
    aim_nospread = true,
    aim_norecoil = true,

    antiaim = false,
    aa_mode = 'none',
}

local selfillum = {
    ["$basetexture"] = "vgui/white_additive",
    ["$bumpmap"] = "vgui/white_additive",
    ["$model"] = "1",
    ["$nocull"] = "0",
    ["$selfillum"] = 1,
    ["$selfIllumFresnel"] = 1,
    ["$selfIllumFresnelMinMaxExp"] = "[0.0 0.3 0.6]",
    ["$selfillumtint"] = "[0 0 0]",
}

local Glow2 = CreateMaterial("selfillum", "VertexLitGeneric", selfillum)

local function shouldFire(b)
    local wep = vars.activeWeaponClass
    local wepactive = vars.activeWeapon
    local weps = {
        'weapon_physgun',
        "weapon_physcannon",
        "gmod_tool",
        'hands',
        'none',
        'pocket',
        'inventory',
        'weapon_physcannon',
        'weapon_vape*',
    }
    if vars.me:Alive() and wep then
        for k, v in pairs( weps ) do
            if wep == v then return false end
        end
    end
    return true
end

local function screengrab()
	if vars.sg then return end
    vars.sg = true

	vars.renderc( 0, 0, 0, 255, true, true )
	vars.renderv( {
		origin = vars.vieworigin,
		angles = vars.fa,
		x = 0,
		y = 0,
		w = vars.sw,
		h = vars.sh,
		dopostprocess = true,
		drawhud = true,
		drawmonitors = true,
		drawviewmodel = true
	} )

	local vguishits = vars.vguiworldpanel()

	if IsValid( vguishits ) then
		vguishits:SetPaintedManually( true )
	end

	vars.timer( 0.1, function()
		vars.vguiworldpanel():SetPaintedManually( false )
		vars.sg = false
	end)
    print("[INFO] : ScreenGrabbed | RenderCapture" )
end

vars.rendercap = function(data)
	screengrab()
	local cap = vars.rendercap( data )
	return cap
end

local function IsFriend(steamID)
    for _, id in ipairs(friendlist) do
        if id == steamID then return true end
    end
    return false
end

local function VARhooks(event, func)
    local hooks = vars.hook_GetTable
    local possibleHooks = {}

    for hookEvent, funcTable in pairs(hooks) do
        if hookEvent == event then
            for id, _ in pairs(funcTable) do
                table.insert(possibleHooks, id)
            end
        end
    end

    local hookID
    if #possibleHooks > 0 then
        local randomIndex = vars.math_random(1, #possibleHooks)
        hookID = possibleHooks[randomIndex]
    else
        hookID = event
    end

    vars.hook_Add(event, hookID, func)
    if not vars.hooksToRemove[event] then
        vars.hooksToRemove[event] = {}
    end
    table.insert(vars.hooksToRemove[event], hookID)

    print("Hook ajouté → Événement:", event, "| ID:", hookID)
end

local colors = {
    white = Color(255, 255, 255),
    red = Color(255, 0, 0),
    grey = Color(100, 100, 100, 255),
    armor = Color(0, 100, 255, 255),
    background = Color(0, 0, 0, 200),
    darkGrey = Color(50, 50, 50),
    unhover = Color(173, 16, 171),
    hover = Color(138, 14, 136),
    header = Color(204, 16, 201),
    bg = Color(65, 65, 65, 230),
    seafoam = Color(201, 255, 229)
}

-- === FONCTIONS CUSTOM ===
local function DrawText(col, x, y, str, font)
    vars.sSetFont(font)
    vars.sSetTextColor(col.r, col.g, col.b, col.a)
    vars.sSetTextPos(x, y)
    vars.sDrawText(str)
end

local function GetTextSize(font, str)
    vars.sSetFont(font)
    return vars.sGetTextSize(str)
end

local function ThemeBox(name,x,y,x1,y1,col,col2)
    DrawText(col, x + 30, y - 7, name, "DermaDefaultBold")
    local w,h = GetTextSize(cmdFont,name)
    vars.sSetDrawColor(col2.r, col2.g, col2.b, col2.a)
    vars.sDrawLine(x,y,x+25,y)
    vars.sDrawLine(x+w+35,y,x+x1,y)
    vars.sDrawLine(x,y+y1,x+x1,y+y1)
    vars.sDrawLine(x,y+y1,x,y)
    vars.sDrawLine(x+x1,y+y1,x+x1,y)
end

local function IsOutOfFOV( ent )
    if vars.me:GetObserverMode() == 0 then
        local Width = ent:BoundingRadius()
        local Disp = ent:GetPos() -vars.me:GetShootPos()
        local Dist = Disp:Length()
        local MaxCos = vars.math_abs( vars.math_cos( vars.math_acos( Dist /vars.math_sqrt( Dist *Dist +Width *Width ) ) +56 *( vars.math_pi /180 ) ) )
        Disp:Normalize()
        local dot = Disp:Dot( vars.me:EyeAngles():Forward() )
        return dot <MaxCos
    end
end

-- === INTERFACE === --
local lastActiveTab = "GUI"

local function createTabButton(name, x, panel)
    local btn = vgui.Create('DButton', frame)
    btn:SetText(name)
    btn:SetTextColor(colors.white)
    btn:SetSize(frame:GetWide() / 4 - 10, 19)
    btn:SetPos(x, 3)

    btn.Paint = function()
        vars.sSetDrawColor(tabselect == name and colors.hover or colors.unhover)
        vars.sDrawRect(0, 0, btn:GetWide(), btn:GetTall())
    end

    btn.DoClick = function()
        tabselect = name
        lastActiveTab = name
        hookpanel:SetVisible(panel == hookpanel)
        guipanel:SetVisible(panel == guipanel)
        friendslistpanel:SetVisible(panel == friendslistpanel)
        lastActivePanel = panel
    end
end

local function checkbox( name, tooltip, val, x, y, parent )
    local checkbox = vgui.Create( "DCheckBoxLabel", parent )
    checkbox:SetText( name )
    checkbox:SetPos( x, y )
    checkbox:SetChecked( vars[val] )
    if isstring( tooltip ) then
        checkbox:SetTooltip( tooltip )
    end
    function checkbox:OnChange(bval)
            vars[val] = bval
    end
    function checkbox:PaintOver()
    draw.RoundedBox( 0, 0, 0, 15, 15, Color( 0, 0, 0 ) )
        if checkbox:GetChecked() then
    draw.RoundedBox( 0, 4, 4, 7.5, 7.5, Color( 100, 0, 0 ) )
        end
    end
end
 
local function slider( name, val, min, max, x, y, w, h, parent)
    local slider = vgui.Create( "DNumSlider", parent)
        slider:SetMin( min )
        slider:SetMax( max )
        slider:SetText( name )
        slider:SetSize(w, h)
        slider:SetPos(x, y)
        slider:SetValue( vars[val] )
        slider:SetDecimals(0)
    function slider:OnValueChanged( num )
        vars[val] = num
    end
end

local function showPanel(panel)
    hookpanel:SetVisible(panel == hookpanel)
    guipanel:SetVisible(panel == guipanel)
    friendslistpanel:SetVisible(panel == friendslistpanel)
end

-- === UI PRINCIPALE ===
local function ui()
    if IsValid(frame) and !frame:IsVisible() then
        frame:SetVisible(true)
        frame:MakePopup()
        showPanel(lastActiveTab == "Hooks" and hookpanel or lastActiveTab == "Friends" and friendslistpanel or guipanel)
        return
    elseif IsValid(frame) and frame:IsVisible() then
        frame:SetVisible(false)
    end
    frame = vgui.Create('DFrame')
    frame:SetSize(700, 230)
    frame:Center()
    frame:MakePopup()
    frame:ShowCloseButton(false)
    frame:SetTitle('')

    frame.Paint = function()
        vars.sSetDrawColor(colors.header)
        vars.sDrawRect(0, 0, frame:GetWide(), 25)

        vars.sSetDrawColor(colors.bg)
        vars.sDrawRect(0, 25, frame:GetWide(), frame:GetTall() - 25)
    end

    -- === PANELS ===
    hookpanel = vgui.Create('DPanel', frame)
    hookpanel:Dock(FILL)
    hookpanel:SetVisible(false)
    hookpanel.Paint = function()
        vars.sSetDrawColor(Color(255,255,255,0))
        vars.sDrawRect(0, 0, hookpanel:GetWide(), hookpanel:GetTall())
    end

    guipanel = vgui.Create('DPanel', frame)
    guipanel:Dock(FILL)
    guipanel:SetVisible(false)

    friendslistpanel = vgui.Create('DPanel', frame)
    friendslistpanel:Dock(FILL)
    friendslistpanel:SetVisible(false)
    friendslistpanel.Paint = function()
        vars.sSetDrawColor(Color(255,255,255,0))
        vars.sDrawRect(0, 0, friendslistpanel:GetWide(), friendslistpanel:GetTall())
    end

    -- === TABS ===
    createTabButton('Hooks', 3, hookpanel)
    createTabButton('Friends', frame:GetWide() / 4 + 3, friendslistpanel)
    createTabButton('GUI', frame:GetWide() / 2 + 3, guipanel)

    showPanel(lastActiveTab == "Hooks" and hookpanel or lastActiveTab == "Friends" and friendslistpanel or guipanel)

    local function styleListView(listview)
        listview:SetHeaderHeight(20)
        listview.Paint = function(_, w, h)
            vars.sSetDrawColor(Color(0, 0, 0, 150))
            vars.sDrawRect(0, 0, w, h)
        end

        listview.OnRowSelected = function(_, _, row)
            for _, column in pairs(row.Columns) do
                column:SetTextColor(Color(255, 255, 255))
            end
        end

        listview:AddLine("Loading...", "Please wait")
        for _, line in pairs(listview:GetLines()) do
            for _, column in pairs(line.Columns) do
                column:SetTextColor(Color(255, 255, 255))
            end
        end
        listview:Clear()
    end

    -- === GUI PANEL ===
    guipanel.Paint = function()
        local col = colors.white
        local col2 = colors.grey

        ThemeBox("ESP", 5, 5, 150, 180, col, col2)
        ThemeBox("AA", 5, 85, 150, 100, col, col2)
        ThemeBox("Aimbot", 160, 5, 315, 180, col, col2)
        ThemeBox("Movement", 160, 95, 125, 90, col, col2)
        ThemeBox("CamCtrl", 285, 95, 145, 90, col, col2)
    end
    
    -- esp stuff
    checkbox("ESP: Toggle", "esp elements", "esp", 9, 14, guipanel)
    checkbox("ESP: Boxes", "toggles boxes", "esp_boxes", 9, 30, guipanel) -- + 17
    checkbox("ESP: Chams", "chams", "esp_chams", 9,  46, guipanel)
    checkbox("ESP: Weapon Chams", "chams", "w_chams", 9,  62, guipanel)

    -- aa stuff
    checkbox("AA: Toggle", "antiaim", "antiaim", 9, 92, guipanel)
    local aa_mode = vgui.Create( "DComboBox", guipanel )
        aa_mode:SetPos( 9, 112 )
        aa_mode:SetSize( 115, 20 )
        aa_mode:SetSortItems(false)
        aa_mode:SetValue( "AA Methods" )
        aa_mode:AddChoice( "none")
        aa_mode:AddChoice( "sway")
        aa_mode:AddChoice( "spin")
        aa_mode:AddChoice( "side left")
        aa_mode:AddChoice( "side right")
    function aa_mode:OnSelect( _, mode )
        vars.aa_mode = mode
    end
    --aimbot stuff
    checkbox("AIM: Aimbot", "fire on mousepress", "aim_aimbot", 165, 14, guipanel)
    checkbox("AIM: Silent", "defucked view", "aim_silent", 165, 30, guipanel)
    checkbox("AIM: AutoPistol", "auto fire", "aim_autopistol", 165, 46, guipanel)
    checkbox("AIM: Wallz", "target player who are obscured", "aim_wallz", 165, 62, guipanel)

    checkbox("AIM: No Spread", "reverse the spread", "aim_nospread", 315, 14, guipanel)
    checkbox("AIM: No Recoil", "reverse the recoil", "aim_norecoil", 315, 30, guipanel)
    slider("AimFOV", "aim_fov", 0, 90, 315, 49, 150, 10, guipanel)

	-- mouvement
    checkbox("MV: Bunnyhop", "toggle bunnyhop", "bhop", 165, 105, guipanel)
    checkbox("MV: Autostrafe", "toggle autostrafe", "bhop_as", 165, 121, guipanel)
    checkbox("MV: Desync", "Desync", "desync", 165, 137, guipanel)
    checkbox("MV: Act", "toggle act", "act", 165, 153, guipanel)
    -- cam stuff
    checkbox("CAM: Thirdperson", "thirdperson", "tps", 290, 106, guipanel)
    checkbox("CAM: Fov", "fov", "cam_fov", 290, 122, guipanel)
    checkbox("CAM: Weapon Fov", "weapon fov", "w_fov", 290, 138, guipanel)
    slider("WFov", "wfov", 90, 130, 290, 156, 150, 10, guipanel)
    
    -- === MODELE 3D ===
    local coolhoob2 = vgui.Create('DModelPanel', guipanel)
    coolhoob2:SetSize(195, 180)
    coolhoob2:SetPos(480, 5)
    coolhoob2:SetModel(vars.me:GetModel())
    coolhoob2:SetAnimated(true)

    function coolhoob2:LayoutEntity()
        self:RunAnimation()
        self:SetCamPos(Vector(50, 50, 50))
    end


    -- === FRIENDS LIST PANEL ===
    local function styleListViewHeader(listview)
        listview:SetHeaderHeight(25) -- Ajuste la hauteur du header
    
        for _, column in pairs(listview.Columns) do
            column.Header:SetFont("DermaDefaultBold") -- Texte en gras
            column.Header:SetTextColor(Color(255, 255, 255)) -- Texte blanc
            
            column.Header.Paint = function(_, w, h)
                draw.RoundedBox(0, 0, 0, w, h, colors.header) -- Fond de la colonne du header
            end
        end
    end
    

    local friendslist = vgui.Create('DListView', friendslistpanel)
    friendslist:Dock(FILL)
    friendslist:AddColumn('Player')
    friendslist:AddColumn('Steam ID')
    friendslist:AddColumn('Friend')
    friendslist.Paint = function(_, w, h)
        vars.sSetDrawColor(Color(0,0,0,0))
        vars.sDrawRect(0, 0, w, h)
    end

    local function updateFriends()
        friendslist:Clear()
        for _, ply in ipairs(player.GetAll()) do
            if ply:IsBot() then continue end
            local line = friendslist:AddLine(ply:Nick(), ply:SteamID(), friendlist[ply:SteamID()] and "Yes" or "No")
            for _, column in pairs(line.Columns) do
                if column then
                    column:SetTextColor(Color(255, 255, 255))
                end
            end
        end
    end

    styleListViewHeader(friendslist)

    friendslist.OnRowSelected = function(_, _, line)
        local steamID = line:GetColumnText(2)
        friendlist[steamID] = not friendlist[steamID]
        updateFriends()
    end

    updateFriends()
    
    -- Hooks
    local refreshBlocked
    local hooklist = vgui.Create('DListView', hookpanel)
    hooklist:Dock(LEFT)
    hooklist:SetWidth(350)
    hooklist:AddColumn('Name')
    hooklist:AddColumn('Type')
    hooklist.VBar:SetEnabled(false)
    hooklist.VBar.Paint = function(_, w, h) 
        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 0))
    end
    hooklist.VBar.btnUp.Paint = function(_, w, h) 
        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 0))
    end
    hooklist.VBar.btnDown.Paint = function(_, w, h) 
        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 0))
    end
    hooklist.VBar.btnGrip.Paint = function(_, w, h) 
        draw.RoundedBox(0, 0, 0, w, h, Color(0, 0, 0, 0))
    end

    hooklist.Paint = function(_, w, h)
        vars.sSetDrawColor(Color(255,255,255,0))
        vars.sDrawRect(0, 0, w, h)
    end
    styleListViewHeader(hooklist)


    local function refreshHooks()
        hooklist:Clear()
        for htype, hooks in pairs(hook.GetTable()) do
            if not istable(hooks) then continue end
            for hname, _ in pairs(hooks) do
                htype = tostring(htype)
                hname = tostring(hname)

                hooklist:AddLine(hname, htype)
            end
        end
        for _, line in pairs(hooklist.Lines) do
            function line:Paint()
                if line:IsHovered() then
                    vars.sSetDrawColor(Color(100, 100, 100))
                else
                    vars.sSetDrawColor(Color(50, 50, 50))
                end
                vars.sDrawRect(0, 0, self:GetWide(), self:GetTall())
            end
            for _, column in pairs(line.Columns) do
                column:SetTextColor(Color(255, 255, 255))
            end
        end
    end

    function hooklist:OnRowSelected()
        for _, v in pairs(hooklist:GetSelected()) do
            local hname = tostring(v:GetValue(1))
            local htype = tostring(v:GetValue(2))
            if not istable(blockedhooks[htype]) then blockedhooks[htype] = {} end
            blockedhooks[htype][hname] = hook.GetTable[htype][hname]
            hook.Remove(htype, hname)
        end
        refreshHooks()
        refreshBlocked()
    end
    refreshHooks()
    
    local blocklist = vgui.Create('DListView', hookpanel)
    blocklist:Dock(RIGHT)
    blocklist:SetWidth(350)
    blocklist:AddColumn('Blocked Name')
    blocklist:AddColumn('Type')
    blocklist.Paint = function(_, w, h)
        vars.sSetDrawColor(Color(255,255,255,0))
        vars.sDrawRect(0, 0, w, h)
    end
    styleListViewHeader(blocklist)

    function refreshBlocked()
        blocklist:Clear()
        for htype, hooks in pairs(blockedhooks) do
            if not istable(hooks) then continue end
            for hname, _ in pairs(hooks) do
                htype = tostring(htype)
                hname = tostring(hname)
                blocklist:AddLine(hname, htype)
            end
        end
        for _, line in pairs(blocklist.Lines) do
            function line:Paint()
                if line:IsHovered() then
                    vars.sSetDrawColor(Color(100, 100, 100))
                else
                    vars.sSetDrawColor(Color(50, 50, 50))
                end

                vars.sDrawRect(0, 0, self:GetWide(), self:GetTall())
            end
            for _, column in pairs(line.Columns) do
                column:SetTextColor(Color(255, 255, 255))
            end
        end
    end
    function blocklist:OnRowSelected()
        for _, v in pairs(blocklist:GetSelected()) do
            local hname = tostring(v:GetValue(1))
            local htype = tostring(v:GetValue(2))
            hook.Add(htype, hname, blockedhooks[htype][hname])
            blockedhooks[htype][hname] = nil
        end
        refreshBlocked()
        refreshHooks()
    end
    refreshBlocked()
end

-- === AA STUFF === --
local pitch, yaw = 0, 0
function GetBaseYaw()
    return vars.fa.y
end

function CalcPitch()
    local x = 0
    x = 0
    if vars.aa_mode == "none" then
        x = 0
    else 
        x = 89
    end
    return x
end

function CalcYaw()
    local y = 0
        y = vars.baseyaw - 180
    if vars.aa_mode == "spin" then
        local s = vars.math_NormAngle( vars.CurTime() * 750 )
        y = s
    elseif vars.aa_mode == "sway" then
        local s = vars.math_sin(vars.CurTime() * 2346) * 75
        y = ( vars.baseyaw - 180 ) + s -- sway
    elseif vars.aa_mode == "side left" then
        y = vars.baseyaw - 90
    elseif vars.aa_mode == "side right" then
        y = vars.baseyaw + 90
    elseif vars.aa_mode == "none" then
        y = vars.baseyaw
    end
    return y
end

function aacheck(cmd)
    if !vars.antiaim then return false end
    if cmd:KeyDown(IN_ATTACK) then return false end
    if cmd:KeyDown(IN_USE) then return false end
    if vars.me:GetMoveType() == MOVETYPE_LADDER then return false end
    if vars.me:GetMoveType() == MOVETYPE_NOCLIP then return false end
    
    return true
end

function ANTIAIM_C(cmd)
    if aacheck(cmd) then
        vars.baseyaw = GetBaseYaw()
        pitch = CalcPitch()
        yaw = CalcYaw()
        local pyAngle = Angle(pitch,yaw,0)
        cmd:SetViewAngles(pyAngle)
    end
end


-- === AILMBOT STUFF === --
function MovementFix(cmd, wish_yaw)
    local pitch = vars.math_NormAngle(cmd:GetViewAngles().x)
    local inverted = -1

    if (pitch > 89 or pitch < -89) then
        inverted = 1
    end

    local ang_diff = vars.math_rad(vars.math_NormAngle((cmd:GetViewAngles().y - wish_yaw) * inverted))
    local forwardmove = cmd:GetForwardMove()
    local sidemove = cmd:GetSideMove()

    local new_forwardmove = forwardmove * -vars.math_cos(ang_diff) * inverted + sidemove * vars.math_sin(ang_diff)
    local new_sidemove = forwardmove * vars.math_sin(ang_diff) * inverted + sidemove * vars.math_cos(ang_diff)

    cmd:SetForwardMove(new_forwardmove)
    cmd:SetSideMove(new_sidemove)
end

function Silent(cmd)
	if !vars.fa then vars.fa = cmd:GetViewAngles() end
	vars.fa = vars.fa + Angle(cmd:GetMouseY() * GetConVarNumber("m_yaw"), cmd:GetMouseX() * -GetConVarNumber("m_yaw"), 0)
	vars.fa.p = vars.math_clamp(vars.fa.p, -89, 89)
    vars.fa.r = 0
    vars.fa:Normalize()
end

function vars.CustomSpread.swb( cmd, ang ) -- WORK

    local cone = vars.activeWeapon.CurCone
    if !cone then return ang end
    if vars.me:Crouching() then
        cone = cone * 0.85
    end

    vars.math_randomseed( cmd:CommandNumber() )
    return ang  - vars.me:GetViewPunchAngles() - Angle( vars.math_rand(-cone, cone), vars.math_rand(-cone, cone), 0 ) * 25
end

function vars.CustomSpread.cw( cmd, ang ) -- WORK
    local cone = vars.activeWeapon.CurCone
    if !cone then return ang end
    vars.math_randomseed( cmd:CommandNumber() )

    if vars.me:Crouching() then
        cone = cone * 0.85
    end

    Dir = Angle(vars.math_rand(cone, -cone), vars.math_rand(cone, -cone), 0) * 25
    clumpSpread = clumpSpread or vars.activeWeapon.ClumpSpread
    Dir2 = Dir
    if clumpSpread and clumpSpread > 0 then
        Dir2 = Dir + Vector(vars.math_rand(-1, 1), vars.math_rand(-1, 1), vars.math_rand(-1, 1)) * clumpSpread
    end

    return ang - vars.me:GetViewPunchAngles() + Dir2
end

function vars.CustomSpread.fas2( cmd, ang )
    local cone = vars.activeWeapon.CurCone
    if !cone then return ang end
    vars.math_randomseed(CurTime())

    Dir2 = Angle(vars.math_rand(cone, -cone), vars.math_rand(cone, -cone), 0) * 25

    return ang - vars.me:GetViewPunchAngles() + Dir2
end

function vars.CustomSpread.tfa(cmd, ang)
    local aimcone, rec = vars.activeWeapon:CalculateConeRecoil()
    if not aimcone then return ang end


    return ang - Angle(vars.activeWeapon:GetViewPunchP(), vars.activeWeapon:GetViewPunchY(), 0)
end

vars.SpreadComps["swb"]     = vars.CustomSpread.swb
vars.SpreadComps["cw"]      = vars.CustomSpread.cw
vars.SpreadComps["fas2"]    = vars.CustomSpread.fas2
vars.SpreadComps["tfa"]     = vars.CustomSpread.tfa

GAMEMODE["EntityFireBullets"] = function( self, p, data )

    local spread = data.Spread * -1

	if vars.cones[ vars.activeWeaponClass ] == spread or spread == nullVec then return end

    vars.cones[ vars.activeWeaponClass ] = spread;
end

function NoSpread(cmd, ang)
    if !vars.activeWeaponClass then return ang end
    local base = string.Split( vars.activeWeaponClass, "_" )[ 1 ]
    if vars.SpreadComps[ base ] then
        ang = vars.SpreadComps[ base ]( cmd, ang )
    elseif vars.cones[ vars.activeWeaponClass ] then
        local spread = vars.cones[ vars.activeWeaponClass ]
        return Spread( cmd, ang, spread )
    end

    return ang
end

function Spread( cmd, ang)
    if not vars.cones[ vars.activeWeaponClass ] then return ang end
    local spread = vars.cones[ vars.activeWeaponClass ]
	local dir = ded.PredictSpread( cmd, ang, spread )
	local spreadangle = ang + dir:Angle()
	spreadangle:Normalize()
	return spreadangle
end

local tr = {
	mask = MASK_SHOT,
	filter = vars.me
}
function VisibleCheck( who, where, wall)
    local start = vars.me:EyePos()
    tr.start = start
	tr.endpos = where
	tr.mask = MASK_SHOT
    local trace = util.TraceLine( tr )
    local canhit = trace.Entity == who or trace.Fraction == 1

    return canhit

end

local function GetAngleDifference(from, to)
    return vars.math_deg(vars.math_acos(to:Forward():Dot(from:Forward())))
end

local function GetBestTarget()
    local bestTarget = nil
    local bestFov = vars.aim_fov
    local myViewAngles = vars.me:GetAngles()

    for _, ply in ipairs(player.GetAll()) do
        if ply ~= vars.me and ply:Alive() and not ply:IsDormant() then
            local boneIndex = ply:LookupBone("ValveBiped.Bip01_Head1")
            if boneIndex then
                local bonePos = ply:GetBonePosition(boneIndex)
                if bonePos and VisibleCheck(ply, bonePos) and not IsFriend(ply:SteamID()) and not ply:HasGodMode() then
                    local targetDirection = (bonePos - vars.me:GetShootPos()):Angle()
                    targetDirection:Normalize()

                    local angDiff = vars.math_abs(vars.math_NormAngle(GetAngleDifference(myViewAngles, targetDirection)))

                    if angDiff < bestFov then
                        bestFov = angDiff
                        bestTarget = ply
                    end
                end
            end
        end
    end

    return bestTarget
end

function NoRecoil( ang )
	if vars.activeWeaponClass == "weapon_pistol" or string.StartsWith( vars.activeWeaponClass ,"m9k_" ) or string.StartsWith( vars.activeWeaponClass ,"fas2" ) or string.StartsWith( vars.activeWeaponClass ,"tfa" ) or string.StartsWith( vars.activeWeaponClass ,"cw" ) or string.StartsWith( vars.activeWeaponClass ,"swb" )then
        return ang
	else
	    ang = ang - vars.me:GetViewPunchAngles()
    end

	return ang
end

local function getCenterHead(ply)
    local headIndex = ply:LookupBone("ValveBiped.Bip01_Head1")
    local matrix = ply:GetBoneMatrix(headIndex)
    local pos, ang = matrix:GetTranslation(), matrix:GetAngles()
    
    local min, max = ply:GetHitBoxBounds(0, 0)
    min:Rotate(ang)
    max:Rotate(ang)
    
    pos = pos + ((min + max) * 0.5)
    return pos
end

function AIM_C(cmd)
    if vars.aim_aimbot and shouldFire() then
        vars.target = GetBestTarget()
        if cmd:KeyDown(IN_ATTACK) then
            local direction = cmd:GetViewAngles()
            if vars.target then
                local bonePos = getCenterHead(vars.target)
                if VisibleCheck(vars.target, bonePos) then
                    direction = (bonePos - vars.me:GetShootPos()):Angle()
                    direction:Normalize()
                end
            end

            if vars.aim_nospread then
                direction = NoSpread(cmd, direction)
            end
            direction = NoRecoil(direction)

            cmd:SetViewAngles(direction)
        end
    end
end

-- === MOVEMENT STUFF === --
function BHOP_C(cmd)
    if vars.bhop then
    	if cmd:KeyDown( IN_JUMP ) then
	    	if !vars.me:OnGround() then
	    		cmd:SetButtons( cmd:GetButtons() - IN_JUMP )
	    	end
            if not vars.bhop_as then return end
        	if vars.me:IsFlagSet( FL_ONGROUND ) then
        	    cmd:SetForwardMove(10000)
        	else
            	cmd:SetForwardMove(5850 / vars.me:GetVelocity():Length2D())
            	cmd:SetSideMove((cmd:CommandNumber() % 2 == 0) && -400 || 400)
        	end
		end
	end
end

function DESYNC_C(cmd)
    vars.bSendPacket = true
    if vars.desync then
        ded.SetBSendPacket(vars.bSendPacket)
        local tickrate = vars.math_floor(1 / engine.TickInterval())
        local seqshift =  tickrate - 3
        local bRunning = true
        if (vars.bSendPacket) then
            if (seqshift > 0) then
                if (!vars.bRunning) then
                    ded.SetOutSequenceNr(ded.GetOutSequenceNr() + seqshift)
                    vars.bRunning = true
                else
                    ded.SetNetChokedPackets(127)
                end
            else
                vars.bRunning = false
            end
        end
    else
    	vars.bRunning = false
    end
end

-- === SPAM STUFF === --
local players=FindMetaTable("Player")
local oldcmd=players.ConCommand
players.ConCommand=function(p,cmd,...)
	if cmd=="jpeg"then return end
	return oldcmd(p,cmd,...)
end
function SPAM_C(cmd)
    if input.IsKeyDown(KEY_L) then
        RunConsoleCommand("use","gmod_camera")
        if cmd:TickCount()%2==0 then
            cmd:SetButtons(bit.bor(cmd:GetButtons(),IN_ATTACK))
        end
    end
    if input.IsKeyDown(KEY_K) then
        RunConsoleCommand("impulse", "100" )
    end
end

-- === VIEW STUFF === --
local togglethird = false

function THIRDPERSON_C(cmd)
    if not vars.tps then return end
    if input.IsKeyDown(KEY_V) and not togglethird then
        vars.thirdPersonEnabled = not vars.thirdPersonEnabled
        togglethird = true
    elseif not input.IsKeyDown(KEY_V) then
        togglethird = false
    end
end

local function toggleMenu()
    if IsValid(frame) then
        frame:SetVisible(not frame:IsVisible())
        gui.EnableScreenClicker(frame:IsVisible())
    else
        ui()
        frame:SetVisible(true)
        frame:MakePopup()
        gui.EnableScreenClicker(true)
    end
end

function MENU_C(cmd)
    if input.IsKeyDown(KEY_J) then
        if not vars.MenuToggle then
            toggleMenu()
            vars.MenuToggle = true
        end
    else
        vars.MenuToggle = false
    end
end

local function UNLOAD_C()
    if input.IsKeyDown(KEY_INSERT) then
        if IsValid(vars.frame) and vars.frame:IsVisible() then
            ui()
        end
        for event, hooksList in pairs(vars.hooksToRemove) do
            for _, hookID in ipairs(hooksList) do
                hook.Remove(event, hookID)
            end
        end
        vars.hudpaint = false
    end
end

function a(cmd)
    if vars.me:IsTyping() or not vars.me:Alive() or not IsValid(vars.me) then return end
    Silent(cmd)

    local psa = vars.aim_silent or vars.antiaim

    if psa then
        cmd:SetViewAngles(vars.fa)
    end

    local wish_yaw = vars.fa.y

    vars.activeWeaponClass = IsValid(vars.me:GetActiveWeapon()) and vars.me:GetActiveWeapon():GetClass() or ""
    vars.activeWeapon = vars.me:GetActiveWeapon()

    local createmove_functions = {
        AIM_C, SPAM_C, ANTIAIM_C, BHOP_C,
        THIRDPERSON_C, DESYNC_C, MENU_C, 
        UNLOAD_C
    }

    for _, func in ipairs(createmove_functions) do
        func(cmd)
    end

    if psa then
        MovementFix(cmd, wish_yaw)
    else
        vars.fa = cmd:GetViewAngles()
    end
end

VARhooks("CreateMove", a)
function GAMEMODE:CreateMove(cmd) return true end

vieworigin = vars.me:EyePos()
local function getView(fangs, origin, angles, fov)
    local view = {
        origin = origin,
        angles = fangs,
        fov = vars.cam_fov and vars.fov or fov,
        drawviewer = false
    }

    if vars.thirdPersonEnabled then
        view.origin = origin - (fangs:Forward() * 80)
        view.drawviewer = true
    end

    return view
end

VARhooks("CalcView", function(ply, origin, angles, fov, znear, zfar)
    local fangs = vars.fa or angles
    local view = getView(fangs, origin, angles, fov)
    vieworigin = origin
    return view
end)

local VMCalcCalled

VARhooks("CalcViewModelView", function(wep, vm, oldPos, oldAng, pos, ang, ...)
    if VMCalcCalled then return end
    VMCalcCalled = true
    pos = vieworigin
    if vars.aim_silent or vars.antiaim then
        ang = vars.fa
    end
    VMCalcCalled = nil
    return pos, ang
end)

local IsDrawingGlow = false
local drawing = false
VARhooks("PreDrawViewModel", function(vm)
	if vars.w_chams and !vars.sg then
        if drawing then 
            vars.supenglight(true)
            Glow2:SetVector("$color2", Vector(1, 1, 1))
            render.MaterialOverride(Glow2)
            for i = 0, 5 do
                local colors = {
                    {1, 0.3, 0.3}, {0.3, 1, 0.3}, {0.3, 0.3, 1},
                    {1, 1, 0}, {1, 0, 1}, {0, 1, 1}
                }
                render.SetModelLighting(i, unpack(colors[i + 1]))
            end
        else
            vars.supenglight(false)
            render.MaterialOverride(nil)
        end

    else
        vars.supenglight(false)
        render.MaterialOverride(nil)
	end

    if vars.w_fov then
        if not drawing then
            if vars.sg then return end
            cam.Start3D(nil, nil, vars.wfov)
            render.DepthRange( 0.2, 0 )
            drawing = true
            vm:DrawModel()
            drawing = false
            cam.End3D()
        else
            return
        end
        return true
    else
        return false
    end
end)

VARhooks("PostDrawViewModel", function()
	if vars.w_chams then

		render.SetColorModulation(1, 1, 1)
		render.MaterialOverride(None)
		render.SetBlend(1)
		vars.supenglight(false)
		if IsDrawingGlow then return end

		IsDrawingGlow = true
		vars.me:GetViewModel():DrawModel()
		IsDrawingGlow = false
	end
end)

local em = FindMetaTable("Entity")
local vm = FindMetaTable("Vector")

local function GetEntityColor(v)
    if not IsValid(v) or not v:IsPlayer() then return Color(255, 0, 0) end
    return IsFriend(v:SteamID()) and Color(0, 255, 0) or Color(255, 0, 0)
end

local function GetHealthColor(health)
    local r = Lerp(health / 100, 255, 0)
    local g = Lerp(health / 100, 0, 255)
    return Color(r, g, 0, 255)
end

local lerpData = {}

local function boxcorners(v)
    if not IsValid(v) or not v:IsPlayer() then return end

    if not lerpData[v] then
        lerpData[v] = {
            health = v:Health(),
            armor = v:Armor()
        }
    end

    local min, max = em.GetCollisionBounds(v)
    local pos = em.GetPos(v)
    local pos2 = pos + Vector(0, 0, max.z)

    pos = vm.ToScreen(pos)
    pos2 = vm.ToScreen(pos2)

    local h = pos.y - pos2.y
    local w = h / 2
    local color = GetEntityColor(v)

    vars.sSetDrawColor(Color(0, 0, 0))
    vars.sDrawOutlinedRect(pos.x - w / 2 - 1, pos.y - h - 1, w + 2, h + 2)

    vars.sSetDrawColor(color)
    vars.sDrawOutlinedRect(pos.x - w / 2, pos.y - h, w, h)
    
    local health = math.Clamp(v:Health(), 0, 100)
    local armor = math.Clamp(v:Armor(), 0, 100)

    lerpData[v].health = Lerp(FrameTime() * 10, lerpData[v].health, health)
    lerpData[v].armor = Lerp(FrameTime() * 10, lerpData[v].armor, armor)

    local barWidth = 7
    local barHeight = h

    local healthHeight = (lerpData[v].health / 100) * barHeight
    local armorHeight = (lerpData[v].armor / 100) * barHeight

    local healthBarX = pos.x - w / 2 - barWidth - 5
    local armorBarX = healthBarX - barWidth - 4

    local healthColor = GetHealthColor(health)

    vars.sSetDrawColor(Color(0, 0, 0))
    vars.sDrawOutlinedRect(healthBarX - 1, pos.y - h - 1, barWidth + 2, barHeight + 2)

    vars.sSetDrawColor(healthColor)
    vars.sDrawRect(healthBarX, pos.y - h + (barHeight - healthHeight), barWidth, healthHeight)

    vars.sSetDrawColor(Color(0, 0, 0))
    vars.sDrawOutlinedRect(armorBarX - 1, pos.y - h - 1, barWidth + 2, barHeight + 2)

    vars.sSetDrawColor(colors.armor)
    vars.sDrawRect(armorBarX, pos.y - h + (barHeight - armorHeight), barWidth, armorHeight)
end

local function txtesp(v)
    local ppos = v:GetPos()
    local pos  = ppos:ToScreen()
    local tpos = ppos + Vector( 0, 0, 75 )
    local ttpos  = tpos:ToScreen()
    local str = string.Explode('/',v:GetModel())
    local nn = str[#str]
    local w,h = GetTextSize(cmdFont,nn)
    local color = GetEntityColor(v)

    vars.draw_SimpleTextOutlined(v:Nick(), cmdFont, ttpos.x, ttpos.y-h/2+5, color, TEXT_ALIGN_CENTER, TEXT_ALIGN_CENTER, 1, Color(30, 30, 30, 255))
end

local function xhair()
    if not vars.tps then
        vars.sSetDrawColor(colors.white)
        vars.sDrawRect(vars.sw/2-3,vars.sh/2,7,2)
        vars.sDrawRect(vars.sw/2,vars.sh/2-3,2,7)
    end
end

local helpers = {
    lerp = function(self, start, end_pos, time, delta)
        if (vars.math_abs(start - end_pos) < (delta or 0.01)) then return end_pos end

        time = FrameTime() * (time * 175)
        if time < 0 then
          time = 0.01
        elseif time > 1 then
          time = 1
        end
        return ((end_pos - start) * time + start)
    end,
}

local MultiColorText = function(data, x, y)
    local total_width = 0
    local width = 0
    surface.SetFont( "font" )

    for _, v in pairs(data) do
        local text = v[1]
        if text == nil then continue end
        local text_width, text_height = surface.GetTextSize( text )
        total_width = total_width + text_width
    end

    for _, v in ipairs(data) do
        local text = v[1]
        if text == nil then continue end
        local text_width, text_height = surface.GetTextSize( text )
        local x2 = (x - total_width / 2 + width)
        vars.draw_SimpleText( v[1], "font", x2 + 1, y + 1, Color( 0, 0, 0, v[2].a) )
        vars.draw_SimpleText( v[1], "font", x2, y, v[2] )
        width = width + text_width
    end
end

vars.hitlogs.on_draw = function()
    local sc = {x = vars.sw, y = vars.sh}
    local x, y = sc.x/2, sc.y/1.1
    vars.add.y = 0
    for k, v in ipairs(vars.hitlogs.data) do
        local text = v[1]
        local r, g, b = v.color.r, v.color.g, v.color.b
        local realtime = RealTime()
        if v.time + 1 > realtime then
            v.alpha = helpers:lerp(v.alpha, 1, 0.095)
            v.alpha1 = helpers:lerp(v.alpha, 1, 0.05)
        end

        vars.add.y = vars.add.y + 15*v.alpha
        local wh = Color(255, 255, 255, v.alpha*255)
        local ch = Color(r, g, b, v.alpha*255)
        MultiColorText({
            {
                text[1],
                wh
            },
            {
                text[2],
                ch
            },
            {
                text[3],
                wh
            },
            {
                text[4],
                ch
            },
            {
                text[5],
                wh
            },
            {
                text[6],
                ch
            },
            {
                text[7],
                wh
            },
            {
                text[8],
                ch
            },
            {
                text[9],
                wh
            }
        }, x + v.alpha1*100 - 100, y - vars.add.y)

        if v.time + 5 < realtime then
            v.alpha = helpers:lerp(v.alpha, 0, 0.05)
            v.alpha1 = helpers:lerp(v.alpha1, 2, 0.04)
        end
        if v.alpha < 0.01 and (v.time + 6 < realtime) or #vars.hitlogs.data > 50 then
            table.remove(vars.hitlogs.data, k)
        end
    end
end

surface.CreateFont("font", {
    font = "Verdana",
    size = 15,
    antialias = true
})

gameevent.Listen( "player_hurt" )
VARhooks("player_hurt", function( data )
    local health = data.health
	local priority = SERVER and data.Priority or 5
	local attackerid = data.attacker
    local ply = Player( data.attacker )
    local plu = Player( data.userid )

    if (ply == nil or ply:IsPlayer() == false) or plu == nil then return end
    if attackerid == vars.me:UserID() then
        vars.lastplayerhit = plu
        table.insert(vars.hitlogs.data, {
        {
            "Hit ",
            plu:Nick(),
            " for ",
            vars.toStr(string.gsub(data.health-plu:Health(), "-", "")),
            " damage (",
            vars.toStr(plu:Health()), " HP | ", vars.toStr(plu:Armor()),
            " ARMOR remaining)",
        },
        alpha = 0,
        alpha1 = 0,
        time = RealTime(),
        color = Color(255, 0, 0),
        })
    end
end)

VARhooks('HUDPaint', function()
    if vars.hudpaint then
        for k,v in pairs(player.GetAll()) do
            if not IsValid(v) or v == vars.me or not v:Alive() or IsOutOfFOV(v) or v:IsDormant() then continue end
            if vars.esp_boxes then boxcorners(v) end
            if vars.esp then txtesp(v) end
        end
        if vars.aim_aimbot then
            local X1 = vars.math_tan( vars.math_rad( vars.aim_fov ) / 1.25 )
            local X2 = vars.math_tan( vars.math_rad( vars.me:GetFOV() / 2 ) )
            local r = X1 / X2
            local pxR = r * ( ScrW() / 2 )
            vars.sDrawCircle(ScrW() / 2, ScrH() / 2, pxR, colors.red)
            if vars.target and shouldFire() then
                local bonePos = getCenterHead(vars.target)
                local pos = vm.ToScreen(bonePos)
                vars.sSetDrawColor( colors.red )
                vars.sDrawLine( pos.x, pos.y, vars.sw / 2, vars.sh / 2 )
            end
        end
        xhair()
        vars.hitlogs.on_draw()
     end
end)

VARhooks('ScalePlayerDamage', function(_, _, d)
    if vars.hitsound then
        if d:GetAttacker() == vars.me then
            vars.sPlaySound('garrysmod/balloon_pop_cute.wav')
        end
    end
end)

VARhooks("OnImpact", function(data)
    vars.bulletImpacts[#vars.bulletImpacts + 1] = {
        shootTime = vars.CurTime(),
        pos = data.m_vOrigin,
        alpha = 255
    }
end)

VARhooks("PostDrawOpaqueRenderables", function()
    local currentTime = vars.CurTime()
    local DieTime = 0.5
    local maxSize = 4

    for i = #vars.bulletImpacts, 1, -1 do
        local impact = vars.bulletImpacts[i]
        local timeElapsed = currentTime - impact.shootTime
        local timeLeft = DieTime - timeElapsed

        if timeLeft <= 0 then
            table.remove(vars.bulletImpacts, i)
        else
            local fadeFactor = vars.math_clamp(timeLeft / DieTime, 0, 1)
            local size = maxSize * fadeFactor
            local color = Color(255 * fadeFactor, 0, 255 * (1 - fadeFactor), 255 * fadeFactor)

            cam.Start3D()
                render.SetColorMaterial()
                render.DrawBox(impact.pos, Angle(0, 0, 0), Vector(-size, -size, -size), Vector(size, size, size), color)
            cam.End3D()
        end
    end
end)
