diff --git a/src/hacks/AutoHeal.cpp b/src/hacks/AutoHeal.cpp index 379f5416..5b836077 100644 --- a/src/hacks/AutoHeal.cpp +++ b/src/hacks/AutoHeal.cpp @@ -12,18 +12,52 @@ namespace hacks { namespace tf { namespace autoheal { -extern CatVar enabled(CV_SWITCH, "autoheal_enabled", "0", "AutoHeal", "Automatically heals nearby teammates"); -extern CatVar silent(CV_SWITCH, "autoheal_silent", "1", "Silent AutoHeal", "Silent AutoHeal. Disable this to make ghetto followbot"); +CatVar enabled(CV_SWITCH, "autoheal_enabled", "0", "AutoHeal", "Automatically heals nearby teammates"); +CatVar silent(CV_SWITCH, "autoheal_silent", "1", "Silent AutoHeal", "Silent AutoHeal. Disable this to make ghetto followbot"); //extern CatVar target_only; int m_iCurrentHealingTarget { -1 }; int m_iNewTarget { 0 }; -static CatVar pop_uber(CV_FLOAT, "autoheal_uber_health", "20", "Pop uber if health% <"); +static CatVar pop_uber_auto(CV_SWITCH, "autoheal_uber", "1", "AutoUber"); +static CatVar pop_uber_percent(CV_FLOAT, "autoheal_uber_health", "30", "Pop uber if health% <"); +static CatVar share_uber(CV_SWITCH, "autoheal_share_uber", "1", "Share ubercharge"); + +bool IsPopped() { + CachedEntity* weapon = g_pLocalPlayer->weapon(); + if (CE_BAD(weapon) || weapon->m_iClassID != g_pClassID->CWeaponMedigun) return false; + return CE_BYTE(weapon, netvar.bChargeRelease); +} + +bool ShouldChargePlayer(int idx) { + CachedEntity* target = ENTITY(idx); + const float damage_accum_duration = g_GlobalVars->curtime - data[idx].accum_damage_start; + const int health = target->m_iHealth; + if (!data[idx].accum_damage_start) return false; + if (health > 30 && data[idx].accum_damage < 45) return false; + const float dd = ((float)data[idx].accum_damage / damage_accum_duration); + if (dd > 40) { + return true; + } + if (health < 30 && data[idx].accum_damage > 10) return true; + return false; +} + +bool ShouldPop() { + if (IsPopped()) return false; + if (m_iCurrentHealingTarget != -1) { + CachedEntity* target = ENTITY(m_iCurrentHealingTarget); + if (CE_GOOD(target)) { + if (ShouldChargePlayer(m_iCurrentHealingTarget)) return true; + } + } + return ShouldChargePlayer(LOCAL_E->m_IDX); +} void CreateMove() { if (!enabled) return; if (GetWeaponMode(g_pLocalPlayer->entity) != weapon_medigun) return; + UpdateData(); int old_target = m_iCurrentHealingTarget; m_iCurrentHealingTarget = BestTarget(); if (m_iNewTarget > 0 && m_iNewTarget < 10) m_iNewTarget++; @@ -38,13 +72,37 @@ void CreateMove() { GetHitbox(target, 7, out); AimAt(g_pLocalPlayer->v_Eye, out, g_pUserCmd); if (silent) g_pLocalPlayer->bUseSilentAngles = true; - if (!m_iNewTarget && (g_GlobalVars->tickcount % 60)) g_pUserCmd->buttons |= IN_ATTACK; - if (((float)target->m_iHealth / (float)target->m_iMaxHealth) * 100 < (float)pop_uber) { - g_pUserCmd->buttons |= IN_ATTACK2; - } + if (!m_iNewTarget && (g_GlobalVars->tickcount % 300)) g_pUserCmd->buttons |= IN_ATTACK; + if (ShouldPop()) g_pUserCmd->buttons |= IN_ATTACK2; return; } +std::vector data(32); +void UpdateData() { + for (int i = 1; i < 32; i++) { + CachedEntity* ent = ENTITY(i); + if (CE_GOOD(ent)) { + int health = ent->m_iHealth; + if (data[i].last_damage > g_GlobalVars->curtime) { + data[i].last_damage = 0.0f; + } + if (g_GlobalVars->curtime - data[i].last_damage > 5.0f) { + data[i].accum_damage = 0; + data[i].accum_damage_start = 0.0f; + } + const int last_health = data[i].last_health; + if (health != last_health) { + data[i].last_health = health; + if (health < last_health) { + data[i].accum_damage += (last_health - health); + if (!data[i].accum_damage_start) data[i].accum_damage_start = g_GlobalVars->curtime; + data[i].last_damage = g_GlobalVars->curtime; + } + } + } + } +} + int BestTarget() { int best = -1; int best_score = -65536; @@ -61,6 +119,10 @@ int BestTarget() { int HealingPriority(int idx) { if (!CanHeal(idx)) return -1; CachedEntity* ent = ENTITY(idx); + if (share_uber && IsPopped()) { + return !HasCondition(ent, TFCond_Ubercharged); + } + int priority = 0; int health = CE_INT(ent, netvar.iHealth); int maxhealth = g_pPlayerResource->GetMaxHealth(ent); diff --git a/src/hacks/AutoHeal.h b/src/hacks/AutoHeal.h index 2cfd6234..f49e70ac 100644 --- a/src/hacks/AutoHeal.h +++ b/src/hacks/AutoHeal.h @@ -10,6 +10,8 @@ #include "IHack.h" +#include "../common.h" + namespace hacks { namespace tf { namespace autoheal { extern CatVar enabled; @@ -17,6 +19,16 @@ extern CatVar silent; // TODO extern CatVar target_only; void CreateMove(); +struct patient_data_s { + float last_damage { 0.0f }; + int last_health { 0 }; + int accum_damage { 0 }; // accumulated damage over X seconds (data stored for AT least 5 seconds) + float accum_damage_start { 0.0f }; +}; + +extern std::vector data; + +void UpdateData(); int BestTarget(); int HealingPriority(int idx); bool CanHeal(int idx); diff --git a/src/hooks/others.cpp b/src/hooks/others.cpp index 11c45d1e..efb4783b 100644 --- a/src/hooks/others.cpp +++ b/src/hooks/others.cpp @@ -127,6 +127,7 @@ bool DispatchUserMessage_hook(void* thisptr, int type, bf_read& buf) { buf.Seek(0); } } + //if (type != net_Tick) logging::Info("Got message: %s", type); return ((DispatchUserMessage_t*)hooks::hkClient->GetMethod(hooks::offFrameStageNotify + 1))(thisptr, type, buf); SEGV_END; return false; } diff --git a/src/ipc.cpp b/src/ipc.cpp index 38cd218c..65584095 100644 --- a/src/ipc.cpp +++ b/src/ipc.cpp @@ -54,9 +54,24 @@ CatCommand connect("ipc_connect", "Connect to IPC server", []() { pthread_create(&listener_thread, nullptr, listen, nullptr); } catch (std::exception& error) { logging::Info("Runtime error: %s", error.what()); + delete peer; + peer = nullptr; } }); +CatCommand lobby("ipc_lobby", "Join a lobby", [](const CCommand& args) { + std::string input(args.ArgS()); + std::size_t lobby_id_start = input.find("[L:1:"); + if (lobby_id_start == std::string::npos) { + logging::Info("couldn't find lobby ID!"); + return; + } + input = input.substr(lobby_id_start + 5); + unsigned long lobby32 = strtoul(input.c_str(), nullptr, 10); + unsigned long long lobby64 = ((25559040ull << 32) | lobby32); + logging::Info("lobby64 ID: %llu", lobby64); + peer->SendMessage(format("connect_lobby ", lobby64).c_str(), 0, ipc::commands::execute_client_cmd, 0, 0); +}); CatCommand disconnect("ipc_disconnect", "Disconnect from IPC server", []() { thread_running = false; pthread_join(listener_thread, nullptr); diff --git a/src/ipc.h b/src/ipc.h index 53e94bdd..5aaa6ea8 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -33,6 +33,7 @@ extern CatCommand connect; extern CatCommand disconnect; extern CatCommand exec; extern CatCommand exec_all; +extern CatCommand lobby; extern CatVar server_name; extern pthread_t listener_thread; diff --git a/src/netvars.cpp b/src/netvars.cpp index 940ef19b..1a774c5d 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp @@ -54,6 +54,8 @@ void NetVars::Init() { this->iPlayerClass = gNetvars.get_offset("DT_TFPlayerResource", "m_iPlayerClass"); this->angEyeAngles = gNetvars.get_offset("DT_TFPlayer", "tfnonlocaldata", "m_angEyeAngles[0]"); this->iWeaponState = gNetvars.get_offset("DT_WeaponMinigun", "m_iWeaponState"); + this->flChargeLevel = gNetvars.get_offset("DT_WeaponMedigun", "NonLocalTFWeaponMedigunData", "m_flChargeLevel"); + this->bChargeRelease = gNetvars.get_offset("DT_WeaponMedigun", "m_bChargeRelease"); } if (TF2C) this->iCritMult = gNetvars.get_offset("DT_TFPlayer", "m_Shared", "m_iCritMult"); if (TF2C) { diff --git a/src/netvars.h b/src/netvars.h index 51b37e6d..0526f152 100644 --- a/src/netvars.h +++ b/src/netvars.h @@ -113,6 +113,9 @@ public: offset_t hOwner; offset_t iWeaponState; offset_t iCritMult; // TF2C + + offset_t flChargeLevel; + offset_t bChargeRelease; }; extern NetVars netvar;