#include namespace Components { Dvar::Var Debug::DebugOverlay; Game::dvar_t** Debug::PlayerDebugHealth = reinterpret_cast(0x7A9F7C); const char* Debug::PMFlagsValues[] = { "PMF_PRONE", "PMF_DUCKED", "PMF_MANTLE", "PMF_LADDER", "PMF_SIGHT_AIMING", "PMF_BACKWARDS_RUN", "PMF_WALKING", "PMF_TIME_HARDLANDING", "PMF_TIME_KNOCKBACK", "PMF_PRONEMOVE_OVERRIDDEN", "PMF_RESPAWNED", "PMF_FROZEN", "PMF_LADDER_FALL", "PMF_JUMPING", "PMF_SPRINTING", "PMF_SHELLSHOCKED", "PMF_MELEE_CHARGE", "PMF_NO_SPRINT", "PMF_NO_JUMP", "PMF_REMOTE_CONTROLLING", "PMF_ANIM_SCRIPTED", "PMF_UNK1", "PMF_DIVING", }; const char* Debug::POFlagsValues[] = { "POF_INVULNERABLE", "POF_REMOTE_EYES", "POF_LASER_ALTVIEW", "POF_THERMAL_VISION", "POF_THERMAL_VISION_OVERLAY_FOF", "POF_REMOTE_CAMERA_SOUNDS", "POF_ALT_SCENE_REAR_VIEW", "POF_ALT_SCENE_TAG_VIEW", "POF_SHIELD_ATTACHED_TO_WORLD_MODEL", "POF_DONT_LERP_VIEWANGLES", "POF_EMP_JAMMED", "POF_FOLLOW", "POF_PLAYER", "POF_SPEC_ALLOW_CYCLE", "POF_SPEC_ALLOW_FREELOOK", "POF_AC130", "POF_COMPASS_PING", "POF_ADS_THIRD_PERSON_TOGGLE", }; const char* Debug::PLFlagsValues[] = { "PLF_ANGLES_LOCKED", "PLF_USES_OFFSET", "PLF_WEAPONVIEW_ONLY", }; const char* Debug::PEFlagsValues[] = { "EF_NONSOLID_BMODEL", "EF_TELEPORT_BIT", "EF_CROUCHING", "EF_PRONE", "EF_UNK1", "EF_NODRAW", "EF_TIMED_OBJECT", "EF_VOTED", "EF_TALK", "EF_FIRING", "EF_TURRET_ACTIVE_PRONE", "EF_TURRET_ACTIVE_DUCK", "EF_LOCK_LIGHT_VIS", "EF_AIM_ASSIST", "EF_LOOP_RUMBLE", "EF_LASER_SIGHT", "EF_MANTLE", "EF_DEAD", "EF_ADS", "EF_NEW", "EF_VEHICLE_ACTIVE", "EF_JAMMING", "EF_COMPASS_PING", "EF_SOFT", }; std::string Debug::BuildPMFlagsString(const Game::playerState_s* ps) { std::string result; for (size_t i = 0; i < ARRAYSIZE(PMFlagsValues); ++i) { result.append(Utils::String::VA("^%c%s\n", ((ps->pm_flags & (1 << i)) == 0) ? '7' : '2', PMFlagsValues[i])); } return result; } std::string Debug::BuildPOFlagsString(const Game::playerState_s* ps) { std::string result; for (size_t i = 0; i < ARRAYSIZE(POFlagsValues); ++i) { result.append(Utils::String::VA("^%c%s\n", ((ps->otherFlags & (1 << i)) == 0) ? '7' : '2', POFlagsValues[i])); } return result; } std::string Debug::BuildPLFlagsString(const Game::playerState_s* ps) { std::string result; for (size_t i = 0; i < ARRAYSIZE(PLFlagsValues); ++i) { result.append(Utils::String::VA("^%c%s\n", ((ps->linkFlags & (1 << i)) == 0) ? '7' : '2', PLFlagsValues[i])); } return result; } std::string Debug::BuildPEFlagsString(const Game::playerState_s* ps) { std::string result; for (size_t i = 0; i < ARRAYSIZE(PEFlagsValues); ++i) { result.append(Utils::String::VA("^%c%s\n", ((ps->eFlags & (1 << i)) == 0) ? '7' : '2', PEFlagsValues[i])); } return result; } void Debug::CG_Debug_DrawPSFlags(const int localClientNum) { const auto* cgameGlob = Game::cgArray; auto* const scrPlace = Game::ScrPlace_GetActivePlacement(localClientNum); constexpr auto maxChars = 4096; constexpr float colorWhite[] = {1.0f, 1.0f, 1.0f, 1.0f}; auto* const font1 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE_2); auto* const font2 = Game::UI_GetFontHandle(scrPlace, 6, MY_SCALE2); Game::UI_DrawText(scrPlace, "Client View of Flags", maxChars, font2, -60.0f, 0, 1, 1, MY_SCALE2, colorWhite, 1); const auto pmf = BuildPMFlagsString(&cgameGlob->predictedPlayerState); Game::UI_DrawText(scrPlace, pmf.data(), maxChars, font1, 30.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3); const auto pof = BuildPOFlagsString(&cgameGlob->predictedPlayerState); Game::UI_DrawText(scrPlace, pof.data(), maxChars, font1, 350.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3); const auto plf = BuildPLFlagsString(&cgameGlob->predictedPlayerState); Game::UI_DrawText(scrPlace, plf.data(), maxChars, font1, 350.0f, 250.0f, 1, 1, MY_SCALE_2, colorWhite, 3); const auto pef = BuildPEFlagsString(&cgameGlob->predictedPlayerState); Game::UI_DrawText(scrPlace, pef.data(), maxChars, font1, 525.0f, 20.0f, 1, 1, MY_SCALE_2, colorWhite, 3); } void Debug::CG_DrawDebugPlayerHealth(const int localClientNum) { float healtha; constexpr float color1[] = {0.0f, 0.0f, 0.0f, 1.0f}; constexpr float color2[] = {0.0f, 1.0f, 0.0f, 1.0f}; assert((*PlayerDebugHealth)->current.enabled); const auto* cgameGlob = Game::cgArray; if (cgameGlob->predictedPlayerState.stats[0] && cgameGlob->predictedPlayerState.stats[2]) { const auto health = static_cast(cgameGlob->predictedPlayerState.stats[0]) / static_cast(cgameGlob->predictedPlayerState.stats[2]); const auto stats = ((health - 1.0f) < 0.0f) ? static_cast(cgameGlob->predictedPlayerState.stats[0]) / static_cast(cgameGlob->predictedPlayerState.stats[2]) : 1.0f; healtha = ((0.0f - health) < 0.0f) ? stats : 0.0f; } else { healtha = 0.0f; } auto* const scrPlace = Game::ScrPlace_GetActivePlacement(localClientNum); Game::CL_DrawStretchPic(scrPlace, 10.0f, 10.0f, 100.0f, 10.0f, 1, 1, 0.0f, 0.0f, 1.0f, 1.0f, color1, *Game::whiteMaterial); Game::CL_DrawStretchPic(scrPlace, 10.0f, 10.0f, 100.0f * healtha, 10.0f, 1, 1, 0.0f, 0.0f, healtha, 1.0f, color2, *Game::whiteMaterial); } void Debug::CG_DrawDebugOverlays_Hk(const int localClientNum) { switch (DebugOverlay.get()) { case 2: CG_Debug_DrawPSFlags(localClientNum); break; default: break; } if ((*PlayerDebugHealth)->current.enabled) { CG_DrawDebugPlayerHealth(localClientNum); } } void Debug::CL_InitDebugDvars() { static const char* debugOverlayNames_0[] = { "Off", "ViewmodelInfo", "Playerstate Flags", "Entity Counts", "Controllers", "FontTest", nullptr, }; DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0, Game::dvar_flag::DVAR_NONE, "Toggles the display of various debug info."); } Debug::Debug() { Scheduler::Once(CL_InitDebugDvars, Scheduler::Pipeline::MAIN); // Hook end of CG_DrawDebugOverlays (This is to ensure some checks are done before our hook is executed). Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick(); } }