diff --git a/docs/cvars.md b/docs/cvars.md index f7692ed3..d9c7d80a 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -262,7 +262,7 @@ |sar_hud_ghost_spec|0|Show the name of the ghost you're currently spectating.| |sar_hud_grounded|0|Draws the state of player being on ground.| |sar_hud_groundframes|0|Draws the number of ground frames since last landing. Setting it to 2 preserves the value.| -|sar_hud_groundspeed|0|Draw the speed of the player upon leaving the ground.
0 = Default,
1 = Groundspeed,
2 = Groundspeed (Gain)| +|sar_hud_groundspeed|0|Draw the speed of the player upon leaving the ground.
0 = Default
1 = Groundspeed
2 = Groundspeed (Gain)| |sar_hud_hide_text|cmd|sar_hud_hide_text \ - hides the nth text value in the HUD| |sar_hud_inspection|0|Draws entity inspection data.| |sar_hud_jump|0|Draws current jump distance.| @@ -503,6 +503,7 @@ |sar_scrollspeed_y|210|Scroll speed HUD y offset.| |sar_seamshot_finder|0|Enables or disables seamshot finder overlay.| |sar_session|cmd|sar_session - prints the current tick of the server since it has loaded| +|sar_set_promo_items_state|cmd|sar_set_promo_items_state \... - enables coop promotional items on spawn.| |sar_show_entinp|0|Print all entity inputs to console.| |sar_skiptodemo|cmd|sar_skiptodemo \ - skip demos in demo queue to this demo| |sar_speedrun_autoreset_clear|cmd|sar_speedrun_autoreset_clear - stop using the autoreset file| diff --git a/src/Cheats.cpp b/src/Cheats.cpp index 3cd1769d..511fb323 100644 --- a/src/Cheats.cpp +++ b/src/Cheats.cpp @@ -331,6 +331,7 @@ Memory::Patch *g_floorReportalPatch; Memory::Patch *g_coopLoadingDotsPatch; Memory::Patch *g_autoGrabPatchServer; Memory::Patch *g_autoGrabPatchClient; +Memory::Patch *g_promoFlagsPatch; void Cheats::Init() { sv_laser_cube_autoaim = Variable("sv_laser_cube_autoaim"); @@ -399,6 +400,14 @@ void Cheats::Init() { g_autoGrabPatchClient->Restore(); } + g_promoFlagsPatch = new Memory::Patch(); + auto portal2PromoFlags = Memory::Scan(MODULE("server"), Offsets::Portal2PromoFlagsSig, Offsets::Portal2PromoFlagsOff); + if (portal2PromoFlags) { + unsigned char promoFlagsByte = 0x00; + g_promoFlagsPatch->Execute(portal2PromoFlags, &promoFlagsByte, 1); // Note: Has to be active before map loads. + g_promoFlagsPatch->Restore(); + } + Variable::RegisterAll(); Command::RegisterAll(); } @@ -422,6 +431,8 @@ void Cheats::Shutdown() { SAFE_DELETE(g_autoGrabPatchServer); g_autoGrabPatchClient->Restore(); SAFE_DELETE(g_autoGrabPatchClient); + g_promoFlagsPatch->Restore(); + SAFE_DELETE(g_promoFlagsPatch); } @@ -589,3 +600,37 @@ void Cheats::CheckAutoGrab() { g_autoGrabPatchClient->Restore(); } } + +DECL_AUTO_COMMAND_COMPLETION(sar_set_promo_items_state, ({"skins", "helmet", "antenna"})) // TODO: Add support for autofilling multiple args. +CON_COMMAND_F_COMPLETION(sar_set_promo_items_state, "sar_set_promo_items_state ... - enables coop promotional items on spawn.\n", FCVAR_CHEAT, AUTOCOMPLETION_FUNCTION(sar_set_promo_items_state)) { + if (!g_promoFlagsPatch || !g_promoFlagsPatch->IsInit()) { + return console->Print("sar_set_promo_items_state is not available.\n"); + } + + if (args.ArgC() < 2) { + return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString); + } + + unsigned char targetFlags = 0; + for (int i = 1; i < args.ArgC(); i++) { + if (strcasecmp(args[i], "off") == 0) { + g_promoFlagsPatch->Restore(); + return; + } + if (strcasecmp(args[i], "all") == 0) { + targetFlags = 0b111; + break; + } + if (strcasecmp(args[i], "skins") == 0) {; + targetFlags |= 0b001; + } else if (strcasecmp(args[i], "helmet") == 0) { + targetFlags |= 0b010; + } else if (strcasecmp(args[i], "antenna") == 0) { + targetFlags |= 0b100; + } else { + return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString); + } + } + g_promoFlagsPatch->Restore(); + g_promoFlagsPatch->Execute(&targetFlags, 1); +} diff --git a/src/Cheats.hpp b/src/Cheats.hpp index 28141e5e..95a055c1 100644 --- a/src/Cheats.hpp +++ b/src/Cheats.hpp @@ -50,3 +50,5 @@ extern Variable hide_gun_when_holding; extern Variable r_flashlightbrightness; extern Command sar_togglewait; + +extern Memory::Patch *g_promoFlagsPatch; diff --git a/src/Modules/Server.cpp b/src/Modules/Server.cpp index 2bfa3623..164a4744 100644 --- a/src/Modules/Server.cpp +++ b/src/Modules/Server.cpp @@ -285,6 +285,7 @@ DETOUR(Server::PlayerRunCommand, CUserCmd *cmd, void *moveHelper) { Cheats::AutoStrafe(slot, thisptr, cmd); Cheats::CheckFloorReportals(); + if (!sv_cheats.GetBool()) g_promoFlagsPatch->Restore(); // We only want to check this once per map load, to preserve the intended behavior. inputHud.SetInputInfo(slot, cmd->buttons, {cmd->sidemove, cmd->forwardmove, cmd->upmove}); diff --git a/src/Offsets/Portal 2 9568.hpp b/src/Offsets/Portal 2 9568.hpp index e616cf66..d132ec06 100644 --- a/src/Offsets/Portal 2 9568.hpp +++ b/src/Offsets/Portal 2 9568.hpp @@ -523,6 +523,10 @@ SIGSCAN_DEFAULT(FloorReportalBranch, "75 7D 8B 8E C0 04 00 00", SIGSCAN_DEFAULT(CPortal_Player__PollForUseEntity_CheckMP, "74 ? ? ? 8B 82 ? ? ? ? FF D0 84 C0 74 ? 8B CE", "74 ? 8B 10 83 EC 0C 50 FF 92 88 00 00 00 83 C4 10 84 C0 ? ? ? ? ? ? ? ? ? ? ? ? ? 00 00") // "OnJump" xref -> CPortal_Player:PreThink -> CBasePlayer::PreThink -> CBasePlayer::ItemPreFrame -> CBasePlayer::PlayerUse -> CPortal_Player vtable offset -> CPortal_Player::PlayerUse -> Second function call from disassembly -> CPortal_Player::PollForUseEntity -> jz instruction +SIGSCAN_DEFAULT(Portal2PromoFlagsSig, "F6 05 ? ? ? ? 02 74 ? 8B CE", + "A1 ? ? ? ? A8 02") // "#P2_WearableType_Flag" xref -> CPortal_Player::GiveDefaultItems -> bitwise & -> portal2 promo flag +OFFSET_DEFAULT(Portal2PromoFlagsOff, 2, 1) + // VPhysics OFFSET_EMPTY(DestroyEnvironment) OFFSET_EMPTY(GetActiveEnvironmentByIndex) diff --git a/src/Utils/Memory.cpp b/src/Utils/Memory.cpp index 8c883161..c58d57ab 100644 --- a/src/Utils/Memory.cpp +++ b/src/Utils/Memory.cpp @@ -245,6 +245,7 @@ Memory::Patch::~Patch() { } bool Memory::Patch::Execute() { if (this->isPatched) return true; // already executed + if (!this->IsInit()) return false; unsigned char *tmpPatch = new unsigned char[this->size]; // We create another patch, because this->patch is gonna be deleted memcpy(tmpPatch, this->patch, this->size); diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index b281f9a3..593506e5 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -50,6 +50,13 @@ namespace Memory { bool Execute(uintptr_t location, unsigned char (&bytes)[size]) { return Execute(location, bytes, size); } + bool Execute(unsigned char *bytes, size_t size) { + return Execute(location, bytes, size); + } + template + bool Execute(unsigned char (&bytes)[size]) { + return Execute(location, bytes, size); + } bool Restore(); bool IsPatched(); bool IsInit();