-- by milk 100909 for TacoScript

local esp = true;

surface.CreateFont("Verdana", 10, 400, true, false, "espfont");
surface.CreateFont("Courier New", 10, 400, true, false, "statusf");

local weapon_good = {};
weapon_good["weapon_physgun"] = true;
weapon_good["gmod_tool"] = true;

local weapon_dumb = {};
weapon_dumb["hands"] = true;
weapon_dumb["ts_hands"] = true;
weapon_dumb["ts2_hands"] = true;
weapon_dumb["ts_keys"] = true;
weapon_dumb["ts2_keys"] = true;
weapon_dumb["ts_medic"] = true;
weapon_dumb["ks_hands"] = true;
weapon_dumb["ks_keys"] = true;
weapon_dumb["weapon_physcannon"] = true;
weapon_dumb["weapon_ts_case"] = true;

local noshow = {};
local spot = {};

local function setesp(ply, cmd, args)
    if esp then
        esp = false;
    else
        esp = true;
    end
end
concommand.Add(".esp", setesp);

local function status(ply)
    out = "";
    if ply:IsSuperAdmin() then
        out = out.."S";
    else
        out = out.." ";
    end
    if ply:IsAdmin() then
        out = out.."A";
    else
        out = out.." ";
    end
    if ply:GetFriendStatus() == "friend" then
        out = out.."F  ";
    elseif ply == LocalPlayer() then
        out = out.."-- ";
    else
        out = out.."   ";
    end
    return out;
end

local function listplayers(ply, cmd, args)
    local i = 0;
    for _, v in ipairs(player.GetAll()) do
        i = i + 1;
        Msg(status(v)..v:Name().."  ");print(v:Nick());
    end
    print(i.." players total.");
end
concommand.Add(".lp", listplayers);

local function listadmins(ply, cmd, args)
    local i = 0;
    for _, v in ipairs(player.GetAll()) do
        if v:IsAdmin() then
            i = i + 1;
            Msg(status(v)..v:Name().."  ");print(v:Nick());
        end
    end
    print(i.." admins total.");
end
concommand.Add(".la", listadmins);

local function listweapons(ply, cmd, args)
    for _, v in ipairs(player.GetAll()) do
        for __, weapon in ipairs(v:GetWeapons()) do
            if !weapon_dumb[weapon:GetClass()] or !weapon_good[weapon:GetClass()] then
                Msg(status(v)..v:Nick().."  ");print(weapon:GetClass());
            end
        end
    end
end
concommand.Add(".lw", listweapons);

local function listmoney(ply, cmd, args)
    for _, v in ipairs(player.GetAll()) do
        Msg(status(v)..tostring(v:GetNWFloat("money")).."  ");print(v:Nick());
    end
end
concommand.Add(".lm", listmoney);

local function search(ply, cmd, args)
    if !args then return; end
    local i = 0;
    for _, v in ipairs(player.GetAll()) do
        if string.match(v:Nick(), args[1]) or
           string.match(v:Name(), args[1]) or
           string.match(v:GetNWFloat("title"), args[1]) or
           string.match(v:GetNWFloat("title2"), args[1]) or
           string.match(v:GetModel(), args[1]) then
            i = i+1;
            Msg(status(v)..v:Name().."  ");print(v:Nick());
            if string.match(v:Name(), args[1]) then
                Msg("           OOC: ");print(v:Name());
            end
            if string.match(v:Nick(), args[1]) then
                Msg("            IC: ");print(v:Nick());
            end
            if string.match(v:GetNWFloat("title"), args[1]) then
                Msg("         Title: ");print(v:GetNWFloat("title"));
            end
            if string.match(v:GetNWFloat("title2"), args[1]) then
                Msg("        Title2: ");print(v:GetNWFloat("title2"));
            end
            if string.match(v:GetModel(), args[1]) then
                Msg("         Model: ");print(v:GetModel());
            end
        end
    end
    print("Found "..i.." matches.");
end
concommand.Add(".s", search);

local function who(ply, cmd, args)
    local i = 0;
    for _, v in ipairs(player.GetAll()) do
        if string.match(v:Nick(), args[1]) or string.match(v:Name(), args[1]) then
            i = i+1;
            if i == 1 then
                LocalPlayer():ConCommand("rp_whohaschar \""..v:Nick().."\"");
                Msg(status(v));print(v:Nick());
                print("     "..v:Name());
                print("");
                Msg("     Title1: ");print(v:GetNWFloat("title"));
                Msg("     Title2: ");print(v:GetNWFloat("title2"));
                print("");
                Msg("     Tokens: ");print(tostring(v:GetNWFloat("money")));
                Msg("     Health: ");print(tostring(v:Health().." ["..v:Armor().."]"));
                print("");
                Msg("      Model: ");print(v:GetModel());
                print("");
                Msg("   Strength: ");print(math.Round(v:GetNWFloat("stat.Strength")));
                Msg("  Endurance: ");print(math.Round(v:GetNWFloat("stat.Endurance")));
                Msg("      Speed: ");print(math.Round(v:GetNWFloat("stat.Speed")));
                Msg("     Sprint: ");print(math.Round(v:GetNWFloat("stat.Sprint")));
                Msg("        Aim: ");print(math.Round(v:GetNWFloat("stat.Aim")));
                print("");
                Msg("    Weapons:\n");
                for _, weapon in ipairs(v:GetWeapons()) do
                    if !weapon_dumb[weapon:GetClass()] then
                        print("      "..weapon:GetClass());
                    end
                end
                print("");
            end
        end
    end
    if i != 1 then
        print("Found "..i.." matches.");
    end
end
concommand.Add(".who", who);

local function cmdall(ply, cmd, args)
    for _, v in ipairs(player.GetAll()) do
        if v != LocalPlayer() then
            if string.match(v:Nick(), ".") then
                LocalPlayer():ConCommand(args[1].."\""..string.Right(v:Nick(), 5).."\" "..tostring(args[2]));
            else
                LocalPlayer():ConCommand(args[1].."\""..v:Nick().."\" "..tostring(args[2]));
            end
        end
    end
end
concommand.Add(".cmdall", cmdall);

local function selfbring(ply, cmd, args)
    if string.match(LocalPlayer():Nick(), ".") then
        LocalPlayer():ConCommand("rp_bring \""  ..string.Right(LocalPlayer():Nick(), 5).. "\"");
    else
        LocalPlayer():ConCommand("rp_bring \""  ..LocalPlayer():Nick().. "\"");
    end
end
concommand.Add(".selfbring", selfbring);

local function selfclip(ply, cmd, args)
    if LocalPlayer():IsAdmin() then
        LocalPlayer():ConCommand("rp_cloak");
    end
    if LocalPlayer():IsSuperAdmin() then
        LocalPlayer():ConCommand("rp_noclip");
    end
    LocalPlayer():ConCommand("noclip");
end
concommand.Add(".selfclip", selfclip);

local function spotplayer(ply, cmd, args)
    local i = 0;
    for _, v in ipairs(player.GetAll()) do
        if string.match(v:Nick(), args[1]) or string.match(v:Name(), args[1]) then
            i = i+1;
            spot[v] = true;
        end
    end
    print("Highlighting "..i.." matches.");
end
concommand.Add(".spot", spotplayer);

local function unspot(ply, cmd, args)
    spot = {};
end
concommand.Add(".unspot", unspot);

local function help(ply, cmd, args)
    Msg("         .esp  ");print("Enable/disable weapon/item/player esp.");
    Msg("  .who ooc/ic  ");print("List information on a certain player.");
    Msg("       .s str  ");print("List players with that in their info.");
    Msg("          .la  ");print("List all admins/superadmins in game.");
    Msg("          .lm  ");print("List all players' money.");
    Msg("          .lp  ");print("List all players and their IC names.");
    Msg("          .lw  ");print("List all players' weapons.");
    Msg("        .spot  ");print("Highlight player(s) on your ESP.");
    Msg("      .unspot  ");print("Clear all highlights.");
    Msg("   .selfbring  ");print("Bring yourself.");
    Msg("    .selfclip  ");print("Cloak/noclip yourself.");
    Msg(".cmdall cmd #  ");print("Do a command to everyone but yourself.");
end
concommand.Add(".help", help);

local function trace_aim(length)
    local trace = {};
    trace.start = LocalPlayer():EyePos();
    trace.endpos = trace.start + LocalPlayer():GetAimVector()*length;
    trace.filter = LocalPlayer();
    return util.TraceLine(trace);
end

local function hookdraw()
    if esp then
        surface.SetFont("espfont");
        local tr = trace_aim(2000);
        local base = 0;
        local diff = 1;
        if tr.HitNonWorld then
            if tr.Entity:IsPlayer() then
                base = tr.Entity:EyePos():ToScreen();
                if spot[tr.Entity] then
                    draw.RoundedBox(0,base.x-2,base.y-10,150,20,Color(255,0,255,100));
                end
                local teamcolor = team.GetColor(tr.Entity:Team());
                surface.SetTextColor(math.Clamp(teamcolor.r+150, 0, 255), math.Clamp(teamcolor.g +150, 0, 255), math.Clamp(teamcolor.b +150, 0, 255), 255);
                if tr.Entity:Nick() != tr.Entity:Name() then
                    surface.SetTextPos(base.x, base.y-10);
                    surface.DrawText(tr.Entity:Nick());
                    surface.SetTextColor(255, 255, 255, 255);
                end
                surface.SetTextPos(base.x, base.y);
                surface.DrawText(tr.Entity:Name());
                surface.SetTextColor(255, 255, 255, 255);
                surface.SetFont("statusf");
                surface.SetTextPos(base.x-20, base.y+2);
                surface.DrawText(status(tr.Entity));
                surface.SetFont("espfont");
                surface.SetTextPos(base.x+40, base.y+10);
                surface.DrawText(math.Round(tr.Entity:EyePos():Distance(LocalPlayer():EyePos())/39.37).."m");
                surface.SetTextPos(base.x+40, base.y+20);
                surface.DrawText(tr.Entity:Health().." ["..tr.Entity:Armor().."]");
                if tr.Entity:GetNWFloat("stat.Strength") > 0 then
                    surface.SetTextPos(base.x+40, base.y+30);
                    surface.DrawText("$"..tr.Entity:GetNWFloat("money"));
                    surface.SetTextPos(base.x+15, base.y+10);
                    surface.DrawText(math.floor(tr.Entity:GetNWFloat("stat.Strength")));
                    surface.SetTextPos(base.x+15, base.y+20);
                    surface.DrawText(math.floor(tr.Entity:GetNWFloat("stat.Endurance")));
                    surface.SetTextPos(base.x+15, base.y+30);
                    surface.DrawText(math.floor(tr.Entity:GetNWFloat("stat.Speed")));
                    surface.SetTextPos(base.x+15, base.y+40);
                    surface.DrawText(math.floor(tr.Entity:GetNWFloat("stat.Sprint")));
                    surface.SetTextPos(base.x+15, base.y+50);
                    surface.DrawText(math.floor(tr.Entity:GetNWFloat("stat.Aim")));
                end
                for _, v in ipairs(tr.Entity:GetWeapons()) do
                    local wepc = v:GetClass();
                    if weapon_good[wepc] then
                        surface.SetTextColor(148, 255, 148, 255);
                        surface.SetTextPos(base.x+100, base.y+(diff*10));
                        surface.DrawText(v:GetClass());
                        diff = diff + 1;
                    elseif weapon_dumb[wepc] then
                    else
                        surface.SetTextColor(255, 148, 148, 255);
                        surface.SetTextPos(base.x+100, base.y+(diff*10));
                        surface.DrawText(wepc);
                        diff = diff + 1;
                    end
                end
                tr = tr.Entity;
            end
        else
            tr = LocalPlayer();
        end
        for _, v in ipairs(player.GetAll()) do
            if v == LocalPlayer() then
                for __, selfweapons in ipairs(v:GetWeapons()) do
                    noshow[selfweapons] = true;
                end
            elseif v != LocalPlayer() and v != tr then
                base = v:EyePos():ToScreen();
                local teamcol = team.GetColor(v:Team());
                surface.SetTextColor(math.Clamp(teamcol.r+160 , 0 , 255), math.Clamp(teamcol.g+160 , 0 , 255), math.Clamp(teamcol.b+160, 0 , 255), 255);
                if spot[v] then
                    draw.RoundedBox(0,base.x-2,base.y-10,150,20,Color(255,0,255,100));
                end
                surface.SetTextPos(base.x, base.y-10);
                if v:Nick() != v:Name() then
                    surface.DrawText(v:Nick());
                    surface.SetTextColor(255, 255, 255, 255);
                end
                surface.SetTextPos(base.x, base.y);
                surface.DrawText(v:Name());
                surface.SetTextColor(255, 255, 255, 255);
                surface.SetFont("statusf");
                surface.SetTextPos(base.x-20, base.y+2);
                surface.DrawText(status(v));
                surface.SetFont("espfont");
                surface.SetTextPos(base.x+40, base.y+10);
                surface.DrawText(tostring(math.Round(v:EyePos():Distance(LocalPlayer():EyePos())/39.37)).."m");
                for __, playerweapons in ipairs(v:GetWeapons()) do
                    noshow[playerweapons] = true;
                end
            end
        end
        for _, v in ipairs(ents.GetAll()) do
            if v:IsWeapon() and !noshow[v] then
                local base = v:GetPos():ToScreen();
                surface.SetTextColor(255, 148, 148, 255);
                surface.SetTextPos(base.x, base.y);
                surface.DrawText(v:GetClass());
                surface.SetTextPos(base.x+40, base.y+10);
                surface.DrawText(tostring(math.Round(v:GetPos():Distance(LocalPlayer():EyePos())/39.37)).."m");
            end
            if string.sub(v:GetClass(), 1, 4) == "item" then
                local base = v:GetPos():ToScreen();
                surface.SetTextColor(148, 148, 255, 255);
                surface.SetTextPos(base.x, base.y);
                surface.DrawText(v:GetModel());
                surface.SetTextPos(base.x+40, base.y+10);
                surface.DrawText(tostring(math.Round(v:GetPos():Distance(LocalPlayer():EyePos())/39.37)).."m");
            end
        end
    end
end
hook.Add("HUDPaint", hookdraw, hookdraw);
