diff --git a/data/menu/nullifiedcat/warp.xml b/data/menu/nullifiedcat/warp.xml
index 7b196cac..c51f31b3 100644
--- a/data/menu/nullifiedcat/warp.xml
+++ b/data/menu/nullifiedcat/warp.xml
@@ -5,7 +5,10 @@
-
+
+
+
+
@@ -25,6 +28,8 @@
+
+
@@ -35,6 +40,14 @@
+
+
+
diff --git a/include/core/interfaces.hpp b/include/core/interfaces.hpp
index c3a17366..c9294bac 100644
--- a/include/core/interfaces.hpp
+++ b/include/core/interfaces.hpp
@@ -17,6 +17,7 @@ class ISurface;
class IPanel;
} // namespace vgui
+class IToolFrameworkInternal;
class ISteamClient;
class ISteamFriends;
class IVEngineClient013;
@@ -95,6 +96,7 @@ extern IUniformRandomStream *g_pUniformStream;
extern int *g_PredictionRandomSeed;
extern IFileSystem *g_IFileSystem;
extern IMDLCache *g_IMDLCache;
+extern IToolFrameworkInternal *g_IToolFramework;
void CreateInterfaces();
void CreateEarlyInterfaces();
diff --git a/include/core/netvars.hpp b/include/core/netvars.hpp
index 9869961d..9f72fdf6 100644
--- a/include/core/netvars.hpp
+++ b/include/core/netvars.hpp
@@ -207,6 +207,7 @@ public:
offset_t m_iPlayerIndex;
offset_t m_hTargetPlayer;
offset_t m_flResetTime;
+ offset_t m_flMaxspeed;
};
extern NetVars netvar;
diff --git a/include/core/offsets.hpp b/include/core/offsets.hpp
index 68b268bd..4fa583cb 100644
--- a/include/core/offsets.hpp
+++ b/include/core/offsets.hpp
@@ -228,4 +228,8 @@ struct offsets
{
return PlatformOffset(0x2fb8, undefined, undefined);
}
+ static constexpr uint32_t Think()
+ {
+ return PlatformOffset(27, undefined, undefined);
+ }
};
diff --git a/include/core/sdk.hpp b/include/core/sdk.hpp
index c2a58ee6..9a3e1f2f 100755
--- a/include/core/sdk.hpp
+++ b/include/core/sdk.hpp
@@ -18,6 +18,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/hacks/Warp.hpp b/include/hacks/Warp.hpp
index 920cf2c7..6955efba 100644
--- a/include/hacks/Warp.hpp
+++ b/include/hacks/Warp.hpp
@@ -3,6 +3,8 @@
class INetMessage;
namespace hacks::tf2::warp
{
+extern bool in_rapidfire;
+extern bool in_warp;
void SendNetMessage(INetMessage &msg);
void CL_SendMove_hook();
} // namespace hacks::tf2::warp
diff --git a/include/helpers.hpp b/include/helpers.hpp
index 5a6638d6..99474f0b 100644
--- a/include/helpers.hpp
+++ b/include/helpers.hpp
@@ -121,6 +121,7 @@ Vector getShootPos(Vector angle);
Vector GetForwardVector(Vector origin, Vector viewangles, float distance, CachedEntity *punch_entity = nullptr);
Vector GetForwardVector(float distance, CachedEntity *punch_entity = nullptr);
CachedEntity *getClosestEntity(Vector vec);
+CachedEntity *getClosestNonlocalEntity(Vector vec);
bool IsSentryBuster(CachedEntity *ent);
std::unique_ptr strfmt(const char *fmt, ...);
// TODO move that to weaponid.h
@@ -218,7 +219,7 @@ template void format_internal(std::stringstream
stream << value;
format_internal(stream, args...);
}
-template std::string format(const Args &... args)
+template std::string format(const Args &...args)
{
std::stringstream stream;
format_internal(stream, args...);
diff --git a/include/hooks.hpp b/include/hooks.hpp
index 4841c07c..9ebc5966 100644
--- a/include/hooks.hpp
+++ b/include/hooks.hpp
@@ -75,4 +75,5 @@ extern VMTHook materialsystem;
extern VMTHook enginevgui;
extern VMTHook vstd;
extern VMTHook eventmanager2;
+extern VMTHook toolbox;
} // namespace hooks
diff --git a/include/hooks/HookTools.hpp b/include/hooks/HookTools.hpp
index 5ae73f74..435b6184 100644
--- a/include/hooks/HookTools.hpp
+++ b/include/hooks/HookTools.hpp
@@ -15,10 +15,13 @@ enum ec_types
CreateMoveLate,
/* This kind of CreateMove will run earlier than "CreateMove" events
* and guranteed to run before EnginePrediction
+ * This is NEEDED for any kind of movement
*/
CreateMove_NoEnginePred,
/* Note: this is still CreateMove, just ran before original is called, needed in some cases like changing tickcount before original gets called*/
CreateMoveEarly,
+ /* Note: This will replace the normal CreateMove when we are warping. Used for performance purposes. */
+ CreateMoveWarp,
#if ENABLE_VISUALS
Draw,
#endif
diff --git a/include/hooks/HookedMethods.hpp b/include/hooks/HookedMethods.hpp
index f61c0056..a9da7c9d 100644
--- a/include/hooks/HookedMethods.hpp
+++ b/include/hooks/HookedMethods.hpp
@@ -94,6 +94,8 @@ DECLARE_HOOKED_METHOD(EmitSound2, void, void *, IRecipientFilter &, int, int, co
DECLARE_HOOKED_METHOD(EmitSound3, void, void *, IRecipientFilter &, int, int, int, float, soundlevel_t, int, int, int, const Vector *, const Vector *, CUtlVector *, bool, float, int);
// g_IPrediction
DECLARE_HOOKED_METHOD(RunCommand, void, IPrediction *, IClientEntity *, CUserCmd *, IMoveHelper *);
+// g_IToolFramework
+DECLARE_HOOKED_METHOD(Think, void, IToolFrameworkInternal *, bool);
} // namespace hooked_methods
// TODO
diff --git a/include/hooks/Think.hpp b/include/hooks/Think.hpp
new file mode 100644
index 00000000..52c39a61
--- /dev/null
+++ b/include/hooks/Think.hpp
@@ -0,0 +1,6 @@
+#pragma once
+namespace hooked_methods
+{
+// Update Prediction, Used by warp/Doubletap aswell
+void UpdatePred();
+} // namespace hooked_methods
diff --git a/include/localplayer.hpp b/include/localplayer.hpp
index d3a99bce..cde34813 100644
--- a/include/localplayer.hpp
+++ b/include/localplayer.hpp
@@ -26,6 +26,8 @@ public:
// Start of CM
void Update();
+ // After prediction
+ void UpdateEye();
// End of CM
void UpdateEnd();
int team;
diff --git a/include/reclasses/C_TFWeaponBase.hpp b/include/reclasses/C_TFWeaponBase.hpp
index dd1714f3..a362e4c7 100644
--- a/include/reclasses/C_TFWeaponBase.hpp
+++ b/include/reclasses/C_TFWeaponBase.hpp
@@ -82,6 +82,13 @@ public:
typedef bool (*fn_t)(IClientEntity *, bool, IClientEntity *);
return vfunc(self, offsets::PlatformOffset(490, offsets::undefined, 490), 0)(self, unknown1, unknown2);
}
+ inline static float ApplyFireDelay(IClientEntity *self, float delay)
+ {
+ typedef float (*ApplyFireDelay_t)(IClientEntity *, float);
+ static auto signature = gSignatures.GetClientSignature("55 89 E5 57 56 53 83 EC 6C C7 45 ? 00 00 00 00 A1 ? ? ? ? C7 45 ? 00 00 00 00 8B 5D ? 85 C0 0F 84 ? ? ? ? 8D 55 ? 89 04 24 31 F6 89 54 24 ? C7 44 24 ? ? ? ? ? C7 44 24 ? ? ? ? ? C7 44 24 ? ? ? ? ? C7 44 24 ? ? ? ? ? C7 44 24 ? 6B 00 00 00 C7 44 24 ? ? ? ? ? C7 44 24 ? 00 00 00 00 C7 44 24 ? 00 00 00 00 C7 44 24 ? 00 00 00 00 C7 44 24 ? 00 00 00 00 FF 50 ? A1 ? ? ? ? 8B 0D ? ? ? ? 8B 55 ? 89 45 ? 8B 45 ? 85 C9 89 55 ? 89 45 ? 0F 85 ? ? ? ? 85 DB 0F 84 ? ? ? ? 8B 7B ? 85 FF 0F 84 ? ? ? ? C7 04 24 ? ? ? ? E8 ? ? ? ? 89 45 ? 8B 07 89 3C 24 FF 10 8B 7D ? 8B 10 C7 44 24 ? 00 00 00 00 89 5C 24 ? C7 44 24 ? ? ? ? ? 89 7C 24 ? 89 04 24 FF 52 ? D9 5D ? F3 0F 10 45 ? F3 0F 11 04 24 E8 ? ? ? ? D9 5D");
+ static ApplyFireDelay_t ApplyFireDelay_fn = (ApplyFireDelay_t) signature;
+ return ApplyFireDelay_fn(self, delay);
+ }
inline static void AddToCritBucket(IClientEntity *self, float value)
{
constexpr float max_bucket_capacity = 1000.0f;
diff --git a/src/core/interfaces.cpp b/src/core/interfaces.cpp
index 81970343..b8634f07 100644
--- a/src/core/interfaces.cpp
+++ b/src/core/interfaces.cpp
@@ -18,44 +18,45 @@
// class ISteamFriends002;
-IVModelRender *g_IVModelRender = nullptr;
-ISteamClient *g_ISteamClient = nullptr;
-ISteamFriends *g_ISteamFriends = nullptr;
-IVEngineClient013 *g_IEngine = nullptr;
-void *demoplayer = nullptr;
-IEngineSound *g_ISoundEngine = nullptr;
-vgui::ISurface *g_ISurface = nullptr;
-vgui::IPanel *g_IPanel = nullptr;
-IClientEntityList *g_IEntityList = nullptr;
-ICvar *g_ICvar = nullptr;
-IGameEventManager2 *g_IEventManager2 = nullptr;
-IBaseClientDLL *g_IBaseClient = nullptr;
-IEngineTrace *g_ITrace = nullptr;
-IVModelInfoClient *g_IModelInfo = nullptr;
-IInputSystem *g_IInputSystem = nullptr;
-CGlobalVarsBase **rg_GlobalVars = nullptr;
-IPrediction *g_IPrediction = nullptr;
-IGameMovement *g_IGameMovement = nullptr;
-IInput *g_IInput = nullptr;
-ISteamUser *g_ISteamUser = nullptr;
-IAchievementMgr *g_IAchievementMgr = nullptr;
-ISteamUserStats *g_ISteamUserStats = nullptr;
-IStudioRender *g_IStudioRender = nullptr;
-IVDebugOverlay *g_IVDebugOverlay = nullptr;
-IMaterialSystemFixed *g_IMaterialSystem = nullptr;
-IVRenderView *g_IVRenderView = nullptr;
-IMaterialSystem *g_IMaterialSystemHL = nullptr;
-IMoveHelperServer *g_IMoveHelperServer = nullptr;
-CBaseClientState *g_IBaseClientState = nullptr;
-IGameEventManager *g_IGameEventManager = nullptr;
-TFGCClientSystem *g_TFGCClientSystem = nullptr;
-CHud *g_CHUD = nullptr;
-CGameRules *g_pGameRules = nullptr;
-IEngineVGui *g_IEngineVGui = nullptr;
-IUniformRandomStream *g_pUniformStream = nullptr;
-int *g_PredictionRandomSeed = nullptr;
-IFileSystem *g_IFileSystem = nullptr;
-IMDLCache *g_IMDLCache = nullptr;
+IVModelRender *g_IVModelRender = nullptr;
+ISteamClient *g_ISteamClient = nullptr;
+ISteamFriends *g_ISteamFriends = nullptr;
+IVEngineClient013 *g_IEngine = nullptr;
+void *demoplayer = nullptr;
+IEngineSound *g_ISoundEngine = nullptr;
+vgui::ISurface *g_ISurface = nullptr;
+vgui::IPanel *g_IPanel = nullptr;
+IClientEntityList *g_IEntityList = nullptr;
+ICvar *g_ICvar = nullptr;
+IGameEventManager2 *g_IEventManager2 = nullptr;
+IBaseClientDLL *g_IBaseClient = nullptr;
+IEngineTrace *g_ITrace = nullptr;
+IVModelInfoClient *g_IModelInfo = nullptr;
+IInputSystem *g_IInputSystem = nullptr;
+CGlobalVarsBase **rg_GlobalVars = nullptr;
+IPrediction *g_IPrediction = nullptr;
+IGameMovement *g_IGameMovement = nullptr;
+IInput *g_IInput = nullptr;
+ISteamUser *g_ISteamUser = nullptr;
+IAchievementMgr *g_IAchievementMgr = nullptr;
+ISteamUserStats *g_ISteamUserStats = nullptr;
+IStudioRender *g_IStudioRender = nullptr;
+IVDebugOverlay *g_IVDebugOverlay = nullptr;
+IMaterialSystemFixed *g_IMaterialSystem = nullptr;
+IVRenderView *g_IVRenderView = nullptr;
+IMaterialSystem *g_IMaterialSystemHL = nullptr;
+IMoveHelperServer *g_IMoveHelperServer = nullptr;
+CBaseClientState *g_IBaseClientState = nullptr;
+IGameEventManager *g_IGameEventManager = nullptr;
+TFGCClientSystem *g_TFGCClientSystem = nullptr;
+CHud *g_CHUD = nullptr;
+CGameRules *g_pGameRules = nullptr;
+IEngineVGui *g_IEngineVGui = nullptr;
+IUniformRandomStream *g_pUniformStream = nullptr;
+int *g_PredictionRandomSeed = nullptr;
+IFileSystem *g_IFileSystem = nullptr;
+IMDLCache *g_IMDLCache = nullptr;
+IToolFrameworkInternal *g_IToolFramework = nullptr;
template T *BruteforceInterface(std::string name, sharedobj::SharedObject &object, int start = 0)
{
@@ -163,6 +164,7 @@ void CreateInterfaces()
g_IPanel = BruteforceInterface("VGUI_Panel", sharedobj::vgui2());
g_pUniformStream = **(IUniformRandomStream ***) (gSignatures.GetVstdSignature("A3 ? ? ? ? C3 89 F6") + 0x1);
+ g_IToolFramework = BruteforceInterface("VTOOLFRAMEWORKVERSION", sharedobj::engine());
#if ENABLE_VISUALS
g_IVDebugOverlay = BruteforceInterface("VDebugOverlay", sharedobj::engine());
g_ISurface = BruteforceInterface("VGUI_Surface", sharedobj::vguimatsurface());
diff --git a/src/core/netvars.cpp b/src/core/netvars.cpp
index 7b001cc8..27dc2d87 100644
--- a/src/core/netvars.cpp
+++ b/src/core/netvars.cpp
@@ -33,6 +33,7 @@ void NetVars::Init()
IF_GAME(IsTF2())
{
+ this->m_flMaxspeed = gNetvars.get_offset("DT_BasePlayer", "m_flMaxspeed");
res_iTeam = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_iTeam");
res_bAlive = gNetvars.get_offset("DT_TFPlayerResource", "baseclass", "m_bAlive");
this->res_iMaxBuffedHealth = gNetvars.get_offset("DT_TFPlayerResource", "m_iMaxBuffedHealth");
diff --git a/src/hack.cpp b/src/hack.cpp
index f0f11b15..621ea3d1 100644
--- a/src/hack.cpp
+++ b/src/hack.cpp
@@ -257,6 +257,10 @@ void hack::Hook()
hooks::prediction.HookMethod(HOOK_ARGS(RunCommand));
hooks::prediction.Apply();
+ hooks::toolbox.Set(g_IToolFramework);
+ hooks::toolbox.HookMethod(HOOK_ARGS(Think));
+ hooks::toolbox.Apply();
+
#if ENABLE_VISUALS
sdl_hooks::applySdlHooks();
#endif
diff --git a/src/hacks/Aimbot.cpp b/src/hacks/Aimbot.cpp
index ba7da296..3d8dad2d 100644
--- a/src/hacks/Aimbot.cpp
+++ b/src/hacks/Aimbot.cpp
@@ -1594,6 +1594,7 @@ static InitRoutine EC([]() {
EC::Register(EC::LevelInit, Reset, "INIT_Aimbot", EC::average);
EC::Register(EC::LevelShutdown, Reset, "RESET_Aimbot", EC::average);
EC::Register(EC::CreateMove, CreateMove, "CM_Aimbot", EC::late);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "CMW_Aimbot", EC::late);
#if ENABLE_VISUALS
EC::Register(EC::Draw, DrawText, "DRAW_Aimbot", EC::average);
#endif
diff --git a/src/hacks/AntiAntiAim.cpp b/src/hacks/AntiAntiAim.cpp
index 7c383c97..3ccebfb4 100644
--- a/src/hacks/AntiAntiAim.cpp
+++ b/src/hacks/AntiAntiAim.cpp
@@ -14,7 +14,7 @@ static settings::Boolean debug{ "anti-anti-aim.debug.enable", "false" };
std::unordered_map resolver_map;
std::array sniperdot_array;
-static inline void modifyAnlges()
+static inline void modifyAngles()
{
for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
{
@@ -55,7 +55,7 @@ void frameStageNotify(ClientFrameStage_t stage)
return;
if (stage == FRAME_NET_UPDATE_POSTDATAUPDATE_START)
{
- modifyAnlges();
+ modifyAngles();
}
#endif
}
@@ -259,8 +259,10 @@ static InitRoutine init([]() {
hook();
EC::Register(EC::Shutdown, shutdown, "antiantiaim_shutdown");
EC::Register(EC::CreateMove, CreateMove, "cm_antiantiaim");
+ EC::Register(EC::CreateMoveWarp, CreateMove, "cmw_antiantiaim");
#if ENABLE_TEXTMODE
- EC::Register(EC::CreateMove, modifyAnlges, "cm_textmodeantiantiaim");
+ EC::Register(EC::CreateMove, modifyAngles, "cm_textmodeantiantiaim");
+ EC::Register(EC::CreateMoveWarp, modifyAngles, "cmw_textmodeantiantiaim");
#endif
});
} // namespace hacks::shared::anti_anti_aim
diff --git a/src/hacks/AntiBackstab.cpp b/src/hacks/AntiBackstab.cpp
index 1f7c132e..f577fc02 100644
--- a/src/hacks/AntiBackstab.cpp
+++ b/src/hacks/AntiBackstab.cpp
@@ -133,5 +133,8 @@ void CreateMove()
noaa = false;
}
-static InitRoutine EC([]() { EC::Register(EC::CreateMove, CreateMove, "antibackstab", EC::late); });
+static InitRoutine EC([]() {
+ EC::Register(EC::CreateMove, CreateMove, "antibackstab", EC::late);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "antibackstab_w", EC::late);
+});
} // namespace hacks::tf2::antibackstab
diff --git a/src/hacks/AntiDisguise.cpp b/src/hacks/AntiDisguise.cpp
index 67032543..3d82d66d 100644
--- a/src/hacks/AntiDisguise.cpp
+++ b/src/hacks/AntiDisguise.cpp
@@ -35,5 +35,8 @@ void cm()
}
}
}
-static InitRoutine EC([]() { EC::Register(EC::CreateMove, cm, "antidisguise", EC::average); });
+static InitRoutine EC([]() {
+ EC::Register(EC::CreateMove, cm, "antidisguise", EC::average);
+ EC::Register(EC::CreateMoveWarp, cm, "antidisguise_w", EC::average);
+});
} // namespace hacks::tf2::antidisguise
diff --git a/src/hacks/AutoBackstab.cpp b/src/hacks/AutoBackstab.cpp
index dd1c8aa9..5ec6425a 100644
--- a/src/hacks/AutoBackstab.cpp
+++ b/src/hacks/AutoBackstab.cpp
@@ -356,5 +356,8 @@ void CreateMove()
}
}
-static InitRoutine EC([]() { EC::Register(EC::CreateMove, CreateMove, "autobackstab", EC::average); });
+static InitRoutine EC([]() {
+ EC::Register(EC::CreateMove, CreateMove, "autobackstab", EC::average);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "autobackstab_w", EC::average);
+});
} // namespace hacks::tf2::autobackstab
diff --git a/src/hacks/AutoReflect.cpp b/src/hacks/AutoReflect.cpp
index c55d8ccc..82d99c24 100644
--- a/src/hacks/AutoReflect.cpp
+++ b/src/hacks/AutoReflect.cpp
@@ -238,6 +238,7 @@ void Draw()
static InitRoutine EC([]() {
EC::Register(EC::CreateMove, CreateMove, "cm_auto_reflect", EC::average);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "cmw_auto_reflect", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "draw_auto_reflect", EC::average);
#endif
diff --git a/src/hacks/Backtrack.cpp b/src/hacks/Backtrack.cpp
index e155fce7..7796aa85 100644
--- a/src/hacks/Backtrack.cpp
+++ b/src/hacks/Backtrack.cpp
@@ -549,7 +549,9 @@ std::optional> getClosestTick(Vector ve
static InitRoutine init([]() {
EC::Register(EC::CreateMove, CreateMove, "backtrack_cm", EC::early);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "backtrack_cmw", EC::early);
EC::Register(EC::CreateMove, CreateMoveLate, "backtrack_cmlate", EC::very_late);
+ EC::Register(EC::CreateMoveWarp, CreateMoveLate, "backtrack_cmwlate", EC::very_late);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "backtrack_draw");
#endif
diff --git a/src/hacks/Bunnyhop.cpp b/src/hacks/Bunnyhop.cpp
index 95388bc1..3596fdf8 100644
--- a/src/hacks/Bunnyhop.cpp
+++ b/src/hacks/Bunnyhop.cpp
@@ -56,5 +56,5 @@ static void CreateMove()
if (!jump)
ticks_last_jump = 0;
}
-static InitRoutine EC([]() { EC::Register(EC::CreateMove_NoEnginePred, CreateMove, "Bunnyhop", EC::average); });
+static InitRoutine EC([]() { EC::Register(EC::CreateMove_NoEnginePred, CreateMove, "Bunnyhop", EC::early); });
} // namespace hacks::shared::bunnyhop
diff --git a/src/hacks/FollowBot.cpp b/src/hacks/FollowBot.cpp
index 833b17f4..c6764258 100644
--- a/src/hacks/FollowBot.cpp
+++ b/src/hacks/FollowBot.cpp
@@ -774,7 +774,7 @@ void rvarCallback(settings::VariableBase &var, int after)
}
static InitRoutine runinit([]() {
- EC::Register(EC::CreateMove, cm, "cm_followbot", EC::average);
+ EC::Register(EC::CreateMove_NoEnginePred, cm, "cm_followbot", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, draw, "draw_followbot", EC::average);
#endif
diff --git a/src/hacks/Misc.cpp b/src/hacks/Misc.cpp
index 56bf915a..3345f797 100644
--- a/src/hacks/Misc.cpp
+++ b/src/hacks/Misc.cpp
@@ -960,6 +960,7 @@ static InitRoutine init([]() {
teammatesPushaway = g_ICvar->FindVar("tf_avoidteammates_pushaway");
EC::Register(EC::Shutdown, Shutdown, "draw_local_player", EC::average);
EC::Register(EC::CreateMove, CreateMove, "cm_misc_hacks", EC::average);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "cmw_misc_hacks", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "draw_misc_hacks", EC::average);
#if !ENFORCE_STREAM_SAFETY
diff --git a/src/hacks/MiscAimbot.cpp b/src/hacks/MiscAimbot.cpp
index 98c28221..bd72ba1e 100644
--- a/src/hacks/MiscAimbot.cpp
+++ b/src/hacks/MiscAimbot.cpp
@@ -589,6 +589,7 @@ float CAM_CapYaw_Hook(IInput *this_, float fVal)
#define foffset(p, i) ((unsigned char *) &p)[i]
static InitRoutine init([]() {
EC::Register(EC::CreateMove, CreateMove, "cm_miscaimbot", EC::average);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "cmw_miscaimbot", EC::average);
static auto signature = gSignatures.GetClientSignature("55 89 E5 53 83 EC 14 E8 ? ? ? ? 85 C0 74 ? 8D 98 ? ? ? ? C7 44 24 ? 11 00 00 00");
diff --git a/src/hacks/NavBot.cpp b/src/hacks/NavBot.cpp
index 4fc0ee0d..c1d99f14 100644
--- a/src/hacks/NavBot.cpp
+++ b/src/hacks/NavBot.cpp
@@ -1395,6 +1395,7 @@ ObjectDestroyListener &listener()
static InitRoutine runinit([]() {
g_IEventManager2->AddListener(&listener(), "object_destroyed", false);
EC::Register(EC::CreateMove, CreateMove, "navbot", EC::early);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "navbot_w", EC::early);
EC::Register(
EC::Shutdown, []() { g_IEventManager2->RemoveListener(&listener()); }, "navbot_shutdown");
});
diff --git a/src/hacks/Trigger.cpp b/src/hacks/Trigger.cpp
index 7b638661..f76b0697 100644
--- a/src/hacks/Trigger.cpp
+++ b/src/hacks/Trigger.cpp
@@ -627,5 +627,8 @@ void Draw()
{
}
-static InitRoutine EC([]() { EC::Register(EC::CreateMove, CreateMove, "triggerbot", EC::average); });
+static InitRoutine EC([]() {
+ EC::Register(EC::CreateMove, CreateMove, "triggerbot", EC::average);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "triggerbot_w", EC::average);
+});
} // namespace hacks::shared::triggerbot
diff --git a/src/hacks/Walkbot.cpp b/src/hacks/Walkbot.cpp
index b340d65b..cc5ec9ff 100644
--- a/src/hacks/Walkbot.cpp
+++ b/src/hacks/Walkbot.cpp
@@ -1219,7 +1219,7 @@ static void cm()
}
static InitRoutine init([]() {
- EC::Register(EC::CreateMove, cm, "cm_walkbot", EC::average);
+ EC::Register(EC::CreateMove_NoEnginePred, cm, "cm_walkbot", EC::average);
EC::Register(EC::LevelInit, OnLevelInit, "init_walkbot", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, Draw, "draw_walkbot", EC::average);
diff --git a/src/hacks/Warp.cpp b/src/hacks/Warp.cpp
index a889736e..871a9c83 100644
--- a/src/hacks/Warp.cpp
+++ b/src/hacks/Warp.cpp
@@ -14,17 +14,22 @@
#include "DetourHook.hpp"
#include "WeaponData.hpp"
#include "MiscTemporary.hpp"
+#include "Think.hpp"
namespace hacks::tf2::warp
{
static settings::Boolean enabled{ "warp.enabled", "false" };
static settings::Boolean no_movement{ "warp.rapidfire.no-movement", "true" };
static settings::Boolean rapidfire{ "warp.rapidfire", "false" };
+static settings::Int distance{ "warp.rapidfire.distance", "0" };
+static settings::Boolean rapidfire_zoom{ "warp.rapidfire.zoom", "true" };
static settings::Boolean wait_full{ "warp.rapidfire.wait-full", "true" };
static settings::Button rapidfire_key{ "warp.rapidfire.key", "" };
static settings::Int rapidfire_key_mode{ "warp.rapidfire.key-mode", "1" };
+static settings::Int rf_disable_on{ "warp.rapidfire.disable-on", "0" };
static settings::Float speed{ "warp.speed", "23" };
static settings::Boolean draw{ "warp.draw", "false" };
+static settings::Boolean draw_bar{ "warp.draw-bar", "false" };
static settings::Button warp_key{ "warp.key", "" };
static settings::Button charge_key{ "warp.charge-key", "" };
static settings::Boolean charge_passively{ "warp.charge-passively", "true" };
@@ -42,13 +47,16 @@ static settings::Boolean warp_right{ "warp.on-hit.right", "true" };
// Hidden control rvars for communtiy servers
static settings::Int maxusrcmdprocessticks("warp.maxusrcmdprocessticks", "24");
-bool in_warp = false;
-bool in_rapidfire = false;
+bool in_warp = false;
+bool in_rapidfire = false;
+bool in_rapidfire_zoom = false;
// Should we choke the packet or not? (in rapidfire)
bool choke_packet = false;
// Were we warping last tick?
// why is this needed at all, why do i have to write this janky bs
bool was_in_warp = false;
+// Is this the first warp tick?
+bool first_warp_tick = false;
// How many ticks we have to add to our CreateMove packet
int ticks_to_add = 0;
@@ -60,6 +68,34 @@ void warpLogic();
static settings::Int size{ "warp.bar-size", "100" };
static settings::Int bar_x{ "warp.bar-x", "50" };
static settings::Int bar_y{ "warp.bar-y", "200" };
+static settings::Int draw_string_x{ "warp.draw-info.x", "8" };
+static settings::Int draw_string_y{ "warp.draw-info.y", "800" };
+
+// Need our own Text drawing
+static std::array warp_strings;
+static size_t warp_strings_count{ 0 };
+static std::array warp_strings_colors{ colors::empty };
+
+void AddWarpString(const std::string &string, const rgba_t &color)
+{
+ warp_strings[warp_strings_count] = string;
+ warp_strings_colors[warp_strings_count] = color;
+ ++warp_strings_count;
+}
+
+void DrawWarpStrings()
+{
+ float x = *draw_string_x;
+ float y = *draw_string_y;
+ for (size_t i = 0; i < warp_strings_count; ++i)
+ {
+ float sx, sy;
+ fonts::menu->stringSize(warp_strings[i], &sx, &sy);
+ draw::String(x, y, warp_strings_colors[i], warp_strings[i].c_str(), *fonts::center_screen);
+ y += fonts::center_screen->size + 1;
+ }
+ warp_strings_count = 0;
+}
static bool should_charge = false;
static int warp_amount = 0;
@@ -109,6 +145,39 @@ bool UpdateRFKey()
return allow_key;
}
+float getFireDelay()
+{
+ auto weapon_data = GetWeaponData(RAW_ENT(LOCAL_W));
+ return re::C_TFWeaponBase::ApplyFireDelay(RAW_ENT(LOCAL_W), weapon_data->m_flTimeFireDelay);
+}
+
+bool canInstaZoom()
+{
+ return in_rapidfire_zoom || (g_pLocalPlayer->holding_sniper_rifle && current_user_cmd->buttons & IN_ATTACK2 && !HasCondition(LOCAL_E) && CE_FLOAT(LOCAL_W, netvar.flNextSecondaryAttack) <= g_GlobalVars->curtime);
+}
+
+// This is needed in order to make zoom/unzoom smooth even with insta zoom
+static int ignore_ticks = 0;
+
+void handleSniper()
+{
+ // Prevent unzooming
+ if (in_rapidfire)
+ {
+ // Holding ready sniper rifle
+ if (canInstaZoom())
+ {
+ current_user_cmd->buttons &= ~IN_ATTACK2;
+ ignore_ticks = TIME_TO_TICKS(0.2f);
+ }
+ }
+ else if (ignore_ticks)
+ {
+ ignore_ticks--;
+ current_user_cmd->buttons &= ~IN_ATTACK2;
+ }
+}
+
bool shouldRapidfire()
{
if (!rapidfire)
@@ -138,9 +207,16 @@ bool shouldRapidfire()
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFMinigun) && CE_INT(LOCAL_W, netvar.iWeaponState) != 3 && CE_INT(LOCAL_W, netvar.iWeaponState) != 2)
return false;
+ // Check if enemies are close enough
+ if (distance)
+ {
+ auto closest = getClosestNonlocalEntity(LOCAL_E->m_vecOrigin());
+ if (!closest || closest->m_flDistance() > *distance)
+ return false;
+ }
+
// We do not have the amount of ticks needed, don't try it
- auto weapon_data = GetWeaponData(RAW_ENT(LOCAL_W));
- if (warp_amount < TIME_TO_TICKS(weapon_data->m_flTimeFireDelay) && (TIME_TO_TICKS(weapon_data->m_flTimeFireDelay) < *maxusrcmdprocessticks - 1 || (wait_full && warp_amount != GetMaxWarpTicks())))
+ if (warp_amount < TIME_TO_TICKS(getFireDelay()) && (TIME_TO_TICKS(getFireDelay()) < *maxusrcmdprocessticks - 1 || (wait_full && warp_amount != GetMaxWarpTicks())))
return false;
// Mouse 1 is held, do it.
@@ -150,6 +226,33 @@ bool shouldRapidfire()
if (LOCAL_W->m_iClassID() == CL_CLASS(CTFFlameThrower))
buttons_pressed = current_user_cmd && current_user_cmd->buttons & IN_ATTACK2;
+ if (g_pLocalPlayer->holding_sniper_rifle)
+ {
+ // Run if m2 is pressed and sniper rifle is ready
+ buttons_pressed = rapidfire_zoom && current_user_cmd && current_user_cmd->buttons & IN_ATTACK2 && canInstaZoom();
+ // Heatmaker is the only exception here, it can also run on m1
+ if (!buttons_pressed && HasCondition(LOCAL_E))
+ buttons_pressed = current_user_cmd && current_user_cmd->buttons & IN_ATTACK;
+ }
+
+ switch (*rf_disable_on)
+ {
+ case 0: // Always on
+ return buttons_pressed;
+ case 1: // Disable on projectile
+ if (g_pLocalPlayer->weapon_mode == weapon_projectile)
+ return false;
+ break;
+ case 2: // Disable on melee
+ if (g_pLocalPlayer->weapon_mode == weapon_melee)
+ return false;
+ break;
+ case 3: // Disable on projectile and melee
+ if (g_pLocalPlayer->weapon_mode == weapon_projectile || g_pLocalPlayer->weapon_mode == weapon_melee)
+ return false;
+ break;
+ }
+
return buttons_pressed;
}
@@ -175,9 +278,17 @@ int GetWarpAmount(bool finalTick)
{
int max_extra_ticks = *maxusrcmdprocessticks - 1;
- // Rapidfire ignores speed
+ // Rapidfire ignores speed, and we send 15 + 7, aka maximum new + backup commands
if (in_rapidfire)
- return max_extra_ticks;
+ {
+ // Warp right before the next shot
+ float shot_time = getFireDelay();
+ // This is to prevent Minigun/Pistol from only shooting once
+ if (TICKS_TO_TIME(22) / shot_time >= 2)
+ shot_time = TICKS_TO_TIME(23);
+ return std::min(22, TIME_TO_TICKS(shot_time) - 2);
+ // return 22;
+ }
// No limit set
if (!*maxusrcmdprocessticks)
max_extra_ticks = INT_MAX;
@@ -214,6 +325,8 @@ void Warp(float accumulated_extra_samples, bool finalTick)
return;
}
+ PROF_SECTION(warp_profiler);
+
int warp_ticks = warp_amount;
if (warp_amount_override)
warp_ticks = warp_amount_override;
@@ -230,6 +343,10 @@ void Warp(float accumulated_extra_samples, bool finalTick)
int packets_sent = 1;
for (int i = 0; i < calls; i++)
{
+ if (!i)
+ first_warp_tick = true;
+ else
+ first_warp_tick = false;
// Choke unless we sent too many already
choke_packet = true;
@@ -239,6 +356,8 @@ void Warp(float accumulated_extra_samples, bool finalTick)
choke_packet = false;
packets_sent = -1;
}
+ else
+ hooked_methods::UpdatePred();
original(accumulated_extra_samples, finalTick);
// Only decrease ticks for the final CL_Move tick
@@ -346,11 +465,6 @@ static bool was_overridden = false;
static int ticks_in_revved = 0;
static bool replaced_last_tick = false;
-// Original Player origin and velocity, needed to not break because our engine pred.
-// We adjust it as the ticks go.
-static Vector original_origin;
-static Vector original_velocity;
-
// Reset all the revv data
void resetRevvstate()
{
@@ -416,20 +530,17 @@ void CL_Move_hook(float accumulated_extra_samples, bool bFinalTick)
if (shouldRapidfire())
{
in_rapidfire = true;
- // Store original info
- original_origin = LOCAL_E->m_vecOrigin();
- velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), original_velocity);
- // Zero out non z movement, it will just get messy else
- original_velocity.x = 0.0f;
- original_velocity.y = 0.0f;
+ if (canInstaZoom())
+ in_rapidfire_zoom = true;
}
Warp(accumulated_extra_samples, bFinalTick);
if (warp_amount < GetMaxWarpTicks())
charged = false;
- in_warp = false;
- in_rapidfire = false;
- was_in_warp = true;
+ in_warp = false;
+ in_rapidfire = false;
+ in_rapidfire_zoom = false;
+ was_in_warp = true;
}
}
@@ -449,24 +560,28 @@ void CreateMoveEarly()
}
}
-// Fix the player origin after engine prediction, as it tries to predict the local
-// player linearly and just breaks doubletap when falling/moving
-void CreateMoveFixPrediction()
-{
- if (hacks::tf2::warp::in_rapidfire && current_user_cmd)
- {
- // Run very simple gravity calculations to ensure we do not miss
- if (no_movement)
- {
- static ConVar *sv_gravity = g_ICvar->FindVar("sv_gravity");
- Vector gravity{ 0.0f, 0.0f, -sv_gravity->GetFloat() };
+static float original_curtime = 0.0f;
- auto mins = RAW_ENT(LOCAL_E)->GetCollideable()->OBBMins();
- auto maxs = RAW_ENT(LOCAL_E)->GetCollideable()->OBBMaxs();
- std::pair minmax{ mins, maxs };
- PredictStep(original_origin, original_velocity, gravity, &minmax);
- // Restore from the engine prediction
- const_cast(RAW_ENT(LOCAL_E)->GetAbsOrigin()) = original_origin;
+// Run before prediction so we can do Faststop logic
+void CreateMovePrePredict()
+{
+ // Attempt to stop fast in place to make movement smoother
+ if (in_rapidfire && no_movement)
+ FastStop();
+ if (in_rapidfire_zoom)
+ {
+ float adjusted_curtime = g_GlobalVars->curtime;
+ // Original curtime we need to use while in here
+ if (first_warp_tick)
+ original_curtime = g_GlobalVars->curtime;
+
+ // Update the data
+ g_pLocalPlayer->bZoomed = true;
+ g_pLocalPlayer->flZoomBegin = adjusted_curtime;
+ // Do not exceed headshot time, we can only hs next tick
+ if (adjusted_curtime - original_curtime > 0.2f)
+ {
+ g_pLocalPlayer->flZoomBegin = original_curtime - 0.2f - TICKS_TO_TIME(1);
}
}
}
@@ -490,10 +605,6 @@ void CreateMove()
was_in_warp = false;
}
-
- // Attempt to stop fast in place to make movement smoother
- if (in_rapidfire && no_movement)
- FastStop();
}
// Does all the logic related to charging and mode/settings specific actions like peek warp
@@ -507,6 +618,8 @@ void warpLogic()
// Handle minigun in rapidfire
handleMinigun();
+ // Handle sniper in rapidfire
+ handleSniper();
// Charge logic
if (!shouldWarp(false))
@@ -859,25 +972,44 @@ void SendNetMessage(INetMessage &msg)
#if ENABLE_VISUALS
void Draw()
{
- if (!enabled || !draw)
+ if (!enabled)
+ return;
+ if (!draw && !draw_bar)
return;
if (!g_IEngine->IsInGame())
return;
if (CE_BAD(LOCAL_E))
return;
+ if (!LOCAL_E->m_bAlivePlayer())
+ return;
- float charge_percent = (float) warp_amount / (float) GetMaxWarpTicks();
- // Draw background
- static rgba_t background_color = colors::FromRGBA8(96, 96, 96, 150);
- float bar_bg_x_size = *size * 2.0f;
- float bar_bg_y_size = *size / 5.0f;
- draw::Rectangle(*bar_x - 5.0f, *bar_y - 5.0f, bar_bg_x_size + 10.0f, bar_bg_y_size + 10.0f, background_color);
- // Draw bar
- rgba_t color_bar = colors::orange;
- if (GetMaxWarpTicks() == warp_amount)
- color_bar = colors::green;
- color_bar.a = 100 / 255.0f;
- draw::Rectangle(*bar_x, *bar_y, *size * 2.0f * charge_percent, *size / 5.0f, color_bar);
+ if (draw)
+ {
+ rgba_t color = colors::orange;
+ if (warp_amount == 0)
+ color = colors::FromRGBA8(128.0f, 128.0f, 128.0f, 255.0f);
+ else if (GetMaxWarpTicks() == warp_amount)
+ color = colors::green;
+ AddWarpString("Shiftable ticks: " + std::to_string(warp_amount), color);
+ }
+
+ if (draw_bar)
+ {
+ float charge_percent = (float) warp_amount / (float) GetMaxWarpTicks();
+ // Draw background
+ static rgba_t background_color = colors::FromRGBA8(96, 96, 96, 150);
+ float bar_bg_x_size = *size * 2.0f;
+ float bar_bg_y_size = *size / 5.0f;
+ draw::Rectangle(*bar_x - 5.0f, *bar_y - 5.0f, bar_bg_x_size + 10.0f, bar_bg_y_size + 10.0f, background_color);
+ // Draw bar
+ rgba_t color_bar = colors::orange;
+ if (GetMaxWarpTicks() == warp_amount)
+ color_bar = colors::green;
+ color_bar.a = 100 / 255.0f;
+ draw::Rectangle(*bar_x, *bar_y, *size * 2.0f * charge_percent, *size / 5.0f, color_bar);
+ }
+
+ DrawWarpStrings();
}
#endif
@@ -948,8 +1080,9 @@ static InitRoutine init([]() {
cl_move_detour.Init(cl_move_addr, (void *) CL_Move_hook);
EC::Register(EC::LevelShutdown, LevelShutdown, "warp_levelshutdown");
- EC::Register(EC::CreateMove, CreateMoveFixPrediction, "warp_createmove_fixpred", EC::very_early);
EC::Register(EC::CreateMove, CreateMove, "warp_createmove", EC::very_late);
+ EC::Register(EC::CreateMoveWarp, CreateMove, "warp_createmovew", EC::very_late);
+ EC::Register(EC::CreateMove_NoEnginePred, CreateMovePrePredict, "warp_prepredict");
EC::Register(EC::CreateMoveEarly, CreateMoveEarly, "warp_createmove_early", EC::very_early);
g_IEventManager2->AddListener(&listener, "player_hurt", false);
EC::Register(
diff --git a/src/helpers.cpp b/src/helpers.cpp
index 1541acd9..5bf7a776 100644
--- a/src/helpers.cpp
+++ b/src/helpers.cpp
@@ -705,6 +705,22 @@ CachedEntity *getClosestEntity(Vector vec)
return best_ent;
}
+CachedEntity *getClosestNonlocalEntity(Vector vec)
+{
+ float distance = FLT_MAX;
+ CachedEntity *best_ent = nullptr;
+ for (int i = 1; i <= g_IEngine->GetMaxClients(); i++)
+ {
+ CachedEntity *ent = ENTITY(i);
+ if (CE_VALID(ent) && ent->m_IDX != g_pLocalPlayer->entity_idx && ent->m_vecDormantOrigin() && ent->m_bAlivePlayer() && ent->m_bEnemy() && vec.DistTo(ent->m_vecOrigin()) < distance)
+ {
+ distance = vec.DistTo(*ent->m_vecDormantOrigin());
+ best_ent = ent;
+ }
+ }
+ return best_ent;
+}
+
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];
@@ -1621,28 +1637,40 @@ void AimAtHitbox(CachedEntity *ent, int hitbox, CUserCmd *cmd, bool compensate_p
AimAt(g_pLocalPlayer->v_Eye, r, cmd, compensate_punch);
}
-// Thanks to "copypaste" on UnknownCheats, this really helped
void FastStop()
{
// Get velocity
Vector vel;
velocity::EstimateAbsVelocity(RAW_ENT(LOCAL_E), vel);
- Vector direction;
- VectorAngles(vel, direction);
- float speed = vel.Length();
+ static auto sv_friction = g_ICvar->FindVar("sv_friction");
+ static auto sv_stopspeed = g_ICvar->FindVar("sv_stopspeed");
- // Prevent overshooting
- speed *= 0.5f;
+ auto speed = vel.Length2D();
+ auto friction = sv_friction->GetFloat() * CE_FLOAT(LOCAL_E, 0x12b8);
+ auto control = (speed < sv_stopspeed->GetFloat()) ? sv_stopspeed->GetFloat() : speed;
+ auto drop = control * friction * g_GlobalVars->interval_per_tick;
- direction.y = current_user_cmd->viewangles.y - direction.y;
+ if (speed > drop - 1.0f)
+ {
+ Vector velocity = vel;
+ Vector direction;
+ VectorAngles(vel, direction);
+ float speed = velocity.Length();
- Vector negated_direction;
- AngleVectors2(VectorToQAngle(direction), &negated_direction);
- negated_direction *= -speed;
+ direction.y = current_user_cmd->viewangles.y - direction.y;
- current_user_cmd->forwardmove = negated_direction.x;
- current_user_cmd->sidemove = negated_direction.y;
+ Vector forward;
+ AngleVectors2(VectorToQAngle(direction), &forward);
+ Vector negated_direction = forward * -speed;
+
+ current_user_cmd->forwardmove = negated_direction.x;
+ current_user_cmd->sidemove = negated_direction.y;
+ }
+ else
+ {
+ current_user_cmd->forwardmove = current_user_cmd->sidemove = 0.0f;
+ }
}
bool IsEntityVisiblePenetration(CachedEntity *entity, int hb)
diff --git a/src/hooks.cpp b/src/hooks.cpp
index 1ceb18df..2cb4e90a 100644
--- a/src/hooks.cpp
+++ b/src/hooks.cpp
@@ -118,4 +118,5 @@ VMTHook soundclient{};
VMTHook enginevgui{};
VMTHook vstd{};
VMTHook eventmanager2{};
+VMTHook toolbox{};
} // namespace hooks
diff --git a/src/hooks/CMakeLists.txt b/src/hooks/CMakeLists.txt
index 343961a2..b00156eb 100755
--- a/src/hooks/CMakeLists.txt
+++ b/src/hooks/CMakeLists.txt
@@ -15,6 +15,7 @@ set(files "${CMAKE_CURRENT_LIST_DIR}/CanPacket.cpp"
"${CMAKE_CURRENT_LIST_DIR}/RunCommand.cpp"
"${CMAKE_CURRENT_LIST_DIR}/SendNetMsg.cpp"
"${CMAKE_CURRENT_LIST_DIR}/Shutdown.cpp"
+ "${CMAKE_CURRENT_LIST_DIR}/Think.cpp"
"${CMAKE_CURRENT_LIST_DIR}/FireEvent.cpp"
"${CMAKE_CURRENT_LIST_DIR}/FireEventClientSide.cpp"
"${CMAKE_CURRENT_LIST_DIR}/IsPlayingTimeDemo.cpp"
diff --git a/src/hooks/CreateMove.cpp b/src/hooks/CreateMove.cpp
index ff754c4c..d9336441 100644
--- a/src/hooks/CreateMove.cpp
+++ b/src/hooks/CreateMove.cpp
@@ -23,7 +23,7 @@
static settings::Boolean minigun_jump{ "misc.minigun-jump-tf2c", "false" };
static settings::Boolean roll_speedhack{ "misc.roll-speedhack", "false" };
static settings::Boolean forward_speedhack{ "misc.roll-speedhack.forward", "false" };
-static settings::Boolean engine_pred{ "misc.engine-prediction", "true" };
+settings::Boolean engine_pred{ "misc.engine-prediction", "true" };
static settings::Boolean debug_projectiles{ "debug.projectiles", "false" };
static settings::Int fullauto{ "misc.full-auto", "0" };
static settings::Boolean fuckmode{ "misc.fuckmode", "false" };
@@ -31,6 +31,7 @@ static settings::Boolean fuckmode{ "misc.fuckmode", "false" };
class CMoveData;
namespace engine_prediction
{
+static Vector original_origin;
void RunEnginePrediction(IClientEntity *ent, CUserCmd *ucmd)
{
@@ -51,6 +52,8 @@ void RunEnginePrediction(IClientEntity *ent, CUserCmd *ucmd)
// Backup
float frameTime = g_GlobalVars->frametime;
float curTime = g_GlobalVars->curtime;
+ int tickcount = g_GlobalVars->tickcount;
+ original_origin = ent->GetAbsOrigin();
CUserCmd defaultCmd{};
if (ucmd == nullptr)
@@ -79,11 +82,19 @@ void RunEnginePrediction(IClientEntity *ent, CUserCmd *ucmd)
g_GlobalVars->frametime = frameTime;
g_GlobalVars->curtime = curTime;
+ g_GlobalVars->tickcount = tickcount;
// Adjust tickbase
NET_INT(ent, netvar.nTickBase)++;
+
return;
}
+// Restore Origin
+void FinishEnginePrediction(IClientEntity *ent, CUserCmd *ucmd)
+{
+ const_cast(ent->GetAbsOrigin()) = original_origin;
+ original_origin.Invalidate();
+}
} // namespace engine_prediction
void PrecalculateCanShoot()
@@ -222,6 +233,8 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
}
// PROF_BEGIN();
+ // Do not update if in warp, since the entities will stay identical either way
+ if (!hacks::tf2::warp::in_warp)
{
PROF_SECTION(EntityCache);
entity_cache::Update();
@@ -317,10 +330,17 @@ DEFINE_HOOKED_METHOD(CreateMove, bool, void *this_, float input_sample_time, CUs
{
PROF_SECTION(CM_WRAPPER);
EC::run(EC::CreateMove_NoEnginePred);
- if (engine_pred)
- engine_prediction::RunEnginePrediction(RAW_ENT(LOCAL_E), current_user_cmd);
- EC::run(EC::CreateMove);
+ if (engine_pred)
+ {
+ engine_prediction::RunEnginePrediction(RAW_ENT(LOCAL_E), current_user_cmd);
+ g_pLocalPlayer->UpdateEye();
+ }
+
+ if (hacks::tf2::warp::in_warp)
+ EC::run(EC::CreateMoveWarp);
+ else
+ EC::run(EC::CreateMove);
}
if (time_replaced)
g_GlobalVars->curtime = curtime_old;
@@ -464,6 +484,12 @@ DEFINE_HOOKED_METHOD(CreateMoveInput, void, IInput *this_, int sequence_nr, floa
// Run EC
EC::run(EC::CreateMoveLate);
+ if (CE_GOOD(LOCAL_E))
+ {
+ // Restore prediction
+ if (engine_prediction::original_origin.IsValid())
+ engine_prediction::FinishEnginePrediction(RAW_ENT(LOCAL_E), current_late_user_cmd);
+ }
// Write the usercmd
WriteCmd(this_, current_late_user_cmd, sequence_nr);
}
diff --git a/src/hooks/Think.cpp b/src/hooks/Think.cpp
new file mode 100644
index 00000000..165bcd6d
--- /dev/null
+++ b/src/hooks/Think.cpp
@@ -0,0 +1,28 @@
+#include "HookedMethods.hpp"
+
+extern settings::Boolean engine_pred;
+namespace hooked_methods
+{
+// Update Prediction, Used by warp/Doubletap aswell
+void UpdatePred()
+{
+ if (isHackActive() && g_IEngine->IsInGame() && CE_GOOD(LOCAL_E) && engine_pred)
+ {
+ int signon_state = *(int *) (*(unsigned *) &g_IBaseClientState + 304);
+ int m_nDeltaTick = *(int *) (*(unsigned *) &g_IBaseClientState + 408);
+ int lastoutgoingcommand = *(int *) (*(unsigned *) &g_IBaseClientState + offsets::lastoutgoingcommand());
+ int chokedcommands = *(int *) (*(unsigned *) &g_IBaseClientState + offsets::lastoutgoingcommand() + 4);
+ int last_command_ack = *(int *) (*(unsigned *) &g_IBaseClientState + offsets::lastoutgoingcommand() + 8);
+
+ // Only run if fully connected
+ if (signon_state == 6 && m_nDeltaTick > 0)
+ g_IPrediction->Update(m_nDeltaTick ? m_nDeltaTick + 1 : 0, m_nDeltaTick > 0, last_command_ack, lastoutgoingcommand + chokedcommands);
+ }
+}
+DEFINE_HOOKED_METHOD(Think, void, IToolFrameworkInternal *_this, bool finaltick)
+{
+ UpdatePred();
+ return original::Think(_this, finaltick);
+}
+
+} // namespace hooked_methods
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index 6c4bd9d5..05d9ba27 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -156,6 +156,11 @@ void LocalPlayer::Update()
}
}
+void LocalPlayer::UpdateEye()
+{
+ v_Eye = entity->m_vecOrigin() + CE_VECTOR(entity, netvar.vViewOffset);
+}
+
void LocalPlayer::UpdateEnd()
{
if (!isFakeAngleCM)
diff --git a/src/navparser.cpp b/src/navparser.cpp
index 4d467915..31496ffb 100644
--- a/src/navparser.cpp
+++ b/src/navparser.cpp
@@ -807,7 +807,7 @@ static void drawcrumbs()
#endif
static InitRoutine runinit([]() {
- EC::Register(EC::CreateMove, cm, "cm_navparser", EC::average);
+ EC::Register(EC::CreateMove_NoEnginePred, cm, "cm_navparser", EC::average);
#if ENABLE_VISUALS
EC::Register(EC::Draw, drawcrumbs, "draw_navparser", EC::average);
#endif
diff --git a/src/nospread.cpp b/src/nospread.cpp
index 7001e888..d3c0e640 100644
--- a/src/nospread.cpp
+++ b/src/nospread.cpp
@@ -845,6 +845,41 @@ void FX_FireBullets_hook(IClientEntity *weapon, int player, Vector *origin, Vect
}*/
static Timer update_nospread_timer{};
+void CreateMove2()
+{
+ if (bullet)
+ {
+ static auto sv_usercmd_custom_random_seed = g_ICvar->FindVar("sv_usercmd_custom_random_seed");
+ if (!sv_usercmd_custom_random_seed)
+ sv_usercmd_custom_random_seed = g_ICvar->FindVar("sv_usercmd_custom_random_seed");
+
+ // Server owner decided it would be a great idea to give the user control over the random seed
+ else if (!sv_usercmd_custom_random_seed->GetBool())
+ {
+ auto seed = MD5_PseudoRandom(current_user_cmd->command_number) & 0x7FFFFFFF;
+ prediction_seed = *reinterpret_cast(&seed);
+ use_usercmd_seed = true;
+ }
+ // Normal server
+ else
+ use_usercmd_seed = false;
+
+ // Synced, mark as such to other modules
+ if (no_spread_synced == SYNCED)
+ is_syncing = false;
+ // Not synced currently, try to sync
+ if (no_spread_synced == NOT_SYNCED && !bad_mantissa)
+ {
+ is_syncing = true;
+ should_update_time = true;
+ update_nospread_timer.update();
+ }
+ // Else if mantissa bad, update every 10 mins
+ else if (no_spread_synced == NOT_SYNCED && update_nospread_timer.test_and_set(10 * 60 * 1000))
+ no_spread_synced = CORRECTING;
+ }
+}
+
static InitRoutine init_bulletnospread([]() {
// Get our detour hooks running
static auto writeusercmd_addr = gSignatures.GetClientSignature("55 89 E5 57 56 53 83 EC 2C 8B 45 ? 8B 7D ? 8B 5D ? 89 45 ? 8B 40");
@@ -855,42 +890,8 @@ static InitRoutine init_bulletnospread([]() {
net_sendpacket_detour.Init(net_sendpacket_addr, (void *) NET_SendPacket_hook);*/
// Register Event callbacks
- EC::Register(
- EC::CreateMove,
- []() {
- if (bullet)
- {
- static auto sv_usercmd_custom_random_seed = g_ICvar->FindVar("sv_usercmd_custom_random_seed");
- if (!sv_usercmd_custom_random_seed)
- sv_usercmd_custom_random_seed = g_ICvar->FindVar("sv_usercmd_custom_random_seed");
-
- // Server owner decided it would be a great idea to give the user control over the random seed
- else if (!sv_usercmd_custom_random_seed->GetBool())
- {
- auto seed = MD5_PseudoRandom(current_user_cmd->command_number) & 0x7FFFFFFF;
- prediction_seed = *reinterpret_cast(&seed);
- use_usercmd_seed = true;
- }
- // Normal server
- else
- use_usercmd_seed = false;
-
- // Synced, mark as such to other modules
- if (no_spread_synced == SYNCED)
- is_syncing = false;
- // Not synced currently, try to sync
- if (no_spread_synced == NOT_SYNCED && !bad_mantissa)
- {
- is_syncing = true;
- should_update_time = true;
- update_nospread_timer.update();
- }
- // Else if mantissa bad, update every 10 mins
- else if (no_spread_synced == NOT_SYNCED && update_nospread_timer.test_and_set(10 * 60 * 1000))
- no_spread_synced = CORRECTING;
- }
- },
- "nospread_createmove2");
+ EC::Register(EC::CreateMove, CreateMove2, "nospread_createmove2");
+ EC::Register(EC::CreateMoveWarp, CreateMove2, "nospread_createmove2w");
#if ENABLE_VISUALS
EC::Register(
EC::Draw,