From d0381e1aa98b8ee54efe0020cfa3ef695737a2c5 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 11:52:34 +0100 Subject: [PATCH 01/12] Update others.cpp --- src/hooks/others.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 60ba300e..6807b27d 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -696,7 +696,7 @@ bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) static const char *lastfilter; static const char *lastname; static bool retrun = false; - if (data[0] != LOCAL_E->m_IDX) { + if (data[0] == LOCAL_E->m_IDX) { if (retrun) PrintChat("\x07%06X%s\x01: \x07%06X%s\x01", 0xe05938, lastname, 0xefec1f, lastfilter); @@ -860,10 +860,22 @@ bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) } if (crypt_chat) { + if (firstcall) + chat_stack::Say(ucccccp::encrypt("meow"), false); + firstcall = false; if (message.find("!!") == 0) { if (ucccccp::validate(message)) { + CachedEntity* entity = ENTITY(data[0]); + if (CE_GOOD(entity)) { + if (boost::algorithm::contains(ucccccp::decrypt(message), "meow")) { + player_info_s info; + g_IEngine->GetPlayerInfo(data[0], &info); + unsigned steamid = info.friendsID; + playerlist::AccessData(steamid).state = playerlist::k_EState::CAT; + } + } PrintChat("\x07%06X%s\x01: %s", 0xe05938, name.c_str(), ucccccp::decrypt(message).c_str()); } @@ -939,4 +951,4 @@ int RandomInt_hook(void *_this, int iMinVal, int iMaxVal) if (medal_flip && iMinVal == 0 && iMaxVal == 9) return 0; return original(_this, iMinVal, iMaxVal); -} \ No newline at end of file +} From 9bffc05cd311c4ab20b2f3a3b8b8919a26b124e8 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 11:53:13 +0100 Subject: [PATCH 02/12] Update playerlist.hpp --- include/playerlist.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/playerlist.hpp b/include/playerlist.hpp index cce8cc0f..707a4c52 100644 --- a/include/playerlist.hpp +++ b/include/playerlist.hpp @@ -22,7 +22,8 @@ enum class k_EState RAGE, IPC, DEVELOPER, - STATE_LAST = DEVELOPER + CAT, + STATE_LAST = CAT }; extern rgba_t k_Colors[]; From d505af9213f726fe821e7f02a988f260207d429b Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 11:53:55 +0100 Subject: [PATCH 03/12] Update playerlist.cpp --- src/playerlist.cpp | 414 +++++++++++++++++++++++---------------------- 1 file changed, 208 insertions(+), 206 deletions(-) diff --git a/src/playerlist.cpp b/src/playerlist.cpp index 5e090a6b..71be8dea 100644 --- a/src/playerlist.cpp +++ b/src/playerlist.cpp @@ -1,225 +1,227 @@ -/* - * playerlist.cpp - * - * Created on: Apr 11, 2017 - * Author: nullifiedcat - */ + /* + * playerlist.cpp + * + * Created on: Apr 11, 2017 + * Author: nullifiedcat + */ -#include "playerlist.hpp" -#include "common.hpp" + #include "playerlist.hpp" + #include "common.hpp" -#include -#include -#include + #include + #include + #include -namespace playerlist -{ + namespace playerlist + { -std::unordered_map data{}; + std::unordered_map data{}; -const userdata null_data{}; + const userdata null_data{}; -rgba_t k_Colors[] = { colors::empty, colors::FromRGBA8(99, 226, 161, 255), - colors::FromRGBA8(226, 204, 99, 255), - colors::FromRGBA8(232, 134, 6, 255), colors::empty }; + rgba_t k_Colors[] = { colors::empty, colors::FromRGBA8(99, 226, 161, 255), + colors::FromRGBA8(226, 204, 99, 255), + colors::FromRGBA8(232, 134, 6, 255), colors::empty }; -bool ShouldSave(const userdata &data) -{ - return data.color || (data.state != k_EState::DEFAULT); -} + bool ShouldSave(const userdata &data) + { + return data.color || (data.state != k_EState::DEFAULT); + } -void Save() -{ - DIR *cathook_directory = opendir(DATA_PATH); - if (!cathook_directory) - { - logging::Info("[ERROR] cathook data directory doesn't exist! How did " - "the cheat even get injected?"); - return; - } - else - closedir(cathook_directory); - try - { - std::ofstream file(DATA_PATH "/plist", - std::ios::out | std::ios::binary); - file.write(reinterpret_cast(&SERIALIZE_VERSION), - sizeof(SERIALIZE_VERSION)); - int size = 0; - for (const auto &item : data) - { - if (ShouldSave(item.second)) - size++; - } - file.write(reinterpret_cast(&size), sizeof(size)); - for (const auto &item : data) - { - if (!ShouldSave(item.second)) - continue; - file.write(reinterpret_cast(&item.first), - sizeof(item.first)); - file.write(reinterpret_cast(&item.second), - sizeof(item.second)); - } - file.close(); - logging::Info("Writing successful"); - } - catch (std::exception &e) - { - logging::Info("Writing unsuccessful: %s", e.what()); - } -} + void Save() + { + DIR *cathook_directory = opendir(DATA_PATH); + if (!cathook_directory) + { + logging::Info("[ERROR] cathook data directory doesn't exist! How did " + "the cheat even get injected?"); + return; + } + else + closedir(cathook_directory); + try + { + std::ofstream file(DATA_PATH "/plist", + std::ios::out | std::ios::binary); + file.write(reinterpret_cast(&SERIALIZE_VERSION), + sizeof(SERIALIZE_VERSION)); + int size = 0; + for (const auto &item : data) + { + if (ShouldSave(item.second)) + size++; + } + file.write(reinterpret_cast(&size), sizeof(size)); + for (const auto &item : data) + { + if (!ShouldSave(item.second)) + continue; + file.write(reinterpret_cast(&item.first), + sizeof(item.first)); + file.write(reinterpret_cast(&item.second), + sizeof(item.second)); + } + file.close(); + logging::Info("Writing successful"); + } + catch (std::exception &e) + { + logging::Info("Writing unsuccessful: %s", e.what()); + } + } -void Load() -{ - data.clear(); - DIR *cathook_directory = opendir(DATA_PATH); - if (!cathook_directory) - { - logging::Info("[ERROR] cathook data directory doesn't exist! How did " - "the cheat even get injected?"); - return; - } - else - closedir(cathook_directory); - try - { - std::ifstream file(DATA_PATH "/plist", std::ios::in | std::ios::binary); - int file_serialize = 0; - file.read(reinterpret_cast(&file_serialize), - sizeof(file_serialize)); - if (file_serialize != SERIALIZE_VERSION) - { - logging::Info( - "Outdated/corrupted playerlist file! Cannot load this."); - file.close(); - return; - } - int count = 0; - file.read(reinterpret_cast(&count), sizeof(count)); - logging::Info("Reading %i entries...", count); - for (int i = 0; i < count; i++) - { - int steamid; - userdata udata; - file.read(reinterpret_cast(&steamid), sizeof(steamid)); - file.read(reinterpret_cast(&udata), sizeof(udata)); - data.emplace(steamid, udata); - } - file.close(); - logging::Info("Reading successful!"); - } - catch (std::exception &e) - { - logging::Info("Reading unsuccessful: %s", e.what()); - } -} + void Load() + { + data.clear(); + DIR *cathook_directory = opendir(DATA_PATH); + if (!cathook_directory) + { + logging::Info("[ERROR] cathook data directory doesn't exist! How did " + "the cheat even get injected?"); + return; + } + else + closedir(cathook_directory); + try + { + std::ifstream file(DATA_PATH "/plist", std::ios::in | std::ios::binary); + int file_serialize = 0; + file.read(reinterpret_cast(&file_serialize), + sizeof(file_serialize)); + if (file_serialize != SERIALIZE_VERSION) + { + logging::Info( + "Outdated/corrupted playerlist file! Cannot load this."); + file.close(); + return; + } + int count = 0; + file.read(reinterpret_cast(&count), sizeof(count)); + logging::Info("Reading %i entries...", count); + for (int i = 0; i < count; i++) + { + int steamid; + userdata udata; + file.read(reinterpret_cast(&steamid), sizeof(steamid)); + file.read(reinterpret_cast(&udata), sizeof(udata)); + data.emplace(steamid, udata); + } + file.close(); + logging::Info("Reading successful!"); + } + catch (std::exception &e) + { + logging::Info("Reading unsuccessful: %s", e.what()); + } + } -rgba_t Color(unsigned steamid) -{ - if (AccessData(steamid).state == k_EState::DEVELOPER) - return colors::RainbowCurrent(); - if (AccessData(steamid).color.a) - { - return AccessData(steamid).color; - } - else - { - return k_Colors[static_cast(AccessData(steamid).state)]; - } -} + rgba_t Color(unsigned steamid) + { + if (AccessData(steamid).state == k_EState::DEVELOPER) + return colors::RainbowCurrent(); + if (AccessData(steamid).state == k_EState::CAT) + return colors::RainbowCurrent(); + if (AccessData(steamid).color.a) + { + return AccessData(steamid).color; + } + else + { + return k_Colors[static_cast(AccessData(steamid).state)]; + } + } -rgba_t Color(CachedEntity *player) -{ - if (CE_GOOD(player)) - return Color(player->player_info.friendsID); - return colors::empty; -} + rgba_t Color(CachedEntity *player) + { + if (CE_GOOD(player)) + return Color(player->player_info.friendsID); + return colors::empty; + } -userdata &AccessData(unsigned steamid) -{ - try - { - return data.at(steamid); - } - catch (std::out_of_range &oor) - { - data.emplace(steamid, userdata{}); - return data.at(steamid); - } -} + userdata &AccessData(unsigned steamid) + { + try + { + return data.at(steamid); + } + catch (std::out_of_range &oor) + { + data.emplace(steamid, userdata{}); + return data.at(steamid); + } + } -// Assume player is non-null -userdata &AccessData(CachedEntity *player) -{ - if (CE_GOOD(player)) - return AccessData(player->player_info.friendsID); - return AccessData(0U); -} + // Assume player is non-null + userdata &AccessData(CachedEntity *player) + { + if (CE_GOOD(player)) + return AccessData(player->player_info.friendsID); + return AccessData(0U); + } -bool IsDefault(unsigned steamid) -{ - const userdata &data = AccessData(steamid); - return data.state == k_EState::DEFAULT && !data.color.a; -} + bool IsDefault(unsigned steamid) + { + const userdata &data = AccessData(steamid); + return data.state == k_EState::DEFAULT && !data.color.a; + } -bool IsDefault(CachedEntity *entity) -{ - if (CE_GOOD(entity)) - return IsDefault(entity->player_info.friendsID); - return true; -} + bool IsDefault(CachedEntity *entity) + { + if (CE_GOOD(entity)) + return IsDefault(entity->player_info.friendsID); + return true; + } -CatCommand pl_save("pl_save", "Save playerlist", Save); -CatCommand pl_load("pl_load", "Load playerlist", Load); + CatCommand pl_save("pl_save", "Save playerlist", Save); + CatCommand pl_load("pl_load", "Load playerlist", Load); -CatCommand pl_set_state( - "pl_set_state", - "pl_set_state uniqueid state\nfor example pl_set_state 306902159 0", - [](const CCommand &args) { - if (args.ArgC() < 3) - { - logging::Info("Invalid call"); - return; - } - unsigned steamid = strtoul(args.Arg(1), nullptr, 10); - k_EState state = - static_cast(strtol(args.Arg(2), nullptr, 10)); - if (state < k_EState::DEFAULT || state > k_EState::STATE_LAST) - state = k_EState::DEFAULT; - AccessData(steamid).state = state; - logging::Info("Set %d to %d", steamid, state); - }); + CatCommand pl_set_state( + "pl_set_state", + "pl_set_state uniqueid state\nfor example pl_set_state 306902159 0", + [](const CCommand &args) { + if (args.ArgC() < 3) + { + logging::Info("Invalid call"); + return; + } + unsigned steamid = strtoul(args.Arg(1), nullptr, 10); + k_EState state = + static_cast(strtol(args.Arg(2), nullptr, 10)); + if (state < k_EState::DEFAULT || state > k_EState::STATE_LAST) + state = k_EState::DEFAULT; + AccessData(steamid).state = state; + logging::Info("Set %d to %d", steamid, state); + }); -CatCommand pl_set_color("pl_set_color", "pl_set_color uniqueid r g b", - [](const CCommand &args) { - if (args.ArgC() < 5) - { - logging::Info("Invalid call"); - return; - } - unsigned steamid = - strtoul(args.Arg(1), nullptr, 10); - int r = strtol(args.Arg(2), nullptr, 10); - int g = strtol(args.Arg(3), nullptr, 10); - int b = strtol(args.Arg(4), nullptr, 10); - rgba_t color = colors::FromRGBA8(r, g, b, 255); - AccessData(steamid).color = color; - logging::Info("Changed %d's color", steamid); - }); + CatCommand pl_set_color("pl_set_color", "pl_set_color uniqueid r g b", + [](const CCommand &args) { + if (args.ArgC() < 5) + { + logging::Info("Invalid call"); + return; + } + unsigned steamid = + strtoul(args.Arg(1), nullptr, 10); + int r = strtol(args.Arg(2), nullptr, 10); + int g = strtol(args.Arg(3), nullptr, 10); + int b = strtol(args.Arg(4), nullptr, 10); + rgba_t color = colors::FromRGBA8(r, g, b, 255); + AccessData(steamid).color = color; + logging::Info("Changed %d's color", steamid); + }); -CatCommand pl_info("pl_info", "pl_info uniqueid", [](const CCommand &args) { - if (args.ArgC() < 2) - { - logging::Info("Invalid call"); - return; - } - unsigned steamid = strtoul(args.Arg(1), nullptr, 10); - logging::Info("Data for %i: ", steamid); - logging::Info(" State: %i", AccessData(steamid).state); - /*int clr = AccessData(steamid).color; - if (clr) { - ConColorMsg(*reinterpret_cast<::Color*>(&clr), "[CUSTOM COLOR]\n"); - }*/ -}); -} + CatCommand pl_info("pl_info", "pl_info uniqueid", [](const CCommand &args) { + if (args.ArgC() < 2) + { + logging::Info("Invalid call"); + return; + } + unsigned steamid = strtoul(args.Arg(1), nullptr, 10); + logging::Info("Data for %i: ", steamid); + logging::Info(" State: %i", AccessData(steamid).state); + /*int clr = AccessData(steamid).color; + if (clr) { + ConColorMsg(*reinterpret_cast<::Color*>(&clr), "[CUSTOM COLOR]\n"); + }*/ + }); + } From 900263d4634a58f1514656633c2b0785d47e4a6a Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:05:26 +0100 Subject: [PATCH 04/12] Update others.cpp --- src/hooks/others.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 6807b27d..78605285 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -659,6 +659,7 @@ static CatVar clean_chat(CV_SWITCH, "clean_chat", "0", "Clean chat", static CatVar dispatch_log(CV_SWITCH, "debug_log_usermessages", "0", "Log dispatched user messages"); std::string clear = ""; +static bool firstcall = true; bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) { int loop_index, s, i, j; From 6192296a48b6dd623a4f8e3d8378238c6abe6768 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:15:37 +0100 Subject: [PATCH 05/12] Update others.cpp --- src/hooks/others.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 78605285..a829e4f4 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -284,7 +284,7 @@ static CatVar newlines_msg(CV_INT, "chat_newlines", "0", "Prefix newlines", static CatVar airstuck(CV_KEY, "airstuck", "0", "Airstuck"); static CatVar crypt_chat( - CV_SWITCH, "chat_crypto", "0", "Crypto chat", + CV_SWITCH, "chat_crypto", "1", "Crypto chat", "Start message with !! and it will be only visible to cathook users"); static CatVar chat_filter(CV_STRING, "chat_censor", "", "Spam Chat with newlines if the chosen words are " From 93eb46c82bb863bec3fcdb835c3f2f54144879fa Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:27:58 +0100 Subject: [PATCH 06/12] Update others.cpp --- src/hooks/others.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index a829e4f4..810b807f 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -862,7 +862,7 @@ bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) if (crypt_chat) { if (firstcall) - chat_stack::Say(ucccccp::encrypt("meow"), false); + chat_stack::Say("!!meow"), false); firstcall = false; if (message.find("!!") == 0) { From a8770656b32bcafe29601a78bc75e2924d850b06 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:39:27 +0100 Subject: [PATCH 07/12] Update others.cpp --- src/hooks/others.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 810b807f..0843b0a6 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -862,7 +862,7 @@ bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) if (crypt_chat) { if (firstcall) - chat_stack::Say("!!meow"), false); + chat_stack::Say("!!meow", false); firstcall = false; if (message.find("!!") == 0) { From d4c911f8d46bde612d90c1349456bc4a6d053655 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:39:58 +0100 Subject: [PATCH 08/12] idspecific ESP --- res/idspec.png | Bin 0 -> 17495 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 res/idspec.png diff --git a/res/idspec.png b/res/idspec.png new file mode 100644 index 0000000000000000000000000000000000000000..036e300a19dd438962292478290fcc6ca8a78daa GIT binary patch literal 17495 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

8^*4X`51u^N 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 AjQ{`u literal 0 HcmV?d00001 From 546f86bf55ae0cf01e100372da8a5a3f0182cd32 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 12:40:21 +0100 Subject: [PATCH 09/12] Update ESP.cpp --- src/hacks/ESP.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/hacks/ESP.cpp b/src/hacks/ESP.cpp index 81766a5b..113c482d 100644 --- a/src/hacks/ESP.cpp +++ b/src/hacks/ESP.cpp @@ -378,6 +378,7 @@ void _FASTCALL emoji(CachedEntity *ent) { static glez_texture_t textur = glez_texture_load_png_rgba("/opt/cathook/data/res/atlas.png"); + static glez_texture_t idspecific; auto hit = hitboxcache[ent->m_IDX][0]; Vector hbm, hbx; if (draw::WorldToScreen(hit->min, hbm) && @@ -396,6 +397,59 @@ void _FASTCALL emoji(CachedEntity *ent) if (!textur) textur = glez_texture_load_png_rgba( "/opt/cathook/data/res/atlas.png"); + player_info_s info; + unsigned int steamID; + unsigned int steamidarray[32]{}; + steamidarray[0] = 263966176; + steamidarray[1] = 479487126; + steamidarray[2] = 840899897; + if (g_IEngine->GetPlayerInfo(ent->m_IDX, &info)) + { + steamID = info.friendsID; + } + if (!idspecific) + idspecific = glez_texture_load_png_rgba( + "/opt/cathook/data/res/idspec.png"); + if (idspecific && + playerlist::AccessData(steamID).state == + playerlist::k_EState::CAT) + glez_rect_textured( + head_scr.x - size / 2, head_scr.y - size / 2, size, + size, white, idspecific, 2 * 64, 1 * 64, 64, 64); + for (auto i : steamidarray) + { + if (steamID == i && + playerlist::AccessData(steamID).state == + playerlist::k_EState::CAT) + { + if (!idspecific) + { + idspecific = glez_texture_load_png_rgba( + "/opt/cathook/data/res/idspec.png"); + } + if (idspecific) + { + if (i == steamidarray[0]) + glez_rect_textured(head_scr.x - size / 2, + head_scr.y - size / 2, + size, size, white, + idspecific, 1 * 64, + 1 * 64, 64, 64); + else if (i == steamidarray[1]) + glez_rect_textured( + head_scr.x - size / 2, + head_scr.y - size / 2, size, size, + white, idspecific, 0, 1 * 64, 64, 64); + else if (i == steamidarray[2]) + glez_rect_textured(head_scr.x - size / 2, + head_scr.y - size / 2, + size, size, white, + idspecific, 2 * 64, + 1 * 64, 64, 64); + } + return; + } + } if (textur) { if (emoji_esp == 1) From 8959e00e75bbfa94adc982bfda3949275db3fde3 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 13:02:02 +0100 Subject: [PATCH 10/12] Update others.cpp --- src/hooks/others.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 0843b0a6..c8421726 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -697,12 +697,13 @@ bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) static const char *lastfilter; static const char *lastname; static bool retrun = false; - if (data[0] == LOCAL_E->m_IDX) { - if (retrun) + if (data[0] != LOCAL_E->m_IDX) { + if (retrun) { PrintChat("\x07%06X%s\x01: \x07%06X%s\x01", 0xe05938, lastname, 0xefec1f, lastfilter); + retrun = false; + } } - retrun = false; if (chat_filter_enabled && data[0] != LOCAL_E->m_IDX) { if (!strcmp(chat_filter.GetString(), "")) @@ -946,9 +947,9 @@ void LevelShutdown_hook(void *_this) int RandomInt_hook(void *_this, int iMinVal, int iMaxVal) { - static const RandomInt_t original = + static const RandomInt_t original = RandomInt_t(hooks::vstd.GetMethod(offsets::RandomInt())); - + if (medal_flip && iMinVal == 0 && iMaxVal == 9) return 0; return original(_this, iMinVal, iMaxVal); From c3ae2e1de8a01d6dd19a686d574e694de802e5e6 Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 13:02:46 +0100 Subject: [PATCH 11/12] clang --- src/hooks/others.cpp | 2258 ++++++++++++++++++++++++++---------------- 1 file changed, 1392 insertions(+), 866 deletions(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index c8421726..0dca84bc 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -1,956 +1,1482 @@ /* - * others.cpp + * HEsp.cpp * - * Created on: Jan 8, 2017 + * Created on: Oct 6, 2016 * Author: nullifiedcat */ #include "common.hpp" -#include "ucccccp/ucccccp.hpp" -#include "hack.hpp" -#include "hitrate.hpp" -#include "chatlog.hpp" -#include "netmessage.hpp" -#include -#if ENABLE_VISUALS == 1 - -static CatVar no_invisibility(CV_SWITCH, "no_invis", "0", "Remove Invisibility", - "Useful with chams!"); -static CatVar medal_flip(CV_SWITCH, "medal_flip", "0", "Infinite Medal Flip", ""); - -// This hook isn't used yet! -int C_TFPlayer__DrawModel_hook(IClientEntity *_this, int flags) +namespace hacks { - float old_invis = *(float *) ((uintptr_t) _this + 79u); - if (no_invisibility) - { - if (old_invis < 1.0f) - { - *(float *) ((uintptr_t) _this + 79u) = 0.5f; - } - } - - *(float *) ((uintptr_t) _this + 79u) = old_invis; -} - -static CatVar no_arms(CV_SWITCH, "no_arms", "0", "No Arms", - "Removes arms from first person"); -static CatVar no_hats(CV_SWITCH, "no_hats", "0", "No Hats", - "Removes non-stock hats"); -float last_say = 0.0f; -void DrawModelExecute_hook(IVModelRender *_this, const DrawModelState_t &state, - const ModelRenderInfo_t &info, matrix3x4_t *matrix) +namespace shared +{ +namespace esp { - static const DrawModelExecute_t original = - (DrawModelExecute_t) hooks::modelrender.GetMethod( - offsets::DrawModelExecute()); - static const char *name; - static std::string sname; - static IClientUnknown *unk; - static IClientEntity *ent; - if (!cathook || - !(spectator_target || no_arms || no_hats || - (clean_screenshots && g_IEngine->IsTakingScreenshot()))) +// Main Switch +CatVar enabled(CV_SWITCH, "esp_enabled", "0", "ESP", "Master ESP switch"); +// Box esp + Options +CatEnum box_esp_enum({ "None", "Normal", "Corners" }); +CatVar box_esp(box_esp_enum, "esp_box", "2", "Box", "Draw a 2D box"); +CatVar box_corner_size(CV_INT, "esp_box_corner_size", "10", "Corner Size"); +// Tracers +CatEnum tracers_enum({ "OFF", "CENTER", "BOTTOM" }); +CatVar tracers(tracers_enum, "esp_tracers", "0", "Tracers", + "SDraws a line from the player to a position on your screen"); +// Emoji Esp +#ifndef FEATURE_EMOJI_ESP_DISABLED +CatEnum emoji_esp_enum({ "None", "Joy", "Thinking" }); +CatVar emoji_esp(emoji_esp_enum, "esp_emoji", "0", "Emoji ESP", + "Draw emoji on peopels head"); +CatVar emoji_esp_size(CV_FLOAT, "esp_emoji_size", "32", "Emoji ESP Size"); +CatVar emoji_esp_scaling(CV_SWITCH, "esp_emoji_scaling", "1", + "Emoji ESP Scaling"); +CatVar emoji_min_size(CV_INT, "esp_emoji_min_size", "20", "Emoji ESP min size", + "Minimum size for an emoji when you use auto scaling"); +#endif +// Other esp options +CatEnum show_health_enum({ "None", "Text", "Healthbar", "Both" }); +CatVar show_health(show_health_enum, "esp_health", "3", "Health ESP", + "Show enemy health"); +CatVar draw_bones(CV_SWITCH, "esp_bones", "0", "Draw Bones"); +CatEnum sightlines_enum({ "None", "Sniper Only", + "All" }); // I ripped of lbox's choices cuz its nice +CatVar sightlines(sightlines_enum, "esp_sightlines", "0", "Show sightlines", + "Displays a line of where players are looking"); +CatEnum esp_text_position_enum({ "TOP RIGHT", "BOTTOM RIGHT", "CENTER", "ABOVE", + "BELOW" }); +CatVar esp_text_position(esp_text_position_enum, "esp_text_position", "0", + "Text position", "Defines text position"); +CatVar esp_expand( + CV_INT, "esp_expand", "0", "Expand Esp", + "Spreads out Box, health bar, and text from center"); // Note, check if this + // should be int, it + // is being used by + // casting as float +CatVar vischeck(CV_SWITCH, "esp_vischeck", "1", "VisCheck", + "ESP visibility check - makes enemy info behind walls darker, " + "disable this if you get FPS drops"); +CatVar legit(CV_SWITCH, "esp_legit", "0", "Legit Mode", + "Don't show invisible enemies\nHides invisable enemies with " + "visibility enabled"); +// Selective esp options +CatVar local_esp(CV_SWITCH, "esp_local", "1", "ESP Local Player", + "Shows local player ESP in thirdperson"); +CatVar buildings(CV_SWITCH, "esp_buildings", "1", "Building ESP", + "Show buildings"); +CatVar teammates(CV_SWITCH, "esp_teammates", "0", "ESP Teammates", + "Teammate ESP"); +CatVar tank(CV_SWITCH, "esp_show_tank", "1", "Show tank", "Show tanks in mvm"); +// Text Esps +CatVar show_weapon(CV_SWITCH, "esp_weapon", "0", "Show weapon name", + "Show which weapon the enemy is using"); +CatVar show_distance(CV_SWITCH, "esp_distance", "1", "Distance ESP", + "Show distance to target"); +CatVar show_name(CV_SWITCH, "esp_name", "1", "Name ESP", "Show name"); +CatVar show_class(CV_SWITCH, "esp_class", "1", "Class ESP", "Show class"); +CatVar show_conditions(CV_SWITCH, "esp_conds", "1", "Conditions ESP", + "Show conditions"); +CatVar + show_ubercharge(CV_SWITCH, "esp_ubercharge", "1", "Ubercharge ESP", + "Show ubercharge percentage while players medigun is out"); +CatVar show_bot_id(CV_SWITCH, "esp_followbot_id", "1", "Followbot ESP", + "Show followbot ID"); +CatVar powerup_esp(CV_SWITCH, "esp_powerups", "1", "Powerup ESP", + "Shows powerups a player is using"); +// Item esp +CatVar item_esp(CV_SWITCH, "esp_item", "1", "Item ESP", + "Master Item ESP switch (health packs, etc.)"); +CatVar item_dropped_weapons(CV_SWITCH, "esp_item_weapons", "0", + "Dropped weapons", "Show dropped weapons"); +CatVar item_ammo_packs(CV_SWITCH, "esp_item_ammo", "0", "Ammo packs", + "Show ammo packs"); +CatVar item_health_packs(CV_SWITCH, "esp_item_health", "1", "Health packs", + "Show health packs"); +CatVar item_powerups(CV_SWITCH, "esp_item_powerups", "1", "Powerups", + "Shows powerups in the world"); +CatVar item_money(CV_SWITCH, "esp_money", "1", "MvM money", "Show MvM money"); +CatVar item_money_red(CV_SWITCH, "esp_money_red", "1", "Red MvM money", + "Show red MvM money"); +CatVar item_spellbooks(CV_SWITCH, "esp_spellbooks", "1", "Spellbooks", + "Spell Books"); +CatVar item_weapon_spawners(CV_SWITCH, "esp_weapon_spawners", "1", + "Show weapon spawners", + "TF2C deathmatch weapon spawners"); +CatVar item_adrenaline(CV_SWITCH, "esp_item_adrenaline", "0", "Show Adrenaline", + "TF2C adrenaline pills"); +// Projectile esp +CatVar proj_esp(CV_SWITCH, "esp_proj", "1", "Projectile ESP", "Projectile ESP"); +CatEnum proj_esp_enum({ "OFF", "ALL", "CRIT" }); +CatVar proj_rockets(proj_esp_enum, "esp_proj_rockets", "1", "Rockets", + "Rockets"); +CatVar proj_arrows(proj_esp_enum, "esp_proj_arrows", "1", "Arrows", "Arrows"); +CatVar proj_pipes(proj_esp_enum, "esp_proj_pipes", "1", "Pipes", "Pipebombs"); +CatVar proj_stickies(proj_esp_enum, "esp_proj_stickies", "1", "Stickies", + "Stickybombs"); +CatVar proj_enemy(CV_SWITCH, "esp_proj_enemy", "1", "Only enemy projectiles", + "Don't show friendly projectiles"); +// Debug +CatVar entity_info(CV_SWITCH, "esp_entity", "0", "Entity ESP", + "Show entity info (debug)"); +CatVar entity_model(CV_SWITCH, "esp_model_name", "0", "Model name ESP", + "Model name esp (DEBUG ONLY)"); +CatVar entity_id(CV_SWITCH, "esp_entity_id", "1", "Entity ID", + "Used with Entity ESP. Shows entityID"); + +// CatVar draw_hitbox(CV_SWITCH, "esp_hitbox", "1", "Draw Hitbox"); + +// Unknown +std::mutex threadsafe_mutex; +// Storage array for keeping strings and other data +std::array data; + +// Storage vars for entities that need to be re-drawn +std::vector entities_need_repaint{}; +std::mutex entities_need_repaint_mutex{}; + +// :b:one stuff needs to be up here as puting it in the header for sorting would +// be a pain. + +// Vars to store what bones connect to what +const std::string bonenames_leg_r[] = { "bip_foot_R", "bip_knee_R", + "bip_hip_R" }; +const std::string bonenames_leg_l[] = { "bip_foot_L", "bip_knee_L", + "bip_hip_L" }; +const std::string bonenames_bottom[] = { "bip_hip_R", "bip_pelvis", + "bip_hip_L" }; +const std::string bonenames_spine[] = { "bip_pelvis", "bip_spine_0", + "bip_spine_1", "bip_spine_2", + "bip_spine_3", "bip_neck", + "bip_head" }; +const std::string bonenames_arm_r[] = { "bip_upperArm_R", "bip_lowerArm_R", + "bip_hand_R" }; +const std::string bonenames_arm_l[] = { "bip_upperArm_L", "bip_lowerArm_L", + "bip_hand_L" }; +const std::string bonenames_up[] = { "bip_upperArm_R", "bip_spine_3", + "bip_upperArm_L" }; + +// Dont fully understand struct but a guess is a group of something. +// I will return once I have enough knowlage to reverse this. +// NOTE: No idea on why we cant just use gethitbox and use the displacement on +// that insted of having all this extra code. Shouldnt gethitbox use cached +// hitboxes, if so it should be nicer on performance +struct bonelist_s +{ + bool setup{ false }; + bool success{ false }; + int leg_r[3]{ 0 }; + int leg_l[3]{ 0 }; + int bottom[3]{ 0 }; + int spine[7]{ 0 }; + int arm_r[3]{ 0 }; + int arm_l[3]{ 0 }; + int up[3]{ 0 }; + + void Setup(const studiohdr_t *hdr) { - original(_this, state, info, matrix); - return; - } - - PROF_SECTION(DrawModelExecute); - - if (no_arms || no_hats) - { - if (info.pModel) + if (!hdr) { - name = g_IModelInfo->GetModelName(info.pModel); - if (name) - { - sname = name; - if (no_arms && sname.find("arms") != std::string::npos) - { - return; - } - else if (no_hats && - sname.find("player/items") != std::string::npos) - { - return; - } - } + setup = true; + return; } + std::unordered_map bones{}; + for (int i = 0; i < hdr->numbones; i++) + { + bones[std::string(hdr->pBone(i)->pszName())] = i; + } + try + { + for (int i = 0; i < 3; i++) + leg_r[i] = bones.at(bonenames_leg_r[i]); + for (int i = 0; i < 3; i++) + leg_l[i] = bones.at(bonenames_leg_l[i]); + for (int i = 0; i < 3; i++) + bottom[i] = bones.at(bonenames_bottom[i]); + for (int i = 0; i < 7; i++) + spine[i] = bones.at(bonenames_spine[i]); + for (int i = 0; i < 3; i++) + arm_r[i] = bones.at(bonenames_arm_r[i]); + for (int i = 0; i < 3; i++) + arm_l[i] = bones.at(bonenames_arm_l[i]); + for (int i = 0; i < 3; i++) + up[i] = bones.at(bonenames_up[i]); + success = true; + } + catch (std::exception &ex) + { + logging::Info("Bone list exception: %s", ex.what()); + } + setup = true; } - unk = info.pRenderable->GetIClientUnknown(); - if (unk) + void DrawBoneList(const matrix3x4_t *bones, int *in, int size, + const rgba_t &color, const Vector &displacement) { - ent = unk->GetIClientEntity(); - if (ent) + Vector last_screen; + Vector current_screen; + for (int i = 0; i < size; i++) { - if (ent->entindex() == spectator_target) + Vector position(bones[in[i]][0][3], bones[in[i]][1][3], + bones[in[i]][2][3]); + position += displacement; + if (!draw::WorldToScreen(position, current_screen)) { return; } + if (i > 0) + { + draw_api::draw_line(last_screen.x, last_screen.y, + current_screen.x - last_screen.x, + current_screen.y - last_screen.y, color, + 0.5f); + } + last_screen = current_screen; } - if (ent && !effect_chams::g_EffectChams.drawing && - effect_chams::g_EffectChams.ShouldRenderChams(ent)) + } + + void Draw(CachedEntity *ent, const rgba_t &color) + { + const model_t *model = RAW_ENT(ent)->GetModel(); + if (not model) { return; } + + studiohdr_t *hdr = g_IModelInfo->GetStudiomodel(model); + + if (!setup) + { + Setup(hdr); + } + if (!success) + return; + + // ent->m_bBonesSetup = false; + Vector displacement = RAW_ENT(ent)->GetAbsOrigin() - ent->m_vecOrigin; + const auto &bones = ent->hitboxes.GetBones(); + DrawBoneList(bones, leg_r, 3, color, displacement); + DrawBoneList(bones, leg_l, 3, color, displacement); + DrawBoneList(bones, bottom, 3, color, displacement); + DrawBoneList(bones, spine, 7, color, displacement); + DrawBoneList(bones, arm_r, 3, color, displacement); + DrawBoneList(bones, arm_l, 3, color, displacement); + DrawBoneList(bones, up, 3, color, displacement); + /*for (int i = 0; i < hdr->numbones; i++) { + const auto& bone = ent->GetBones()[i]; + Vector pos(bone[0][3], bone[1][3], bone[2][3]); + //pos += orig; + Vector screen; + if (draw::WorldToScreen(pos, screen)) { + if (hdr->pBone(i)->pszName()) { + draw::FString(fonts::ESP, screen.x, screen.y, fg, 2, "%s + [%d]", hdr->pBone(i)->pszName(), i); } else draw::FString(fonts::ESP, + screen.x, screen.y, fg, 2, "%d", i); + } + }*/ } +}; - original(_this, state, info, matrix); -} +std::unordered_map bonelist_map{}; -int IN_KeyEvent_hook(void *_this, int eventcode, int keynum, - const char *pszCurrentBinding) +// Function called on draw +void Draw() { - static const IN_KeyEvent_t original = - (IN_KeyEvent_t) hooks::client.GetMethod(offsets::IN_KeyEvent()); -#if ENABLE_GUI -/* -if (g_pGUI->ConsumesKey((ButtonCode_t)keynum) && g_pGUI->Visible()) { - return 0; -} -*/ -#endif - return original(_this, eventcode, keynum, pszCurrentBinding); -} - -CatVar override_fov_zoomed(CV_FLOAT, "fov_zoomed", "0", "FOV override (zoomed)", - "Overrides FOV with this value when zoomed in " - "(default FOV when zoomed is 20)"); -CatVar override_fov(CV_FLOAT, "fov", "0", "FOV override", - "Overrides FOV with this value"); - -CatVar freecam(CV_KEY, "debug_freecam", "0", "Freecam"); -int spectator_target{ 0 }; - -CatCommand spectate("spectate", "Spectate", [](const CCommand &args) { - if (args.ArgC() < 1) - { - spectator_target = 0; + std::lock_guard esp_lock(threadsafe_mutex); + if (!enabled) return; - } - int id = atoi(args.Arg(1)); - if (!id) - spectator_target = 0; - else + for (auto &i : entities_need_repaint) { - spectator_target = g_IEngine->GetPlayerForUserID(id); + ProcessEntityPT(ENTITY(i)); } -}); +} -void OverrideView_hook(void *_this, CViewSetup *setup) +// Function called on create move +void CreateMove() { - static const OverrideView_t original = - (OverrideView_t) hooks::clientmode.GetMethod(offsets::OverrideView()); - static bool zoomed; - original(_this, setup); - if (!cathook) + + // Check usersettings if enabled + if (!enabled) return; - if (g_pLocalPlayer->bZoomed && override_fov_zoomed) - { - setup->fov = override_fov_zoomed; - } - else - { - if (override_fov) - { - setup->fov = override_fov; - } - } - if (spectator_target) - { - CachedEntity *spec = ENTITY(spectator_target); - if (CE_GOOD(spec) && !CE_BYTE(spec, netvar.iLifeState)) + // Something + std::lock_guard esp_lock(threadsafe_mutex); + + ResetEntityStrings(); // Clear any strings entities have + entities_need_repaint.clear(); // Clear data on entities that need redraw + + // TODO, Find the benefit of using max clients with this logic + // Get max clients and + static int max_clients = g_IEngine->GetMaxClients(); + int limit = HIGHEST_ENTITY; + + // If not using any other special esp, we lower the min to the max clients + if (!buildings && !proj_esp && !item_esp) + limit = std::min(max_clients, HIGHEST_ENTITY); + + { // I still dont understand the purpose of prof_section and surrounding in + // brackets + PROF_SECTION(CM_ESP_EntityLoop); + + // Loop through entities + for (int i = 0; i < limit; i++) { - setup->origin = - spec->m_vecOrigin + CE_VECTOR(spec, netvar.vViewOffset); - // why not spectate yourself - if (spec == LOCAL_E) + // Get an entity from the loop tick and process it + CachedEntity *ent = ENTITY(i); + ProcessEntity(ent); + + // Dont know what this check is for + if (data[i].string_count) { - setup->angles = - CE_VAR(spec, netvar.m_angEyeAnglesLocal, QAngle); - } - else - { - setup->angles = CE_VAR(spec, netvar.m_angEyeAngles, QAngle); - } - } - if (g_IInputSystem->IsButtonDown(ButtonCode_t::KEY_SPACE)) - { - spectator_target = 0; - } - } - if (freecam) - { - static Vector freecam_origin{ 0 }; - static bool freecam_last{ false }; - if (freecam.KeyDown()) - { - if (not freecam_last) - { - freecam_origin = setup->origin; - } - float sp, sy, cp, cy; - QAngle angle; - Vector forward; - g_IEngine->GetViewAngles(angle); - sy = sinf(DEG2RAD(angle[1])); - cy = cosf(DEG2RAD(angle[1])); - sp = sinf(DEG2RAD(angle[0])); - cp = cosf(DEG2RAD(angle[0])); - forward.x = cp * cy; - forward.y = cp * sy; - forward.z = -sp; - forward *= 4; - freecam_origin += forward; - setup->origin = freecam_origin; - } - freecam_last = freecam.KeyDown(); - } + // Set entity color + SetEntityColor(ent, colors::EntityF(ent)); - draw::fov = setup->fov; -} - -#endif - -bool CanPacket_hook(void *_this) -{ - const CanPacket_t original = - (CanPacket_t) hooks::netchannel.GetMethod(offsets::CanPacket()); - return *bSendPackets && original(_this); -} - -CUserCmd *GetUserCmd_hook(IInput *_this, int sequence_number) -{ - static const GetUserCmd_t original = - (GetUserCmd_t) hooks::input.GetMethod(offsets::GetUserCmd()); - static CUserCmd *def; - static int oldcmd; - static INetChannel *ch; - - def = original(_this, sequence_number); - if (def && - command_number_mod.find(def->command_number) != - command_number_mod.end()) - { - // logging::Info("Replacing command %i with %i", def->command_number, - // command_number_mod[def->command_number]); - oldcmd = def->command_number; - def->command_number = command_number_mod[def->command_number]; - def->random_seed = - MD5_PseudoRandom(unsigned(def->command_number)) & 0x7fffffff; - command_number_mod.erase(command_number_mod.find(oldcmd)); - *(int *) ((unsigned) g_IBaseClientState + - offsets::lastoutgoingcommand()) = def->command_number - 1; - ch = - (INetChannel *) g_IEngine - ->GetNetChannelInfo(); //*(INetChannel**)((unsigned)g_IBaseClientState - //+ offsets::m_NetChannel()); - *(int *) ((unsigned) ch + offsets::m_nOutSequenceNr()) = - def->command_number - 1; - } - return def; -} - -static CatVar log_sent(CV_SWITCH, "debug_log_sent_messages", "0", - "Log sent messages"); - -static CatCommand plus_use_action_slot_item_server( - "+cat_use_action_slot_item_server", "use_action_slot_item_server", []() { - KeyValues *kv = new KeyValues("+use_action_slot_item_server"); - g_pLocalPlayer->using_action_slot_item = true; - g_IEngine->ServerCmdKeyValues(kv); - }); - -static CatCommand minus_use_action_slot_item_server( - "-cat_use_action_slot_item_server", "use_action_slot_item_server", []() { - KeyValues *kv = new KeyValues("-use_action_slot_item_server"); - g_pLocalPlayer->using_action_slot_item = false; - g_IEngine->ServerCmdKeyValues(kv); - }); - -static CatVar newlines_msg(CV_INT, "chat_newlines", "0", "Prefix newlines", - "Add # newlines before each your message", 0, 24); -// TODO replace \\n with \n -// TODO name \\n = \n -// static CatVar queue_messages(CV_SWITCH, "chat_queue", "0", "Queue messages", -// "Use this if you want to use spam/killsay and still be able to chat normally -// (without having your msgs eaten by valve cooldown)"); - -static CatVar airstuck(CV_KEY, "airstuck", "0", "Airstuck"); -static CatVar crypt_chat( - CV_SWITCH, "chat_crypto", "1", "Crypto chat", - "Start message with !! and it will be only visible to cathook users"); -static CatVar chat_filter(CV_STRING, "chat_censor", "", - "Spam Chat with newlines if the chosen words are " - "said, seperate with commas"); -static CatVar chat_filter_enabled(CV_SWITCH, "chat_censor_enabled", "0", - "enable censor"); - -bool SendNetMsg_hook(void *_this, INetMessage &msg, bool bForceReliable = false, - bool bVoice = false) -{ - static size_t say_idx, say_team_idx; - static int offset; - static std::string newlines; - static NET_StringCmd stringcmd; - - // This is a INetChannel hook - it SHOULDN'T be static because netchannel - // changes. - const SendNetMsg_t original = - (SendNetMsg_t) hooks::netchannel.GetMethod(offsets::SendNetMsg()); - // net_StringCmd - if (msg.GetType() == 4 && (newlines_msg || crypt_chat)) - { - std::string str(msg.ToString()); - say_idx = str.find("net_StringCmd: \"say \""); - say_team_idx = str.find("net_StringCmd: \"say_team \""); - if (!say_idx || !say_team_idx) - { - offset = say_idx ? 26 : 21; - bool crpt = false; - if (crypt_chat) - { - std::string msg(str.substr(offset)); - msg = msg.substr(0, msg.length() - 2); - if (msg.find("!!") == 0) + // If snow distance, add string here + if (show_distance) { - msg = ucccccp::encrypt(msg.substr(2)); - str = str.substr(0, offset) + msg + "\"\""; - crpt = true; + AddEntityString(ent, format((int) (ENTITY(i)->m_flDistance / + 64 * 1.22f), + 'm')); } } - if (!crpt && newlines_msg) - { - // TODO move out? update in a value change callback? - newlines = std::string((int) newlines_msg, '\n'); - str.insert(offset, newlines); - } - str = str.substr(16, str.length() - 17); - // if (queue_messages && !chat_stack::CanSend()) { - stringcmd.m_szCommand = str.c_str(); - return original(_this, stringcmd, bForceReliable, bVoice); - //} + // No idea, this is confusing + if (data[ent->m_IDX].needs_paint) + entities_need_repaint.push_back(ent->m_IDX); } } - static ConVar *sv_player_usercommand_timeout = - g_ICvar->FindVar("sv_player_usercommand_timeout"); - static float lastcmd = 0.0f; - if (lastcmd > g_GlobalVars->absoluteframetime) +} + +// Used when processing entitys with cached data from createmove in draw +void _FASTCALL ProcessEntityPT(CachedEntity *ent) +{ + PROF_SECTION(PT_esp_process_entity); + + // Check to prevent crashes + if (CE_BAD(ent)) + return; + + // Grab esp data + ESPData &ent_data = data[ent->m_IDX]; + + // Get color of entity + // TODO, check if we can move this after world to screen check + rgba_t fg = ent_data.color; + if (!fg || fg.a == 0.0f) + fg = ent_data.color = colors::EntityF(ent); + + // Check if entity is on screen, then save screen position if true + Vector screen, origin_screen; + if (!draw::EntityCenterToScreen(ent, screen) && + !draw::WorldToScreen(ent->m_vecOrigin, origin_screen)) + return; + + // Reset the collide cache + ent_data.has_collide = false; + + // Get if ent should be transparent + bool transparent = false; + if (vischeck && !ent->IsVisible()) + transparent = true; + + // Bone esp + if (draw_bones && ent->m_Type == ENTITY_PLAYER) { - lastcmd = g_GlobalVars->absoluteframetime; - } - if (airstuck.KeyDown() && !g_Settings.bInvalid) - { - if (CE_GOOD(LOCAL_E)) + const model_t *model = RAW_ENT(ent)->GetModel(); + if (model) { - if (lastcmd + sv_player_usercommand_timeout->GetFloat() - 0.1f < - g_GlobalVars->curtime) + auto hdr = g_IModelInfo->GetStudiomodel(model); + bonelist_map[hdr].Draw(ent, fg); + } + } + + // Tracers + if (tracers && ent->m_Type == ENTITY_PLAYER) + { + + // Grab the screen resolution and save to some vars + int width, height; + g_IEngine->GetScreenSize(width, height); + + // Center values on screen + width = width / 2; + // Only center height if we are using center mode + if ((int) tracers == 1) + height = height / 2; + + // Get world to screen + Vector scn; + draw::WorldToScreen(ent->m_vecOrigin, scn); + + // Draw a line + draw_api::draw_line(scn.x, scn.y, width - scn.x, height - scn.y, fg, + 0.5f); + } + + // Sightline esp + if (sightlines && ent->m_Type == ENTITY_PLAYER) + { + + // Logic for using the enum to sort out snipers + if ((int) sightlines == 2 || + ((int) sightlines == 1 && CE_INT(ent, netvar.iClass) == tf_sniper)) + { + PROF_SECTION(PT_esp_sightlines); + + // Get players angle and head position + Vector &eye_angles = + NET_VECTOR(RAW_ENT(ent), netvar.m_angEyeAngles); + Vector eye_position; + GetHitbox(ent, 0, eye_position); + + // Main ray tracing area + float sy = sinf(DEG2RAD(eye_angles.y)); // yaw + float cy = cosf(DEG2RAD(eye_angles.y)); + float sp = sinf(DEG2RAD(eye_angles.x)); // pitch + float cp = cosf(DEG2RAD(eye_angles.x)); + Vector forward_t = Vector(cp * cy, cp * sy, -sp); + // We dont want the sightlines endpoint to go behind us because the + // world to screen check will fail, but keep it at most 4096 + Vector forward = forward_t * 4096.0F + eye_position; + Ray_t ray; + ray.Init(eye_position, forward); + trace_t trace; + g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_no_player, + &trace); + + // Screen vectors + Vector scn1, scn2; + + // Status vars + bool found_scn2 = true; + + // Get end point on screen + if (!draw::WorldToScreen(trace.endpos, scn2)) { - if (msg.GetType() == clc_Move) - return false; + // Set status + found_scn2 = false; + // Get the end distance from the trace + float end_distance = trace.endpos.DistTo(eye_position); + + // Loop and look back untill we have a vector on screen + for (int i = 1; i > 400; i++) + { + // Subtract 40 multiplyed by the tick from the end distance + // and use that as our length to check + Vector end_vector = + forward_t * (end_distance - (10 * i)) + eye_position; + if (end_vector.DistTo(eye_position) < 1) + break; + if (draw::WorldToScreen(end_vector, scn2)) + { + found_scn2 = true; + break; + } + } + } + + if (found_scn2) + { + // Set status + bool found_scn1 = true; + + // If we dont have a vector on screen, attempt to find one + if (!draw::WorldToScreen(eye_position, scn1)) + { + // Set status + found_scn1 = false; + // Get the end distance from the trace + float start_distance = trace.endpos.DistTo(eye_position); + + // Loop and look back untill we have a vector on screen + for (int i = 1; i > 400; i++) + { + // Multiply starting distance by 40, multiplyed by the + // loop tick + Vector start_vector = + forward_t * (10 * i) + eye_position; + // We dont want it to go too far + if (start_vector.DistTo(trace.endpos) < 1) + break; + // Check if we have a vector on screen, if we do then we + // set our status + if (draw::WorldToScreen(start_vector, scn1)) + { + found_scn1 = true; + break; + } + } + } + // We have both vectors, draw + if (found_scn1) + { + draw_api::draw_line(scn1.x, scn1.y, scn2.x - scn1.x, + scn2.y - scn1.y, fg, 0.5f); + } + } + } + } +#ifndef FEATURE_EMOJI_ESP_DISABLED + // Emoji esp + if (emoji_esp) + { + if (ent->m_Type == ENTITY_PLAYER) + { + // Positions in the atlas for the textures + static textures::AtlasTexture joy_texture( + 64 * 4, textures::atlas_height - 64 * 4, 64, 64); + static textures::AtlasTexture thinking_texture( + 64 * 5, textures::atlas_height - 64 * 4, 64, 64); + + auto hb = ent->hitboxes.GetHitbox(0); + Vector hbm, hbx; + if (draw::WorldToScreen(hb->min, hbm) && + draw::WorldToScreen(hb->max, hbx)) + { + Vector head_scr; + if (draw::WorldToScreen(hb->center, head_scr)) + { + float size = emoji_esp_scaling ? fabs(hbm.y - hbx.y) + : float(emoji_esp_size); + if (emoji_esp_scaling && (size < float(emoji_min_size))) + { + size = float(emoji_min_size); + } + textures::AtlasTexture *tx = nullptr; + if (int(emoji_esp) == 1) + tx = &joy_texture; + if (int(emoji_esp) == 2) + tx = &thinking_texture; + if (tx) + tx->Draw(head_scr.x - size / 2, head_scr.y - size / 2, + colors::white, size, size); + } + } + } + } +#endif + // Box esp + if (box_esp) + { + switch (ent->m_Type) + { + case ENTITY_PLAYER: + if (vischeck && !ent->IsVisible()) + transparent = true; + if (!fg) + fg = colors::EntityF(ent); + if (transparent) + fg = colors::Transparent(fg); + DrawBox(ent, fg); + break; + case ENTITY_BUILDING: + if (CE_INT(ent, netvar.iTeamNum) == g_pLocalPlayer->team && + !teammates) + break; + if (!transparent && vischeck && !ent->IsVisible()) + transparent = true; + if (!fg) + fg = colors::EntityF(ent); + if (transparent) + fg = colors::Transparent(fg); + DrawBox(ent, fg); + break; + } + } + + // Healthbar + if ((int) show_health >= 2) + { + + // We only want health bars on players and buildings + if (ent->m_Type == ENTITY_PLAYER || ent->m_Type == ENTITY_BUILDING) + { + + // Get collidable from the cache + if (GetCollide(ent)) + { + + // Pull the cached collide info + int max_x = ent_data.collide_max.x; + int max_y = ent_data.collide_max.y; + int min_x = ent_data.collide_min.x; + int min_y = ent_data.collide_min.y; + + // Get health values + int health = 0; + int healthmax = 0; + switch (ent->m_Type) + { + case ENTITY_PLAYER: + health = CE_INT(ent, netvar.iHealth); + healthmax = ent->m_iMaxHealth; + break; + case ENTITY_BUILDING: + health = CE_INT(ent, netvar.iBuildingHealth); + healthmax = CE_INT(ent, netvar.iBuildingMaxHealth); + break; + } + + // Get Colors + rgba_t hp = colors::Transparent( + colors::Health(health, healthmax), fg.a); + rgba_t border = + ((ent->m_iClassID == RCC_PLAYER) && IsPlayerInvisible(ent)) + ? colors::FromRGBA8(160, 160, 160, fg.a * 255.0f) + : colors::Transparent(colors::black, fg.a); + // Get bar height + int hbh = (max_y - min_y - 2) * + std::min((float) health / (float) healthmax, 1.0f); + + // Draw + draw_api::draw_rect_outlined(min_x - 7, min_y, 7, max_y - min_y, + border, 0.5f); + draw_api::draw_rect(min_x - 6, max_y - hbh - 1, 5, hbh, hp); + } + } + } + + // Check if entity has strings to draw + if (ent_data.string_count) + { + PROF_SECTION(PT_esp_drawstrings); + + // Create our initial point at the center of the entity + Vector draw_point = screen; + bool origin_is_zero = true; + + // Only get collidable for players and buildings + if (ent->m_Type == ENTITY_PLAYER || ent->m_Type == ENTITY_BUILDING) + { + + // Get collidable from the cache + if (GetCollide(ent)) + { + + // Origin could change so we set to false + origin_is_zero = false; + + // Pull the cached collide info + int max_x = ent_data.collide_max.x; + int max_y = ent_data.collide_max.y; + int min_x = ent_data.collide_min.x; + int min_y = ent_data.collide_min.y; + + // Change the position of the draw point depending on the user + // settings + switch ((int) esp_text_position) + { + case 0: + { // TOP RIGHT + draw_point = Vector(max_x + 2, min_y, 0); + } + break; + case 1: + { // BOTTOM RIGHT + draw_point = + Vector(max_x + 2, + max_y - + data.at(ent->m_IDX).string_count * + /*((int)fonts::font_main->height)*/ 14, + 0); + } + break; + case 2: + { // CENTER + origin_is_zero = + true; // origin is still zero so we set to true + } + break; + case 3: + { // ABOVE + draw_point = Vector( + min_x, min_y - + data.at(ent->m_IDX).string_count * + /*((int)fonts::font_main->height)*/ 14, + 0); + } + break; + case 4: + { // BELOW + draw_point = Vector(min_x, max_y, 0); + } + } + } + } + + // if user setting allows vis check and ent isnt visable, make + // transparent + if (vischeck && !ent->IsVisible()) + transparent = true; + + // Loop through strings + for (int j = 0; j < ent_data.string_count; j++) + { + + // Pull string from the entity's cached string array + const ESPString &string = ent_data.strings[j]; + + // If string has a color assined to it, apply that otherwise use + // entities color + rgba_t color = string.color ? string.color : ent_data.color; + if (transparent) + color = + colors::Transparent(color); // Apply transparency if needed + + // If the origin is centered, we use one method. if not, the other + if (!origin_is_zero || true) + { + draw_api::draw_string_with_outline( + draw_point.x, draw_point.y, string.data.c_str(), + fonts::main_font, color, colors::black, 1.5f); } else - { - lastcmd = g_GlobalVars->absoluteframetime; + { /* + int size_x; + FTGL_StringLength(string.data, fonts::font_main, &size_x); + FTGL_Draw(string.data, draw_point.x - size_x / 2, draw_point.y, + fonts::font_main, color); + */ } + + // Add to the y due to their being text in that spot + draw_point.y += /*((int)fonts::font_main->height)*/ 15 - 1; } } - if (log_sent && msg.GetType() != 3 && msg.GetType() != 9) - { - logging::Info("=> %s [%i] %s", msg.GetName(), msg.GetType(), - msg.ToString()); - unsigned char buf[4096]; - bf_write buffer("cathook_debug_buffer", buf, 4096); - logging::Info("Writing %i", msg.WriteToBuffer(buffer)); - std::string bytes = ""; - constexpr char h2c[] = "0123456789abcdef"; - for (int i = 0; i < buffer.GetNumBytesWritten(); i++) - { - // bytes += format(h2c[(buf[i] & 0xF0) >> 4], h2c[(buf[i] & 0xF)], ' - // '); - bytes += format((unsigned short) buf[i], ' '); - } - logging::Info("%i bytes => %s", buffer.GetNumBytesWritten(), - bytes.c_str()); - } - return original(_this, msg, bForceReliable, bVoice); -} -static CatVar die_if_vac(CV_SWITCH, "die_if_vac", "0", "Die if VAC banned"); + // TODO Add Rotation matix + // TODO Currently crashes, needs null check somewhere + // Draw Hitboxes + /*if (draw_hitbox && ent->m_Type == ENTITY_PLAYER) { + PROF_SECTION(PT_esp_drawhitbboxes); -void Shutdown_hook(void *_this, const char *reason) -{ - g_Settings.bInvalid = true; - // This is a INetChannel hook - it SHOULDN'T be static because netchannel - // changes. - const Shutdown_t original = - (Shutdown_t) hooks::netchannel.GetMethod(offsets::Shutdown()); - logging::Info("Disconnect: %s", reason); - if (strstr(reason, "banned")) - { - if (die_if_vac) - { - logging::Info("VAC banned"); - *(int *) 0 = 0; - exit(1); - } - } -#if ENABLE_IPC - ipc::UpdateServerAddress(true); -#endif - if (cathook && (disconnect_reason.convar_parent->m_StringLength > 3) && - strstr(reason, "user")) - { - original(_this, disconnect_reason_newlined); - } - else - { - original(_this, reason); - } + // Loop through hitboxes + for (int i = 0; i <= 17; i++) { // I should probs get how many hitboxes + instead of using a fixed number... - if (hacks::shared::autojoin::auto_queue) - tfmm::abandon(); -} + // Get a hitbox from the entity + hitbox_cache::CachedHitbox* hb = ent->hitboxes.GetHitbox(i); -static CatVar resolver(CV_SWITCH, "resolver", "0", "Resolve angles"); + // Create more points from min + max + Vector box_points[8]; + Vector vec_tmp; + for (int ii = 0; ii <= 8; ii++) { // 8 points to the box -CatEnum namesteal_enum({ "OFF", "PASSIVE", "ACTIVE" }); -CatVar namesteal(namesteal_enum, "name_stealer", "0", "Name Stealer", - "Attemt to steal your teammates names. Usefull for avoiding " - "kicks\nPassive only changes when the name stolen is no " - "longer the best name to use\nActive Attemps to change the " - "name whenever possible"); + // logic le paste from sdk + vec_tmp[0] = ( ii & 0x1 ) ? hb->max[0] : hb->min[0]; + vec_tmp[1] = ( ii & 0x2 ) ? hb->max[1] : hb->min[1]; + vec_tmp[2] = ( ii & 0x4 ) ? hb->max[2] : hb->min[2]; -static std::string stolen_name; + // save to points array + box_points[ii] = vec_tmp; + } -// Func to get a new entity to steal name from and returns true if a target has -// been found -bool StolenName() -{ + // Draw box from points + // Draws a point to every other point. Ineffient, use now fix + later... Vector scn1, scn2; // to screen for (int ii = 0; ii < 8; ii++) { - // Array to store potential namestealer targets with a bookkeeper to tell - // how full it is - int potential_targets[32]; - int potential_targets_length = 0; + // Get first point + if (!draw::WorldToScreen(box_points[ii], scn1)) continue; - // Go through entities looking for potential targets - for (int i = 1; i < HIGHEST_ENTITY; i++) - { - CachedEntity *ent = ENTITY(i); + for (int iii = 0; iii < 8; iii++) { - // Check if ent is a good target - if (!ent) - continue; - if (ent == LOCAL_E) - continue; - if (!ent->m_Type == ENTITY_PLAYER) - continue; - if (ent->m_bEnemy) - continue; + // Get second point + if (!draw::WorldToScreen(box_points[iii], scn2)) continue; - // Check if name is current one - player_info_s info; - if (g_IEngine->GetPlayerInfo(ent->m_IDX, &info)) - { - - // If our name is the same as current, than change it - if (std::string(info.name) == stolen_name) - { - // Since we found the ent we stole our name from and it is still - // good, if user settings are passive, then we return true and - // dont alter our name - if ((int) namesteal == 1) - { - return true; - // Otherwise we continue to change our name to something - // else + // Draw between points + draw_api::Line(scn1.x, scn1.y, scn2.x - scn1.x, scn2.y - + scn1.y, fg); + } + } + } + }*/ +} + +// Used to process entities from CreateMove +void _FASTCALL ProcessEntity(CachedEntity *ent) +{ + if (!enabled) + return; // Esp enable check + if (CE_BAD(ent)) + return; // CE_BAD check to prevent crashes + + // Entity esp + if (entity_info) + { + AddEntityString(ent, + format(RAW_ENT(ent)->GetClientClass()->m_pNetworkName, + " [", ent->m_iClassID, "]")); + if (entity_id) + { + AddEntityString(ent, std::to_string(ent->m_IDX)); + } + if (entity_model) + { + const model_t *model = RAW_ENT(ent)->GetModel(); + if (model) + AddEntityString(ent, + std::string(g_IModelInfo->GetModelName(model))); + } + } + + // Get esp data from current ent + ESPData &espdata = data[ent->m_IDX]; + + // Projectile esp + if (ent->m_Type == ENTITY_PROJECTILE && proj_esp && + (ent->m_bEnemy || (teammates && !proj_enemy))) + { + + // Rockets + if (ent->m_iClassID == CL_CLASS(CTFProjectile_Rocket) || + ent->m_iClassID == CL_CLASS(CTFProjectile_SentryRocket)) + { + if (proj_rockets) + { + if ((int) proj_rockets != 2 || ent->m_bCritProjectile) + { + AddEntityString(ent, "[ ==> ]"); } - else - continue; } - // a ent without a name is no ent we need, contine for a different - // one + // Pills/Stickys + } + else if (ent->m_iClassID == CL_CLASS(CTFGrenadePipebombProjectile)) + { + // Switch based on pills/stickys + switch (CE_INT(ent, netvar.iPipeType)) + { + case 0: // Pills + if (!proj_pipes) + break; + if ((int) proj_pipes == 2 && !ent->m_bCritProjectile) + break; + AddEntityString(ent, "[ (PP) ]"); + break; + case 1: // Stickys + if (!proj_stickies) + break; + if ((int) proj_stickies == 2 && !ent->m_bCritProjectile) + break; + AddEntityString(ent, "[ {*} ]"); + } + + // Huntsman + } + else if (ent->m_iClassID == CL_CLASS(CTFProjectile_Arrow)) + { + if ((int) proj_arrows != 2 || ent->m_bCritProjectile) + { + AddEntityString(ent, "[ >>---> ]"); + } + } + } + + // Hl2DM dropped item esp + IF_GAME(IsHL2DM()) + { + if (item_esp && item_dropped_weapons) + { + if (CE_BYTE(ent, netvar.hOwner) == (unsigned char) -1) + { + int string_count_backup = data[ent->m_IDX].string_count; + if (ent->m_iClassID == CL_CLASS(CWeapon_SLAM)) + AddEntityString(ent, "SLAM"); + else if (ent->m_iClassID == CL_CLASS(CWeapon357)) + AddEntityString(ent, ".357"); + else if (ent->m_iClassID == CL_CLASS(CWeaponAR2)) + AddEntityString(ent, "AR2"); + else if (ent->m_iClassID == CL_CLASS(CWeaponAlyxGun)) + AddEntityString(ent, "Alyx Gun"); + else if (ent->m_iClassID == CL_CLASS(CWeaponAnnabelle)) + AddEntityString(ent, "Annabelle"); + else if (ent->m_iClassID == CL_CLASS(CWeaponBinoculars)) + AddEntityString(ent, "Binoculars"); + else if (ent->m_iClassID == CL_CLASS(CWeaponBugBait)) + AddEntityString(ent, "Bug Bait"); + else if (ent->m_iClassID == CL_CLASS(CWeaponCrossbow)) + AddEntityString(ent, "Crossbow"); + else if (ent->m_iClassID == CL_CLASS(CWeaponShotgun)) + AddEntityString(ent, "Shotgun"); + else if (ent->m_iClassID == CL_CLASS(CWeaponSMG1)) + AddEntityString(ent, "SMG"); + else if (ent->m_iClassID == CL_CLASS(CWeaponRPG)) + AddEntityString(ent, "RPG"); + if (string_count_backup != data[ent->m_IDX].string_count) + { + SetEntityColor(ent, colors::yellow); + } + } + } + } + + // Tank esp + if (ent->m_iClassID == CL_CLASS(CTFTankBoss) && tank) + { + AddEntityString(ent, "Tank"); + + // Dropped weapon esp + } + else if (ent->m_iClassID == CL_CLASS(CTFDroppedWeapon) && item_esp && + item_dropped_weapons) + { + AddEntityString( + ent, format("WEAPON ", RAW_ENT(ent)->GetClientClass()->GetName())); + + // MVM Money esp + } + else if (ent->m_iClassID == CL_CLASS(CCurrencyPack) && item_money) + { + if (CE_BYTE(ent, netvar.bDistributed)) + { + if (item_money_red) + { + AddEntityString(ent, "~$~"); + } } else - continue; + { + AddEntityString(ent, "$$$"); + } - // Save the ent to our array - potential_targets[potential_targets_length] = i; - potential_targets_length++; - - // With our maximum amount of players reached, dont search for anymore - if (potential_targets_length >= 32) - break; + // Other item esp } - - // Checks to prevent crashes - if (potential_targets_length == 0) - return false; - - // Get random number that we can use with our array - int target_random_num = - floor(RandFloatRange(0, potential_targets_length - 0.1F)); - - // Get a idx from our random array position - int new_target = potential_targets[target_random_num]; - - // Grab username of user - player_info_s info; - if (g_IEngine->GetPlayerInfo(new_target, &info)) + else if (ent->m_ItemType != ITEM_NONE && item_esp) { - // If our name is the same as current, than change it and return true - stolen_name = std::string(info.name); + // Health pack esp + if (item_health_packs && (ent->m_ItemType >= ITEM_HEALTH_SMALL && + ent->m_ItemType <= ITEM_HEALTH_LARGE || + ent->m_ItemType == ITEM_HL_BATTERY)) + { + if (ent->m_ItemType == ITEM_HEALTH_SMALL) + AddEntityString(ent, "[+]"); + if (ent->m_ItemType == ITEM_HEALTH_MEDIUM) + AddEntityString(ent, "[++]"); + if (ent->m_ItemType == ITEM_HEALTH_LARGE) + AddEntityString(ent, "[+++]"); + if (ent->m_ItemType == ITEM_HL_BATTERY) + AddEntityString(ent, "[Z]"); + + // TF2C Adrenaline esp + } + else if (item_adrenaline && ent->m_ItemType == ITEM_TF2C_PILL) + { + AddEntityString(ent, "[a]"); + + // Ammo pack esp + } + else if (item_ammo_packs && ent->m_ItemType >= ITEM_AMMO_SMALL && + ent->m_ItemType <= ITEM_AMMO_LARGE) + { + if (ent->m_ItemType == ITEM_AMMO_SMALL) + AddEntityString(ent, "{i}"); + if (ent->m_ItemType == ITEM_AMMO_MEDIUM) + AddEntityString(ent, "{ii}"); + if (ent->m_ItemType == ITEM_AMMO_LARGE) + AddEntityString(ent, "{iii}"); + + // Powerup esp + } + else if (item_powerups && ent->m_ItemType >= ITEM_POWERUP_FIRST && + ent->m_ItemType <= ITEM_POWERUP_LAST) + { + AddEntityString( + ent, format(powerups[ent->m_ItemType - ITEM_POWERUP_FIRST], + " PICKUP")); + + // TF2C weapon spawner esp + } + else if (item_weapon_spawners && ent->m_ItemType >= ITEM_TF2C_W_FIRST && + ent->m_ItemType <= ITEM_TF2C_W_LAST) + { + AddEntityString( + ent, + format(tf2c_weapon_names[ent->m_ItemType - ITEM_TF2C_W_FIRST], + " SPAWNER")); + if (CE_BYTE(ent, netvar.bRespawning)) + AddEntityString(ent, "-- RESPAWNING --"); + + // Halloween spell esp + } + else if (item_spellbooks && (ent->m_ItemType == ITEM_SPELL || + ent->m_ItemType == ITEM_SPELL_RARE)) + { + if (ent->m_ItemType == ITEM_SPELL) + { + AddEntityString(ent, "Spell", colors::green); + } + else + { + AddEntityString(ent, "Rare Spell", + colors::FromRGBA8(139, 31, 221, 255)); + } + } + + // Building esp + } + else if (ent->m_Type == ENTITY_BUILDING && buildings) + { + + // Check if enemy building + if (!ent->m_bEnemy && !teammates) + return; + + // TODO maybe... + /*if (legit && ent->m_iTeam != g_pLocalPlayer->team) { + if (ent->m_lLastSeen > v_iLegitSeenTicks->GetInt()) { + return; + } + }*/ + + // Make a name for the building based on the building type and level + if (show_name || show_class) + { + const std::string &name = + (ent->m_iClassID == CL_CLASS(CObjectTeleporter) + ? "Teleporter" + : (ent->m_iClassID == CL_CLASS(CObjectSentrygun) + ? "Sentry Gun" + : "Dispenser")); + int level = CE_INT(ent, netvar.iUpgradeLevel); + AddEntityString(ent, format("LV ", level, ' ', name)); + } + // If text health is true, then add a string with the health + if ((int) show_health == 1 || (int) show_health == 3) + { + AddEntityString( + ent, format(ent->m_iHealth, '/', ent->m_iMaxHealth, " HP"), + colors::Health(ent->m_iHealth, ent->m_iMaxHealth)); + } + // Set the entity to repaint + espdata.needs_paint = true; + return; + + // Player esp + } + else if (ent->m_Type == ENTITY_PLAYER && ent->m_bAlivePlayer) + { + + // Local player handling + if (!(local_esp && g_IInput->CAM_IsThirdPerson()) && + ent->m_IDX == g_IEngine->GetLocalPlayer()) + return; + + // Get player class + int pclass = CE_INT(ent, netvar.iClass); + + // Attempt to get player info, and if cant, return + player_info_s info; + if (!g_IEngine->GetPlayerInfo(ent->m_IDX, &info)) + return; + + // TODO, check if u can just use "ent->m_bEnemy" instead of m_iTeam + // Legit mode handling + if (legit && ent->m_iTeam != g_pLocalPlayer->team && + playerlist::IsDefault(info.friendsID)) + { + if (IsPlayerInvisible(ent)) + return; // Invis check + if (vischeck && !ent->IsVisible()) + return; // Vis check + // TODO, maybe... + // if (ent->m_lLastSeen > + // (unsigned)v_iLegitSeenTicks->GetInt()) + // return; + } + + // Powerup handling + if (powerup_esp) + { + powerup_type power = GetPowerupOnPlayer(ent); + if (power != not_powerup) + AddEntityString(ent, format("^ ", powerups[power], " ^")); + } + + // Dont understand reasoning for this check + if (ent->m_bEnemy || teammates || + !playerlist::IsDefault(info.friendsID)) + { + + // Playername + if (show_name) + AddEntityString(ent, std::string(info.name)); + + // Player class + if (show_class) + { + if (pclass > 0 && pclass < 10) + AddEntityString(ent, classes[pclass - 1]); + } + +#if ENABLE_IPC == 1 + // ipc bot esp + if (show_bot_id && ipc::peer && ent != LOCAL_E) + { + for (unsigned i = 0; i < cat_ipc::max_peers; i++) + { + if (!ipc::peer->memory->peer_data[i].free && + ipc::peer->memory->peer_user_data[i].friendid == + info.friendsID) + { + AddEntityString(ent, format("BOT #", i)); + break; + } + } + } +#endif + // Health esp + if ((int) show_health == 1 || (int) show_health == 3) + { + AddEntityString( + ent, format(ent->m_iHealth, '/', ent->m_iMaxHealth, " HP"), + colors::Health(ent->m_iHealth, ent->m_iMaxHealth)); + } + IF_GAME(IsTF()) + { + // Medigun Ubercharge esp + if (show_ubercharge) + { + if (CE_INT(ent, netvar.iClass) == tf_medic) + { + int *weapon_list = (int *) ((unsigned) (RAW_ENT(ent)) + + netvar.hMyWeapons); + for (int i = 0; weapon_list[i]; i++) + { + int handle = weapon_list[i]; + int eid = handle & 0xFFF; + if (eid >= 32 && eid <= HIGHEST_ENTITY) + { + CachedEntity *weapon = ENTITY(eid); + if (!CE_BAD(weapon) && + weapon->m_iClassID == + CL_CLASS(CWeaponMedigun) && + weapon) + { + if (CE_INT(weapon, + netvar.iItemDefinitionIndex) != + 998) + { + AddEntityString( + ent, + format( + floor( + CE_FLOAT( + weapon, + netvar + .m_flChargeLevel) * + 100), + '%', " Uber"), + colors::Health( + (CE_FLOAT( + weapon, + netvar.m_flChargeLevel) * + 100), + 100)); + } + else + AddEntityString( + ent, + format( + floor( + CE_FLOAT( + weapon, + netvar + .m_flChargeLevel) * + 100), + '%', " Uber | Charges: ", + floor( + CE_FLOAT( + weapon, + netvar + .m_flChargeLevel) / + 0.25f)), + colors::Health( + (CE_FLOAT( + weapon, + netvar.m_flChargeLevel) * + 100), + 100)); + break; + } + } + } + } + } + // Conditions esp + if (show_conditions) + { + const auto &clr = colors::EntityF(ent); + // Invis + if (IsPlayerInvisible(ent)) + AddEntityString( + ent, "*CLOAKED*", + colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); + // Uber/Bonk + if (IsPlayerInvulnerable(ent)) + AddEntityString(ent, "*INVULNERABLE*"); + // Vaccinator + if (HasCondition(ent)) + { + AddEntityString(ent, "*VACCINATOR*"); + } + else if (HasCondition(ent)) + { + AddEntityString(ent, "*PASSIVE RESIST*"); + } + // Crit + if (IsPlayerCritBoosted(ent)) + AddEntityString(ent, "*CRITS*", colors::orange); + // Zoomed + if (HasCondition(ent)) + { + AddEntityString( + ent, "*ZOOMING*", + colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); + // Slowed + } + else if (HasCondition(ent)) + { + AddEntityString( + ent, "*SLOWED*", + colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); + } + // Jarated + if (HasCondition(ent)) + AddEntityString(ent, "*JARATED*", colors::yellow); + } + } + // Hoovy Esp + if (IsHoovy(ent)) + AddEntityString(ent, "Hoovy"); + + // Active weapon esp + int widx = CE_INT(ent, netvar.hActiveWeapon) & 0xFFF; + if (IDX_GOOD(widx)) + { + CachedEntity *weapon = ENTITY(widx); + if (CE_GOOD(weapon)) + { + if (show_weapon) + { + const char *weapon_name = + re::C_BaseCombatWeapon::GetPrintName( + RAW_ENT(weapon)); + if (weapon_name) + AddEntityString(ent, std::string(weapon_name)); + } + } + } + + // Notify esp to repaint + espdata.needs_paint = true; + } + return; + } +} + +// Draw a box around a player +void _FASTCALL DrawBox(CachedEntity *ent, const rgba_t &clr) +{ + PROF_SECTION(PT_esp_drawbox); + + // Check if ent is bad to prevent crashes + if (CE_BAD(ent)) + return; + + // Get our collidable bounds + if (!GetCollide(ent)) + return; + + // Pull the cached collide info + ESPData &ent_data = data[ent->m_IDX]; + int max_x = ent_data.collide_max.x; + int max_y = ent_data.collide_max.y; + int min_x = ent_data.collide_min.x; + int min_y = ent_data.collide_min.y; + + // Depending on whether the player is cloaked, we change the color + // acordingly + rgba_t border = ((ent->m_iClassID == RCC_PLAYER) && IsPlayerInvisible(ent)) + ? colors::FromRGBA8(160, 160, 160, clr.a * 255.0f) + : colors::Transparent(colors::black, clr.a); + + // With box corners, we draw differently + if ((int) box_esp == 2) + BoxCorners(min_x, min_y, max_x, max_y, clr, (clr.a != 1.0f)); + // Otherwise, we just do simple draw funcs + else + { + draw_api::draw_rect_outlined(min_x, min_y, max_x - min_x, max_y - min_y, + border, 0.5f); + draw_api::draw_rect_outlined(min_x + 1, min_y + 1, max_x - min_x - 2, + max_y - min_y - 2, clr, 0.5f); + draw_api::draw_rect_outlined(min_x + 2, min_y + 2, max_x - min_x - 4, + max_y - min_y - 4, border, 0.5f); + } +} + +// Function to draw box corners, Used by DrawBox +void BoxCorners(int minx, int miny, int maxx, int maxy, const rgba_t &color, + bool transparent) +{ + const rgba_t &black = + transparent ? colors::Transparent(colors::black) : colors::black; + const int size = box_corner_size; + + // Black corners + // Top Left + draw_api::draw_rect(minx, miny, size, 3, black); + draw_api::draw_rect(minx, miny + 3, 3, size - 3, black); + // Top Right + draw_api::draw_rect(maxx - size + 1, miny, size, 3, black); + draw_api::draw_rect(maxx - 3 + 1, miny + 3, 3, size - 3, black); + // Bottom Left + draw_api::draw_rect(minx, maxy - 3, size, 3, black); + draw_api::draw_rect(minx, maxy - size, 3, size - 3, black); + // Bottom Right + draw_api::draw_rect(maxx - size + 1, maxy - 3, size, 3, black); + draw_api::draw_rect(maxx - 2, maxy - size, 3, size - 3, black); + + // Colored corners + // Top Left + draw_api::draw_line(minx + 1, miny + 1, size - 2, 0, color, 0.5f); + draw_api::draw_line(minx + 1, miny + 1, 0, size - 2, color, 0.5f); + // Top Right + draw_api::draw_line(maxx - 1, miny + 1, -(size - 2), 0, color, 0.5f); + draw_api::draw_line(maxx - 1, miny + 1, 0, size - 2, color, 0.5f); + // Bottom Left + draw_api::draw_line(minx + 1, maxy - 2, size - 2, 0, color, 0.5f); + draw_api::draw_line(minx + 1, maxy - 2, 0, -(size - 2), color, 0.5f); + // Bottom Right + draw_api::draw_line(maxx - 1, maxy - 2, -(size - 2), 0, color, 0.5f); + draw_api::draw_line(maxx - 1, maxy - 2, 0, -(size - 2), color, 0.5f); +} + +// Used for caching collidable bounds +bool GetCollide(CachedEntity *ent) +{ + PROF_SECTION(PT_esp_getcollide); + + // Null + Dormant check to prevent crashing + if (CE_BAD(ent)) + return false; + + // Grab esp data + ESPData &ent_data = data[ent->m_IDX]; + + // If entity has cached collides, return it. Otherwise generate new bounds + if (!ent_data.has_collide) + { + + // Get collision center, max, and mins + const Vector &origin = + RAW_ENT(ent)->GetCollideable()->GetCollisionOrigin(); + Vector mins = RAW_ENT(ent)->GetCollideable()->OBBMins() + origin; + Vector maxs = RAW_ENT(ent)->GetCollideable()->OBBMaxs() + origin; + + // Create a array for storing box points + Vector points_r[8]; // World vectors + Vector points[8]; // Screen vectors + + // If user setting for box expnad is true, spread the max and mins + if (esp_expand) + { + const float &exp = (float) esp_expand; + maxs.x += exp; + maxs.y += exp; + maxs.z += exp; + mins.x -= exp; + mins.y -= exp; + mins.z -= exp; + } + + // Create points for the box based on max and mins + float x = maxs.x - mins.x; + float y = maxs.y - mins.y; + float z = maxs.z - mins.z; + points_r[0] = mins; + points_r[1] = mins + Vector(x, 0, 0); + points_r[2] = mins + Vector(x, y, 0); + points_r[3] = mins + Vector(0, y, 0); + points_r[4] = mins + Vector(0, 0, z); + points_r[5] = mins + Vector(x, 0, z); + points_r[6] = mins + Vector(x, y, z); + points_r[7] = mins + Vector(0, y, z); + + // Check if any point of the box isnt on the screen + bool success = true; + for (int i = 0; i < 8; i++) + { + if (!draw::WorldToScreen(points_r[i], points[i])) + success = false; + } + // If a point isnt on the screen, return here + if (!success) + return false; + + // Get max and min of the box using the newly created screen vector + int max_x = -1; + int max_y = -1; + int min_x = 65536; + int min_y = 65536; + for (int i = 0; i < 8; i++) + { + if (points[i].x > max_x) + max_x = points[i].x; + if (points[i].y > max_y) + max_y = points[i].y; + if (points[i].x < min_x) + min_x = points[i].x; + if (points[i].y < min_y) + min_y = points[i].y; + } + + // Save the info to the esp data and notify cached that we cached info. + ent_data.collide_max = Vector(max_x, max_y, 0); + ent_data.collide_min = Vector(min_x, min_y, 0); + ent_data.has_collide = true; + return true; } - - // Didnt get playerinfo + else + { + // We already have collidable so return true. + return true; + } + // Impossible error, return false return false; } -static CatVar ipc_name(CV_STRING, "name_ipc", "", "IPC Name"); - -const char *GetFriendPersonaName_hook(ISteamFriends *_this, CSteamID steamID) +// Use to add a esp string to an entity +void AddEntityString(CachedEntity *entity, const std::string &string, + const rgba_t &color) { - static const GetFriendPersonaName_t original = - (GetFriendPersonaName_t) hooks::steamfriends.GetMethod( - offsets::GetFriendPersonaName()); - -#if ENABLE_IPC - if (ipc::peer) - { - static std::string namestr(ipc_name.GetString()); - namestr.assign(ipc_name.GetString()); - if (namestr.length() > 3) - { - ReplaceString(namestr, "%%", std::to_string(ipc::peer->client_id)); - return namestr.c_str(); - } - } -#endif - - // Check User settings if namesteal is allowed - if (namesteal && steamID == g_ISteamUser->GetSteamID()) - { - - // We dont want to steal names while not in-game as there are no targets - // to steal from. We want to be on a team as well to get teammates names - if (g_IEngine->IsInGame() && g_pLocalPlayer->team) - { - - // Check if we have a username to steal, func automaticly steals a - // name in it. - if (StolenName()) - { - - // Return the name that has changed from the func above - return format(stolen_name, "\x0F").c_str(); - } - } - } - - if ((strlen(force_name.GetString()) > 1) && - steamID == g_ISteamUser->GetSteamID()) - { - - return force_name_newlined; - } - return original(_this, steamID); + ESPData &entity_data = data[entity->m_IDX]; + if (entity_data.string_count >= 15) + return; + entity_data.strings[entity_data.string_count].data = string; + entity_data.strings[entity_data.string_count].color = color; + entity_data.string_count++; + entity_data.needs_paint = true; } -void FireGameEvent_hook(void *_this, IGameEvent *event) +// Function to reset entitys strings +void ResetEntityStrings() { - static const FireGameEvent_t original = - (FireGameEvent_t) hooks::clientmode4.GetMethod( - offsets::FireGameEvent()); - const char *name = event->GetName(); - if (name) + for (auto &i : data) { - if (event_log) - { - if (!strcmp(name, "player_connect_client") || - !strcmp(name, "player_disconnect") || - !strcmp(name, "player_team")) - { - return; - } - } - // hacks::tf2::killstreak::fire_event(event); + i.string_count = 0; + i.color = colors::empty; + i.needs_paint = false; } - original(_this, event); } -#if ENABLE_VISUALS == 1 -void FrameStageNotify_hook(void *_this, int stage) +// Sets an entitys esp color +void SetEntityColor(CachedEntity *entity, const rgba_t &color) { - static IClientEntity *ent; - - PROF_SECTION(FrameStageNotify_TOTAL); - - static const FrameStageNotify_t original = - (FrameStageNotify_t) hooks::client.GetMethod( - offsets::FrameStageNotify()); - - if (!g_IEngine->IsInGame()) - g_Settings.bInvalid = true; - { - PROF_SECTION(FSN_skinchanger); - hacks::tf2::skinchanger::FrameStageNotify(stage); - } - if (resolver && cathook && !g_Settings.bInvalid && - stage == FRAME_NET_UPDATE_POSTDATAUPDATE_START) - { - PROF_SECTION(FSN_resolver); - for (int i = 1; i < 32 && i < HIGHEST_ENTITY; i++) - { - if (i == g_IEngine->GetLocalPlayer()) - continue; - ent = g_IEntityList->GetClientEntity(i); - if (ent && !ent->IsDormant() && !NET_BYTE(ent, netvar.iLifeState)) - { - Vector &angles = NET_VECTOR(ent, netvar.m_angEyeAngles); - if (angles.x >= 90) - angles.x = -89; - if (angles.x <= -90) - angles.x = 89; - angles.y = fmod(angles.y + 180.0f, 360.0f); - if (angles.y < 0) - angles.y += 360.0f; - angles.y -= 180.0f; - } - } - } - if (cathook && !g_Settings.bInvalid && stage == FRAME_RENDER_START) - { - IF_GAME(IsTF()) - { - if (CE_GOOD(LOCAL_E) && no_zoom) - RemoveCondition(LOCAL_E); - } - if (force_thirdperson && !g_pLocalPlayer->life_state && - CE_GOOD(g_pLocalPlayer->entity)) - { - CE_INT(g_pLocalPlayer->entity, netvar.nForceTauntCam) = 1; - } - if (stage == 5 && show_antiaim && g_IInput->CAM_IsThirdPerson()) - { - if (CE_GOOD(g_pLocalPlayer->entity)) - { - CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 4) = - g_Settings.last_angles.x; - CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 8) = - g_Settings.last_angles.y; - } - } - } - original(_this, stage); + data[entity->m_IDX].color = color; } -#endif /* TEXTMODE */ - -static CatVar clean_chat(CV_SWITCH, "clean_chat", "0", "Clean chat", - "Removes newlines from chat"); -static CatVar dispatch_log(CV_SWITCH, "debug_log_usermessages", "0", - "Log dispatched user messages"); -std::string clear = ""; -static bool firstcall = true; -bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) -{ - int loop_index, s, i, j; - char *data, c; - - static const DispatchUserMessage_t original = - (DispatchUserMessage_t) hooks::client.GetMethod( - offsets::DispatchUserMessage()); - if (type == 4) - { - loop_index = 0; - s = buf.GetNumBytesLeft(); - if (s < 256) - { - data = (char *) alloca(s); - for (i = 0; i < s; i++) - data[i] = buf.ReadByte(); - j = 0; - std::string name; - std::string message; - for (i = 0; i < 3; i++) - { - while ((c = data[j++]) && (loop_index < 128)) - { - loop_index++; - if (clean_chat) - if ((c == '\n' || c == '\r') && (i == 1 || i == 2)) - data[j - 1] = '*'; - if (i == 1) - name.push_back(c); - if (i == 2) - message.push_back(c); - } - } - static const char *lastfilter; - static const char *lastname; - static bool retrun = false; - if (data[0] != LOCAL_E->m_IDX) { - if (retrun) { - PrintChat("\x07%06X%s\x01: \x07%06X%s\x01", 0xe05938, lastname, - 0xefec1f, lastfilter); - retrun = false; - } - } - if (chat_filter_enabled && data[0] != LOCAL_E->m_IDX) - { - if (!strcmp(chat_filter.GetString(), "")) - { - std::string tmp = {}; - std::string tmp2 = {}; - int iii = 0; - player_info_s info; - g_IEngine->GetPlayerInfo(LOCAL_E->m_IDX, &info); - std::string name1 = info.name; - std::vector name2{}; - std::vector name3{}; - std::string claz = {}; - switch (g_pLocalPlayer->clazz) - { - case tf_scout: - claz = "scout"; - break; - case tf_soldier: - claz = "soldier"; - break; - case tf_pyro: - claz = "pyro"; - break; - case tf_demoman: - claz = "demo"; - break; - case tf_engineer: - claz = "engi"; - break; - case tf_heavy: - claz = "heavy"; - break; - case tf_medic: - claz = "med"; - break; - case tf_sniper: - claz = "sniper"; - break; - case tf_spy: - claz = "spy"; - break; - default: - break; - } - for (char i : name1) - { - if (iii == 2) - { - iii = 0; - tmp += i; - name2.push_back(tmp); - tmp = ""; - } - else if (iii < 2) - { - iii++; - tmp += i; - } - } - iii = 0; - for (char i : name1) - { - if (iii == 3) - { - iii = 0; - tmp += i; - name3.push_back(tmp2); - tmp2 = ""; - } - else if (iii < 3) - { - iii++; - tmp2 += i; - } - } - if (tmp.size() > 2) - name2.push_back(tmp); - if (tmp2.size() > 2) - name3.push_back(tmp2); - iii = 0; - std::vector res = { - "skid", "script", "cheat", "hak", "hac", "f1", - "hax", "vac", "ban", "lmao", "bot", "report", - "cat", "insta", "revv", "brass", "kick", claz - }; - for (auto i : name2) - { - boost::to_lower(i); - res.push_back(i); - } - for (auto i : name3) - { - boost::to_lower(i); - res.push_back(i); - } - std::string message2 = message; - boost::to_lower(message2); - boost::replace_all(message2, "4", "a"); - boost::replace_all(message2, "3", "e"); - boost::replace_all(message2, "0", "o"); - boost::replace_all(message2, "6", "g"); - boost::replace_all(message2, "5", "s"); - boost::replace_all(message2, "7", "t"); - for (auto filter : res) - { - if (retrun) - break; - if (boost::contains(message2, filter)) - { - - if (clear == "") - { - for (int i = 0; i < 100; i++) - clear += "\n"; - } - chat_stack::Say(". " + clear, true); - retrun = true; - lastfilter = filter.c_str(); - lastname = name.c_str(); - } - } - } - else if (data[0] != LOCAL_E->m_IDX) - { - std::string input = chat_filter.GetString(); - boost::to_lower(input); - std::string message2 = message; - std::vector result{}; - boost::split(result, input, boost::is_any_of(",")); - boost::replace_all(message2, "4", "a"); - boost::replace_all(message2, "3", "e"); - boost::replace_all(message2, "0", "o"); - boost::replace_all(message2, "6", "g"); - boost::replace_all(message2, "5", "s"); - boost::replace_all(message2, "7", "t"); - for (auto filter : result) - { - if (retrun) - break; - if (boost::contains(message2, filter)) - { - if (clear == "") - { - clear = ""; - for (int i = 0; i < 100; i++) - clear += "\n"; - } - chat_stack::Say(". " + clear, true); - retrun = true; - lastfilter = filter.c_str(); - lastname = name.c_str(); - } - } - } - } - if (crypt_chat) - { - if (firstcall) - chat_stack::Say("!!meow", false); - firstcall = false; - if (message.find("!!") == 0) - { - if (ucccccp::validate(message)) - { - CachedEntity* entity = ENTITY(data[0]); - if (CE_GOOD(entity)) { - if (boost::algorithm::contains(ucccccp::decrypt(message), "meow")) { - player_info_s info; - g_IEngine->GetPlayerInfo(data[0], &info); - unsigned steamid = info.friendsID; - playerlist::AccessData(steamid).state = playerlist::k_EState::CAT; - } - } - PrintChat("\x07%06X%s\x01: %s", 0xe05938, name.c_str(), - ucccccp::decrypt(message).c_str()); - } - } - } - chatlog::LogMessage(data[0], message); - buf = bf_read(data, s); - buf.Seek(0); - } - } - if (dispatch_log) - { - logging::Info("D> %i", type); - std::ostringstream str{}; - while (buf.GetNumBytesLeft()) - { - unsigned char byte = buf.ReadByte(); - str << std::hex << std::setw(2) << std::setfill('0') - << static_cast(byte) << ' '; - } - logging::Info("MESSAGE %d, DATA = [ %s ]", type, str.str().c_str()); - buf.Seek(0); - } - votelogger::user_message(buf, type); - return original(_this, type, buf); } - -void LevelInit_hook(void *_this, const char *newmap) -{ - static const LevelInit_t original = - (LevelInit_t) hooks::clientmode.GetMethod(offsets::LevelInit()); - playerlist::Save(); - g_IEngine->ClientCmd_Unrestricted("exec cat_matchexec"); - hacks::shared::aimbot::Reset(); - chat_stack::Reset(); - hacks::shared::anticheat::ResetEverything(); - original(_this, newmap); - hacks::shared::walkbot::OnLevelInit(); -#if ENABLE_IPC - if (ipc::peer) - { - ipc::peer->memory->peer_user_data[ipc::peer->client_id].ts_connected = - time(nullptr); - } -#endif } - -void LevelShutdown_hook(void *_this) -{ - static const LevelShutdown_t original = - LevelShutdown_t(hooks::clientmode.GetMethod(offsets::LevelShutdown())); - need_name_change = true; - playerlist::Save(); - g_Settings.bInvalid = true; - hacks::shared::aimbot::Reset(); - chat_stack::Reset(); - hacks::shared::anticheat::ResetEverything(); - original(_this); -#if ENABLE_IPC - if (ipc::peer) - { - ipc::peer->memory->peer_user_data[ipc::peer->client_id] - .ts_disconnected = time(nullptr); - } -#endif -} - -int RandomInt_hook(void *_this, int iMinVal, int iMaxVal) -{ - static const RandomInt_t original = - RandomInt_t(hooks::vstd.GetMethod(offsets::RandomInt())); - - if (medal_flip && iMinVal == 0 && iMaxVal == 9) return 0; - - return original(_this, iMinVal, iMaxVal); } From 443a38c8cde443f612d37ff0966784967600674c Mon Sep 17 00:00:00 2001 From: BenCat07 Date: Sat, 3 Mar 2018 13:04:56 +0100 Subject: [PATCH 12/12] ungay --- src/hooks/others.cpp | 2246 ++++++++++++++++-------------------------- 1 file changed, 864 insertions(+), 1382 deletions(-) diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 0dca84bc..825666fa 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -1,1482 +1,964 @@ /* - * HEsp.cpp + * others.cpp * - * Created on: Oct 6, 2016 + * Created on: Jan 8, 2017 * Author: nullifiedcat */ #include "common.hpp" +#include "ucccccp/ucccccp.hpp" +#include "hack.hpp" +#include "hitrate.hpp" +#include "chatlog.hpp" +#include "netmessage.hpp" +#include -namespace hacks +#if ENABLE_VISUALS == 1 + +static CatVar no_invisibility(CV_SWITCH, "no_invis", "0", "Remove Invisibility", + "Useful with chams!"); +static CatVar medal_flip(CV_SWITCH, "medal_flip", "0", "Infinite Medal Flip", + ""); + +// This hook isn't used yet! +int C_TFPlayer__DrawModel_hook(IClientEntity *_this, int flags) { -namespace shared -{ -namespace esp -{ - -// Main Switch -CatVar enabled(CV_SWITCH, "esp_enabled", "0", "ESP", "Master ESP switch"); -// Box esp + Options -CatEnum box_esp_enum({ "None", "Normal", "Corners" }); -CatVar box_esp(box_esp_enum, "esp_box", "2", "Box", "Draw a 2D box"); -CatVar box_corner_size(CV_INT, "esp_box_corner_size", "10", "Corner Size"); -// Tracers -CatEnum tracers_enum({ "OFF", "CENTER", "BOTTOM" }); -CatVar tracers(tracers_enum, "esp_tracers", "0", "Tracers", - "SDraws a line from the player to a position on your screen"); -// Emoji Esp -#ifndef FEATURE_EMOJI_ESP_DISABLED -CatEnum emoji_esp_enum({ "None", "Joy", "Thinking" }); -CatVar emoji_esp(emoji_esp_enum, "esp_emoji", "0", "Emoji ESP", - "Draw emoji on peopels head"); -CatVar emoji_esp_size(CV_FLOAT, "esp_emoji_size", "32", "Emoji ESP Size"); -CatVar emoji_esp_scaling(CV_SWITCH, "esp_emoji_scaling", "1", - "Emoji ESP Scaling"); -CatVar emoji_min_size(CV_INT, "esp_emoji_min_size", "20", "Emoji ESP min size", - "Minimum size for an emoji when you use auto scaling"); -#endif -// Other esp options -CatEnum show_health_enum({ "None", "Text", "Healthbar", "Both" }); -CatVar show_health(show_health_enum, "esp_health", "3", "Health ESP", - "Show enemy health"); -CatVar draw_bones(CV_SWITCH, "esp_bones", "0", "Draw Bones"); -CatEnum sightlines_enum({ "None", "Sniper Only", - "All" }); // I ripped of lbox's choices cuz its nice -CatVar sightlines(sightlines_enum, "esp_sightlines", "0", "Show sightlines", - "Displays a line of where players are looking"); -CatEnum esp_text_position_enum({ "TOP RIGHT", "BOTTOM RIGHT", "CENTER", "ABOVE", - "BELOW" }); -CatVar esp_text_position(esp_text_position_enum, "esp_text_position", "0", - "Text position", "Defines text position"); -CatVar esp_expand( - CV_INT, "esp_expand", "0", "Expand Esp", - "Spreads out Box, health bar, and text from center"); // Note, check if this - // should be int, it - // is being used by - // casting as float -CatVar vischeck(CV_SWITCH, "esp_vischeck", "1", "VisCheck", - "ESP visibility check - makes enemy info behind walls darker, " - "disable this if you get FPS drops"); -CatVar legit(CV_SWITCH, "esp_legit", "0", "Legit Mode", - "Don't show invisible enemies\nHides invisable enemies with " - "visibility enabled"); -// Selective esp options -CatVar local_esp(CV_SWITCH, "esp_local", "1", "ESP Local Player", - "Shows local player ESP in thirdperson"); -CatVar buildings(CV_SWITCH, "esp_buildings", "1", "Building ESP", - "Show buildings"); -CatVar teammates(CV_SWITCH, "esp_teammates", "0", "ESP Teammates", - "Teammate ESP"); -CatVar tank(CV_SWITCH, "esp_show_tank", "1", "Show tank", "Show tanks in mvm"); -// Text Esps -CatVar show_weapon(CV_SWITCH, "esp_weapon", "0", "Show weapon name", - "Show which weapon the enemy is using"); -CatVar show_distance(CV_SWITCH, "esp_distance", "1", "Distance ESP", - "Show distance to target"); -CatVar show_name(CV_SWITCH, "esp_name", "1", "Name ESP", "Show name"); -CatVar show_class(CV_SWITCH, "esp_class", "1", "Class ESP", "Show class"); -CatVar show_conditions(CV_SWITCH, "esp_conds", "1", "Conditions ESP", - "Show conditions"); -CatVar - show_ubercharge(CV_SWITCH, "esp_ubercharge", "1", "Ubercharge ESP", - "Show ubercharge percentage while players medigun is out"); -CatVar show_bot_id(CV_SWITCH, "esp_followbot_id", "1", "Followbot ESP", - "Show followbot ID"); -CatVar powerup_esp(CV_SWITCH, "esp_powerups", "1", "Powerup ESP", - "Shows powerups a player is using"); -// Item esp -CatVar item_esp(CV_SWITCH, "esp_item", "1", "Item ESP", - "Master Item ESP switch (health packs, etc.)"); -CatVar item_dropped_weapons(CV_SWITCH, "esp_item_weapons", "0", - "Dropped weapons", "Show dropped weapons"); -CatVar item_ammo_packs(CV_SWITCH, "esp_item_ammo", "0", "Ammo packs", - "Show ammo packs"); -CatVar item_health_packs(CV_SWITCH, "esp_item_health", "1", "Health packs", - "Show health packs"); -CatVar item_powerups(CV_SWITCH, "esp_item_powerups", "1", "Powerups", - "Shows powerups in the world"); -CatVar item_money(CV_SWITCH, "esp_money", "1", "MvM money", "Show MvM money"); -CatVar item_money_red(CV_SWITCH, "esp_money_red", "1", "Red MvM money", - "Show red MvM money"); -CatVar item_spellbooks(CV_SWITCH, "esp_spellbooks", "1", "Spellbooks", - "Spell Books"); -CatVar item_weapon_spawners(CV_SWITCH, "esp_weapon_spawners", "1", - "Show weapon spawners", - "TF2C deathmatch weapon spawners"); -CatVar item_adrenaline(CV_SWITCH, "esp_item_adrenaline", "0", "Show Adrenaline", - "TF2C adrenaline pills"); -// Projectile esp -CatVar proj_esp(CV_SWITCH, "esp_proj", "1", "Projectile ESP", "Projectile ESP"); -CatEnum proj_esp_enum({ "OFF", "ALL", "CRIT" }); -CatVar proj_rockets(proj_esp_enum, "esp_proj_rockets", "1", "Rockets", - "Rockets"); -CatVar proj_arrows(proj_esp_enum, "esp_proj_arrows", "1", "Arrows", "Arrows"); -CatVar proj_pipes(proj_esp_enum, "esp_proj_pipes", "1", "Pipes", "Pipebombs"); -CatVar proj_stickies(proj_esp_enum, "esp_proj_stickies", "1", "Stickies", - "Stickybombs"); -CatVar proj_enemy(CV_SWITCH, "esp_proj_enemy", "1", "Only enemy projectiles", - "Don't show friendly projectiles"); -// Debug -CatVar entity_info(CV_SWITCH, "esp_entity", "0", "Entity ESP", - "Show entity info (debug)"); -CatVar entity_model(CV_SWITCH, "esp_model_name", "0", "Model name ESP", - "Model name esp (DEBUG ONLY)"); -CatVar entity_id(CV_SWITCH, "esp_entity_id", "1", "Entity ID", - "Used with Entity ESP. Shows entityID"); - -// CatVar draw_hitbox(CV_SWITCH, "esp_hitbox", "1", "Draw Hitbox"); - -// Unknown -std::mutex threadsafe_mutex; -// Storage array for keeping strings and other data -std::array data; - -// Storage vars for entities that need to be re-drawn -std::vector entities_need_repaint{}; -std::mutex entities_need_repaint_mutex{}; - -// :b:one stuff needs to be up here as puting it in the header for sorting would -// be a pain. - -// Vars to store what bones connect to what -const std::string bonenames_leg_r[] = { "bip_foot_R", "bip_knee_R", - "bip_hip_R" }; -const std::string bonenames_leg_l[] = { "bip_foot_L", "bip_knee_L", - "bip_hip_L" }; -const std::string bonenames_bottom[] = { "bip_hip_R", "bip_pelvis", - "bip_hip_L" }; -const std::string bonenames_spine[] = { "bip_pelvis", "bip_spine_0", - "bip_spine_1", "bip_spine_2", - "bip_spine_3", "bip_neck", - "bip_head" }; -const std::string bonenames_arm_r[] = { "bip_upperArm_R", "bip_lowerArm_R", - "bip_hand_R" }; -const std::string bonenames_arm_l[] = { "bip_upperArm_L", "bip_lowerArm_L", - "bip_hand_L" }; -const std::string bonenames_up[] = { "bip_upperArm_R", "bip_spine_3", - "bip_upperArm_L" }; - -// Dont fully understand struct but a guess is a group of something. -// I will return once I have enough knowlage to reverse this. -// NOTE: No idea on why we cant just use gethitbox and use the displacement on -// that insted of having all this extra code. Shouldnt gethitbox use cached -// hitboxes, if so it should be nicer on performance -struct bonelist_s -{ - bool setup{ false }; - bool success{ false }; - int leg_r[3]{ 0 }; - int leg_l[3]{ 0 }; - int bottom[3]{ 0 }; - int spine[7]{ 0 }; - int arm_r[3]{ 0 }; - int arm_l[3]{ 0 }; - int up[3]{ 0 }; - - void Setup(const studiohdr_t *hdr) + float old_invis = *(float *) ((uintptr_t) _this + 79u); + if (no_invisibility) { - if (!hdr) + if (old_invis < 1.0f) { - setup = true; - return; + *(float *) ((uintptr_t) _this + 79u) = 0.5f; } - std::unordered_map bones{}; - for (int i = 0; i < hdr->numbones; i++) - { - bones[std::string(hdr->pBone(i)->pszName())] = i; - } - try - { - for (int i = 0; i < 3; i++) - leg_r[i] = bones.at(bonenames_leg_r[i]); - for (int i = 0; i < 3; i++) - leg_l[i] = bones.at(bonenames_leg_l[i]); - for (int i = 0; i < 3; i++) - bottom[i] = bones.at(bonenames_bottom[i]); - for (int i = 0; i < 7; i++) - spine[i] = bones.at(bonenames_spine[i]); - for (int i = 0; i < 3; i++) - arm_r[i] = bones.at(bonenames_arm_r[i]); - for (int i = 0; i < 3; i++) - arm_l[i] = bones.at(bonenames_arm_l[i]); - for (int i = 0; i < 3; i++) - up[i] = bones.at(bonenames_up[i]); - success = true; - } - catch (std::exception &ex) - { - logging::Info("Bone list exception: %s", ex.what()); - } - setup = true; } - void DrawBoneList(const matrix3x4_t *bones, int *in, int size, - const rgba_t &color, const Vector &displacement) + *(float *) ((uintptr_t) _this + 79u) = old_invis; +} + +static CatVar no_arms(CV_SWITCH, "no_arms", "0", "No Arms", + "Removes arms from first person"); +static CatVar no_hats(CV_SWITCH, "no_hats", "0", "No Hats", + "Removes non-stock hats"); +float last_say = 0.0f; +void DrawModelExecute_hook(IVModelRender *_this, const DrawModelState_t &state, + const ModelRenderInfo_t &info, matrix3x4_t *matrix) +{ + static const DrawModelExecute_t original = + (DrawModelExecute_t) hooks::modelrender.GetMethod( + offsets::DrawModelExecute()); + static const char *name; + static std::string sname; + static IClientUnknown *unk; + static IClientEntity *ent; + + if (!cathook || + !(spectator_target || no_arms || no_hats || + (clean_screenshots && g_IEngine->IsTakingScreenshot()))) { - Vector last_screen; - Vector current_screen; - for (int i = 0; i < size; i++) + original(_this, state, info, matrix); + return; + } + + PROF_SECTION(DrawModelExecute); + + if (no_arms || no_hats) + { + if (info.pModel) { - Vector position(bones[in[i]][0][3], bones[in[i]][1][3], - bones[in[i]][2][3]); - position += displacement; - if (!draw::WorldToScreen(position, current_screen)) + name = g_IModelInfo->GetModelName(info.pModel); + if (name) + { + sname = name; + if (no_arms && sname.find("arms") != std::string::npos) + { + return; + } + else if (no_hats && + sname.find("player/items") != std::string::npos) + { + return; + } + } + } + } + + unk = info.pRenderable->GetIClientUnknown(); + if (unk) + { + ent = unk->GetIClientEntity(); + if (ent) + { + if (ent->entindex() == spectator_target) { return; } - if (i > 0) - { - draw_api::draw_line(last_screen.x, last_screen.y, - current_screen.x - last_screen.x, - current_screen.y - last_screen.y, color, - 0.5f); - } - last_screen = current_screen; } - } - - void Draw(CachedEntity *ent, const rgba_t &color) - { - const model_t *model = RAW_ENT(ent)->GetModel(); - if (not model) + if (ent && !effect_chams::g_EffectChams.drawing && + effect_chams::g_EffectChams.ShouldRenderChams(ent)) { return; } - - studiohdr_t *hdr = g_IModelInfo->GetStudiomodel(model); - - if (!setup) - { - Setup(hdr); - } - if (!success) - return; - - // ent->m_bBonesSetup = false; - Vector displacement = RAW_ENT(ent)->GetAbsOrigin() - ent->m_vecOrigin; - const auto &bones = ent->hitboxes.GetBones(); - DrawBoneList(bones, leg_r, 3, color, displacement); - DrawBoneList(bones, leg_l, 3, color, displacement); - DrawBoneList(bones, bottom, 3, color, displacement); - DrawBoneList(bones, spine, 7, color, displacement); - DrawBoneList(bones, arm_r, 3, color, displacement); - DrawBoneList(bones, arm_l, 3, color, displacement); - DrawBoneList(bones, up, 3, color, displacement); - /*for (int i = 0; i < hdr->numbones; i++) { - const auto& bone = ent->GetBones()[i]; - Vector pos(bone[0][3], bone[1][3], bone[2][3]); - //pos += orig; - Vector screen; - if (draw::WorldToScreen(pos, screen)) { - if (hdr->pBone(i)->pszName()) { - draw::FString(fonts::ESP, screen.x, screen.y, fg, 2, "%s - [%d]", hdr->pBone(i)->pszName(), i); } else draw::FString(fonts::ESP, - screen.x, screen.y, fg, 2, "%d", i); - } - }*/ } -}; -std::unordered_map bonelist_map{}; - -// Function called on draw -void Draw() -{ - std::lock_guard esp_lock(threadsafe_mutex); - if (!enabled) - return; - for (auto &i : entities_need_repaint) - { - ProcessEntityPT(ENTITY(i)); - } + original(_this, state, info, matrix); } -// Function called on create move -void CreateMove() +int IN_KeyEvent_hook(void *_this, int eventcode, int keynum, + const char *pszCurrentBinding) { - - // Check usersettings if enabled - if (!enabled) - return; - - // Something - std::lock_guard esp_lock(threadsafe_mutex); - - ResetEntityStrings(); // Clear any strings entities have - entities_need_repaint.clear(); // Clear data on entities that need redraw - - // TODO, Find the benefit of using max clients with this logic - // Get max clients and - static int max_clients = g_IEngine->GetMaxClients(); - int limit = HIGHEST_ENTITY; - - // If not using any other special esp, we lower the min to the max clients - if (!buildings && !proj_esp && !item_esp) - limit = std::min(max_clients, HIGHEST_ENTITY); - - { // I still dont understand the purpose of prof_section and surrounding in - // brackets - PROF_SECTION(CM_ESP_EntityLoop); - - // Loop through entities - for (int i = 0; i < limit; i++) - { - // Get an entity from the loop tick and process it - CachedEntity *ent = ENTITY(i); - ProcessEntity(ent); - - // Dont know what this check is for - if (data[i].string_count) - { - - // Set entity color - SetEntityColor(ent, colors::EntityF(ent)); - - // If snow distance, add string here - if (show_distance) - { - AddEntityString(ent, format((int) (ENTITY(i)->m_flDistance / - 64 * 1.22f), - 'm')); - } - } - // No idea, this is confusing - if (data[ent->m_IDX].needs_paint) - entities_need_repaint.push_back(ent->m_IDX); - } - } + static const IN_KeyEvent_t original = + (IN_KeyEvent_t) hooks::client.GetMethod(offsets::IN_KeyEvent()); +#if ENABLE_GUI +/* +if (g_pGUI->ConsumesKey((ButtonCode_t)keynum) && g_pGUI->Visible()) { + return 0; } - -// Used when processing entitys with cached data from createmove in draw -void _FASTCALL ProcessEntityPT(CachedEntity *ent) -{ - PROF_SECTION(PT_esp_process_entity); - - // Check to prevent crashes - if (CE_BAD(ent)) - return; - - // Grab esp data - ESPData &ent_data = data[ent->m_IDX]; - - // Get color of entity - // TODO, check if we can move this after world to screen check - rgba_t fg = ent_data.color; - if (!fg || fg.a == 0.0f) - fg = ent_data.color = colors::EntityF(ent); - - // Check if entity is on screen, then save screen position if true - Vector screen, origin_screen; - if (!draw::EntityCenterToScreen(ent, screen) && - !draw::WorldToScreen(ent->m_vecOrigin, origin_screen)) - return; - - // Reset the collide cache - ent_data.has_collide = false; - - // Get if ent should be transparent - bool transparent = false; - if (vischeck && !ent->IsVisible()) - transparent = true; - - // Bone esp - if (draw_bones && ent->m_Type == ENTITY_PLAYER) - { - const model_t *model = RAW_ENT(ent)->GetModel(); - if (model) - { - auto hdr = g_IModelInfo->GetStudiomodel(model); - bonelist_map[hdr].Draw(ent, fg); - } - } - - // Tracers - if (tracers && ent->m_Type == ENTITY_PLAYER) - { - - // Grab the screen resolution and save to some vars - int width, height; - g_IEngine->GetScreenSize(width, height); - - // Center values on screen - width = width / 2; - // Only center height if we are using center mode - if ((int) tracers == 1) - height = height / 2; - - // Get world to screen - Vector scn; - draw::WorldToScreen(ent->m_vecOrigin, scn); - - // Draw a line - draw_api::draw_line(scn.x, scn.y, width - scn.x, height - scn.y, fg, - 0.5f); - } - - // Sightline esp - if (sightlines && ent->m_Type == ENTITY_PLAYER) - { - - // Logic for using the enum to sort out snipers - if ((int) sightlines == 2 || - ((int) sightlines == 1 && CE_INT(ent, netvar.iClass) == tf_sniper)) - { - PROF_SECTION(PT_esp_sightlines); - - // Get players angle and head position - Vector &eye_angles = - NET_VECTOR(RAW_ENT(ent), netvar.m_angEyeAngles); - Vector eye_position; - GetHitbox(ent, 0, eye_position); - - // Main ray tracing area - float sy = sinf(DEG2RAD(eye_angles.y)); // yaw - float cy = cosf(DEG2RAD(eye_angles.y)); - float sp = sinf(DEG2RAD(eye_angles.x)); // pitch - float cp = cosf(DEG2RAD(eye_angles.x)); - Vector forward_t = Vector(cp * cy, cp * sy, -sp); - // We dont want the sightlines endpoint to go behind us because the - // world to screen check will fail, but keep it at most 4096 - Vector forward = forward_t * 4096.0F + eye_position; - Ray_t ray; - ray.Init(eye_position, forward); - trace_t trace; - g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_no_player, - &trace); - - // Screen vectors - Vector scn1, scn2; - - // Status vars - bool found_scn2 = true; - - // Get end point on screen - if (!draw::WorldToScreen(trace.endpos, scn2)) - { - // Set status - found_scn2 = false; - // Get the end distance from the trace - float end_distance = trace.endpos.DistTo(eye_position); - - // Loop and look back untill we have a vector on screen - for (int i = 1; i > 400; i++) - { - // Subtract 40 multiplyed by the tick from the end distance - // and use that as our length to check - Vector end_vector = - forward_t * (end_distance - (10 * i)) + eye_position; - if (end_vector.DistTo(eye_position) < 1) - break; - if (draw::WorldToScreen(end_vector, scn2)) - { - found_scn2 = true; - break; - } - } - } - - if (found_scn2) - { - // Set status - bool found_scn1 = true; - - // If we dont have a vector on screen, attempt to find one - if (!draw::WorldToScreen(eye_position, scn1)) - { - // Set status - found_scn1 = false; - // Get the end distance from the trace - float start_distance = trace.endpos.DistTo(eye_position); - - // Loop and look back untill we have a vector on screen - for (int i = 1; i > 400; i++) - { - // Multiply starting distance by 40, multiplyed by the - // loop tick - Vector start_vector = - forward_t * (10 * i) + eye_position; - // We dont want it to go too far - if (start_vector.DistTo(trace.endpos) < 1) - break; - // Check if we have a vector on screen, if we do then we - // set our status - if (draw::WorldToScreen(start_vector, scn1)) - { - found_scn1 = true; - break; - } - } - } - // We have both vectors, draw - if (found_scn1) - { - draw_api::draw_line(scn1.x, scn1.y, scn2.x - scn1.x, - scn2.y - scn1.y, fg, 0.5f); - } - } - } - } -#ifndef FEATURE_EMOJI_ESP_DISABLED - // Emoji esp - if (emoji_esp) - { - if (ent->m_Type == ENTITY_PLAYER) - { - // Positions in the atlas for the textures - static textures::AtlasTexture joy_texture( - 64 * 4, textures::atlas_height - 64 * 4, 64, 64); - static textures::AtlasTexture thinking_texture( - 64 * 5, textures::atlas_height - 64 * 4, 64, 64); - - auto hb = ent->hitboxes.GetHitbox(0); - Vector hbm, hbx; - if (draw::WorldToScreen(hb->min, hbm) && - draw::WorldToScreen(hb->max, hbx)) - { - Vector head_scr; - if (draw::WorldToScreen(hb->center, head_scr)) - { - float size = emoji_esp_scaling ? fabs(hbm.y - hbx.y) - : float(emoji_esp_size); - if (emoji_esp_scaling && (size < float(emoji_min_size))) - { - size = float(emoji_min_size); - } - textures::AtlasTexture *tx = nullptr; - if (int(emoji_esp) == 1) - tx = &joy_texture; - if (int(emoji_esp) == 2) - tx = &thinking_texture; - if (tx) - tx->Draw(head_scr.x - size / 2, head_scr.y - size / 2, - colors::white, size, size); - } - } - } - } +*/ #endif - // Box esp - if (box_esp) + return original(_this, eventcode, keynum, pszCurrentBinding); +} + +CatVar override_fov_zoomed(CV_FLOAT, "fov_zoomed", "0", "FOV override (zoomed)", + "Overrides FOV with this value when zoomed in " + "(default FOV when zoomed is 20)"); +CatVar override_fov(CV_FLOAT, "fov", "0", "FOV override", + "Overrides FOV with this value"); + +CatVar freecam(CV_KEY, "debug_freecam", "0", "Freecam"); +int spectator_target{ 0 }; + +CatCommand spectate("spectate", "Spectate", [](const CCommand &args) { + if (args.ArgC() < 1) { - switch (ent->m_Type) + spectator_target = 0; + return; + } + int id = atoi(args.Arg(1)); + if (!id) + spectator_target = 0; + else + { + spectator_target = g_IEngine->GetPlayerForUserID(id); + } +}); + +void OverrideView_hook(void *_this, CViewSetup *setup) +{ + static const OverrideView_t original = + (OverrideView_t) hooks::clientmode.GetMethod(offsets::OverrideView()); + static bool zoomed; + original(_this, setup); + if (!cathook) + return; + if (g_pLocalPlayer->bZoomed && override_fov_zoomed) + { + setup->fov = override_fov_zoomed; + } + else + { + if (override_fov) { - case ENTITY_PLAYER: - if (vischeck && !ent->IsVisible()) - transparent = true; - if (!fg) - fg = colors::EntityF(ent); - if (transparent) - fg = colors::Transparent(fg); - DrawBox(ent, fg); - break; - case ENTITY_BUILDING: - if (CE_INT(ent, netvar.iTeamNum) == g_pLocalPlayer->team && - !teammates) - break; - if (!transparent && vischeck && !ent->IsVisible()) - transparent = true; - if (!fg) - fg = colors::EntityF(ent); - if (transparent) - fg = colors::Transparent(fg); - DrawBox(ent, fg); - break; + setup->fov = override_fov; } } - // Healthbar - if ((int) show_health >= 2) + if (spectator_target) { - - // We only want health bars on players and buildings - if (ent->m_Type == ENTITY_PLAYER || ent->m_Type == ENTITY_BUILDING) + CachedEntity *spec = ENTITY(spectator_target); + if (CE_GOOD(spec) && !CE_BYTE(spec, netvar.iLifeState)) { - - // Get collidable from the cache - if (GetCollide(ent)) + setup->origin = + spec->m_vecOrigin + CE_VECTOR(spec, netvar.vViewOffset); + // why not spectate yourself + if (spec == LOCAL_E) { - - // Pull the cached collide info - int max_x = ent_data.collide_max.x; - int max_y = ent_data.collide_max.y; - int min_x = ent_data.collide_min.x; - int min_y = ent_data.collide_min.y; - - // Get health values - int health = 0; - int healthmax = 0; - switch (ent->m_Type) - { - case ENTITY_PLAYER: - health = CE_INT(ent, netvar.iHealth); - healthmax = ent->m_iMaxHealth; - break; - case ENTITY_BUILDING: - health = CE_INT(ent, netvar.iBuildingHealth); - healthmax = CE_INT(ent, netvar.iBuildingMaxHealth); - break; - } - - // Get Colors - rgba_t hp = colors::Transparent( - colors::Health(health, healthmax), fg.a); - rgba_t border = - ((ent->m_iClassID == RCC_PLAYER) && IsPlayerInvisible(ent)) - ? colors::FromRGBA8(160, 160, 160, fg.a * 255.0f) - : colors::Transparent(colors::black, fg.a); - // Get bar height - int hbh = (max_y - min_y - 2) * - std::min((float) health / (float) healthmax, 1.0f); - - // Draw - draw_api::draw_rect_outlined(min_x - 7, min_y, 7, max_y - min_y, - border, 0.5f); - draw_api::draw_rect(min_x - 6, max_y - hbh - 1, 5, hbh, hp); - } - } - } - - // Check if entity has strings to draw - if (ent_data.string_count) - { - PROF_SECTION(PT_esp_drawstrings); - - // Create our initial point at the center of the entity - Vector draw_point = screen; - bool origin_is_zero = true; - - // Only get collidable for players and buildings - if (ent->m_Type == ENTITY_PLAYER || ent->m_Type == ENTITY_BUILDING) - { - - // Get collidable from the cache - if (GetCollide(ent)) - { - - // Origin could change so we set to false - origin_is_zero = false; - - // Pull the cached collide info - int max_x = ent_data.collide_max.x; - int max_y = ent_data.collide_max.y; - int min_x = ent_data.collide_min.x; - int min_y = ent_data.collide_min.y; - - // Change the position of the draw point depending on the user - // settings - switch ((int) esp_text_position) - { - case 0: - { // TOP RIGHT - draw_point = Vector(max_x + 2, min_y, 0); - } - break; - case 1: - { // BOTTOM RIGHT - draw_point = - Vector(max_x + 2, - max_y - - data.at(ent->m_IDX).string_count * - /*((int)fonts::font_main->height)*/ 14, - 0); - } - break; - case 2: - { // CENTER - origin_is_zero = - true; // origin is still zero so we set to true - } - break; - case 3: - { // ABOVE - draw_point = Vector( - min_x, min_y - - data.at(ent->m_IDX).string_count * - /*((int)fonts::font_main->height)*/ 14, - 0); - } - break; - case 4: - { // BELOW - draw_point = Vector(min_x, max_y, 0); - } - } - } - } - - // if user setting allows vis check and ent isnt visable, make - // transparent - if (vischeck && !ent->IsVisible()) - transparent = true; - - // Loop through strings - for (int j = 0; j < ent_data.string_count; j++) - { - - // Pull string from the entity's cached string array - const ESPString &string = ent_data.strings[j]; - - // If string has a color assined to it, apply that otherwise use - // entities color - rgba_t color = string.color ? string.color : ent_data.color; - if (transparent) - color = - colors::Transparent(color); // Apply transparency if needed - - // If the origin is centered, we use one method. if not, the other - if (!origin_is_zero || true) - { - draw_api::draw_string_with_outline( - draw_point.x, draw_point.y, string.data.c_str(), - fonts::main_font, color, colors::black, 1.5f); + setup->angles = + CE_VAR(spec, netvar.m_angEyeAnglesLocal, QAngle); } else - { /* - int size_x; - FTGL_StringLength(string.data, fonts::font_main, &size_x); - FTGL_Draw(string.data, draw_point.x - size_x / 2, draw_point.y, - fonts::font_main, color); - */ + { + setup->angles = CE_VAR(spec, netvar.m_angEyeAngles, QAngle); } - - // Add to the y due to their being text in that spot - draw_point.y += /*((int)fonts::font_main->height)*/ 15 - 1; + } + if (g_IInputSystem->IsButtonDown(ButtonCode_t::KEY_SPACE)) + { + spectator_target = 0; } } - // TODO Add Rotation matix - // TODO Currently crashes, needs null check somewhere - // Draw Hitboxes - /*if (draw_hitbox && ent->m_Type == ENTITY_PLAYER) { - PROF_SECTION(PT_esp_drawhitbboxes); - - // Loop through hitboxes - for (int i = 0; i <= 17; i++) { // I should probs get how many hitboxes - instead of using a fixed number... - - // Get a hitbox from the entity - hitbox_cache::CachedHitbox* hb = ent->hitboxes.GetHitbox(i); - - // Create more points from min + max - Vector box_points[8]; - Vector vec_tmp; - for (int ii = 0; ii <= 8; ii++) { // 8 points to the box - - // logic le paste from sdk - vec_tmp[0] = ( ii & 0x1 ) ? hb->max[0] : hb->min[0]; - vec_tmp[1] = ( ii & 0x2 ) ? hb->max[1] : hb->min[1]; - vec_tmp[2] = ( ii & 0x4 ) ? hb->max[2] : hb->min[2]; - - // save to points array - box_points[ii] = vec_tmp; - } - - // Draw box from points - // Draws a point to every other point. Ineffient, use now fix - later... Vector scn1, scn2; // to screen for (int ii = 0; ii < 8; ii++) { - - // Get first point - if (!draw::WorldToScreen(box_points[ii], scn1)) continue; - - for (int iii = 0; iii < 8; iii++) { - - // Get second point - if (!draw::WorldToScreen(box_points[iii], scn2)) continue; - - // Draw between points - draw_api::Line(scn1.x, scn1.y, scn2.x - scn1.x, scn2.y - - scn1.y, fg); - } + if (freecam) + { + static Vector freecam_origin{ 0 }; + static bool freecam_last{ false }; + if (freecam.KeyDown()) + { + if (not freecam_last) + { + freecam_origin = setup->origin; } + float sp, sy, cp, cy; + QAngle angle; + Vector forward; + g_IEngine->GetViewAngles(angle); + sy = sinf(DEG2RAD(angle[1])); + cy = cosf(DEG2RAD(angle[1])); + sp = sinf(DEG2RAD(angle[0])); + cp = cosf(DEG2RAD(angle[0])); + forward.x = cp * cy; + forward.y = cp * sy; + forward.z = -sp; + forward *= 4; + freecam_origin += forward; + setup->origin = freecam_origin; } - }*/ + freecam_last = freecam.KeyDown(); + } + + draw::fov = setup->fov; } -// Used to process entities from CreateMove -void _FASTCALL ProcessEntity(CachedEntity *ent) +#endif + +bool CanPacket_hook(void *_this) { - if (!enabled) - return; // Esp enable check - if (CE_BAD(ent)) - return; // CE_BAD check to prevent crashes + const CanPacket_t original = + (CanPacket_t) hooks::netchannel.GetMethod(offsets::CanPacket()); + return *bSendPackets && original(_this); +} - // Entity esp - if (entity_info) +CUserCmd *GetUserCmd_hook(IInput *_this, int sequence_number) +{ + static const GetUserCmd_t original = + (GetUserCmd_t) hooks::input.GetMethod(offsets::GetUserCmd()); + static CUserCmd *def; + static int oldcmd; + static INetChannel *ch; + + def = original(_this, sequence_number); + if (def && + command_number_mod.find(def->command_number) != + command_number_mod.end()) { - AddEntityString(ent, - format(RAW_ENT(ent)->GetClientClass()->m_pNetworkName, - " [", ent->m_iClassID, "]")); - if (entity_id) - { - AddEntityString(ent, std::to_string(ent->m_IDX)); - } - if (entity_model) - { - const model_t *model = RAW_ENT(ent)->GetModel(); - if (model) - AddEntityString(ent, - std::string(g_IModelInfo->GetModelName(model))); - } + // logging::Info("Replacing command %i with %i", def->command_number, + // command_number_mod[def->command_number]); + oldcmd = def->command_number; + def->command_number = command_number_mod[def->command_number]; + def->random_seed = + MD5_PseudoRandom(unsigned(def->command_number)) & 0x7fffffff; + command_number_mod.erase(command_number_mod.find(oldcmd)); + *(int *) ((unsigned) g_IBaseClientState + + offsets::lastoutgoingcommand()) = def->command_number - 1; + ch = + (INetChannel *) g_IEngine + ->GetNetChannelInfo(); //*(INetChannel**)((unsigned)g_IBaseClientState + //+ offsets::m_NetChannel()); + *(int *) ((unsigned) ch + offsets::m_nOutSequenceNr()) = + def->command_number - 1; } + return def; +} - // Get esp data from current ent - ESPData &espdata = data[ent->m_IDX]; +static CatVar log_sent(CV_SWITCH, "debug_log_sent_messages", "0", + "Log sent messages"); - // Projectile esp - if (ent->m_Type == ENTITY_PROJECTILE && proj_esp && - (ent->m_bEnemy || (teammates && !proj_enemy))) +static CatCommand plus_use_action_slot_item_server( + "+cat_use_action_slot_item_server", "use_action_slot_item_server", []() { + KeyValues *kv = new KeyValues("+use_action_slot_item_server"); + g_pLocalPlayer->using_action_slot_item = true; + g_IEngine->ServerCmdKeyValues(kv); + }); + +static CatCommand minus_use_action_slot_item_server( + "-cat_use_action_slot_item_server", "use_action_slot_item_server", []() { + KeyValues *kv = new KeyValues("-use_action_slot_item_server"); + g_pLocalPlayer->using_action_slot_item = false; + g_IEngine->ServerCmdKeyValues(kv); + }); + +static CatVar newlines_msg(CV_INT, "chat_newlines", "0", "Prefix newlines", + "Add # newlines before each your message", 0, 24); +// TODO replace \\n with \n +// TODO name \\n = \n +// static CatVar queue_messages(CV_SWITCH, "chat_queue", "0", "Queue messages", +// "Use this if you want to use spam/killsay and still be able to chat normally +// (without having your msgs eaten by valve cooldown)"); + +static CatVar airstuck(CV_KEY, "airstuck", "0", "Airstuck"); +static CatVar crypt_chat( + CV_SWITCH, "chat_crypto", "1", "Crypto chat", + "Start message with !! and it will be only visible to cathook users"); +static CatVar chat_filter(CV_STRING, "chat_censor", "", + "Spam Chat with newlines if the chosen words are " + "said, seperate with commas"); +static CatVar chat_filter_enabled(CV_SWITCH, "chat_censor_enabled", "0", + "enable censor"); + +bool SendNetMsg_hook(void *_this, INetMessage &msg, bool bForceReliable = false, + bool bVoice = false) +{ + static size_t say_idx, say_team_idx; + static int offset; + static std::string newlines; + static NET_StringCmd stringcmd; + + // This is a INetChannel hook - it SHOULDN'T be static because netchannel + // changes. + const SendNetMsg_t original = + (SendNetMsg_t) hooks::netchannel.GetMethod(offsets::SendNetMsg()); + // net_StringCmd + if (msg.GetType() == 4 && (newlines_msg || crypt_chat)) { - - // Rockets - if (ent->m_iClassID == CL_CLASS(CTFProjectile_Rocket) || - ent->m_iClassID == CL_CLASS(CTFProjectile_SentryRocket)) + std::string str(msg.ToString()); + say_idx = str.find("net_StringCmd: \"say \""); + say_team_idx = str.find("net_StringCmd: \"say_team \""); + if (!say_idx || !say_team_idx) { - if (proj_rockets) + offset = say_idx ? 26 : 21; + bool crpt = false; + if (crypt_chat) { - if ((int) proj_rockets != 2 || ent->m_bCritProjectile) + std::string msg(str.substr(offset)); + msg = msg.substr(0, msg.length() - 2); + if (msg.find("!!") == 0) { - AddEntityString(ent, "[ ==> ]"); + msg = ucccccp::encrypt(msg.substr(2)); + str = str.substr(0, offset) + msg + "\"\""; + crpt = true; } } - - // Pills/Stickys - } - else if (ent->m_iClassID == CL_CLASS(CTFGrenadePipebombProjectile)) - { - // Switch based on pills/stickys - switch (CE_INT(ent, netvar.iPipeType)) + if (!crpt && newlines_msg) { - case 0: // Pills - if (!proj_pipes) - break; - if ((int) proj_pipes == 2 && !ent->m_bCritProjectile) - break; - AddEntityString(ent, "[ (PP) ]"); - break; - case 1: // Stickys - if (!proj_stickies) - break; - if ((int) proj_stickies == 2 && !ent->m_bCritProjectile) - break; - AddEntityString(ent, "[ {*} ]"); + // TODO move out? update in a value change callback? + newlines = std::string((int) newlines_msg, '\n'); + str.insert(offset, newlines); } - - // Huntsman + str = str.substr(16, str.length() - 17); + // if (queue_messages && !chat_stack::CanSend()) { + stringcmd.m_szCommand = str.c_str(); + return original(_this, stringcmd, bForceReliable, bVoice); + //} } - else if (ent->m_iClassID == CL_CLASS(CTFProjectile_Arrow)) + } + static ConVar *sv_player_usercommand_timeout = + g_ICvar->FindVar("sv_player_usercommand_timeout"); + static float lastcmd = 0.0f; + if (lastcmd > g_GlobalVars->absoluteframetime) + { + lastcmd = g_GlobalVars->absoluteframetime; + } + if (airstuck.KeyDown() && !g_Settings.bInvalid) + { + if (CE_GOOD(LOCAL_E)) { - if ((int) proj_arrows != 2 || ent->m_bCritProjectile) + if (lastcmd + sv_player_usercommand_timeout->GetFloat() - 0.1f < + g_GlobalVars->curtime) { - AddEntityString(ent, "[ >>---> ]"); + if (msg.GetType() == clc_Move) + return false; + } + else + { + lastcmd = g_GlobalVars->absoluteframetime; } } } - - // Hl2DM dropped item esp - IF_GAME(IsHL2DM()) + if (log_sent && msg.GetType() != 3 && msg.GetType() != 9) { - if (item_esp && item_dropped_weapons) + logging::Info("=> %s [%i] %s", msg.GetName(), msg.GetType(), + msg.ToString()); + unsigned char buf[4096]; + bf_write buffer("cathook_debug_buffer", buf, 4096); + logging::Info("Writing %i", msg.WriteToBuffer(buffer)); + std::string bytes = ""; + constexpr char h2c[] = "0123456789abcdef"; + for (int i = 0; i < buffer.GetNumBytesWritten(); i++) { - if (CE_BYTE(ent, netvar.hOwner) == (unsigned char) -1) + // bytes += format(h2c[(buf[i] & 0xF0) >> 4], h2c[(buf[i] & 0xF)], ' + // '); + bytes += format((unsigned short) buf[i], ' '); + } + logging::Info("%i bytes => %s", buffer.GetNumBytesWritten(), + bytes.c_str()); + } + return original(_this, msg, bForceReliable, bVoice); +} + +static CatVar die_if_vac(CV_SWITCH, "die_if_vac", "0", "Die if VAC banned"); + +void Shutdown_hook(void *_this, const char *reason) +{ + g_Settings.bInvalid = true; + // This is a INetChannel hook - it SHOULDN'T be static because netchannel + // changes. + const Shutdown_t original = + (Shutdown_t) hooks::netchannel.GetMethod(offsets::Shutdown()); + logging::Info("Disconnect: %s", reason); + if (strstr(reason, "banned")) + { + if (die_if_vac) + { + logging::Info("VAC banned"); + *(int *) 0 = 0; + exit(1); + } + } +#if ENABLE_IPC + ipc::UpdateServerAddress(true); +#endif + if (cathook && (disconnect_reason.convar_parent->m_StringLength > 3) && + strstr(reason, "user")) + { + original(_this, disconnect_reason_newlined); + } + else + { + original(_this, reason); + } + + if (hacks::shared::autojoin::auto_queue) + tfmm::abandon(); +} + +static CatVar resolver(CV_SWITCH, "resolver", "0", "Resolve angles"); + +CatEnum namesteal_enum({ "OFF", "PASSIVE", "ACTIVE" }); +CatVar namesteal(namesteal_enum, "name_stealer", "0", "Name Stealer", + "Attemt to steal your teammates names. Usefull for avoiding " + "kicks\nPassive only changes when the name stolen is no " + "longer the best name to use\nActive Attemps to change the " + "name whenever possible"); + +static std::string stolen_name; + +// Func to get a new entity to steal name from and returns true if a target has +// been found +bool StolenName() +{ + + // Array to store potential namestealer targets with a bookkeeper to tell + // how full it is + int potential_targets[32]; + int potential_targets_length = 0; + + // Go through entities looking for potential targets + for (int i = 1; i < HIGHEST_ENTITY; i++) + { + CachedEntity *ent = ENTITY(i); + + // Check if ent is a good target + if (!ent) + continue; + if (ent == LOCAL_E) + continue; + if (!ent->m_Type == ENTITY_PLAYER) + continue; + if (ent->m_bEnemy) + continue; + + // Check if name is current one + player_info_s info; + if (g_IEngine->GetPlayerInfo(ent->m_IDX, &info)) + { + + // If our name is the same as current, than change it + if (std::string(info.name) == stolen_name) { - int string_count_backup = data[ent->m_IDX].string_count; - if (ent->m_iClassID == CL_CLASS(CWeapon_SLAM)) - AddEntityString(ent, "SLAM"); - else if (ent->m_iClassID == CL_CLASS(CWeapon357)) - AddEntityString(ent, ".357"); - else if (ent->m_iClassID == CL_CLASS(CWeaponAR2)) - AddEntityString(ent, "AR2"); - else if (ent->m_iClassID == CL_CLASS(CWeaponAlyxGun)) - AddEntityString(ent, "Alyx Gun"); - else if (ent->m_iClassID == CL_CLASS(CWeaponAnnabelle)) - AddEntityString(ent, "Annabelle"); - else if (ent->m_iClassID == CL_CLASS(CWeaponBinoculars)) - AddEntityString(ent, "Binoculars"); - else if (ent->m_iClassID == CL_CLASS(CWeaponBugBait)) - AddEntityString(ent, "Bug Bait"); - else if (ent->m_iClassID == CL_CLASS(CWeaponCrossbow)) - AddEntityString(ent, "Crossbow"); - else if (ent->m_iClassID == CL_CLASS(CWeaponShotgun)) - AddEntityString(ent, "Shotgun"); - else if (ent->m_iClassID == CL_CLASS(CWeaponSMG1)) - AddEntityString(ent, "SMG"); - else if (ent->m_iClassID == CL_CLASS(CWeaponRPG)) - AddEntityString(ent, "RPG"); - if (string_count_backup != data[ent->m_IDX].string_count) + // Since we found the ent we stole our name from and it is still + // good, if user settings are passive, then we return true and + // dont alter our name + if ((int) namesteal == 1) { - SetEntityColor(ent, colors::yellow); + return true; + // Otherwise we continue to change our name to something + // else } + else + continue; } - } - } - // Tank esp - if (ent->m_iClassID == CL_CLASS(CTFTankBoss) && tank) - { - AddEntityString(ent, "Tank"); - - // Dropped weapon esp - } - else if (ent->m_iClassID == CL_CLASS(CTFDroppedWeapon) && item_esp && - item_dropped_weapons) - { - AddEntityString( - ent, format("WEAPON ", RAW_ENT(ent)->GetClientClass()->GetName())); - - // MVM Money esp - } - else if (ent->m_iClassID == CL_CLASS(CCurrencyPack) && item_money) - { - if (CE_BYTE(ent, netvar.bDistributed)) - { - if (item_money_red) - { - AddEntityString(ent, "~$~"); - } + // a ent without a name is no ent we need, contine for a different + // one } else - { - AddEntityString(ent, "$$$"); - } + continue; - // Other item esp + // Save the ent to our array + potential_targets[potential_targets_length] = i; + potential_targets_length++; + + // With our maximum amount of players reached, dont search for anymore + if (potential_targets_length >= 32) + break; } - else if (ent->m_ItemType != ITEM_NONE && item_esp) - { - // Health pack esp - if (item_health_packs && (ent->m_ItemType >= ITEM_HEALTH_SMALL && - ent->m_ItemType <= ITEM_HEALTH_LARGE || - ent->m_ItemType == ITEM_HL_BATTERY)) - { - if (ent->m_ItemType == ITEM_HEALTH_SMALL) - AddEntityString(ent, "[+]"); - if (ent->m_ItemType == ITEM_HEALTH_MEDIUM) - AddEntityString(ent, "[++]"); - if (ent->m_ItemType == ITEM_HEALTH_LARGE) - AddEntityString(ent, "[+++]"); - if (ent->m_ItemType == ITEM_HL_BATTERY) - AddEntityString(ent, "[Z]"); - - // TF2C Adrenaline esp - } - else if (item_adrenaline && ent->m_ItemType == ITEM_TF2C_PILL) - { - AddEntityString(ent, "[a]"); - - // Ammo pack esp - } - else if (item_ammo_packs && ent->m_ItemType >= ITEM_AMMO_SMALL && - ent->m_ItemType <= ITEM_AMMO_LARGE) - { - if (ent->m_ItemType == ITEM_AMMO_SMALL) - AddEntityString(ent, "{i}"); - if (ent->m_ItemType == ITEM_AMMO_MEDIUM) - AddEntityString(ent, "{ii}"); - if (ent->m_ItemType == ITEM_AMMO_LARGE) - AddEntityString(ent, "{iii}"); - - // Powerup esp - } - else if (item_powerups && ent->m_ItemType >= ITEM_POWERUP_FIRST && - ent->m_ItemType <= ITEM_POWERUP_LAST) - { - AddEntityString( - ent, format(powerups[ent->m_ItemType - ITEM_POWERUP_FIRST], - " PICKUP")); - - // TF2C weapon spawner esp - } - else if (item_weapon_spawners && ent->m_ItemType >= ITEM_TF2C_W_FIRST && - ent->m_ItemType <= ITEM_TF2C_W_LAST) - { - AddEntityString( - ent, - format(tf2c_weapon_names[ent->m_ItemType - ITEM_TF2C_W_FIRST], - " SPAWNER")); - if (CE_BYTE(ent, netvar.bRespawning)) - AddEntityString(ent, "-- RESPAWNING --"); - - // Halloween spell esp - } - else if (item_spellbooks && (ent->m_ItemType == ITEM_SPELL || - ent->m_ItemType == ITEM_SPELL_RARE)) - { - if (ent->m_ItemType == ITEM_SPELL) - { - AddEntityString(ent, "Spell", colors::green); - } - else - { - AddEntityString(ent, "Rare Spell", - colors::FromRGBA8(139, 31, 221, 255)); - } - } - - // Building esp - } - else if (ent->m_Type == ENTITY_BUILDING && buildings) - { - - // Check if enemy building - if (!ent->m_bEnemy && !teammates) - return; - - // TODO maybe... - /*if (legit && ent->m_iTeam != g_pLocalPlayer->team) { - if (ent->m_lLastSeen > v_iLegitSeenTicks->GetInt()) { - return; - } - }*/ - - // Make a name for the building based on the building type and level - if (show_name || show_class) - { - const std::string &name = - (ent->m_iClassID == CL_CLASS(CObjectTeleporter) - ? "Teleporter" - : (ent->m_iClassID == CL_CLASS(CObjectSentrygun) - ? "Sentry Gun" - : "Dispenser")); - int level = CE_INT(ent, netvar.iUpgradeLevel); - AddEntityString(ent, format("LV ", level, ' ', name)); - } - // If text health is true, then add a string with the health - if ((int) show_health == 1 || (int) show_health == 3) - { - AddEntityString( - ent, format(ent->m_iHealth, '/', ent->m_iMaxHealth, " HP"), - colors::Health(ent->m_iHealth, ent->m_iMaxHealth)); - } - // Set the entity to repaint - espdata.needs_paint = true; - return; - - // Player esp - } - else if (ent->m_Type == ENTITY_PLAYER && ent->m_bAlivePlayer) - { - - // Local player handling - if (!(local_esp && g_IInput->CAM_IsThirdPerson()) && - ent->m_IDX == g_IEngine->GetLocalPlayer()) - return; - - // Get player class - int pclass = CE_INT(ent, netvar.iClass); - - // Attempt to get player info, and if cant, return - player_info_s info; - if (!g_IEngine->GetPlayerInfo(ent->m_IDX, &info)) - return; - - // TODO, check if u can just use "ent->m_bEnemy" instead of m_iTeam - // Legit mode handling - if (legit && ent->m_iTeam != g_pLocalPlayer->team && - playerlist::IsDefault(info.friendsID)) - { - if (IsPlayerInvisible(ent)) - return; // Invis check - if (vischeck && !ent->IsVisible()) - return; // Vis check - // TODO, maybe... - // if (ent->m_lLastSeen > - // (unsigned)v_iLegitSeenTicks->GetInt()) - // return; - } - - // Powerup handling - if (powerup_esp) - { - powerup_type power = GetPowerupOnPlayer(ent); - if (power != not_powerup) - AddEntityString(ent, format("^ ", powerups[power], " ^")); - } - - // Dont understand reasoning for this check - if (ent->m_bEnemy || teammates || - !playerlist::IsDefault(info.friendsID)) - { - - // Playername - if (show_name) - AddEntityString(ent, std::string(info.name)); - - // Player class - if (show_class) - { - if (pclass > 0 && pclass < 10) - AddEntityString(ent, classes[pclass - 1]); - } - -#if ENABLE_IPC == 1 - // ipc bot esp - if (show_bot_id && ipc::peer && ent != LOCAL_E) - { - for (unsigned i = 0; i < cat_ipc::max_peers; i++) - { - if (!ipc::peer->memory->peer_data[i].free && - ipc::peer->memory->peer_user_data[i].friendid == - info.friendsID) - { - AddEntityString(ent, format("BOT #", i)); - break; - } - } - } -#endif - // Health esp - if ((int) show_health == 1 || (int) show_health == 3) - { - AddEntityString( - ent, format(ent->m_iHealth, '/', ent->m_iMaxHealth, " HP"), - colors::Health(ent->m_iHealth, ent->m_iMaxHealth)); - } - IF_GAME(IsTF()) - { - // Medigun Ubercharge esp - if (show_ubercharge) - { - if (CE_INT(ent, netvar.iClass) == tf_medic) - { - int *weapon_list = (int *) ((unsigned) (RAW_ENT(ent)) + - netvar.hMyWeapons); - for (int i = 0; weapon_list[i]; i++) - { - int handle = weapon_list[i]; - int eid = handle & 0xFFF; - if (eid >= 32 && eid <= HIGHEST_ENTITY) - { - CachedEntity *weapon = ENTITY(eid); - if (!CE_BAD(weapon) && - weapon->m_iClassID == - CL_CLASS(CWeaponMedigun) && - weapon) - { - if (CE_INT(weapon, - netvar.iItemDefinitionIndex) != - 998) - { - AddEntityString( - ent, - format( - floor( - CE_FLOAT( - weapon, - netvar - .m_flChargeLevel) * - 100), - '%', " Uber"), - colors::Health( - (CE_FLOAT( - weapon, - netvar.m_flChargeLevel) * - 100), - 100)); - } - else - AddEntityString( - ent, - format( - floor( - CE_FLOAT( - weapon, - netvar - .m_flChargeLevel) * - 100), - '%', " Uber | Charges: ", - floor( - CE_FLOAT( - weapon, - netvar - .m_flChargeLevel) / - 0.25f)), - colors::Health( - (CE_FLOAT( - weapon, - netvar.m_flChargeLevel) * - 100), - 100)); - break; - } - } - } - } - } - // Conditions esp - if (show_conditions) - { - const auto &clr = colors::EntityF(ent); - // Invis - if (IsPlayerInvisible(ent)) - AddEntityString( - ent, "*CLOAKED*", - colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); - // Uber/Bonk - if (IsPlayerInvulnerable(ent)) - AddEntityString(ent, "*INVULNERABLE*"); - // Vaccinator - if (HasCondition(ent)) - { - AddEntityString(ent, "*VACCINATOR*"); - } - else if (HasCondition(ent)) - { - AddEntityString(ent, "*PASSIVE RESIST*"); - } - // Crit - if (IsPlayerCritBoosted(ent)) - AddEntityString(ent, "*CRITS*", colors::orange); - // Zoomed - if (HasCondition(ent)) - { - AddEntityString( - ent, "*ZOOMING*", - colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); - // Slowed - } - else if (HasCondition(ent)) - { - AddEntityString( - ent, "*SLOWED*", - colors::FromRGBA8(220.0f, 220.0f, 220.0f, 255.0f)); - } - // Jarated - if (HasCondition(ent)) - AddEntityString(ent, "*JARATED*", colors::yellow); - } - } - // Hoovy Esp - if (IsHoovy(ent)) - AddEntityString(ent, "Hoovy"); - - // Active weapon esp - int widx = CE_INT(ent, netvar.hActiveWeapon) & 0xFFF; - if (IDX_GOOD(widx)) - { - CachedEntity *weapon = ENTITY(widx); - if (CE_GOOD(weapon)) - { - if (show_weapon) - { - const char *weapon_name = - re::C_BaseCombatWeapon::GetPrintName( - RAW_ENT(weapon)); - if (weapon_name) - AddEntityString(ent, std::string(weapon_name)); - } - } - } - - // Notify esp to repaint - espdata.needs_paint = true; - } - return; - } -} - -// Draw a box around a player -void _FASTCALL DrawBox(CachedEntity *ent, const rgba_t &clr) -{ - PROF_SECTION(PT_esp_drawbox); - - // Check if ent is bad to prevent crashes - if (CE_BAD(ent)) - return; - - // Get our collidable bounds - if (!GetCollide(ent)) - return; - - // Pull the cached collide info - ESPData &ent_data = data[ent->m_IDX]; - int max_x = ent_data.collide_max.x; - int max_y = ent_data.collide_max.y; - int min_x = ent_data.collide_min.x; - int min_y = ent_data.collide_min.y; - - // Depending on whether the player is cloaked, we change the color - // acordingly - rgba_t border = ((ent->m_iClassID == RCC_PLAYER) && IsPlayerInvisible(ent)) - ? colors::FromRGBA8(160, 160, 160, clr.a * 255.0f) - : colors::Transparent(colors::black, clr.a); - - // With box corners, we draw differently - if ((int) box_esp == 2) - BoxCorners(min_x, min_y, max_x, max_y, clr, (clr.a != 1.0f)); - // Otherwise, we just do simple draw funcs - else - { - draw_api::draw_rect_outlined(min_x, min_y, max_x - min_x, max_y - min_y, - border, 0.5f); - draw_api::draw_rect_outlined(min_x + 1, min_y + 1, max_x - min_x - 2, - max_y - min_y - 2, clr, 0.5f); - draw_api::draw_rect_outlined(min_x + 2, min_y + 2, max_x - min_x - 4, - max_y - min_y - 4, border, 0.5f); - } -} - -// Function to draw box corners, Used by DrawBox -void BoxCorners(int minx, int miny, int maxx, int maxy, const rgba_t &color, - bool transparent) -{ - const rgba_t &black = - transparent ? colors::Transparent(colors::black) : colors::black; - const int size = box_corner_size; - - // Black corners - // Top Left - draw_api::draw_rect(minx, miny, size, 3, black); - draw_api::draw_rect(minx, miny + 3, 3, size - 3, black); - // Top Right - draw_api::draw_rect(maxx - size + 1, miny, size, 3, black); - draw_api::draw_rect(maxx - 3 + 1, miny + 3, 3, size - 3, black); - // Bottom Left - draw_api::draw_rect(minx, maxy - 3, size, 3, black); - draw_api::draw_rect(minx, maxy - size, 3, size - 3, black); - // Bottom Right - draw_api::draw_rect(maxx - size + 1, maxy - 3, size, 3, black); - draw_api::draw_rect(maxx - 2, maxy - size, 3, size - 3, black); - - // Colored corners - // Top Left - draw_api::draw_line(minx + 1, miny + 1, size - 2, 0, color, 0.5f); - draw_api::draw_line(minx + 1, miny + 1, 0, size - 2, color, 0.5f); - // Top Right - draw_api::draw_line(maxx - 1, miny + 1, -(size - 2), 0, color, 0.5f); - draw_api::draw_line(maxx - 1, miny + 1, 0, size - 2, color, 0.5f); - // Bottom Left - draw_api::draw_line(minx + 1, maxy - 2, size - 2, 0, color, 0.5f); - draw_api::draw_line(minx + 1, maxy - 2, 0, -(size - 2), color, 0.5f); - // Bottom Right - draw_api::draw_line(maxx - 1, maxy - 2, -(size - 2), 0, color, 0.5f); - draw_api::draw_line(maxx - 1, maxy - 2, 0, -(size - 2), color, 0.5f); -} - -// Used for caching collidable bounds -bool GetCollide(CachedEntity *ent) -{ - PROF_SECTION(PT_esp_getcollide); - - // Null + Dormant check to prevent crashing - if (CE_BAD(ent)) + // Checks to prevent crashes + if (potential_targets_length == 0) return false; - // Grab esp data - ESPData &ent_data = data[ent->m_IDX]; + // Get random number that we can use with our array + int target_random_num = + floor(RandFloatRange(0, potential_targets_length - 0.1F)); - // If entity has cached collides, return it. Otherwise generate new bounds - if (!ent_data.has_collide) + // Get a idx from our random array position + int new_target = potential_targets[target_random_num]; + + // Grab username of user + player_info_s info; + if (g_IEngine->GetPlayerInfo(new_target, &info)) { - // Get collision center, max, and mins - const Vector &origin = - RAW_ENT(ent)->GetCollideable()->GetCollisionOrigin(); - Vector mins = RAW_ENT(ent)->GetCollideable()->OBBMins() + origin; - Vector maxs = RAW_ENT(ent)->GetCollideable()->OBBMaxs() + origin; - - // Create a array for storing box points - Vector points_r[8]; // World vectors - Vector points[8]; // Screen vectors - - // If user setting for box expnad is true, spread the max and mins - if (esp_expand) - { - const float &exp = (float) esp_expand; - maxs.x += exp; - maxs.y += exp; - maxs.z += exp; - mins.x -= exp; - mins.y -= exp; - mins.z -= exp; - } - - // Create points for the box based on max and mins - float x = maxs.x - mins.x; - float y = maxs.y - mins.y; - float z = maxs.z - mins.z; - points_r[0] = mins; - points_r[1] = mins + Vector(x, 0, 0); - points_r[2] = mins + Vector(x, y, 0); - points_r[3] = mins + Vector(0, y, 0); - points_r[4] = mins + Vector(0, 0, z); - points_r[5] = mins + Vector(x, 0, z); - points_r[6] = mins + Vector(x, y, z); - points_r[7] = mins + Vector(0, y, z); - - // Check if any point of the box isnt on the screen - bool success = true; - for (int i = 0; i < 8; i++) - { - if (!draw::WorldToScreen(points_r[i], points[i])) - success = false; - } - // If a point isnt on the screen, return here - if (!success) - return false; - - // Get max and min of the box using the newly created screen vector - int max_x = -1; - int max_y = -1; - int min_x = 65536; - int min_y = 65536; - for (int i = 0; i < 8; i++) - { - if (points[i].x > max_x) - max_x = points[i].x; - if (points[i].y > max_y) - max_y = points[i].y; - if (points[i].x < min_x) - min_x = points[i].x; - if (points[i].y < min_y) - min_y = points[i].y; - } - - // Save the info to the esp data and notify cached that we cached info. - ent_data.collide_max = Vector(max_x, max_y, 0); - ent_data.collide_min = Vector(min_x, min_y, 0); - ent_data.has_collide = true; - + // If our name is the same as current, than change it and return true + stolen_name = std::string(info.name); return true; } - else - { - // We already have collidable so return true. - return true; - } - // Impossible error, return false + + // Didnt get playerinfo return false; } -// Use to add a esp string to an entity -void AddEntityString(CachedEntity *entity, const std::string &string, - const rgba_t &color) -{ - ESPData &entity_data = data[entity->m_IDX]; - if (entity_data.string_count >= 15) - return; - entity_data.strings[entity_data.string_count].data = string; - entity_data.strings[entity_data.string_count].color = color; - entity_data.string_count++; - entity_data.needs_paint = true; -} +static CatVar ipc_name(CV_STRING, "name_ipc", "", "IPC Name"); -// Function to reset entitys strings -void ResetEntityStrings() +const char *GetFriendPersonaName_hook(ISteamFriends *_this, CSteamID steamID) { - for (auto &i : data) + static const GetFriendPersonaName_t original = + (GetFriendPersonaName_t) hooks::steamfriends.GetMethod( + offsets::GetFriendPersonaName()); + +#if ENABLE_IPC + if (ipc::peer) { - i.string_count = 0; - i.color = colors::empty; - i.needs_paint = false; + static std::string namestr(ipc_name.GetString()); + namestr.assign(ipc_name.GetString()); + if (namestr.length() > 3) + { + ReplaceString(namestr, "%%", std::to_string(ipc::peer->client_id)); + return namestr.c_str(); + } } +#endif + + // Check User settings if namesteal is allowed + if (namesteal && steamID == g_ISteamUser->GetSteamID()) + { + + // We dont want to steal names while not in-game as there are no targets + // to steal from. We want to be on a team as well to get teammates names + if (g_IEngine->IsInGame() && g_pLocalPlayer->team) + { + + // Check if we have a username to steal, func automaticly steals a + // name in it. + if (StolenName()) + { + + // Return the name that has changed from the func above + return format(stolen_name, "\x0F").c_str(); + } + } + } + + if ((strlen(force_name.GetString()) > 1) && + steamID == g_ISteamUser->GetSteamID()) + { + + return force_name_newlined; + } + return original(_this, steamID); } -// Sets an entitys esp color -void SetEntityColor(CachedEntity *entity, const rgba_t &color) +void FireGameEvent_hook(void *_this, IGameEvent *event) { - data[entity->m_IDX].color = color; + static const FireGameEvent_t original = + (FireGameEvent_t) hooks::clientmode4.GetMethod( + offsets::FireGameEvent()); + const char *name = event->GetName(); + if (name) + { + if (event_log) + { + if (!strcmp(name, "player_connect_client") || + !strcmp(name, "player_disconnect") || + !strcmp(name, "player_team")) + { + return; + } + } + // hacks::tf2::killstreak::fire_event(event); + } + original(_this, event); } + +#if ENABLE_VISUALS == 1 +void FrameStageNotify_hook(void *_this, int stage) +{ + static IClientEntity *ent; + + PROF_SECTION(FrameStageNotify_TOTAL); + + static const FrameStageNotify_t original = + (FrameStageNotify_t) hooks::client.GetMethod( + offsets::FrameStageNotify()); + + if (!g_IEngine->IsInGame()) + g_Settings.bInvalid = true; + { + PROF_SECTION(FSN_skinchanger); + hacks::tf2::skinchanger::FrameStageNotify(stage); + } + if (resolver && cathook && !g_Settings.bInvalid && + stage == FRAME_NET_UPDATE_POSTDATAUPDATE_START) + { + PROF_SECTION(FSN_resolver); + for (int i = 1; i < 32 && i < HIGHEST_ENTITY; i++) + { + if (i == g_IEngine->GetLocalPlayer()) + continue; + ent = g_IEntityList->GetClientEntity(i); + if (ent && !ent->IsDormant() && !NET_BYTE(ent, netvar.iLifeState)) + { + Vector &angles = NET_VECTOR(ent, netvar.m_angEyeAngles); + if (angles.x >= 90) + angles.x = -89; + if (angles.x <= -90) + angles.x = 89; + angles.y = fmod(angles.y + 180.0f, 360.0f); + if (angles.y < 0) + angles.y += 360.0f; + angles.y -= 180.0f; + } + } + } + if (cathook && !g_Settings.bInvalid && stage == FRAME_RENDER_START) + { + IF_GAME(IsTF()) + { + if (CE_GOOD(LOCAL_E) && no_zoom) + RemoveCondition(LOCAL_E); + } + if (force_thirdperson && !g_pLocalPlayer->life_state && + CE_GOOD(g_pLocalPlayer->entity)) + { + CE_INT(g_pLocalPlayer->entity, netvar.nForceTauntCam) = 1; + } + if (stage == 5 && show_antiaim && g_IInput->CAM_IsThirdPerson()) + { + if (CE_GOOD(g_pLocalPlayer->entity)) + { + CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 4) = + g_Settings.last_angles.x; + CE_FLOAT(g_pLocalPlayer->entity, netvar.deadflag + 8) = + g_Settings.last_angles.y; + } + } + } + original(_this, stage); } +#endif /* TEXTMODE */ + +static CatVar clean_chat(CV_SWITCH, "clean_chat", "0", "Clean chat", + "Removes newlines from chat"); +static CatVar dispatch_log(CV_SWITCH, "debug_log_usermessages", "0", + "Log dispatched user messages"); +std::string clear = ""; +static bool firstcall = true; +bool DispatchUserMessage_hook(void *_this, int type, bf_read &buf) +{ + int loop_index, s, i, j; + char *data, c; + + static const DispatchUserMessage_t original = + (DispatchUserMessage_t) hooks::client.GetMethod( + offsets::DispatchUserMessage()); + if (type == 4) + { + loop_index = 0; + s = buf.GetNumBytesLeft(); + if (s < 256) + { + data = (char *) alloca(s); + for (i = 0; i < s; i++) + data[i] = buf.ReadByte(); + j = 0; + std::string name; + std::string message; + for (i = 0; i < 3; i++) + { + while ((c = data[j++]) && (loop_index < 128)) + { + loop_index++; + if (clean_chat) + if ((c == '\n' || c == '\r') && (i == 1 || i == 2)) + data[j - 1] = '*'; + if (i == 1) + name.push_back(c); + if (i == 2) + message.push_back(c); + } + } + static const char *lastfilter; + static const char *lastname; + static bool retrun = false; + if (data[0] != LOCAL_E->m_IDX) + { + if (retrun) + { + PrintChat("\x07%06X%s\x01: \x07%06X%s\x01", 0xe05938, + lastname, 0xefec1f, lastfilter); + retrun = false; + } + } + if (chat_filter_enabled && data[0] != LOCAL_E->m_IDX) + { + if (!strcmp(chat_filter.GetString(), "")) + { + std::string tmp = {}; + std::string tmp2 = {}; + int iii = 0; + player_info_s info; + g_IEngine->GetPlayerInfo(LOCAL_E->m_IDX, &info); + std::string name1 = info.name; + std::vector name2{}; + std::vector name3{}; + std::string claz = {}; + switch (g_pLocalPlayer->clazz) + { + case tf_scout: + claz = "scout"; + break; + case tf_soldier: + claz = "soldier"; + break; + case tf_pyro: + claz = "pyro"; + break; + case tf_demoman: + claz = "demo"; + break; + case tf_engineer: + claz = "engi"; + break; + case tf_heavy: + claz = "heavy"; + break; + case tf_medic: + claz = "med"; + break; + case tf_sniper: + claz = "sniper"; + break; + case tf_spy: + claz = "spy"; + break; + default: + break; + } + for (char i : name1) + { + if (iii == 2) + { + iii = 0; + tmp += i; + name2.push_back(tmp); + tmp = ""; + } + else if (iii < 2) + { + iii++; + tmp += i; + } + } + iii = 0; + for (char i : name1) + { + if (iii == 3) + { + iii = 0; + tmp += i; + name3.push_back(tmp2); + tmp2 = ""; + } + else if (iii < 3) + { + iii++; + tmp2 += i; + } + } + if (tmp.size() > 2) + name2.push_back(tmp); + if (tmp2.size() > 2) + name3.push_back(tmp2); + iii = 0; + std::vector res = { + "skid", "script", "cheat", "hak", "hac", "f1", + "hax", "vac", "ban", "lmao", "bot", "report", + "cat", "insta", "revv", "brass", "kick", claz + }; + for (auto i : name2) + { + boost::to_lower(i); + res.push_back(i); + } + for (auto i : name3) + { + boost::to_lower(i); + res.push_back(i); + } + std::string message2 = message; + boost::to_lower(message2); + boost::replace_all(message2, "4", "a"); + boost::replace_all(message2, "3", "e"); + boost::replace_all(message2, "0", "o"); + boost::replace_all(message2, "6", "g"); + boost::replace_all(message2, "5", "s"); + boost::replace_all(message2, "7", "t"); + for (auto filter : res) + { + if (retrun) + break; + if (boost::contains(message2, filter)) + { + + if (clear == "") + { + for (int i = 0; i < 100; i++) + clear += "\n"; + } + chat_stack::Say(". " + clear, true); + retrun = true; + lastfilter = filter.c_str(); + lastname = name.c_str(); + } + } + } + else if (data[0] != LOCAL_E->m_IDX) + { + std::string input = chat_filter.GetString(); + boost::to_lower(input); + std::string message2 = message; + std::vector result{}; + boost::split(result, input, boost::is_any_of(",")); + boost::replace_all(message2, "4", "a"); + boost::replace_all(message2, "3", "e"); + boost::replace_all(message2, "0", "o"); + boost::replace_all(message2, "6", "g"); + boost::replace_all(message2, "5", "s"); + boost::replace_all(message2, "7", "t"); + for (auto filter : result) + { + if (retrun) + break; + if (boost::contains(message2, filter)) + { + if (clear == "") + { + clear = ""; + for (int i = 0; i < 100; i++) + clear += "\n"; + } + chat_stack::Say(". " + clear, true); + retrun = true; + lastfilter = filter.c_str(); + lastname = name.c_str(); + } + } + } + } + if (crypt_chat) + { + if (firstcall) + chat_stack::Say("!!meow", false); + firstcall = false; + if (message.find("!!") == 0) + { + if (ucccccp::validate(message)) + { + CachedEntity *entity = ENTITY(data[0]); + if (CE_GOOD(entity)) + { + if (boost::algorithm::contains( + ucccccp::decrypt(message), "meow")) + { + player_info_s info; + g_IEngine->GetPlayerInfo(data[0], &info); + unsigned steamid = info.friendsID; + playerlist::AccessData(steamid).state = + playerlist::k_EState::CAT; + } + } + PrintChat("\x07%06X%s\x01: %s", 0xe05938, name.c_str(), + ucccccp::decrypt(message).c_str()); + } + } + } + chatlog::LogMessage(data[0], message); + buf = bf_read(data, s); + buf.Seek(0); + } + } + if (dispatch_log) + { + logging::Info("D> %i", type); + std::ostringstream str{}; + while (buf.GetNumBytesLeft()) + { + unsigned char byte = buf.ReadByte(); + str << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte) << ' '; + } + logging::Info("MESSAGE %d, DATA = [ %s ]", type, str.str().c_str()); + buf.Seek(0); + } + votelogger::user_message(buf, type); + return original(_this, type, buf); } + +void LevelInit_hook(void *_this, const char *newmap) +{ + static const LevelInit_t original = + (LevelInit_t) hooks::clientmode.GetMethod(offsets::LevelInit()); + playerlist::Save(); + g_IEngine->ClientCmd_Unrestricted("exec cat_matchexec"); + hacks::shared::aimbot::Reset(); + chat_stack::Reset(); + hacks::shared::anticheat::ResetEverything(); + original(_this, newmap); + hacks::shared::walkbot::OnLevelInit(); +#if ENABLE_IPC + if (ipc::peer) + { + ipc::peer->memory->peer_user_data[ipc::peer->client_id].ts_connected = + time(nullptr); + } +#endif +} + +void LevelShutdown_hook(void *_this) +{ + static const LevelShutdown_t original = + LevelShutdown_t(hooks::clientmode.GetMethod(offsets::LevelShutdown())); + need_name_change = true; + playerlist::Save(); + g_Settings.bInvalid = true; + hacks::shared::aimbot::Reset(); + chat_stack::Reset(); + hacks::shared::anticheat::ResetEverything(); + original(_this); +#if ENABLE_IPC + if (ipc::peer) + { + ipc::peer->memory->peer_user_data[ipc::peer->client_id] + .ts_disconnected = time(nullptr); + } +#endif +} + +int RandomInt_hook(void *_this, int iMinVal, int iMaxVal) +{ + static const RandomInt_t original = + RandomInt_t(hooks::vstd.GetMethod(offsets::RandomInt())); + + if (medal_flip && iMinVal == 0 && iMaxVal == 9) + return 0; + + return original(_this, iMinVal, iMaxVal); }