commit
c16971471e
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -13,3 +13,6 @@
|
|||||||
[submodule "ucccccp"]
|
[submodule "ucccccp"]
|
||||||
path = external/ucccccp
|
path = external/ucccccp
|
||||||
url = https://github.com/nullworks/ucccccp.git
|
url = https://github.com/nullworks/ucccccp.git
|
||||||
|
[submodule "external/co-library"]
|
||||||
|
path = external/co-library
|
||||||
|
url = https://github.com/nullworks/co-library.git
|
||||||
|
@ -12,6 +12,8 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${CMAKE_BUILD_TYPE_VALUES})
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
project(cathook VERSION 0.0.1)
|
project(cathook VERSION 0.0.1)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
add_library(cathook SHARED "")
|
add_library(cathook SHARED "")
|
||||||
|
|
||||||
set(GameSpecific 1 CACHE BOOL "Build for specific target game (As opposed to universal, but slower, lib)")
|
set(GameSpecific 1 CACHE BOOL "Build for specific target game (As opposed to universal, but slower, lib)")
|
||||||
@ -31,6 +33,7 @@ set(Textmode 0 CACHE BOOL "Various textmode-only features for bots")
|
|||||||
set(EnableTextmodeStdin 0 CACHE BOOL "Textmode Stdin -> Console bridge (EXPERIMENTAL)")
|
set(EnableTextmodeStdin 0 CACHE BOOL "Textmode Stdin -> Console bridge (EXPERIMENTAL)")
|
||||||
set(EnableWarnings 1 CACHE BOOL "Enable compile warnings")
|
set(EnableWarnings 1 CACHE BOOL "Enable compile warnings")
|
||||||
set(EnableNullGraphics 0 CACHE BOOL "Enable experimental textmode hooks (CRASHES)")
|
set(EnableNullGraphics 0 CACHE BOOL "Enable experimental textmode hooks (CRASHES)")
|
||||||
|
set(EnableOnlineFeatures 1 CACHE BOOL "Enable online features (WIP)")
|
||||||
|
|
||||||
if(NOT EnableVisuals)
|
if(NOT EnableVisuals)
|
||||||
set(EnableGUI 0)
|
set(EnableGUI 0)
|
||||||
@ -62,6 +65,11 @@ if(EnableIPC)
|
|||||||
target_link_libraries(cathook SimpleIPC)
|
target_link_libraries(cathook SimpleIPC)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(EnableOnlineFeatures)
|
||||||
|
add_subdirectory(external/co-library)
|
||||||
|
target_link_libraries(cathook co-library)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(EnableVisuals)
|
if(EnableVisuals)
|
||||||
add_subdirectory(external/libglez)
|
add_subdirectory(external/libglez)
|
||||||
target_include_directories(cathook PRIVATE include/visual)
|
target_include_directories(cathook PRIVATE include/visual)
|
||||||
|
1
external/co-library
vendored
Submodule
1
external/co-library
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 393948a6bf7ee4a62f5a3bd82d94c05d4bc405aa
|
@ -39,7 +39,8 @@ target_sources(cathook PRIVATE
|
|||||||
"${CMAKE_CURRENT_LIST_DIR}/velocity.hpp"
|
"${CMAKE_CURRENT_LIST_DIR}/velocity.hpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/votelogger.hpp"
|
"${CMAKE_CURRENT_LIST_DIR}/votelogger.hpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.hpp"
|
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.hpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/Options.hpp")
|
"${CMAKE_CURRENT_LIST_DIR}/Options.hpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/PlayerTools.hpp")
|
||||||
|
|
||||||
target_include_directories(cathook PRIVATE "${CMAKE_CURRENT_LIST_DIR}")
|
target_include_directories(cathook PRIVATE "${CMAKE_CURRENT_LIST_DIR}")
|
||||||
|
|
||||||
@ -50,6 +51,7 @@ add_subdirectory(hacks)
|
|||||||
add_subdirectory(hooks)
|
add_subdirectory(hooks)
|
||||||
add_subdirectory(reclasses)
|
add_subdirectory(reclasses)
|
||||||
add_subdirectory(sdk)
|
add_subdirectory(sdk)
|
||||||
|
add_subdirectory(online)
|
||||||
|
|
||||||
if(EnableVisuals)
|
if(EnableVisuals)
|
||||||
add_subdirectory(visual)
|
add_subdirectory(visual)
|
||||||
|
44
include/PlayerTools.hpp
Normal file
44
include/PlayerTools.hpp
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
Created on 23.06.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
#include <colors.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class CachedEntity;
|
||||||
|
|
||||||
|
namespace player_tools
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class IgnoreReason
|
||||||
|
{
|
||||||
|
DO_NOT_IGNORE,
|
||||||
|
IS_HOOVY,
|
||||||
|
IS_TAUNTING,
|
||||||
|
LOCAL_PLAYER_LIST,
|
||||||
|
ONLINE_NO_TARGET,
|
||||||
|
ONLINE_FRIENDLY_SOFTWARE,
|
||||||
|
DEVELOPER,
|
||||||
|
OTHER
|
||||||
|
};
|
||||||
|
|
||||||
|
IgnoreReason shouldTargetSteamId(unsigned id);
|
||||||
|
IgnoreReason shouldTarget(CachedEntity *player);
|
||||||
|
|
||||||
|
bool shouldAlwaysRenderEspSteamId(unsigned id);
|
||||||
|
bool shouldAlwaysRenderEsp(CachedEntity *entity);
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
std::optional<colors::rgba_t> forceEspColorSteamId(unsigned id);
|
||||||
|
std::optional<colors::rgba_t> forceEspColor(CachedEntity *entity);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void onKilledBy(CachedEntity *entity);
|
||||||
|
|
||||||
|
}
|
@ -23,7 +23,6 @@ extern ConVar *cl_interpolate;
|
|||||||
|
|
||||||
extern CatVar event_log;
|
extern CatVar event_log;
|
||||||
extern CatVar cathook; // Master switch
|
extern CatVar cathook; // Master switch
|
||||||
extern CatVar ignore_taunting;
|
|
||||||
extern bool *bSendPackets;
|
extern bool *bSendPackets;
|
||||||
extern CatVar show_antiaim;
|
extern CatVar show_antiaim;
|
||||||
extern CatVar force_thirdperson;
|
extern CatVar force_thirdperson;
|
||||||
|
@ -7,5 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class CachedEntity;
|
||||||
|
|
||||||
void UpdateHoovyList();
|
void UpdateHoovyList();
|
||||||
bool IsHoovy(CachedEntity *entity);
|
bool IsHoovy(CachedEntity *entity);
|
||||||
|
2
include/online/CMakeLists.txt
Normal file
2
include/online/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target_sources(cathook PRIVATE
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/Online.hpp")
|
40
include/online/Online.hpp
Normal file
40
include/online/Online.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
Created on 23.06.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <colors.hpp>
|
||||||
|
#include <config.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace online
|
||||||
|
{
|
||||||
|
|
||||||
|
struct user_data
|
||||||
|
{
|
||||||
|
bool is_anonymous{ false };
|
||||||
|
bool is_using_friendly_software{ false };
|
||||||
|
bool is_steamid_verified{ false };
|
||||||
|
std::string username{};
|
||||||
|
std::vector<std::string> shown_groups{};
|
||||||
|
std::string software_name{};
|
||||||
|
bool has_software{ false };
|
||||||
|
bool no_target{ false };
|
||||||
|
bool is_developer{};
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
bool has_color{ false };
|
||||||
|
colors::rgba_t color{};
|
||||||
|
bool rainbow{ false };
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Identify unidentified users, send online status, etc
|
||||||
|
*/
|
||||||
|
void update();
|
||||||
|
|
||||||
|
user_data *getUserData(unsigned steamId);
|
||||||
|
|
||||||
|
}
|
@ -36,7 +36,7 @@ public:
|
|||||||
void SetEntityColor(CachedEntity *ent, rgba_t color);
|
void SetEntityColor(CachedEntity *ent, rgba_t color);
|
||||||
rgba_t ChamsColor(IClientEntity *entity);
|
rgba_t ChamsColor(IClientEntity *entity);
|
||||||
bool ShouldRenderChams(IClientEntity *entity);
|
bool ShouldRenderChams(IClientEntity *entity);
|
||||||
void RenderChams(int idx);
|
void RenderChams(IClientEntity *entity);
|
||||||
void BeginRenderChams();
|
void BeginRenderChams();
|
||||||
void EndRenderChams();
|
void EndRenderChams();
|
||||||
void RenderChamsRecursive(IClientEntity *entity);
|
void RenderChamsRecursive(IClientEntity *entity);
|
||||||
|
@ -48,6 +48,8 @@ struct rgba_t
|
|||||||
constexpr rgba_t(float _r, float _g, float _b, float _a = 1.0f)
|
constexpr rgba_t(float _r, float _g, float _b, float _a = 1.0f)
|
||||||
: r(_r), g(_g), b(_b), a(_a){};
|
: r(_r), g(_g), b(_b), a(_a){};
|
||||||
|
|
||||||
|
explicit rgba_t(const char hex[6]);
|
||||||
|
|
||||||
constexpr operator glez::rgba() const
|
constexpr operator glez::rgba() const
|
||||||
{
|
{
|
||||||
return *reinterpret_cast<const glez::rgba *>(this);
|
return *reinterpret_cast<const glez::rgba *>(this);
|
||||||
@ -76,6 +78,16 @@ struct rgba_t
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr bool operator==(const rgba_t& lhs, const rgba_t& rhs)
|
||||||
|
{
|
||||||
|
return rhs.r == lhs.r && rhs.g == lhs.g && rhs.b == lhs.b && rhs.a == lhs.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator!=(const rgba_t& lhs, const rgba_t& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
constexpr rgba_t FromRGBA8(float r, float g, float b, float a)
|
constexpr rgba_t FromRGBA8(float r, float g, float b, float a)
|
||||||
{
|
{
|
||||||
return rgba_t{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
|
return rgba_t{ r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
|
||||||
|
@ -32,7 +32,8 @@ target_sources(cathook PRIVATE
|
|||||||
"${CMAKE_CURRENT_LIST_DIR}/velocity.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/velocity.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/votelogger.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/votelogger.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.cpp"
|
"${CMAKE_CURRENT_LIST_DIR}/MiscTemporary.cpp"
|
||||||
"${CMAKE_CURRENT_LIST_DIR}/Options.cpp")
|
"${CMAKE_CURRENT_LIST_DIR}/Options.cpp"
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/PlayerTools.cpp")
|
||||||
|
|
||||||
add_subdirectory(core)
|
add_subdirectory(core)
|
||||||
add_subdirectory(classinfo)
|
add_subdirectory(classinfo)
|
||||||
@ -41,6 +42,7 @@ add_subdirectory(hacks)
|
|||||||
add_subdirectory(hooks)
|
add_subdirectory(hooks)
|
||||||
add_subdirectory(reclasses)
|
add_subdirectory(reclasses)
|
||||||
add_subdirectory(sdk)
|
add_subdirectory(sdk)
|
||||||
|
add_subdirectory(online)
|
||||||
|
|
||||||
if(EnableVisuals)
|
if(EnableVisuals)
|
||||||
add_subdirectory(visual)
|
add_subdirectory(visual)
|
||||||
|
161
src/PlayerTools.cpp
Normal file
161
src/PlayerTools.cpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
Created on 23.06.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <core/cvwrapper.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <hoovy.hpp>
|
||||||
|
#include <playerlist.hpp>
|
||||||
|
#include <online/Online.hpp>
|
||||||
|
#include "PlayerTools.hpp"
|
||||||
|
#include "entitycache.hpp"
|
||||||
|
|
||||||
|
static std::unordered_map<unsigned, unsigned> betrayal_list{};
|
||||||
|
|
||||||
|
static CatCommand forgive_all("pt_forgive_all", "Clear betrayal list", []() {
|
||||||
|
betrayal_list.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
namespace settings
|
||||||
|
{
|
||||||
|
|
||||||
|
static CatVar online_notarget(CV_SWITCH, "pt_ignore_notarget", "1", "Ignore notarget", "Ignore online players with notarget role");
|
||||||
|
static CatVar hoovy(CV_SWITCH, "pt_ignore_hoovy", "1", "Ignore hoovy");
|
||||||
|
static CatVar online_friendly_software(CV_SWITCH, "pt_ignore_friendly_software", "1", "Ignore friendly software", "Ignore CO-compatible software");
|
||||||
|
static CatVar online_only_verified(CV_SWITCH, "pt_ignore_only_verified", "0", "Only ignore verified", "If online checks are enabled, only apply ignore if SteamID is verified (not recommended right now)");
|
||||||
|
static CatVar online_anonymous(CV_SWITCH, "pt_ignore_anonymous", "1", "Ignore anonymous", "Apply ignore checks to anonymous accounts too");
|
||||||
|
static CatVar betrayal_limit(CV_INT, "pt_betrayal_limit", "3", "Betrayal limit", "Stop ignoring a player after N kills while you ignored them");
|
||||||
|
static CatVar taunting(CV_SWITCH, "pt_ignore_taunting", "1", "Ignore taunting", "Don't shoot taunting players");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace player_tools
|
||||||
|
{
|
||||||
|
|
||||||
|
IgnoreReason shouldTargetSteamId(unsigned id)
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
return IgnoreReason::DO_NOT_IGNORE;
|
||||||
|
|
||||||
|
if (settings::betrayal_limit)
|
||||||
|
{
|
||||||
|
if (betrayal_list[id] > int(settings::betrayal_limit))
|
||||||
|
return IgnoreReason::DO_NOT_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& pl = playerlist::AccessData(id);
|
||||||
|
if (playerlist::IsFriendly(pl.state))
|
||||||
|
return IgnoreReason::LOCAL_PLAYER_LIST;
|
||||||
|
|
||||||
|
auto *co = online::getUserData(id);
|
||||||
|
if (co)
|
||||||
|
{
|
||||||
|
bool check_verified = !settings::online_only_verified || co->is_steamid_verified;
|
||||||
|
bool check_anonymous = settings::online_anonymous || !co->is_anonymous;
|
||||||
|
|
||||||
|
if (check_verified && check_anonymous)
|
||||||
|
{
|
||||||
|
if (settings::online_notarget && co->no_target)
|
||||||
|
return IgnoreReason::ONLINE_NO_TARGET;
|
||||||
|
if (settings::online_friendly_software && co->is_using_friendly_software)
|
||||||
|
return IgnoreReason::ONLINE_FRIENDLY_SOFTWARE;
|
||||||
|
}
|
||||||
|
// Always check developer status, no exceptions
|
||||||
|
if (co->is_developer)
|
||||||
|
return IgnoreReason::DEVELOPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IgnoreReason::DO_NOT_IGNORE;
|
||||||
|
}
|
||||||
|
IgnoreReason shouldTarget(CachedEntity *entity)
|
||||||
|
{
|
||||||
|
if (entity->m_Type() == ENTITY_PLAYER)
|
||||||
|
{
|
||||||
|
if (settings::hoovy && IsHoovy(entity))
|
||||||
|
return IgnoreReason::IS_HOOVY;
|
||||||
|
if (settings::taunting && HasCondition<TFCond_Taunting>(entity))
|
||||||
|
return IgnoreReason::IS_TAUNTING;
|
||||||
|
|
||||||
|
return shouldTargetSteamId(entity->player_info.friendsID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IgnoreReason::DO_NOT_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldAlwaysRenderEspSteamId(unsigned id)
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto& pl = playerlist::AccessData(id);
|
||||||
|
if (pl.state != playerlist::k_EState::DEFAULT)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto *co = online::getUserData(id);
|
||||||
|
if (co)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool shouldAlwaysRenderEsp(CachedEntity *entity)
|
||||||
|
{
|
||||||
|
if (entity->m_Type() == ENTITY_PLAYER)
|
||||||
|
{
|
||||||
|
return shouldAlwaysRenderEspSteamId(entity->player_info.friendsID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
std::optional<colors::rgba_t> forceEspColorSteamId(unsigned id)
|
||||||
|
{
|
||||||
|
if (id == 0)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto pl = playerlist::Color(id);
|
||||||
|
if (pl != colors::empty)
|
||||||
|
return std::optional<colors::rgba_t>{ pl };
|
||||||
|
|
||||||
|
auto *co = online::getUserData(id);
|
||||||
|
if (co)
|
||||||
|
{
|
||||||
|
if (co->has_color)
|
||||||
|
return std::optional<colors::rgba_t>{ co->color };
|
||||||
|
if (co->rainbow)
|
||||||
|
return std::optional<colors::rgba_t>{ colors::RainbowCurrent() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
std::optional<colors::rgba_t> forceEspColor(CachedEntity *entity)
|
||||||
|
{
|
||||||
|
if (entity->m_Type() == ENTITY_PLAYER)
|
||||||
|
{
|
||||||
|
return forceEspColorSteamId(entity->player_info.friendsID);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void onKilledBy(unsigned id)
|
||||||
|
{
|
||||||
|
auto reason = shouldTargetSteamId(id);
|
||||||
|
if (reason != IgnoreReason::DO_NOT_IGNORE)
|
||||||
|
{
|
||||||
|
// We ignored the gamer, but they still shot us
|
||||||
|
if (betrayal_list.find(id) == betrayal_list.end())
|
||||||
|
betrayal_list[id] = 0;
|
||||||
|
betrayal_list[id]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onKilledBy(CachedEntity *entity)
|
||||||
|
{
|
||||||
|
onKilledBy(entity->player_info.friendsID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -33,8 +33,6 @@ CatVar force_name(CV_STRING, "name", "", "Force name");
|
|||||||
CatVar
|
CatVar
|
||||||
cathook(CV_SWITCH, "enabled", "1", "CatHook enabled",
|
cathook(CV_SWITCH, "enabled", "1", "CatHook enabled",
|
||||||
"Disabling this completely disables cathook (can be re-enabled)");
|
"Disabling this completely disables cathook (can be re-enabled)");
|
||||||
CatVar ignore_taunting(CV_SWITCH, "ignore_taunting", "1", "Ignore taunting",
|
|
||||||
"Aimbot/Triggerbot won't attack taunting enemies");
|
|
||||||
// CatVar send_packets(CV_SWITCH, "sendpackets", "1", "Send packets", "Internal
|
// CatVar send_packets(CV_SWITCH, "sendpackets", "1", "Send packets", "Internal
|
||||||
// use");
|
// use");
|
||||||
CatVar show_antiaim(CV_SWITCH, "thirdperson_angles", "1", "Real TP angles",
|
CatVar show_antiaim(CV_SWITCH, "thirdperson_angles", "1", "Real TP angles",
|
||||||
|
13
src/hack.cpp
13
src/hack.cpp
@ -532,6 +532,13 @@ free(logname);*/
|
|||||||
#if ENABLE_VISUALS
|
#if ENABLE_VISUALS
|
||||||
hacks::shared::esp::Init();
|
hacks::shared::esp::Init();
|
||||||
#endif
|
#endif
|
||||||
|
#if not ENABLE_VISUALS
|
||||||
|
hack::command_stack().push("exec cat_autoexec_textmode");
|
||||||
|
#endif
|
||||||
|
hack::command_stack().push("exec cat_autoexec");
|
||||||
|
hack::command_stack().push("cat_killsay_reload");
|
||||||
|
hack::command_stack().push("cat_spam_reload");
|
||||||
|
|
||||||
logging::Info("Clearing initializer stack");
|
logging::Info("Clearing initializer stack");
|
||||||
while (!init_stack().empty())
|
while (!init_stack().empty())
|
||||||
{
|
{
|
||||||
@ -540,12 +547,6 @@ free(logname);*/
|
|||||||
}
|
}
|
||||||
logging::Info("Initializer stack done");
|
logging::Info("Initializer stack done");
|
||||||
|
|
||||||
#if not ENABLE_VISUALS
|
|
||||||
hack::command_stack().push("exec cat_autoexec_textmode");
|
|
||||||
#endif
|
|
||||||
hack::command_stack().push("exec cat_autoexec");
|
|
||||||
hack::command_stack().push("cat_killsay_reload");
|
|
||||||
hack::command_stack().push("cat_spam_reload");
|
|
||||||
hack::initialized = true;
|
hack::initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <hacks/AntiAim.hpp>
|
#include <hacks/AntiAim.hpp>
|
||||||
#include <hacks/ESP.hpp>
|
#include <hacks/ESP.hpp>
|
||||||
#include <glez/draw.hpp>
|
#include <glez/draw.hpp>
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
namespace hacks::shared::aimbot
|
namespace hacks::shared::aimbot
|
||||||
@ -60,8 +61,6 @@ static CatVar
|
|||||||
static CatVar ignore_vaccinator(
|
static CatVar ignore_vaccinator(
|
||||||
CV_SWITCH, "aimbot_ignore_vaccinator", "1", "Ignore Vaccinator",
|
CV_SWITCH, "aimbot_ignore_vaccinator", "1", "Ignore Vaccinator",
|
||||||
"Hitscan weapons won't fire if enemy is vaccinated against bullets");
|
"Hitscan weapons won't fire if enemy is vaccinated against bullets");
|
||||||
static CatVar ignore_hoovy(CV_SWITCH, "aimbot_ignore_hoovy", "0",
|
|
||||||
"Ignore Hoovies", "Aimbot won't attack hoovies");
|
|
||||||
static CatVar ignore_cloak(CV_SWITCH, "aimbot_ignore_cloak", "1",
|
static CatVar ignore_cloak(CV_SWITCH, "aimbot_ignore_cloak", "1",
|
||||||
"Ignore cloaked", "Don't aim at invisible enemies");
|
"Ignore cloaked", "Don't aim at invisible enemies");
|
||||||
static CatVar ignore_deadringer(CV_SWITCH, "aimbot_ignore_deadringer", "1",
|
static CatVar ignore_deadringer(CV_SWITCH, "aimbot_ignore_deadringer", "1",
|
||||||
@ -574,8 +573,10 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taunting
|
// Some global checks
|
||||||
if (ignore_taunting && HasCondition<TFCond_Taunting>(entity))
|
if (player_tools::shouldTarget(entity) != player_tools::IgnoreReason::DO_NOT_IGNORE)
|
||||||
|
return false;
|
||||||
|
if (hacks::shared::catbot::should_ignore_player(entity))
|
||||||
return false;
|
return false;
|
||||||
// Invulnerable players, ex: uber, bonk
|
// Invulnerable players, ex: uber, bonk
|
||||||
if (IsPlayerInvulnerable(entity))
|
if (IsPlayerInvulnerable(entity))
|
||||||
@ -605,19 +606,7 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
HasCondition<TFCond_UberBulletResist>(entity))
|
HasCondition<TFCond_UberBulletResist>(entity))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Friendly player
|
|
||||||
if (playerlist::IsFriendly(playerlist::AccessData(entity).state))
|
|
||||||
return false;
|
|
||||||
IF_GAME(IsTF())
|
|
||||||
{
|
|
||||||
// Hoovys
|
|
||||||
if (ignore_hoovy && IsHoovy(entity))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hacks::shared::catbot::should_ignore_player(entity))
|
|
||||||
return false;
|
|
||||||
// Preform hitbox prediction
|
// Preform hitbox prediction
|
||||||
int hitbox = BestHitbox(entity);
|
int hitbox = BestHitbox(entity);
|
||||||
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
AimbotCalculatedData_s &cd = calculated_data_array[entity->m_IDX];
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
* Author: nullifiedcat & Lighty
|
* Author: nullifiedcat & Lighty
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
namespace hacks::tf::autodetonator
|
namespace hacks::tf::autodetonator
|
||||||
@ -53,19 +54,16 @@ bool IsTarget(CachedEntity *ent)
|
|||||||
// Dont detonate on dead players
|
// Dont detonate on dead players
|
||||||
if (!ent->m_bAlivePlayer())
|
if (!ent->m_bAlivePlayer())
|
||||||
return false;
|
return false;
|
||||||
// Dont detonate on friendly players
|
|
||||||
if (playerlist::IsFriendly(playerlist::AccessData(ent).state))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
// Global checks
|
||||||
|
if (player_tools::shouldTarget(ent) != player_tools::IgnoreReason::DO_NOT_IGNORE)
|
||||||
|
return false;
|
||||||
IF_GAME(IsTF())
|
IF_GAME(IsTF())
|
||||||
{
|
{
|
||||||
// Dont target invulnerable players, ex: uber, bonk
|
// Dont target invulnerable players, ex: uber, bonk
|
||||||
if (IsPlayerInvulnerable(ent))
|
if (IsPlayerInvulnerable(ent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// If settings allow, ignore taunting players
|
|
||||||
if (ignore_taunting && HasCondition<TFCond_Taunting>(ent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// If settings allow, dont target cloaked players
|
// If settings allow, dont target cloaked players
|
||||||
if (legit && IsPlayerInvisible(ent))
|
if (legit && IsPlayerInvisible(ent))
|
||||||
|
@ -24,7 +24,7 @@ int m_iNewTarget{ 0 };
|
|||||||
static CatVar pop_uber_auto(CV_SWITCH, "autoheal_uber", "1", "AutoUber",
|
static CatVar pop_uber_auto(CV_SWITCH, "autoheal_uber", "1", "AutoUber",
|
||||||
"Use ubercharge automatically");
|
"Use ubercharge automatically");
|
||||||
static CatVar
|
static CatVar
|
||||||
pop_uber_percent(CV_FLOAT, "autoheal_uber_health", "30",
|
pop_uber_percent(CV_FLOAT, "autoheal_uber_health", "0",
|
||||||
"Pop uber if health% <",
|
"Pop uber if health% <",
|
||||||
"When under a percentage of health, use ubercharge");
|
"When under a percentage of health, use ubercharge");
|
||||||
static CatVar share_uber(
|
static CatVar share_uber(
|
||||||
@ -351,9 +351,17 @@ bool IsPopped()
|
|||||||
bool ShouldChargePlayer(int idx)
|
bool ShouldChargePlayer(int idx)
|
||||||
{
|
{
|
||||||
CachedEntity *target = ENTITY(idx);
|
CachedEntity *target = ENTITY(idx);
|
||||||
|
const int health = target->m_iHealth();
|
||||||
|
if (float(pop_uber_percent) > 0)
|
||||||
|
{
|
||||||
|
const float pophealth = target->m_iMaxHealth() * (float(pop_uber_percent) / 100);
|
||||||
|
if (health < pophealth)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
const float damage_accum_duration =
|
const float damage_accum_duration =
|
||||||
g_GlobalVars->curtime - data[idx].accum_damage_start;
|
g_GlobalVars->curtime - data[idx].accum_damage_start;
|
||||||
const int health = target->m_iHealth();
|
|
||||||
if (!data[idx].accum_damage_start)
|
if (!data[idx].accum_damage_start)
|
||||||
return false;
|
return false;
|
||||||
if (health > 30 && data[idx].accum_damage < 45)
|
if (health > 30 && data[idx].accum_damage < 45)
|
||||||
@ -366,6 +374,8 @@ bool ShouldChargePlayer(int idx)
|
|||||||
if (health < 30 && data[idx].accum_damage > 10)
|
if (health < 30 && data[idx].accum_damage > 10)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShouldPop()
|
bool ShouldPop()
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include <hacks/AutoSticky.hpp>
|
#include <hacks/AutoSticky.hpp>
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
|
|
||||||
namespace hacks::tf::autosticky
|
namespace hacks::tf::autosticky
|
||||||
{
|
{
|
||||||
@ -58,8 +59,9 @@ bool IsTarget(CachedEntity *ent)
|
|||||||
// Dont detonate on dead players
|
// Dont detonate on dead players
|
||||||
if (!ent->m_bAlivePlayer())
|
if (!ent->m_bAlivePlayer())
|
||||||
return false;
|
return false;
|
||||||
// Dont detonate on friendly players
|
|
||||||
if (playerlist::IsFriendly(playerlist::AccessData(ent).state))
|
// Global checks
|
||||||
|
if (player_tools::shouldTarget(ent) != player_tools::IgnoreReason::DO_NOT_IGNORE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
IF_GAME(IsTF())
|
IF_GAME(IsTF())
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include <hacks/ESP.hpp>
|
#include <hacks/ESP.hpp>
|
||||||
#include <glez/draw.hpp>
|
#include <glez/draw.hpp>
|
||||||
|
#include <online/Online.hpp>
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
namespace hacks::shared::esp
|
namespace hacks::shared::esp
|
||||||
@ -146,6 +148,11 @@ static CatVar entity_model(CV_SWITCH, "esp_model_name", "0", "Model name ESP",
|
|||||||
static CatVar entity_id(CV_SWITCH, "esp_entity_id", "1", "Entity ID",
|
static CatVar entity_id(CV_SWITCH, "esp_entity_id", "1", "Entity ID",
|
||||||
"Used with Entity ESP. Shows entityID");
|
"Used with Entity ESP. Shows entityID");
|
||||||
|
|
||||||
|
// Online
|
||||||
|
static CatVar online(CV_SWITCH, "esp_online", "1", "Show online info", "Username, etc");
|
||||||
|
static CatVar online_groups(CV_SWITCH, "esp_online_groups", "1", "Show online groups", "Admin, developer, etc");
|
||||||
|
static CatVar online_software(CV_SWITCH, "esp_online_software", "1", "Show software", "cathook, lmaobox, etc");
|
||||||
|
|
||||||
// CatVar draw_hitbox(CV_SWITCH, "esp_hitbox", "1", "Draw Hitbox");
|
// CatVar draw_hitbox(CV_SWITCH, "esp_hitbox", "1", "Draw Hitbox");
|
||||||
|
|
||||||
// Unknown
|
// Unknown
|
||||||
@ -1153,10 +1160,12 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
|
|||||||
if (!g_IEngine->GetPlayerInfo(ent->m_IDX, &info))
|
if (!g_IEngine->GetPlayerInfo(ent->m_IDX, &info))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
online::user_data *data = online ? online::getUserData(info.friendsID) : nullptr;
|
||||||
|
|
||||||
// TODO, check if u can just use "ent->m_bEnemy()" instead of m_iTeam
|
// TODO, check if u can just use "ent->m_bEnemy()" instead of m_iTeam
|
||||||
// Legit mode handling
|
// Legit mode handling
|
||||||
if (legit && ent->m_iTeam() != g_pLocalPlayer->team &&
|
if (legit && ent->m_iTeam() != g_pLocalPlayer->team &&
|
||||||
playerlist::IsDefault(info.friendsID))
|
playerlist::IsDefault(info.friendsID) && !(data))
|
||||||
{
|
{
|
||||||
if (IsPlayerInvisible(ent))
|
if (IsPlayerInvisible(ent))
|
||||||
return; // Invis check
|
return; // Invis check
|
||||||
@ -1168,6 +1177,23 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
|
|||||||
// return;
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data)
|
||||||
|
{
|
||||||
|
AddEntityString(ent, "CO: " + data->username, colors::yellow);
|
||||||
|
if (data->is_steamid_verified)
|
||||||
|
AddEntityString(ent, "Verified SteamID", colors::green);
|
||||||
|
if (online_groups)
|
||||||
|
for (auto& s: data->shown_groups)
|
||||||
|
AddEntityString(ent, s, colors::orange);
|
||||||
|
if (online_software && data->has_software)
|
||||||
|
{
|
||||||
|
if (data->is_using_friendly_software)
|
||||||
|
AddEntityString(ent, "Software: " + data->software_name);
|
||||||
|
else
|
||||||
|
AddEntityString(ent, "Software: " + data->software_name, colors::red);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Powerup handling
|
// Powerup handling
|
||||||
if (powerup_esp)
|
if (powerup_esp)
|
||||||
{
|
{
|
||||||
@ -1177,8 +1203,7 @@ void _FASTCALL ProcessEntity(CachedEntity *ent)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dont understand reasoning for this check
|
// Dont understand reasoning for this check
|
||||||
if (ent->m_bEnemy() || teammates ||
|
if (ent->m_bEnemy() || teammates || player_tools::shouldAlwaysRenderEsp(ent))
|
||||||
!playerlist::IsDefault(info.friendsID))
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// Playername
|
// Playername
|
||||||
|
@ -23,7 +23,7 @@ static CatVar draw_crumb(CV_SWITCH, "fb_draw", "1", "Draw crumbs",
|
|||||||
"Self explanitory");
|
"Self explanitory");
|
||||||
static CatVar follow_distance(CV_INT, "fb_distance", "175", "Follow Distance",
|
static CatVar follow_distance(CV_INT, "fb_distance", "175", "Follow Distance",
|
||||||
"How close the bots should stay to the target");
|
"How close the bots should stay to the target");
|
||||||
static CatVar follow_activation(CV_INT, "fb_activation", "175",
|
static CatVar follow_activation(CV_INT, "fb_activation", "1000",
|
||||||
"Activation Distance",
|
"Activation Distance",
|
||||||
"How close a player should be until the "
|
"How close a player should be until the "
|
||||||
"followbot will pick them as a target");
|
"followbot will pick them as a target");
|
||||||
@ -45,14 +45,49 @@ static CatVar always_medigun(CV_SWITCH, "fb_always_medigun", "0",
|
|||||||
"Always Medigun", "Always use medigun");
|
"Always Medigun", "Always use medigun");
|
||||||
static CatVar sync_taunt(CV_SWITCH, "fb_sync_taunt", "0", "Synced taunt",
|
static CatVar sync_taunt(CV_SWITCH, "fb_sync_taunt", "0", "Synced taunt",
|
||||||
"Taunt when follow target does");
|
"Taunt when follow target does");
|
||||||
static CatVar change(CV_SWITCH, "fb_switch", "1", "Change followbot target",
|
static CatVar change(CV_SWITCH, "fb_switch", "0", "Change followbot target",
|
||||||
"Always change roaming target when possible");
|
"Always change roaming target when possible");
|
||||||
|
static CatVar autojump(CV_SWITCH, "fb_autojump", "1", "Autojump",
|
||||||
|
"Automatically jump if stuck");
|
||||||
|
static CatVar afk(CV_SWITCH, "fb_afk", "1", "Switch target if AFK",
|
||||||
|
"Automatically switch target if the target is afk");
|
||||||
|
static CatVar afktime(
|
||||||
|
CV_INT, "fb_afk_time", "15000", "Max AFK Time",
|
||||||
|
"Max time in ms spent standing still before player gets declared afk");
|
||||||
|
|
||||||
// Something to store breadcrumbs created by followed players
|
// Something to store breadcrumbs created by followed players
|
||||||
static std::vector<Vector> breadcrumbs;
|
static std::vector<Vector> breadcrumbs;
|
||||||
static const int crumb_limit = 64; // limit
|
static const int crumb_limit = 64; // limit
|
||||||
|
|
||||||
// Followed entity, externed for highlight color
|
// Followed entity, externed for highlight color
|
||||||
int follow_target = 0;
|
int follow_target = 0;
|
||||||
|
bool inited;
|
||||||
|
|
||||||
|
Timer lastTaunt{}; //time since taunt was last executed, used to avoid kicks
|
||||||
|
std::array<Timer, 32> afkTicks; //for how many ms the player hasn't been moving
|
||||||
|
|
||||||
|
void checkAFK()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < g_GlobalVars->maxClients; i++)
|
||||||
|
{
|
||||||
|
auto entity = ENTITY(i);
|
||||||
|
if (CE_BAD(entity))
|
||||||
|
continue;
|
||||||
|
if (!CE_VECTOR(entity, netvar.vVelocity).IsZero(5.0f))
|
||||||
|
{
|
||||||
|
afkTicks[i].update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
for (int i; i < afkTicks.size(); i++)
|
||||||
|
{
|
||||||
|
afkTicks[i].update();
|
||||||
|
}
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
|
||||||
void WorldTick()
|
void WorldTick()
|
||||||
{
|
{
|
||||||
@ -61,6 +96,8 @@ void WorldTick()
|
|||||||
follow_target = 0;
|
follow_target = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!inited)
|
||||||
|
init();
|
||||||
|
|
||||||
// We need a local player to control
|
// We need a local player to control
|
||||||
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
if (CE_BAD(LOCAL_E) || !LOCAL_E->m_bAlivePlayer())
|
||||||
@ -69,6 +106,9 @@ void WorldTick()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (afk)
|
||||||
|
checkAFK();
|
||||||
|
|
||||||
// Still good check
|
// Still good check
|
||||||
if (follow_target)
|
if (follow_target)
|
||||||
{
|
{
|
||||||
@ -122,6 +162,12 @@ void WorldTick()
|
|||||||
continue;
|
continue;
|
||||||
if (entity == LOCAL_E) // Follow self lol
|
if (entity == LOCAL_E) // Follow self lol
|
||||||
continue;
|
continue;
|
||||||
|
if (entity->m_bEnemy())
|
||||||
|
continue;
|
||||||
|
if (afk && afkTicks[i].check(int(afktime))) //don't follow target that was determined afk
|
||||||
|
continue;
|
||||||
|
if (IsPlayerDisguised(entity) || IsPlayerInvisible(entity))
|
||||||
|
continue;
|
||||||
if (!entity->m_bAlivePlayer()) // Dont follow dead players
|
if (!entity->m_bAlivePlayer()) // Dont follow dead players
|
||||||
continue;
|
continue;
|
||||||
if (follow_activation &&
|
if (follow_activation &&
|
||||||
@ -148,29 +194,51 @@ void WorldTick()
|
|||||||
entity->m_flDistance()) // favor closer entitys
|
entity->m_flDistance()) // favor closer entitys
|
||||||
continue;
|
continue;
|
||||||
// ooooo, a target
|
// ooooo, a target
|
||||||
follow_target = entity->m_IDX;
|
follow_target = i;
|
||||||
|
afkTicks[i].update(); //set afk time to 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// last check for entity before we continue
|
// last check for entity before we continue
|
||||||
if (!follow_target)
|
if (!follow_target)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the player is close enough, we dont need to follow the path
|
|
||||||
CachedEntity *followtar = ENTITY(follow_target);
|
CachedEntity *followtar = ENTITY(follow_target);
|
||||||
// wtf is this needed
|
// wtf is this needed
|
||||||
if (CE_BAD(followtar))
|
if (CE_BAD(followtar))
|
||||||
return;
|
return;
|
||||||
auto tar_orig = followtar->m_vecOrigin();
|
// Check if we are following a disguised/spy
|
||||||
auto loc_orig = LOCAL_E->m_vecOrigin();
|
if (IsPlayerDisguised(followtar) || IsPlayerInvisible(followtar))
|
||||||
auto dist_to_target = loc_orig.DistTo(tar_orig);
|
{
|
||||||
if (dist_to_target < 30)
|
follow_target = 0;
|
||||||
breadcrumbs.clear();
|
return;
|
||||||
|
}
|
||||||
|
//check if target is afk
|
||||||
|
if (afk)
|
||||||
|
{
|
||||||
|
if (afkTicks[follow_target].check(int(afktime)))
|
||||||
|
{
|
||||||
|
follow_target = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Update timer on new target
|
// Update timer on new target
|
||||||
static Timer idle_time{};
|
static Timer idle_time{};
|
||||||
if (breadcrumbs.empty())
|
if (breadcrumbs.empty())
|
||||||
idle_time.update();
|
idle_time.update();
|
||||||
|
|
||||||
|
// If the player is close enough, we dont need to follow the path
|
||||||
|
auto tar_orig = followtar->m_vecOrigin();
|
||||||
|
auto loc_orig = LOCAL_E->m_vecOrigin();
|
||||||
|
auto dist_to_target = loc_orig.DistTo(tar_orig);
|
||||||
|
|
||||||
|
if ((dist_to_target < (float) follow_distance) &&
|
||||||
|
VisCheckEntFromEnt(LOCAL_E, followtar))
|
||||||
|
{
|
||||||
|
idle_time.update();
|
||||||
|
}
|
||||||
|
|
||||||
// New crumbs, we add one if its empty so we have something to follow
|
// New crumbs, we add one if its empty so we have something to follow
|
||||||
if ((breadcrumbs.empty() ||
|
if ((breadcrumbs.empty() ||
|
||||||
tar_orig.DistTo(breadcrumbs.at(breadcrumbs.size() - 1)) > 40.0F) &&
|
tar_orig.DistTo(breadcrumbs.at(breadcrumbs.size() - 1)) > 40.0F) &&
|
||||||
@ -179,26 +247,32 @@ void WorldTick()
|
|||||||
|
|
||||||
// Prune old and close crumbs that we wont need anymore, update idle timer
|
// Prune old and close crumbs that we wont need anymore, update idle timer
|
||||||
// too
|
// too
|
||||||
while (breadcrumbs.size() > 1 && loc_orig.DistTo(breadcrumbs.at(0)) < 60.f)
|
for (int i = 0; i < breadcrumbs.size(); i++)
|
||||||
{
|
{
|
||||||
idle_time.update();
|
if (loc_orig.DistTo(breadcrumbs.at(i)) < 60.f)
|
||||||
breadcrumbs.erase(breadcrumbs.begin());
|
{
|
||||||
|
idle_time.update();
|
||||||
|
for (int j = 0; j <= i; j++)
|
||||||
|
breadcrumbs.erase(breadcrumbs.begin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//moved because its worthless otherwise
|
||||||
|
if (sync_taunt && HasCondition<TFCond_Taunting>(followtar) && lastTaunt.test_and_set(1000)) {
|
||||||
|
g_IEngine->ClientCmd("taunt");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Follow the crumbs when too far away, or just starting to follow
|
// Follow the crumbs when too far away, or just starting to follow
|
||||||
if (dist_to_target > (float) follow_distance)
|
if (dist_to_target > (float) follow_distance)
|
||||||
{
|
{
|
||||||
// Check for idle
|
// Check for idle
|
||||||
if (idle_time.check(3000) ||
|
if (autojump && idle_time.check(3000))
|
||||||
(breadcrumbs.size() > 1 && LOCAL_E->m_vecVelocity.IsZero(5.0f)))
|
|
||||||
g_pUserCmd->buttons |= IN_JUMP;
|
g_pUserCmd->buttons |= IN_JUMP;
|
||||||
if (idle_time.test_and_set(5000))
|
if (idle_time.test_and_set(5000))
|
||||||
{
|
{
|
||||||
follow_target = 0;
|
follow_target = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sync_taunt && HasCondition<TFCond_Taunting>(ENTITY(follow_target)))
|
|
||||||
g_IEngine->ClientCmd("taunt");
|
|
||||||
static float last_slot_check = 0.0f;
|
static float last_slot_check = 0.0f;
|
||||||
if (g_GlobalVars->curtime < last_slot_check)
|
if (g_GlobalVars->curtime < last_slot_check)
|
||||||
last_slot_check = 0.0f;
|
last_slot_check = 0.0f;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <hacks/Trigger.hpp>
|
#include <hacks/Trigger.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include <hacks/Backtrack.hpp>
|
#include <hacks/Backtrack.hpp>
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
|
|
||||||
namespace hacks::shared::triggerbot
|
namespace hacks::shared::triggerbot
|
||||||
{
|
{
|
||||||
@ -272,6 +273,10 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
if (!entity->m_bEnemy() && !teammates)
|
if (!entity->m_bEnemy() && !teammates)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Global checks
|
||||||
|
if (player_tools::shouldTarget(entity) != player_tools::IgnoreReason::DO_NOT_IGNORE)
|
||||||
|
return false;
|
||||||
|
|
||||||
IF_GAME(IsTF())
|
IF_GAME(IsTF())
|
||||||
{
|
{
|
||||||
// If settings allow waiting for charge, and current charge cant
|
// If settings allow waiting for charge, and current charge cant
|
||||||
@ -291,9 +296,6 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If settings allow, ignore taunting players
|
|
||||||
if (ignore_taunting && HasCondition<TFCond_Taunting>(entity))
|
|
||||||
return false;
|
|
||||||
// Dont target invulnerable players, ex: uber, bonk
|
// Dont target invulnerable players, ex: uber, bonk
|
||||||
if (IsPlayerInvulnerable(entity))
|
if (IsPlayerInvulnerable(entity))
|
||||||
return false;
|
return false;
|
||||||
@ -307,17 +309,6 @@ bool IsTargetStateGood(CachedEntity *entity)
|
|||||||
HasCondition<TFCond_UberBulletResist>(entity))
|
HasCondition<TFCond_UberBulletResist>(entity))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Dont target players marked as friendly
|
|
||||||
if (playerlist::IsFriendly(playerlist::AccessData(entity).state))
|
|
||||||
return false;
|
|
||||||
IF_GAME(IsTF())
|
|
||||||
{
|
|
||||||
// If settings allow, ignore hoovys
|
|
||||||
if (ignore_hoovy && IsHoovy(entity))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Head hitbox detection
|
// Head hitbox detection
|
||||||
if (HeadPreferable(entity))
|
if (HeadPreferable(entity))
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <hacks/hacklist.hpp>
|
#include <hacks/hacklist.hpp>
|
||||||
|
#include <online/Online.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
#include "hitrate.hpp"
|
#include "hitrate.hpp"
|
||||||
#include "hack.hpp"
|
#include "hack.hpp"
|
||||||
@ -31,6 +32,7 @@ DEFINE_HOOKED_METHOD(Paint, void, IEngineVGui *this_, PaintMode_t mode)
|
|||||||
{
|
{
|
||||||
hitrate::Update();
|
hitrate::Update();
|
||||||
}
|
}
|
||||||
|
online::update();
|
||||||
#if ENABLE_IPC
|
#if ENABLE_IPC
|
||||||
static Timer nametimer{};
|
static Timer nametimer{};
|
||||||
if (nametimer.test_and_set(1000 * 10))
|
if (nametimer.test_and_set(1000 * 10))
|
||||||
|
2
src/online/CMakeLists.txt
Normal file
2
src/online/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
target_sources(cathook PRIVATE
|
||||||
|
"${CMAKE_CURRENT_LIST_DIR}/Online.cpp")
|
289
src/online/Online.cpp
Normal file
289
src/online/Online.cpp
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
/*
|
||||||
|
Created on 23.06.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <online/Online.hpp>
|
||||||
|
#include <core/cvwrapper.hpp>
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <optional>
|
||||||
|
#include <timer.hpp>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#undef null
|
||||||
|
|
||||||
|
#include <co/OnlineService.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <init.hpp>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace online
|
||||||
|
{
|
||||||
|
|
||||||
|
void saveApiKeyAndHost(std::string host);
|
||||||
|
void claimSteamId();
|
||||||
|
|
||||||
|
static co::OnlineService cathookOnlineService{};
|
||||||
|
static std::unordered_map<unsigned, std::optional<user_data>> data{};
|
||||||
|
static std::unordered_map<unsigned, bool> identify_queue{};
|
||||||
|
static Timer identify_timer{};
|
||||||
|
static bool identify_stale{ false };
|
||||||
|
static std::string api_key{};
|
||||||
|
|
||||||
|
static CatVar enable(CV_SWITCH, "online", "1", "Enable online features");
|
||||||
|
static CatCommand login("online_login", "Login", [](const CCommand& args) {
|
||||||
|
if (args.ArgC() != 3)
|
||||||
|
{
|
||||||
|
logging::Info("\nUsage: online_login <API_KEY> \"<IP:PORT>\"\nKey will be saved in your data folder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string host(args.Arg(2));
|
||||||
|
logging::Info("[CO] Host = %s", host.c_str());
|
||||||
|
try {
|
||||||
|
cathookOnlineService.setHost(host);
|
||||||
|
} catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Error setting host: %s", ex.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string key(args.Arg(1));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cathookOnlineService.login(key, [key, host](co::ApiCallResult result, std::optional<co::logged_in_user> me) {
|
||||||
|
if (result == co::ApiCallResult::OK)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Successfully logged in. Welcome, %s", me->username.c_str());
|
||||||
|
api_key = key;
|
||||||
|
saveApiKeyAndHost(host);
|
||||||
|
claimSteamId();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logging::Info("[CO] There was an error logging in: code %d", result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("[CO] Exception: %s", ex.what());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
static CatCommand flush("online_flush_cache", "Flush player cache", [](const CCommand& args) {
|
||||||
|
data.clear();
|
||||||
|
identify_queue.clear();
|
||||||
|
identify_stale = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// INTERNAL METHODS
|
||||||
|
|
||||||
|
void claimSteamId()
|
||||||
|
{
|
||||||
|
auto id = g_ISteamUser->GetSteamID();
|
||||||
|
logging::Info("[CO] Claiming SteamID %u", id.GetAccountID());
|
||||||
|
try {
|
||||||
|
cathookOnlineService.gameStartup(id.GetAccountID());
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("[CO] Exception: %s", ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tryLoadApiKeyAndHost()
|
||||||
|
{
|
||||||
|
std::ifstream keyfile(DATA_PATH "/api_key", std::ios::in);
|
||||||
|
if (keyfile)
|
||||||
|
{
|
||||||
|
std::string host{};
|
||||||
|
keyfile >> api_key >> host;
|
||||||
|
if (!api_key.empty() && !host.empty())
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
cathookOnlineService.setHost(host);
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("Error while setting host: %s", ex.what());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void saveApiKeyAndHost(std::string host)
|
||||||
|
{
|
||||||
|
std::ofstream keyfile(DATA_PATH "/api_key", std::ios::out);
|
||||||
|
if (!keyfile)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Something went wrong while saving API key");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
keyfile << api_key << '\n' << host << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueUserForIdentification(unsigned steamId)
|
||||||
|
{
|
||||||
|
|
||||||
|
identify_queue[steamId] = false;
|
||||||
|
identify_timer.update();
|
||||||
|
identify_stale = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void markSteamIdNonOnline(unsigned id)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] %u - not online", id);
|
||||||
|
data[id] = std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void processOnlineIdentity(unsigned id, co::identified_user& user)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] %u - online", id);
|
||||||
|
user_data udata{};
|
||||||
|
udata.username = user.username;
|
||||||
|
udata.is_anonymous = (user.username == "anonymous");
|
||||||
|
udata.is_steamid_verified = user.steamid_verified;
|
||||||
|
for (auto& i: user.groups)
|
||||||
|
{
|
||||||
|
if (i.display_name.has_value())
|
||||||
|
udata.shown_groups.push_back(*i.display_name);
|
||||||
|
if (i.name == "notarget")
|
||||||
|
udata.no_target = true;
|
||||||
|
if (i.name == "owner" || i.name == "contributor")
|
||||||
|
{
|
||||||
|
udata.is_developer = true;
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
udata.rainbow = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if ENABLE_VISUALS
|
||||||
|
if (user.color.has_value())
|
||||||
|
{
|
||||||
|
udata.has_color = true;
|
||||||
|
udata.color = colors::rgba_t(user.color->c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (user.uses_software.has_value())
|
||||||
|
{
|
||||||
|
udata.has_software = true;
|
||||||
|
udata.is_using_friendly_software = user.uses_software->friendly;
|
||||||
|
udata.software_name = user.uses_software->name;
|
||||||
|
}
|
||||||
|
data[id] = std::move(udata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processIdentifyResponse(std::vector<unsigned> input, co::identified_user_group& group)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Processing identify response containing %u / %u entries", group.users.size(), input.size());
|
||||||
|
for (auto i: input)
|
||||||
|
{
|
||||||
|
auto u = group.users.find(i);
|
||||||
|
if (u == group.users.end())
|
||||||
|
markSteamIdNonOnline(i);
|
||||||
|
else
|
||||||
|
processOnlineIdentity(i, (*u).second);
|
||||||
|
|
||||||
|
identify_queue.erase(i);
|
||||||
|
logging::Info("[CO] Removed %u from identify queue, left %u\n", i, identify_queue.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendIdentifyRequest()
|
||||||
|
{
|
||||||
|
std::vector<unsigned> steamIds{};
|
||||||
|
auto it = identify_queue.begin();
|
||||||
|
// Create a list of up to 32 steamId's
|
||||||
|
for (int i = 0; i < 32 && it != identify_queue.end(); ++i, ++it)
|
||||||
|
{
|
||||||
|
if (!it->second)
|
||||||
|
{
|
||||||
|
it->second = true;
|
||||||
|
steamIds.push_back(it->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logging::Info("[CO] Sending identify request for %u players", steamIds.size());
|
||||||
|
try {
|
||||||
|
cathookOnlineService.userIdentify(steamIds, (std::function<void(co::ApiCallResult, std::optional<co::identified_user_group>)>)[steamIds](co::ApiCallResult result, std::optional<co::identified_user_group> group) {
|
||||||
|
if (result == co::ApiCallResult::OK)
|
||||||
|
{
|
||||||
|
processIdentifyResponse(steamIds, *group);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Something went wrong while identifying %u players: code %d", steamIds.size(), result);
|
||||||
|
for (auto i: steamIds)
|
||||||
|
{
|
||||||
|
identify_queue[i] = false;
|
||||||
|
}
|
||||||
|
identify_stale = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("[CO] Exception: %s", ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
InitRoutine init([]() {
|
||||||
|
cathookOnlineService.setErrorHandler((std::function<void(std::string)>)[](std::string error) {
|
||||||
|
logging::Info("[CO] Error: %s", error.c_str());
|
||||||
|
});
|
||||||
|
if (tryLoadApiKeyAndHost())
|
||||||
|
{
|
||||||
|
logging::Info("[CO] API key loaded successfully");
|
||||||
|
try {
|
||||||
|
cathookOnlineService.login(api_key, [](co::ApiCallResult result, std::optional<co::logged_in_user> me) {
|
||||||
|
if (result == co::ApiCallResult::OK)
|
||||||
|
{
|
||||||
|
logging::Info("[CO] Successfully logged in. Welcome, %s", me->username.c_str());
|
||||||
|
claimSteamId();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logging::Info("[CO] There was an error logging in: code %d", result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("[CO] Exception: %s", ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// EXTERNAL METHODS
|
||||||
|
|
||||||
|
void update()
|
||||||
|
{
|
||||||
|
if (!enable)
|
||||||
|
return;
|
||||||
|
// Only send a request after 3 seconds passed since last unknown steamId was added to the queue
|
||||||
|
if (!api_key.empty() && identify_stale && identify_timer.check(3000) && !identify_queue.empty())
|
||||||
|
{
|
||||||
|
sendIdentifyRequest();
|
||||||
|
identify_stale = false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
cathookOnlineService.processPendingCalls();
|
||||||
|
} catch (std::exception& ex) {
|
||||||
|
logging::Info("[CO] Exception: %s", ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user_data *getUserData(unsigned steamId)
|
||||||
|
{
|
||||||
|
if (!enable)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!steamId)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
auto it = data.find(steamId);
|
||||||
|
// User not identified
|
||||||
|
if (it == data.end())
|
||||||
|
{
|
||||||
|
// Queue user for identification
|
||||||
|
if (identify_queue.find(steamId) == identify_queue.end())
|
||||||
|
queueUserForIdentification(steamId);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// SteamID belongs to online user
|
||||||
|
if (it->second.has_value())
|
||||||
|
return &*it->second;
|
||||||
|
// SteamID does not belong to online user
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -227,8 +227,6 @@ bool EffectChams::ShouldRenderChams(IClientEntity *entity)
|
|||||||
if (entity->entindex() < 0)
|
if (entity->entindex() < 0)
|
||||||
return false;
|
return false;
|
||||||
CachedEntity *ent = ENTITY(entity->entindex());
|
CachedEntity *ent = ENTITY(entity->entindex());
|
||||||
if (CE_BAD(ent))
|
|
||||||
return false;
|
|
||||||
if (ent->m_IDX == LOCAL_E->m_IDX && !chamsself)
|
if (ent->m_IDX == LOCAL_E->m_IDX && !chamsself)
|
||||||
return false;
|
return false;
|
||||||
switch (ent->m_Type())
|
switch (ent->m_Type())
|
||||||
@ -319,12 +317,9 @@ void EffectChams::RenderChamsRecursive(IClientEntity *entity)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EffectChams::RenderChams(int idx)
|
void EffectChams::RenderChams(IClientEntity *entity)
|
||||||
{
|
{
|
||||||
CMatRenderContextPtr ptr(GET_RENDER_CONTEXT);
|
CMatRenderContextPtr ptr(GET_RENDER_CONTEXT);
|
||||||
IClientEntity *entity = g_IEntityList->GetClientEntity(idx);
|
|
||||||
if (entity && !entity->IsDormant())
|
|
||||||
{
|
|
||||||
if (ShouldRenderChams(entity))
|
if (ShouldRenderChams(entity))
|
||||||
{
|
{
|
||||||
rgba_t color = ChamsColor(entity);
|
rgba_t color = ChamsColor(entity);
|
||||||
@ -348,7 +343,6 @@ void EffectChams::RenderChams(int idx)
|
|||||||
g_IVModelRender->ForcedMaterialOverride(flat ? mat_unlit
|
g_IVModelRender->ForcedMaterialOverride(flat ? mat_unlit
|
||||||
: mat_lit);
|
: mat_lit);
|
||||||
RenderChamsRecursive(entity);
|
RenderChamsRecursive(entity);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -366,11 +360,10 @@ void EffectChams::Render(int x, int y, int w, int h)
|
|||||||
BeginRenderChams();
|
BeginRenderChams();
|
||||||
for (int i = 1; i < HIGHEST_ENTITY; i++)
|
for (int i = 1; i < HIGHEST_ENTITY; i++)
|
||||||
{
|
{
|
||||||
IClientEntity *ent = g_IEntityList->GetClientEntity(i);
|
IClientEntity *entity = g_IEntityList->GetClientEntity(i);
|
||||||
if (ent && !ent->IsDormant())
|
if (!entity || entity->IsDormant() || CE_BAD(ENTITY(i)))
|
||||||
{
|
return;
|
||||||
RenderChams(i);
|
RenderChams(entity);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EndRenderChams();
|
EndRenderChams();
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
* Author: nullifiedcat
|
* Author: nullifiedcat
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <online/Online.hpp>
|
||||||
|
#include <PlayerTools.hpp>
|
||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
static CatVar user_red_blue(CV_INT, "esp_color_red_b", "0", "Red Team: Blue",
|
static CatVar user_red_blue(CV_INT, "esp_color_red_b", "0", "Red Team: Blue",
|
||||||
@ -130,9 +132,10 @@ rgba_t colors::EntityF(CachedEntity *ent)
|
|||||||
else if (ent->m_iTeam() == TEAM_RED)
|
else if (ent->m_iTeam() == TEAM_RED)
|
||||||
result = red_v;
|
result = red_v;
|
||||||
}
|
}
|
||||||
plclr = playerlist::Color(ent);
|
|
||||||
if (plclr.a)
|
auto o = player_tools::forceEspColor(ent);
|
||||||
result = plclr;
|
if (o.has_value())
|
||||||
|
return *o;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,3 +147,30 @@ rgba_t colors::RainbowCurrent()
|
|||||||
return colors::FromHSL(fabs(sin(g_GlobalVars->curtime / 2.0f)) * 360.0f,
|
return colors::FromHSL(fabs(sin(g_GlobalVars->curtime / 2.0f)) * 360.0f,
|
||||||
0.85f, 0.9f);
|
0.85f, 0.9f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned char hexToChar(char i)
|
||||||
|
{
|
||||||
|
if (i >= '0' && i <= '9')
|
||||||
|
return i - '0';
|
||||||
|
if (i >= 'a' && i <= 'f')
|
||||||
|
return i - 'a' + 10;
|
||||||
|
if (i >= 'A' && i <= 'F')
|
||||||
|
return i - 'A' + 10;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int hexToByte(char hi, char lo)
|
||||||
|
{
|
||||||
|
return (hexToChar(hi) << 4) | (hexToChar(lo));
|
||||||
|
}
|
||||||
|
|
||||||
|
colors::rgba_t::rgba_t(const char hex[6])
|
||||||
|
{
|
||||||
|
auto ri = hexToByte(hex[0], hex[1]);
|
||||||
|
auto gi = hexToByte(hex[2], hex[3]);
|
||||||
|
auto bi = hexToByte(hex[4], hex[5]);
|
||||||
|
r = float(ri) / 255.0f;
|
||||||
|
g = float(gi) / 255.0f;
|
||||||
|
b = float(bi) / 255.0f;
|
||||||
|
a = 1.0f;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user