This repository has been archived on 2024-06-01. You can view files and clone it, but cannot push or open issues or pull requests.
cathook/src/helpers.cpp
2018-07-22 12:45:17 +02:00

1332 lines
39 KiB
C++

/*
* helpers.cpp
*
* Created on: Oct 8, 2016
* Author: nullifiedcat
*/
#include "common.hpp"
#include <sys/mman.h>
std::vector<ConVar *> &RegisteredVarsList()
{
static std::vector<ConVar *> list{};
return list;
}
std::vector<ConCommand *> &RegisteredCommandsList()
{
static std::vector<ConCommand *> list{};
return list;
}
void BeginConVars()
{
logging::Info("Begin ConVars");
if (!std::ifstream("tf/cfg/cat_autoexec.cfg"))
{
std::ofstream cfg_autoexec("tf/cfg/cat_autoexec.cfg",
std::ios::out | std::ios::trunc);
if (cfg_autoexec.good())
{
cfg_autoexec << "// Put your custom cathook settings in this "
"file\n// This script will be executed EACH TIME "
"YOU INJECT CATHOOK\n";
}
}
if (!std::ifstream("tf/cfg/cat_autoexec.cfg"))
{
std::ofstream cfg_autoexec("tf/cfg/cat_matchexec.cfg",
std::ios::out | std::ios::trunc);
if (cfg_autoexec.good())
{
cfg_autoexec << "// Put your custom cathook settings in this "
"file\n// This script will be executed EACH TIME "
"YOU JOIN A MATCH\n";
}
}
logging::Info(":b:");
SetCVarInterface(g_ICvar);
}
void EndConVars()
{
logging::Info("Registering ConVars");
RegisterCatVars();
RegisterCatCommands();
ConVar_Register();
std::ofstream cfg_defaults("tf/cfg/cat_defaults.cfg",
std::ios::out | std::ios::trunc);
if (cfg_defaults.good())
{
cfg_defaults << "// This file is auto-generated and will be "
"overwritten each time you inject cathook\n// Do not "
"make edits to this file\n\n// Every registered "
"variable dump\n";
for (const auto &i : RegisteredVarsList())
{
cfg_defaults << i->GetName() << " \"" << i->GetDefault() << "\"\n";
}
cfg_defaults << "\n// Every registered command dump\n";
for (const auto &i : RegisteredCommandsList())
{
cfg_defaults << "// " << i->GetName() << "\n";
}
}
}
ConVar *CreateConVar(std::string name, std::string value, std::string help)
{
char *namec = new char[256];
char *valuec = new char[256];
char *helpc = new char[256];
strncpy(namec, name.c_str(), 255);
strncpy(valuec, value.c_str(), 255);
strncpy(helpc, help.c_str(), 255);
// logging::Info("Creating ConVar: %s %s %s", namec, valuec, helpc);
ConVar *ret = new ConVar(const_cast<const char *>(namec),
const_cast<const char *>(valuec), 0,
const_cast<const char *>(helpc));
g_ICvar->RegisterConCommand(ret);
RegisteredVarsList().push_back(ret);
return ret;
}
// Function for when you want to goto a vector
void WalkTo(const Vector &vector)
{
if (CE_BAD(LOCAL_E))
return;
// Calculate how to get to a vector
auto result = ComputeMove(LOCAL_E->m_vecOrigin(), vector);
// Push our move to usercmd
g_pUserCmd->forwardmove = result.first;
g_pUserCmd->sidemove = result.second;
}
// Function to get the corner location that a vischeck to an entity is possible
// from
Vector VischeckCorner(CachedEntity *player, CachedEntity *target, float maxdist,
bool checkWalkable)
{
int maxiterations = maxdist / 40;
Vector origin = player->m_vecOrigin();
// if we can see an entity, we don't need to run calculations
if (VisCheckEntFromEnt(player, target))
{
if (!checkWalkable)
return origin;
else if (canReachVector(origin, target->m_vecOrigin()))
return origin;
}
for (int i = 0; i < 4; i++) // for loop for all 4 directions
{
// 40 * maxiterations = range in HU
for (int j = 0; j < maxiterations; j++)
{
Vector virtualOrigin = origin;
// what direction to go in
switch (i)
{
case 0:
virtualOrigin.x = virtualOrigin.x + 40 * (j + 1);
break;
case 1:
virtualOrigin.x = virtualOrigin.x - 40 * (j + 1);
break;
case 2:
virtualOrigin.y = virtualOrigin.y + 40 * (j + 1);
break;
case 3:
virtualOrigin.y = virtualOrigin.y - 40 * (j + 1);
break;
}
// check if player can see the players virtualOrigin
if (!IsVectorVisible(origin, virtualOrigin, true))
continue;
// check if the virtualOrigin can see the target
if (!VisCheckEntFromEntVector(virtualOrigin, player, target))
continue;
if (!checkWalkable)
return virtualOrigin;
// check if the location is accessible
if (!canReachVector(origin, virtualOrigin))
continue;
if (canReachVector(virtualOrigin, target->m_vecOrigin()))
return virtualOrigin;
}
}
// if we didn't find anything, return an empty Vector
return { 0, 0, 0 };
}
// return Two Corners that connect perfectly to ent and local player
std::pair<Vector,Vector> VischeckWall(CachedEntity *player, CachedEntity *target, float maxdist,
bool checkWalkable)
{
int maxiterations = maxdist / 40;
Vector origin = player->m_vecOrigin();
// if we can see an entity, we don't need to run calculations
if (VisCheckEntFromEnt(player, target))
{
std::pair<Vector, Vector> orig(origin, target->m_vecOrigin());
if (!checkWalkable)
return orig;
else if (canReachVector(origin, target->m_vecOrigin()))
return orig;
}
for (int i = 0; i < 4; i++) // for loop for all 4 directions
{
// 40 * maxiterations = range in HU
for (int j = 0; j < maxiterations; j++)
{
Vector virtualOrigin = origin;
// what direction to go in
switch (i)
{
case 0:
virtualOrigin.x = virtualOrigin.x + 40 * (j + 1);
break;
case 1:
virtualOrigin.x = virtualOrigin.x - 40 * (j + 1);
break;
case 2:
virtualOrigin.y = virtualOrigin.y + 40 * (j + 1);
break;
case 3:
virtualOrigin.y = virtualOrigin.y - 40 * (j + 1);
break;
}
// check if player can see the players virtualOrigin
if (!IsVectorVisible(origin, virtualOrigin, true))
continue;
for (int i = 0; i < 4; i++) // for loop for all 4 directions
{
// 40 * maxiterations = range in HU
for (int j = 0; j < maxiterations; j++)
{
Vector virtualOrigin2 = target->m_vecOrigin();
// what direction to go in
switch (i)
{
case 0:
virtualOrigin2.x = virtualOrigin2.x + 40 * (j + 1);
break;
case 1:
virtualOrigin2.x = virtualOrigin2.x - 40 * (j + 1);
break;
case 2:
virtualOrigin2.y = virtualOrigin2.y + 40 * (j + 1);
break;
case 3:
virtualOrigin2.y = virtualOrigin2.y - 40 * (j + 1);
break;
}
// check if the virtualOrigin2 can see the target
if (!VisCheckEntFromEntVector(virtualOrigin2, player, target))
continue;
if (!IsVectorVisible(virtualOrigin, virtualOrigin2, true))
continue;
if (!IsVectorVisible(virtualOrigin2, target->m_vecOrigin(), true))
continue;
std::pair<Vector, Vector> toret(virtualOrigin, virtualOrigin2);
if (!checkWalkable)
return toret;
// check if the location is accessible
if (!canReachVector(origin, virtualOrigin) || !canReachVector(virtualOrigin2, virtualOrigin))
continue;
if (canReachVector(virtualOrigin2, target->m_vecOrigin()))
return toret;
}
}
}
}
// if we didn't find anything, return an empty Vector
return { {0, 0, 0}, {0, 0, 0} };
}
// Returns a vectors max value. For example: {123,-150, 125} = 125
float vectorMax(Vector i)
{
return fmaxf(fmaxf(i.x, i.y), i.z);
}
// Returns a vectors absolute value. For example {123,-150, 125} = {123,150,
// 125}
Vector vectorAbs(Vector i)
{
Vector result = i;
result.x = fabsf(result.x);
result.y = fabsf(result.y);
result.z = fabsf(result.z);
return result;
}
// check to see if we can reach a vector or if it is too high / doesn't leave
// enough space for the player, optional second vector
bool canReachVector(Vector loc, Vector dest)
{
if (!dest.IsZero())
{
Vector dist = dest - loc;
int maxiterations = floor(dest.DistTo(loc)) / 40;
for (int i = 0; i < maxiterations; i++)
{
// math to get the next vector 40.0f in the direction of dest
Vector vec =
loc + dist / vectorMax(vectorAbs(dist)) * 40.0f * (i + 1);
if (DistanceToGround({vec.x,vec.y,vec.z + 5}) >= 40)
return false;
for (int j = 0; j < 4; j++)
{
Vector directionalLoc = vec;
// what direction to check
switch (j)
{
case 0:
directionalLoc.x = directionalLoc.x + 40;
break;
case 1:
directionalLoc.x = directionalLoc.x - 40;
break;
case 2:
directionalLoc.y = directionalLoc.y + 40;
break;
case 3:
directionalLoc.y = directionalLoc.y - 40;
break;
}
trace_t trace;
Ray_t ray;
ray.Init(vec, directionalLoc);
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_no_player,
&trace);
// distance of trace < than 26
if (trace.startpos.DistTo(trace.endpos) < 26.0f)
return false;
}
}
}
else
{
// check if the vector is too high above ground
// higher to avoid small false positives, player can jump 42 hu according to
// the tf2 wiki
if (DistanceToGround({loc.x,loc.y,loc.z + 5}) >= 40)
return false;
// check if there is enough space arround the vector for a player to fit
// for loop for all 4 directions
for (int i = 0; i < 4; i++)
{
Vector directionalLoc = loc;
// what direction to check
switch (i)
{
case 0:
directionalLoc.x = directionalLoc.x + 40;
break;
case 1:
directionalLoc.x = directionalLoc.x - 40;
break;
case 2:
directionalLoc.y = directionalLoc.y + 40;
break;
case 3:
directionalLoc.y = directionalLoc.y - 40;
break;
}
trace_t trace;
Ray_t ray;
ray.Init(loc, directionalLoc);
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_no_player, &trace);
// distance of trace < than 26
if (trace.startpos.DistTo(trace.endpos) < 26.0f)
return false;
}
}
return true;
}
std::string GetLevelName()
{
std::string name(g_IEngine->GetLevelName());
size_t slash = name.find('/');
if (slash == std::string::npos)
slash = 0;
else
slash++;
size_t bsp = name.find(".bsp");
size_t length =
(bsp == std::string::npos ? name.length() - slash : bsp - slash);
return name.substr(slash, length);
}
std::pair<float, float> ComputeMove(const Vector &a, const Vector &b)
{
Vector diff = (b - a);
if (diff.Length() == 0)
return { 0, 0 };
const float x = diff.x;
const float y = diff.y;
Vector vsilent(x, y, 0);
float speed = sqrt(vsilent.x * vsilent.x + vsilent.y * vsilent.y);
Vector ang;
VectorAngles(vsilent, ang);
float yaw = DEG2RAD(ang.y - g_pUserCmd->viewangles.y);
return { cos(yaw) * 450, -sin(yaw) * 450 };
}
ConCommand *CreateConCommand(const char *name, FnCommandCallback_t callback,
const char *help)
{
ConCommand *ret = new ConCommand(name, callback, help);
g_ICvar->RegisterConCommand(ret);
RegisteredCommandsList().push_back(ret);
return ret;
}
const char *GetBuildingName(CachedEntity *ent)
{
if (!ent)
return "[NULL]";
int classid = ent->m_iClassID();
if (classid == CL_CLASS(CObjectSentrygun))
return "Sentry";
if (classid == CL_CLASS(CObjectDispenser))
return "Dispenser";
if (classid == CL_CLASS(CObjectTeleporter))
return "Teleporter";
return "[NULL]";
}
void format_internal(std::stringstream &stream)
{
(void) (stream);
}
void ReplaceString(std::string &input, const std::string &what,
const std::string &with_what)
{
size_t index;
index = input.find(what);
while (index != std::string::npos)
{
input.replace(index, what.size(), with_what);
index = input.find(what, index + with_what.size());
}
}
powerup_type GetPowerupOnPlayer(CachedEntity *player)
{
if (CE_BAD(player))
return powerup_type::not_powerup;
// if (!HasCondition<TFCond_HasRune>(player)) return
// powerup_type::not_powerup;
if (HasCondition<TFCond_RuneStrength>(player))
return powerup_type::strength;
if (HasCondition<TFCond_RuneHaste>(player))
return powerup_type::haste;
if (HasCondition<TFCond_RuneRegen>(player))
return powerup_type::regeneration;
if (HasCondition<TFCond_RuneResist>(player))
return powerup_type::resistance;
if (HasCondition<TFCond_RuneVampire>(player))
return powerup_type::vampire;
if (HasCondition<TFCond_RuneWarlock>(player))
return powerup_type::reflect;
if (HasCondition<TFCond_RunePrecision>(player))
return powerup_type::precision;
if (HasCondition<TFCond_RuneAgility>(player))
return powerup_type::agility;
if (HasCondition<TFCond_RuneKnockout>(player))
return powerup_type::knockout;
if (HasCondition<TFCond_KingRune>(player))
return powerup_type::king;
if (HasCondition<TFCond_PlagueRune>(player))
return powerup_type::plague;
if (HasCondition<TFCond_SupernovaRune>(player))
return powerup_type::supernova;
return powerup_type::not_powerup;
}
// A function to tell if a player is using a specific weapon
bool HasWeapon(CachedEntity *ent, int wantedId)
{
if (CE_BAD(ent))
return false;
// Create a var to store the handle
int *hWeapons;
// Grab the handle and store it into the var
hWeapons = (int *) ((unsigned) (RAW_ENT(ent) + netvar.hMyWeapons));
// Go through the handle array and search for the item
for (int i = 0; hWeapons[i]; i++)
{
// Get the weapon id from the handle array
IClientEntity *weapon =
g_IEntityList->GetClientEntityFromHandle(hWeapons[i]);
// if weapon is what we are looking for, return true
if (weapon && NET_INT(weapon, netvar.iItemDefinitionIndex) == wantedId)
return true;
}
// We didnt find the weapon we needed, return false
return false;
}
bool HasDarwins(CachedEntity *ent)
{
if (CE_BAD(ent))
return false;
// Check if player is sniper
if (CE_INT(ent, netvar.iClass) != tf_sniper)
return false;
// Check if player is using darwins, 231 is the id for darwins danger sheild
if (HasWeapon(ent, 231))
return true;
// Else return false
return false;
}
void VectorTransform(const float *in1, const matrix3x4_t &in2, float *out)
{
out[0] = (in1[0] * in2[0][0] + in1[1] * in2[0][1] + in1[2] * in2[0][2]) +
in2[0][3];
out[1] = (in1[0] * in2[1][0] + in1[1] * in2[1][1] + in1[2] * in2[1][2]) +
in2[1][3];
out[2] = (in1[0] * in2[2][0] + in1[1] * in2[2][1] + in1[2] * in2[2][2]) +
in2[2][3];
}
bool GetHitbox(CachedEntity *entity, int hb, Vector &out)
{
hitbox_cache::CachedHitbox *box;
if (CE_BAD(entity))
return false;
box = entity->hitboxes.GetHitbox(hb);
if (!box)
out = entity->m_vecOrigin();
else
out = box->center;
return true;
}
void VectorAngles(Vector &forward, Vector &angles)
{
float tmp, yaw, pitch;
if (forward[1] == 0 && forward[0] == 0)
{
yaw = 0;
if (forward[2] > 0)
pitch = 270;
else
pitch = 90;
}
else
{
yaw = (atan2(forward[1], forward[0]) * 180 / PI);
if (yaw < 0)
yaw += 360;
tmp = sqrt((forward[0] * forward[0] + forward[1] * forward[1]));
pitch = (atan2(-forward[2], tmp) * 180 / PI);
if (pitch < 0)
pitch += 360;
}
angles[0] = pitch;
angles[1] = yaw;
angles[2] = 0;
}
char GetUpperChar(ButtonCode_t button)
{
switch (button)
{
case KEY_0:
return ')';
case KEY_1:
return '!';
case KEY_2:
return '@';
case KEY_3:
return '#';
case KEY_4:
return '$';
case KEY_5:
return '%';
case KEY_6:
return '^';
case KEY_7:
return '&';
case KEY_8:
return '*';
case KEY_9:
return '(';
case KEY_LBRACKET:
return '{';
case KEY_RBRACKET:
return '}';
case KEY_SEMICOLON:
return ':';
case KEY_BACKQUOTE:
return '~';
case KEY_APOSTROPHE:
return '"';
case KEY_COMMA:
return '<';
case KEY_PERIOD:
return '>';
case KEY_SLASH:
return '?';
case KEY_BACKSLASH:
return '|';
case KEY_MINUS:
return '_';
case KEY_EQUAL:
return '+';
default:
if (strlen(g_IInputSystem->ButtonCodeToString(button)) != 1)
return 0;
return toupper(*g_IInputSystem->ButtonCodeToString(button));
}
}
char GetChar(ButtonCode_t button)
{
switch (button)
{
case KEY_PAD_DIVIDE:
return '/';
case KEY_PAD_MULTIPLY:
return '*';
case KEY_PAD_MINUS:
return '-';
case KEY_PAD_PLUS:
return '+';
case KEY_SEMICOLON:
return ';';
default:
if (button >= KEY_PAD_0 && button <= KEY_PAD_9)
{
return button - KEY_PAD_0 + '0';
}
if (strlen(g_IInputSystem->ButtonCodeToString(button)) != 1)
return 0;
return *g_IInputSystem->ButtonCodeToString(button);
}
}
void FixMovement(CUserCmd &cmd, Vector &viewangles)
{
Vector movement, ang;
float speed, yaw;
movement.x = cmd.forwardmove;
movement.y = cmd.sidemove;
movement.z = cmd.upmove;
speed = sqrt(movement.x * movement.x + movement.y * movement.y);
VectorAngles(movement, ang);
yaw = DEG2RAD(ang.y - viewangles.y + cmd.viewangles.y);
cmd.forwardmove = cos(yaw) * speed;
cmd.sidemove = sin(yaw) * speed;
}
bool AmbassadorCanHeadshot()
{
if (IsAmbassador(g_pLocalPlayer->weapon()))
{
if ((g_GlobalVars->curtime -
CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flLastFireTime)) <= 1.0)
{
return false;
}
}
return true;
}
float RandFloatRange(float min, float max)
{
return (min + 1) +
(((float) rand()) / (float) RAND_MAX) * (max - (min + 1));
}
bool IsEntityVisible(CachedEntity *entity, int hb)
{
Vector hit;
if (g_Settings.bInvalid)
return false;
if (entity == g_pLocalPlayer->entity)
return true;
if (hb == -1)
{
return IsEntityVectorVisible(entity, entity->m_vecOrigin());
}
else
{
return entity->hitboxes.VisibilityCheck(hb);
}
}
static CatVar tcm(CV_SWITCH, "debug_tcm", "1", "TCM");
std::mutex trace_lock;
bool IsEntityVectorVisible(CachedEntity *entity, Vector endpos)
{
trace_t trace_object;
Ray_t ray;
if (g_Settings.bInvalid)
return false;
if (entity == g_pLocalPlayer->entity)
return true;
if (CE_BAD(g_pLocalPlayer->entity))
return false;
if (CE_BAD(entity))
return false;
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
ray.Init(g_pLocalPlayer->v_Eye, endpos);
{
PROF_SECTION(IEVV_TraceRay);
std::lock_guard<std::mutex> lock(trace_lock);
if (!tcm || g_Settings.is_create_move)
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default,
&trace_object);
}
return (((IClientEntity *) trace_object.m_pEnt) == RAW_ENT(entity) || trace_object.fraction >= 0.99f);
}
// For when you need to vis check something that isnt the local player
bool VisCheckEntFromEnt(CachedEntity *startEnt, CachedEntity *endEnt)
{
// We setSelf as the starting ent as we dont want to hit it, we want the
// other ent
trace_t trace;
trace::filter_default.SetSelf(RAW_ENT(startEnt));
// Setup the trace starting with the origin of the starting ent attemting to
// hit the origin of the end ent
Ray_t ray;
ray.Init(startEnt->m_vecOrigin(), endEnt->m_vecOrigin());
{
PROF_SECTION(IEVV_TraceRay);
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
}
// Is the entity that we hit our target ent? if so, the vis check passes
if (trace.m_pEnt)
{
if ((((IClientEntity *) trace.m_pEnt)) == RAW_ENT(endEnt))
return true;
}
// Since we didnt hit our target ent, the vis check failed so return false
return false;
}
// Use when you need to vis check something but its not the ent origin that you
// use, so we check from the vector to the ent, ignoring the first just in case
bool VisCheckEntFromEntVector(Vector startVector, CachedEntity *startEnt,
CachedEntity *endEnt)
{
// We setSelf as the starting ent as we dont want to hit it, we want the
// other ent
trace_t trace;
trace::filter_default.SetSelf(RAW_ENT(startEnt));
// Setup the trace starting with the origin of the starting ent attemting to
// hit the origin of the end ent
Ray_t ray;
ray.Init(startVector, endEnt->m_vecOrigin());
{
PROF_SECTION(IEVV_TraceRay);
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_default, &trace);
}
// Is the entity that we hit our target ent? if so, the vis check passes
if (trace.m_pEnt)
{
if ((((IClientEntity *) trace.m_pEnt)) == RAW_ENT(endEnt))
return true;
}
// Since we didnt hit our target ent, the vis check failed so return false
return false;
}
Vector GetBuildingPosition(CachedEntity *ent)
{
Vector res;
res = ent->m_vecOrigin();
int classid = ent->m_iClassID();
if (classid == CL_CLASS(CObjectDispenser))
res.z += 30;
if (classid == CL_CLASS(CObjectTeleporter))
res.z += 8;
if (classid == CL_CLASS(CObjectSentrygun))
{
switch (CE_INT(ent, netvar.iUpgradeLevel))
{
case 1:
res.z += 30;
break;
case 2:
res.z += 50;
break;
case 3:
res.z += 60;
break;
}
}
return res;
}
bool IsBuildingVisible(CachedEntity *ent)
{
return IsEntityVectorVisible(ent, GetBuildingPosition(ent));
}
void fClampAngle(Vector &qaAng)
{
while (qaAng[0] > 89)
qaAng[0] -= 180;
while (qaAng[0] < -89)
qaAng[0] += 180;
while (qaAng[1] > 180)
qaAng[1] -= 360;
while (qaAng[1] < -180)
qaAng[1] += 360;
qaAng.z = 0;
}
float DistToSqr(CachedEntity *entity)
{
if (CE_BAD(entity))
return 0.0f;
return g_pLocalPlayer->v_Origin.DistToSqr(entity->m_vecOrigin());
}
void Patch(void *address, void *patch, size_t length)
{
void *page = (void *) ((uintptr_t) address & ~0xFFF);
logging::Info("mprotect: %d",
mprotect(page, 0xFFF, PROT_READ | PROT_WRITE | PROT_EXEC));
memcpy(address, patch, length);
logging::Info("mprotect reverse: %d", mprotect(page, 0xFFF, PROT_EXEC));
}
bool IsProjectileCrit(CachedEntity *ent)
{
if (ent->m_bGrenadeProjectile())
return CE_BYTE(ent, netvar.Grenade_bCritical);
return CE_BYTE(ent, netvar.Rocket_bCritical);
}
weaponmode GetWeaponMode()
{
int weapon_handle, slot;
CachedEntity *weapon;
if (CE_BAD(LOCAL_E))
return weapon_invalid;
weapon_handle = CE_INT(LOCAL_E, netvar.hActiveWeapon);
if (IDX_BAD((weapon_handle & 0xFFF)))
{
// logging::Info("IDX_BAD: %i", weapon_handle & 0xFFF);
return weaponmode::weapon_invalid;
}
weapon = (ENTITY(weapon_handle & 0xFFF));
if (CE_BAD(weapon))
return weaponmode::weapon_invalid;
int classid = weapon->m_iClassID();
slot = re::C_BaseCombatWeapon::GetSlot(RAW_ENT(weapon));
if (slot == 2)
return weaponmode::weapon_melee;
if (slot > 2)
{
return weaponmode::weapon_pda;
}
else if (classid == CL_CLASS(CTFLunchBox) ||
classid == CL_CLASS(CTFLunchBox_Drink) ||
classid == CL_CLASS(CTFBuffItem))
{
return weaponmode::weapon_consumable;
}
else if (classid == CL_CLASS(CTFRocketLauncher_DirectHit) ||
classid == CL_CLASS(CTFRocketLauncher) ||
classid == CL_CLASS(CTFGrenadeLauncher) ||
classid == CL_CLASS(CTFPipebombLauncher) ||
classid == CL_CLASS(CTFCompoundBow) ||
classid == CL_CLASS(CTFBat_Wood) ||
classid == CL_CLASS(CTFBat_Giftwrap) ||
classid == CL_CLASS(CTFFlareGun) ||
classid == CL_CLASS(CTFFlareGun_Revenge) ||
classid == CL_CLASS(CTFSyringeGun) ||
classid == CL_CLASS(CTFCrossbow) ||
classid == CL_CLASS(CTFShotgunBuildingRescue) ||
classid == CL_CLASS(CTFDRGPomson))
{
return weaponmode::weapon_projectile;
}
else if (classid == CL_CLASS(CTFJar) || classid == CL_CLASS(CTFJarMilk))
{
return weaponmode::weapon_throwable;
}
else if (classid == CL_CLASS(CWeaponMedigun))
{
return weaponmode::weapon_medigun;
}
return weaponmode::weapon_hitscan;
}
bool LineIntersectsBox(Vector &bmin, Vector &bmax, Vector &lmin, Vector &lmax)
{
if (lmax.x < bmin.x && lmin.x < bmin.x)
return false;
if (lmax.y < bmin.y && lmin.y < bmin.y)
return false;
if (lmax.z < bmin.z && lmin.z < bmin.z)
return false;
if (lmax.x > bmax.x && lmin.x > bmax.x)
return false;
if (lmax.y > bmax.y && lmin.y > bmax.y)
return false;
if (lmax.z > bmax.z && lmin.z > bmax.z)
return false;
return true;
}
// TODO add bison and grapple hook
// TODO FIX this function
bool GetProjectileData(CachedEntity *weapon, float &speed, float &gravity)
{
float rspeed, rgrav;
IF_GAME(!IsTF()) return false;
if (CE_BAD(weapon))
return false;
rspeed = 0.0f;
rgrav = 0.0f;
typedef float(GetProjectileData)(IClientEntity *);
int classid = weapon->m_iClassID();
if (classid == CL_CLASS(CTFRocketLauncher_DirectHit))
{
rspeed = 1980.0f;
}
else if (classid == CL_CLASS(CTFRocketLauncher))
{
rspeed = 1100.0f;
}
else if (classid == CL_CLASS(CTFGrenadeLauncher))
{
IF_GAME(IsTF2())
{
rspeed = 1200.0f;
rgrav = 0.4f;
}
else IF_GAME(IsTF2C())
{
rspeed = 1100.0f;
rgrav = 0.5f;
}
}
else if (classid == CL_CLASS(CTFPipebombLauncher))
{
float chargebegin = *((float *) ((unsigned) RAW_ENT(LOCAL_W) + 3152));
float chargetime = g_GlobalVars->curtime - chargebegin;
rspeed =
(fminf(fmaxf(chargetime / 4.0f, 0.0f), 1.0f) * 1500.0f) + 900.0f;
rgrav =
(fminf(fmaxf(chargetime / 4.0f, 0.0f), 1.0f) * -0.70000001f) + 0.5f;
}
else if (classid == CL_CLASS(CTFCompoundBow))
{
float chargetime =
g_GlobalVars->curtime * g_GlobalVars->interval_per_tick -
CE_FLOAT(weapon, netvar.flChargeBeginTime) *
g_GlobalVars->interval_per_tick;
rspeed = (fminf(fmaxf(chargetime, 0.0f), 1.0f) * 800.0f) + 1800.0f;
rgrav = (fminf(fmaxf(chargetime, 0.0f), 1.0f) * -0.40000001f) + 0.5f;
}
else if (classid == CL_CLASS(CTFBat_Wood))
{
rspeed = 3000.0f;
rgrav = 0.5f;
}
else if (classid == CL_CLASS(CTFFlareGun))
{
rspeed = 2000.0f;
rgrav = 0.25f;
}
else if (classid == CL_CLASS(CTFSyringeGun))
{
rgrav = 0.2f;
rspeed = 990.0f;
}
else if (classid == CL_CLASS(CTFCrossbow))
{
rgrav = 0.2f;
rspeed = 2400.0f;
}
else if (classid == CL_CLASS(CTFShotgunBuildingRescue))
{
rgrav = 0.2f;
rspeed = 2400.0f;
}
else if (classid == CL_CLASS(CTFDRGPomson))
{
rspeed = 1200.0f;
}
else if (classid == CL_CLASS(CTFWeaponFlameBall))
{
// ??
rspeed = 2500.0f;
}
speed = rspeed;
gravity = rgrav;
return (rspeed || rgrav);
}
constexpr unsigned developer_list[] = { 306902159 };
bool Developer(CachedEntity *ent)
{
/*
for (int i = 0; i < sizeof(developer_list) / sizeof(unsigned); i++) {
if (developer_list[i] == ent->player_info.friendsID) return true;
}
*/
return false;
}
/*const char* MakeInfoString(IClientEntity* player) {
char* buf = new char[256]();
player_info_t info;
if (!engineClient->GetPlayerInfo(player->entindex(), &info)) return (const
char*)0; logging::Info("a"); int hWeapon = NET_INT(player,
netvar.hActiveWeapon); if (NET_BYTE(player, netvar.iLifeState)) { sprintf(buf,
"%s is dead %s", info.name, tfclasses[NET_INT(player, netvar.iClass)]); return
buf;
}
if (hWeapon) {
IClientEntity* weapon = ENTITY(hWeapon & 0xFFF);
sprintf(buf, "%s is %s with %i health using %s", info.name,
tfclasses[NET_INT(player, netvar.iClass)], NET_INT(player, netvar.iHealth),
weapon->GetClientClass()->GetName()); } else { sprintf(buf, "%s is %s with %i
health", info.name, tfclasses[NET_INT(player, netvar.iClass)], NET_INT(player,
netvar.iHealth));
}
logging::Info("Result: %s", buf);
return buf;
}*/
bool IsVectorVisible(Vector origin, Vector target, bool enviroment_only)
{
trace_t trace_visible;
Ray_t ray;
trace::filter_no_player.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
ray.Init(origin, target);
if (!enviroment_only)
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_no_player,
&trace_visible);
else
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_no_player,
&trace_visible);
return (trace_visible.fraction == 1.0f);
}
void WhatIAmLookingAt(int *result_eindex, Vector *result_pos)
{
Ray_t ray;
Vector forward;
float sp, sy, cp, cy;
QAngle angle;
trace_t trace;
trace::filter_default.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
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 = forward * 8192.0f + g_pLocalPlayer->v_Eye;
ray.Init(g_pLocalPlayer->v_Eye, forward);
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_default, &trace);
if (result_pos)
*result_pos = trace.endpos;
if (result_eindex)
{
*result_eindex = 0;
}
if (trace.m_pEnt && result_eindex)
{
*result_eindex = ((IClientEntity *) (trace.m_pEnt))->entindex();
}
}
bool IsSentryBuster(CachedEntity *entity)
{
return (entity->m_Type() == EntityType::ENTITY_PLAYER &&
CE_INT(entity, netvar.iClass) == tf_class::tf_demoman &&
g_pPlayerResource->GetMaxHealth(entity) == 2500);
}
bool IsAmbassador(CachedEntity *entity)
{
IF_GAME(!IsTF2()) return false;
if (entity->m_iClassID() != CL_CLASS(CTFRevolver))
return false;
const int &defidx = CE_INT(entity, netvar.iItemDefinitionIndex);
return (defidx == 61 || defidx == 1006);
}
bool IsPlayerInvulnerable(CachedEntity *player)
{
return HasConditionMask<
KInvulnerabilityMask.cond_0, KInvulnerabilityMask.cond_1,
KInvulnerabilityMask.cond_2, KInvulnerabilityMask.cond_3>(player);
}
bool IsPlayerCritBoosted(CachedEntity *player)
{
return HasConditionMask<KCritBoostMask.cond_0, KCritBoostMask.cond_1,
KCritBoostMask.cond_2, KCritBoostMask.cond_3>(
player);
}
bool IsPlayerInvisible(CachedEntity *player)
{
return HasConditionMask<KInvisibilityMask.cond_0, KInvisibilityMask.cond_1,
KInvisibilityMask.cond_2, KInvisibilityMask.cond_3>(
player);
}
bool IsPlayerDisguised(CachedEntity *player)
{
return HasConditionMask<KDisguisedMask.cond_0, KDisguisedMask.cond_1,
KDisguisedMask.cond_2, KDisguisedMask.cond_3>(
player);
}
// F1 c&p
Vector CalcAngle(Vector src, Vector dst)
{
Vector AimAngles, delta;
float hyp;
delta = src - dst;
hyp = sqrtf(
(delta.x * delta.x) +
(delta.y * delta.y)); // SUPER SECRET IMPROVEMENT CODE NAME DONUT STEEL
AimAngles.x = atanf(delta.z / hyp) * RADPI;
AimAngles.y = atanf(delta.y / delta.x) * RADPI;
AimAngles.z = 0.0f;
if (delta.x >= 0.0)
AimAngles.y += 180.0f;
return AimAngles;
}
void MakeVector(Vector angle, Vector &vector)
{
float pitch, yaw, tmp;
pitch = float(angle[0] * PI / 180);
yaw = float(angle[1] * PI / 180);
tmp = float(cos(pitch));
vector[0] = float(-tmp * -cos(yaw));
vector[1] = float(sin(yaw) * tmp);
vector[2] = float(-sin(pitch));
}
float GetFov(Vector angle, Vector src, Vector dst)
{
Vector ang, aim;
float mag, u_dot_v;
ang = CalcAngle(src, dst);
MakeVector(angle, aim);
MakeVector(ang, ang);
mag = sqrtf(pow(aim.x, 2) + pow(aim.y, 2) + pow(aim.z, 2));
u_dot_v = aim.Dot(ang);
return RAD2DEG(acos(u_dot_v / (pow(mag, 2))));
}
bool CanHeadshot()
{
return (g_pLocalPlayer->flZoomBegin > 0.0f &&
(g_GlobalVars->curtime - g_pLocalPlayer->flZoomBegin > 0.2f));
}
bool CanShoot()
{
float servertime, nextattack;
servertime = (float) (CE_INT(g_pLocalPlayer->entity, netvar.nTickBase)) *
g_GlobalVars->interval_per_tick;
nextattack = CE_FLOAT(g_pLocalPlayer->weapon(), netvar.flNextPrimaryAttack);
return nextattack <= servertime;
}
QAngle VectorToQAngle(Vector in)
{
return *(QAngle *) &in;
}
Vector QAngleToVector(QAngle in)
{
return *(Vector *) &in;
}
void AimAt(Vector origin, Vector target, CUserCmd *cmd)
{
Vector angles, tr;
tr = (target - origin);
VectorAngles(tr, angles);
fClampAngle(angles);
cmd->viewangles = angles;
}
void AimAtHitbox(CachedEntity *ent, int hitbox, CUserCmd *cmd)
{
Vector r;
r = ent->m_vecOrigin();
GetHitbox(ent, hitbox, r);
AimAt(g_pLocalPlayer->v_Eye, r, cmd);
}
bool IsEntityVisiblePenetration(CachedEntity *entity, int hb)
{
trace_t trace_visible;
Ray_t ray;
Vector hit;
int ret;
bool correct_entity;
IClientEntity *ent;
trace::filter_penetration.SetSelf(RAW_ENT(g_pLocalPlayer->entity));
trace::filter_penetration.Reset();
ret = GetHitbox(entity, hb, hit);
if (ret)
{
return false;
}
ray.Init(g_pLocalPlayer->v_Origin + g_pLocalPlayer->v_ViewOffset, hit);
g_ITrace->TraceRay(ray, MASK_SHOT_HULL, &trace::filter_penetration,
&trace_visible);
correct_entity = false;
if (trace_visible.m_pEnt)
{
correct_entity =
((IClientEntity *) trace_visible.m_pEnt) == RAW_ENT(entity);
}
if (!correct_entity)
return false;
g_ITrace->TraceRay(ray, 0x4200400B, &trace::filter_default, &trace_visible);
if (trace_visible.m_pEnt)
{
ent = (IClientEntity *) trace_visible.m_pEnt;
if (ent)
{
if (ent->GetClientClass()->m_ClassID == RCC_PLAYER)
{
if (ent == RAW_ENT(entity))
return false;
if (trace_visible.hitbox >= 0)
{
return true;
}
}
}
}
return false;
}
// Used for getting class names
CatCommand print_classnames(
"debug_print_classnames", "Lists classnames currently available in console",
[]() {
// Create a tmp ent for the loop
CachedEntity *ent;
// Go through all the entities
for (int i = 0; i < HIGHEST_ENTITY; i++)
{
// Get an entity
ent = ENTITY(i);
// Check for null/dormant
if (CE_BAD(ent))
continue;
// Print in console, the class name of the ent
logging::Info(
format(RAW_ENT(ent)->GetClientClass()->m_pNetworkName).c_str());
}
});
void PrintChat(const char *fmt, ...)
{
#if not ENABLE_VISUALS
return;
#endif
CHudBaseChat *chat = (CHudBaseChat *) g_CHUD->FindElement("CHudChat");
if (chat)
{
std::unique_ptr<char[]> buf(new char[1024]);
va_list list;
va_start(list, fmt);
vsprintf(buf.get(), fmt, list);
va_end(list);
std::unique_ptr<char> str(strfmt("\x07%06X[\x07%06XCAT\x07%06X]\x01 %s",
0x5e3252, 0xba3d9a, 0x5e3252,
buf.get()).release());
// FIXME DEBUG LOG
logging::Info("%s", str.get());
chat->Printf(str.get());
}
else
{
}
}
// You shouldn't delete[] this unique_ptr since it
// does it on its own
std::unique_ptr<char[]> strfmt(const char *fmt, ...)
{
// char *buf = new char[1024];
auto buf = std::make_unique<char[]>(1024);
va_list list;
va_start(list, fmt);
vsprintf(buf.get(), fmt, list);
va_end(list);
return buf;
}
const char *powerups[] = { "STRENGTH", "RESISTANCE", "VAMPIRE", "REFLECT",
"HASTE", "REGENERATION", "PRECISION", "AGILITY",
"KNOCKOUT", "KING", "PLAGUE", "SUPERNOVA",
"CRITS" };
const std::string classes[] = { "Scout", "Sniper", "Soldier",
"Demoman", "Medic", "Heavy",
"Pyro", "Spy", "Engineer" };